import { useFetcher, useNavigate, type FormProps, type Fetcher, } from '@remix-run/react'; import React, {useRef, useEffect} from 'react'; import type {PredictiveSearchReturn} from '~/lib/search'; import {useAside} from './Aside'; type SearchFormPredictiveChildren = (args: { fetchResults: (event: React.ChangeEvent) => void; goToSearch: () => void; inputRef: React.MutableRefObject; fetcher: Fetcher; }) => React.ReactNode; type SearchFormPredictiveProps = Omit & { children: SearchFormPredictiveChildren | null; }; export const SEARCH_ENDPOINT = '/search'; /** * Search form component that sends search requests to the `/search` route **/ export function SearchFormPredictive({ children, className = 'predictive-search-form', ...props }: SearchFormPredictiveProps) { const fetcher = useFetcher({key: 'search'}); const inputRef = useRef(null); const navigate = useNavigate(); const aside = useAside(); /** Reset the input value and blur the input */ function resetInput(event: React.FormEvent) { event.preventDefault(); event.stopPropagation(); if (inputRef?.current?.value) { inputRef.current.blur(); } } /** Navigate to the search page with the current input value */ function goToSearch() { const term = inputRef?.current?.value; navigate(SEARCH_ENDPOINT + (term ? `?q=${term}` : '')); aside.close(); } /** Fetch search results based on the input value */ function fetchResults(event: React.ChangeEvent) { fetcher.submit( {q: event.target.value || '', limit: 5, predictive: true}, {method: 'GET', action: SEARCH_ENDPOINT}, ); } // ensure the passed input has a type of search, because SearchResults // will select the element based on the input useEffect(() => { inputRef?.current?.setAttribute('type', 'search'); }, []); if (typeof children !== 'function') { return null; } return ( {children({inputRef, fetcher, fetchResults, goToSearch})} ); }