import * as Ariakit from '@ariakit/react'; import { matchSorter } from 'match-sorter'; import { startTransition, useMemo, useState, useEffect, useRef } from 'react'; import { cn } from '~/utils'; import type { OptionWithIcon } from '~/common'; import { Search } from 'lucide-react'; interface ControlComboboxProps { selectedValue: string; displayValue?: string; items: OptionWithIcon[]; setValue: (value: string) => void; ariaLabel: string; searchPlaceholder?: string; selectPlaceholder?: string; isCollapsed: boolean; SelectIcon?: React.ReactNode; } export default function ControlCombobox({ selectedValue, displayValue, items, setValue, ariaLabel, searchPlaceholder, selectPlaceholder, isCollapsed, SelectIcon, }: ControlComboboxProps) { const [searchValue, setSearchValue] = useState(''); const buttonRef = useRef(null); const [buttonWidth, setButtonWidth] = useState(null); const matches = useMemo(() => { return matchSorter(items, searchValue, { keys: ['value', 'label'], baseSort: (a, b) => (a.index < b.index ? -1 : 1), }); }, [searchValue, items]); useEffect(() => { if (buttonRef.current && !isCollapsed) { setButtonWidth(buttonRef.current.offsetWidth); } }, [isCollapsed]); return (
{ startTransition(() => { setSearchValue(value); }); }} > {ariaLabel} {SelectIcon != null && (
{SelectIcon}
)} {!isCollapsed && ( {displayValue ?? selectPlaceholder} )}
{matches.map((item) => ( } > {item.icon != null && (
{item.icon}
)} {item.label}
))}
); }