Skip to content

Commit

Permalink
Improve search UI (#2643)
Browse files Browse the repository at this point in the history
  • Loading branch information
gregberge authored Dec 18, 2024
1 parent 8af1abc commit aaf8daf
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 86 deletions.
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion packages/gitbook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"memoizee": "^0.4.15",
"next": "14.2.15",
"next-themes": "^0.2.1",
"nuqs": "^1.17.4",
"nuqs": "^2.2.3",
"object-hash": "^3.0.0",
"openapi-types": "^12.1.3",
"p-map": "^7.0.0",
Expand Down
81 changes: 42 additions & 39 deletions packages/gitbook/src/app/(site)/(content)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CustomizationThemeMode } from '@gitbook/api';
import { Metadata, Viewport } from 'next';
import { headers } from 'next/headers';
import { NuqsAdapter } from 'nuqs/adapters/next/app';
import React from 'react';
import * as ReactDOM from 'react-dom';

Expand Down Expand Up @@ -55,46 +56,48 @@ export default async function ContentLayout(props: { children: React.ReactNode }
});

return (
<ClientContexts
nonce={nonce}
forcedTheme={
getQueryStringTheme() ??
(customization.themes.toggeable ? undefined : customization.themes.default)
}
>
<SpaceLayout
space={space}
contentTarget={contentTarget}
site={site}
spaces={spaces}
sections={sections}
customization={customization}
pages={pages}
ancestors={ancestors}
content={content}
<NuqsAdapter>
<ClientContexts
nonce={nonce}
forcedTheme={
getQueryStringTheme() ??
(customization.themes.toggeable ? undefined : customization.themes.default)
}
>
{children}
</SpaceLayout>

{scripts.length > 0 ? (
<>
<LoadIntegrations />
{scripts.map(({ script }) => (
<script key={script} async src={script} nonce={nonce} />
))}
</>
) : null}

{scripts.some((script) => script.cookies) || customization.privacyPolicy.url ? (
<React.Suspense fallback={null}>
<CookiesToast privacyPolicy={customization.privacyPolicy.url} />
</React.Suspense>
) : null}

<RocketLoaderDetector nonce={nonce} />

<AdminToolbar space={space} content={content} />
</ClientContexts>
<SpaceLayout
space={space}
contentTarget={contentTarget}
site={site}
spaces={spaces}
sections={sections}
customization={customization}
pages={pages}
ancestors={ancestors}
content={content}
>
{children}
</SpaceLayout>

{scripts.length > 0 ? (
<>
<LoadIntegrations />
{scripts.map(({ script }) => (
<script key={script} async src={script} nonce={nonce} />
))}
</>
) : null}

{scripts.some((script) => script.cookies) || customization.privacyPolicy.url ? (
<React.Suspense fallback={null}>
<CookiesToast privacyPolicy={customization.privacyPolicy.url} />
</React.Suspense>
) : null}

<RocketLoaderDetector nonce={nonce} />

<AdminToolbar space={space} content={content} />
</ClientContexts>
</NuqsAdapter>
);
}

