import * as Ariakit from '@ariakit/react'; import { matchSorter } from 'match-sorter'; import { Search, ChevronDown } from 'lucide-react'; import { useMemo, useState, useRef, memo, useEffect } from 'react'; import { SelectRenderer } from '@ariakit/react-core/select/select-renderer'; import type { OptionWithIcon } from '~/common'; import './AnimatePopover.css'; import { cn } from '~/utils'; interface ControlComboboxProps { selectedValue: string; displayValue?: string; items: OptionWithIcon[]; setValue: (value: string) => void; ariaLabel: string; searchPlaceholder?: string; selectPlaceholder?: string; isCollapsed: boolean; SelectIcon?: React.ReactNode; containerClassName?: string; iconClassName?: string; showCarat?: boolean; className?: string; disabled?: boolean; iconSide?: 'left' | 'right'; selectId?: string; } const ROW_HEIGHT = 36; function ControlCombobox({ selectedValue, displayValue, items, setValue, ariaLabel, searchPlaceholder, selectPlaceholder, containerClassName, isCollapsed, SelectIcon, showCarat, className, disabled, iconClassName, iconSide = 'left', selectId, }: ControlComboboxProps) { const [searchValue, setSearchValue] = useState(''); const buttonRef = useRef(null); const [buttonWidth, setButtonWidth] = useState(null); const getItem = (option: OptionWithIcon) => ({ id: `item-${option.value}`, value: option.value as string | undefined, label: option.label, icon: option.icon, }); const combobox = Ariakit.useComboboxStore({ defaultItems: items.map(getItem), resetValueOnHide: true, value: searchValue, setValue: setSearchValue, }); const select = Ariakit.useSelectStore({ combobox, defaultItems: items.map(getItem), value: selectedValue, setValue, }); const matches = useMemo(() => { const filteredItems = matchSorter(items, searchValue, { keys: ['value', 'label'], baseSort: (a, b) => (a.index < b.index ? -1 : 1), }); return filteredItems.map(getItem); }, [searchValue, items]); useEffect(() => { if (buttonRef.current && !isCollapsed) { setButtonWidth(buttonRef.current.offsetWidth); } }, [isCollapsed]); const selectIconClassName = cn( 'flex h-5 w-5 items-center justify-center overflow-hidden rounded-full', iconClassName, ); const optionIconClassName = cn( 'mr-2 flex h-5 w-5 items-center justify-center overflow-hidden rounded-full', iconClassName, ); return (
{ariaLabel} {SelectIcon != null && iconSide === 'left' && (
{SelectIcon}
)} {!isCollapsed && ( <> {displayValue != null ? displayValue || selectPlaceholder : selectedValue || selectPlaceholder} {SelectIcon != null && iconSide === 'right' && (
{SelectIcon}
)} {showCarat && } )}
{({ value, icon, label, ...item }) => ( } > {icon != null && iconSide === 'left' && (
{icon}
)} {label} {icon != null && iconSide === 'right' && (
{icon}
)}
)}
); } export default memo(ControlCombobox);