import { Check, ChevronDown } from 'lucide-react'; import { useRef } from 'react'; import { AriaComboBoxProps, AriaListBoxOptions, useButton, useComboBox, useFilter, useId, useListBox, useOption, } from 'react-aria'; import { Item, ListState, Node, useComboBoxState } from 'react-stately'; import Popover from '~/components/Popover'; import cn from '~/utils/cn'; export interface SelectProps extends AriaComboBoxProps { className?: string; } function Select(props: SelectProps) { const { contains } = useFilter({ sensitivity: 'base' }); const state = useComboBoxState({ ...props, defaultFilter: contains }); const id = useId(props.id); const buttonRef = useRef(null); const inputRef = useRef(null); const listBoxRef = useRef(null); const popoverRef = useRef(null); const { buttonProps: triggerProps, inputProps, listBoxProps, labelProps, descriptionProps, } = useComboBox( { ...props, inputRef, buttonRef, listBoxRef, popoverRef, }, state, ); const { buttonProps } = useButton(triggerProps, buttonRef); return (
{props.description && (
{props.description}
)} {state.isOpen && ( )}
); } interface ListBoxProps extends AriaListBoxOptions { listBoxRef?: React.RefObject; state: ListState; } function ListBox(props: ListBoxProps) { const { listBoxRef, state } = props; const ref = listBoxRef ?? useRef(null); const { listBoxProps } = useListBox(props, state, ref); return (
    {[...state.collection].map((item) => (
); } interface OptionProps { item: Node; state: ListState; } function Option({ item, state }: OptionProps) { const ref = useRef(null); const { optionProps, isDisabled, isSelected, isFocused } = useOption( { key: item.key, }, state, ref, ); return (
  • {item.rendered} {isSelected && }
  • ); } export default Object.assign(Select, { Item });