{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tree-table",
  "title": "Tree Table",
  "description": "Hierarchical rows with expand/collapse, sorting, and search that keeps a matching row's ancestors visible.",
  "dependencies": [
    "@tanstack/react-table"
  ],
  "registryDependencies": [
    "table",
    "input"
  ],
  "files": [
    {
      "path": "src/components/tree/columns.tsx",
      "content": "'use client'\n\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { ChevronRight } from 'lucide-react'\n\nexport type OrgNode = {\n  id: string\n  name: string\n  role: string\n  status: string\n  children?: OrgNode[]\n}\n\nexport const columns: ColumnDef<OrgNode>[] = [\n  {\n    accessorKey: 'name',\n    header: 'Name',\n    cell: ({ row }) => (\n      <div\n        className=\"flex items-center gap-1.5\"\n        style={{ paddingLeft: `${row.depth * 1.25}rem` }}\n      >\n        {row.getCanExpand() ? (\n          <button\n            type=\"button\"\n            onClick={row.getToggleExpandedHandler()}\n            className=\"flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground\"\n            aria-label={row.getIsExpanded() ? 'Collapse row' : 'Expand row'}\n          >\n            <ChevronRight\n              className={`h-3.5 w-3.5 transition-transform ${\n                row.getIsExpanded() ? 'rotate-90' : ''\n              }`}\n            />\n          </button>\n        ) : (\n          <span className=\"w-4 shrink-0\" />\n        )}\n        {row.original.name}\n      </div>\n    ),\n  },\n  {\n    accessorKey: 'role',\n    header: 'Role',\n    sortingFn: 'alphanumeric',\n  },\n  {\n    accessorKey: 'status',\n    header: 'Status',\n    sortingFn: 'basic',\n  },\n]\n",
      "type": "registry:component",
      "target": "@components/tables/tree-table/columns.tsx"
    },
    {
      "path": "src/components/tree/data-table.tsx",
      "content": "'use client'\n\nimport {\n  flexRender,\n  getCoreRowModel,\n  getExpandedRowModel,\n  getFilteredRowModel,\n  getSortedRowModel,\n  useReactTable,\n} from '@tanstack/react-table'\n\nimport type {\n  ColumnDef,\n  ExpandedState,\n  SortingState,\n} from '@tanstack/react-table'\n\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from '#/components/ui/table'\nimport { useState } from 'react'\nimport { ArrowDown, ArrowUp, ArrowUpDown } from 'lucide-react'\nimport { Input } from '#/components/ui/input'\n\ninterface TreeDataTableProps<TData extends { children?: TData[] }> {\n  columns: ColumnDef<TData, any>[]\n  data: TData[]\n}\n\nexport function TreeDataTable<TData extends { children?: TData[] }>({\n  columns,\n  data,\n}: TreeDataTableProps<TData>) {\n  const [sorting, setSorting] = useState<SortingState>([])\n  const [globalFilter, setGlobalFilter] = useState('')\n  const [expanded, setExpanded] = useState<ExpandedState>(true)\n\n  const table = useReactTable({\n    data,\n    columns,\n    getCoreRowModel: getCoreRowModel(),\n    getSubRows: (row) => row.children,\n    getFilteredRowModel: getFilteredRowModel(),\n    getSortedRowModel: getSortedRowModel(),\n    getExpandedRowModel: getExpandedRowModel(),\n    filterFromLeafRows: true,\n    onExpandedChange: setExpanded,\n    onSortingChange: setSorting,\n    onGlobalFilterChange: setGlobalFilter,\n    state: {\n      sorting,\n      globalFilter,\n      expanded,\n    },\n  })\n\n  return (\n    <div>\n      <Input\n        type=\"text\"\n        placeholder=\"Search...\"\n        onChange={(e) => table.setGlobalFilter(e.target.value)}\n        className=\"mb-3 w-64\"\n      />\n      <div className=\"overflow-hidden rounded-md border\">\n        <Table>\n          <TableHeader>\n            {table.getHeaderGroups().map((headerGroup) => (\n              <TableRow key={headerGroup.id}>\n                {headerGroup.headers.map((header) => {\n                  const canSort = header.column.getCanSort()\n                  const sortHandler = header.column.getToggleSortingHandler()\n                  return (\n                    <TableHead\n                      key={header.id}\n                      onClick={sortHandler}\n                      onKeyDown={(e) => {\n                        if (canSort && (e.key === 'Enter' || e.key === ' ')) {\n                          e.preventDefault()\n                          sortHandler?.(e)\n                        }\n                      }}\n                      tabIndex={canSort ? 0 : undefined}\n                      role={canSort ? 'button' : undefined}\n                      className={\n                        canSort ? 'cursor-pointer select-none' : undefined\n                      }\n                    >\n                      <div className=\"flex flex-row items-center gap-2\">\n                        {header.isPlaceholder\n                          ? null\n                          : flexRender(\n                              header.column.columnDef.header,\n                              header.getContext(),\n                            )}\n                        {canSort ? (\n                          <span>\n                            {header.column.getIsSorted() === 'asc' ? (\n                              <ArrowUp className=\"h-3 w-3\" />\n                            ) : header.column.getIsSorted() === 'desc' ? (\n                              <ArrowDown className=\"h-3 w-3\" />\n                            ) : (\n                              <ArrowUpDown className=\"h-3 w-3 opacity-50\" />\n                            )}\n                          </span>\n                        ) : null}\n                      </div>\n                    </TableHead>\n                  )\n                })}\n              </TableRow>\n            ))}\n          </TableHeader>\n          <TableBody>\n            {table.getRowModel().rows.length ? (\n              table.getRowModel().rows.map((row) => (\n                <TableRow key={row.id}>\n                  {row.getVisibleCells().map((cell) => (\n                    <TableCell key={cell.id}>\n                      {flexRender(\n                        cell.column.columnDef.cell,\n                        cell.getContext(),\n                      )}\n                    </TableCell>\n                  ))}\n                </TableRow>\n              ))\n            ) : (\n              <TableRow>\n                <TableCell\n                  colSpan={columns.length}\n                  className=\"h-24 text-center\"\n                >\n                  No results.\n                </TableCell>\n              </TableRow>\n            )}\n          </TableBody>\n        </Table>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/tables/tree-table/data-table.tsx"
    },
    {
      "path": "src/components/tree/index.tsx",
      "content": "import { columns } from './columns'\nimport type { OrgNode } from './columns'\nimport { TreeDataTable } from './data-table'\n\nconst data: OrgNode[] = [\n  {\n    id: 'engineering',\n    name: 'Engineering',\n    role: 'Department',\n    status: '—',\n    children: [\n      {\n        id: 'frontend',\n        name: 'Frontend',\n        role: 'Team',\n        status: '—',\n        children: [\n          {\n            id: 'ava',\n            name: 'Ava Thompson',\n            role: 'Frontend Lead',\n            status: 'Active',\n          },\n          {\n            id: 'liam',\n            name: 'Liam Chen',\n            role: 'Frontend Engineer',\n            status: 'Active',\n          },\n        ],\n      },\n      {\n        id: 'backend',\n        name: 'Backend',\n        role: 'Team',\n        status: '—',\n        children: [\n          {\n            id: 'sofia',\n            name: 'Sofia Patel',\n            role: 'Backend Lead',\n            status: 'Active',\n          },\n          {\n            id: 'noah',\n            name: 'Noah Garcia',\n            role: 'Backend Engineer',\n            status: 'Inactive',\n          },\n        ],\n      },\n    ],\n  },\n  {\n    id: 'design',\n    name: 'Design',\n    role: 'Department',\n    status: '—',\n    children: [\n      {\n        id: 'product-design',\n        name: 'Product Design',\n        role: 'Team',\n        status: '—',\n        children: [\n          {\n            id: 'mia',\n            name: 'Mia Johnson',\n            role: 'Design Lead',\n            status: 'Active',\n          },\n          {\n            id: 'ethan',\n            name: 'Ethan Kim',\n            role: 'Product Designer',\n            status: 'Pending',\n          },\n        ],\n      },\n    ],\n  },\n]\n\nexport function TreeTableDemo() {\n  return <TreeDataTable columns={columns} data={data} />\n}\n",
      "type": "registry:component",
      "target": "@components/tables/tree-table/index.tsx"
    }
  ],
  "type": "registry:block"
}