{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "p-table-3",
  "description": "Table with TanStack Table and checkboxes",
  "dependencies": [
    "@tanstack/react-table"
  ],
  "registryDependencies": [
    "@coss/badge",
    "@coss/checkbox",
    "@coss/frame",
    "@coss/table"
  ],
  "files": [
    {
      "path": "registry/default/particles/p-table-3.tsx",
      "content": "\"use client\";\n\nimport {\n  type ColumnDef,\n  flexRender,\n  getCoreRowModel,\n  useReactTable,\n} from \"@tanstack/react-table\";\nimport type React from \"react\";\nimport { useMemo, useState } from \"react\";\nimport { Badge } from \"@/registry/default/ui/badge\";\nimport { Checkbox } from \"@/registry/default/ui/checkbox\";\nimport { Frame } from \"@/registry/default/ui/frame\";\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableFooter,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/registry/default/ui/table\";\n\ntype Project = {\n  id: string;\n  project: string;\n  status: \"Paid\" | \"Unpaid\" | \"Pending\" | \"Failed\";\n  team: string;\n  budget: number;\n};\n\nconst data: Project[] = [\n  {\n    budget: 12500,\n    id: \"1\",\n    project: \"Website Redesign\",\n    status: \"Paid\",\n    team: \"Frontend Team\",\n  },\n  {\n    budget: 8750,\n    id: \"2\",\n    project: \"Mobile App\",\n    status: \"Unpaid\",\n    team: \"Mobile Team\",\n  },\n  {\n    budget: 5200,\n    id: \"3\",\n    project: \"API Integration\",\n    status: \"Pending\",\n    team: \"Backend Team\",\n  },\n  {\n    budget: 3800,\n    id: \"4\",\n    project: \"Database Migration\",\n    status: \"Paid\",\n    team: \"DevOps Team\",\n  },\n  {\n    budget: 7200,\n    id: \"5\",\n    project: \"User Dashboard\",\n    status: \"Paid\",\n    team: \"UX Team\",\n  },\n  {\n    budget: 2100,\n    id: \"6\",\n    project: \"Security Audit\",\n    status: \"Failed\",\n    team: \"Security Team\",\n  },\n];\n\nconst getStatusColor = (status: Project[\"status\"]) => {\n  switch (status) {\n    case \"Paid\":\n      return \"bg-emerald-500\";\n    case \"Unpaid\":\n      return \"bg-muted-foreground/64\";\n    case \"Pending\":\n      return \"bg-amber-500\";\n    case \"Failed\":\n      return \"bg-red-500\";\n    default:\n      return \"bg-muted-foreground/64\";\n  }\n};\n\nconst getColumns = (): ColumnDef<Project>[] => [\n  {\n    cell: ({ row }) => {\n      const toggleHandler = row.getToggleSelectedHandler();\n      return (\n        <Checkbox\n          aria-label=\"Select row\"\n          checked={row.getIsSelected()}\n          disabled={!row.getCanSelect()}\n          onCheckedChange={(value) => {\n            // Create a synthetic event for the handler\n            const syntheticEvent = {\n              target: { checked: !!value },\n            } as unknown as React.ChangeEvent<HTMLInputElement>;\n            toggleHandler(syntheticEvent);\n          }}\n        />\n      );\n    },\n    enableSorting: false,\n    header: ({ table }) => {\n      const isAllSelected = table.getIsAllPageRowsSelected();\n      const isSomeSelected = table.getIsSomePageRowsSelected();\n      const toggleHandler = table.getToggleAllPageRowsSelectedHandler();\n      return (\n        <Checkbox\n          aria-label=\"Select all\"\n          checked={isAllSelected}\n          indeterminate={isSomeSelected && !isAllSelected}\n          onCheckedChange={(value) => {\n            // Create a synthetic event for the handler\n            const syntheticEvent = {\n              target: { checked: !!value },\n            } as unknown as React.ChangeEvent<HTMLInputElement>;\n            toggleHandler(syntheticEvent);\n          }}\n        />\n      );\n    },\n    id: \"select\",\n  },\n  {\n    accessorKey: \"project\",\n    cell: ({ row }) => (\n      <div className=\"font-medium\">{row.getValue(\"project\")}</div>\n    ),\n    header: \"Project\",\n  },\n  {\n    accessorKey: \"status\",\n    cell: ({ row }) => {\n      const status = row.getValue(\"status\") as Project[\"status\"];\n      return (\n        <Badge variant=\"outline\">\n          <span\n            aria-hidden=\"true\"\n            className={`size-1.5 rounded-full ${getStatusColor(status)}`}\n          />\n          {status}\n        </Badge>\n      );\n    },\n    header: \"Status\",\n  },\n  {\n    accessorKey: \"team\",\n    header: \"Team\",\n  },\n  {\n    accessorKey: \"budget\",\n    cell: ({ row }) => {\n      const amount = Number.parseFloat(row.getValue(\"budget\"));\n      const formatted = new Intl.NumberFormat(\"en-US\", {\n        currency: \"USD\",\n        maximumFractionDigits: 0,\n        minimumFractionDigits: 0,\n        style: \"currency\",\n      }).format(amount);\n      return <div className=\"text-right\">{formatted}</div>;\n    },\n    header: () => <div className=\"text-right\">Budget</div>,\n  },\n];\n\nexport default function Particle() {\n  const [tableData] = useState<Project[]>(data);\n  const [rowSelection, setRowSelection] = useState({});\n\n  const columns = useMemo(() => getColumns(), []);\n\n  const table = useReactTable({\n    columns,\n    data: tableData,\n    enableRowSelection: true,\n    getCoreRowModel: getCoreRowModel(),\n    onRowSelectionChange: setRowSelection,\n    state: {\n      rowSelection,\n    },\n  });\n\n  const totalBudget = tableData.reduce(\n    (sum, project) => sum + project.budget,\n    0,\n  );\n  const formattedTotal = new Intl.NumberFormat(\"en-US\", {\n    currency: \"USD\",\n    maximumFractionDigits: 0,\n    minimumFractionDigits: 0,\n    style: \"currency\",\n  }).format(totalBudget);\n\n  return (\n    <Frame className=\"w-full\">\n      <Table variant=\"card\">\n        <TableHeader>\n          {table.getHeaderGroups().map((headerGroup) => (\n            <TableRow key={headerGroup.id}>\n              {headerGroup.headers.map((header) => {\n                return (\n                  <TableHead key={header.id}>\n                    {header.isPlaceholder\n                      ? null\n                      : flexRender(\n                          header.column.columnDef.header,\n                          header.getContext(),\n                        )}\n                  </TableHead>\n                );\n              })}\n            </TableRow>\n          ))}\n        </TableHeader>\n        <TableBody>\n          {table.getRowModel().rows?.length ? (\n            table.getRowModel().rows.map((row) => (\n              <TableRow\n                data-state={row.getIsSelected() && \"selected\"}\n                key={row.id}\n              >\n                {row.getVisibleCells().map((cell) => (\n                  <TableCell key={cell.id}>\n                    {flexRender(cell.column.columnDef.cell, cell.getContext())}\n                  </TableCell>\n                ))}\n              </TableRow>\n            ))\n          ) : (\n            <TableRow>\n              <TableCell className=\"h-24 text-center\" colSpan={columns.length}>\n                No results.\n              </TableCell>\n            </TableRow>\n          )}\n        </TableBody>\n        <TableFooter>\n          <TableRow>\n            <TableCell colSpan={4}>Total Budget</TableCell>\n            <TableCell className=\"text-right\">{formattedTotal}</TableCell>\n          </TableRow>\n        </TableFooter>\n      </Table>\n    </Frame>\n  );\n}\n",
      "type": "registry:block"
    }
  ],
  "meta": {
    "className": "**:data-[slot=preview]:w-full sm:**:data-[slot=preview]:max-w-4xl",
    "colSpan": 2
  },
  "categories": [
    "checkbox",
    "table",
    "tanstack"
  ],
  "type": "registry:block"
}