{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "disclosure-row",
  "type": "registry:ui",
  "dependencies": [
    "clsx",
    "tailwind-merge"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "disclosure-row.tsx",
      "target": "components/ui/disclosure-row.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface DisclosureRowProps\n  extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onToggle\"> {\n  /** The row itself. A ProgressRow, a title, whatever the panel is about. */\n  summary: React.ReactNode;\n  /** Categorical: any CSS color for the left rail. Omit it and no rail draws. */\n  railColor?: string;\n  /** Trailing content inside the button, after the summary. Counts, badges. */\n  aside?: React.ReactNode;\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  disabled?: boolean;\n  /** Names the button for assistive tech when the summary is not plain text. */\n  buttonLabel?: string;\n  children?: React.ReactNode;\n}\n\nexport const DisclosureRow = React.forwardRef<\n  HTMLDivElement,\n  DisclosureRowProps\n>(\n  (\n    {\n      className,\n      summary,\n      railColor,\n      aside,\n      open,\n      defaultOpen = false,\n      onOpenChange,\n      disabled = false,\n      buttonLabel,\n      children,\n      ...props\n    },\n    ref,\n  ) => {\n    const panelId = React.useId();\n    const [uncontrolled, setUncontrolled] = React.useState(defaultOpen);\n    const isOpen = open ?? uncontrolled;\n\n    const toggle = () => {\n      const next = !isOpen;\n      if (open === undefined) setUncontrolled(next);\n      onOpenChange?.(next);\n    };\n\n    return (\n      <div\n        ref={ref}\n        data-open={isOpen ? \"\" : undefined}\n        className={cn(\n          \"overflow-hidden rounded-xl border border-border bg-ink/[0.02]\",\n          className,\n        )}\n        {...props}\n      >\n        <button\n          type=\"button\"\n          aria-expanded={isOpen}\n          aria-controls={panelId}\n          aria-label={buttonLabel}\n          disabled={disabled}\n          onClick={toggle}\n          className={cn(\n            \"group flex min-h-16 w-full items-center gap-4 px-4 py-3 text-left outline-none\",\n            \"transition-control duration-fast ease-soft focus-visible:ring-focus\",\n            disabled\n              ? \"cursor-not-allowed opacity-55\"\n              : \"cursor-pointer hover:bg-ink/[0.03]\",\n          )}\n        >\n          {railColor != null && (\n            <span\n              aria-hidden=\"true\"\n              className=\"min-h-11 w-1 shrink-0 self-stretch rounded-full\"\n              style={{ backgroundColor: railColor }}\n            />\n          )}\n\n          <ChevronIcon />\n\n          <span className=\"min-w-0 flex-1\">{summary}</span>\n\n          {aside != null && (\n            <span className=\"shrink-0 text-right\">{aside}</span>\n          )}\n        </button>\n\n        <DisclosurePanel id={panelId} open={isOpen}>\n          {children}\n        </DisclosurePanel>\n      </div>\n    );\n  },\n);\nDisclosureRow.displayName = \"DisclosureRow\";\n\nfunction DisclosurePanel({\n  id,\n  open,\n  children,\n}: {\n  id: string;\n  open: boolean;\n  children: React.ReactNode;\n}) {\n  // A closed panel keeps its content mounted so the height can transition, which\n  // leaves its links and buttons focusable. React 18 and 19 disagree on how an\n  // `inert` prop serializes, so the attribute goes on the node instead.\n  const ref = React.useRef<HTMLDivElement>(null);\n  const useIsomorphicLayoutEffect =\n    typeof window === \"undefined\" ? React.useEffect : React.useLayoutEffect;\n  useIsomorphicLayoutEffect(() => {\n    const node = ref.current;\n    if (!node) return;\n    if (open) node.removeAttribute(\"inert\");\n    else node.setAttribute(\"inert\", \"\");\n  }, [open]);\n\n  return (\n    <div\n      ref={ref}\n      id={id}\n      data-disclosure-panel=\"\"\n      className={cn(\n        \"grid transition-[grid-template-rows] duration-base ease-soft motion-reduce:transition-none\",\n        open ? \"grid-rows-[1fr]\" : \"grid-rows-[0fr]\",\n      )}\n    >\n      <div className=\"overflow-hidden\">{children}</div>\n    </div>\n  );\n}\n\nfunction ChevronIcon() {\n  return (\n    <svg\n      viewBox=\"0 0 14 14\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"1.5\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      aria-hidden=\"true\"\n      className={cn(\n        \"size-3.5 shrink-0 text-muted transition-transform duration-fast ease-soft\",\n        \"group-aria-expanded:rotate-90 motion-reduce:transition-none\",\n      )}\n    >\n      <path d=\"M5 3l4 4-4 4\" />\n    </svg>\n  );\n}\n"
    }
  ]
}