{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tree-view-row",
  "type": "registry:component",
  "title": "Tree View Row",
  "description": "Canonical shadcn-styled row for `@grida/tree-view`.",
  "dependencies": [
    "@grida/tree-view",
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/tree-view-row.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport type { Row } from \"@grida/tree-view\";\nimport { useTree, useTreeSnapshot } from \"@grida/tree-view/react\";\nimport { cn } from \"@/lib/utils\";\nimport { ChevronDownIcon, ChevronRightIcon } from \"lucide-react\";\nimport * as React from \"react\";\n\nexport interface TreeViewRowProps {\n  /** Row produced by `controller.getRows()`. */\n  row: Row;\n  /** Optional label override. Falls back to `row.meta.label ?? row.id`. */\n  label?: React.ReactNode;\n  /** Optional leading icon. */\n  icon?: React.ReactNode;\n  /** Number of pixels per depth step. Defaults to 16. */\n  indent?: number;\n}\n\n/**\n * Canonical shadcn-styled row for `@grida/tree-view`.\n *\n * - Subscribes to its own selection / focus / expanded slice via\n *   `useTreeSnapshot` so siblings don't re-render when only your row's\n *   state changes.\n * - Drives a single `data-state` attribute so Tailwind variants can style\n *   every state from one className.\n * - Toggles expansion on chevron click; emits selection on the row body.\n */\nexport function TreeViewRow({\n  row,\n  label,\n  icon,\n  indent = 16,\n}: TreeViewRowProps) {\n  const controller = useTree();\n  const isContainer =\n    controller.source.isContainer?.(row.id) ?? row.isContainer;\n\n  const state = useTreeSnapshot(\n    (c) => ({\n      selected: c.getSelection().includes(row.id),\n      focused: c.getFocused() === row.id,\n      expanded: c.isExpanded(row.id),\n    }),\n    (a, b) =>\n      a.selected === b.selected &&\n      a.focused === b.focused &&\n      a.expanded === b.expanded\n  );\n\n  const dataState = state.selected\n    ? \"selected\"\n    : state.focused\n      ? \"focused\"\n      : \"idle\";\n\n  const handleToggle = (e: React.MouseEvent) => {\n    e.stopPropagation();\n    if (state.expanded) controller.collapse(row.id);\n    else controller.expand(row.id);\n  };\n\n  const handleSelect = (e: React.MouseEvent) => {\n    const mode = e.shiftKey\n      ? \"range\"\n      : e.metaKey || e.ctrlKey\n        ? \"toggle\"\n        : \"replace\";\n    controller.select([row.id], mode);\n    controller.focus(row.id);\n  };\n\n  return (\n    <div\n      role=\"treeitem\"\n      aria-selected={state.selected}\n      aria-expanded={isContainer ? state.expanded : undefined}\n      data-state={dataState}\n      onClick={handleSelect}\n      style={{ paddingLeft: row.depth * indent }}\n      className={cn(\n        \"flex h-7 items-center gap-1.5 px-1.5 text-sm cursor-default select-none rounded-sm\",\n        \"hover:bg-accent/60 data-[state=selected]:bg-accent data-[state=selected]:text-accent-foreground\",\n        \"data-[state=focused]:ring-1 data-[state=focused]:ring-ring/40\"\n      )}\n    >\n      {isContainer ? (\n        <button\n          type=\"button\"\n          onClick={handleToggle}\n          className=\"size-4 inline-flex items-center justify-center text-muted-foreground hover:text-foreground\"\n          aria-label={state.expanded ? \"Collapse\" : \"Expand\"}\n        >\n          {state.expanded ? (\n            <ChevronDownIcon className=\"size-3.5\" />\n          ) : (\n            <ChevronRightIcon className=\"size-3.5\" />\n          )}\n        </button>\n      ) : (\n        <span className=\"size-4\" />\n      )}\n      {icon ? <span className=\"text-muted-foreground\">{icon}</span> : null}\n      <span className=\"truncate\">{label ?? row.id}</span>\n    </div>\n  );\n}\n"
    }
  ]
}
