{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "form",
  "type": "registry:component",
  "title": "Form",
  "description": "Validation wrapper for composing labels, descriptions, controls, and messages.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/form/form.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport {\n  Controller,\n  type ControllerProps,\n  type DefaultValues,\n  type FieldPath,\n  type FieldValues,\n  FormProvider,\n  type Resolver,\n  type SubmitErrorHandler,\n  useForm,\n  useFormContext,\n  type UseFormReturn,\n} from \"react-hook-form\";\n\nimport { cn } from \"@vllnt/ui\";\nimport { Label } from \"@vllnt/ui\";\n\ntype FormInstance<TFieldValues extends FieldValues> = UseFormReturn<\n  TFieldValues,\n  unknown,\n  TFieldValues\n>;\n\ntype FormRenderChildren<TFieldValues extends FieldValues> =\n  | ((form: FormInstance<TFieldValues>) => React.ReactNode)\n  | React.ReactNode;\n\ntype FormSubmitHandler<TFieldValues extends FieldValues> = (\n  values: TFieldValues,\n  form: FormInstance<TFieldValues>,\n) => Promise<void> | void;\n\ntype FormErrorHandler<TFieldValues extends FieldValues> = (\n  errors: Parameters<SubmitErrorHandler<TFieldValues>>[0],\n  form: FormInstance<TFieldValues>,\n) => Promise<void> | void;\n\ntype BaseFormProps<TFieldValues extends FieldValues> = Omit<\n  React.ComponentPropsWithoutRef<\"form\">,\n  \"children\"\n> & {\n  children?: FormRenderChildren<TFieldValues>;\n  controlId?: string;\n  descriptionId?: string;\n  disabled?: boolean;\n  invalid?: boolean;\n  messageId?: string;\n  onError?: FormErrorHandler<TFieldValues>;\n  onValidSubmit?: FormSubmitHandler<TFieldValues>;\n  required?: boolean;\n};\n\ntype ManagedFormProps<TFieldValues extends FieldValues> = {\n  defaultValues?: DefaultValues<TFieldValues>;\n  form?: undefined;\n  resolver?: Resolver<TFieldValues>;\n  schema?: Parameters<typeof zodResolver>[0];\n  values?: TFieldValues;\n};\n\ntype ProvidedFormProps<TFieldValues extends FieldValues> = {\n  defaultValues?: never;\n  form: FormInstance<TFieldValues>;\n  resolver?: never;\n  schema?: never;\n  values?: never;\n};\n\nexport type FormProps<TFieldValues extends FieldValues = FieldValues> =\n  BaseFormProps<TFieldValues> &\n    (ManagedFormProps<TFieldValues> | ProvidedFormProps<TFieldValues>);\n\ntype FormNativeSubmitHandler =\n  React.ComponentPropsWithoutRef<\"form\">[\"onSubmit\"];\n\ntype FormRootContextValue = {\n  controlId?: string;\n  descriptionId?: string;\n  disabled: boolean;\n  invalid: boolean;\n  messageId?: string;\n  required: boolean;\n};\n\ntype FormFieldContextValue = {\n  name: string;\n};\n\ntype FormItemContextValue = {\n  controlId: string;\n  descriptionId: string;\n  disabled: boolean;\n  hasDescription: boolean;\n  hasMessage: boolean;\n  hasMessageSlot: boolean;\n  id: string;\n  invalid: boolean;\n  messageId: string;\n  required: boolean;\n};\n\nconst FormRootContext = React.createContext<FormRootContextValue | undefined>(\n  undefined,\n);\n\nconst FormFieldContext = React.createContext<FormFieldContextValue | undefined>(\n  undefined,\n);\n\nconst FormItemContext = React.createContext<FormItemContextValue | undefined>(\n  undefined,\n);\n\nfunction useFormRootContext(componentName: string) {\n  const context = React.useContext(FormRootContext);\n\n  if (context === undefined) {\n    throw new Error(`${componentName} must be used within Form.`);\n  }\n\n  return context;\n}\n\nfunction useFormItemContext(componentName: string) {\n  const context = React.useContext(FormItemContext);\n\n  if (context === undefined) {\n    throw new Error(`${componentName} must be used within FormItem.`);\n  }\n\n  return context;\n}\n\nfunction composeIds(...ids: (string | undefined)[]) {\n  const value = ids.filter((id) => id !== undefined && id.length > 0).join(\" \");\n\n  return value.length > 0 ? value : undefined;\n}\n\nfunction resolveItemId(\n  baseId: string | undefined,\n  generatedId: string,\n  suffix: string,\n) {\n  if (baseId === undefined) {\n    return `${generatedId}-${suffix}`;\n  }\n\n  return baseId.endsWith(`-${suffix}`)\n    ? `${baseId}-${generatedId}`\n    : `${baseId}-${suffix}-${generatedId}`;\n}\n\nfunction isNamedFormChild(\n  child: React.ReactNode,\n  name: \"FormDescription\" | \"FormMessage\",\n): child is React.ReactElement<{ children?: React.ReactNode }> {\n  if (!React.isValidElement<{ children?: React.ReactNode }>(child)) {\n    return false;\n  }\n\n  const { type } = child;\n  if (typeof type === \"string\" || typeof type === \"symbol\") {\n    return false;\n  }\n\n  return \"displayName\" in type && type.displayName === name;\n}\n\nfunction hasVisibleContent(children: React.ReactNode): boolean {\n  return React.Children.toArray(children).some((child) => {\n    if (child === null || child === undefined || typeof child === \"boolean\") {\n      return false;\n    }\n\n    if (typeof child === \"string\") {\n      return child.length > 0;\n    }\n\n    if (typeof child === \"number\") {\n      return true;\n    }\n\n    if (React.isValidElement<{ children?: React.ReactNode }>(child)) {\n      const nestedChildren = child.props.children;\n\n      return nestedChildren === undefined\n        ? true\n        : hasVisibleContent(nestedChildren);\n    }\n\n    return true;\n  });\n}\n\nfunction hasFormChild(\n  children: React.ReactNode,\n  name: \"FormDescription\" | \"FormMessage\",\n): boolean {\n  return React.Children.toArray(children).some((child) => {\n    if (isNamedFormChild(child, name)) {\n      return true;\n    }\n\n    if (React.isValidElement<{ children?: React.ReactNode }>(child)) {\n      return hasFormChild(child.props.children, name);\n    }\n\n    return false;\n  });\n}\n\nfunction hasRenderedFormChild(\n  children: React.ReactNode,\n  name: \"FormDescription\" | \"FormMessage\",\n): boolean {\n  return React.Children.toArray(children).some((child) => {\n    if (isNamedFormChild(child, name)) {\n      return name === \"FormMessage\"\n        ? hasVisibleContent(child.props.children)\n        : true;\n    }\n\n    if (React.isValidElement<{ children?: React.ReactNode }>(child)) {\n      return hasRenderedFormChild(child.props.children, name);\n    }\n\n    return false;\n  });\n}\n\nfunction createManagedSubmitHandler<TFieldValues extends FieldValues>(\n  form: FormInstance<TFieldValues>,\n  options: {\n    onError?: FormErrorHandler<TFieldValues>;\n    onValidSubmit?: FormSubmitHandler<TFieldValues>;\n    shouldValidate: boolean;\n  },\n): ReturnType<FormInstance<TFieldValues>[\"handleSubmit\"]> | undefined {\n  const { onError, onValidSubmit, shouldValidate } = options;\n\n  if (!shouldValidate) {\n    return undefined;\n  }\n\n  return form.handleSubmit(\n    async (submittedValues) => {\n      if (onValidSubmit !== undefined) {\n        await onValidSubmit(submittedValues, form);\n      }\n    },\n    async (errors) => {\n      if (onError !== undefined) {\n        await onError(errors, form);\n      }\n    },\n  );\n}\n\nfunction createSubmitHandler(\n  nativeSubmit: FormNativeSubmitHandler,\n  handleValidatedSubmit:\n    | ((event?: React.BaseSyntheticEvent) => Promise<void>)\n    | undefined,\n): FormNativeSubmitHandler {\n  return async (event) => {\n    nativeSubmit?.(event);\n\n    if (handleValidatedSubmit && !event.defaultPrevented) {\n      await handleValidatedSubmit(event);\n    }\n  };\n}\n\nfunction useFormRootContextValue(\n  value: FormRootContextValue,\n): FormRootContextValue {\n  const { controlId, descriptionId, disabled, invalid, messageId, required } =\n    value;\n\n  return React.useMemo(\n    () => ({\n      controlId,\n      descriptionId,\n      disabled,\n      invalid,\n      messageId,\n      required,\n    }),\n    [controlId, descriptionId, disabled, invalid, messageId, required],\n  );\n}\n\ntype FormMarkupProps<TFieldValues extends FieldValues> = {\n  children: FormProps<TFieldValues>[\"children\"];\n  className?: string;\n  disabled: boolean;\n  form: FormInstance<TFieldValues>;\n  formProps: Omit<\n    React.ComponentPropsWithoutRef<\"form\">,\n    \"children\" | \"className\" | \"onSubmit\"\n  >;\n  formRef: React.ForwardedRef<HTMLFormElement>;\n  handleValidatedSubmit?: ReturnType<\n    FormInstance<TFieldValues>[\"handleSubmit\"]\n  >;\n  invalid: boolean;\n  onSubmit: FormNativeSubmitHandler;\n  rootContextValue: FormRootContextValue;\n};\n\nfunction FormMarkup<TFieldValues extends FieldValues>({\n  children,\n  className,\n  disabled,\n  form,\n  formProps,\n  formRef,\n  handleValidatedSubmit,\n  invalid,\n  onSubmit,\n  rootContextValue,\n}: FormMarkupProps<TFieldValues>) {\n  return (\n    <FormRootContext.Provider value={rootContextValue}>\n      <FormProvider {...form}>\n        <form\n          className={cn(\"space-y-2\", className)}\n          data-disabled={\n            disabled || form.formState.isSubmitting ? \"true\" : undefined\n          }\n          data-invalid={invalid ? \"true\" : undefined}\n          data-submitting={form.formState.isSubmitting ? \"true\" : undefined}\n          onSubmit={\n            onSubmit === undefined && handleValidatedSubmit === undefined\n              ? undefined\n              : createSubmitHandler(onSubmit, handleValidatedSubmit)\n          }\n          ref={formRef}\n          {...formProps}\n        >\n          {typeof children === \"function\" ? children(form) : children}\n        </form>\n      </FormProvider>\n    </FormRootContext.Provider>\n  );\n}\n\nfunction FormInner<TFieldValues extends FieldValues = FieldValues>(\n  {\n    children,\n    className,\n    controlId,\n    defaultValues,\n    descriptionId,\n    disabled = false,\n    form: providedForm,\n    invalid = false,\n    messageId,\n    onError,\n    onSubmit,\n    onValidSubmit,\n    required = false,\n    resolver,\n    schema,\n    values,\n    ...props\n  }: FormProps<TFieldValues>,\n  ref: React.ForwardedRef<HTMLFormElement>,\n) {\n  const internalForm = useForm<TFieldValues>({\n    defaultValues,\n    resolver:\n      schema === undefined\n        ? resolver\n        : (zodResolver(schema) as Resolver<TFieldValues>),\n    values,\n  });\n  const form: FormInstance<TFieldValues> = providedForm ?? internalForm;\n  const isManagedForm =\n    providedForm !== undefined ||\n    resolver !== undefined ||\n    schema !== undefined;\n  const rootContextValue = useFormRootContextValue({\n    controlId,\n    descriptionId,\n    disabled,\n    invalid,\n    messageId,\n    required,\n  });\n  const handleValidatedSubmit = createManagedSubmitHandler(form, {\n    onError,\n    onValidSubmit,\n    shouldValidate:\n      isManagedForm || onValidSubmit !== undefined || onError !== undefined,\n  });\n\n  return (\n    <FormMarkup\n      className={className}\n      disabled={disabled}\n      form={form}\n      formProps={props}\n      formRef={ref}\n      handleValidatedSubmit={handleValidatedSubmit}\n      invalid={invalid}\n      onSubmit={onSubmit}\n      rootContextValue={rootContextValue}\n    >\n      {children}\n    </FormMarkup>\n  );\n}\n\nconst FormBase = React.forwardRef(FormInner);\nFormBase.displayName = \"Form\";\n\nconst Form = FormBase as <TFieldValues extends FieldValues = FieldValues>(\n  props: FormProps<TFieldValues> & React.RefAttributes<HTMLFormElement>,\n) => React.ReactElement;\n\nfunction FormField<\n  TFieldValues extends FieldValues = FieldValues,\n  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({ ...props }: ControllerProps<TFieldValues, TName>) {\n  const fieldContextValue = React.useMemo(\n    () => ({ name: props.name }),\n    [props.name],\n  );\n\n  return (\n    <FormFieldContext.Provider value={fieldContextValue}>\n      <Controller {...props} />\n    </FormFieldContext.Provider>\n  );\n}\n\nfunction useFormField() {\n  const fieldContext = React.useContext(FormFieldContext);\n  const itemContext = useFormItemContext(\"useFormField\");\n  const { formState, getFieldState } = useFormContext();\n\n  if (fieldContext === undefined) {\n    return {\n      disabled: itemContext.disabled,\n      error: undefined,\n      formDescriptionId: itemContext.descriptionId,\n      formItemId: itemContext.controlId,\n      formMessageId: itemContext.messageId,\n      hasDescription: itemContext.hasDescription,\n      hasMessage: itemContext.hasMessage,\n      hasMessageSlot: itemContext.hasMessageSlot,\n      id: itemContext.id,\n      invalid: itemContext.invalid,\n      isDirty: false,\n      isTouched: false,\n      isValidating: false,\n      name: \"\",\n      required: itemContext.required,\n    };\n  }\n\n  const fieldState = getFieldState(fieldContext.name, formState);\n\n  return {\n    disabled: itemContext.disabled,\n    error: fieldState.error,\n    formDescriptionId: itemContext.descriptionId,\n    formItemId: itemContext.controlId,\n    formMessageId: itemContext.messageId,\n    hasDescription: itemContext.hasDescription,\n    hasMessage: itemContext.hasMessage,\n    hasMessageSlot: itemContext.hasMessageSlot,\n    id: itemContext.id,\n    invalid: itemContext.invalid || fieldState.invalid,\n    isDirty: fieldState.isDirty,\n    isTouched: fieldState.isTouched,\n    isValidating: fieldState.isValidating,\n    name: fieldContext.name,\n    required: itemContext.required,\n  };\n}\n\nconst FormItem = React.forwardRef<\n  HTMLDivElement,\n  React.ComponentPropsWithoutRef<\"div\"> & {\n    disabled?: boolean;\n    invalid?: boolean;\n    required?: boolean;\n  }\n>(\n  (\n    {\n      children,\n      className,\n      disabled: itemDisabled,\n      invalid: itemInvalid,\n      required: itemRequired,\n      ...props\n    },\n    ref,\n  ) => {\n    const {\n      controlId: controlIdBase,\n      descriptionId: descriptionIdBase,\n      disabled,\n      invalid,\n      messageId: messageIdBase,\n      required,\n    } = useFormRootContext(\"FormItem\");\n    const generatedId = React.useId();\n    const hasDescription = hasRenderedFormChild(children, \"FormDescription\");\n    const hasMessage = hasRenderedFormChild(children, \"FormMessage\");\n    const hasMessageSlot = hasFormChild(children, \"FormMessage\");\n\n    const effectiveDisabled = itemDisabled ?? disabled;\n    const effectiveInvalid = itemInvalid ?? invalid;\n    const effectiveRequired = itemRequired ?? required;\n\n    const value = React.useMemo<FormItemContextValue>(\n      () => ({\n        controlId: resolveItemId(controlIdBase, generatedId, \"control\"),\n        descriptionId: resolveItemId(\n          descriptionIdBase,\n          generatedId,\n          \"description\",\n        ),\n        disabled: effectiveDisabled,\n        hasDescription,\n        hasMessage,\n        hasMessageSlot,\n        id: generatedId,\n        invalid: effectiveInvalid,\n        messageId: resolveItemId(messageIdBase, generatedId, \"message\"),\n        required: effectiveRequired,\n      }),\n      [\n        controlIdBase,\n        descriptionIdBase,\n        effectiveDisabled,\n        effectiveInvalid,\n        effectiveRequired,\n        generatedId,\n        hasDescription,\n        hasMessage,\n        hasMessageSlot,\n        messageIdBase,\n      ],\n    );\n\n    return (\n      <FormItemContext.Provider value={value}>\n        <div className={cn(\"space-y-2\", className)} ref={ref} {...props}>\n          {children}\n        </div>\n      </FormItemContext.Provider>\n    );\n  },\n);\nFormItem.displayName = \"FormItem\";\n\nconst FormLabel = React.forwardRef<\n  React.ComponentRef<typeof Label>,\n  React.ComponentPropsWithoutRef<typeof Label>\n>(({ className, htmlFor, ...props }, ref) => {\n  const { formItemId, invalid } = useFormField();\n\n  return (\n    <Label\n      className={cn(invalid && \"text-destructive\", className)}\n      data-invalid={invalid ? \"true\" : undefined}\n      htmlFor={htmlFor ?? formItemId}\n      ref={ref}\n      {...props}\n    />\n  );\n});\nFormLabel.displayName = \"FormLabel\";\n\ntype FormControlProps = React.ComponentPropsWithoutRef<typeof Slot> & {\n  disabled?: boolean;\n  required?: boolean;\n};\n\nconst FormControl = React.forwardRef<HTMLElement, FormControlProps>(\n  (\n    { disabled: controlDisabled, id: _id, required: controlRequired, ...props },\n    ref,\n  ) => {\n    const {\n      disabled,\n      error,\n      formDescriptionId,\n      formItemId,\n      formMessageId,\n      hasDescription,\n      hasMessage,\n      hasMessageSlot,\n      invalid,\n      required,\n    } = useFormField();\n    const { formState } = useFormContext();\n    const hasErrorMessage = hasVisibleContent(error?.message);\n    const describedBy = composeIds(\n      props[\"aria-describedby\"],\n      hasDescription ? formDescriptionId : undefined,\n      error === undefined\n        ? hasMessage && invalid\n          ? formMessageId\n          : undefined\n        : hasMessageSlot && hasErrorMessage\n          ? formMessageId\n          : undefined,\n    );\n    const effectiveDisabled =\n      controlDisabled ?? (disabled || formState.isSubmitting);\n    const effectiveRequired = controlRequired ?? required;\n    const nativeConstraintProps: {\n      disabled?: boolean;\n      required?: boolean;\n    } = {\n      disabled: effectiveDisabled || undefined,\n      required: effectiveRequired || undefined,\n    };\n\n    return (\n      <Slot\n        {...props}\n        {...nativeConstraintProps}\n        aria-describedby={describedBy}\n        aria-disabled={\n          props[\"aria-disabled\"] ?? (effectiveDisabled || undefined)\n        }\n        aria-invalid={props[\"aria-invalid\"] ?? (invalid || undefined)}\n        aria-required={\n          props[\"aria-required\"] ?? (effectiveRequired || undefined)\n        }\n        data-disabled={effectiveDisabled ? \"true\" : undefined}\n        data-invalid={invalid ? \"true\" : undefined}\n        id={formItemId}\n        ref={ref}\n      />\n    );\n  },\n);\nFormControl.displayName = \"FormControl\";\n\nconst FormDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.ComponentPropsWithoutRef<\"p\">\n>(({ className, id: _id, ...props }, ref) => {\n  const { formDescriptionId } = useFormField();\n\n  return (\n    <p\n      {...props}\n      className={cn(\"text-sm text-muted-foreground\", className)}\n      id={formDescriptionId}\n      ref={ref}\n    />\n  );\n});\nFormDescription.displayName = \"FormDescription\";\n\nconst FormMessage = React.forwardRef<\n  HTMLParagraphElement,\n  React.ComponentPropsWithoutRef<\"p\">\n>(({ children, className, id: _id, ...props }, ref) => {\n  const { error, formMessageId, invalid } = useFormField();\n  const body = error?.message ?? children;\n\n  if (!hasVisibleContent(body)) {\n    return null;\n  }\n\n  return (\n    <p\n      {...props}\n      className={cn(\n        \"text-sm font-medium\",\n        invalid || error !== undefined ? \"text-destructive\" : \"text-foreground\",\n        className,\n      )}\n      id={formMessageId}\n      ref={ref}\n      role={invalid || error !== undefined ? \"alert\" : undefined}\n    >\n      {body}\n    </p>\n  );\n});\nFormMessage.displayName = \"FormMessage\";\n\nexport {\n  Form,\n  FormControl,\n  FormDescription,\n  FormField,\n  FormItem,\n  FormLabel,\n  FormMessage,\n  useFormField,\n};\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
