{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "field",
  "title": "Field",
  "description": "Form field composition: Field, FieldControl, FieldLabel, FieldDescription, FieldError. Auto-wires accessibility: FieldControl (a Slot around any control) receives a generated id, aria-describedby pointing at the description and error, and aria-invalid; the label targets the control automatically. invalid/disabled props drive data attributes, label color and dimming. orientation switches vertical (default) and horizontal (label-left, e.g. switch rows).",
  "registryDependencies": [
    "@ai2/label",
    "@ai2/tokens",
    "@ai2/primitives"
  ],
  "files": [
    {
      "path": "registry/ai2/ui/field.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Slot } from \"@/registry/ai2/ui/primitives\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Label } from \"@/registry/ai2/ui/label\"\n\ninterface FieldContextValue {\n  controlId: string\n  descriptionId: string\n  errorId: string\n  invalid: boolean\n  hasDescription: boolean\n  hasError: boolean\n  registerDescription: (present: boolean) => void\n  registerError: (present: boolean) => void\n}\n\nconst FieldContext = React.createContext<FieldContextValue | null>(null)\n\nfunction useFieldContext() {\n  return React.useContext(FieldContext)\n}\n\ninterface FieldProps extends React.ComponentProps<\"div\"> {\n  orientation?: \"vertical\" | \"horizontal\"\n  /** Marks the whole field invalid: control gets aria-invalid, label turns danger. */\n  invalid?: boolean\n  /** Dims the label via the group chain; does not disable the control itself. */\n  disabled?: boolean\n}\n\nfunction Field({\n  className,\n  orientation = \"vertical\",\n  invalid = false,\n  disabled = false,\n  ...props\n}: FieldProps) {\n  const uid = React.useId()\n  const [hasDescription, setHasDescription] = React.useState(false)\n  const [hasError, setHasError] = React.useState(false)\n  const isInvalid = invalid || hasError\n\n  const ctx = React.useMemo<FieldContextValue>(\n    () => ({\n      controlId: `${uid}-control`,\n      descriptionId: `${uid}-description`,\n      errorId: `${uid}-error`,\n      invalid: isInvalid,\n      hasDescription,\n      hasError,\n      registerDescription: setHasDescription,\n      registerError: setHasError,\n    }),\n    [uid, isInvalid, hasDescription, hasError]\n  )\n\n  return (\n    <FieldContext.Provider value={ctx}>\n      <div\n        data-slot=\"field\"\n        data-orientation={orientation}\n        data-invalid={isInvalid ? \"true\" : undefined}\n        data-disabled={disabled ? \"true\" : undefined}\n        className={cn(\n          \"group flex gap-2\",\n          orientation === \"horizontal\"\n            ? \"flex-row items-center justify-between\"\n            : \"flex-col\",\n          className\n        )}\n        {...props}\n      />\n    </FieldContext.Provider>\n  )\n}\n\n/** Wraps the form control (Input, Select trigger, Switch...) and wires it to\n *  the field: id for the label, aria-describedby to description/error,\n *  aria-invalid when the field is invalid. Renders no extra DOM (Slot). */\nfunction FieldControl({\n  ...props\n}: React.ComponentProps<typeof Slot.Root>) {\n  const ctx = useFieldContext()\n  const describedBy =\n    [\n      ctx?.hasDescription ? ctx.descriptionId : null,\n      ctx?.hasError ? ctx.errorId : null,\n    ]\n      .filter(Boolean)\n      .join(\" \") || undefined\n\n  return (\n    <Slot.Root\n      data-slot=\"field-control\"\n      id={ctx?.controlId}\n      aria-describedby={describedBy}\n      aria-invalid={ctx?.invalid || undefined}\n      {...props}\n    />\n  )\n}\n\nfunction FieldLabel({\n  className,\n  htmlFor,\n  ...props\n}: React.ComponentProps<typeof Label>) {\n  const ctx = useFieldContext()\n  return (\n    <Label\n      data-slot=\"field-label\"\n      htmlFor={htmlFor ?? ctx?.controlId}\n      className={cn(\"gap-1 group-data-[invalid=true]:text-danger\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction FieldDescription({\n  className,\n  ...props\n}: React.ComponentProps<\"p\">) {\n  const ctx = useFieldContext()\n  const register = ctx?.registerDescription\n  React.useEffect(() => {\n    register?.(true)\n    return () => register?.(false)\n  }, [register])\n  return (\n    <p\n      data-slot=\"field-description\"\n      id={ctx?.descriptionId}\n      className={cn(\"text-sm text-muted-foreground\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction FieldError({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<\"p\">) {\n  const ctx = useFieldContext()\n  const register = ctx?.registerError\n  const present = Boolean(children)\n  React.useEffect(() => {\n    register?.(present)\n    return () => register?.(false)\n  }, [register, present])\n  if (!children) return null\n  return (\n    <p\n      data-slot=\"field-error\"\n      id={ctx?.errorId}\n      role=\"alert\"\n      className={cn(\"text-sm font-medium text-danger\", className)}\n      {...props}\n    >\n      {children}\n    </p>\n  )\n}\n\nexport {\n  Field,\n  FieldControl,\n  FieldLabel,\n  FieldDescription,\n  FieldError,\n  type FieldProps,\n}\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Wrap the control in FieldControl to get id/aria-describedby/aria-invalid wiring for free. FieldError renders nothing when empty, so you can bind it directly to a form library's error string; a present error marks the whole field invalid.",
  "categories": [
    "form"
  ],
  "type": "registry:ui"
}