Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions components/search/context.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useCallback, useContext } from 'react';
import { useCallback } from 'react';

import SearchFocusedContext from './context';
import useFocus from './useFocus';

export default function SearchDummy() {
const { isFocused, setIsFocused } = useContext(SearchFocusedContext);
export default function SearchFallback() {
const { isFocused, setIsFocused } = useFocus();
const handleFocus = useCallback(() => setIsFocused(true), [setIsFocused]);
return (
<div className="ais-SearchBox w-full search-dummy" onFocus={handleFocus}>
Expand Down
45 changes: 31 additions & 14 deletions components/search/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
import SearchDummy from './dummy';
import useSearch from './useSearch';
import withInstantSearch from './withInstantSearch';
import { lazy, Suspense, useMemo } from 'react';
import { InstantSearch } from 'react-instantsearch';

import { getAlgoliaIndexName, getAlgoliaClient } from '../../utils/algolia';

import { FocusOuterWrapper, FocusProvider } from './useFocus';

import SearchFallback from './fallback';

const SearchWidgets = lazy(() => import('./widgets'));

const FUTURE = {
preserveSharedStateOnUnmount: true,
};

export default function Search() {
const searchClient = useMemo(() => getAlgoliaClient(), []);
const indexName = useMemo(() => getAlgoliaIndexName(), []);

function Search() {
const { AlgoliaSearch, handleFocus, isFocused, rootReference } = useSearch();
return (
<div className="relative" onFocus={handleFocus} ref={rootReference}>
{typeof AlgoliaSearch === 'function' ? (
<AlgoliaSearch isFocused={isFocused} />
) : (
<SearchDummy />
)}
</div>
<InstantSearch
future={FUTURE}
indexName={indexName}
searchClient={searchClient}
>
<FocusProvider>
<FocusOuterWrapper className="relative">
<Suspense fallback={<SearchFallback />}>
<SearchWidgets />
</Suspense>
</FocusOuterWrapper>
</FocusProvider>
</InstantSearch>
);
}

export default withInstantSearch(Search);
68 changes: 68 additions & 0 deletions components/search/useFocus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {
type Dispatch,
type SetStateAction,
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react';

const FocusContext = createContext<{
isFocused: boolean;
setIsFocused: Dispatch<SetStateAction<boolean>>;
}>({ isFocused: false, setIsFocused: () => {} });

export function FocusProvider({ children }) {
const [isFocused, setIsFocused] = useState(false);

const focusValue = useMemo(
() => ({ isFocused, setIsFocused }),
[isFocused, setIsFocused]
);

return (
<FocusContext.Provider value={focusValue}>{children}</FocusContext.Provider>
);
}

export function FocusOuterWrapper({ children, ...props }) {
const rootReference = useRef(null);
const { isFocused, setIsFocused } = useFocus();

const handleOutsideClick = useCallback(
(event) => {
if (
!rootReference.current ||
!rootReference.current.contains(event.target)
) {
setIsFocused(false);
}
},
[setIsFocused]
);

useEffect(() => {
if (isFocused) {
document.addEventListener('click', handleOutsideClick);
}
return () => {
document.removeEventListener('click', handleOutsideClick);
};
}, [handleOutsideClick, isFocused]);

const handleFocus = useCallback(() => setIsFocused(true), [setIsFocused]);

return (
<div {...props} onFocus={handleFocus} ref={rootReference}>
{children}
</div>
);
}

export default function useFocus() {
const { isFocused, setIsFocused } = useContext(FocusContext);
return { isFocused, setIsFocused };
}
49 changes: 0 additions & 49 deletions components/search/useSearch.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Highlight, Snippet } from 'react-instantsearch';

import SectionIcon from './sectionIcon';
import useHit from './useHit';
import type { Hit } from './validation';
import type { Hit } from './models';

export default function Hit({ hit }: { hit: Hit & HitType }) {
const { handleClick, isLoading } = useHit();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import { useRouter } from 'next/router';
import { useCallback, useContext, useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';

import SearchFocusedContext from './context';
import useFocus from '../../useFocus';

export default function useHit() {
const router = useRouter();
const { setIsFocused } = useContext(SearchFocusedContext);
const { setIsFocused } = useFocus();
const [isLoading, setIsLoading] = useState(false);

const handleLoadingEnded = useCallback(() => {
setIsLoading(false);
router.events.off('routeChangeComplete', handleLoadingEnded);
setIsFocused(false);
}, [router.events, setIsFocused]);

const handleClick = useCallback(() => {
setIsLoading(true);
router.events.on('routeChangeComplete', handleLoadingEnded);
}, [router.events, handleLoadingEnded]);

useEffect(
() => () => {
router.events.off('routeChangeComplete', handleLoadingEnded);
},
[router.events, handleLoadingEnded]
);

return { handleClick, isLoading };
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import {
useInstantSearch,
} from 'react-instantsearch';

import useFocus from '../useFocus';

import Hit from './hit';
import NoResults from './noResults';
import NoResultsBoundary from './noResultsBoundary';
import type { Hit as HitType } from './validation';
import type { Hit as HitType } from './hit/models';

import 'instantsearch.css/themes/satellite.css';

Expand Down Expand Up @@ -75,17 +77,21 @@ function transformPageTypeItems(items: MenuItem[]) {
}));
}

export default function AlgoliaSearch({ isFocused }: { isFocused: boolean }) {
export default function SearchWidgets() {
const { isFocused } = useFocus();

const hitsClassNames = useMemo<HitsProps<HitType>['classNames']>(
() => ({
item: 'border-ui-sidebar border-b',
list: 'px-4 py-2 m-0',
}),
[]
);

const {
indexUiState: { menu: { section } = {} },
} = useInstantSearch();

return (
<>
<SearchBox
Expand Down
38 changes: 0 additions & 38 deletions components/search/withInstantSearch.tsx

This file was deleted.