SPB Git forge

spb/spb-cloud

Public
10commits 1branches 0releases
3.2 MBsize
maindefault branch
2 h agolast push
JavaScript 56.7% TypeScript 42.6%
2.5 KB · 80 lines tsx
Raw Blame History
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