// components/simple-multi-select.tsx import React, { useState, useRef, useEffect } from 'react'; import { X } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; type Option = { value: string; label: string; }; type MultiSelectProps = { options: Option[]; selected: string[]; onChange: (selected: string[]) => void; placeholder?: string; className?: string; }; export function SimpleMultiSelect({ options, selected, onChange, placeholder = "Select options", className, }: MultiSelectProps) { const [isOpen, setIsOpen] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const containerRef = useRef(null); // Handle click outside to close useEffect(() => { function handleClickOutside(event: MouseEvent) { if (containerRef.current && !containerRef.current.contains(event.target as Node)) { setIsOpen(false); setSearchTerm(''); } } document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const handleSelect = (value: string) => { if (!selected.includes(value)) { onChange([...selected, value]); } setSearchTerm(''); }; const handleRemove = (value: string) => { onChange(selected.filter(item => item !== value)); }; const filteredOptions = options.filter(option => !selected.includes(option.value) && option.label.toLowerCase().includes(searchTerm.toLowerCase()) ); return (
{/* Input trigger */}
setIsOpen(true)} > {selected.map(value => { const option = options.find(o => o.value === value); return ( {option?.label || value} ); })} setSearchTerm(e.target.value)} onFocus={() => setIsOpen(true)} placeholder={selected.length === 0 ? placeholder : ''} className="flex-1 outline-none bg-transparent min-w-[50px] text-sm" />
{/* Inline options list — expands modal height, no overflow clipping */} {isOpen && filteredOptions.length > 0 && (
{filteredOptions.map(option => (
{ e.preventDefault(); handleSelect(option.value); }} > {option.label}
))}
)}
); }