'use client' import { useEffect, useRef, useState, useTransition } from 'react' import { useRouter } from 'next/navigation' import { searchText } from '@/lib/i18n/shell' import { toSearchParams, type SearchFilters } from '@/lib/search/query' const DEBOUNCE_MS = 250 /** * Das Suchfeld. * * Tippen schreibt in die Adresse, mit 250 ms Verzögerung — dadurch ist jeder * Zustand teilbar und der Zurück-Knopf tut das Richtige. Während die neue * Seite lädt, bleiben die alten Treffer stehen und werden gedimmt, statt zu * verschwinden. Siehe docs/09-performance-und-ux.md. * * Einen `AbortController` braucht es hier nicht: der Router verwirft eine * überholte Navigation von selbst, und `useTransition` sagt uns, ob noch eine * läuft. */ export function SearchBox({ filters }: { filters: SearchFilters }) { const router = useRouter() const [text, setText] = useState(filters.q) const [pending, startTransition] = useTransition() const timer = useRef | null>(null) // Kommt der Begriff von aussen (Zurück-Knopf, gespeicherte Ansicht), muss // das Feld mitgehen. useEffect(() => { setText(filters.q) }, [filters.q]) function suchen(wert: string) { setText(wert) if (timer.current) clearTimeout(timer.current) timer.current = setTimeout(() => { startTransition(() => { router.replace(`/suche?${toSearchParams({ ...filters, q: wert, cursor: undefined })}`) }) }, DEBOUNCE_MS) } return (
suchen(event.target.value)} placeholder={searchText.placeholder} aria-label={searchText.placeholder} autoFocus className="w-full max-w-xl border border-line bg-raised px-3 py-2 text-sm" />
) }