import React, { useState, useRef, useEffect, forwardRef } from 'react'; import { Search, Check } from 'lucide-react'; interface Option { value: string | number; label: string; description?: string; metadata?: Record; } interface SearchableSelectProps { options: Option[]; value: Option['value'] | null; onChange: (value: Option['value']) => void; placeholder?: string; renderOption?: (option: Option) => React.ReactNode; } export const SearchableSelect = forwardRef( ({ options, value, onChange, placeholder = 'Search...', renderOption }, ref) => { const [isOpen, setIsOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const containerRef = useRef(null); const inputRef = useRef(null); const selectedOption = options.find(opt => opt.value === value); const filteredOptions = options.filter(option => option.label.toLowerCase().includes(searchQuery.toLowerCase()) || option.description?.toLowerCase().includes(searchQuery.toLowerCase()) ); useEffect(() => { function 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 && inputRef.current) { inputRef.current.focus(); } }, [isOpen]); return (
{isOpen && (
setSearchQuery(e.target.value)} onClick={(e) => e.stopPropagation()} />
{filteredOptions.length === 0 ? (
No results found
) : (
    {filteredOptions.map((option) => (
  • ))}
)}
)}
); } ); SearchableSelect.displayName = 'SearchableSelect';