import Fuse from "fuse.js"; import { useEffect, useRef, useState, useMemo } from "react"; import Card from "@components/Card"; import slugify from "@utils/slugify"; import type { CollectionEntry } from "astro:content"; export type SearchItem = { title: string; description: string; data: CollectionEntry<"blog">["data"]; }; interface Props { searchList: SearchItem[]; } interface SearchResult { item: SearchItem; refIndex: number; } export default function SearchBar({ searchList }: Props) { const inputRef = useRef(null); const [inputVal, setInputVal] = useState(""); const [searchResults, setSearchResults] = useState( null ); const handleChange = (e: React.FormEvent) => { setInputVal(e.currentTarget.value); }; const fuse = useMemo( () => new Fuse(searchList, { keys: ["title", "description"], includeMatches: true, minMatchCharLength: 2, threshold: 0.5, }), [searchList] ); useEffect(() => { // if URL has search query, // insert that search query in input field const searchUrl = new URLSearchParams(window.location.search); const searchStr = searchUrl.get("q"); if (searchStr) setInputVal(searchStr); // put focus cursor at the end of the string setTimeout(function () { inputRef.current!.selectionStart = inputRef.current!.selectionEnd = searchStr?.length || 0; }, 50); }, []); useEffect(() => { // Add search result only if // input value is more than one character let inputResult = inputVal.length > 1 ? fuse.search(inputVal) : []; setSearchResults(inputResult); // Update search string in URL if (inputVal.length > 0) { const searchParams = new URLSearchParams(window.location.search); searchParams.set("q", inputVal); const newRelativePathQuery = window.location.pathname + "?" + searchParams.toString(); history.replaceState(history.state, "", newRelativePathQuery); } else { history.replaceState(history.state, "", window.location.pathname); } }, [inputVal]); return ( <> {inputVal.length > 1 && (
Found {searchResults?.length} {searchResults?.length && searchResults?.length === 1 ? " result" : " results"}{" "} for '{inputVal}'
)}
    {searchResults && searchResults.map(({ item, refIndex }) => ( ))}
); }