From d45b9a9f1ce06f97cd98fdb20da0a53dfac02c98 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Tue, 26 Dec 2023 21:32:01 -0600 Subject: [PATCH] docs: column sizing/resizing guide (#5228) --- docs/guide/column-sizing.md | 140 ++++- docs/guide/row-selection.md | 2 +- docs/guide/virtualization.md | 18 + .../column-resizing-performant/.gitignore | 5 + .../column-resizing-performant/README.md | 6 + .../column-resizing-performant/index.html | 13 + .../column-resizing-performant/package.json | 21 + .../column-resizing-performant/src/index.css | 73 +++ .../column-resizing-performant/src/main.tsx | 255 +++++++++ .../src/makeData.ts | 48 ++ .../tsconfig.dev.json | 12 + .../column-resizing-performant/vite.config.js | 25 + examples/react/column-sizing/src/index.css | 9 +- examples/react/column-sizing/src/main.tsx | 516 ++++++++++-------- package.json | 1 + .../table-core/src/features/ColumnSizing.ts | 2 +- tsconfig.json | 3 + 17 files changed, 911 insertions(+), 238 deletions(-) create mode 100644 docs/guide/virtualization.md create mode 100644 examples/react/column-resizing-performant/.gitignore create mode 100644 examples/react/column-resizing-performant/README.md create mode 100644 examples/react/column-resizing-performant/index.html create mode 100644 examples/react/column-resizing-performant/package.json create mode 100644 examples/react/column-resizing-performant/src/index.css create mode 100644 examples/react/column-resizing-performant/src/main.tsx create mode 100644 examples/react/column-resizing-performant/src/makeData.ts create mode 100644 examples/react/column-resizing-performant/tsconfig.dev.json create mode 100644 examples/react/column-resizing-performant/vite.config.js diff --git a/docs/guide/column-sizing.md b/docs/guide/column-sizing.md index 51f22985cc..7c1f10946a 100644 --- a/docs/guide/column-sizing.md +++ b/docs/guide/column-sizing.md @@ -7,15 +7,18 @@ title: Column Sizing Want to skip to the implementation? Check out these examples: - [column-sizing](../examples/react/column-sizing) +- [column-resizing-performant](../examples/react/column-resizing-performant) ## API [Column Sizing API](../api/features/column-sizing) -## Overview +## Guide The column sizing feature allows you to optionally specify the width of each column including min and max widths. It also allows you and your users the ability to dynamically change the width of all columns at will, eg. by dragging the column headers. +### Column Widths + Columns by default are given the following measurement options: ```tsx @@ -28,9 +31,30 @@ export const defaultColumnSizing = { These defaults can be overridden by both `tableOptions.defaultColumn` and individual column defs, in that order. +```tsx +const columns = [ + { + accessorKey: 'col1', + size: 270, //set column size for this column + }, + //... +] + +const table = useReactTable({ + //override default column sizing + defaultColumn: { + size: 200, //starting column size + minSize: 50, //enforced during column resizing + maxSize: 500, //enforced during column resizing + }, +}) +``` + +The column "sizes" are stored in the table state as numbers, and are usually interpreted as pixel unit values, but you can hook up these column sizing values to your css styles however you see fit. + As a headless utility, table logic for column sizing is really only a collection of states that you can apply to your own layouts how you see fit (our example above implements 2 styles of this logic). You can apply these width measurements in a variety of ways: -- `table` elements or any elements being displayed in a table css mode +- semantic `table` elements or any elements being displayed in a table css mode - `div/span` elements or any elements being displayed in a non-table css mode - Block level elements with strict widths - Absolutely positioned elements with strict widths @@ -39,3 +63,115 @@ As a headless utility, table logic for column sizing is really only a collection - Really any layout mechanism that can interpolate cell widths into a table structure. Each of these approaches has its own tradeoffs and limitations which are usually opinions held by a UI/component library or design system, luckily not you 😉. + +### Column Resizing + +TanStack Table provides built-in column resizing state and APIs that allow you to easily implement column resizing in your table UI with a variety of options for UX and performance. + +#### Enable Column Resizing + +By default, the `column.getCanResize()` API will return `true` by default for all columns, but you can either disable column resizing for all columns with the `enableColumnResizing` table option, or disable column resizing on a per-column basis with the `enableResizing` column option. + +```tsx +const columns = [ + { + accessorKey: 'id', + enableResizing: false, //disable resizing for just this column + size: 200, //starting column size + }, + //... +] +``` + +#### Column Resize Mode + +By default, the column resize mode is set to `"onEnd"`. This means that the `column.getSize()` API will not return the new column size until the user has finished resizing (dragging) the column. Usually a small UI indicator will be displayed while the user is resizing the column. + +In React TanStack Table adapter, where achieving 60 fps column resizing renders can be difficult, depending on the complexity of your table or web page, the `"onEnd"` column resize mode can be a good default option to avoid stuttering or lagging while the user resizes columns. That is not to say that you cannot achieve 60 fps column resizing renders while using TanStack React Table, but you may have to do some extra memoization or other performance optimizations in order to achieve this. + +> Advanced column resizing performance tips will be discussed [down below](#advancedcolumnresizingperformance). + +If you want to change the column resize mode to `"onChange"` for immediate column resizing renders, you can do so with the `columnResizeMode` table option. + +```tsx +const table = useReactTable({ + //... + columnResizeMode: 'onChange', //change column resize mode to "onChange" +}) +``` + +#### Column Resize Direction + +By default, TanStack Table assumes that the table markup is laid out in a left-to-right direction. For right-to-left layouts, you may need to change the column resize direction to `"rtl"`. + +```tsx +const table = useReactTable({ + //... + columnResizeDirection: 'rtl', //change column resize direction to "rtl" for certain locales +}) +``` + +#### Connect Column Resizing APIs to UI + +There are a few really handy APIs that you can use to hook up your column resizing drag interactions to your UI. + +##### Column Size APIs + +To apply the size of a column to the column head cells, data cells, or footer cells, you can use the following APIs: + +```ts +header.getSize() +column.getSize() +cell.column.getSize() +``` + +How you apply these size styles to your markup is up to you, but it is pretty common to use either CSS variables or inline styles to apply the column sizes. + +```tsx + +``` + +Though, as discussed in the [advanced column resizing performance section](#advancedcolumnresizingperformance), you may want to consider using CSS variables to apply column sizes to your markup. + +##### Column Resize APIs + +TanStack Table provides a pre-built event handler to make your drag interactions easy to implement. These event handlers are just convenience functions that call other internal APIs to update the column sizing state and re-render the table. Use `header.getResizeHandler()` to connect to your column resize drag interactions, for both mouse and touch events. + +```tsx + +``` + +##### Column Resize Indicator with ColumnSizingInfoState + +TanStack Table keeps track of an state object called `columnSizingInfo` that you can use to render a column resize indicator UI. + +```jsx + +``` + +#### Advanced Column Resizing Performance + +If you are creating large or complex tables (and using React 😉), you may find that if you do not add proper memoization to your render logic, your users may experience degraded performance while resizing columns. + +We have created a [performant column resizing example](../examples/react/column-resizing-performant) that demonstrates how to achieve 60 fps column resizing renders with a complex table that may otherwise have slow renders. It is recommended that you just look at that example to see how it is done, but these are the basic things to keep in mind: + +1. Don't use `column.getSize()` on every header and every data cell. Instead, calculate all column widths once upfront, **memoized**! +2. Memoize your Table Body while resizing is in progress. +3. Use CSS variables to communicate column widths to your table cells. + +If you follow these steps, you should see significant performance improvements while resizing columns. + +If you are not using React, and are using the Svelte, Vue, or Solid adapters instead, you may not need to worry about this as much, but similar principles apply. diff --git a/docs/guide/row-selection.md b/docs/guide/row-selection.md index 1d78fd8b5e..04abbab5d0 100644 --- a/docs/guide/row-selection.md +++ b/docs/guide/row-selection.md @@ -161,7 +161,7 @@ const columns = [ ] ``` -#### Connect Row Selection to Row Click Events +#### Connect Row Selection APIs to UI If you want a simpler row selection UI, you can just hook up click events to the row itself. The `row.getToggleSelectedHandler()` API is also useful for this use case. diff --git a/docs/guide/virtualization.md b/docs/guide/virtualization.md new file mode 100644 index 0000000000..a88c32a7bf --- /dev/null +++ b/docs/guide/virtualization.md @@ -0,0 +1,18 @@ +--- +title: Virtualization +--- + +## Examples + +Want to skip to the implementation? Check out these examples: + +- [virtualized-rows](../examples/react/virtualized-rows) +- [virtualized-infinite-scrolling](../examples/react/virtualized-infinite-scrolling) + +## API + +[TanStack Virtual Virtualizer API](../../../../virtual/v3/docs/api/virtualizer) + +## Guide + +The TanStack Table packages do not come with any virtualization APIs or features built-in, but TanStack Table can easily work with other virtualization libraries like [react-window](https://www.npmjs.com/package/react-window) or TanStack's own [TanStack Virtual](https://tanstack.com/virtual/v3) diff --git a/examples/react/column-resizing-performant/.gitignore b/examples/react/column-resizing-performant/.gitignore new file mode 100644 index 0000000000..d451ff16c1 --- /dev/null +++ b/examples/react/column-resizing-performant/.gitignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local diff --git a/examples/react/column-resizing-performant/README.md b/examples/react/column-resizing-performant/README.md new file mode 100644 index 0000000000..b168d3c4b1 --- /dev/null +++ b/examples/react/column-resizing-performant/README.md @@ -0,0 +1,6 @@ +# Example + +To run this example: + +- `npm install` or `yarn` +- `npm run start` or `yarn start` diff --git a/examples/react/column-resizing-performant/index.html b/examples/react/column-resizing-performant/index.html new file mode 100644 index 0000000000..dfcecb0b7b --- /dev/null +++ b/examples/react/column-resizing-performant/index.html @@ -0,0 +1,13 @@ + + + + + + Vite App + + + +
+ + + diff --git a/examples/react/column-resizing-performant/package.json b/examples/react/column-resizing-performant/package.json new file mode 100644 index 0000000000..d0d4f20921 --- /dev/null +++ b/examples/react/column-resizing-performant/package.json @@ -0,0 +1,21 @@ +{ + "name": "tanstack-table-example-column-resizing-performant", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build", + "serve": "vite preview --port 3001", + "start": "vite" + }, + "dependencies": { + "@tanstack/react-table": "8.11.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@rollup/plugin-replace": "^5.0.1", + "@vitejs/plugin-react": "^2.2.0", + "vite": "^3.2.3" + } +} diff --git a/examples/react/column-resizing-performant/src/index.css b/examples/react/column-resizing-performant/src/index.css new file mode 100644 index 0000000000..f4e2d1501f --- /dev/null +++ b/examples/react/column-resizing-performant/src/index.css @@ -0,0 +1,73 @@ +* { + box-sizing: border-box; +} + +html { + font-family: sans-serif; + font-size: 14px; +} + +table, +.divTable { + border: 1px solid lightgray; + width: fit-content; +} + +.tr { + display: flex; +} + +tr, +.tr { + width: fit-content; + height: 30px; +} + +th, +.th, +td, +.td { + box-shadow: inset 0 0 0 1px lightgray; + padding: 0.25rem; +} + +th, +.th { + padding: 2px 4px; + position: relative; + font-weight: bold; + text-align: center; + height: 30px; +} + +td, +.td { + height: 30px; +} + +.resizer { + position: absolute; + top: 0; + height: 100%; + right: 0; + width: 5px; + background: rgba(0, 0, 0, 0.5); + cursor: col-resize; + user-select: none; + touch-action: none; +} + +.resizer.isResizing { + background: blue; + opacity: 1; +} + +@media (hover: hover) { + .resizer { + opacity: 0; + } + + *:hover > .resizer { + opacity: 1; + } +} diff --git a/examples/react/column-resizing-performant/src/main.tsx b/examples/react/column-resizing-performant/src/main.tsx new file mode 100644 index 0000000000..2c6c73a978 --- /dev/null +++ b/examples/react/column-resizing-performant/src/main.tsx @@ -0,0 +1,255 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' + +import './index.css' + +import { + useReactTable, + getCoreRowModel, + ColumnDef, + flexRender, + Table, +} from '@tanstack/react-table' +import { makeData } from './makeData' + +type Person = { + firstName: string + lastName: string + age: number + visits: number + status: string + progress: number +} + +const defaultColumns: ColumnDef[] = [ + { + header: 'Name', + footer: props => props.column.id, + columns: [ + { + accessorKey: 'firstName', + cell: info => info.getValue(), + footer: props => props.column.id, + }, + { + accessorFn: row => row.lastName, + id: 'lastName', + cell: info => info.getValue(), + header: () => Last Name, + footer: props => props.column.id, + }, + ], + }, + { + header: 'Info', + footer: props => props.column.id, + columns: [ + { + accessorKey: 'age', + header: () => 'Age', + footer: props => props.column.id, + }, + { + accessorKey: 'visits', + header: () => Visits, + footer: props => props.column.id, + }, + { + accessorKey: 'status', + header: 'Status', + footer: props => props.column.id, + }, + { + accessorKey: 'progress', + header: 'Profile Progress', + footer: props => props.column.id, + }, + ], + }, +] + +function App() { + const [data, setData] = React.useState(() => makeData(200)) + const [columns] = React.useState(() => [ + ...defaultColumns, + ]) + + const rerender = React.useReducer(() => ({}), {})[1] + + const table = useReactTable({ + data, + columns, + columnResizeMode: 'onChange', + getCoreRowModel: getCoreRowModel(), + debugTable: true, + debugHeaders: true, + debugColumns: true, + }) + + /** + * Instead of calling `column.getSize()` on every render for every header + * and especially every data cell (very expensive), + * we will calculate all column sizes at once at the root table level in a useMemo + * and pass the column sizes down as CSS variables to the element. + */ + const columnSizeVars = React.useMemo(() => { + const headers = table.getFlatHeaders() + const colSizes: { [key: string]: number } = {} + for (let i = 0; i < headers.length; i++) { + const header = headers[i]! + colSizes[`--header-${header.id}-size`] = header.getSize() + colSizes[`--col-${header.column.id}-size`] = header.column.getSize() + } + return colSizes + }, [table.getState().columnSizingInfo]) + + //demo purposes + const [enableMemo, setEnableMemo] = React.useState(true) + + return ( +
+ + This example has artificially slow cell renders to simulate complex + usage + +
+ +
+ +
+        {JSON.stringify(
+          {
+            columnSizing: table.getState().columnSizing,
+            columnSizingInfo: table.getState().columnSizingInfo,
+          },
+          null,
+          2
+        )}
+      
+
({data.length} rows) +
+ {/* Here in the
equivalent element (surrounds all table head and data cells), we will define our CSS variables for column sizes */} +
element + width: table.getTotalSize(), + }, + }} + > +
+ {table.getHeaderGroups().map(headerGroup => ( +
+ {headerGroup.headers.map(header => ( +
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} +
header.column.resetSize(), + onMouseDown: header.getResizeHandler(), + onTouchStart: header.getResizeHandler(), + className: `resizer ${ + header.column.getIsResizing() ? 'isResizing' : '' + }`, + }} + /> +
+ ))} +
+ ))} +
+ {/* When resizing any column we will render this special memoized version of our table body */} + {table.getState().columnSizingInfo.isResizingColumn && enableMemo ? ( + + ) : ( + + )} +
+
+ + ) +} + +//un-memoized normal table body component - see memoized version below +function TableBody({ table }: { table: Table }) { + return ( +
+ {table.getRowModel().rows.map(row => ( +
+ {row.getVisibleCells().map(cell => { + //simulate expensive render + for (let i = 0; i < 10000; i++) { + Math.random() + } + + return ( +
+ {cell.renderValue()} +
+ ) + })} +
+ ))} +
+ ) +} + +//special memoized wrapper for our table body that we will use during column resizing +export const MemoizedTableBody = React.memo( + TableBody, + (prev, next) => prev.table.options.data === next.table.options.data +) as typeof TableBody + +const rootElement = document.getElementById('root') +if (!rootElement) throw new Error('Failed to find the root element') + +ReactDOM.createRoot(rootElement).render( + + + +) diff --git a/examples/react/column-resizing-performant/src/makeData.ts b/examples/react/column-resizing-performant/src/makeData.ts new file mode 100644 index 0000000000..0a22b0d4fd --- /dev/null +++ b/examples/react/column-resizing-performant/src/makeData.ts @@ -0,0 +1,48 @@ +import { faker } from '@faker-js/faker' + +export type Person = { + firstName: string + lastName: string + age: number + visits: number + progress: number + status: 'relationship' | 'complicated' | 'single' + subRows?: Person[] +} + +const range = (len: number) => { + const arr = [] + for (let i = 0; i < len; i++) { + arr.push(i) + } + return arr +} + +const newPerson = (): Person => { + return { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + age: faker.number.int(40), + visits: faker.number.int(1000), + progress: faker.number.int(100), + status: faker.helpers.shuffle([ + 'relationship', + 'complicated', + 'single', + ])[0]!, + } +} + +export function makeData(...lens: number[]) { + const makeDataLevel = (depth = 0): Person[] => { + const len = lens[depth]! + return range(len).map((d): Person => { + return { + ...newPerson(), + subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined, + } + }) + } + + return makeDataLevel() +} diff --git a/examples/react/column-resizing-performant/tsconfig.dev.json b/examples/react/column-resizing-performant/tsconfig.dev.json new file mode 100644 index 0000000000..c09bc865f0 --- /dev/null +++ b/examples/react/column-resizing-performant/tsconfig.dev.json @@ -0,0 +1,12 @@ +{ + "composite": true, + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./build/types" + }, + "files": ["src/main.tsx"], + "include": [ + "src" + // "__tests__/**/*.test.*" + ] +} diff --git a/examples/react/column-resizing-performant/vite.config.js b/examples/react/column-resizing-performant/vite.config.js new file mode 100644 index 0000000000..e567193bbf --- /dev/null +++ b/examples/react/column-resizing-performant/vite.config.js @@ -0,0 +1,25 @@ +import * as path from 'path' +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import rollupReplace from '@rollup/plugin-replace' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + rollupReplace({ + preventAssignment: true, + values: { + __DEV__: JSON.stringify(true), + 'process.env.NODE_ENV': JSON.stringify('development'), + }, + }), + react(), + ], + resolve: process.env.USE_SOURCE + ? { + alias: { + 'react-table': path.resolve(__dirname, '../../../src/index.ts'), + }, + } + : {}, +}) diff --git a/examples/react/column-sizing/src/index.css b/examples/react/column-sizing/src/index.css index d80ec97dd3..5a32330d58 100644 --- a/examples/react/column-sizing/src/index.css +++ b/examples/react/column-sizing/src/index.css @@ -47,7 +47,6 @@ td, .resizer { position: absolute; - right: 0; top: 0; height: 100%; width: 5px; @@ -57,6 +56,14 @@ td, touch-action: none; } +.resizer.ltr { + right: 0; +} + +.resizer.rtl { + left: 0; +} + .resizer.isResizing { background: blue; opacity: 1; diff --git a/examples/react/column-sizing/src/main.tsx b/examples/react/column-sizing/src/main.tsx index 619856ff69..cd48853c75 100644 --- a/examples/react/column-sizing/src/main.tsx +++ b/examples/react/column-sizing/src/main.tsx @@ -9,6 +9,7 @@ import { getCoreRowModel, ColumnDef, flexRender, + ColumnResizeDirection, } from '@tanstack/react-table' type Person = { @@ -108,12 +109,16 @@ function App() { const [columnResizeMode, setColumnResizeMode] = React.useState('onChange') + const [columnResizeDirection, setColumnResizeDirection] = + React.useState('ltr') + const rerender = React.useReducer(() => ({}), {})[1] const table = useReactTable({ data, columns, columnResizeMode, + columnResizeDirection, getCoreRowModel: getCoreRowModel(), debugTable: true, debugHeaders: true, @@ -130,261 +135,306 @@ function App() { -
-
{'
'} -
-
- - {table.getHeaderGroups().map(headerGroup => ( - - {headerGroup.headers.map(header => ( - + ))} + + ))} + + + {table.getRowModel().rows.map(row => ( + + {row.getVisibleCells().map(cell => ( + + ))} + + ))} + +
- {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} -
+ setColumnResizeDirection(e.target.value as ColumnResizeDirection) + } + className="border p-2 border-black rounded" + > + + + +
+
+
{''} +
+
+ + {table.getHeaderGroups().map(headerGroup => ( + + {headerGroup.headers.map(header => ( + - ))} - - ))} - - - {table.getRowModel().rows.map(row => ( - - {row.getVisibleCells().map(cell => ( - - ))} - - ))} - -
-
- {flexRender(cell.column.columnDef.cell, cell.getContext())} -
-
-
-
{'
(relative)'}
-
-
-
- {table.getHeaderGroups().map(headerGroup => ( -
- {headerGroup.headers.map(header => ( -
- {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} -
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} +
header.column.resetSize(), + onMouseDown: header.getResizeHandler(), + onTouchStart: header.getResizeHandler(), + className: `resizer ${ + table.options.columnResizeDirection + } ${ + header.column.getIsResizing() ? 'isResizing' : '' + }`, + style: { + transform: + columnResizeMode === 'onEnd' && + header.column.getIsResizing() + ? `translateX(${ + (table.options.columnResizeDirection === + 'rtl' + ? -1 + : 1) * + (table.getState().columnSizingInfo + .deltaOffset ?? 0) + }px)` + : '', + }, + }} + /> +
- - ))} - - ))} - + > + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} +
+ +
+
{'
(relative)'}
+
- {table.getRowModel().rows.map(row => ( -
- {row.getVisibleCells().map(cell => ( -
- {flexRender(cell.column.columnDef.cell, cell.getContext())} -
- ))} -
- ))} -
-
-
-
-
{'
(absolute positioning)'}
-
-
-
- {table.getHeaderGroups().map(headerGroup => ( -
- {headerGroup.headers.map(header => ( -
- {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} +
+ {table.getHeaderGroups().map(headerGroup => ( +
+ {headerGroup.headers.map(header => (
-
- ))} -
- ))} + > + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} +
header.column.resetSize(), + onMouseDown: header.getResizeHandler(), + onTouchStart: header.getResizeHandler(), + className: `resizer ${ + table.options.columnResizeDirection + } ${ + header.column.getIsResizing() ? 'isResizing' : '' + }`, + style: { + transform: + columnResizeMode === 'onEnd' && + header.column.getIsResizing() + ? `translateX(${ + (table.options.columnResizeDirection === + 'rtl' + ? -1 + : 1) * + (table.getState().columnSizingInfo + .deltaOffset ?? 0) + }px)` + : '', + }, + }} + /> +
+ ))} +
+ ))} +
+
+ {table.getRowModel().rows.map(row => ( +
+ {row.getVisibleCells().map(cell => ( +
+ {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} +
+ ))} +
+ ))} +
+
+
+
{'
(absolute positioning)'}
+
- {table.getRowModel().rows.map(row => ( -
- {row.getVisibleCells().map(cell => ( -
- {flexRender(cell.column.columnDef.cell, cell.getContext())} -
- ))} -
- ))} +
+ {table.getHeaderGroups().map(headerGroup => ( +
+ {headerGroup.headers.map(header => ( +
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} +
header.column.resetSize(), + onMouseDown: header.getResizeHandler(), + onTouchStart: header.getResizeHandler(), + className: `resizer ${ + table.options.columnResizeDirection + } ${ + header.column.getIsResizing() ? 'isResizing' : '' + }`, + style: { + transform: + columnResizeMode === 'onEnd' && + header.column.getIsResizing() + ? `translateX(${ + (table.options.columnResizeDirection === + 'rtl' + ? -1 + : 1) * + (table.getState().columnSizingInfo + .deltaOffset ?? 0) + }px)` + : '', + }, + }} + /> +
+ ))} +
+ ))} +
+
+ {table.getRowModel().rows.map(row => ( +
+ {row.getVisibleCells().map(cell => ( +
+ {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} +
+ ))} +
+ ))} +
diff --git a/package.json b/package.json index 0ff6fbd9ea..7a691a04f5 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "./examples/react/column-groups", "./examples/react/column-ordering", "./examples/react/column-pinning", + "./examples/react/column-resizing-performant", "./examples/react/column-sizing", "./examples/react/column-visibility", "./examples/react/editable-data", diff --git a/packages/table-core/src/features/ColumnSizing.ts b/packages/table-core/src/features/ColumnSizing.ts index 452cf8dc1f..6506bae4d5 100644 --- a/packages/table-core/src/features/ColumnSizing.ts +++ b/packages/table-core/src/features/ColumnSizing.ts @@ -40,7 +40,7 @@ export interface ColumnSizingOptions { enableColumnResizing?: boolean /** * Enables or disables right-to-left support for resizing the column. defaults to 'ltr'. - * @link [API Docs](https://tanstack.com/table/v8/docs/api/features/column-sizing#rtl) + * @link [API Docs](https://tanstack.com/table/v8/docs/api/features/column-sizing#columnResizeDirection) * @link [Guide](https://tanstack.com/table/v8/docs/guide/column-sizing) */ columnResizeDirection?: ColumnResizeDirection diff --git a/tsconfig.json b/tsconfig.json index c5676bd608..449d692b49 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,6 +19,9 @@ { "path": "examples/react/column-pinning/tsconfig.dev.json" }, + { + "path": "examples/react/column-resizing-performant/tsconfig.dev.json" + }, { "path": "examples/react/column-sizing/tsconfig.dev.json" },