"use client"; import { RefreshCw, Search, X } from "lucide-react"; import { useTranslations } from "next-intl"; import { useCallback, useEffect, useRef, useState } from "react"; import { DataListRetriever, useDebounce } from "../../hooks"; import { Button, Input } from "../../shadcnui"; type ContentTableSearchProps = { data: DataListRetriever; }; export function ContentTableSearch({ data }: ContentTableSearchProps) { const t = useTranslations(); const searchTermRef = useRef(""); const inputRef = useRef(null); const [searchTerm, setSearchTerm] = useState(""); const [isFocused, setIsFocused] = useState(false); const [isSearching, setIsSearching] = useState(false); const isExpanded = isFocused || searchTerm.length > 0; const search = useCallback( async (searchedTerm: string) => { try { if (searchedTerm === searchTermRef.current) return; searchTermRef.current = searchedTerm; await data.search(searchedTerm); } finally { setIsSearching(false); } }, [searchTermRef, data], ); const updateSearchTerm = useDebounce(search, 500); useEffect(() => { setIsSearching(true); updateSearchTerm(searchTerm); }, [updateSearchTerm, searchTerm]); const handleSearchIconClick = () => { if (!isExpanded) { setIsFocused(true); // Small delay to ensure the input is rendered before focusing setTimeout(() => inputRef.current?.focus(), 50); } }; const handleBlur = () => { // Auto-collapse only if search is empty if (searchTerm.length === 0) { setIsFocused(false); } }; const handleClear = () => { setSearchTerm(""); search(""); setIsFocused(false); }; return (
{/* Collapsed, this glyph is the ONLY way to open search — as a bare it had no role, no tab stop and no name, so search was unreachable without a pointer on every list in the product. Expanded, it is purely decorative and must NOT be a tab stop. Labels reuse keys that exist in every consuming app; a new package-level key would throw MISSING_MESSAGE wherever it had not been added. */} {isExpanded ? ( ) : ( {t(`ui.search.button`)} )} {isExpanded && ( { if (e.key === "Escape") { handleClear(); } }} onFocus={() => setIsFocused(true)} onBlur={handleBlur} placeholder={t(`ui.search.placeholder_global`)} type="text" className="border-border/50 focus-visible:border-border h-10 w-full pr-8 pl-8 text-xs shadow-none focus-visible:ring-0" onChange={(e) => setSearchTerm(e.target.value)} value={searchTerm} /> )} {isExpanded && isSearching && ( )} {isExpanded && !isSearching && searchTermRef.current && ( )}
); }