import { memo } from "preact/compat"; import { useCallback, useEffect, useMemo, useRef, useState, } from "preact/hooks"; import InputSearch from "../ui/InputSearch"; import useI18n from "../utils/hooks/useI18n"; import type { TargetedInputEvent } from "preact"; import type { ReactElement } from "preact/compat"; import type { IconButtonProps } from "../ui/IconButton/IconButton"; import type { InputProps } from "../ui/Input/Input"; import type { InputSearchProps } from "../ui/InputSearch/InputSearch"; export type SearchBaseProps = { autofocus?: boolean; cancelButtonProps?: IconButtonProps; childrenContainerClassName?: string; inputProps?: InputProps; withResultsClassName?: string; } & InputSearchProps & Pick< Partial>, "resultClassName" | "resultsClassName" | "resultsContainerClassName" >; import { cloneElement, createContext, toChildArray } from "preact"; import { twMerge } from "tailwind-merge"; import type { SearchResultsProps } from "../SearchResults/SearchResults"; export interface SearchContextType { open: boolean; query: string; selectedQuery: string; setOpen: (open: boolean) => void; setQuery: (query: string) => void; setResults: (id: string, results: unknown[]) => void; setSelectedQuery: (query: string) => void; } export const SearchContext = createContext({ open: false, query: "", selectedQuery: "", setOpen: () => {}, setQuery: () => {}, setResults: () => {}, setSelectedQuery: () => {}, }); function SearchBase({ autofocus, cancelButtonProps, children, childrenContainerClassName, inputProps, resultClassName, resultsClassName, resultsContainerClassName, withResultsClassName, ...props }: SearchBaseProps) { const { t } = useI18n(); // We use a selectedQuery state to avoid making a new request when we select a result. const [selectedQuery, setSelectedQuery] = useState(""); const [query, setQuery] = useState(""); const [showResults, setShowResults] = useState(false); const [open, setOpen] = useState(false); const resultsBySearchRef = useRef>({}); // Autofocus the input const inputRef = useRef(null); useEffect(() => { if (autofocus && inputRef.current) { inputRef.current.focus(); } }, [autofocus]); const setResults = useCallback( (id: string, results: unknown[]) => { resultsBySearchRef.current[id] = results; setShowResults( open && Object.values(resultsBySearchRef.current).some((r) => { return r.length > 0; }), ); }, [open], ); const value = useMemo(() => { return { open, query, selectedQuery, setOpen, setQuery, setResults: setResults, setSelectedQuery, }; }, [open, query, selectedQuery, setResults]); const inputPropss: InputProps = useMemo(() => { return { placeholder: t("search_placeholder"), ref: inputRef, ...(inputProps || {}), onChange: (evt: TargetedInputEvent) => { setQuery((evt.target as HTMLInputElement).value); setSelectedQuery(""); setOpen(true); }, onFocus: () => { setOpen(true); }, value: selectedQuery || query, }; }, [query, selectedQuery, t, inputProps]); const cancelButtonPropss: IconButtonProps = useMemo(() => { return { ...(cancelButtonProps || {}), onClick: () => { setSelectedQuery(""); setQuery(""); setOpen(false); }, }; }, [cancelButtonProps]); return (
{toChildArray(children)?.map((child: ReactElement) => { return cloneElement(child, { resultClassName, resultsClassName, resultsContainerClassName, }); })} {/*} {showTrajectoriesResults && ( <> {t("search_trajectories_results")} {trajectories.results.map((trajectory: RealtimeTrajectory) => { return ( ); })} )} */}
); } export default memo(SearchBase);