'use client'; import * as SelectPrimitive from '@radix-ui/react-select'; import { cva } from 'class-variance-authority'; import * as React from 'react'; import { BasicField, BasicFieldOptions, extractBasicFieldProps, } from '../basic-field'; import { cn, ComponentAnatomy, defineStyleAnatomy } from '../core/styling'; import { mergeRefs } from '../core/utils'; import { extractInputPartProps, hiddenInputStyles, InputAddon, InputAnatomy, InputContainer, InputIcon, InputStyling, } from '../input'; /* ------------------------------------------------------------------------------------------------- * Anatomy * -----------------------------------------------------------------------------------------------*/ export const SelectAnatomy = defineStyleAnatomy({ root: cva([ 'UI-Select__root', 'inline-flex items-center justify-between relative whitespace-nowrap truncate', ]), chevronIcon: cva([ 'UI-Combobox__chevronIcon', 'ml-2 h-4 w-4 shrink-0 opacity-50', ]), scrollButton: cva([ 'UI-Select__scrollButton', 'flex items-center justify-center h-[25px] bg-[--paper] text-base cursor-default', ]), content: cva([ 'UI-Select__content', 'w-full overflow-hidden rounded-[--radius] shadow-md bg-[--paper] border leading-none z-50', ]), viewport: cva(['UI-Select__viewport', 'p-1 z-10']), item: cva([ 'UI-Select__item', 'text-base leading-none rounded-[--radius] flex items-center h-8 pr-2 pl-8 relative', 'select-none disabled:opacity-50 disabled:pointer-events-none', 'data-highlighted:outline-none data-highlighted:bg-[--subtle]', 'data-[disabled=true]:opacity-50 data-[disabled=true]:pointer-events-none', ]), checkIcon: cva([ 'UI-Select__checkIcon', 'absolute left-2 w-4 inline-flex items-center justify-center', ]), }); /* ------------------------------------------------------------------------------------------------- * Select * -----------------------------------------------------------------------------------------------*/ export type SelectOption = { value: string; label?: string; disabled?: boolean; }; export type SelectProps = InputStyling & BasicFieldOptions & Omit, 'value' | 'defaultValue'> & ComponentAnatomy & { /** * The options to display in the dropdown */ options: SelectOption[] | undefined; /** * The placeholder text */ placeholder?: string; /** * Direction of the text */ dir?: 'ltr' | 'rtl'; /** * The selected value */ value?: string | undefined; /** * Callback fired when the selected value changes */ onValueChange?: (value: string) => void; /** * Callback fired when the dropdown opens or closes */ onOpenChange?: (open: boolean) => void; /** * Default selected value when uncontrolled */ defaultValue?: string; /** * Ref to the input element */ inputRef?: React.Ref; }; export const Select = React.forwardRef( (props, ref) => { const [props1, basicFieldProps] = extractBasicFieldProps( props, React.useId() ); const [ { size, intent, leftAddon, leftIcon, rightAddon, rightIcon, /**/ className, placeholder, options, chevronIconClass, scrollButtonClass, contentClass, viewportClass, checkIconClass, itemClass, /**/ dir, value: controlledValue, onValueChange, onOpenChange, defaultValue, inputRef, ...rest }, { inputContainerProps, leftAddonProps, leftIconProps, rightAddonProps, rightIconProps, }, ] = extractInputPartProps({ ...props1, size: props1.size ?? 'md', intent: props1.intent ?? 'basic', leftAddon: props1.leftAddon, leftIcon: props1.leftIcon, rightAddon: props1.rightAddon, rightIcon: props1.rightIcon, }); const isFirst = React.useRef(true); const buttonRef = React.useRef(null); const [_value, _setValue] = React.useState( controlledValue ?? defaultValue ); const handleOnValueChange = React.useCallback((value: string) => { if (value === '__placeholder__') { _setValue(''); onValueChange?.(''); return; } _setValue(value); onValueChange?.(value); }, []); React.useEffect(() => { if (!defaultValue || !isFirst.current) { _setValue(controlledValue); } isFirst.current = false; }, [controlledValue]); return ( {!!placeholder && !basicFieldProps.required && ( {placeholder} )} {options?.map((option) => ( {option.label} ))} ); } ); Select.displayName = 'Select';