Spaces:
Runtime error
Runtime error
import * as React from "react" | |
import * as LabelPrimitive from "@radix-ui/react-label" | |
import { Slot } from "@radix-ui/react-slot" | |
import { | |
Controller, | |
ControllerProps, | |
FieldPath, | |
FieldValues, | |
FormProvider, | |
useFormContext, | |
} from "react-hook-form" | |
import { cn } from "@/lib/utils" | |
import { Label } from "@/components/ui/label" | |
const Form = FormProvider | |
type FormFieldContextValue< | |
TFieldValues extends FieldValues = FieldValues, | |
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> | |
> = { | |
name: TName | |
} | |
const FormFieldContext = React.createContext<FormFieldContextValue>( | |
{} as FormFieldContextValue | |
) | |
const FormField = < | |
TFieldValues extends FieldValues = FieldValues, | |
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> | |
>({ | |
...props | |
}: ControllerProps<TFieldValues, TName>) => { | |
return ( | |
<FormFieldContext.Provider value={{ name: props.name }}> | |
<Controller {...props} /> | |
</FormFieldContext.Provider> | |
) | |
} | |
const useFormField = () => { | |
const fieldContext = React.useContext(FormFieldContext) | |
const itemContext = React.useContext(FormItemContext) | |
const { getFieldState, formState } = useFormContext() | |
const fieldState = getFieldState(fieldContext.name, formState) | |
if (!fieldContext) { | |
throw new Error("useFormField should be used within <FormField>") | |
} | |
const { id } = itemContext | |
return { | |
id, | |
name: fieldContext.name, | |
formItemId: `${id}-form-item`, | |
formDescriptionId: `${id}-form-item-description`, | |
formMessageId: `${id}-form-item-message`, | |
...fieldState, | |
} | |
} | |
type FormItemContextValue = { | |
id: string | |
} | |
const FormItemContext = React.createContext<FormItemContextValue>( | |
{} as FormItemContextValue | |
) | |
const FormItem = React.forwardRef< | |
HTMLDivElement, | |
React.HTMLAttributes<HTMLDivElement> | |
>(({ className, ...props }, ref) => { | |
const id = React.useId() | |
return ( | |
<FormItemContext.Provider value={{ id }}> | |
<div ref={ref} className={cn("space-y-2", className)} {...props} /> | |
</FormItemContext.Provider> | |
) | |
}) | |
FormItem.displayName = "FormItem" | |
const FormLabel = React.forwardRef< | |
React.ElementRef<typeof LabelPrimitive.Root>, | |
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> | |
>(({ className, ...props }, ref) => { | |
const { error, formItemId } = useFormField() | |
return ( | |
<Label | |
ref={ref} | |
className={cn(error && "text-destructive", className)} | |
htmlFor={formItemId} | |
{...props} | |
/> | |
) | |
}) | |
FormLabel.displayName = "FormLabel" | |
const FormControl = React.forwardRef< | |
React.ElementRef<typeof Slot>, | |
React.ComponentPropsWithoutRef<typeof Slot> | |
>(({ ...props }, ref) => { | |
const { error, formItemId, formDescriptionId, formMessageId } = useFormField() | |
return ( | |
<Slot | |
ref={ref} | |
id={formItemId} | |
aria-describedby={ | |
!error | |
? `${formDescriptionId}` | |
: `${formDescriptionId} ${formMessageId}` | |
} | |
aria-invalid={!!error} | |
{...props} | |
/> | |
) | |
}) | |
FormControl.displayName = "FormControl" | |
const FormDescription = React.forwardRef< | |
HTMLParagraphElement, | |
React.HTMLAttributes<HTMLParagraphElement> | |
>(({ className, ...props }, ref) => { | |
const { formDescriptionId } = useFormField() | |
return ( | |
<p | |
ref={ref} | |
id={formDescriptionId} | |
className={cn("text-sm text-muted-foreground", className)} | |
{...props} | |
/> | |
) | |
}) | |
FormDescription.displayName = "FormDescription" | |
const FormMessage = React.forwardRef< | |
HTMLParagraphElement, | |
React.HTMLAttributes<HTMLParagraphElement> | |
>(({ className, children, ...props }, ref) => { | |
const { error, formMessageId } = useFormField() | |
const body = error ? String(error?.message) : children | |
if (!body) { | |
return null | |
} | |
return ( | |
<p | |
ref={ref} | |
id={formMessageId} | |
className={cn("text-sm font-medium text-destructive", className)} | |
{...props} | |
> | |
{body} | |
</p> | |
) | |
}) | |
FormMessage.displayName = "FormMessage" | |
export { | |
useFormField, | |
Form, | |
FormItem, | |
FormLabel, | |
FormControl, | |
FormDescription, | |
FormMessage, | |
FormField, | |
} | |