From 1189c3391d360c94655a0b6e2e6bd1972f307b49 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Sun, 2 Aug 2026 17:00:13 -0500 Subject: [PATCH] fix(table-core): wire expansion auto-reset into the core row model and guard first runs Auto-resets are `onAfterUpdate` hooks on the row model stage memos. Two related defects made them fire in the wrong places. Expansion auto-reset was only wired from the grouped row model, so `autoResetExpanded` silently did nothing for tables that use expansion without grouping (#5801). It is now wired from `createCoreRowModel` alongside the existing pageIndex, sorting, and cell-selection resets, so a data reference change resets expansion regardless of which features are installed. `table_autoResetExpanded` gained the same feature guard the sorting and cell-selection resets already use, since the core row model runs on tables without the expanding feature. Auto-resets also fired on the very first computation of every stage (#5968). `memo` seeds its dependency list to `[]`, so the initial run always compares as changed, and merely reading a row model on mount scheduled resets. For uncontrolled tables this was self-cancelling (the reset targets `initialState`), which is why it went unnoticed, but it wiped a seeded `initialState.pagination.pageIndex` and pushed unsolicited `onExpandedChange` / `onPaginationChange` calls at controlled consumers on mount. v7 and v8 suppressed the first run with a `registered` flag; the v9 rewrite dropped it. A new `skipFirstRun` util restores that suppression. It is applied in the row model factories, which already run once per table, so each table gets its own flag. The grouped row model tracked previous inputs already and only needed its first-run condition inverted. The worker bridge reports every stage as changed in its first response, so it carries an equivalent `hasAppliedResults` flag. Landing order matters: wiring expansion into the core row model without the first-run guard would have extended the mount-time wipe from grouped tables to every table using expansion, so both halves ship together. - fix: wire `table_autoResetExpanded` into `createCoreRowModel` (#5801) - fix: skip auto-resets on the first computation of each stage (#5968) - add `skipFirstRun` util; apply to core, filtered, and sorted row models - invert the grouped row model's first-run condition - guard `table_autoResetExpanded` when the expanding feature is absent - skip auto-resets on the worker bridge's first applied result - tests: expansion reset without the grouping feature; first-run guard suite covering seeded initial state and controlled-state consumers Examples: migrate the basic examples to `createColumnHelper` across all frameworks, document more table options inline as commented-out defaults, and replace the `filterFns.between` workaround in the React expanding example with the `filterFn_between` individual export that already existed. Closes #5801 Closes #5968 Supersedes #6443 Co-Authored-By: Claude Opus 5 (1M context) --- examples/alpine/aggregation/src/main.ts | 1 + examples/alpine/basic-create-table/index.html | 2 +- .../alpine/basic-create-table/src/main.ts | 52 +++++------ examples/alpine/column-sizing/src/main.ts | 1 + examples/alpine/expanding/src/main.ts | 9 +- examples/alpine/row-pinning/src/main.ts | 1 + examples/alpine/row-selection/src/main.ts | 1 + examples/angular/aggregation/src/app/app.ts | 1 + .../angular/basic-inject-table/src/app/app.ts | 54 ++++++------ examples/angular/column-sizing/src/app/app.ts | 1 + examples/angular/expanding/src/app/app.html | 26 ++++++ examples/angular/expanding/src/app/app.ts | 53 ++++++++++- examples/angular/row-pinning/src/app/app.ts | 5 +- examples/angular/row-selection/src/app/app.ts | 1 + .../aggregation/app/templates/application.gts | 1 + .../app/templates/application.gts | 1 + .../expanding/app/templates/application.gts | 9 +- .../row-pinning/app/templates/application.gts | 5 +- .../app/templates/application.gts | 1 + examples/lit/aggregation/src/main.ts | 1 + examples/lit/column-sizing/src/main.ts | 1 + examples/lit/expanding/src/main.ts | 9 +- examples/lit/row-pinning/src/main.ts | 1 + examples/lit/row-selection/src/main.ts | 1 + examples/octane/aggregation/src/main.tsrx | 1 + examples/octane/basic-use-table/src/main.tsrx | 51 ++++++----- examples/octane/column-sizing/src/main.tsrx | 1 + examples/octane/expanding/src/main.tsrx | 9 +- examples/octane/row-pinning/src/main.tsrx | 5 +- examples/octane/row-selection/src/main.tsrx | 1 + examples/preact/aggregation/src/main.tsx | 1 + examples/preact/basic-use-table/src/main.tsx | 54 ++++++------ examples/preact/column-sizing/src/main.tsx | 1 + examples/preact/expanding/src/main.tsx | 9 +- examples/preact/row-pinning/src/main.tsx | 5 +- examples/preact/row-selection/src/main.tsx | 1 + examples/react/aggregation/src/main.tsx | 3 + examples/react/basic-use-table/src/main.tsx | 54 ++++++------ examples/react/column-pinning/src/main.tsx | 1 + examples/react/column-resizing/src/main.tsx | 2 + examples/react/column-sizing/src/main.tsx | 1 + examples/react/column-visibility/src/main.tsx | 1 + examples/react/expanding/src/main.tsx | 12 ++- examples/react/filters/src/main.tsx | 1 + examples/react/grouping/src/main.tsx | 1 + examples/react/row-pinning/src/main.tsx | 5 +- examples/react/row-selection/src/main.tsx | 1 + examples/solid/aggregation/src/App.tsx | 1 + examples/solid/basic-use-table/src/App.tsx | 55 ++++++------ examples/solid/column-sizing/src/App.tsx | 1 + examples/solid/expanding/src/App.tsx | 9 +- examples/solid/row-pinning/src/App.tsx | 5 +- examples/solid/row-selection/src/App.tsx | 1 + examples/svelte/aggregation/src/App.svelte | 4 +- .../svelte/basic-create-table/src/App.svelte | 49 +++++------ examples/svelte/column-sizing/src/App.svelte | 1 + examples/svelte/expanding/src/App.svelte | 9 +- examples/svelte/row-pinning/src/App.svelte | 5 +- examples/svelte/row-selection/src/App.svelte | 1 + examples/vanilla/aggregation/src/main.ts | 1 + examples/vue/aggregation/src/App.vue | 1 + examples/vue/basic-use-table/src/App.tsx | 53 +++++------ examples/vue/column-sizing/src/App.vue | 1 + examples/vue/expanding/src/App.tsx | 9 +- examples/vue/row-pinning/src/App.tsx | 5 +- examples/vue/row-selection/src/App.vue | 1 + .../src/core/row-models/createCoreRowModel.ts | 8 +- .../createFilteredRowModel.ts | 4 +- .../column-grouping/createGroupedRowModel.ts | 8 +- .../rowExpandingFeature.utils.ts | 4 + .../row-sorting/createSortedRowModel.ts | 8 +- packages/table-core/src/utils.ts | 19 ++++ .../src/worker/createTableWorker.ts | 10 ++- .../implementation/core/autoReset.test.ts | 88 ++++++++++++++++--- 74 files changed, 559 insertions(+), 261 deletions(-) diff --git a/examples/alpine/aggregation/src/main.ts b/examples/alpine/aggregation/src/main.ts index b8fc8e806a..f08851ba31 100644 --- a/examples/alpine/aggregation/src/main.ts +++ b/examples/alpine/aggregation/src/main.ts @@ -103,6 +103,7 @@ Alpine.data('table', () => { return { rowSource: local.rowSource } }, initialState: { pagination: { pageIndex: 0, pageSize: 10 } }, + // manualAggregation: true, // supply aggregate values yourself instead of calculating them locally debugTable: true, debugColumns: true, }) diff --git a/examples/alpine/basic-create-table/index.html b/examples/alpine/basic-create-table/index.html index f0d57a7ff9..72219dae0f 100644 --- a/examples/alpine/basic-create-table/index.html +++ b/examples/alpine/basic-create-table/index.html @@ -6,7 +6,7 @@ TanStack Alpine Table - Basic Create Table - +
diff --git a/examples/alpine/basic-create-table/src/main.ts b/examples/alpine/basic-create-table/src/main.ts index f736bffd42..19f88508bf 100644 --- a/examples/alpine/basic-create-table/src/main.ts +++ b/examples/alpine/basic-create-table/src/main.ts @@ -1,8 +1,12 @@ import Alpine from 'alpinejs' -import { FlexRender, createTable, tableFeatures } from '@tanstack/alpine-table' +import { + FlexRender, + createColumnHelper, + createTable, + tableFeatures, +} from '@tanstack/alpine-table' import { makeData } from './makeData' import './index.css' -import type { ColumnDef } from '@tanstack/alpine-table' import type { Person } from './makeData' // This example uses the standalone `createTable` function to create a table without the `createTableHook` util. @@ -10,46 +14,42 @@ import type { Person } from './makeData' // 1. New in V9! Tell the table which features and row models we want to use. In this case, this will be a basic table with no additional features const features = tableFeatures({}) // util method to create sharable TFeatures object/type -// 4. Define the columns for your table. This uses the new `ColumnDef` type to define columns. -// Alternatively, check out the createTableHook/createAppColumnHelper util for an even more type-safe way to define columns. -const columns: Array> = [ - { - accessorKey: 'firstName', // accessorKey method (most common for simple use-cases) +// 2. Create a column helper with the table features and row type +const columnHelper = createColumnHelper() + +// 3. Define the columns for your table with the column helper +const columns = columnHelper.columns([ + columnHelper.accessor('firstName', { header: 'First Name', cell: (info) => info.getValue(), - }, - { - accessorFn: (row) => row.lastName, // accessorFn used (alternative) along with a custom id + }), + columnHelper.accessor((row) => row.lastName, { id: 'lastName', header: () => 'Last Name', cell: (info) => info.getValue(), - }, - { - accessorFn: (row) => Number(row.age), // accessorFn used to transform the data + }), + columnHelper.accessor((row) => Number(row.age), { id: 'age', header: () => 'Age', cell: (info) => info.renderValue(), - }, - { - accessorKey: 'visits', + }), + columnHelper.accessor('visits', { header: () => 'Visits', - }, - { - accessorKey: 'status', + }), + columnHelper.accessor('status', { header: 'Status', - }, - { - accessorKey: 'progress', + }), + columnHelper.accessor('progress', { header: 'Profile Progress', - }, -] + }), +]) -// 5. Register the Alpine component. Store data in Alpine-reactive state so the +// 4. Register the Alpine component. Store data in Alpine-reactive state so the // buttons can swap it out and the table re-renders. Alpine.data('table', () => { const local = Alpine.reactive({ data: makeData(20) }) - // 6. Create the table instance with required features, columns, and data + // 5. Create the table instance with required features, columns, and data const table = createTable({ debugTable: true, // optionally, enable console logging debug messages features, // new required option in V9. Tell the table which features you are importing and using (better tree-shaking) diff --git a/examples/alpine/column-sizing/src/main.ts b/examples/alpine/column-sizing/src/main.ts index fdbedda9f2..fbdcfb514f 100644 --- a/examples/alpine/column-sizing/src/main.ts +++ b/examples/alpine/column-sizing/src/main.ts @@ -64,6 +64,7 @@ Alpine.data('table', () => { }, columnResizeMode: 'onChange', columnResizeDirection: 'ltr', + // defaultColumn: { size: 150, minSize: 50, maxSize: 500 }, // set sizing defaults for every column // initialState: { columnSizing: { firstName: 200 } }, // set column sizes on first render // atoms: { columnSizing: columnSizingAtom }, // preferred: own sizing state with an external atom // state: { columnSizing }, // classic controlled state; pair with onColumnSizingChange diff --git a/examples/alpine/expanding/src/main.ts b/examples/alpine/expanding/src/main.ts index 8e14407c36..aceb0dfb89 100644 --- a/examples/alpine/expanding/src/main.ts +++ b/examples/alpine/expanding/src/main.ts @@ -98,7 +98,11 @@ Alpine.data('table', () => { get data() { return local.data }, - getSubRows: (row) => row.subRows, + getSubRows: (row) => row.subRows, // tell the table where nested rows live + // enableRowSelection: row => row.original.age > 18, // enable selection conditionally; default true + // enableMultiRowSelection: false, // allow only one selected row at a time; default true + // enableSubRowSelection: false, // disable sub-row selection; default true + // enableRowRangeSelection: false, // disable shift-click range selection; default true // initialState: { expanded: { '0': true } }, // expand rows on first render // atoms: { expanded: expandedAtom }, // preferred: own expanded state with an external atom // state: { expanded }, // classic controlled state; pair with onExpandedChange @@ -110,8 +114,11 @@ Alpine.data('table', () => { // paginateExpandedRows: false, // keep expanded children on their parent page; default true // autoResetExpanded: false, // keep expanded rows after page-altering changes; default true // autoResetAll: false, // turn off every feature's automatic reset, including expansion + // enableFilters: false, // disable all column and global filtering; default true + // enableColumnFilters: false, // disable per-column filters; default true // filterFromLeafRows: true, // with filtering, keep parents whose descendants match // maxLeafRowFilterDepth: 0, // with filtering, only filter root rows + // manualFiltering: true, // pass data that is already filtered, for example from a server debugTable: true, }) diff --git a/examples/alpine/row-pinning/src/main.ts b/examples/alpine/row-pinning/src/main.ts index af7678e85c..6f4463d4e7 100644 --- a/examples/alpine/row-pinning/src/main.ts +++ b/examples/alpine/row-pinning/src/main.ts @@ -99,6 +99,7 @@ Alpine.data('table', () => { }, initialState: { pagination: { pageSize: 20, pageIndex: 0 }, + // rowPinning: { top: ['0'], bottom: ['1'] }, // pin rows on first render }, getSubRows: (row) => row.subRows, keepPinnedRows: true, diff --git a/examples/alpine/row-selection/src/main.ts b/examples/alpine/row-selection/src/main.ts index c4f38e18ad..1343ffafee 100644 --- a/examples/alpine/row-selection/src/main.ts +++ b/examples/alpine/row-selection/src/main.ts @@ -82,6 +82,7 @@ Alpine.data('table', () => { return local.data }, enableRowSelection: true, + // enableRowSelection: row => row.original.age > 18, // or enable selection conditionally // initialState: { rowSelection: { '0': true } }, // select rows on first render // atoms: { rowSelection: rowSelectionAtom }, // preferred: own selection state with an external atom // state: { rowSelection }, // classic controlled state; pair with onRowSelectionChange diff --git a/examples/angular/aggregation/src/app/app.ts b/examples/angular/aggregation/src/app/app.ts index 7d8f117bd8..77b2872f6c 100644 --- a/examples/angular/aggregation/src/app/app.ts +++ b/examples/angular/aggregation/src/app/app.ts @@ -101,6 +101,7 @@ export class App { data: this.data(), meta: { rowSource: this.rowSource() }, initialState: { pagination: { pageIndex: 0, pageSize: 10 } }, + // manualAggregation: true, // supply aggregate values yourself instead of calculating them locally debugTable: true, debugColumns: true, })) diff --git a/examples/angular/basic-inject-table/src/app/app.ts b/examples/angular/basic-inject-table/src/app/app.ts index e03448c5d7..0a3af5e14f 100644 --- a/examples/angular/basic-inject-table/src/app/app.ts +++ b/examples/angular/basic-inject-table/src/app/app.ts @@ -5,9 +5,13 @@ import { inject, signal, } from '@angular/core' -import { FlexRender, injectTable, tableFeatures } from '@tanstack/angular-table' +import { + FlexRender, + createColumnHelper, + injectTable, + tableFeatures, +} from '@tanstack/angular-table' import { injectTanStackTableDevtools } from '@tanstack/angular-table-devtools' -import type { ColumnDef } from '@tanstack/angular-table' // This example uses the Angular standalone `injectTable` helper to create a table without the `createTableHook` util. @@ -61,39 +65,35 @@ const defaultData: Array = [ // In this case, this will be a basic table with no additional features const features = tableFeatures({}) -// 4. Define the columns for your table. This uses the new `ColumnDef` type to define columns. -// Alternatively, check out the createTableHook/createColumnHelper util for an even more type-safe way to define columns. -const columns: Array> = [ - { - accessorKey: 'firstName', +// 4. Create a column helper with the table features and row type +const columnHelper = createColumnHelper() + +// 5. Define the columns for your table with the column helper +const columns = columnHelper.columns([ + columnHelper.accessor('firstName', { header: 'First Name', cell: (info) => info.getValue(), - }, - { - accessorFn: (row) => row.lastName, + }), + columnHelper.accessor((row) => row.lastName, { id: 'lastName', header: () => 'Last Name', - cell: (info) => info.getValue(), - }, - { - accessorFn: (row) => Number(row.age), + cell: (info) => info.getValue(), + }), + columnHelper.accessor((row) => Number(row.age), { id: 'age', header: () => 'Age', cell: (info) => info.renderValue(), - }, - { - accessorKey: 'visits', + }), + columnHelper.accessor('visits', { header: () => 'Visits', - }, - { - accessorKey: 'status', + }), + columnHelper.accessor('status', { header: 'Status', - }, - { - accessorKey: 'progress', + }), + columnHelper.accessor('progress', { header: 'Profile Progress', - }, -] + }), +]) @Component({ selector: 'app-root', @@ -104,10 +104,10 @@ const columns: Array> = [ export class App { private readonly injector = inject(Injector) - // 5. Store data with a stable reference + // 6. Store data with a stable reference readonly data = signal>([...defaultData]) - // 6. Create the table instance with required features, columns, and data + // 7. Create the table instance with required features, columns, and data readonly table = injectTable(() => ({ key: 'basic-inject-table', // needed for devtools debugTable: true, diff --git a/examples/angular/column-sizing/src/app/app.ts b/examples/angular/column-sizing/src/app/app.ts index 43f3aa816b..17d49e41fc 100644 --- a/examples/angular/column-sizing/src/app/app.ts +++ b/examples/angular/column-sizing/src/app/app.ts @@ -65,6 +65,7 @@ export class App { features, columns, data: this.data(), + // defaultColumn: { size: 150, minSize: 50, maxSize: 500 }, // set sizing defaults for every column // initialState: { columnSizing: { firstName: 200 } }, // set column sizes on first render // atoms: { columnSizing: columnSizingAtom }, // preferred: own sizing state with an external atom // state: { columnSizing }, // classic controlled state; pair with onColumnSizingChange diff --git a/examples/angular/expanding/src/app/app.html b/examples/angular/expanding/src/app/app.html index c5fd3fc79f..04a61ca99b 100644 --- a/examples/angular/expanding/src/app/app.html +++ b/examples/angular/expanding/src/app/app.html @@ -22,6 +22,32 @@ >
+ + @if (header.column.getCanFilter()) { +
+ @if (isNumberColumn(header.column)) { + + + } @else { + + } +
+ } } } diff --git a/examples/angular/expanding/src/app/app.ts b/examples/angular/expanding/src/app/app.ts index 13f3e5612c..e6213ff195 100644 --- a/examples/angular/expanding/src/app/app.ts +++ b/examples/angular/expanding/src/app/app.ts @@ -1,8 +1,12 @@ import { ChangeDetectionStrategy, Component, signal } from '@angular/core' import { FlexRender, + columnFilteringFeature, createExpandedRowModel, + createFilteredRowModel, createPaginatedRowModel, + filterFn_inNumberRange, + filterFn_includesString, flexRenderComponent, injectTable, rowExpandingFeature, @@ -17,14 +21,20 @@ import { ExpandableHeaderCell, } from './expandable-cell/expandable-cell' import type { Person } from './makeData' -import type { ColumnDef, ExpandedState } from '@tanstack/angular-table' +import type { Column, ColumnDef, ExpandedState } from '@tanstack/angular-table' export const features = tableFeatures({ + columnFilteringFeature, rowExpandingFeature: rowExpandingFeature, rowPaginationFeature: rowPaginationFeature, rowSelectionFeature: rowSelectionFeature, + filteredRowModel: createFilteredRowModel(), paginatedRowModel: createPaginatedRowModel(), expandedRowModel: createExpandedRowModel(), + filterFns: { + includesString: filterFn_includesString, + inNumberRange: filterFn_inNumberRange, + }, }) const defaultColumns: Array> = [ @@ -93,7 +103,11 @@ export class App { typeof updater === 'function' ? this.expanded.update(updater) : this.expanded.set(updater), - getSubRows: (row) => row.subRows, + getSubRows: (row) => row.subRows, // tell the table where nested rows live + // enableRowSelection: row => row.original.age > 18, // enable selection conditionally; default true + // enableMultiRowSelection: false, // allow only one selected row at a time; default true + // enableSubRowSelection: false, // disable sub-row selection; default true + // enableRowRangeSelection: false, // disable shift-click range selection; default true // initialState: { expanded: { '0': true } }, // expand rows on first render // atoms: { expanded: expandedAtom }, // preferred: own expanded state with an external atom // enableExpanding: false, // disable expanding for every row; default true @@ -103,8 +117,11 @@ export class App { // paginateExpandedRows: false, // keep expanded children on their parent page; default true // autoResetExpanded: false, // keep expanded rows after page-altering changes; default true // autoResetAll: false, // turn off every feature's automatic reset, including expansion + // enableFilters: false, // disable all column and global filtering; default true + // enableColumnFilters: false, // disable per-column filters; default true // filterFromLeafRows: true, // with filtering, keep parents whose descendants match // maxLeafRowFilterDepth: 0, // with filtering, only filter root rows + // manualFiltering: true, // pass data that is already filtered, for example from a server debugTable: true, })) @@ -122,6 +139,38 @@ export class App { this.table.setPageSize(Number(event.target.value)) } + isNumberColumn(column: Column): boolean { + const firstValue = this.table + .getPreFilteredRowModel() + .flatRows[0]?.getValue(column.id) + return typeof firstValue === 'number' + } + + setTextFilter(column: Column, event: Event): void { + column.setFilterValue((event.target as HTMLInputElement).value) + } + + getNumberFilterValue( + column: Column, + index: 0 | 1, + ): number | '' { + const value = column.getFilterValue() as [number?, number?] | undefined + return value?.[index] ?? '' + } + + setNumberFilter( + column: Column, + event: Event, + index: 0 | 1, + ): void { + const value = (event.target as HTMLInputElement).value + column.setFilterValue((old: [number?, number?] | undefined) => { + const next: [number?, number?] = [...(old ?? [])] + next[index] = value === '' ? undefined : Number(value) + return next + }) + } + refreshData = () => this.data.set(makeData(100, 5, 3)) stressTest = () => this.data.set(makeData(10_000, 5, 3)) } diff --git a/examples/angular/row-pinning/src/app/app.ts b/examples/angular/row-pinning/src/app/app.ts index d656146a2b..886601cda9 100644 --- a/examples/angular/row-pinning/src/app/app.ts +++ b/examples/angular/row-pinning/src/app/app.ts @@ -73,7 +73,10 @@ export class App { features, columns, data: this.data(), - initialState: { pagination: { pageSize: 20, pageIndex: 0 } }, + initialState: { + pagination: { pageSize: 20, pageIndex: 0 }, + // rowPinning: { top: ['0'], bottom: ['1'] }, // pin rows on first render + }, state: { expanded: this.expanded(), rowPinning: this.rowPinning() }, onExpandedChange: (updater: Updater) => isFunction(updater) diff --git a/examples/angular/row-selection/src/app/app.ts b/examples/angular/row-selection/src/app/app.ts index 98008cf5c2..44c2a11014 100644 --- a/examples/angular/row-selection/src/app/app.ts +++ b/examples/angular/row-selection/src/app/app.ts @@ -102,6 +102,7 @@ export class App { }, enableRowSelection: this.enableRowSelection(), // enable row selection for all rows + // enableRowSelection: row => row.original.age > 18, // or enable selection conditionally onRowSelectionChange: (updaterOrValue) => { this.rowSelection.set( typeof updaterOrValue === 'function' diff --git a/examples/ember/aggregation/app/templates/application.gts b/examples/ember/aggregation/app/templates/application.gts index d4de45eafa..810271e171 100644 --- a/examples/ember/aggregation/app/templates/application.gts +++ b/examples/ember/aggregation/app/templates/application.gts @@ -121,6 +121,7 @@ export default class PaginationTable extends Component { data: this.data, meta: { rowSource: this.rowSource }, initialState: { pagination: { pageIndex: 0, pageSize: 10 } }, + // manualAggregation: true, // supply aggregate values yourself instead of calculating them locally // initialState: { pagination: { pageIndex: 1, pageSize: 20 } }, // set the initial page once // atoms: { pagination: paginationAtom }, // preferred: own pagination state with an external atom // state: { pagination }, // classic controlled state; pair with onPaginationChange diff --git a/examples/ember/column-sizing/app/templates/application.gts b/examples/ember/column-sizing/app/templates/application.gts index a03fc81dfc..aae91abb30 100644 --- a/examples/ember/column-sizing/app/templates/application.gts +++ b/examples/ember/column-sizing/app/templates/application.gts @@ -82,6 +82,7 @@ export default class ColumnSizingTable extends Component { features, columns, data: this.data, + // defaultColumn: { size: 150, minSize: 50, maxSize: 500 }, // set sizing defaults for every column // initialState: { columnSizing: { firstName: 200 } }, // set column sizes on first render // atoms: { columnSizing: columnSizingAtom }, // preferred: own sizing state with an external atom // state: { columnSizing }, // classic controlled state; pair with onColumnSizingChange diff --git a/examples/ember/expanding/app/templates/application.gts b/examples/ember/expanding/app/templates/application.gts index c4213b3266..b4f37e5951 100644 --- a/examples/ember/expanding/app/templates/application.gts +++ b/examples/ember/expanding/app/templates/application.gts @@ -231,8 +231,12 @@ export default class ExpandingTable extends Component { features, columns, data: this.data, - getSubRows: (row: Person) => row.subRows, + getSubRows: (row: Person) => row.subRows, // tell the table where nested rows live getRowCanExpand: () => true, + // enableRowSelection: row => row.original.age > 18, // enable selection conditionally; default true + // enableMultiRowSelection: false, // allow only one selected row at a time; default true + // enableSubRowSelection: false, // disable sub-row selection; default true + // enableRowRangeSelection: false, // disable shift-click range selection; default true // initialState: { expanded: { '0': true } }, // expand rows on first render // atoms: { expanded: expandedAtom }, // preferred: own expanded state with an external atom // state: { expanded }, // classic controlled state; pair with onExpandedChange @@ -243,8 +247,11 @@ export default class ExpandingTable extends Component { // paginateExpandedRows: false, // keep expanded children on their parent page; default true // autoResetExpanded: false, // keep expanded rows after page-altering changes; default true // autoResetAll: false, // turn off every feature's automatic reset, including expansion + // enableFilters: false, // disable all column and global filtering; default true + // enableColumnFilters: false, // disable per-column filters; default true // filterFromLeafRows: true, // with filtering, keep parents whose descendants match // maxLeafRowFilterDepth: 0, // with filtering, only filter root rows + // manualFiltering: true, // pass data that is already filtered, for example from a server })) get headerGroups() { diff --git a/examples/ember/row-pinning/app/templates/application.gts b/examples/ember/row-pinning/app/templates/application.gts index 9e5da9824d..724821a51f 100644 --- a/examples/ember/row-pinning/app/templates/application.gts +++ b/examples/ember/row-pinning/app/templates/application.gts @@ -114,7 +114,10 @@ export default class RowPinningTable extends Component { getRowId: (row: Person, index: number) => `${row.firstName}-${row.lastName}-${index}`, getSubRows: (row: Person) => row.subRows, - initialState: { pagination: { pageSize: 20, pageIndex: 0 } }, + initialState: { + pagination: { pageSize: 20, pageIndex: 0 }, + // rowPinning: { top: ['0'], bottom: ['1'] }, // pin rows on first render + }, state: { rowPinning: this.rowPinning, }, diff --git a/examples/ember/row-selection/app/templates/application.gts b/examples/ember/row-selection/app/templates/application.gts index 4d5a9be8fe..818df50ad8 100644 --- a/examples/ember/row-selection/app/templates/application.gts +++ b/examples/ember/row-selection/app/templates/application.gts @@ -180,6 +180,7 @@ export default class RowSelectionTable extends Component { rowSelection: this.rowSelection, }, enableRowSelection: true, + // enableRowSelection: row => row.original.age > 18, // or enable selection conditionally onRowSelectionChange: (updater) => { this.rowSelection = typeof updater === 'function' ? updater(this.rowSelection) : updater diff --git a/examples/lit/aggregation/src/main.ts b/examples/lit/aggregation/src/main.ts index 46fa6f764c..22b5cfec16 100644 --- a/examples/lit/aggregation/src/main.ts +++ b/examples/lit/aggregation/src/main.ts @@ -113,6 +113,7 @@ class LitTableExample extends LitElement { data: this._data, meta: { rowSource: this._rowSource }, initialState: { pagination: { pageIndex: 0, pageSize: 10 } }, + // manualAggregation: true, // supply aggregate values yourself instead of calculating them locally // initialState: { pagination: { pageIndex: 1, pageSize: 20 } }, // set the initial page once // atoms: { pagination: paginationAtom }, // preferred: own pagination state with an external atom // state: { pagination }, // classic controlled state; pair with onPaginationChange diff --git a/examples/lit/column-sizing/src/main.ts b/examples/lit/column-sizing/src/main.ts index 0479eb7e83..474244d5ed 100644 --- a/examples/lit/column-sizing/src/main.ts +++ b/examples/lit/column-sizing/src/main.ts @@ -68,6 +68,7 @@ class LitTableExample extends LitElement { columns, columnResizeMode: 'onChange', columnResizeDirection: 'ltr', + // defaultColumn: { size: 150, minSize: 50, maxSize: 500 }, // set sizing defaults for every column // initialState: { columnSizing: { firstName: 200 } }, // set column sizes on first render // atoms: { columnSizing: columnSizingAtom }, // preferred: own sizing state with an external atom // state: { columnSizing }, // classic controlled state; pair with onColumnSizingChange diff --git a/examples/lit/expanding/src/main.ts b/examples/lit/expanding/src/main.ts index f245f9024d..1905b411b4 100644 --- a/examples/lit/expanding/src/main.ts +++ b/examples/lit/expanding/src/main.ts @@ -201,7 +201,11 @@ class LitTableExample extends LitElement { features, columns, data: this._data, - getSubRows: (row) => row.subRows, + getSubRows: (row) => row.subRows, // tell the table where nested rows live + // enableRowSelection: row => row.original.age > 18, // enable selection conditionally; default true + // enableMultiRowSelection: false, // allow only one selected row at a time; default true + // enableSubRowSelection: false, // disable sub-row selection; default true + // enableRowRangeSelection: false, // disable shift-click range selection; default true // initialState: { expanded: { '0': true } }, // expand rows on first render // atoms: { expanded: expandedAtom }, // preferred: own expanded state with an external atom // state: { expanded }, // classic controlled state; pair with onExpandedChange @@ -213,8 +217,11 @@ class LitTableExample extends LitElement { // paginateExpandedRows: false, // keep expanded children on their parent page; default true // autoResetExpanded: false, // keep expanded rows after page-altering changes; default true // autoResetAll: false, // turn off every feature's automatic reset, including expansion + // enableFilters: false, // disable all column and global filtering; default true + // enableColumnFilters: false, // disable per-column filters; default true // filterFromLeafRows: true, // with filtering, keep parents whose descendants match // maxLeafRowFilterDepth: 0, // with filtering, only filter root rows + // manualFiltering: true, // pass data that is already filtered, for example from a server debugTable: true, }, (state) => ({ diff --git a/examples/lit/row-pinning/src/main.ts b/examples/lit/row-pinning/src/main.ts index 85907a0843..6728f0ab08 100644 --- a/examples/lit/row-pinning/src/main.ts +++ b/examples/lit/row-pinning/src/main.ts @@ -191,6 +191,7 @@ class LitTableExample extends LitElement { data: this._data, initialState: { pagination: { pageSize: 20, pageIndex: 0 }, + // rowPinning: { top: ['0'], bottom: ['1'] }, // pin rows on first render }, getSubRows: (row) => row.subRows, keepPinnedRows: true, diff --git a/examples/lit/row-selection/src/main.ts b/examples/lit/row-selection/src/main.ts index c09184be78..d7d69f7695 100644 --- a/examples/lit/row-selection/src/main.ts +++ b/examples/lit/row-selection/src/main.ts @@ -100,6 +100,7 @@ class LitTableExample extends LitElement { data: this._data, columns, enableRowSelection: true, + // enableRowSelection: row => row.original.age > 18, // or enable selection conditionally // initialState: { rowSelection: { '0': true } }, // select rows on first render // atoms: { rowSelection: rowSelectionAtom }, // preferred: own selection state with an external atom // state: { rowSelection }, // classic controlled state; pair with onRowSelectionChange diff --git a/examples/octane/aggregation/src/main.tsrx b/examples/octane/aggregation/src/main.tsrx index 965baad9f6..408050ad23 100644 --- a/examples/octane/aggregation/src/main.tsrx +++ b/examples/octane/aggregation/src/main.tsrx @@ -128,6 +128,7 @@ function App() @{ data, meta: { rowSource }, initialState: { pagination: { pageIndex: 0, pageSize: 10 } }, + // manualAggregation: true, // supply aggregate values yourself instead of calculating them locally debugTable: true, debugColumns: true, }, diff --git a/examples/octane/basic-use-table/src/main.tsrx b/examples/octane/basic-use-table/src/main.tsrx index 26b61356d6..5e074ec448 100644 --- a/examples/octane/basic-use-table/src/main.tsrx +++ b/examples/octane/basic-use-table/src/main.tsrx @@ -1,6 +1,9 @@ import { createRoot, useState } from 'octane' -import { tableFeatures, useTable } from '@tanstack/octane-table' -import type { ColumnDef } from '@tanstack/octane-table' +import { + createColumnHelper, + tableFeatures, + useTable, +} from '@tanstack/octane-table' import './index.css' // This example uses the classic standalone `useTable` hook to create a table without the new `createTableHelper` util. @@ -26,41 +29,37 @@ const defaultData: Array = [ // 3. New in V9! Tell the table which features and row models we want to use. In this case, this will be a basic table with no additional features const features = tableFeatures({}) -// 4. Define the columns for your table. This uses the new `ColumnDef` type to define columns. Alternatively, check out the createTableHelper/createColumnHelper util for an even more type-safe way to define columns. -const columns: Array> = [ - { - // accessorKey method (most common for simple use-cases) - accessorKey: 'firstName', +// 4. Create a column helper with the table features and row type +const columnHelper = createColumnHelper() + +// 5. Define the columns for your table with the column helper +const columns = columnHelper.columns([ + columnHelper.accessor('firstName', { header: 'First Name', cell: (info) => String(info.getValue()), - }, - { - // accessorFn used (alternative) along with a custom id - accessorFn: (row) => row.lastName, + }), + columnHelper.accessor((row) => row.lastName, { id: 'lastName', header: () => Last Name, - cell: (info) => {String(info.getValue())}, - }, - { - // accessorFn used to transform the data - accessorFn: (row) => Number(row.age), + cell: (info) => {String(info.getValue())}, + }), + columnHelper.accessor((row) => Number(row.age), { id: 'age', header: () => 'Age', cell: (info) => String(info.renderValue()), - }, - { - accessorKey: 'visits', + }), + columnHelper.accessor('visits', { header: () => Visits, - }, - { accessorKey: 'status', header: 'Status' }, - { accessorKey: 'progress', header: 'Profile Progress' }, -] + }), + columnHelper.accessor('status', { header: 'Status' }), + columnHelper.accessor('progress', { header: 'Profile Progress' }), +]) function App() @{ - // 5. Store data with a stable reference + // 6. Store data with a stable reference const [data] = useState(() => [...defaultData]) - // 6. Create the table instance with required features, columns, and data + // 7. Create the table instance with required features, columns, and data const table = useTable( { debugTable: true, // optionally, enable console logging debug messages @@ -72,7 +71,7 @@ function App() @{ (state) => state, // default selector ) - // 7. Render your table markup from the table instance APIs + // 8. Render your table markup from the table instance APIs
diff --git a/examples/octane/column-sizing/src/main.tsrx b/examples/octane/column-sizing/src/main.tsrx index 3fe9336714..dc66bc1e6f 100644 --- a/examples/octane/column-sizing/src/main.tsrx +++ b/examples/octane/column-sizing/src/main.tsrx @@ -64,6 +64,7 @@ function App() @{ features, columns, data, + // defaultColumn: { size: 150, minSize: 50, maxSize: 500 }, // set sizing defaults for every column // initialState: { columnSizing: { firstName: 200 } }, // set column sizes on first render // atoms: { columnSizing: columnSizingAtom }, // preferred: own sizing state with an external atom // state: { columnSizing }, // classic controlled state; pair with onColumnSizingChange diff --git a/examples/octane/expanding/src/main.tsrx b/examples/octane/expanding/src/main.tsrx index 74db6976e8..132aa1bbfe 100644 --- a/examples/octane/expanding/src/main.tsrx +++ b/examples/octane/expanding/src/main.tsrx @@ -144,7 +144,11 @@ function App() @{ features, columns, data, - getSubRows: (row) => row.subRows, + getSubRows: (row) => row.subRows, // tell the table where nested rows live + // enableRowSelection: row => row.original.age > 18, // enable selection conditionally; default true + // enableMultiRowSelection: false, // allow only one selected row at a time; default true + // enableSubRowSelection: false, // disable sub-row selection; default true + // enableRowRangeSelection: false, // disable shift-click range selection; default true // initialState: { expanded: { '0': true } }, // expand rows on first render // atoms: { expanded: expandedAtom }, // preferred: own expanded state with an external atom // state: { expanded }, // classic controlled state; pair with onExpandedChange @@ -156,8 +160,11 @@ function App() @{ // paginateExpandedRows: false, // keep expanded children on their parent page; default true // autoResetExpanded: false, // keep expanded rows after page-altering changes; default true // autoResetAll: false, // turn off every feature's automatic reset, including expansion + // enableFilters: false, // disable all column and global filtering; default true + // enableColumnFilters: false, // disable per-column filters; default true // filterFromLeafRows: true, // with filtering, keep parents whose descendants match // maxLeafRowFilterDepth: 0, // with filtering, only filter root rows + // manualFiltering: true, // pass data that is already filtered, for example from a server debugTable: true, }, (state) => state, // default selector diff --git a/examples/octane/row-pinning/src/main.tsrx b/examples/octane/row-pinning/src/main.tsrx index 1d471707c7..76033fa9f0 100644 --- a/examples/octane/row-pinning/src/main.tsrx +++ b/examples/octane/row-pinning/src/main.tsrx @@ -159,7 +159,10 @@ function App() @{ features, columns, data, - initialState: { pagination: { pageSize: 20, pageIndex: 0 } }, + initialState: { + pagination: { pageSize: 20, pageIndex: 0 }, + // rowPinning: { top: ['0'], bottom: ['1'] }, // pin rows on first render + }, state: { expanded, rowPinning, diff --git a/examples/octane/row-selection/src/main.tsrx b/examples/octane/row-selection/src/main.tsrx index 9d043f51b7..05de0142f9 100644 --- a/examples/octane/row-selection/src/main.tsrx +++ b/examples/octane/row-selection/src/main.tsrx @@ -113,6 +113,7 @@ function App() @{ data, getRowId: (row) => row.id, enableRowSelection: true, // enable row selection for all rows + // enableRowSelection: row => row.original.age > 18, // or enable selection conditionally // initialState: { rowSelection: { '0': true } }, // select rows on first render // state: { rowSelection }, // classic controlled state; pair with onRowSelectionChange // onRowSelectionChange: setRowSelection, diff --git a/examples/preact/aggregation/src/main.tsx b/examples/preact/aggregation/src/main.tsx index 1ad9500ce6..849ec7a4ea 100644 --- a/examples/preact/aggregation/src/main.tsx +++ b/examples/preact/aggregation/src/main.tsx @@ -128,6 +128,7 @@ function App() { data, meta: { rowSource }, initialState: { pagination: { pageIndex: 0, pageSize: 10 } }, + // manualAggregation: true, // supply aggregate values yourself instead of calculating them locally debugTable: true, debugColumns: true, }, diff --git a/examples/preact/basic-use-table/src/main.tsx b/examples/preact/basic-use-table/src/main.tsx index 33af43dac5..7d9fba8c85 100644 --- a/examples/preact/basic-use-table/src/main.tsx +++ b/examples/preact/basic-use-table/src/main.tsx @@ -1,12 +1,15 @@ import { render } from 'preact' import { TanStackDevtools } from '@tanstack/preact-devtools' import { useState } from 'preact/hooks' -import { tableFeatures, useTable } from '@tanstack/preact-table' +import { + createColumnHelper, + tableFeatures, + useTable, +} from '@tanstack/preact-table' import { tableDevtoolsPlugin, useTanStackTableDevtools, } from '@tanstack/preact-table-devtools' -import type { ColumnDef } from '@tanstack/preact-table' import './index.css' // This example uses the classic standalone `useTable` hook to create a table without the new `createTableHelper` util. @@ -60,46 +63,43 @@ const defaultData: Array = [ // 3. New in V9! Tell the table which features and row models we want to use. In this case, this will be a basic table with no additional features const features = tableFeatures({}) // util method to create sharable TFeatures object/type -// 4. Define the columns for your table. This uses the new `ColumnDef` type to define columns. Alternatively, check out the createTableHelper/createColumnHelper util for an even more type-safe way to define columns. -const columns: Array> = [ - { - accessorKey: 'firstName', // accessorKey method (most common for simple use-cases) +// 4. Create a column helper with the table features and row type +const columnHelper = createColumnHelper() + +// 5. Define the columns for your table with the column helper +const columns = columnHelper.columns([ + columnHelper.accessor('firstName', { header: 'First Name', cell: (info) => info.getValue(), - }, - { - accessorFn: (row) => row.lastName, // accessorFn used (alternative) along with a custom id + }), + columnHelper.accessor((row) => row.lastName, { id: 'lastName', header: () => Last Name, - cell: (info) => {info.getValue()}, - }, - { - accessorFn: (row) => Number(row.age), // accessorFn used to transform the data + cell: (info) => {info.getValue()}, + }), + columnHelper.accessor((row) => Number(row.age), { id: 'age', header: () => 'Age', cell: (info) => { return info.renderValue() }, - }, - { - accessorKey: 'visits', + }), + columnHelper.accessor('visits', { header: () => Visits, - }, - { - accessorKey: 'status', + }), + columnHelper.accessor('status', { header: 'Status', - }, - { - accessorKey: 'progress', + }), + columnHelper.accessor('progress', { header: 'Profile Progress', - }, -] + }), +]) function App() { - // 5. Store data with a stable reference + // 6. Store data with a stable reference const [data, _setData] = useState(() => [...defaultData]) - // 6. Create the table instance with required features, columns, and data + // 7. Create the table instance with required features, columns, and data const table = useTable( { key: 'basic-use-table', // needed for devtools @@ -114,7 +114,7 @@ function App() { useTanStackTableDevtools(table) - // 7. Render your table markup from the table instance APIs + // 8. Render your table markup from the table instance APIs return (
diff --git a/examples/preact/column-sizing/src/main.tsx b/examples/preact/column-sizing/src/main.tsx index 88fec79966..dcf4819b5b 100644 --- a/examples/preact/column-sizing/src/main.tsx +++ b/examples/preact/column-sizing/src/main.tsx @@ -64,6 +64,7 @@ function App() { features, columns, data, + // defaultColumn: { size: 150, minSize: 50, maxSize: 500 }, // set sizing defaults for every column // initialState: { columnSizing: { firstName: 200 } }, // set column sizes on first render // atoms: { columnSizing: columnSizingAtom }, // preferred: own sizing state with an external atom // state: { columnSizing }, // classic controlled state; pair with onColumnSizingChange diff --git a/examples/preact/expanding/src/main.tsx b/examples/preact/expanding/src/main.tsx index ec362cd60e..02ba75d7aa 100644 --- a/examples/preact/expanding/src/main.tsx +++ b/examples/preact/expanding/src/main.tsx @@ -144,7 +144,11 @@ function App() { features, columns, data, - getSubRows: (row) => row.subRows, + getSubRows: (row) => row.subRows, // tell the table where nested rows live + // enableRowSelection: row => row.original.age > 18, // enable selection conditionally; default true + // enableMultiRowSelection: false, // allow only one selected row at a time; default true + // enableSubRowSelection: false, // disable sub-row selection; default true + // enableRowRangeSelection: false, // disable shift-click range selection; default true // initialState: { expanded: { '0': true } }, // expand rows on first render // atoms: { expanded: expandedAtom }, // preferred: own expanded state with an external atom // state: { expanded }, // classic controlled state; pair with onExpandedChange @@ -156,8 +160,11 @@ function App() { // paginateExpandedRows: false, // keep expanded children on their parent page; default true // autoResetExpanded: false, // keep expanded rows after page-altering changes; default true // autoResetAll: false, // turn off every feature's automatic reset, including expansion + // enableFilters: false, // disable all column and global filtering; default true + // enableColumnFilters: false, // disable per-column filters; default true // filterFromLeafRows: true, // with filtering, keep parents whose descendants match // maxLeafRowFilterDepth: 0, // with filtering, only filter root rows + // manualFiltering: true, // pass data that is already filtered, for example from a server debugTable: true, }, (state) => state, // default selector diff --git a/examples/preact/row-pinning/src/main.tsx b/examples/preact/row-pinning/src/main.tsx index 3d83b1347a..171ea56280 100644 --- a/examples/preact/row-pinning/src/main.tsx +++ b/examples/preact/row-pinning/src/main.tsx @@ -159,7 +159,10 @@ function App() { features, columns, data, - initialState: { pagination: { pageSize: 20, pageIndex: 0 } }, + initialState: { + pagination: { pageSize: 20, pageIndex: 0 }, + // rowPinning: { top: ['0'], bottom: ['1'] }, // pin rows on first render + }, state: { expanded, rowPinning, diff --git a/examples/preact/row-selection/src/main.tsx b/examples/preact/row-selection/src/main.tsx index 35c4d5f796..45916a6709 100644 --- a/examples/preact/row-selection/src/main.tsx +++ b/examples/preact/row-selection/src/main.tsx @@ -121,6 +121,7 @@ function App() { data, getRowId: (row) => row.id, enableRowSelection: true, // enable row selection for all rows + // enableRowSelection: row => row.original.age > 18, // or enable selection conditionally // initialState: { rowSelection: { '0': true } }, // select rows on first render // state: { rowSelection }, // classic controlled state; pair with onRowSelectionChange // onRowSelectionChange: setRowSelection, diff --git a/examples/react/aggregation/src/main.tsx b/examples/react/aggregation/src/main.tsx index dcda48bd78..d4892a0a19 100644 --- a/examples/react/aggregation/src/main.tsx +++ b/examples/react/aggregation/src/main.tsx @@ -106,6 +106,8 @@ function App() { columnHelper.accessor('amount', { header: 'Amount', aggregationFn: 'sum', + // maxAggregationDepth: 1, // include one level of descendants in this column's aggregate + // getAggregationValue: () => ({ value: 0 }), // provide a precomputed value and skip local fallback cell: ({ getValue }) => getValue().toLocaleString(), footer: ({ column, table }) => formatValue( @@ -141,6 +143,7 @@ function App() { data, meta: { rowSource }, initialState: { pagination: { pageIndex: 0, pageSize: 10 } }, + // manualAggregation: true, // supply aggregate values yourself instead of calculating them locally debugTable: true, debugColumns: true, }, diff --git a/examples/react/basic-use-table/src/main.tsx b/examples/react/basic-use-table/src/main.tsx index 7cecd1efa8..25b46fb1f7 100644 --- a/examples/react/basic-use-table/src/main.tsx +++ b/examples/react/basic-use-table/src/main.tsx @@ -1,12 +1,15 @@ import * as React from 'react' import { TanStackDevtools } from '@tanstack/react-devtools' import ReactDOM from 'react-dom/client' -import { tableFeatures, useTable } from '@tanstack/react-table' +import { + createColumnHelper, + tableFeatures, + useTable, +} from '@tanstack/react-table' import { tableDevtoolsPlugin, useTanStackTableDevtools, } from '@tanstack/react-table-devtools' -import type { ColumnDef } from '@tanstack/react-table' import './index.css' // This example uses the classic standalone `useTable` hook to create a table without the new `createTableHelper` util. @@ -60,46 +63,43 @@ const defaultData: Array = [ // 3. New in V9! Tell the table which features and row models we want to use. In this case, this will be a basic table with no additional features const features = tableFeatures({}) // util method to create sharable TFeatures object/type -// 4. Define the columns for your table. This uses the new `ColumnDef` type to define columns. Alternatively, check out the createTableHelper/createColumnHelper util for an even more type-safe way to define columns. -const columns: Array> = [ - { - accessorKey: 'firstName', // accessorKey method (most common for simple use-cases) +// 4. Create a column helper with the table features and row type +const columnHelper = createColumnHelper() + +// 5. Define the columns for your table with the column helper +const columns = columnHelper.columns([ + columnHelper.accessor('firstName', { header: 'First Name', cell: (info) => info.getValue(), - }, - { - accessorFn: (row) => row.lastName, // accessorFn used (alternative) along with a custom id + }), + columnHelper.accessor((row) => row.lastName, { id: 'lastName', header: () => Last Name, - cell: (info) => {info.getValue()}, - }, - { - accessorFn: (row) => Number(row.age), // accessorFn used to transform the data + cell: (info) => {info.getValue()}, + }), + columnHelper.accessor((row) => Number(row.age), { id: 'age', header: () => 'Age', cell: (info) => { return info.renderValue() }, - }, - { - accessorKey: 'visits', + }), + columnHelper.accessor('visits', { header: () => Visits, - }, - { - accessorKey: 'status', + }), + columnHelper.accessor('status', { header: 'Status', - }, - { - accessorKey: 'progress', + }), + columnHelper.accessor('progress', { header: 'Profile Progress', - }, -] + }), +]) function App() { - // 5. Store data with a stable reference + // 6. Store data with a stable reference const [data, _setData] = React.useState(() => [...defaultData]) - // 6. Create the table instance with required features, columns, and data + // 7. Create the table instance with required features, columns, and data const table = useTable( { key: 'basic-use-table', // needed for devtools, omit if you don't want to use the devtools @@ -115,7 +115,7 @@ function App() { // optionally, add this table instance to the devtools useTanStackTableDevtools(table) - // 7. Render your table markup from the table instance APIs + // 8. Render your table markup from the table instance APIs return (
diff --git a/examples/react/column-pinning/src/main.tsx b/examples/react/column-pinning/src/main.tsx index d65a8a0fdf..f766a45886 100644 --- a/examples/react/column-pinning/src/main.tsx +++ b/examples/react/column-pinning/src/main.tsx @@ -38,6 +38,7 @@ const columns = columnHelper.columns([ }), columnHelper.accessor((row) => row.lastName, { id: 'lastName', + // enablePinning: false, // prevent this column from being pinned cell: (info) => info.getValue(), header: () => Last Name, footer: (props) => props.column.id, diff --git a/examples/react/column-resizing/src/main.tsx b/examples/react/column-resizing/src/main.tsx index 782ce37a6f..74bdfa129f 100644 --- a/examples/react/column-resizing/src/main.tsx +++ b/examples/react/column-resizing/src/main.tsx @@ -25,6 +25,8 @@ const columns = columnHelper.columns([ footer: (props) => props.column.id, columns: columnHelper.columns([ columnHelper.accessor('firstName', { + // enableResizing: false, // prevent this column from being resized + // size: 180, minSize: 80, maxSize: 400, // override sizing defaults for this column cell: (info) => info.getValue(), footer: (props) => props.column.id, }), diff --git a/examples/react/column-sizing/src/main.tsx b/examples/react/column-sizing/src/main.tsx index b951a9397c..db5bb4b713 100644 --- a/examples/react/column-sizing/src/main.tsx +++ b/examples/react/column-sizing/src/main.tsx @@ -64,6 +64,7 @@ function App() { features, columns, data, + // defaultColumn: { size: 150, minSize: 50, maxSize: 500 }, // set sizing defaults for every column // initialState: { columnSizing: { firstName: 200 } }, // set column sizes on first render // atoms: { columnSizing: columnSizingAtom }, // preferred: own sizing state with an external atom // state: { columnSizing }, // classic controlled state; pair with onColumnSizingChange diff --git a/examples/react/column-visibility/src/main.tsx b/examples/react/column-visibility/src/main.tsx index 6d44995a56..a4ca37a3a9 100644 --- a/examples/react/column-visibility/src/main.tsx +++ b/examples/react/column-visibility/src/main.tsx @@ -20,6 +20,7 @@ const columns = columnHelper.columns([ footer: (props) => props.column.id, columns: columnHelper.columns([ columnHelper.accessor('firstName', { + // enableHiding: false, // keep this column visible cell: (info) => info.getValue(), footer: (props) => props.column.id, }), diff --git a/examples/react/expanding/src/main.tsx b/examples/react/expanding/src/main.tsx index 79fb71300a..0c230c9f05 100644 --- a/examples/react/expanding/src/main.tsx +++ b/examples/react/expanding/src/main.tsx @@ -7,9 +7,9 @@ import { createFilteredRowModel, createPaginatedRowModel, createSortedRowModel, + filterFn_between, filterFn_inNumberRange, filterFn_includesString, - filterFns, rowExpandingFeature, rowPaginationFeature, rowSelectionFeature, @@ -37,7 +37,7 @@ const features = tableFeatures({ paginatedRowModel: createPaginatedRowModel(), sortedRowModel: createSortedRowModel(), filterFns: { - between: filterFns.between, // no individual export for this fn (yet) + between: filterFn_between, includesString: filterFn_includesString, inNumberRange: filterFn_inNumberRange, }, @@ -146,7 +146,10 @@ function App() { columns, data, getSubRows: (row) => row.subRows, // tell the table where nested rows live - // enableSubRowSelection: false, + // enableRowSelection: row => row.original.age > 18, // enable selection conditionally; default true + // enableMultiRowSelection: false, // allow only one selected row at a time; default true + // enableSubRowSelection: false, // disable sub-row selection; default true + // enableRowRangeSelection: false, // disable shift-click range selection; default true // initialState: { expanded: { '0': true } }, // expand rows on first render // atoms: { expanded: expandedAtom }, // preferred: own expanded state with an external atom // state: { expanded }, // classic controlled state; pair with onExpandedChange @@ -158,8 +161,11 @@ function App() { // paginateExpandedRows: false, // keep expanded children on their parent page; default true // autoResetExpanded: false, // keep expanded rows after page-altering changes; default true // autoResetAll: false, // turn off every feature's automatic reset, including expansion + // enableFilters: false, // disable all column and global filtering; default true + // enableColumnFilters: false, // disable per-column filters; default true // filterFromLeafRows: true, // with filtering, keep parents whose descendants match // maxLeafRowFilterDepth: 0, // with filtering, only filter root rows + // manualFiltering: true, // pass data that is already filtered, for example from a server debugTable: true, debugRows: true, }, diff --git a/examples/react/filters/src/main.tsx b/examples/react/filters/src/main.tsx index 4ba5123537..beddfd5465 100644 --- a/examples/react/filters/src/main.tsx +++ b/examples/react/filters/src/main.tsx @@ -50,6 +50,7 @@ function App() { }), columnHelper.accessor('firstName', { cell: (info) => info.getValue(), + // enableColumnFilter: false, // disable filtering for this column }), columnHelper.accessor((row) => row.lastName, { id: 'lastName', diff --git a/examples/react/grouping/src/main.tsx b/examples/react/grouping/src/main.tsx index 2fba7fddd4..881c93d7c2 100644 --- a/examples/react/grouping/src/main.tsx +++ b/examples/react/grouping/src/main.tsx @@ -67,6 +67,7 @@ function App() { }), columnHelper.accessor('age', { header: () => 'Age', + // enableGrouping: false, // prevent grouping by this column }), columnHelper.accessor('visits', { header: () => Visits, diff --git a/examples/react/row-pinning/src/main.tsx b/examples/react/row-pinning/src/main.tsx index a109265958..91cfb377bb 100644 --- a/examples/react/row-pinning/src/main.tsx +++ b/examples/react/row-pinning/src/main.tsx @@ -160,7 +160,10 @@ function App() { features, columns, data, - initialState: { pagination: { pageSize: 20, pageIndex: 0 } }, + initialState: { + pagination: { pageSize: 20, pageIndex: 0 }, + // rowPinning: { top: ['0'], bottom: ['1'] }, // pin rows on first render + }, state: { expanded, rowPinning, diff --git a/examples/react/row-selection/src/main.tsx b/examples/react/row-selection/src/main.tsx index d26e7c98b0..1525d7be78 100644 --- a/examples/react/row-selection/src/main.tsx +++ b/examples/react/row-selection/src/main.tsx @@ -118,6 +118,7 @@ function App() { data, getRowId: (row) => row.id, enableRowSelection: true, // enable row selection for all rows + // enableRowSelection: row => row.original.age > 18, // or enable selection conditionally // initialState: { rowSelection: { '0': true } }, // select rows on first render // state: { rowSelection }, // classic controlled state; pair with onRowSelectionChange // onRowSelectionChange: setRowSelection, diff --git a/examples/solid/aggregation/src/App.tsx b/examples/solid/aggregation/src/App.tsx index 6f763d6d0b..652b679df4 100644 --- a/examples/solid/aggregation/src/App.tsx +++ b/examples/solid/aggregation/src/App.tsx @@ -125,6 +125,7 @@ function App() { return { rowSource: rowSource() } }, initialState: { pagination: { pageIndex: 0, pageSize: 10 } }, + // manualAggregation: true, // supply aggregate values yourself instead of calculating them locally debugTable: true, debugColumns: true, }) diff --git a/examples/solid/basic-use-table/src/App.tsx b/examples/solid/basic-use-table/src/App.tsx index a843453e9f..4bb67ad50a 100644 --- a/examples/solid/basic-use-table/src/App.tsx +++ b/examples/solid/basic-use-table/src/App.tsx @@ -1,7 +1,11 @@ -import { FlexRender, createTable, tableFeatures } from '@tanstack/solid-table' +import { + FlexRender, + createColumnHelper, + createTable, + tableFeatures, +} from '@tanstack/solid-table' import { useTanStackTableDevtools } from '@tanstack/solid-table-devtools' import { For, createSignal } from 'solid-js' -import type { ColumnDef } from '@tanstack/solid-table' // This example uses the standalone `createTable` function to create a table without the `createTableHook` util. @@ -54,44 +58,41 @@ const defaultData: Array = [ // 3. New in V9! Tell the table which features and row models we want to use. In this case, this will be a basic table with no additional features const features = tableFeatures({}) // util method to create sharable TFeatures object/type -// 4. Define the columns for your table. This uses the new `ColumnDef` type to define columns. Alternatively, check out the createTableHook/createAppColumnHelper util for an even more type-safe way to define columns. -const columns: Array> = [ - { - accessorKey: 'firstName', // accessorKey method (most common for simple use-cases) +// 4. Create a column helper with the table features and row type +const columnHelper = createColumnHelper() + +// 5. Define the columns for your table with the column helper +const columns = columnHelper.columns([ + columnHelper.accessor('firstName', { header: 'First Name', cell: (info) => info.getValue(), - }, - { - accessorFn: (row) => row.lastName, // accessorFn used (alternative) along with a custom id + }), + columnHelper.accessor((row) => row.lastName, { id: 'lastName', header: () => Last Name, - cell: (info) => {info.getValue()}, - }, - { - accessorFn: (row) => Number(row.age), // accessorFn used to transform the data + cell: (info) => {info.getValue()}, + }), + columnHelper.accessor((row) => Number(row.age), { id: 'age', header: () => 'Age', cell: (info) => info.renderValue(), - }, - { - accessorKey: 'visits', + }), + columnHelper.accessor('visits', { header: () => Visits, - }, - { - accessorKey: 'status', + }), + columnHelper.accessor('status', { header: 'Status', - }, - { - accessorKey: 'progress', + }), + columnHelper.accessor('progress', { header: 'Profile Progress', - }, -] + }), +]) function App() { - // 5. Store data with a reactive reference + // 6. Store data with a reactive reference const [data] = createSignal([...defaultData]) - // 6. Create the table instance with required features, columns, and data + // 7. Create the table instance with required features, columns, and data const table = createTable({ key: 'basic-use-table', // needed for devtools debugTable: true, @@ -104,7 +105,7 @@ function App() { useTanStackTableDevtools(table) - // 7. Render your table markup from the table instance APIs + // 8. Render your table markup from the table instance APIs return (
diff --git a/examples/solid/column-sizing/src/App.tsx b/examples/solid/column-sizing/src/App.tsx index c4ca0a13fb..a5cbc4cea0 100644 --- a/examples/solid/column-sizing/src/App.tsx +++ b/examples/solid/column-sizing/src/App.tsx @@ -58,6 +58,7 @@ function App() { get data() { return data() }, + // defaultColumn: { size: 150, minSize: 50, maxSize: 500 }, // set sizing defaults for every column // initialState: { columnSizing: { firstName: 200 } }, // set column sizes on first render // atoms: { columnSizing: columnSizingAtom }, // preferred: own sizing state with an external atom // state: { columnSizing }, // classic controlled state; pair with onColumnSizingChange diff --git a/examples/solid/expanding/src/App.tsx b/examples/solid/expanding/src/App.tsx index 2ff37bbe8f..d7b3fe327a 100644 --- a/examples/solid/expanding/src/App.tsx +++ b/examples/solid/expanding/src/App.tsx @@ -130,7 +130,11 @@ function App() { get data() { return data() }, - getSubRows: (row) => row.subRows, + getSubRows: (row) => row.subRows, // tell the table where nested rows live + // enableRowSelection: row => row.original.age > 18, // enable selection conditionally; default true + // enableMultiRowSelection: false, // allow only one selected row at a time; default true + // enableSubRowSelection: false, // disable sub-row selection; default true + // enableRowRangeSelection: false, // disable shift-click range selection; default true // initialState: { expanded: { '0': true } }, // expand rows on first render // atoms: { expanded: expandedAtom }, // preferred: own expanded state with an external atom // state: { expanded }, // classic controlled state; pair with onExpandedChange @@ -142,8 +146,11 @@ function App() { // paginateExpandedRows: false, // keep expanded children on their parent page; default true // autoResetExpanded: false, // keep expanded rows after page-altering changes; default true // autoResetAll: false, // turn off every feature's automatic reset, including expansion + // enableFilters: false, // disable all column and global filtering; default true + // enableColumnFilters: false, // disable per-column filters; default true // filterFromLeafRows: true, // with filtering, keep parents whose descendants match // maxLeafRowFilterDepth: 0, // with filtering, only filter root rows + // manualFiltering: true, // pass data that is already filtered, for example from a server debugTable: true, }) diff --git a/examples/solid/row-pinning/src/App.tsx b/examples/solid/row-pinning/src/App.tsx index 409e3b7189..9fc2ae27f9 100644 --- a/examples/solid/row-pinning/src/App.tsx +++ b/examples/solid/row-pinning/src/App.tsx @@ -138,7 +138,10 @@ function App() { get data() { return data() }, - initialState: { pagination: { pageSize: 20, pageIndex: 0 } }, + initialState: { + pagination: { pageSize: 20, pageIndex: 0 }, + // rowPinning: { top: ['0'], bottom: ['1'] }, // pin rows on first render + }, get state() { return { expanded: expanded(), diff --git a/examples/solid/row-selection/src/App.tsx b/examples/solid/row-selection/src/App.tsx index f144e221d3..1e82a542bf 100644 --- a/examples/solid/row-selection/src/App.tsx +++ b/examples/solid/row-selection/src/App.tsx @@ -131,6 +131,7 @@ function App() { get enableRowSelection() { return enableRowSelection() }, + // enableRowSelection: row => row.original.age > 18, // or enable selection conditionally // initialState: { rowSelection: { '0': true } }, // select rows on first render // atoms: { rowSelection: rowSelectionAtom }, // preferred: own selection state with an external atom // state: { rowSelection }, // classic controlled state; pair with onRowSelectionChange diff --git a/examples/svelte/aggregation/src/App.svelte b/examples/svelte/aggregation/src/App.svelte index 5cccaefced..c91c407450 100644 --- a/examples/svelte/aggregation/src/App.svelte +++ b/examples/svelte/aggregation/src/App.svelte @@ -41,7 +41,9 @@ features, columns, get data() { return data }, get meta() { return { rowSource } }, - initialState: { pagination: { pageIndex: 0, pageSize: 10 } }, debugTable: true, debugColumns: true, + initialState: { pagination: { pageIndex: 0, pageSize: 10 } }, + // manualAggregation: true, // supply aggregate values yourself instead of calculating them locally + debugTable: true, debugColumns: true, }) const pagination = $derived(table.atoms.pagination.get()) function changeRowSource(value: RowSource) { diff --git a/examples/svelte/basic-create-table/src/App.svelte b/examples/svelte/basic-create-table/src/App.svelte index 22fca5c49f..8abba2514c 100644 --- a/examples/svelte/basic-create-table/src/App.svelte +++ b/examples/svelte/basic-create-table/src/App.svelte @@ -1,9 +1,8 @@ - +