import { type ReactElement, useEffect, useState } from 'react' import Fuse from 'fuse.js' import { useStore } from '@nanostores/react' import { isSearchOpen } from '@stores/search' import SearchResults from './Results' import styles from './Search.module.css' import type { CollectionEntry } from 'astro:content' import Input from '@components/Input' export type Post = CollectionEntry<'articles' | 'links' | 'photos'> // Configure fuse.js // https://fusejs.io/api/options.html const fuseOptions = { keys: ['data.title', 'data.lead', 'slug'], includeMatches: true, minMatchCharLength: 2, threshold: 0.5 } export default function Search(): ReactElement { const $isSearchOpen = useStore(isSearchOpen) const [query, setQuery] = useState('') const [results, setResults] = useState() const [allPosts, setAllPosts] = useState() // fetch all post data on open useEffect(() => { if (!$isSearchOpen) return fetch('/api/posts') .then((res) => res.json()) .then((json) => setAllPosts(json)) }, [$isSearchOpen]) // Handle search and set results const fuse = allPosts ? new Fuse(allPosts, fuseOptions) : null useEffect(() => { if (!query || query === '' || !fuse) { setResults([]) return } const results = fuse .search(query) .map((result) => result.item) .slice(0, 6) setResults(results) }, [query]) // animate closing of search async function toggleSearch(): Promise { isSearchOpen.set(!$isSearchOpen) } return $isSearchOpen ? ( <>
setQuery(e.target.value)} />
) : ( <> ) }