/** * @file components/layout/SearchBar.tsx * @description Client component managing URL search parameters with debounced input and smooth transition state. */ "use client"; import { useState, useEffect, useTransition } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { Search, X } from "lucide-react"; /** * Properties for the SearchBar component. * * @interface SearchBarProps * @property {string} [placeholder] - Placeholder text displayed inside the search input. * @property {number} [debounceMs] - Delay in milliseconds before updating the URL search parameter. * @property {boolean} [autoFocus] - Flag indicating whether the input should automatically gain focus on mount. */ interface SearchBarProps { placeholder?: string; debounceMs?: number; autoFocus?: boolean; } /** * Renders a debounced search bar input that syncs its local state with the URL's "search" query parameter. * * @param {SearchBarProps} props - The component props. * @returns {JSX.Element} The rendered search bar component. */ export default function SearchBar({ placeholder = "Search...", debounceMs = 400, autoFocus = false, }: SearchBarProps) { const router = useRouter(); const searchParams = useSearchParams(); const searchQuery = searchParams.get("search") || ""; const [localValue, setLocalValue] = useState(searchQuery); const [isPending, startTransition] = useTransition(); // Debounced URL Update useEffect(() => { const timer = setTimeout(() => { if (localValue !== searchQuery) { const params = new URLSearchParams(searchParams.toString()); if (localValue.trim()) { params.set("search", localValue.trim()); } else { params.delete("search"); } startTransition(() => { router.push(`?${params.toString()}`, { scroll: false }); }); } }, debounceMs); return () => clearTimeout(timer); }, [localValue, searchQuery, searchParams, debounceMs, router]); /** * Resets the local search input value to an empty string. */ const handleClear = () => { setLocalValue(""); }; return (
setLocalValue(e.target.value)} autoFocus={autoFocus} className="bg-transparent focus:outline-none w-full placeholder:text-foreground-muted/60" /> {localValue && ( )}
); }