"use client"; import * as React from "react"; import { AnimatePresence, motion } from "framer-motion"; import { IconSearch, IconX } from "@tabler/icons-react"; import { cn } from "../lib/utils"; import { Spinner } from "./spinner"; /** * Search field with an icon, a clear button that appears with text, an * optional loading state, and debounced `onSearch`. Escape clears it. */ export function SearchInput({ value, onValueChange, onSearch, debounceMs = 250, placeholder = "Search", loading = false, className, autoFocus, }: { value: string; onValueChange: (value: string) => void; /** Called after typing pauses, and immediately on Enter. */ onSearch?: (value: string) => void; debounceMs?: number; placeholder?: string; loading?: boolean; className?: string; autoFocus?: boolean; }) { const timer = React.useRef | null>(null); const inputRef = React.useRef(null); React.useEffect(() => () => { if (timer.current) clearTimeout(timer.current); }, []); const schedule = (next: string) => { if (!onSearch) return; if (timer.current) clearTimeout(timer.current); timer.current = setTimeout(() => onSearch(next), debounceMs); }; // Immediate searches (Enter, Escape, Clear) cancel the pending one, so a // query typed just before clearing cannot fire afterwards. const searchNow = (next: string) => { if (timer.current) clearTimeout(timer.current); timer.current = null; onSearch?.(next); }; return (
); }