{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "field",
  "type": "registry:ui",
  "title": "Field",
  "description": "Label + control + help text + error, wired together.",
  "registryDependencies": [
    "https://junco.madebytobi.com/r/junco-theme.json",
    "https://junco.madebytobi.com/r/label.json",
    "https://junco.madebytobi.com/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/junco/ui/field.tsx",
      "type": "registry:ui",
      "content": "import * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { Label } from \"@/components/ui/label\";\n\ninterface FieldContextValue {\n  id: string;\n  descriptionId?: string;\n  errorId?: string;\n  isInvalid: boolean;\n  isDisabled: boolean;\n  isRequired: boolean;\n}\n\nconst FieldContext = React.createContext<FieldContextValue | null>(null);\n\ninterface ControlProps {\n  id?: string;\n  disabled?: boolean;\n  required?: boolean;\n  \"aria-describedby\"?: string;\n  \"aria-invalid\"?: React.AriaAttributes[\"aria-invalid\"];\n}\n\n/**\n * Lets a control pick up its id, description, error and state from a surrounding <Field>.\n * Props passed directly to the control win.\n */\nexport function useFieldControl<P extends ControlProps>(props: P): P {\n  const field = React.useContext(FieldContext);\n  if (!field) return props;\n  const describedBy = [props[\"aria-describedby\"], field.descriptionId, field.errorId].filter(Boolean).join(\" \");\n  return {\n    ...props,\n    id: props.id ?? field.id,\n    disabled: props.disabled ?? (field.isDisabled || undefined),\n    required: props.required ?? (field.isRequired || undefined),\n    \"aria-describedby\": describedBy || undefined,\n    \"aria-invalid\": props[\"aria-invalid\"] ?? (field.isInvalid || undefined),\n  };\n}\n\nexport interface FieldProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n  label?: React.ReactNode;\n  /** Help text under the control. */\n  description?: React.ReactNode;\n  /** Error message. Setting it marks the control invalid. */\n  error?: React.ReactNode;\n  isRequired?: boolean;\n  isDisabled?: boolean;\n  /** Id for the control. Generated when omitted. */\n  id?: string;\n  classNames?: { root?: string; label?: string; description?: string; error?: string };\n  children: React.ReactNode;\n}\n\n/**\n * Label + control + help text + error, wired together:\n *\n *   <Field label=\"Email\" description=\"Work email\" error={errors.email}><Input /></Field>\n */\nexport const Field = React.forwardRef<HTMLDivElement, FieldProps>(\n  (\n    { className, classNames, label, description, error, isRequired = false, isDisabled = false, id, children, ...props },\n    ref,\n  ) => {\n    const autoId = React.useId();\n    const controlId = id ?? `field-${autoId}`;\n    const hasError = error !== undefined && error !== null && error !== false && error !== \"\";\n    const ctx: FieldContextValue = {\n      id: controlId,\n      descriptionId: description ? `${controlId}-description` : undefined,\n      errorId: hasError ? `${controlId}-error` : undefined,\n      isInvalid: hasError,\n      isDisabled,\n      isRequired,\n    };\n\n    return (\n      <FieldContext.Provider value={ctx}>\n        <div\n          ref={ref}\n          data-invalid={hasError || undefined}\n          data-disabled={isDisabled || undefined}\n          className={cn(\"grid content-start gap-1.5\", classNames?.root, className)}\n          {...props}\n        >\n          {label && (\n            <Label id={`${controlId}-label`} htmlFor={controlId} isRequired={isRequired} className={cn(isDisabled && \"opacity-50\", classNames?.label)}>\n              {label}\n            </Label>\n          )}\n          {children}\n          {description && (\n            <p id={ctx.descriptionId} className={cn(\"text-xs text-muted-foreground\", classNames?.description)}>\n              {description}\n            </p>\n          )}\n          {hasError && (\n            <p id={ctx.errorId} aria-live=\"polite\" className={cn(\"text-xs text-destructive-text\", classNames?.error)}>\n              {error}\n            </p>\n          )}\n        </div>\n      </FieldContext.Provider>\n    );\n  },\n);\nField.displayName = \"Field\";\n"
    }
  ],
  "docs": "Docs and examples: https://junco.madebytobi.com/docs/components/field",
  "meta": {
    "hash": "e88c40768407ea0f",
    "source": "packages/react/src/components/field.tsx",
    "exports": [
      "useFieldControl",
      "FieldProps",
      "Field"
    ],
    "props": {
      "Field": {
        "extends": "Omit<React.HTMLAttributes<HTMLDivElement>, \"children\">",
        "props": [
          {
            "name": "label",
            "optional": true,
            "type": "React.ReactNode"
          },
          {
            "name": "description",
            "optional": true,
            "type": "React.ReactNode",
            "description": "Help text under the control."
          },
          {
            "name": "error",
            "optional": true,
            "type": "React.ReactNode",
            "description": "Error message. Setting it marks the control invalid."
          },
          {
            "name": "isRequired",
            "optional": true,
            "type": "boolean"
          },
          {
            "name": "isDisabled",
            "optional": true,
            "type": "boolean"
          },
          {
            "name": "id",
            "optional": true,
            "type": "string",
            "description": "Id for the control. Generated when omitted."
          },
          {
            "name": "classNames",
            "optional": true,
            "type": "{ root?: string; label?: string; description?: string; error?: string }"
          },
          {
            "name": "children",
            "optional": false,
            "type": "React.ReactNode"
          }
        ]
      }
    },
    "usage": "<Field label=\"Email\" description=\"Work email\" error={errors.email}><Input /></Field>",
    "docsUrl": "https://junco.madebytobi.com/docs/components/field"
  }
}
