{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "form",
  "title": "Form",
  "description": "Validation wrapper for composing labels, descriptions, controls, and messages.",
  "dependencies": [
    "@vllnt/ui@^0.3.0"
  ],
  "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.use(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.use(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.Ref<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  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  ref,\n  required = false,\n  resolver,\n  schema,\n  values,\n  ...props\n}: FormProps<TFieldValues> & { ref?: React.Ref<HTMLFormElement> }) {\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\nFormInner.displayName = \"Form\";\nconst Form = FormInner;\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.use(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 = ({\n  children,\n  className,\n  disabled: itemDisabled,\n  invalid: itemInvalid,\n  ref,\n  required: itemRequired,\n  ...props\n}: React.ComponentPropsWithoutRef<\"div\"> & {\n  disabled?: boolean;\n  invalid?: boolean;\n  ref?: React.Ref<HTMLDivElement>;\n  required?: boolean;\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};\nFormItem.displayName = \"FormItem\";\n\nconst FormLabel = ({\n  className,\n  htmlFor,\n  ref,\n  ...props\n}: React.ComponentPropsWithoutRef<typeof Label> & {\n  ref?: React.Ref<React.ComponentRef<typeof Label>>;\n}) => {\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 = ({\n  disabled: controlDisabled,\n  id: _id,\n  ref,\n  required: controlRequired,\n  ...props\n}: FormControlProps & { ref?: React.Ref<HTMLElement> }) => {\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={props[\"aria-disabled\"] ?? (effectiveDisabled || undefined)}\n      aria-invalid={props[\"aria-invalid\"] ?? (invalid || undefined)}\n      aria-required={props[\"aria-required\"] ?? (effectiveRequired || undefined)}\n      data-disabled={effectiveDisabled ? \"true\" : undefined}\n      data-invalid={invalid ? \"true\" : undefined}\n      id={formItemId}\n      ref={ref}\n    />\n  );\n};\nFormControl.displayName = \"FormControl\";\n\nconst FormDescription = ({\n  className,\n  id: _id,\n  ref,\n  ...props\n}: React.ComponentPropsWithoutRef<\"p\"> & {\n  ref?: React.Ref<HTMLParagraphElement>;\n}) => {\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 = ({\n  children,\n  className,\n  id: _id,\n  ref,\n  ...props\n}: React.ComponentPropsWithoutRef<\"p\"> & {\n  ref?: React.Ref<HTMLParagraphElement>;\n}) => {\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"
    }
  ],
  "type": "registry:component",
  "version": "0.3.0",
  "stability": "stable"
}
