import { Controller, type Control, type FieldPath, type FieldValues } from "react-hook-form" import { AsyncSelect, type AsyncSelectOption, type AsyncSelectProps, } from "@/components/inputs/async-select" import { Select, type SelectProps } from "@/components/ui/select" import { FormFieldShell, type FormFieldShellControlProps, resolveFormFieldIds, } from "@/components/form/form-field-shell" export type FormSelectKind = "simple" | "async" type FormSelectBaseProps< TFieldValues extends FieldValues, TName extends FieldPath, > = FormFieldShellControlProps & { control: Control name: TName fieldClassName?: string } export type FormSimpleSelectProps< TFieldValues extends FieldValues = FieldValues, TName extends FieldPath = FieldPath, > = Omit & FormSelectBaseProps & { kind?: "simple" emptyValue?: unknown onValueChange?: (value: string) => void } export type FormSelectAsyncVariantProps< TFieldValues extends FieldValues = FieldValues, TName extends FieldPath = FieldPath, TValue extends string = string, TData = unknown, TOption extends AsyncSelectOption = AsyncSelectOption, > = Omit, "value" | "onValueChange" | "disabled"> & FormSelectBaseProps & { kind: "async" emptyValue?: unknown onValueChange?: (value: TValue | undefined, option?: TOption) => void } export type FormSelectProps< TFieldValues extends FieldValues = FieldValues, TName extends FieldPath = FieldPath, TValue extends string = string, TData = unknown, TOption extends AsyncSelectOption = AsyncSelectOption, > = | FormSimpleSelectProps | FormSelectAsyncVariantProps function buildShellProps< TFieldValues extends FieldValues, TName extends FieldPath, TValue extends string, TData, TOption extends AsyncSelectOption, >(props: FormSelectProps) { return { label: props.label, description: props.description, success: props.success, loading: props.loading, required: props.required, className: props.className, htmlFor: props.name, layout: props.layout, descriptionPosition: props.descriptionPosition, labelAction: props.labelAction, requiredIndicator: props.requiredIndicator, errorIcon: props.errorIcon, showErrorIcon: props.showErrorIcon, disabled: props.disabled, readOnly: props.readOnly, labelClassName: props.labelClassName, labelRowClassName: props.labelRowClassName, descriptionClassName: props.descriptionClassName, errorClassName: props.errorClassName, contentClassName: props.contentClassName, } satisfies FormFieldShellControlProps } function FormSelect< TFieldValues extends FieldValues = FieldValues, TName extends FieldPath = FieldPath, TValue extends string = string, TData = unknown, TOption extends AsyncSelectOption = AsyncSelectOption, >(props: FormSelectProps) { const shellProps = buildShellProps(props) const kind = props.kind ?? "simple" const resolvedFieldClassName = props.fieldClassName ?? "w-full" return ( { const error = fieldState.error?.message const controlId = `${props.name}` const resolvedIds = resolveFormFieldIds(controlId, { description: props.description, error, }) if (kind === "async") { const { control: _control, name: _name, label: _label, description: _description, required: _required, className: _className, layout: _layout, descriptionPosition: _descriptionPosition, labelAction: _labelAction, requiredIndicator: _requiredIndicator, errorIcon: _errorIcon, showErrorIcon: _showErrorIcon, disabled, readOnly, labelClassName: _labelClassName, labelRowClassName: _labelRowClassName, descriptionClassName: _descriptionClassName, errorClassName: _errorClassName, contentClassName: _contentClassName, fieldClassName, emptyValue, kind: _kind, onValueChange, ...asyncProps } = props as FormSelectAsyncVariantProps return ( {...asyncProps} value={field.value == null || field.value === "" ? undefined : (String(field.value) as TValue)} disabled={disabled || readOnly} aria-describedby={resolvedIds.describedBy} aria-errormessage={error ? resolvedIds.errorId : undefined} onValueChange={(nextValue, option) => { field.onChange(nextValue ?? emptyValue) onValueChange?.(nextValue, option) }} triggerClassName={fieldClassName ?? resolvedFieldClassName} /> ) } const { control: _control, name: _name, label: _label, description: _description, required: _required, className: _className, layout: _layout, descriptionPosition: _descriptionPosition, labelAction: _labelAction, requiredIndicator: _requiredIndicator, errorIcon: _errorIcon, showErrorIcon: _showErrorIcon, disabled, readOnly, labelClassName: _labelClassName, labelRowClassName: _labelRowClassName, descriptionClassName: _descriptionClassName, errorClassName: _errorClassName, contentClassName: _contentClassName, fieldClassName, emptyValue, kind: _kind, onValueChange, ...simpleProps } = props as FormSimpleSelectProps return (