"use client" import * as React from "react" import { X } from "lucide-react" import { Badge } from "@/components/ui/badge" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command" import { cn } from "@/lib/utils" export interface Option { label: string value: string } interface MultiSelectProps { options: Option[] selected: string[] onChange: (selected: string[]) => void placeholder?: string className?: string disabled?: boolean } function MultiSelect({ options, selected, onChange, placeholder = "Select items...", className, disabled = false, }: MultiSelectProps) { const [open, setOpen] = React.useState(false) const [inputValue, setInputValue] = React.useState("") const handleUnselect = (item: string) => { onChange(selected.filter((i) => i !== item)) } const handleKeyDown = (e: React.KeyboardEvent) => { const input = e.target as HTMLInputElement if (input.value === "") { if (e.key === "Backspace") { onChange(selected.slice(0, -1)) } } } const selectables = options.filter((option) => !selected.includes(option.value)) // Handle creating new option when user types something not in the list const handleSelect = (value: string) => { if (value === inputValue && !options.find(option => option.value === value)) { // Create new option onChange([...selected, value]) } else { onChange([...selected, value]) } setInputValue("") } return (
{selected.map((item) => { const option = options.find((opt) => opt.value === item) return ( {option?.label || item} ) })} setOpen(false)} onFocus={() => setOpen(true)} placeholder={placeholder} disabled={disabled} className="ml-2 flex-1 bg-transparent outline-none placeholder:text-muted-foreground" />
{open && (inputValue.length > 0 || selectables.length > 0) ? (
{/* Show option to create new category if input doesn't match existing options */} {inputValue.length > 0 && !options.find(option => option.label.toLowerCase().includes(inputValue.toLowerCase()) || option.value.toLowerCase().includes(inputValue.toLowerCase()) ) && ( { e.preventDefault() e.stopPropagation() }} onSelect={() => { handleSelect(inputValue) setOpen(false) }} className="cursor-pointer min-h-[44px] md:min-h-0 py-2.5 md:py-1.5 text-base md:text-sm" > Create "{inputValue}" )} {/* Show existing options that match the search */} {selectables .filter(option => option.label.toLowerCase().includes(inputValue.toLowerCase()) || option.value.toLowerCase().includes(inputValue.toLowerCase()) ) .map((option) => ( { e.preventDefault() e.stopPropagation() }} onSelect={() => { handleSelect(option.value) setOpen(false) }} className="cursor-pointer min-h-[44px] md:min-h-0 py-2.5 md:py-1.5 text-base md:text-sm" > {option.label} ))} {selectables.length === 0 && inputValue.length === 0 && ( No more options available. )}
) : null}
) } export { MultiSelect }