{
  "$schema": "https://blode.co/ui/schema/registry-item.json",
  "name": "form",
  "title": "Form",
  "author": "Matthew Blode",
  "description": "A form component with validation powered by React Hook Form and Zod.",
  "dependencies": ["@base-ui/react", "@hookform/resolvers", "zod", "react-hook-form"],
  "registryDependencies": ["button", "label"],
  "files": [
    {
      "path": "ui/form.tsx",
      "content": "\"use client\";\n\nimport { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type * as React from \"react\";\nimport { createContext, isValidElement, useContext, useId, useMemo } from \"react\";\nimport { Controller, FormProvider, useFormContext, useFormState } from \"react-hook-form\";\nimport type { ControllerProps, FieldPath, FieldValues } from \"react-hook-form\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Label } from \"@/components/ui/label\";\n\nconst Form = FormProvider;\n\ninterface FormFieldContextValue<\n  TFieldValues extends FieldValues = FieldValues,\n  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> {\n  name: TName;\n}\n\nconst FormFieldContext = createContext<FormFieldContextValue>({} as FormFieldContextValue);\n\nconst FormField = <\n  TFieldValues extends FieldValues = FieldValues,\n  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n  ...props\n}: ControllerProps<TFieldValues, TName>) => {\n  const value = useMemo(() => ({ name: props.name }), [props.name]);\n\n  return (\n    <FormFieldContext.Provider value={value}>\n      <Controller {...props} />\n    </FormFieldContext.Provider>\n  );\n};\n\ninterface FormItemContextValue {\n  id: string;\n}\n\nconst FormItemContext = createContext<FormItemContextValue>({} as FormItemContextValue);\n\nconst useFormField = () => {\n  const fieldContext = useContext(FormFieldContext);\n  const itemContext = useContext(FormItemContext);\n  const { getFieldState } = useFormContext();\n  const formState = useFormState({ name: fieldContext.name });\n  const fieldState = getFieldState(fieldContext.name, formState);\n\n  if (!fieldContext) {\n    throw new Error(\"useFormField should be used within <FormField>\");\n  }\n\n  const { id } = itemContext;\n\n  return {\n    formDescriptionId: `${id}-form-item-description`,\n    formItemId: `${id}-form-item`,\n    formMessageId: `${id}-form-item-message`,\n    id,\n    name: fieldContext.name,\n    ...fieldState,\n  };\n};\n\nconst FormItem = ({ className, ...props }: React.ComponentProps<\"div\">) => {\n  const id = useId();\n  const value = useMemo(() => ({ id }), [id]);\n\n  return (\n    <FormItemContext.Provider value={value}>\n      <div className={cn(\"grid gap-2\", className)} data-slot=\"form-item\" {...props} />\n    </FormItemContext.Provider>\n  );\n};\n\nconst FormLabel = ({ className, ...props }: React.ComponentProps<typeof Label>) => {\n  const { error, formItemId } = useFormField();\n\n  return (\n    <Label\n      className={cn(\"data-[error=true]:text-destructive\", className)}\n      data-error={!!error}\n      data-slot=\"form-label\"\n      htmlFor={formItemId}\n      {...props}\n    />\n  );\n};\n\nconst FormControl = ({ children, ...props }: React.HTMLAttributes<HTMLElement>) => {\n  const { error, formItemId, formDescriptionId, formMessageId } = useFormField();\n  const hasValidChild = isValidElement(children);\n\n  const rendered = useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(\n      {\n        \"aria-describedby\": error\n          ? `${formDescriptionId} ${formMessageId}`\n          : `${formDescriptionId}`,\n        \"aria-invalid\": !!error,\n        id: formItemId,\n      },\n      props as React.ComponentProps<\"div\">,\n    ),\n    render: (hasValidChild ? children : <div />) as React.ReactElement,\n    state: {\n      slot: \"form-control\",\n    },\n  });\n\n  if (!hasValidChild) {\n    return null;\n  }\n\n  return rendered;\n};\n\nconst FormDescription = ({ className, ...props }: React.ComponentProps<\"p\">) => {\n  const { formDescriptionId } = useFormField();\n\n  return (\n    <p\n      className={cn(\"text-muted-foreground text-sm\", className)}\n      data-slot=\"form-description\"\n      id={formDescriptionId}\n      {...props}\n    />\n  );\n};\n\nconst FormMessage = ({ className, ...props }: React.ComponentProps<\"p\">) => {\n  const { error, formMessageId } = useFormField();\n  const body = error ? String(error?.message ?? \"\") : props.children;\n\n  if (!body) {\n    return null;\n  }\n\n  return (\n    <p\n      className={cn(\"text-destructive text-sm\", className)}\n      data-slot=\"form-message\"\n      id={formMessageId}\n      {...props}\n    >\n      {body}\n    </p>\n  );\n};\n\nexport {\n  useFormField,\n  Form,\n  FormItem,\n  FormLabel,\n  FormControl,\n  FormDescription,\n  FormMessage,\n  FormField,\n};\n",
      "type": "registry:ui",
      "target": ""
    }
  ],
  "type": "registry:ui"
}
