{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "field",
  "title": "Field",
  "description": "Shared form field coordinator for label, hint, marker-flagged error alert, required indicator, density, and aria-describedby/aria-invalid context.",
  "registryDependencies": [
    "https://frame12.dev/r/frame12-styles.json"
  ],
  "files": [
    {
      "path": "ui/field.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\n\nexport type FieldDensity = 'comfortable' | 'compact';\n\nexport interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {\n  required?: boolean;\n}\n\nexport const Label = React.forwardRef<HTMLLabelElement, LabelProps>(function Label(\n  { required, className = '', children, ...rest },\n  ref,\n) {\n  return (\n    <label ref={ref} className={`f12-label${className ? ` ${className}` : ''}`} {...rest}>\n      {children}\n      {required && (\n        <span className=\"f12-field-req\" aria-hidden=\"true\">\n          {' '}\n          *\n        </span>\n      )}\n    </label>\n  );\n});\n\nexport interface FieldContextValue {\n  id: string;\n  label?: React.ReactNode;\n  labelId?: string;\n  hintId?: string;\n  errorId?: string;\n  describedBy?: string;\n  invalid?: boolean;\n  disabled?: boolean;\n  required?: boolean;\n  density: FieldDensity;\n  isRadioGroup?: boolean;\n  isControlGroup?: boolean;\n  registerRadioGroup?: () => void;\n  registerControlGroup?: () => void;\n  registerControlId?: (instanceId: string, explicitId?: string) => () => void;\n}\n\nconst FIELD_CTX_KEY = Symbol.for('frame12.FieldContext');\nconst globalFieldRegistry = globalThis as unknown as Record<\n  symbol,\n  React.Context<FieldContextValue | null> | undefined\n>;\nconst FieldContext = (globalFieldRegistry[FIELD_CTX_KEY] ??=\n  React.createContext<FieldContextValue | null>(null));\n\nexport function useFieldContext(): FieldContextValue | null {\n  return React.useContext(FieldContext);\n}\n\ninterface SubtreeInspection {\n  hasRadioGroup: boolean;\n  hasLabeledCheckOrSwitch: boolean;\n  controlCount: number;\n  firstControlId?: string;\n}\n\nfunction inspectFieldSubtree(node: React.ReactNode): SubtreeInspection {\n  const result: SubtreeInspection = {\n    hasRadioGroup: false,\n    hasLabeledCheckOrSwitch: false,\n    controlCount: 0,\n    firstControlId: undefined,\n  };\n\n  const walk = (n: React.ReactNode) => {\n    React.Children.forEach(n, (child) => {\n      if (!React.isValidElement<Record<string, unknown>>(child)) return;\n\n      if (child.type === React.Fragment) {\n        walk(child.props.children as React.ReactNode);\n        return;\n      }\n\n      const fnType = child.type as {\n        f12IsGroup?: boolean;\n        f12IsCheckOrSwitch?: boolean;\n        f12IsControl?: boolean;\n      };\n\n      const isNativeControl =\n        typeof child.type === 'string' &&\n        (child.type === 'input' || child.type === 'textarea' || child.type === 'select');\n\n      const isRadioGroup = Boolean(fnType && fnType.f12IsGroup);\n      const isCheckOrSwitch = Boolean(fnType && fnType.f12IsCheckOrSwitch);\n      const isFrame12Control = Boolean(fnType && fnType.f12IsControl);\n\n      if (isRadioGroup) {\n        result.hasRadioGroup = true;\n        result.controlCount += 1;\n        if (\n          !result.firstControlId &&\n          typeof child.props.id === 'string' &&\n          child.props.id.length > 0\n        ) {\n          result.firstControlId = child.props.id;\n        }\n        return;\n      }\n\n      if (isCheckOrSwitch || isFrame12Control || isNativeControl) {\n        result.controlCount += 1;\n        if (isCheckOrSwitch && Boolean(child.props.label)) {\n          result.hasLabeledCheckOrSwitch = true;\n        }\n        if (\n          !result.firstControlId &&\n          typeof child.props.id === 'string' &&\n          child.props.id.length > 0\n        ) {\n          result.firstControlId = child.props.id;\n        } else if (!result.firstControlId && isFrame12Control && child.props.children) {\n          const findTriggerId = (sub: React.ReactNode) => {\n            React.Children.forEach(sub, (sc) => {\n              if (result.firstControlId || !React.isValidElement<Record<string, unknown>>(sc)) {\n                return;\n              }\n              const scType = sc.type as { f12IsSelectTrigger?: boolean };\n              if (\n                scType?.f12IsSelectTrigger &&\n                typeof sc.props.id === 'string' &&\n                sc.props.id.length > 0\n              ) {\n                result.firstControlId = sc.props.id;\n                return;\n              }\n              if (sc.props?.children) {\n                findTriggerId(sc.props.children as React.ReactNode);\n              }\n            });\n          };\n          findTriggerId(child.props.children as React.ReactNode);\n        }\n        return;\n      }\n\n      if (child.props && child.props.children) {\n        walk(child.props.children as React.ReactNode);\n      }\n    });\n  };\n\n  walk(node);\n  return result;\n}\n\nexport interface FieldProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n  id?: string;\n  htmlFor?: string;\n  label?: React.ReactNode;\n  hint?: React.ReactNode;\n  error?: React.ReactNode;\n  density?: FieldDensity;\n  required?: boolean;\n  disabled?: boolean;\n  children?: React.ReactNode | ((field: FieldContextValue) => React.ReactNode);\n}\n\nexport const Field = React.forwardRef<HTMLDivElement, FieldProps>(function Field(\n  {\n    id,\n    htmlFor,\n    label,\n    hint,\n    error,\n    density = 'comfortable',\n    required,\n    disabled,\n    className = '',\n    children,\n    ...rest\n  },\n  ref,\n) {\n  const autoId = React.useId().replace(/:/g, '');\n  const [registeredRadioGroup, setRegisteredRadioGroup] = React.useState(false);\n  const [registeredControlGroup, setRegisteredControlGroup] = React.useState(false);\n  const [registeredMultiControl, setRegisteredMultiControl] = React.useState(false);\n  const [registeredControlId, setRegisteredControlId] = React.useState<string | undefined>(\n    undefined,\n  );\n  const registeredInstancesRef = React.useRef<Map<string, string | undefined>>(new Map());\n\n  const registerRadioGroup = React.useCallback(() => {\n    setRegisteredRadioGroup((prev) => (prev ? prev : true));\n  }, []);\n\n  const registerControlGroup = React.useCallback(() => {\n    setRegisteredControlGroup((prev) => (prev ? prev : true));\n  }, []);\n\n  const registerControlId = React.useCallback(\n    (instanceId: string, explicitId?: string) => {\n      if (!instanceId) return () => {};\n      registeredInstancesRef.current.set(instanceId, explicitId);\n      if (registeredInstancesRef.current.size > 1) {\n        setRegisteredControlGroup((prev) => (prev ? prev : true));\n        setRegisteredMultiControl((prev) => (prev ? prev : true));\n      }\n      if (explicitId) {\n        setRegisteredControlId((prev) => prev ?? explicitId);\n      }\n      return () => {\n        registeredInstancesRef.current.delete(instanceId);\n        if (registeredInstancesRef.current.size <= 1) {\n          setRegisteredMultiControl(false);\n        }\n      };\n    },\n    [],\n  );\n\n  const fallbackId = `f12-field-${autoId}`;\n  const prelimId = htmlFor || registeredControlId || id || fallbackId;\n  const prelimLabelId = label ? `${prelimId}-label` : undefined;\n  const prelimHintId = hint ? `${prelimId}-hint` : undefined;\n  const prelimErrorId = error ? `${prelimId}-error` : undefined;\n  const prelimDescribedBy =\n    [prelimHintId, prelimErrorId].filter(Boolean).join(' ') || undefined;\n\n  const prelimContext: FieldContextValue = {\n    id: prelimId,\n    label,\n    labelId: prelimLabelId,\n    hintId: prelimHintId,\n    errorId: prelimErrorId,\n    describedBy: prelimDescribedBy,\n    invalid: Boolean(error),\n    disabled,\n    required: registeredMultiControl ? undefined : required,\n    density,\n    isRadioGroup: registeredRadioGroup,\n    isControlGroup: registeredControlGroup,\n    registerRadioGroup,\n    registerControlGroup,\n    registerControlId,\n  };\n\n  const initialChildren =\n    typeof children === 'function' ? children(prelimContext) : children;\n  const inspected = inspectFieldSubtree(initialChildren);\n\n  const isRadioGroup = inspected.hasRadioGroup || registeredRadioGroup;\n  const isMultiControlGroup =\n    !isRadioGroup && (inspected.controlCount > 1 || registeredMultiControl);\n  const isControlGroup =\n    !isRadioGroup &&\n    (inspected.hasLabeledCheckOrSwitch ||\n      inspected.controlCount > 1 ||\n      registeredControlGroup);\n\n  const controlId =\n    htmlFor || inspected.firstControlId || registeredControlId || id || fallbackId;\n  const wrapperId = id && id !== controlId ? id : isControlGroup && id ? id : undefined;\n  const metaBaseId = id || controlId;\n  const labelId = label ? `${metaBaseId}-label` : undefined;\n  const hintId = hint ? `${metaBaseId}-hint` : undefined;\n  const errorId = error ? `${metaBaseId}-error` : undefined;\n  const describedBy = [hintId, errorId].filter(Boolean).join(' ') || undefined;\n  const isCompact = density === 'compact';\n\n  const contextValue = React.useMemo<FieldContextValue>(\n    () => ({\n      id: controlId,\n      label,\n      labelId,\n      hintId,\n      errorId,\n      describedBy,\n      invalid: Boolean(error),\n      disabled,\n      required: isMultiControlGroup ? undefined : required,\n      density,\n      isRadioGroup,\n      isControlGroup,\n      registerRadioGroup,\n      registerControlGroup,\n      registerControlId,\n    }),\n    [\n      controlId,\n      label,\n      labelId,\n      hintId,\n      errorId,\n      describedBy,\n      error,\n      disabled,\n      required,\n      density,\n      isRadioGroup,\n      isControlGroup,\n      isMultiControlGroup,\n      registerRadioGroup,\n      registerControlGroup,\n      registerControlId,\n    ],\n  );\n\n  const resolvedChildren =\n    typeof children === 'function'\n      ? controlId !== prelimId ||\n        labelId !== prelimLabelId ||\n        hintId !== prelimHintId ||\n        errorId !== prelimErrorId ||\n        isRadioGroup !== registeredRadioGroup ||\n        isControlGroup !== registeredControlGroup ||\n        contextValue.required !== prelimContext.required\n        ? children(contextValue)\n        : initialChildren\n      : initialChildren;\n\n  const childElement = React.isValidElement<Record<string, unknown>>(resolvedChildren)\n    ? resolvedChildren\n    : null;\n\n  let renderedChildren: React.ReactNode;\n  if (\n    childElement &&\n    typeof childElement.type === 'string' &&\n    (childElement.type === 'input' ||\n      childElement.type === 'textarea' ||\n      childElement.type === 'select')\n  ) {\n    const childProps = childElement.props;\n    const mergedDescribedBy =\n      [childProps['aria-describedby'] as string | undefined, describedBy]\n        .filter(Boolean)\n        .join(' ') || undefined;\n    const existingClass =\n      typeof childProps.className === 'string' ? childProps.className : '';\n    const inputType =\n      childElement.type === 'input'\n        ? ((childProps.type as string | undefined) ?? 'text')\n        : '';\n    let defaultControlClass = '';\n    if (childElement.type === 'textarea') {\n      defaultControlClass = `f12-textarea${isCompact ? ' f12-textarea-compact' : ''}`;\n    } else if (childElement.type === 'select') {\n      defaultControlClass = `f12-select-trigger${isCompact ? ' f12-input-compact' : ''}`;\n    } else if (inputType === 'checkbox') {\n      defaultControlClass = `f12-checkbox-input${isCompact ? ' f12-checkbox-compact' : ''}`;\n    } else if (inputType === 'radio') {\n      defaultControlClass = `f12-radio-input${isCompact ? ' f12-radio-compact' : ''}`;\n    } else if (inputType !== 'hidden') {\n      defaultControlClass = `f12-input${isCompact ? ' f12-input-compact' : ''}`;\n    }\n    const mergedClassName =\n      [defaultControlClass, existingClass].filter(Boolean).join(' ') || undefined;\n\n    renderedChildren = React.cloneElement(childElement, {\n      id: (childProps.id as string | undefined) || controlId,\n      className: mergedClassName,\n      'aria-describedby': mergedDescribedBy,\n      'aria-invalid':\n        (childProps['aria-invalid'] as boolean | 'true' | 'false' | undefined) ??\n        (error ? true : undefined),\n      disabled: (childProps.disabled as boolean | undefined) ?? disabled,\n      required: (childProps.required as boolean | undefined) ?? required,\n    });\n  } else {\n    renderedChildren = resolvedChildren;\n  }\n\n  return (\n    <FieldContext.Provider value={contextValue}>\n      <div\n        ref={ref}\n        id={wrapperId}\n        role={isControlGroup ? 'group' : undefined}\n        aria-labelledby={isControlGroup && labelId ? labelId : undefined}\n        aria-describedby={isControlGroup ? describedBy : undefined}\n        className={`f12-field${isCompact ? ' f12-field-compact' : ''}${className ? ` ${className}` : ''}`}\n        data-density={isCompact ? 'compact' : undefined}\n        data-disabled={disabled ? '' : undefined}\n        {...rest}\n      >\n        {label && !isRadioGroup && (\n          isControlGroup ? (\n            <div id={labelId} className=\"f12-label\">\n              {label}\n              {required && (\n                <span className=\"f12-field-req\" aria-hidden=\"true\">\n                  {' '}\n                  *\n                </span>\n              )}\n            </div>\n          ) : (\n            <label id={labelId} htmlFor={controlId} className=\"f12-label\">\n              {label}\n              {required && (\n                <span className=\"f12-field-req\" aria-hidden=\"true\">\n                  {' '}\n                  *\n                </span>\n              )}\n            </label>\n          )\n        )}\n        {renderedChildren}\n        {hint && (\n          <p id={hintId} className=\"f12-hint\">\n            {hint}\n          </p>\n        )}\n        {error && (\n          <p id={errorId} className=\"f12-error\" role=\"alert\">\n            <span className=\"f12-error-flag\" aria-hidden=\"true\">\n              !\n            </span>\n            <span>{error}</span>\n          </p>\n        )}\n      </div>\n    </FieldContext.Provider>\n  );\n});\n",
      "type": "registry:ui",
      "target": "components/frame12/field.tsx"
    }
  ],
  "type": "registry:ui"
}