import { type FocusEventHandler, type PointerEventHandler, type RefAttributes, type RefObject, useCallback, useContext, useRef, useState } from "react"; import { faMagnifyingGlass } from "@fortawesome/pro-duotone-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { ComboBox as AriaComboBox, type ComboBoxProps as AriaComboBoxProps, Group as AriaGroup, type GroupProps as AriaGroupProps, Input as AriaInput, ListBox as AriaListBox, type ListBoxProps as AriaListBoxProps, ComboBoxStateContext, } from "react-aria-components"; import { HintText } from "@/components/base/input/hint-text"; import { Label } from "@/components/base/input/label"; import { Popover } from "@/components/base/select/popover"; import { type CommonProps, SelectContext, type SelectItemType, sizes } from "@/components/base/select/select"; import { useResizeObserver } from "@/hooks/use-resize-observer"; import { cx } from "@/utils/cx"; interface ComboBoxProps extends Omit, "children" | "items">, RefAttributes, CommonProps { shortcut?: boolean; items?: SelectItemType[]; popoverClassName?: string; shortcutClassName?: string; children: AriaListBoxProps["children"]; } interface ComboBoxValueProps extends AriaGroupProps { size: "sm" | "md"; shortcut: boolean; placeholder?: string; shortcutClassName?: string; onFocus?: FocusEventHandler; onPointerEnter?: PointerEventHandler; ref?: RefObject; } const ComboBoxValue = ({ size, shortcut, placeholder, shortcutClassName, ...otherProps }: ComboBoxValueProps) => { const state = useContext(ComboBoxStateContext); const value = state?.selectedItem?.value || null; const inputValue = state?.inputValue || null; const first = inputValue?.split(value?.supportingText)?.[0] || ""; const last = inputValue?.split(first)[1]; return ( cx( "relative flex w-full items-center gap-2 rounded-lg bg-primary shadow-xs ring-1 ring-primary outline-hidden transition-shadow duration-100 ease-linear ring-inset", isDisabled && "cursor-not-allowed bg-disabled_subtle", isFocusWithin && "ring-2 ring-brand", sizes[size].root, ) } > {({ isDisabled }) => ( <>
{inputValue && ( )}
{shortcut && (
)} )}
); }; export const ComboBox = ({ placeholder = "Search", shortcut = true, size = "sm", children, items, shortcutClassName, ...otherProps }: ComboBoxProps) => { const placeholderRef = useRef(null); const [popoverWidth, setPopoverWidth] = useState(""); // Resize observer for popover width const onResize = useCallback(() => { if (!placeholderRef.current) return; const divRect = placeholderRef.current?.getBoundingClientRect(); setPopoverWidth(divRect.width + "px"); }, [placeholderRef, setPopoverWidth]); useResizeObserver({ ref: placeholderRef, box: "border-box", onResize, }); return ( {(state) => (
{otherProps.label && ( )} {children} {otherProps.hint && {otherProps.hint}}
)}
); };