{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "p-table-4",
  "description": "Table with TanStack Table, sorting, and pagination",
  "dependencies": [
    "@tanstack/react-table",
    "lucide-react"
  ],
  "registryDependencies": [
    "@coss/badge",
    "@coss/button",
    "@coss/checkbox",
    "@coss/frame",
    "@coss/pagination",
    "@coss/select",
    "@coss/table"
  ],
  "files": [
    {
      "path": "registry/default/particles/p-table-4.tsx",
      "content": "\"use client\";\n\nimport {\n  type ColumnDef,\n  flexRender,\n  getCoreRowModel,\n  getPaginationRowModel,\n  getSortedRowModel,\n  type PaginationState,\n  type SortingState,\n  useReactTable,\n} from \"@tanstack/react-table\";\nimport { ChevronDownIcon, ChevronUpIcon, PlaneTakeoffIcon } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { cn } from \"@/registry/default/lib/utils\";\nimport { Badge } from \"@/registry/default/ui/badge\";\nimport { Button } from \"@/registry/default/ui/button\";\nimport { Checkbox } from \"@/registry/default/ui/checkbox\";\nimport { Frame, FrameFooter } from \"@/registry/default/ui/frame\";\nimport {\n  Pagination,\n  PaginationContent,\n  PaginationItem,\n  PaginationNext,\n  PaginationPrevious,\n} from \"@/registry/default/ui/pagination\";\nimport {\n  Select,\n  SelectItem,\n  SelectPopup,\n  SelectTrigger,\n  SelectValue,\n} from \"@/registry/default/ui/select\";\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/registry/default/ui/table\";\n\ntype Flight = {\n  id: string;\n  flightCode: string;\n  destination: string;\n  departureTime: string;\n  arrivalTime: string;\n  terminal: string;\n  duration: string;\n  status: \"On Time\" | \"Delayed\" | \"Cancelled\" | \"Boarding\";\n  gate: string;\n};\n\nconst getStatusColor = (status: Flight[\"status\"]) => {\n  switch (status) {\n    case \"On Time\":\n      return \"bg-emerald-500\";\n    case \"Delayed\":\n      return \"bg-amber-500\";\n    case \"Cancelled\":\n      return \"bg-red-500\";\n    case \"Boarding\":\n      return \"bg-blue-500\";\n    default:\n      return \"bg-muted-foreground/64\";\n  }\n};\n\nconst columns: ColumnDef<Flight>[] = [\n  {\n    cell: ({ row }) => (\n      <Checkbox\n        aria-label=\"Select row\"\n        checked={row.getIsSelected()}\n        onCheckedChange={(value) => row.toggleSelected(!!value)}\n      />\n    ),\n    enableSorting: false,\n    header: ({ table }) => {\n      const isAllSelected = table.getIsAllPageRowsSelected();\n      const isSomeSelected = table.getIsSomePageRowsSelected();\n      return (\n        <Checkbox\n          aria-label=\"Select all rows\"\n          checked={isAllSelected}\n          indeterminate={isSomeSelected && !isAllSelected}\n          onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}\n        />\n      );\n    },\n    id: \"select\",\n    size: 28,\n  },\n  {\n    accessorKey: \"flightCode\",\n    cell: ({ row }) => (\n      <div className=\"font-medium font-mono text-muted-foreground\">\n        {row.getValue(\"flightCode\")}\n      </div>\n    ),\n    header: \"Flight\",\n    size: 80,\n  },\n  {\n    accessorKey: \"departureTime\",\n    cell: ({ row }) => {\n      const isCancelled = row.original.status === \"Cancelled\";\n      const isDelayed = row.original.status === \"Delayed\";\n      return (\n        <div\n          className={cn(\n            \"flex items-center gap-1.5 font-normal tabular-nums\",\n            isCancelled && \"text-muted-foreground line-through opacity-50\",\n          )}\n        >\n          <div className={isDelayed ? \"text-warning-foreground\" : undefined}>\n            {row.original.departureTime}\n          </div>\n          <div\n            aria-hidden=\"true\"\n            className=\"flex items-center gap-0.5 opacity-50 before:size-1.5 before:rounded-full before:border before:border-muted-foreground after:h-px after:w-3 after:border-muted-foreground after:border-t after:border-dashed\"\n          />\n          <div\n            className={cn(\n              \"text-muted-foreground\",\n              isCancelled && \"line-through\",\n            )}\n          >\n            {row.original.duration}\n          </div>\n          <div\n            aria-hidden=\"true\"\n            className=\"flex items-center gap-0.5 opacity-50 before:order-1 before:size-1.5 before:rounded-full before:border before:border-muted-foreground after:h-px after:w-3 after:border-muted-foreground after:border-t after:border-dashed\"\n          />\n          <div>{row.original.arrivalTime}</div>\n        </div>\n      );\n    },\n    header: \"Time\",\n    size: 220,\n  },\n  {\n    accessorKey: \"destination\",\n    cell: ({ row }) => (\n      <div className=\"font-medium\">{row.getValue(\"destination\")}</div>\n    ),\n    header: \"Destination\",\n    size: 180,\n  },\n  {\n    accessorKey: \"status\",\n    cell: ({ row }) => {\n      const status = row.getValue(\"status\") as Flight[\"status\"];\n      return (\n        <Badge variant=\"outline\">\n          <span\n            aria-hidden=\"true\"\n            className={cn(\"size-1.5 rounded-full\", getStatusColor(status))}\n          />\n          {status}\n        </Badge>\n      );\n    },\n    header: \"Status\",\n    size: 120,\n  },\n  {\n    accessorKey: \"terminal\",\n    cell: ({ row }) => (\n      <Badge className=\"font-normal tabular-nums\" size=\"lg\" variant=\"outline\">\n        <PlaneTakeoffIcon />\n        <span>{row.getValue(\"terminal\")}</span>\n      </Badge>\n    ),\n    header: \"Terminal\",\n    size: 90,\n  },\n  {\n    accessorKey: \"gate\",\n    header: \"Gate\",\n    size: 80,\n  },\n];\n\nexport default function Particle() {\n  const pageSize = 10;\n\n  const [pagination, setPagination] = useState<PaginationState>({\n    pageIndex: 0,\n    pageSize: pageSize,\n  });\n\n  const [sorting, setSorting] = useState<SortingState>([\n    {\n      desc: false,\n      id: \"departureTime\",\n    },\n  ]);\n\n  const table = useReactTable({\n    columns,\n    data: flights,\n    enableSortingRemoval: false,\n    getCoreRowModel: getCoreRowModel(),\n    getPaginationRowModel: getPaginationRowModel(),\n    getSortedRowModel: getSortedRowModel(),\n    onPaginationChange: setPagination,\n    onSortingChange: setSorting,\n    state: {\n      pagination,\n      sorting,\n    },\n  });\n\n  return (\n    <Frame className=\"w-full\">\n      <Table variant=\"card\" className=\"table-fixed\">\n        <TableHeader>\n          {table.getHeaderGroups().map((headerGroup) => (\n            <TableRow className=\"hover:bg-transparent\" key={headerGroup.id}>\n              {headerGroup.headers.map((header) => {\n                const columnSize = header.column.getSize();\n                return (\n                  <TableHead\n                    key={header.id}\n                    style={\n                      columnSize ? { width: `${columnSize}px` } : undefined\n                    }\n                  >\n                    {header.isPlaceholder ? null : header.column.getCanSort() ? (\n                      <div\n                        className=\"flex h-full cursor-pointer select-none items-center justify-between gap-2\"\n                        onClick={header.column.getToggleSortingHandler()}\n                        onKeyDown={(e) => {\n                          if (e.key === \"Enter\" || e.key === \" \") {\n                            e.preventDefault();\n                            header.column.getToggleSortingHandler()?.(e);\n                          }\n                        }}\n                        role=\"button\"\n                        tabIndex={0}\n                      >\n                        {flexRender(\n                          header.column.columnDef.header,\n                          header.getContext(),\n                        )}\n                        {{\n                          asc: (\n                            <ChevronUpIcon\n                              aria-hidden=\"true\"\n                              className=\"size-4 shrink-0 opacity-80\"\n                            />\n                          ),\n                          desc: (\n                            <ChevronDownIcon\n                              aria-hidden=\"true\"\n                              className=\"size-4 shrink-0 opacity-80\"\n                            />\n                          ),\n                        }[header.column.getIsSorted() as string] ?? null}\n                      </div>\n                    ) : (\n                      flexRender(\n                        header.column.columnDef.header,\n                        header.getContext(),\n                      )\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\" : undefined}\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      </Table>\n      <FrameFooter className=\"p-2\">\n        <div className=\"flex items-center justify-between gap-2\">\n          {/* Results range selector */}\n          <div className=\"flex items-center gap-2 whitespace-nowrap\">\n            <p className=\"text-muted-foreground text-sm\">Viewing</p>\n            <Select\n              items={Array.from({ length: table.getPageCount() }, (_, i) => {\n                const start = i * table.getState().pagination.pageSize + 1;\n                const end = Math.min(\n                  (i + 1) * table.getState().pagination.pageSize,\n                  table.getRowCount(),\n                );\n                const pageNum = i + 1;\n                return { label: `${start}-${end}`, value: pageNum };\n              })}\n              onValueChange={(value) => {\n                table.setPageIndex((value as number) - 1);\n              }}\n              value={table.getState().pagination.pageIndex + 1}\n            >\n              <SelectTrigger\n                aria-label=\"Select result range\"\n                className=\"w-fit min-w-none\"\n                size=\"sm\"\n              >\n                <SelectValue />\n              </SelectTrigger>\n              <SelectPopup>\n                {Array.from({ length: table.getPageCount() }, (_, i) => {\n                  const start = i * table.getState().pagination.pageSize + 1;\n                  const end = Math.min(\n                    (i + 1) * table.getState().pagination.pageSize,\n                    table.getRowCount(),\n                  );\n                  const pageNum = i + 1;\n                  return (\n                    <SelectItem key={pageNum} value={pageNum}>\n                      {`${start}-${end}`}\n                    </SelectItem>\n                  );\n                })}\n              </SelectPopup>\n            </Select>\n            <p className=\"text-muted-foreground text-sm\">\n              of{\" \"}\n              <strong className=\"font-medium text-foreground\">\n                {table.getRowCount()}\n              </strong>{\" \"}\n              results\n            </p>\n          </div>\n\n          {/* Pagination */}\n          <Pagination className=\"justify-end\">\n            <PaginationContent>\n              <PaginationItem>\n                <PaginationPrevious\n                  className=\"sm:*:[svg]:hidden\"\n                  render={\n                    <Button\n                      disabled={!table.getCanPreviousPage()}\n                      onClick={() => table.previousPage()}\n                      size=\"sm\"\n                      variant=\"outline\"\n                    />\n                  }\n                />\n              </PaginationItem>\n              <PaginationItem>\n                <PaginationNext\n                  className=\"sm:*:[svg]:hidden\"\n                  render={\n                    <Button\n                      disabled={!table.getCanNextPage()}\n                      onClick={() => table.nextPage()}\n                      size=\"sm\"\n                      variant=\"outline\"\n                    />\n                  }\n                />\n              </PaginationItem>\n            </PaginationContent>\n          </Pagination>\n        </div>\n      </FrameFooter>\n    </Frame>\n  );\n}\n\nconst flights: Flight[] = [\n  {\n    arrivalTime: \"11:45\",\n    departureTime: \"08:30\",\n    destination: \"Los Angeles\",\n    duration: \"5h 15m\",\n    flightCode: \"AA1234\",\n    gate: \"A12\",\n    id: \"1\",\n    status: \"On Time\",\n    terminal: \"1\",\n  },\n  {\n    arrivalTime: \"17:10\",\n    departureTime: \"14:20\",\n    destination: \"San Francisco\",\n    duration: \"4h 50m\",\n    flightCode: \"DL5678\",\n    gate: \"B24\",\n    id: \"2\",\n    status: \"Delayed\",\n    terminal: \"2\",\n  },\n  {\n    arrivalTime: \"13:30\",\n    departureTime: \"10:15\",\n    destination: \"Miami\",\n    duration: \"3h 15m\",\n    flightCode: \"UA9012\",\n    gate: \"C8\",\n    id: \"3\",\n    status: \"On Time\",\n    terminal: \"1\",\n  },\n  {\n    arrivalTime: \"18:20\",\n    departureTime: \"16:45\",\n    destination: \"Seattle\",\n    duration: \"2h 35m\",\n    flightCode: \"SW3456\",\n    gate: \"D15\",\n    id: \"4\",\n    status: \"On Time\",\n    terminal: \"3\",\n  },\n  {\n    arrivalTime: \"12:30\",\n    departureTime: \"09:00\",\n    destination: \"Salt Lake City\",\n    duration: \"5h 30m\",\n    flightCode: \"JB7890\",\n    gate: \"E3\",\n    id: \"5\",\n    status: \"Cancelled\",\n    terminal: \"2\",\n  },\n  {\n    arrivalTime: \"14:15\",\n    departureTime: \"11:30\",\n    destination: \"Phoenix\",\n    duration: \"2h 45m\",\n    flightCode: \"AS2345\",\n    gate: \"F7\",\n    id: \"6\",\n    status: \"On Time\",\n    terminal: \"1\",\n  },\n  {\n    arrivalTime: \"20:30\",\n    departureTime: \"13:00\",\n    destination: \"Las Vegas\",\n    duration: \"5h 30m\",\n    flightCode: \"HA6789\",\n    gate: \"G12\",\n    id: \"7\",\n    status: \"Delayed\",\n    terminal: \"2\",\n  },\n  {\n    arrivalTime: \"09:00\",\n    departureTime: \"07:15\",\n    destination: \"Dallas\",\n    duration: \"1h 45m\",\n    flightCode: \"FX0123\",\n    gate: \"H5\",\n    id: \"8\",\n    status: \"Boarding\",\n    terminal: \"1\",\n  },\n  {\n    arrivalTime: \"08:30\",\n    departureTime: \"06:00\",\n    destination: \"Denver\",\n    duration: \"2h 30m\",\n    flightCode: \"WN4567\",\n    gate: \"I9\",\n    id: \"9\",\n    status: \"Boarding\",\n    terminal: \"2\",\n  },\n  {\n    arrivalTime: \"15:20\",\n    departureTime: \"12:45\",\n    destination: \"Portland\",\n    duration: \"2h 35m\",\n    flightCode: \"B61234\",\n    gate: \"J14\",\n    id: \"10\",\n    status: \"On Time\",\n    terminal: \"3\",\n  },\n  {\n    arrivalTime: \"18:45\",\n    departureTime: \"15:30\",\n    destination: \"Atlanta\",\n    duration: \"3h 15m\",\n    flightCode: \"NK8901\",\n    gate: \"K6\",\n    id: \"11\",\n    status: \"On Time\",\n    terminal: \"1\",\n  },\n  {\n    arrivalTime: \"12:00\",\n    departureTime: \"09:45\",\n    destination: \"Chicago\",\n    duration: \"2h 15m\",\n    flightCode: \"F92345\",\n    gate: \"L11\",\n    id: \"12\",\n    status: \"Delayed\",\n    terminal: \"2\",\n  },\n  {\n    arrivalTime: \"14:15\",\n    departureTime: \"11:00\",\n    destination: \"Boston\",\n    duration: \"3h 15m\",\n    flightCode: \"SY6789\",\n    gate: \"M3\",\n    id: \"13\",\n    status: \"On Time\",\n    terminal: \"1\",\n  },\n  {\n    arrivalTime: \"16:45\",\n    departureTime: \"13:30\",\n    destination: \"New York\",\n    duration: \"3h 15m\",\n    flightCode: \"G40123\",\n    gate: \"N8\",\n    id: \"14\",\n    status: \"On Time\",\n    terminal: \"3\",\n  },\n  {\n    arrivalTime: \"11:20\",\n    departureTime: \"08:00\",\n    destination: \"Washington\",\n    duration: \"3h 20m\",\n    flightCode: \"YX5678\",\n    gate: \"O12\",\n    id: \"15\",\n    status: \"Delayed\",\n    terminal: \"2\",\n  },\n  {\n    arrivalTime: \"13:50\",\n    departureTime: \"10:30\",\n    destination: \"Orlando\",\n    duration: \"3h 20m\",\n    flightCode: \"4U9012\",\n    gate: \"P5\",\n    id: \"16\",\n    status: \"Delayed\",\n    terminal: \"1\",\n  },\n  {\n    arrivalTime: \"16:30\",\n    departureTime: \"14:00\",\n    destination: \"Houston\",\n    duration: \"2h 30m\",\n    flightCode: \"QF3456\",\n    gate: \"Q9\",\n    id: \"17\",\n    status: \"On Time\",\n    terminal: \"3\",\n  },\n  {\n    arrivalTime: \"10:00\",\n    departureTime: \"07:30\",\n    destination: \"Minneapolis\",\n    duration: \"2h 30m\",\n    flightCode: \"LH7890\",\n    gate: \"R7\",\n    id: \"18\",\n    status: \"Cancelled\",\n    terminal: \"2\",\n  },\n  {\n    arrivalTime: \"19:30\",\n    departureTime: \"16:15\",\n    destination: \"Detroit\",\n    duration: \"3h 15m\",\n    flightCode: \"KL2345\",\n    gate: \"S4\",\n    id: \"19\",\n    status: \"Cancelled\",\n    terminal: \"1\",\n  },\n  {\n    arrivalTime: \"15:10\",\n    departureTime: \"12:00\",\n    destination: \"Philadelphia\",\n    duration: \"3h 10m\",\n    flightCode: \"AF6789\",\n    gate: \"T16\",\n    id: \"20\",\n    status: \"On Time\",\n    terminal: \"3\",\n  },\n  {\n    arrivalTime: \"12:25\",\n    departureTime: \"09:15\",\n    destination: \"Charlotte\",\n    duration: \"3h 10m\",\n    flightCode: \"BA0123\",\n    gate: \"U10\",\n    id: \"21\",\n    status: \"On Time\",\n    terminal: \"2\",\n  },\n  {\n    arrivalTime: \"18:00\",\n    departureTime: \"15:45\",\n    destination: \"Nashville\",\n    duration: \"2h 15m\",\n    flightCode: \"IB4567\",\n    gate: \"V8\",\n    id: \"22\",\n    status: \"Delayed\",\n    terminal: \"1\",\n  },\n  {\n    arrivalTime: \"14:00\",\n    departureTime: \"11:45\",\n    destination: \"Austin\",\n    duration: \"2h 15m\",\n    flightCode: \"EK8901\",\n    gate: \"W13\",\n    id: \"23\",\n    status: \"Cancelled\",\n    terminal: \"3\",\n  },\n  {\n    arrivalTime: \"16:40\",\n    departureTime: \"13:15\",\n    destination: \"Tampa\",\n    duration: \"3h 25m\",\n    flightCode: \"QR2345\",\n    gate: \"X6\",\n    id: \"24\",\n    status: \"On Time\",\n    terminal: \"2\",\n  },\n  {\n    arrivalTime: \"11:30\",\n    departureTime: \"08:45\",\n    destination: \"Raleigh\",\n    duration: \"2h 45m\",\n    flightCode: \"TK6789\",\n    gate: \"Y11\",\n    id: \"25\",\n    status: \"On Time\",\n    terminal: \"1\",\n  },\n  {\n    arrivalTime: \"12:45\",\n    departureTime: \"10:00\",\n    destination: \"Indianapolis\",\n    duration: \"2h 45m\",\n    flightCode: \"VS3456\",\n    gate: \"Z4\",\n    id: \"26\",\n    status: \"On Time\",\n    terminal: \"2\",\n  },\n  {\n    arrivalTime: \"20:00\",\n    departureTime: \"17:30\",\n    destination: \"Kansas City\",\n    duration: \"2h 30m\",\n    flightCode: \"LX7890\",\n    gate: \"A8\",\n    id: \"27\",\n    status: \"Delayed\",\n    terminal: \"3\",\n  },\n  {\n    arrivalTime: \"15:20\",\n    departureTime: \"12:30\",\n    destination: \"Columbus\",\n    duration: \"2h 50m\",\n    flightCode: \"OS1234\",\n    gate: \"B19\",\n    id: \"28\",\n    status: \"On Time\",\n    terminal: \"1\",\n  },\n  {\n    arrivalTime: \"20:15\",\n    departureTime: \"18:00\",\n    destination: \"Milwaukee\",\n    duration: \"2h 15m\",\n    flightCode: \"SN5678\",\n    gate: \"C22\",\n    id: \"29\",\n    status: \"On Time\",\n    terminal: \"2\",\n  },\n  {\n    arrivalTime: \"21:30\",\n    departureTime: \"19:15\",\n    destination: \"Memphis\",\n    duration: \"2h 15m\",\n    flightCode: \"TP9012\",\n    gate: \"D6\",\n    id: \"30\",\n    status: \"On Time\",\n    terminal: \"3\",\n  },\n];\n",
      "type": "registry:block"
    }
  ],
  "meta": {
    "className": "**:data-[slot=preview]:w-full sm:**:data-[slot=preview]:max-w-4xl",
    "colSpan": 2
  },
  "categories": [
    "checkbox",
    "pagination",
    "select",
    "table",
    "tanstack"
  ],
  "type": "registry:block"
}