{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "collapsible",
  "title": "CollapsibleField",
  "description": "Collapsible layout field for BuzzForm v2.",
  "registryDependencies": [
    "@buzzform/init",
    "card",
    "collapsible",
    "badge"
  ],
  "files": [
    {
      "path": "registry/shadcn/fields/collapsible.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport type { CollapsibleField as CollapsibleFieldDef } from \"@buildnbuzz/form-react\";\nimport { useLayoutField, useNestedErrorCount } from \"@buildnbuzz/form-react\";\nimport { resolveExpr, toDotNotation } 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 { cn } from \"@/lib/utils\";\n\n// ---------------------------------------------------------------------------\n// UI options\n// ---------------------------------------------------------------------------\n\ninterface CollapsibleUi {\n  /** Visual variant. Defaults to `\"bordered\"`. */\n  variant?: \"card\" | \"bordered\" | \"ghost\";\n  /** Vertical spacing between nested fields. Defaults to `\"md\"`. */\n  spacing?: \"sm\" | \"md\" | \"lg\";\n  /** Show an error-count badge next to the trigger label. Defaults to `true`. */\n  showErrorBadge?: boolean;\n  /** Description rendered below the trigger label when expanded. */\n  description?: string;\n  /** Icon rendered before the trigger label. */\n  icon?: React.ReactNode;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst spacingClass: Record<NonNullable<CollapsibleUi[\"spacing\"]>, string> = {\n  sm: \"space-y-3\",\n  md: \"space-y-4\",\n  lg: \"space-y-6\",\n};\n\n// ---------------------------------------------------------------------------\n// Shared sub-components\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\nfunction TriggerLabel({\n  label,\n  icon,\n  errorCount,\n  showErrorBadge,\n  bold,\n}: {\n  label: React.ReactNode;\n  icon?: React.ReactNode;\n  errorCount: number;\n  showErrorBadge: boolean;\n  bold?: boolean;\n}) {\n  return (\n    <div className=\"flex-1 min-w-0 flex items-center gap-2\">\n      {icon && (\n        <span className=\"shrink-0 text-muted-foreground\">{icon}</span>\n      )}\n      <span\n        className={cn(\n          \"text-sm truncate\",\n          bold ? \"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    </div>\n  );\n}\n\n// ---------------------------------------------------------------------------\n// CollapsibleField\n// ---------------------------------------------------------------------------\n\nexport function CollapsibleField({ children }: { children?: React.ReactNode }) {\n  const { field, fieldPath, formData, contextData, label } =\n    useLayoutField<CollapsibleFieldDef>();\n\n  const ui = field.ui as CollapsibleUi | undefined;\n  const variant = ui?.variant ?? \"bordered\";\n  const spacing = ui?.spacing ?? \"md\";\n  const showErrorBadge = ui?.showErrorBadge !== false;\n  const description = ui?.description;\n  const icon = ui?.icon;\n\n  const resolvedCollapsed = resolveExpr(\n    field.collapsed,\n    { data: formData, context: contextData },\n  );\n  const [isOpen, setIsOpen] = React.useState(resolvedCollapsed !== true);\n\n  // Read current error count from the store snapshot (re-renders when form state changes)\n  const errorCount = useNestedErrorCount(field.fields, toDotNotation(fieldPath));\n\n  const triggerLabelProps = {\n    label: label || \"Toggle\",\n    icon,\n    errorCount,\n    showErrorBadge,\n  };\n\n  const descriptionNode = description ? (\n    <p className=\"text-sm text-muted-foreground mb-3\">{description}</p>\n  ) : null;\n\n  // ── GHOST ──────────────────────────────────────────────────────────────────\n  if (variant === \"ghost\") {\n    return (\n      <Collapsible open={isOpen} onOpenChange={setIsOpen}>\n        <CollapsibleTrigger\n          className=\"w-full px-2 py-2 rounded-md 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\"} ${typeof label === \"string\" ? label : \"Section\"}`}\n        >\n          <TriggerLabel {...triggerLabelProps} />\n          <TriggerChevron open={isOpen} />\n        </CollapsibleTrigger>\n        <CollapsibleContent>\n          <div className={cn(\"pt-4 pl-2\", spacingClass[spacing])}>\n            {descriptionNode}\n            {children}\n          </div>\n        </CollapsibleContent>\n      </Collapsible>\n    );\n  }\n\n  // ── BORDERED ───────────────────────────────────────────────────────────────\n  if (variant === \"bordered\") {\n    return (\n      <Collapsible open={isOpen} onOpenChange={setIsOpen}>\n        <div className=\"border border-dashed border-border rounded-lg overflow-hidden\">\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\"} ${typeof label === \"string\" ? label : \"Section\"}`}\n          >\n            <TriggerLabel {...triggerLabelProps} />\n            <TriggerChevron open={isOpen} />\n          </CollapsibleTrigger>\n          <CollapsibleContent>\n            <div className={cn(\"px-4 pt-2 pb-4\", spacingClass[spacing])}>\n              {descriptionNode}\n              {children}\n            </div>\n          </CollapsibleContent>\n        </div>\n      </Collapsible>\n    );\n  }\n\n  // ── CARD (default) ─────────────────────────────────────────────────────────\n  return (\n    <Collapsible open={isOpen} onOpenChange={setIsOpen}>\n      <Card className=\"py-0 gap-0\">\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\"} ${typeof label === \"string\" ? label : \"Section\"}`}\n          >\n            <TriggerLabel {...triggerLabelProps} bold />\n            <TriggerChevron open={isOpen} />\n          </CollapsibleTrigger>\n        </CardHeader>\n        <CollapsibleContent>\n          <CardContent className={cn(\"px-4 pt-3 pb-4\", spacingClass[spacing])}>\n            {descriptionNode}\n            {children}\n          </CardContent>\n        </CollapsibleContent>\n      </Card>\n    </Collapsible>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/buzzform/fields/collapsible.tsx"
    }
  ],
  "type": "registry:ui"
}