JavaScript 56.7%
TypeScript 42.6%
1"use client";23import { Search, X } from "lucide-react";4import { useState, useCallback, useEffect, useRef } from "react";5import Kbd from "./Kbd";67interface SearchBarProps {8 onSearch: (query: string) => void;9 placeholder?: string;10 /** Délai de debounce en ms (0 = immédiat) */11 debounce?: number;12 autoFocus?: boolean;13 className?: string;14}1516export default function SearchBar({17 onSearch,18 placeholder = "Rechercher…",19 debounce = 200,20 autoFocus,21 className = "",22}: SearchBarProps) {23 const [query, setQuery] = useState("");24 const [focused, setFocused] = useState(false);25 const timer = useRef<ReturnType<typeof setTimeout> | null>(null);2627 const emit = useCallback(28 (value: string) => {29 if (timer.current) clearTimeout(timer.current);30 if (debounce <= 0) { onSearch(value); return; }31 timer.current = setTimeout(() => onSearch(value), debounce);32 },33 [onSearch, debounce]34 );3536 useEffect(() => () => { if (timer.current) clearTimeout(timer.current); }, []);3738 const handleChange = (value: string) => {39 setQuery(value);40 emit(value);41 };4243 const handleClear = () => {44 setQuery("");45 if (timer.current) clearTimeout(timer.current);46 onSearch("");47 };4849 return (50 <div className={`relative ${className}`}>51 <Search className={`absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 pointer-events-none transition-colors ${focused ? "text-blue-500" : "text-gray-400 dark:text-gray-500"}`} />52 <input53 type="search"54 value={query}55 onChange={(e) => handleChange(e.target.value)}56 onFocus={() => setFocused(true)}57 onBlur={() => setFocused(false)}58 onKeyDown={(e) => { if (e.key === "Escape" && query) { e.preventDefault(); handleClear(); } }}59 placeholder={placeholder}60 autoFocus={autoFocus}61 aria-label={placeholder}62 className="field pl-10 pr-16 h-10 [&::-webkit-search-cancel-button]:hidden"63 />64 <div className="absolute right-1.5 top-1/2 -translate-y-1/2 flex items-center gap-1">65 {query ? (66 <button67 onClick={handleClear}68 className="text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-200 w-8 h-8 flex items-center justify-center rounded-lg hover:bg-gray-200/70 dark:hover:bg-white/10 transition-colors"69 aria-label="Effacer la recherche"70 >71 <X className="w-4 h-4" />72 </button>73 ) : (74 <span className="hidden md:inline-flex pointer-events-none pr-1"><Kbd>/</Kbd></span>75 )}76 </div>77 </div>78 );79}80