Skip to content
This repository has been archived by the owner on Jan 16, 2024. It is now read-only.

Commit

Permalink
feat(components): add VirtualTable component
Browse files Browse the repository at this point in the history
  ## what
  - add `VirtualTable` component

  ## how
  - uses
    - `@tanstack/react-table` to generate a table
    - `@tanstack/react-virtual` to handle virtualization

  ## why
  - to display a table without the `DataTable` functionality

  ## where
  - ./src/components/virtual-table/index.tsx

  ## usage
  • Loading branch information
Clumsy-Coder committed Jan 12, 2024
1 parent 8ec4501 commit 09259ea
Showing 1 changed file with 137 additions and 0 deletions.
137 changes: 137 additions & 0 deletions src/components/virtual-table/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"use client";

import { useRef } from "react";
import {
ColumnDef,
flexRender,
getCoreRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { useVirtualizer } from "@tanstack/react-virtual";

import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { cn } from "@/lib/utils";

interface Props<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
/**
* Height of the table. Must be a number denoting pixels
*
* Ex: 400 would mean 400px. this will be set using tailwindcss `h-[400px]`
*/
tableHeight: number;
}

/**
* A Virtual table using `@tanstack/react-table`. The virtualization is from `@tanstack/react-virtual`
*
* Code obtained from
* - ./src/components/ui/data-table/index.tsx
* - https://codesandbox.io/p/devbox/tanstack-table-example-virtualized-rows-33u7fj?file=%2Fsrc%2Fmain.tsx
* - https://codesandbox.io/p/devbox/tanstack-react-virtual-example-dynamic-mr8t3x?file=%2Fsrc%2Fmain.tsx
*/
function VirtualTable<TData, TValue>({
columns,
data,
tableHeight,
}: Props<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
});

const { rows } = table.getRowModel();

//The virtualizer needs to know the scrollable container element
const tableContainerRef = useRef<HTMLDivElement>(null);

const rowVirtualizer = useVirtualizer({
count: rows.length,
estimateSize: () => 33, //estimate row height for accurate scrollbar dragging
getScrollElement: () => tableContainerRef.current,
//measure dynamic row height, except in firefox because it measures table border height incorrectly
measureElement:
typeof window !== "undefined" &&
navigator.userAgent.indexOf("Firefox") === -1
? (element) => element?.getBoundingClientRect().height
: undefined,
overscan: 5,
});

return (
<div>
<div
className={cn(
"rounded-md border",
"overflow-auto",
`h-[${tableHeight}px]`, // set using props. sets the overall table height
)}
ref={tableContainerRef}
>
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody className={cn(`h-[${rowVirtualizer.getTotalSize()}px]`)}>
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
const row = rows[virtualRow.index];

return (
<TableRow
data-index={virtualRow.index}
ref={(node) => rowVirtualizer.measureElement(node)}
key={row.id}
>
{row.getVisibleCells().map((cell) => {
return (
<TableCell
key={cell.id}
style={{
// display: "flex",
width: cell.column.getSize(),
}}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</div>
</div>
);
}

export default VirtualTable;

0 comments on commit 09259ea

Please sign in to comment.