import Label from './label' import { Slot } from '@radix-ui/react-slot' import { createContext, forwardRef, useContext, useId } from 'react' import type { ControllerProps, FieldPath, FieldValues, UseFormReturn } from 'react-hook-form' import { Controller, FormProvider, useFormContext } from 'react-hook-form' import { tv } from 'tailwind-variants' import type { Merge } from 'type-fest' const form = tv({ slots: { item: 'flex flex-col gap-2', description: 'text-sm text-fg-weaker', errorMessage: 'text-sm font-medium text-fg-danger', }, }) function FormRoot({ form, asChild, ...props }: Merge< React.ComponentProps<'form'>, { // eslint-disable-next-line @typescript-eslint/no-explicit-any form: UseFormReturn asChild?: boolean } >) { const Comp = asChild ? Slot : 'form' return ( ) } const FormFieldContext = createContext<{ name: FieldPath } | undefined>(undefined) const FormField = < TFieldValues extends FieldValues = FieldValues, TName extends FieldPath = FieldPath, >({ ...props }: ControllerProps) => { return ( ) } const FormItemContext = createContext<{ id: string } | undefined>(undefined) const FormItem = forwardRef< HTMLDivElement, Merge< React.HTMLAttributes, { asChild?: boolean } > >(({ asChild, ...props }, ref) => { const id = useId() const { item } = form() const Comp = asChild ? Slot : 'div' return ( ) }) FormItem.displayName = 'FormItem' const useFormField = () => { const fieldContext = useContext(FormFieldContext) const itemContext = useContext(FormItemContext) const { getFieldState, formState } = useFormContext() if (!fieldContext) { throw new Error('useFormField should be used within ') } if (!itemContext) { throw new Error('useFormField should be used within ') } const fieldState = getFieldState(fieldContext.name, formState) const { id } = itemContext return { ...fieldState, id, name: fieldContext.name, formItemId: `${id}-form-item`, formDescriptionId: `${id}-form-item-description`, formMessageId: `${id}-form-item-message`, } } const FormLabel = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >((props, ref) => { const { error, formItemId } = useFormField() return