{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "paginated-table", "title": "Paginated Table", "description": "Cinatra design-system table-with-pagination composite.", "registryDependencies": [ "utils", "pagination", "table" ], "files": [ { "path": "src/components/ui/paginated-table.tsx", "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport {\n Pagination,\n PaginationContent,\n PaginationItem,\n PaginationNext,\n PaginationPrevious,\n} from \"@/components/ui/pagination\"\nimport { Table } from \"@/components/ui/table\"\nimport { cn } from \"@/lib/utils\"\n\nconst DEFAULT_TABLE_PAGE_SIZE = 25\n\ntype PaginatedTableProps = React.ComponentProps & {\n pageSize?: number\n pagination?: boolean\n}\n\nfunction getTableBodyRows(frame: HTMLDivElement | null): HTMLTableRowElement[] {\n const table = frame?.querySelector(\"table\")\n\n if (!table) {\n return []\n }\n\n return Array.from(table.tBodies).flatMap((body) => Array.from(body.rows))\n}\n\nfunction PaginatedTable({\n className,\n children,\n id,\n pageSize = DEFAULT_TABLE_PAGE_SIZE,\n pagination = true,\n ...props\n}: PaginatedTableProps) {\n const generatedTableId = React.useId()\n const tableId = id ?? generatedTableId\n const frameRef = React.useRef(null)\n const [rowCount, setRowCount] = React.useState(0)\n const [pageIndex, setPageIndex] = React.useState(0)\n const safePageSize = Number.isFinite(pageSize) && pageSize > 0\n ? pageSize\n : DEFAULT_TABLE_PAGE_SIZE\n const shouldPaginate = pagination && rowCount > safePageSize\n const pageCount = shouldPaginate ? Math.ceil(rowCount / safePageSize) : 1\n const safePageIndex = shouldPaginate\n ? Math.min(pageIndex, Math.max(0, pageCount - 1))\n : 0\n const firstRow = shouldPaginate ? safePageIndex * safePageSize + 1 : 1\n const lastRow = shouldPaginate\n ? Math.min(rowCount, (safePageIndex + 1) * safePageSize)\n : rowCount\n const isFirstPage = safePageIndex === 0\n const isLastPage = safePageIndex >= pageCount - 1\n\n const refreshRowCount = React.useCallback(() => {\n setRowCount(getTableBodyRows(frameRef.current).length)\n }, [])\n\n React.useEffect(() => {\n refreshRowCount()\n\n const frame = frameRef.current\n if (!frame || typeof MutationObserver === \"undefined\") {\n return\n }\n\n const observer = new MutationObserver(refreshRowCount)\n observer.observe(frame, { childList: true, subtree: true })\n\n return () => observer.disconnect()\n }, [children, refreshRowCount])\n\n React.useEffect(() => {\n setPageIndex((current) => Math.min(current, Math.max(0, pageCount - 1)))\n }, [pageCount])\n\n React.useEffect(() => {\n const frame = frameRef.current\n const rows = getTableBodyRows(frame)\n const firstVisibleRow = safePageIndex * safePageSize\n const lastVisibleRow = firstVisibleRow + safePageSize\n\n rows.forEach((row, index) => {\n row.hidden = shouldPaginate && (index < firstVisibleRow || index >= lastVisibleRow)\n })\n\n return () => {\n getTableBodyRows(frame).forEach((row) => {\n row.hidden = false\n })\n }\n }, [children, rowCount, safePageIndex, safePageSize, shouldPaginate])\n\n const handlePrevious = React.useCallback((event: React.MouseEvent) => {\n event.preventDefault()\n\n if (!isFirstPage) {\n setPageIndex((current) => Math.max(0, current - 1))\n }\n }, [isFirstPage])\n\n const handleNext = React.useCallback((event: React.MouseEvent) => {\n event.preventDefault()\n\n if (!isLastPage) {\n setPageIndex((current) => Math.min(pageCount - 1, current + 1))\n }\n }, [isLastPage, pageCount])\n\n return (\n \n \n {children}\n \n {shouldPaginate ? (\n \n \n {firstRow}–{lastRow} of {rowCount}\n \n
\n \n Page {safePageIndex + 1} of {pageCount}\n \n \n \n \n \n \n \n \n \n \n \n
\n \n ) : null}\n \n )\n}\n\nexport { PaginatedTable }\n", "type": "registry:ui" } ], "type": "registry:ui" }