import React, { useState, useRef, useEffect, useMemo } from "react"; import { ChevronDown, Search, X } from "lucide-react"; export interface SearchableSelectOption { value: string; label: string; disabled?: boolean; className?: string; // For custom styling like red text style?: React.CSSProperties; // For custom inline styles } interface SearchableSelectProps { label?: string; placeholder?: string; options: SearchableSelectOption[]; value: string; onChange: (value: string) => void; disabled?: boolean; className?: string; error?: string; required?: boolean; } export const SearchableSelect: React.FC = ({ label, placeholder = "Selecione...", options, value, onChange, disabled = false, className = "", error, required = false, }) => { const [isOpen, setIsOpen] = useState(false); const [searchTerm, setSearchTerm] = useState(""); const containerRef = useRef(null); const searchInputRef = useRef(null); // Close when clicking outside 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); }, []); // Focus search input when opening useEffect(() => { if (isOpen && searchInputRef.current) { setTimeout(() => { searchInputRef.current?.focus(); }, 50); } }, [isOpen]); // Handle Selection const handleSelect = (optionValue: string) => { onChange(optionValue); setIsOpen(false); setSearchTerm(""); }; const selectedOption = options.find((opt) => opt.value === value); // Filter options const filteredOptions = useMemo(() => { if (!searchTerm) return options; const lowerTerm = searchTerm.toLowerCase(); return options.filter((opt) => opt.label.toLowerCase().includes(lowerTerm)); }, [options, searchTerm]); return (
{label && ( )}
{/* Trigger Button */} {/* Dropdown Menu */} {isOpen && (
{/* Search Input */}
setSearchTerm(e.target.value)} /> {searchTerm && ( )}
{/* Options List */}
{filteredOptions.length > 0 ? ( filteredOptions.map((opt) => ( )) ) : (
Nenhum resultado encontrado
)}
)}
{error && {error}}
); };