"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...", showAllOption = true, allOptionLabel = "All" }: MultiSelectProps) { const [open, setOpen] = React.useState(false) const [searchValue, setSearchValue] = React.useState("") const isAllSelected = value.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 (value.includes(optionValue)) { // Remove the item newValue = value.filter(v => v !== optionValue && v !== "*") } else { // Add the item and remove "All" if present newValue = [...value.filter(v => v !== "*"), optionValue] // Check max selection limit if (maxSelection && newValue.length > maxSelection) { return } } onValueChange(newValue) } } const getDisplayText = () => { if (isAllSelected) { return allOptionLabel } if (value.length === 0) { return placeholder } // Extract the noun from placeholder (e.g., "Select data sources..." -> "data sources") const noun = placeholder.toLowerCase().replace('select ', '').replace('...', '') return `${value.length} ${noun}` } return ( No items found. {showAllOption && ( handleSelect("*")} className="cursor-pointer" > {allOptionLabel} * )} {filteredOptions.map((option) => ( handleSelect(option.value)} className="cursor-pointer" disabled={isAllSelected} > {option.label} {option.count !== undefined && ( {option.count} )} ))} ) }