diff --git a/src/lib/api/discovery.ts b/src/lib/api/discovery.ts index 1675184..9ccd76c 100644 --- a/src/lib/api/discovery.ts +++ b/src/lib/api/discovery.ts @@ -18,9 +18,10 @@ export async function getRelatedTracks(id: number) { }) } -export async function getSearchSuggestions(query: string) { +export async function getSearchSuggestions(query: string, signal?: AbortSignal) { return await $api('/search/queries', { searchParams: { q: query }, + signal, schema: Collection( v.object({ output: v.string(), diff --git a/src/lib/components/SearchBar.tsx b/src/lib/components/SearchBar.tsx index abca941..1466d8e 100644 --- a/src/lib/components/SearchBar.tsx +++ b/src/lib/components/SearchBar.tsx @@ -1,34 +1,111 @@ +import { getSearchSuggestions } from '$lib/api/discovery' +import { Autocomplete } from '@base-ui/react/autocomplete' +import { useQuery } from '@tanstack/react-query' import { useNavigate } from '@tanstack/react-router' -import { SearchIcon } from 'lucide-react' -import { useState } from 'react' -import { Input } from './ui/Input' +import { SearchIcon, XIcon } from 'lucide-react' +import { useEffect, useState } from 'react' export function SearchBar({ value = '' }: { value?: string }) { const navigate = useNavigate() const [query, setQuery] = useState(value) + const [debouncedQuery, setDebouncedQuery] = useState(value.trim()) + + useEffect(() => { + const timeout = setTimeout(() => setDebouncedQuery(query.trim()), 250) + return () => clearTimeout(timeout) + }, [query]) + + const suggestionsQuery = useQuery({ + queryKey: ['search-suggestions', debouncedQuery], + queryFn: ({ signal }) => getSearchSuggestions(debouncedQuery, signal), + enabled: Boolean(debouncedQuery), + }) + + const isWaiting = Boolean(query.trim()) && query.trim() !== debouncedQuery + const suggestions = isWaiting + ? [] + : suggestionsQuery.data?.collection ?? [] + + function search(nextQuery: string) { + navigate({ + to: '/search', + search: { q: nextQuery }, + }) + } return (
) }