diff --git a/packages/basehub/src/react/search/primitive.tsx b/packages/basehub/src/react/search/primitive.tsx index 10af553..97072dc 100644 --- a/packages/basehub/src/react/search/primitive.tsx +++ b/packages/basehub/src/react/search/primitive.tsx @@ -520,62 +520,93 @@ const Root = < * Input * -----------------------------------------------------------------------------------------------*/ +const useIsoLayoutEffect = + typeof window === "undefined" ? React.useEffect : React.useLayoutEffect; + const Input = React.forwardRef< HTMLInputElement, - Omit ->(({ asChild, onChange, onKeyDown, ...props }, ref) => { - const { id, query, onQueryChange, onIndexChange, recentSearches, result } = - useContext(); - const Comp = asChild ? Slot : "input"; + Omit< + JSX.IntrinsicElements["input"] & { + asChild?: boolean; + disableSelectionPrefill?: boolean; + }, + "ref" + > +>( + ( + { asChild, onChange, onKeyDown, disableSelectionPrefill, ...props }, + ref + ) => { + const { id, query, onQueryChange, onIndexChange, recentSearches, result } = + useContext(); + const Comp = asChild ? Slot : "input"; - return ( - { - onChange?.(e as React.ChangeEvent); - if (e.target instanceof HTMLInputElement) { - onQueryChange(e.target.value); - } - }} - onKeyDown={(e) => { - onKeyDown?.(e as React.KeyboardEvent); - // handle arrow keys and enter - if (e.key === "ArrowDown") { - e.preventDefault(); - onIndexChange({ type: "incr", scrollIntoView: true }); - } else if (e.key === "ArrowUp") { - e.preventDefault(); - onIndexChange({ type: "decr", scrollIntoView: true }); - } else if (e.key === "Enter") { - e.preventDefault(); - const selectedNode = document.querySelector( - `[data-basehub-hit-for="${id}"], [data-selected="true"]` - ); - if (selectedNode) { - const href = selectedNode.getAttribute("href"); - if (href) { - if (e.metaKey) { - window.open(href, "_blank"); - } else { - window.location.href = href; - } - if (recentSearches) { - const hit = result?.hits.find( - (h) => h._key === selectedNode.dataset.basehubHitKey - ); - if (hit) { - recentSearches.add(hit); + const onQueryChangeRef = React.useRef(onQueryChange); + onQueryChangeRef.current = onQueryChange; + + const queryRef = React.useRef(query); + queryRef.current = query; + + useIsoLayoutEffect(() => { + if (!onQueryChangeRef.current) return; + const defaultQuery = disableSelectionPrefill + ? "" + : window.getSelection()?.toString() || ""; + + if (queryRef.current === defaultQuery) return; + + onQueryChangeRef.current(defaultQuery); + }, [disableSelectionPrefill]); + + return ( + { + onChange?.(e as React.ChangeEvent); + if (e.target instanceof HTMLInputElement) { + onQueryChange(e.target.value); + } + }} + onKeyDown={(e) => { + onKeyDown?.(e as React.KeyboardEvent); + // handle arrow keys and enter + if (e.key === "ArrowDown") { + e.preventDefault(); + onIndexChange({ type: "incr", scrollIntoView: true }); + } else if (e.key === "ArrowUp") { + e.preventDefault(); + onIndexChange({ type: "decr", scrollIntoView: true }); + } else if (e.key === "Enter") { + e.preventDefault(); + const selectedNode = document.querySelector( + `[data-basehub-hit-for="${id}"], [data-selected="true"]` + ); + if (selectedNode) { + const href = selectedNode.getAttribute("href"); + if (href) { + if (e.metaKey) { + window.open(href, "_blank"); + } else { + window.location.href = href; + } + if (recentSearches) { + const hit = result?.hits.find( + (h) => h._key === selectedNode.dataset.basehubHitKey + ); + if (hit) { + recentSearches.add(hit); + } } } } } - } - }} - ref={ref} - /> - ); -}); + }} + ref={ref} + /> + ); + } +); /* ------------------------------------------------------------------------------------------------- * Placeholder diff --git a/playground/src/app/search/index.tsx b/playground/src/app/search/index.tsx index a6935d5..476045d 100644 --- a/playground/src/app/search/index.tsx +++ b/playground/src/app/search/index.tsx @@ -4,10 +4,10 @@ import { SearchBox, useSearch } from "../../../.basehub/react-search"; import s from "./search.module.css"; export const Search = () => { - const [count, setCount] = useState(0); + const [open, setOpen] = useState(false); const search = useSearch<{ some: "thing" }>({ - _searchKey: "somet", + _searchKey: null, queryBy: ["_title", "content"], saveRecentSearches: { getStorage: () => window.localStorage, @@ -19,22 +19,54 @@ export const Search = () => { <>
- - - + {open && ( + + + + +
    +

    Recent Searches

    + {search.recentSearches?.hits?.map((hit) => { + return ( +
  • + + + + + + +
  • + ); + })} +
+
+
+ +
No results found for {search.query}
+
-
    -

    Recent Searches

    - {search.recentSearches?.hits?.map((hit) => { +
      + {search.result?.hits.map((hit) => { return (
    • { - -
    • ); })}
    - - -
    No results found for {search.query}
    -
    - -
      - {search.result?.hits.map((hit) => { - return ( -
    • - - - - -
    • - ); - })} -
    -
    - + + )} ); };