Virtualized Rows
(0 rows)
ID | First Name | Last Name | Age | Visits | Status | Profile Progress | Created At |
---|
import {useEffect, useMemo, useRef, useState} from "react"import {useVirtualizer} from "@tanstack/react-virtual"import {ArrowDown} from "lucide-react"import {clsx} from "@qui/base"import {QIcon} from "@qui/react"import {ColumnDef,flexRender,getCoreRowModel,getSortedRowModel,QTable,QTbody,QTd,QTh,QThead,QTr,Row,useReactTable,} from "@qui/react-table"import {makeData, Person} from "./make-data"// This is a dynamic row height example, which is more complicated, but allows for a// more realistic table.export default function VirtualizedRows() {const columns = useMemo<ColumnDef<Person>[]>(() => [{accessorKey: "id",header: "ID",size: 60,},{accessorKey: "firstName",cell: (info) => info.getValue(),header: "First Name",},{accessorFn: (row) => row.lastName,cell: (info) => info.getValue(),header: "Last Name",id: "lastName",},{accessorKey: "age",header: "Age",size: 80,},{accessorKey: "visits",header: "Visits",size: 80,},{accessorKey: "status",header: "Status",},{accessorKey: "progress",header: "Profile Progress",minSize: 120,},{accessorKey: "createdAt",cell: (info) => info.getValue<Date>().toLocaleString(),header: "Created At",size: 200,},],[],)const [data, setData] = useState<Person[]>([])useEffect(() => {setData(makeData(30000))}, [])const table = useReactTable({columns,data,getCoreRowModel: getCoreRowModel(),getSortedRowModel: getSortedRowModel(),})const {rows} = table.getRowModel()// The virtualizer needs to know the scrollable container elementconst tableContainerRef = useRef<HTMLDivElement>(null)const rowVirtualizer = useVirtualizer({count: rows.length,estimateSize: () => 33, // estimate row height for accurate scrollbar dragginggetScrollElement: () => tableContainerRef.current,// measure dynamic row height, except in firefox because it measures table// border height incorrectlymeasureElement:typeof window !== "undefined" &&navigator.userAgent.indexOf("Firefox") === -1? (element) => element?.getBoundingClientRect().height: undefined,overscan: 5,})return (<div className="overflow-x-auto">({data.length} rows)<divref={tableContainerRef}className="relative mt-2 h-[800px] overflow-auto">{/** Even though we're still using semantic table tags, we must use* CSS grid and flexbox for dynamic row heights*/}<QTable className="grid table-fixed border-0"><QThead className="sticky top-0 z-10 grid bg-[var(--q-background-1)]">{table.getHeaderGroups().map((headerGroup) => (<QTrkey={headerGroup.id}className="flex w-full"style={{borderBottom: "solid 1px var(--q-border-1-subtle)"}}>{headerGroup.headers.map((header) => {if (header.isPlaceholder) {return (<QThkey={header.id}colSpan={header.colSpan}style={{width: header.getSize()}}/>)}const sorted = header.column.getIsSorted()return (<QThkey={header.id}colSpan={header.colSpan}onClick={header.column.getToggleSortingHandler()}style={{width: header.getSize()}}><div className="inline-flex h-full items-center gap-2">{flexRender(header.column.columnDef.header,header.getContext(),)}{sorted ? (<QIconclassName={clsx("transition-[transform] ease-in-out",{"rotate-180": sorted === "asc",},)}icon={ArrowDown}size="s"/>) : null}</div></QTh>)})}</QTr>))}</QThead><QTbodyclassName="relative grid"style={{height: `${rowVirtualizer.getTotalSize()}px`, // tells scrollbar how big the table is}}>{rowVirtualizer.getVirtualItems().map((virtualRow) => {const row = rows[virtualRow.index] as Row<Person>return (<QTrkey={row.id}ref={(node) => rowVirtualizer.measureElement(node)} // measure dynamic row heightclassName="absolute flex w-full border-l-[1px]"data-index={virtualRow.index} // needed for dynamic row height measurementstyle={{borderColor: "var(--q-border-1-subtle)",transform: `translateY(${virtualRow.start}px)`, // this should always be a `style` as it changes on scroll}}>{row.getVisibleCells().map((cell) => {return (<QTdkey={cell.id}className="flex"style={{width: cell.column.getSize(),}}>{flexRender(cell.column.columnDef.cell,cell.getContext(),)}</QTd>)})}</QTr>)})}</QTbody></QTable></div></div>)}