"use client"; import * as React from "react"; import { ChevronDown, Check } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, } from "@/components/ui/command"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { ScrollArea } from "@/components/ui/scroll-area"; interface Option { value: string; label: string; count?: number; } interface MultiSelectProps { options: Option[]; value: string[]; onValueChange: (value: string[]) => void; placeholder?: string; className?: string; maxSelection?: number; searchPlaceholder?: string; showAllOption?: boolean; allOptionLabel?: string; } export function MultiSelect({ options, value, onValueChange, placeholder = "Select items...", className, maxSelection, searchPlaceholder = "Search options...", showAllOption = true, allOptionLabel = "All", }: MultiSelectProps) { const [open, setOpen] = React.useState(false); const [searchValue, setSearchValue] = React.useState(""); // Normalize value to empty array if undefined/null to prevent crashes const safeValue = value ?? []; const isAllSelected = safeValue.includes("*"); const filteredOptions = options.filter((option) => option.label.toLowerCase().includes(searchValue.toLowerCase()), ); const handleSelect = (optionValue: string) => { if (optionValue === "*") { // Toggle "All" selection if (isAllSelected) { onValueChange([]); } else { onValueChange(["*"]); } } else { let newValue: string[]; if (safeValue.includes(optionValue)) { // Remove the item newValue = safeValue.filter((v) => v !== optionValue && v !== "*"); } else { // Add the item and remove "All" if present newValue = [...safeValue.filter((v) => v !== "*"), optionValue]; // Check max selection limit if (maxSelection && newValue.length > maxSelection) { return; } } onValueChange(newValue); } }; const getDisplayText = () => { if (isAllSelected) { return allOptionLabel; } if (safeValue.length === 0) { return placeholder; } // Extract the noun from placeholder (e.g., "Select data sources..." -> "data sources") const noun = placeholder .toLowerCase() .replace("select ", "") .replace("...", ""); return `${safeValue.length} ${noun}`; }; return ( No items found. {showAllOption && ( handleSelect("*")} className="cursor-pointer" > {allOptionLabel} )} {filteredOptions.map((option) => ( handleSelect(option.value)} className="cursor-pointer" > {option.label} {option.count !== undefined && ( {option.count} )} ))} ); }