// admin-spa/src/components/ui/searchable-select.tsx import * as React from "react"; import { Button } from "@/components/ui/button"; import { Popover, PopoverTrigger, PopoverContent, } from "@/components/ui/popover"; import { Command, CommandInput, CommandList, CommandItem, CommandEmpty, } from "@/components/ui/command"; import { Check, ChevronsUpDown } from "lucide-react"; import { cn } from "@/lib/utils"; export interface Option { value: string; /** What to render in the button/list. Can be a string or React node. */ label: React.ReactNode; /** Optional text used for filtering. Falls back to string label or value. */ triggerLabel?: React.ReactNode; } interface Props { value?: string; onChange?: (v: string) => void; options: Option[]; placeholder?: string; emptyLabel?: string; className?: string; disabled?: boolean; search?: string; onSearch?: (v: string) => void; showCheckIndicator?: boolean; } export function SearchableSelect({ value, onChange, options, placeholder = "Select...", emptyLabel = "No results found.", className, disabled = false, search, onSearch, showCheckIndicator = true, }: Props) { const [open, setOpen] = React.useState(false); const selected = options.find((o) => o.value === value); React.useEffect(() => { if (disabled && open) setOpen(false); }, [disabled, open]); return ( !disabled && setOpen(o)}> {emptyLabel} {options.map((opt) => ( 0 ? opt.searchText : (typeof opt.label === 'string' ? opt.label : opt.value) } onSelect={() => { onChange?.(opt.value); setOpen(false); }} > {showCheckIndicator && ( )} {opt.label} ))} ); }