import * as React from 'react' import { Check, ChevronsUpDown } from 'lucide-react' import { useTranslation } from 'react-i18next' import { cn } from '@/lib/utils' import { Button } from '@/components/ui/button' import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from '@/components/ui/command' import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover' export type ComboboxOption = { value: string label: string icon?: React.ReactNode } interface ComboboxProps { options: ComboboxOption[] value?: string onValueChange: (value: string) => void placeholder?: string searchPlaceholder?: string emptyText?: string className?: string allowCustomValue?: boolean } export function Combobox({ options, value, onValueChange, placeholder = 'Select option...', searchPlaceholder = 'Search...', emptyText = 'No option found.', className, allowCustomValue = false, }: ComboboxProps) { const { t } = useTranslation() const [open, setOpen] = React.useState(false) const [searchValue, setSearchValue] = React.useState('') const selectedOption = options.find((option) => option.value === value) const displayValue = selectedOption?.label || value || placeholder const filteredOptions = React.useMemo(() => { if (!searchValue) return options const search = searchValue.toLowerCase() return options.filter( (option) => option.label.toLowerCase().includes(search) || option.value.toLowerCase().includes(search) ) }, [options, searchValue]) const handleSelect = (selectedValue: string) => { onValueChange(selectedValue === value ? '' : selectedValue) setOpen(false) setSearchValue('') } const handleKeyDown = (e: React.KeyboardEvent) => { if (allowCustomValue && e.key === 'Enter' && searchValue) { e.preventDefault() // Check if search value matches any existing option const exactMatch = options.find( (opt) => opt.value.toLowerCase() === searchValue.toLowerCase() ) if (exactMatch) { handleSelect(exactMatch.value) } else { // Use custom value onValueChange(searchValue) setOpen(false) setSearchValue('') } } } return ( e.stopPropagation()} onTouchMove={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()} > {emptyText} {allowCustomValue && searchValue && (
{t('Press Enter to use "{{value}}"', { value: searchValue, })}
)}
{filteredOptions.map((option) => ( {option.icon && {option.icon}} {option.label} ))}
) }