import type { FC, ReactNode, Ref, RefAttributes } from "react"; import { createContext, isValidElement } from "react"; import { faChevronDown } from "@fortawesome/pro-duotone-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import type { SelectProps as AriaSelectProps } from "react-aria-components"; import { Button as AriaButton, ListBox as AriaListBox, Select as AriaSelect, SelectValue as AriaSelectValue } from "react-aria-components"; import { Avatar } from "@/components/base/avatar/avatar"; import { HintText } from "@/components/base/input/hint-text"; import { Label } from "@/components/base/input/label"; import { cx } from "@/utils/cx"; import { isReactComponent } from "@/utils/is-react-component"; import { ComboBox } from "./combobox"; import { Popover } from "./popover"; import { SelectItem } from "./select-item"; export type SelectItemType = { id: string; label?: string; avatarUrl?: string; isDisabled?: boolean; supportingText?: string; icon?: FC | ReactNode; }; export interface CommonProps { hint?: string; label?: string; tooltip?: string; size?: "sm" | "md"; placeholder?: string; } interface SelectProps extends Omit, "children" | "items">, RefAttributes, CommonProps { items?: SelectItemType[]; popoverClassName?: string; placeholderIcon?: FC | ReactNode; children: ReactNode | ((item: SelectItemType) => ReactNode); } interface SelectValueProps { isOpen: boolean; size: "sm" | "md"; isFocused: boolean; isDisabled: boolean; placeholder?: string; ref?: Ref; placeholderIcon?: FC | ReactNode; } export const sizes = { sm: { root: "py-2 px-3", shortcut: "pr-2.5" }, md: { root: "py-2.5 px-3.5", shortcut: "pr-3" }, }; const SelectValue = ({ isOpen, isFocused, isDisabled, size, placeholder, placeholderIcon, ref }: SelectValueProps) => { return ( className={cx( "flex h-max w-full items-center justify-start gap-2 truncate text-left align-middle", // Icon styles "*:data-icon:size-5 *:data-icon:shrink-0 *:data-icon:text-fg-quaternary in-disabled:*:data-icon:text-fg-disabled", sizes[size].root, )} > {(state) => { const Icon = state.selectedItem?.icon || placeholderIcon; return ( <> {state.selectedItem?.avatarUrl ? ( ) : isReactComponent(Icon) ? ( ); }; export const SelectContext = createContext<{ size: "sm" | "md" }>({ size: "sm" }); const Select = ({ placeholder = "Select", placeholderIcon, size = "sm", children, items, label, hint, tooltip, className, ...rest }: SelectProps) => { return ( cx("flex flex-col gap-1.5", typeof className === "function" ? className(state) : className)}> {(state) => ( <> {label && ( )} {children} {hint && {hint}} )} ); }; const _Select = Select as typeof Select & { ComboBox: typeof ComboBox; Item: typeof SelectItem; }; _Select.ComboBox = ComboBox; _Select.Item = SelectItem; export { _Select as Select };