{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "group",
  "title": "GroupField",
  "description": "Group field for BuzzForm v2 with card/collapsible variants.",
  "registryDependencies": [
    "@buzzform/init",
    "card",
    "collapsible",
    "badge"
  ],
  "files": [
    {
      "path": "registry/shadcn/fields/group.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport type { GroupField as GroupFieldDef } from \"@buildnbuzz/form-react\";\nimport { useDataField, useNestedErrorCount } from \"@buildnbuzz/form-react\";\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/ui/collapsible\";\nimport { Card, CardContent, CardHeader } from \"@/components/ui/card\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { IconPlaceholder } from \"@/components/icon-placeholder\";\nimport {\n  Field,\n  FieldDescription,\n  FieldError,\n  FieldGroup,\n  FieldLegend,\n  FieldSet,\n} from \"@/components/ui/field\";\nimport { cn } from \"@/lib/utils\";\n\n// ---------------------------------------------------------------------------\n// UI options\n// ---------------------------------------------------------------------------\n\ninterface GroupUi {\n  /** Visual variant. Defaults to `\"card\"`. */\n  variant?: \"card\" | \"bordered\" | \"ghost\" | \"flat\";\n  /** Vertical spacing between nested fields. Defaults to `\"md\"`. */\n  spacing?: \"sm\" | \"md\" | \"lg\";\n  /** Start collapsed (card and bordered variants only). Defaults to `false`. */\n  collapsed?: boolean;\n  /** Show an error-count badge next to the label. Defaults to `true`. */\n  showErrorBadge?: boolean;\n  /** Description rendered below the label. */\n  description?: string;\n  /** Controls required asterisk visibility. */\n  asterisk?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst spacingClass: Record<NonNullable<GroupUi[\"spacing\"]>, string> = {\n  sm: \"space-y-3\",\n  md: \"space-y-4\",\n  lg: \"space-y-6\",\n};\n\nfunction TriggerChevron({ open }: { open: boolean }) {\n  return (\n    <IconPlaceholder\n      lucide=\"ChevronDown\"\n      hugeicons=\"ArrowDown01Icon\"\n      tabler=\"IconChevronDown\"\n      phosphor=\"CaretDown\"\n      remixicon=\"RiArrowDownSLine\"\n      className={cn(\n        \"size-3.5 text-muted-foreground transition-transform duration-200 shrink-0 ml-2\",\n        !open && \"-rotate-90\",\n      )}\n    />\n  );\n}\n\n// ---------------------------------------------------------------------------\n// GroupField\n// ---------------------------------------------------------------------------\n\nexport function GroupField({ children }: { children?: React.ReactNode }) {\n  const {\n    fieldApi,\n    field,\n    isDisabled,\n    isRequired,\n    label,\n    description: resolvedDescription,\n    errors,\n    isInvalid,\n    descriptionId,\n    errorId,\n  } = useDataField<GroupFieldDef>();\n\n  const ui = field.ui as GroupUi | undefined;\n  const variant = ui?.variant ?? \"card\";\n  const spacing = ui?.spacing ?? \"md\";\n  const showErrorBadge = ui?.showErrorBadge !== false;\n  const description = ui?.description ?? resolvedDescription;\n\n  const [isOpen, setIsOpen] = React.useState(!(ui?.collapsed ?? false));\n\n  // Error count scoped to this group's nested fields\n  const errorCount = useNestedErrorCount(field.fields, fieldApi.name);\n\n  // Inline label+badge used inside trigger buttons (phrasing content only)\n  const triggerLabelContent = label ? (\n    <>\n      <span\n        className={cn(\n          \"text-sm truncate\",\n          variant === \"card\" ? \"font-semibold\" : \"font-medium\",\n        )}\n      >\n        {label}\n      </span>\n      {showErrorBadge && errorCount > 0 && (\n        <Badge variant=\"destructive\" className=\"h-5 px-1.5 text-xs\">\n          {errorCount}\n        </Badge>\n      )}\n    </>\n  ) : null;\n\n  const descriptionNode = description ? (\n    <FieldDescription id={descriptionId}>{description}</FieldDescription>\n  ) : null;\n\n  const errorNode = isInvalid ? (\n    <FieldError id={errorId} errors={errors} />\n  ) : null;\n\n  // ── FLAT ───────────────────────────────────────────────────────────────────\n  if (variant === \"flat\") {\n    return (\n      <FieldGroup data-field={fieldApi.name}>\n        <Field data-invalid={isInvalid} data-disabled={isDisabled}>\n          <FieldSet disabled={isDisabled} className={spacingClass[spacing]}>\n            {label && (\n              <FieldLegend>\n                {label}\n                {isRequired && ui?.asterisk !== false ? (\n                  <span className=\"text-destructive ml-1\">*</span>\n                ) : null}\n                {showErrorBadge && errorCount > 0 && (\n                  <Badge variant=\"destructive\" className=\"h-5 px-1.5 text-xs ml-2\">\n                    {errorCount}\n                  </Badge>\n                )}\n              </FieldLegend>\n            )}\n            {descriptionNode}\n            {errorNode}\n            {children}\n          </FieldSet>\n        </Field>\n      </FieldGroup>\n    );\n  }\n\n  // ── GHOST ──────────────────────────────────────────────────────────────────\n  if (variant === \"ghost\") {\n    return (\n      <FieldGroup data-field={fieldApi.name}>\n        <Field data-invalid={isInvalid} data-disabled={isDisabled}>\n          <FieldSet\n            disabled={isDisabled}\n            className=\"border border-border/50 rounded-lg p-4\"\n          >\n            {label && (\n              <FieldLegend className=\"flex items-center gap-2\">\n                {label}\n                {isRequired && ui?.asterisk !== false ? (\n                  <span className=\"text-destructive ml-1\">*</span>\n                ) : null}\n                {showErrorBadge && errorCount > 0 && (\n                  <Badge variant=\"destructive\" className=\"h-5 px-1.5 text-xs\">\n                    {errorCount}\n                  </Badge>\n                )}\n              </FieldLegend>\n            )}\n            {descriptionNode}\n            {errorNode}\n            <div className={spacingClass[spacing]}>{children}</div>\n          </FieldSet>\n        </Field>\n      </FieldGroup>\n    );\n  }\n\n  // ── BORDERED ───────────────────────────────────────────────────────────────\n  if (variant === \"bordered\") {\n    return (\n      <FieldGroup data-field={fieldApi.name}>\n        <Field data-invalid={isInvalid} data-disabled={isDisabled}>\n          <Collapsible open={isOpen} onOpenChange={setIsOpen}>\n            <FieldSet\n              disabled={isDisabled}\n              className=\"border border-dashed border-border rounded-lg overflow-hidden\"\n            >\n              {label && (\n                <CollapsibleTrigger\n                  className=\"w-full px-4 py-2 flex flex-row items-center justify-between hover:bg-muted/50 transition-colors select-none cursor-pointer\"\n                  aria-expanded={isOpen}\n                  aria-label={`${isOpen ? \"Collapse\" : \"Expand\"} ${label}`}\n                >\n                  <span className=\"flex items-center gap-2\">\n                    {triggerLabelContent}\n                  </span>\n                  <TriggerChevron open={isOpen} />\n                </CollapsibleTrigger>\n              )}\n              <CollapsibleContent>\n                <div className={cn(\"px-4 pt-2 pb-4\", spacingClass[spacing])}>\n                  {descriptionNode}\n                  {errorNode}\n                  {children}\n                </div>\n              </CollapsibleContent>\n            </FieldSet>\n          </Collapsible>\n        </Field>\n      </FieldGroup>\n    );\n  }\n\n  // ── CARD (default) ─────────────────────────────────────────────────────────\n  return (\n    <FieldGroup data-field={fieldApi.name}>\n      <Field data-invalid={isInvalid} data-disabled={isDisabled}>\n        <Collapsible open={isOpen} onOpenChange={setIsOpen}>\n          <Card className=\"py-0 gap-0\">\n            {label && (\n              <CardHeader className=\"p-0 border-b-0\">\n                <CollapsibleTrigger\n                  className={cn(\n                    \"w-full px-4 py-3 flex flex-row items-center justify-between\",\n                    \"hover:bg-muted/75 bg-muted/50 transition-colors select-none cursor-pointer\",\n                    isOpen && \"border-b\",\n                  )}\n                  aria-expanded={isOpen}\n                  aria-label={`${isOpen ? \"Collapse\" : \"Expand\"} ${label}`}\n                >\n                  <span className=\"flex items-center gap-2\">\n                    {triggerLabelContent}\n                  </span>\n                  <TriggerChevron open={isOpen} />\n                </CollapsibleTrigger>\n              </CardHeader>\n            )}\n            <CollapsibleContent>\n              <CardContent className={cn(\"px-4 pt-3 pb-4\", spacingClass[spacing])}>\n                {descriptionNode}\n                {errorNode}\n                {children}\n              </CardContent>\n            </CollapsibleContent>\n          </Card>\n        </Collapsible>\n      </Field>\n    </FieldGroup>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/buzzform/fields/group.tsx"
    }
  ],
  "type": "registry:ui"
}