import * as React from 'react' import { cva, type VariantProps } from 'class-variance-authority' import { CheckIcon, XCircle, ChevronDown, XIcon, WandSparkles } from 'lucide-react' import { cn } from '@/lib/utils' import { Separator } from '@/components/ui/separator' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from '@/components/ui/command' const multiSelectVariants = cva( 'm-1 transition ease-in-out delay-150 hover:-translate-y-1 hover:scale-110 duration-300', { variants: { variant: { default: 'border-foreground/10 text-foreground bg-card hover:bg-card/80', secondary: 'border-foreground/10 bg-secondary text-secondary-foreground hover:bg-secondary/80', destructive: 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80', inverted: 'inverted', }, }, defaultVariants: { variant: 'default', }, } ) interface MultiSelectProps extends React.ButtonHTMLAttributes, VariantProps { options: { label: string value: string icon?: React.ComponentType<{ className?: string }> }[] onValueChange: (value: string[]) => void defaultValue: string[] placeholder?: string animation?: number maxCount?: number asChild?: boolean className?: string } export const MultiSelect = React.forwardRef( ( { options, onValueChange, variant, defaultValue = [], placeholder = 'Select options', animation = 0, maxCount = 3, asChild = false, className, ...props }, ref ) => { const [selectedValues, setSelectedValues] = React.useState(defaultValue) const [isPopoverOpen, setIsPopoverOpen] = React.useState(false) const [isAnimating, setIsAnimating] = React.useState(false) React.useEffect(() => { if (JSON.stringify(selectedValues) !== JSON.stringify(defaultValue)) { setSelectedValues(defaultValue) } }, [defaultValue, selectedValues]) const handleInputKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { setIsPopoverOpen(true) } else if (event.key === 'Backspace' && !event.currentTarget.value) { const newSelectedValues = [...selectedValues] newSelectedValues.pop() setSelectedValues(newSelectedValues) onValueChange(newSelectedValues) } } const toggleOption = (value: string) => { const newSelectedValues = selectedValues.includes(value) ? selectedValues.filter((v) => v !== value) : [...selectedValues, value] setSelectedValues(newSelectedValues) onValueChange(newSelectedValues) } const handleClear = () => { setSelectedValues([]) onValueChange([]) } const handleTogglePopover = () => { setIsPopoverOpen((prev) => !prev) } const clearExtraOptions = () => { const newSelectedValues = selectedValues.slice(0, maxCount) setSelectedValues(newSelectedValues) onValueChange(newSelectedValues) } const toggleAll = () => { if (selectedValues.length === options.length) { handleClear() } else { const allValues = options.map((option) => option.value) setSelectedValues(allValues) onValueChange(allValues) } } return ( setIsPopoverOpen(false)}> No results found.
(Select All)
{options.map((option) => { const isSelected = selectedValues.includes(option.value) return ( toggleOption(option.value)} className="cursor-pointer" >
{option.icon && } {option.label}
) })}
{selectedValues.length > 0 && ( <> Clear )} setIsPopoverOpen(false)} className="flex-1 justify-center cursor-pointer" > Close
{animation > 0 && selectedValues.length > 0 && ( setIsAnimating(!isAnimating)} /> )}
) } ) MultiSelect.displayName = 'MultiSelect'