Files
gitbook/src/components/Search/useSearch.ts
T
Steven H 9f75929269 Fix an issue where following a link to a different space would not update the ToC or header. (#284)
* Fix an issue where clicking links to other spaces would not update ToC/header

* Fix

* Remove dev text

* Use URL.CanParse for testing the external url

---------

Co-authored-by: Samy Pessé <samypesse@gmail.com>
2024-03-15 20:09:29 +01:00

103 lines
2.9 KiB
TypeScript

import { useQueryStates, parseAsBoolean, parseAsString, UseQueryStatesOptions } from 'nuqs';
import React from 'react';
import { LinkProps } from '../primitives';
export interface SearchState {
query: string;
ask: boolean;
global: boolean;
}
// KeyMap needs to be statically defined to avoid `setRawState` being redefined on every render.
const keyMap = {
q: parseAsString,
ask: parseAsBoolean,
global: parseAsBoolean,
};
const options: UseQueryStatesOptions = {
history: 'replace',
};
/**
* 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);
const state = React.useMemo<SearchState | null>(() => {
if (rawState === null || rawState.q === null) {
return null;
}
return { query: rawState.q, ask: !!rawState.ask, global: !!rawState.global };
}, [rawState]);
const stateRef = React.useRef(state);
React.useLayoutEffect(() => {
stateRef.current = state;
}, [state]);
const setState = React.useCallback<
(update: React.SetStateAction<SearchState | null>) => Promise<URLSearchParams>
>(
(update) => {
if (typeof update === 'function') {
update = update(stateRef.current);
}
if (update === null) {
return setRawState({
q: null,
ask: null,
global: null,
});
} else {
return setRawState({
q: update.query,
ask: update.ask ? true : null,
global: update.global ? true : null,
});
}
},
[setRawState],
);
return [state, setState];
}
/**
* Hook to create a link to a search query.
*/
export function useSearchLink(): (query: Partial<SearchState>) => LinkProps {
const [, setSearch] = useSearch();
return React.useCallback(
(query) => {
const searchParams = new URLSearchParams();
searchParams.set('q', query.query ?? '');
query.ask ? searchParams.set('ask', 'on') : searchParams.delete('ask');
query.global ? searchParams.set('global', 'on') : searchParams.delete('global');
return {
href: '?' + searchParams.toString(),
prefetch: false,
onClick: (event) => {
event.preventDefault();
setSearch((prev) => ({
query: '',
ask: false,
global: false,
...(prev ?? {}),
...query,
}));
},
};
},
[setSearch],
);
}