"use client"; import * as React from "react"; import * as Popover from "@radix-ui/react-popover"; import { Check, ChevronDown, Globe, Search } from "lucide-react"; import { cn } from "@/lib/utils"; import type { CountryOption } from "./types"; /** * Searchable country combobox (Radix Popover + listbox). Value "" means "Any country". * Keyboard: type to filter, ArrowUp/Down to move, Enter to select, Escape to close. */ export function CountrySelect({ value, onChange, countries, id, disabled }: { value: string; onChange: (code: string) => void; countries: CountryOption[]; id?: string; disabled?: boolean }) { const [open, setOpen] = React.useState(false); const [query, setQuery] = React.useState(""); const [active, setActive] = React.useState(0); const listRef = React.useRef(null); const listboxId = React.useId(); const handleOpenChange = (next: boolean) => { setOpen(next); if (!next) { setQuery(""); setActive(0); } }; const options = React.useMemo(() => { const q = query.trim().toLowerCase(); const all: CountryOption[] = [{ code: "", name: "Any country" }, ...countries]; if (!q) return all; return all.filter((c) => c.code === "" || c.name.toLowerCase().includes(q) || c.code.toLowerCase() === q || c.code.toLowerCase().startsWith(q)); }, [countries, query]); React.useEffect(() => { const node = listRef.current?.children[active] as HTMLElement | undefined; node?.scrollIntoView({ block: "nearest" }); }, [active]); const selected = countries.find((c) => c.code === value); const select = (code: string) => { onChange(code); setOpen(false); }; return ( e.preventDefault()}>
{ setQuery(e.target.value); setActive(0); }} placeholder="Search countries…" aria-label="Search countries" aria-controls={listboxId} aria-activedescendant={options[active] ? `country-opt-${options[active].code || "any"}` : undefined} className="h-9 w-full bg-transparent text-sm outline-none placeholder:text-fg-subtle" onKeyDown={(e) => { if (e.key === "ArrowDown") { e.preventDefault(); setActive((a) => Math.min(a + 1, options.length - 1)); } else if (e.key === "ArrowUp") { e.preventDefault(); setActive((a) => Math.max(a - 1, 0)); } else if (e.key === "Enter") { e.preventDefault(); const o = options[active]; if (o) select(o.code); } }} />
    {options.length === 0 ?
  • No match.
  • : null} {options.map((c, i) => { const isSel = c.code === value; return (
  • setActive(i)} onMouseDown={(e) => e.preventDefault()} onClick={() => select(c.code)} className={cn("relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-2 pr-8 text-sm", i === active && "bg-bg-muted")} > {c.code || "—"} {c.name} {isSel ? : null}
  • ); })}
); }