{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "radio",
  "title": "RadioField",
  "description": "Radio group field for BuzzForm v2.",
  "registryDependencies": [
    "@buzzform/init",
    "radio-group",
    "field"
  ],
  "files": [
    {
      "path": "registry/shadcn/fields/radio.tsx",
      "content": "\"use client\";\n\nimport type { RadioField as RadioFieldDef } from \"@buildnbuzz/form-react\";\nimport { useDataField, useFieldOptions } from \"@buildnbuzz/form-react\";\nimport { RadioGroup, RadioGroupItem } from \"@/components/ui/radio-group\";\nimport {\n  Field,\n  FieldContent,\n  FieldDescription,\n  FieldError,\n  FieldGroup,\n  FieldLabel,\n} from \"@/components/ui/field\";\nimport { cn } from \"@/lib/utils\";\n\ninterface OptionUi {\n  description?: string;\n}\n\ntype OptionGroupVariant = \"default\" | \"card\";\ntype OptionGroupDirection = \"vertical\" | \"horizontal\";\ntype OptionGroupColumns = 1 | 2 | 3 | 4 | string | number | undefined;\n\ninterface RadioUi {\n  variant?: OptionGroupVariant;\n  direction?: OptionGroupDirection;\n  columns?: OptionGroupColumns;\n  card?: {\n    size?: \"sm\" | \"md\" | \"lg\";\n    bordered?: boolean;\n  };\n  autoFocus?: boolean;\n  className?: string;\n  width?: string | number;\n  asterisk?: boolean;\n}\n\nfunction getGridColumnsClass(columns: OptionGroupColumns | undefined) {\n  if (columns === 2) return \"sm:grid-cols-2\";\n  if (columns === 3) return \"sm:grid-cols-2 md:grid-cols-3\";\n  if (columns === 4) return \"sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4\";\n  return undefined;\n}\n\nfunction getOptionGroupLayoutClassName({\n  variant = \"default\",\n  direction = \"vertical\",\n  columns,\n}: {\n  variant?: OptionGroupVariant;\n  direction?: OptionGroupDirection;\n  columns?: OptionGroupColumns;\n}) {\n  const isCard = variant === \"card\";\n  const isHorizontal = direction === \"horizontal\";\n\n  // If horizontal and NO columns chosen, use fluid flex-row layout\n  if (isHorizontal && !columns) {\n    return \"!flex flex-row flex-wrap gap-x-4 gap-y-2\";\n  }\n\n  const effectiveColumns =\n    isCard || isHorizontal ? (columns ?? (isCard ? 2 : undefined)) : undefined;\n\n  if (!effectiveColumns || effectiveColumns === 1) {\n    return \"flex flex-col gap-2\";\n  }\n\n  return cn(\"grid gap-2\", getGridColumnsClass(effectiveColumns));\n}\n\n/** Card size classes */\nconst cardSizeClasses = {\n  sm: \"p-2.5 sm:p-3\",\n  md: \"p-3 sm:p-4\",\n  lg: \"p-4 sm:p-5\",\n} as const;\n\nexport function RadioField() {\n  const {\n    fieldApi,\n    field,\n    isDisabled,\n    isReadOnly,\n    isRequired,\n    label,\n    description,\n    errors,\n    isInvalid,\n    descriptionId,\n    errorId,\n    ariaDescribedBy,\n  } = useDataField<RadioFieldDef>();\n\n  const value = (fieldApi.state.value as string) ?? \"\";\n  const ui = field.ui as RadioUi | undefined;\n\n  const width = ui?.width;\n  const widthStyle = width\n    ? {\n        width: typeof width === \"number\" ? `${width}px` : width,\n        flex: \"0 1 auto\",\n      }\n    : undefined;\n\n  // UI options with defaults\n  const variant = ui?.variant ?? \"default\";\n  const direction = ui?.direction ?? \"vertical\";\n  const columns = ui?.columns;\n  const cardSize = ui?.card?.size ?? \"md\";\n  const cardBordered = ui?.card?.bordered ?? true;\n\n  const isCardVariant = variant === \"card\";\n  const isHorizontal = direction === \"horizontal\";\n\n  const layoutClasses = getOptionGroupLayoutClassName({\n    variant,\n    direction,\n    columns,\n  });\n\n  // Get options\n  const { options, isLoading } = useFieldOptions(field.options);\n\n  return (\n    <FieldGroup\n      data-field={fieldApi.name}\n      className={ui?.className}\n      style={widthStyle}\n    >\n      <Field data-invalid={isInvalid} data-disabled={isDisabled}>\n        {label && (\n          <FieldLabel htmlFor={`${fieldApi.name}-0`}>\n            {label}\n            {isRequired && ui?.asterisk !== false && (\n              <span className=\"text-destructive ml-1\">*</span>\n            )}\n          </FieldLabel>\n        )}\n\n        {description && !isInvalid && (\n          <FieldDescription id={descriptionId} className=\"mb-2\">\n            {description}\n          </FieldDescription>\n        )}\n\n        <FieldContent>\n          <RadioGroup\n            value={value}\n            onValueChange={(val) => {\n              if (isReadOnly) return;\n              fieldApi.handleChange(val);\n              fieldApi.handleBlur();\n            }}\n            disabled={isDisabled}\n            className={layoutClasses}\n            aria-describedby={ariaDescribedBy}\n          >\n            {isLoading ? (\n              <div className=\"py-2 text-sm text-muted-foreground animate-pulse\">\n                Loading options...\n              </div>\n            ) : (\n              options.map((opt, i) => {\n                const {\n                  value: val,\n                  label: optLabel,\n                  disabled: optDisabled,\n                  ui: optUi,\n                } = opt;\n                const optDesc = (optUi as OptionUi | undefined)?.description;\n                const isOptDisabled = optDisabled || isDisabled;\n                const isSelected = value === val;\n                const id = `${fieldApi.name}-${i}`;\n\n                // Card variant\n                if (isCardVariant) {\n                  const showDescription = optDesc && cardSize !== \"sm\";\n\n                  return (\n                    <label\n                      key={`${val}-${i}`}\n                      htmlFor={id}\n                      className={cn(\n                        \"cursor-pointer transition-all duration-150 rounded-lg flex items-start gap-3\",\n                        cardSizeClasses[cardSize],\n                        cardBordered && \"border\",\n                        cardBordered &&\n                          isSelected &&\n                          \"border-primary ring-1 ring-primary/20 bg-primary/5 dark:bg-primary/10\",\n                        cardBordered &&\n                          !isSelected &&\n                          \"border-border hover:border-foreground/20 hover:bg-accent/30\",\n                        !cardBordered && \"hover:bg-accent/50\",\n                        !cardBordered && isSelected && \"bg-accent\",\n                        isOptDisabled &&\n                          \"opacity-50 cursor-not-allowed pointer-events-none\",\n                      )}\n                      data-checked={isSelected}\n                      data-disabled={isOptDisabled}\n                    >\n                      <RadioGroupItem\n                        value={val}\n                        id={id}\n                        disabled={isOptDisabled}\n                        className=\"shrink-0 mt-0.5\"\n                        autoFocus={ui?.autoFocus && i === 0}\n                      />\n                      <div className=\"flex flex-col gap-0.5 min-w-0 flex-1\">\n                        <div className=\"flex items-center gap-2\">\n                          <span\n                            className={cn(\n                              \"font-medium\",\n                              cardSize === \"lg\" ? \"text-base\" : \"text-sm\",\n                            )}\n                          >\n                            {optLabel}\n                          </span>\n                        </div>\n                        {showDescription && (\n                          <span\n                            className={cn(\n                              \"text-muted-foreground line-clamp-2\",\n                              cardSize === \"lg\" ? \"text-sm\" : \"text-xs\",\n                            )}\n                          >\n                            {optDesc}\n                          </span>\n                        )}\n                      </div>\n                    </label>\n                  );\n                }\n\n                // Default variant\n                return (\n                  <Field\n                    key={`${val}-${i}`}\n                    orientation=\"horizontal\"\n                    className={cn(\n                      \"items-center gap-2.5 space-y-0\",\n                      isHorizontal && !columns && \"w-auto\",\n                      isOptDisabled && \"opacity-50 cursor-not-allowed\",\n                    )}\n                  >\n                    <RadioGroupItem\n                      value={val}\n                      id={id}\n                      disabled={isOptDisabled}\n                      autoFocus={ui?.autoFocus && i === 0}\n                    />\n                    <FieldLabel\n                      htmlFor={id}\n                      className={cn(\n                        \"font-normal cursor-pointer m-0 text-sm\",\n                        isOptDisabled && \"cursor-not-allowed\",\n                      )}\n                    >\n                      <span>{optLabel}</span>\n                    </FieldLabel>\n                  </Field>\n                );\n              })\n            )}\n\n            {options.length === 0 && !isLoading && (\n              <div className=\"py-2 text-sm text-muted-foreground italic\">\n                No options available\n              </div>\n            )}\n          </RadioGroup>\n        </FieldContent>\n\n        {isInvalid && <FieldError id={errorId} errors={errors} />}\n      </Field>\n    </FieldGroup>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/buzzform/fields/radio.tsx"
    }
  ],
  "type": "registry:ui"
}