import React, { useState, useRef, useEffect, useMemo } from 'react'; import { SearchIcon } from './Icons'; interface MultiSelectDropdownProps { label: string; selected: string[]; options: string[]; onChange: (newSelected: string[]) => void; className?: string; } const MultiSelectDropdown: React.FC = ({ label, selected, options, onChange, className }) => { const [isOpen, setIsOpen] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const dropdownRef = useRef(null); const inputRef = useRef(null); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { setIsOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); // Focus input when opening useEffect(() => { if (isOpen && inputRef.current) { inputRef.current.focus(); } if (!isOpen) { setSearchTerm(''); // Reset search when closing } }, [isOpen]); const filteredOptions = useMemo(() => { if (!searchTerm) return options; return options.filter(opt => opt.toLowerCase().includes(searchTerm.toLowerCase())); }, [options, searchTerm]); const toggleOption = (option: string) => { if (selected.includes(option)) { onChange(selected.filter((item) => item !== option)); } else { onChange([...selected, option]); } }; const handleSelectAll = () => { // If searching, only select/deselect visible options if (searchTerm) { const allFilteredSelected = filteredOptions.every(opt => selected.includes(opt)); if (allFilteredSelected) { // Deselect all filtered options onChange(selected.filter(item => !filteredOptions.includes(item))); } else { // Select all filtered options (add unique ones) const newSelected = Array.from(new Set([...selected, ...filteredOptions])); onChange(newSelected); } } else { // Standard behavior if (selected.length === options.length) { onChange([]); // Deselect all } else { onChange([...options]); // Select all } } }; const handleClear = () => { onChange([]); }; return (
{isOpen && (
{/* Search Bar */}
setSearchTerm(e.target.value)} />
{filteredOptions.map((opt) => ( ))} {filteredOptions.length === 0 &&
No matches found
}
)}
); }; export default MultiSelectDropdown;