Expand Down
35 changes: 11 additions & 24 deletions packages/gitbook/src/components/Search/SearchAskAnswer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,42 +51,29 @@ export function SearchAskAnswer(props: { pointer: SiteContentPointer; query: str
React.useEffect(() => {
let cancelled = false;

setState({
type: 'loading',
});
setState({ type: 'loading' });

(async () => {
const stream = iterateStreamResponse(
streamAskQuestion(organizationId, siteId, siteSpaceId ?? null, query),
);
const response = streamAskQuestion(organizationId, siteId, siteSpaceId ?? null, query);
const stream = iterateStreamResponse(response);

setSearchState((prev) =>
prev
? {
...prev,
ask: true,
query,
}
: null,
);
// When we pass in "ask" mode, the query could still be updated by the client
// we ensure that the query is up-to-date before starting the stream.
setSearchState((prev) => (prev ? { ...prev, query, ask: true } : null));

for await (const chunk of stream) {
if (cancelled) {
return;
}
setState({
type: 'answer',
answer: chunk,
});

setState({ type: 'answer', answer: chunk });
}
})().catch((error) => {
})().catch(() => {
if (cancelled) {
return;
}

setState({
type: 'error',
});
setState({ type: 'error' });
});

return () => {
Expand All @@ -96,7 +83,7 @@ export function SearchAskAnswer(props: { pointer: SiteContentPointer; query: str
cancelled = true;
}
};
}, [organizationId, siteId, siteSpaceId, query]);
}, [organizationId, siteId, siteSpaceId, query, setState, setSearchState]);

React.useEffect(() => {
return () => {
Expand Down
20 changes: 6 additions & 14 deletions packages/gitbook/src/components/Search/SearchModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { tcls } from '@/lib/tailwind';
import { SearchAskAnswer, searchAskState } from './SearchAskAnswer';
import { SearchResults, SearchResultsRef } from './SearchResults';
import { SearchScopeToggle } from './SearchScopeToggle';
import { SearchState, useSearch } from './useSearch';
import { SearchState, UpdateSearchState, useSearch } from './useSearch';
import { LoadingPane } from '../primitives/LoadingPane';

interface SearchModalProps {
Expand Down Expand Up @@ -55,10 +55,6 @@ export function SearchModal(props: SearchModalProps) {
};
}, [isSearchOpened]);

const onChangeQuery = (newQuery: SearchState) => {
setSearchState(newQuery);
};

const onClose = async (to?: string) => {
await setSearchState(null);
if (to) {
Expand Down Expand Up @@ -129,7 +125,7 @@ export function SearchModal(props: SearchModalProps) {
<SearchModalBody
{...props}
state={state}
onChangeQuery={onChangeQuery}
setSearchState={setSearchState}
onClose={onClose}
/>
</div>
Expand All @@ -142,7 +138,7 @@ export function SearchModal(props: SearchModalProps) {
function SearchModalBody(
props: SearchModalProps & {
state: SearchState;
onChangeQuery: (newQuery: SearchState) => void;
setSearchState: UpdateSearchState;
onClose: (to?: string) => void;
},
) {
Expand All @@ -154,7 +150,7 @@ function SearchModalBody(
withAsk,
isMultiVariants,
state,
onChangeQuery,
setSearchState,
onClose,
} = props;

Expand Down Expand Up @@ -193,7 +189,7 @@ function SearchModalBody(
};

const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
onChangeQuery({
setSearchState({
ask: false, // When typing, we go back to the default search mode
query: event.target.value,
global: state.global,
Expand Down Expand Up @@ -312,11 +308,7 @@ function SearchModalBody(
query={state.query}
withAsk={withAsk}
onSwitchToAsk={() => {
onChangeQuery({
ask: true,
query: state.query,
global: state.global,
});
setSearchState((state) => (state ? { ...state, ask: true } : null));
}}
></SearchResults>
) : null}
Expand Down
15 changes: 7 additions & 8 deletions packages/gitbook/src/components/Search/useSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,17 @@ const keyMap = {
global: parseAsBoolean,
};

const options: UseQueryStatesOptions = {
history: 'replace',
};
export type UpdateSearchState = (
update: React.SetStateAction<SearchState | null>,
) => Promise<URLSearchParams>;

/**
* Hook to access the current search query and update it.
*/
export function useSearch(): [
SearchState | null,
(update: React.SetStateAction<SearchState | null>) => Promise<URLSearchParams>,
] {
const [rawState, setRawState] = useQueryStates(keyMap, options);
export function useSearch(): [SearchState | null, UpdateSearchState] {
const [rawState, setRawState] = useQueryStates(keyMap, {
history: 'replace',
});

const state = React.useMemo<SearchState | null>(() => {
if (rawState === null || rawState.q === null) {
Expand Down

0 comments on commit aaf8daf

Please sign in to comment.