"use client"; import { SelectHTMLAttributes, forwardRef, useState, useRef, useEffect, ReactNode } from "react"; interface SelectOption { value: string; label: string; } interface SearchableSelectProps extends Omit, 'onChange'> { label?: string; error?: string; helperText?: string; leftIcon?: string | ReactNode; options: SelectOption[]; placeholder?: string; onChange?: (value: string) => void; value?: string; } const SearchableSelect = forwardRef( ( { label, error, helperText, leftIcon, options, placeholder, className = "", onChange, value, required, ...props }, ref ) => { const [isOpen, setIsOpen] = useState(false); const [searchTerm, setSearchTerm] = useState(""); const [selectedOption, setSelectedOption] = useState( options.find(opt => opt.value === value) || null ); const containerRef = useRef(null); const searchInputRef = useRef(null); const filteredOptions = options.filter(option => option.label.toLowerCase().includes(searchTerm.toLowerCase()) ); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(event.target as Node)) { setIsOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []); useEffect(() => { if (isOpen && searchInputRef.current) { searchInputRef.current.focus(); } }, [isOpen]); useEffect(() => { if (value) { const option = options.find(opt => opt.value === value); if (option) { setSelectedOption(option); } } }, [value, options]); const handleSelect = (option: SelectOption) => { setSelectedOption(option); setIsOpen(false); setSearchTerm(""); if (onChange) { onChange(option.value); } }; return (
{/* Hidden select for form compatibility */} {label && ( )}
{leftIcon && (
{typeof leftIcon === 'string' ? ( ) : (
{leftIcon}
)}
)} {/* Custom trigger */} {/* Dropdown */} {isOpen && (
{/* Search input */}
setSearchTerm(e.target.value)} placeholder="Buscar..." className="w-full pl-9 pr-3 py-2 text-[14px] border border-zinc-200 dark:border-zinc-700 rounded-md outline-none focus:border-brand-500 shadow-none bg-white dark:bg-zinc-900 text-zinc-900 dark:text-white placeholder:text-zinc-500 dark:placeholder:text-zinc-400" />
{/* Options list */}
{filteredOptions.length > 0 ? ( filteredOptions.map((option) => ( )) ) : (
Nenhum resultado encontrado
)}
)}
{helperText && !error && (

{helperText}

)} {error && (

{error}

)}
); } ); SearchableSelect.displayName = "SearchableSelect"; export default SearchableSelect;