import { useMemo, useRef, useState } from "react"; import { FixedSizeList as List } from "react-window"; import { Loader2, ChevronDownIcon } from "lucide-react"; interface Option { label: string; value: string; } interface VirtualSelectProps { options: Option[]; value: string; onChange: (value: string) => void; placeholder?: string; loading?: boolean; height?: number; itemHeight?: number; maxVisible?: number; } export function VirtualSelect({ options, value, onChange, placeholder = "Pilih opsi...", loading = false, itemHeight = 36, maxVisible = 6, }: Readonly) { const [open, setOpen] = useState(false); const [query, setQuery] = useState(""); const buttonRef = useRef(null); const selectedLabel = options.find((o) => o.value === value)?.label || ""; const filteredOptions = useMemo(() => { return options.filter((opt) => opt.label.toLowerCase().includes(query.toLowerCase()) ); }, [options, query]); const handleSelect = (val: string) => { onChange(val); setOpen(false); setQuery(""); // reset search on select }; return (
{/* Trigger */} {open && (
{/* Search Input */}
setQuery(e.target.value)} className="w-full rounded-md border border-input px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring" />
{/* Options */} {loading ? (
) : filteredOptions.length === 0 ? (
Tidak ada hasil
) : ( {({ index, style }) => { const opt = filteredOptions[index]; const isSelected = opt.value === value; return (
handleSelect(opt.value)} > {opt.label}
); }}
)}
)}
); }