{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "form",
  "title": "Form",
  "author": "SAASTRO <https://saastro.io>",
  "description": "Puente entre react-hook-form y los primitivos. FormField/FormControl.",
  "dependencies": [
    "@base-ui/react",
    "react-hook-form"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/ui/form.tsx",
      "content": "/**\n * Minimal shadcn-style form primitives — just enough surface for\n * @saastro/forms to wire react-hook-form fields. Standard shadcn ships\n * this file via `shadcn add form`, but at the time of writing the\n * registry doesn't expose it under that name from this project's\n * `components.json`. Hand-rolling it keeps the bundle predictable.\n *\n * Exposes:\n *   - Form: el `FormProvider` de react-hook-form, re-exportado con el nombre\n *     que usa shadcn. No es un primitivo de Base UI y no lo pretende: es el\n *     contexto que `FormField` necesita para encontrar el formulario. Faltaba,\n *     y sin él los consumidores tienen que importarlo de react-hook-form por su\n *     cuenta, que es justo la dependencia que este fichero existe para tapar.\n *   - FormField: Controller wrapper that exposes `field` props to children\n *   - FormControl: reenvía id/aria-* al input subyacente, y pone\n *     `aria-invalid` cuando el FormField que lo envuelve tiene error. Con Radix esto era\n *     `Slot`; en Base UI el equivalente es el hook `useRender`, que fusiona las\n *     props sobre el hijo. Se mantiene hecho a mano A PROPÓSITO: el `FormControl`\n *     oficial de shadcn usa `useFormField()` y exige un `FormItem` alrededor,\n *     que @saastro/forms no monta — usarlo reventaría en runtime.\n *     NO regenerar este fichero con `shadcn add form`.\n *\n * The remaining named primitives @saastro/forms looks up (Field,\n * FieldLabel, FieldDescription, FieldError) come from the standard\n * shadcn `field.tsx`.\n */\nimport * as React from \"react\";\nimport {\n  Controller,\n  FormProvider,\n  type ControllerProps,\n  type FieldPath,\n  type FieldValues,\n} from \"react-hook-form\";\nimport { useRender } from \"@base-ui/react/use-render\";\n\n/**\n * EL ESTADO DEL CAMPO, PARA QUE FormControl PUEDA PONER `aria-invalid`.\n *\n * Los inputs pintan el borde rojo con `aria-invalid:border-destructive`, y el\n * único que sabe si el campo tiene error es el `Controller`. El FormControl de\n * shadcn lo saca de `useFormField()`, que exige un `FormItem` alrededor y que\n * @saastro/forms no monta; aquí `FormField` lo publica en un contexto propio\n * y `FormControl` lo lee. Fuera de un `FormField`, el contexto no está y\n * `FormControl` se queda como antes: no pone nada.\n */\nconst FormFieldContext = React.createContext<{ invalid: boolean } | null>(null);\n\n/** El contexto de react-hook-form, con el nombre que espera shadcn. */\nexport const Form = FormProvider;\n\n/**\n * EL SEGUNDO GENÉRICO NO ES ADORNO: SIN ÉL, `field.value` MIENTE.\n *\n * Faltaba `TName`, y sin él `ControllerProps` no puede estrechar por el `name=`\n * de cada campo: `field.value` sale como la UNIÓN de todos los campos del\n * formulario. En un diálogo que mezcla textos con un booleano, eso es\n * `string | boolean`, y el error aparece lejos de aquí — en el `value` del\n * `Input`, del `Select` y del `Switch`, ocho a la vez y todos en el fichero del\n * consumidor. Medido: con el genérico, cero.\n *\n * Es la firma que trae el shadcn oficial.\n */\nexport function FormField<\n  TFieldValues extends FieldValues = FieldValues,\n  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({ render, ...props }: ControllerProps<TFieldValues, TName>) {\n  return (\n    <Controller\n      {...props}\n      render={(args) => (\n        <FormFieldContext.Provider value={{ invalid: !!args.fieldState.error }}>\n          {render(args)}\n        </FormFieldContext.Provider>\n      )}\n    />\n  );\n}\n\nexport interface FormControlProps extends React.ComponentProps<\"div\"> {\n  children?: React.ReactElement;\n}\n\nexport const FormControl = React.forwardRef<HTMLElement, FormControlProps>(\n  function FormControl({ children, ...props }, ref) {\n    const campo = React.useContext(FormFieldContext);\n    return useRender({\n      render: children as React.ReactElement,\n      // Primero el de FormField; un aria-invalid en FormControl lo pisa, y uno\n      // puesto en el propio hijo gana a los dos (useRender fusiona el hijo último).\n      props: {\n        ...(campo?.invalid ? { \"aria-invalid\": true } : null),\n        ...props,\n      },\n      ref: ref as React.Ref<HTMLElement>,\n    });\n  },\n);\n\nexport const FormItem = React.forwardRef<\n  HTMLDivElement,\n  React.ComponentProps<\"div\">\n>(function FormItem({ className, ...props }, ref) {\n  return <div ref={ref} className={className} {...props} />;\n});\n\nexport const FormLabel = React.forwardRef<\n  HTMLLabelElement,\n  React.ComponentProps<\"label\">\n>(function FormLabel({ className, ...props }, ref) {\n  return <label ref={ref} className={className} {...props} />;\n});\n\nexport const FormMessage = React.forwardRef<\n  HTMLParagraphElement,\n  React.ComponentProps<\"p\">\n>(function FormMessage({ className, children, ...props }, ref) {\n  if (!children) return null;\n  return (\n    <p ref={ref} className={className} {...props}>\n      {children}\n    </p>\n  );\n});\n\nexport const FormDescription = FormMessage;\n",
      "type": "registry:ui",
      "target": "@ui/form.tsx"
    }
  ],
  "meta": {
    "group": "Forms",
    "iframeHeight": "480px"
  },
  "docs": "Primitivo sobre Base UI. Se usa `render={<a/>}`, no `asChild`. OJO: `render` NO funciona dentro de un .astro — un formulario con validación va en isla con @saastro/forms, nunca en .astro.",
  "categories": [
    "ui",
    "forms"
  ],
  "type": "registry:ui"
}