import { useEffect, useMemo, useRef, useState } from 'react'; import { AnimatePresence, motion } from 'motion/react'; import { Check, ChevronDown } from 'lucide-react'; export default function BatchMultiSelect({ options, selected, onChange, placeholder, }: { options: string[], selected: string[], onChange: (val: string[]) => void, placeholder: string }) { const rootRef = useRef(null); const [isOpen, setIsOpen] = useState(false); const [search, setSearch] = useState(''); const selectedSet = useMemo(() => new Set(selected), [selected]); const filtered = useMemo(() => { if (!search) return options; return options.filter(opt => opt.toLowerCase().includes(search.toLowerCase())); }, [options, search]); const label = selected.length === 0 ? placeholder : selected.length === options.length ? '全部批次' : selected.length === 1 ? selected[0] : `已选 ${selected.length} 个批次`; const toggle = (opt: string) => { if (selectedSet.has(opt)) { onChange(selected.filter(item => item !== opt)); } else { onChange([...selected, opt]); } }; useEffect(() => { if (!isOpen) return; const handlePointerDown = (event: PointerEvent) => { const target = event.target; if (target instanceof Node && !rootRef.current?.contains(target)) { setIsOpen(false); setSearch(''); } }; document.addEventListener('pointerdown', handlePointerDown); return () => document.removeEventListener('pointerdown', handlePointerDown); }, [isOpen]); return (
{isOpen && (
setSearch(e.target.value)} />
{filtered.map((opt: string) => { const checked = selectedSet.has(opt); return ( ); })} {filtered.length === 0 && (
无匹配项
)}
)}
); }