"use client"; import * as React from "react"; import { Check, ChevronsUpDown } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { Input } from "@/components/ui/input"; interface ComboboxProps { options: { id: string; name: string }[]; value: string; onValueChange: (value: string) => void; placeholder?: string; searchPlaceholder?: string; emptyMessage?: string; loading?: boolean; searchValue?: string; onSearchValueChange?: (value: string) => void; disableLocalFilter?: boolean; } export function Combobox({ options, value, onValueChange, placeholder = "Select option...", searchPlaceholder = "Search...", emptyMessage = "No results found.", loading = false, searchValue, onSearchValueChange, disableLocalFilter = false, }: ComboboxProps) { const [open, setOpen] = React.useState(false); const [internalSearch, setInternalSearch] = React.useState(""); const search = searchValue ?? internalSearch; const filteredOptions = React.useMemo(() => { if (disableLocalFilter) return options; if (!search) return options; const lower = search.toLowerCase(); return options.filter( (option) => option.name.toLowerCase().includes(lower) || option.id.toLowerCase().includes(lower) ); }, [disableLocalFilter, options, search]); const selectedOption = options.find((opt) => opt.id === value); const selectedLabel = selectedOption?.name ?? (value ? value : placeholder); return (
{ const next = e.target.value; if (onSearchValueChange) { onSearchValueChange(next); } else { setInternalSearch(next); } }} className="h-9" />
{loading ? (
Loading...
) : filteredOptions.length === 0 ? (
{emptyMessage}
) : (
{filteredOptions.map((option) => ( ))}
)}
); }