import React, { useState, useRef, useEffect } from "react"; import { ChevronDown, X, Search, Check } from "lucide-react"; import { __ } from "../../lib/i18n"; import { Input } from "./input"; export interface MultiSelectOption { value: string | number; label: string; } export interface MultiSelectProps { value: (string | number)[]; onChange: (value: (string | number)[]) => void; options: MultiSelectOption[]; placeholder?: string; searchPlaceholder?: string; className?: string; error?: boolean; disabled?: boolean; } export const MultiSelect: React.FC = ({ value, onChange, options, placeholder = __("Select options...", "yatra"), searchPlaceholder = __("Search...", "yatra"), className = "", error = false, disabled = false, }) => { const [isOpen, setIsOpen] = useState(false); const [searchTerm, setSearchTerm] = useState(""); const containerRef = useRef(null); const searchInputRef = useRef(null); // Filter options based on search term const filteredOptions = options.filter((option) => option.label.toLowerCase().includes(searchTerm.toLowerCase()), ); // Get selected options const selectedOptions = options.filter((opt) => value.some((v) => String(v) === String(opt.value)), ); // Close dropdown when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( containerRef.current && !containerRef.current.contains(event.target as Node) ) { setIsOpen(false); setSearchTerm(""); } }; if (isOpen) { document.addEventListener("mousedown", handleClickOutside); setTimeout(() => { searchInputRef.current?.focus(); }, 100); } return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [isOpen]); const handleToggle = (optionValue: string | number) => { const isSelected = value.some((v) => String(v) === String(optionValue)); if (isSelected) { onChange(value.filter((v) => String(v) !== String(optionValue))); } else { onChange([...value, optionValue]); } }; const handleRemove = (optionValue: string | number, e: React.MouseEvent) => { e.stopPropagation(); onChange(value.filter((v) => String(v) !== String(optionValue))); }; const handleClearAll = (e: React.MouseEvent) => { e.stopPropagation(); onChange([]); }; return (
!disabled && setIsOpen(!isOpen)} className={`flex min-h-11 w-full rounded-md border-2 ${ error ? "border-red-500" : "border-gray-300 dark:border-gray-600" } bg-white dark:bg-gray-800 px-3 py-2 text-base ring-offset-white cursor-pointer focus-within:outline-none focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:ring-offset-gray-900 dark:focus-within:ring-blue-400 transition-colors ${ disabled ? "opacity-50 cursor-not-allowed" : "" }`} >
{selectedOptions.length > 0 ? ( selectedOptions.map((option) => ( {option.label} {!disabled && ( )} )) ) : ( {placeholder} )}
{value.length > 0 && !disabled && ( )}
{isOpen && (
{/* Search Input */}
setSearchTerm(e.target.value)} className="pl-8 h-9" onClick={(e) => e.stopPropagation()} />
{/* Options List */}
{filteredOptions.length === 0 ? (
{__("No options found", "yatra")}
) : ( filteredOptions.map((option) => { const isSelected = value.some( (v) => String(v) === String(option.value), ); return ( ); }) )}
{/* Selected count */} {value.length > 0 && (
{value.length}{" "} {value.length === 1 ? __("selected", "yatra") : __("selected", "yatra")}
)}
)}
); };