From ab05445cb3682fdd47ce8f499f752ef5cd0515ad Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Fri, 8 May 2026 11:48:50 -0500 Subject: [PATCH 1/2] feat: change default state selectors to all --- .../products-table/products-table.html | 9 +- .../src/app/components/table-components.ts | 17 +- .../components/users-table/users-table.html | 9 +- .../angular/composable-tables/src/styles.css | 6 + examples/lit/column-ordering/src/main.ts | 4 +- examples/lit/column-pinning-split/src/main.ts | 4 +- examples/lit/column-pinning/src/main.ts | 4 +- .../src/components/products-table.ts | 19 +- .../src/components/table-components.ts | 16 +- .../src/components/users-table.ts | 19 +- examples/lit/composable-tables/src/index.css | 6 + .../preact/basic-external-atoms/src/main.tsx | 29 +- .../preact/basic-external-state/src/main.tsx | 33 +- .../preact/basic-use-app-table/src/main.tsx | 15 +- examples/preact/basic-use-table/src/main.tsx | 19 +- examples/preact/column-groups/src/main.tsx | 17 +- examples/preact/column-ordering/src/main.tsx | 195 +++---- .../preact/column-pinning-split/src/main.tsx | 539 +++++++++--------- .../preact/column-pinning-sticky/src/main.tsx | 321 +++++------ examples/preact/column-pinning/src/main.tsx | 263 +++++---- .../column-resizing-performant/src/main.tsx | 10 +- examples/preact/column-resizing/src/main.tsx | 6 +- examples/preact/column-sizing/src/main.tsx | 6 +- .../preact/column-visibility/src/main.tsx | 171 +++--- .../src/components/table-components.tsx | 135 +++-- .../preact/composable-tables/src/index.css | 6 + .../preact/composable-tables/src/main.tsx | 17 +- examples/preact/custom-plugin/src/main.tsx | 35 +- examples/preact/expanding/src/main.tsx | 271 +++++---- examples/preact/filters-faceted/src/main.tsx | 274 +++++---- examples/preact/filters-fuzzy/src/main.tsx | 318 +++++------ examples/preact/filters/src/main.tsx | 270 +++++---- examples/preact/grouping/src/main.tsx | 6 +- examples/preact/pagination/src/main.tsx | 237 ++++---- examples/preact/row-pinning/src/main.tsx | 2 +- examples/preact/row-selection/src/main.tsx | 388 ++++++------- examples/preact/sorting/src/main.tsx | 132 ++--- examples/preact/sub-components/src/main.tsx | 117 ++-- .../preact/with-tanstack-query/src/main.tsx | 25 +- .../react/basic-external-atoms/src/main.tsx | 2 +- .../react/basic-external-state/src/main.tsx | 33 +- .../react/basic-use-app-table/src/main.tsx | 15 +- examples/react/basic-use-table/src/main.tsx | 19 +- examples/react/column-dnd/src/main.tsx | 6 +- examples/react/column-groups/src/main.tsx | 17 +- examples/react/column-ordering/src/main.tsx | 195 +++---- .../react/column-pinning-split/src/main.tsx | 539 +++++++++--------- .../react/column-pinning-sticky/src/main.tsx | 321 +++++------ examples/react/column-pinning/src/main.tsx | 263 +++++---- .../column-resizing-performant/src/main.tsx | 10 +- examples/react/column-resizing/src/main.tsx | 6 +- examples/react/column-sizing/src/main.tsx | 6 +- examples/react/column-visibility/src/main.tsx | 179 +++--- .../src/components/table-components.tsx | 131 +++-- .../react/composable-tables/src/index.css | 6 + examples/react/composable-tables/src/main.tsx | 41 +- examples/react/custom-plugin/src/main.tsx | 35 +- examples/react/expanding/src/main.tsx | 267 +++++---- examples/react/filters-faceted/src/main.tsx | 270 +++++---- examples/react/filters-fuzzy/src/main.tsx | 314 +++++----- examples/react/filters/src/main.tsx | 266 +++++---- examples/react/grouping/src/main.tsx | 6 +- .../react/kitchen-sink-hero-ui/src/main.tsx | 2 +- .../react/kitchen-sink-mantine/src/main.tsx | 2 +- .../kitchen-sink-material-ui/src/main.tsx | 2 +- .../kitchen-sink-react-aria/src/main.tsx | 2 +- .../kitchen-sink-shadcn-base/src/main.tsx | 2 +- .../kitchen-sink-shadcn-radix/src/main.tsx | 2 +- examples/react/lib-hero-ui/src/main.tsx | 2 +- examples/react/lib-mantine/src/main.tsx | 2 +- examples/react/lib-material-ui/src/main.tsx | 2 +- examples/react/lib-react-aria/src/main.tsx | 2 +- examples/react/lib-shadcn-base/src/main.tsx | 2 +- examples/react/lib-shadcn-radix/src/main.tsx | 2 +- .../hooks/useMRT_TableInstance.ts | 2 +- .../hooks/useMRT_TableInstance.ts | 2 +- examples/react/pagination/src/main.tsx | 233 ++++---- examples/react/row-dnd/src/main.tsx | 6 +- examples/react/row-pinning/src/main.tsx | 2 +- examples/react/row-selection/src/main.tsx | 401 ++++++------- examples/react/sorting/src/main.tsx | 142 +++-- examples/react/sub-components/src/main.tsx | 117 ++-- .../src/main.tsx | 47 +- .../react/virtualized-columns/src/main.tsx | 17 +- .../src/main.tsx | 23 +- .../src/main.tsx | 137 +++-- .../vite.config.js | 8 +- examples/react/virtualized-rows/src/main.tsx | 122 ++-- .../react/with-tanstack-form/src/main.tsx | 257 ++++----- .../react/with-tanstack-query/src/main.tsx | 25 +- .../src/components/table.tsx | 2 +- examples/solid/composable-tables/src/App.tsx | 19 +- .../src/components/table-components.tsx | 9 +- .../solid/composable-tables/src/index.css | 6 + .../src/components/ProductsTable.svelte | 12 +- .../src/components/TableToolbar.svelte | 20 +- .../src/components/UsersTable.svelte | 12 +- .../svelte/composable-tables/src/index.css | 6 + examples/svelte/row-selection/src/App.svelte | 5 +- .../src/components/ProductsTable.vue | 5 +- .../src/components/UsersTable.vue | 5 +- .../src/components/table-components.ts | 25 +- examples/vue/composable-tables/src/index.css | 6 + .../src/helpers/createTableHook.ts | 5 +- packages/angular-table/src/injectTable.ts | 17 +- packages/lit-table/src/TableController.ts | 10 +- packages/lit-table/src/createTableHook.ts | 5 +- packages/preact-table/src/Subscribe.ts | 4 +- packages/preact-table/src/createTableHook.tsx | 5 +- packages/preact-table/src/useTable.ts | 7 +- packages/react-table/src/Subscribe.ts | 4 +- packages/react-table/src/createTableHook.tsx | 5 +- packages/react-table/src/useTable.ts | 7 +- packages/solid-table/src/createTable.ts | 10 +- packages/solid-table/src/createTableHook.tsx | 5 +- .../svelte-table/src/createTable.svelte.ts | 7 +- .../src/createTableHook.svelte.ts | 5 +- packages/vue-table/src/createTableHook.ts | 5 +- packages/vue-table/src/useTable.ts | 10 +- 119 files changed, 4197 insertions(+), 4528 deletions(-) diff --git a/examples/angular/composable-tables/src/app/components/products-table/products-table.html b/examples/angular/composable-tables/src/app/components/products-table/products-table.html index 13541da773..ad0643dbea 100644 --- a/examples/angular/composable-tables/src/app/components/products-table/products-table.html +++ b/examples/angular/composable-tables/src/app/components/products-table/products-table.html @@ -1,10 +1,9 @@
- - diff --git a/examples/angular/composable-tables/src/app/components/table-components.ts b/examples/angular/composable-tables/src/app/components/table-components.ts index 2029d19352..e742d00c36 100644 --- a/examples/angular/composable-tables/src/app/components/table-components.ts +++ b/examples/angular/composable-tables/src/app/components/table-components.ts @@ -11,12 +11,16 @@ import { injectTableContext } from '../table' template: `

{{ title() }}

- - - - @if (onRefresh(); as onRefresh) { - - } +
+ @if (onRefresh(); as onRefresh) { + + } + @if (onStressTest(); as onStressTest) { + + } + + +
`, changeDetection: ChangeDetectionStrategy.OnPush, @@ -24,6 +28,7 @@ import { injectTableContext } from '../table' export class TableToolbar { readonly title = input.required() readonly onRefresh = input<() => void>() + readonly onStressTest = input<() => void>() readonly table = injectTableContext() diff --git a/examples/angular/composable-tables/src/app/components/users-table/users-table.html b/examples/angular/composable-tables/src/app/components/users-table/users-table.html index 461e2ce917..3744b5b0f7 100644 --- a/examples/angular/composable-tables/src/app/components/users-table/users-table.html +++ b/examples/angular/composable-tables/src/app/components/users-table/users-table.html @@ -1,10 +1,9 @@
- -
diff --git a/examples/angular/composable-tables/src/styles.css b/examples/angular/composable-tables/src/styles.css index 75b7db2d40..4d8fef0142 100644 --- a/examples/angular/composable-tables/src/styles.css +++ b/examples/angular/composable-tables/src/styles.css @@ -174,6 +174,12 @@ tfoot th { border-spacing: 0; } +.table-toolbar-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + .table-toolbar button { border: 1px solid #ccc; border-radius: 4px; diff --git a/examples/lit/column-ordering/src/main.ts b/examples/lit/column-ordering/src/main.ts index 556d36b7a1..2cadeb1bec 100644 --- a/examples/lit/column-ordering/src/main.ts +++ b/examples/lit/column-ordering/src/main.ts @@ -13,7 +13,7 @@ import { makeData } from './makeData' import type { ColumnDef, ColumnOrderState, - VisibilityState, + ColumnVisibilityState, } from '@tanstack/lit-table' import type { Person } from './makeData' @@ -85,7 +85,7 @@ class LitTableExample extends LitElement { private columnOrder: ColumnOrderState = [] @state() - private columnVisibility: VisibilityState = {} + private columnVisibility: ColumnVisibilityState = {} protected render() { const table = this.tableController.table( diff --git a/examples/lit/column-pinning-split/src/main.ts b/examples/lit/column-pinning-split/src/main.ts index 7ba6eaeffe..ee6261783e 100644 --- a/examples/lit/column-pinning-split/src/main.ts +++ b/examples/lit/column-pinning-split/src/main.ts @@ -15,7 +15,7 @@ import type { ColumnDef, ColumnOrderState, ColumnPinningState, - VisibilityState, + ColumnVisibilityState, } from '@tanstack/lit-table' import type { Person } from './makeData' @@ -88,7 +88,7 @@ class LitTableExample extends LitElement { private columnOrder: ColumnOrderState = [] @state() - private columnVisibility: VisibilityState = {} + private columnVisibility: ColumnVisibilityState = {} @state() private columnPinning: ColumnPinningState = { left: [], right: [] } diff --git a/examples/lit/column-pinning/src/main.ts b/examples/lit/column-pinning/src/main.ts index 5f0cf6c2f6..4a1b4c5f4b 100644 --- a/examples/lit/column-pinning/src/main.ts +++ b/examples/lit/column-pinning/src/main.ts @@ -15,7 +15,7 @@ import type { ColumnDef, ColumnOrderState, ColumnPinningState, - VisibilityState, + ColumnVisibilityState, } from '@tanstack/lit-table' import type { Person } from './makeData' @@ -88,7 +88,7 @@ class LitTableExample extends LitElement { private columnOrder: ColumnOrderState = [] @state() - private columnVisibility: VisibilityState = {} + private columnVisibility: ColumnVisibilityState = {} @state() private columnPinning: ColumnPinningState = { left: [], right: [] } diff --git a/examples/lit/composable-tables/src/components/products-table.ts b/examples/lit/composable-tables/src/components/products-table.ts index 5f9f28c524..96d75df3c4 100644 --- a/examples/lit/composable-tables/src/components/products-table.ts +++ b/examples/lit/composable-tables/src/components/products-table.ts @@ -78,26 +78,13 @@ export class ProductsTable extends LitElement { return html`
-
- - -
{ + this.data = makeProductData(200_000) + }} > diff --git a/examples/lit/composable-tables/src/components/table-components.ts b/examples/lit/composable-tables/src/components/table-components.ts index 8127cfb3b9..bea12f1cc0 100644 --- a/examples/lit/composable-tables/src/components/table-components.ts +++ b/examples/lit/composable-tables/src/components/table-components.ts @@ -113,6 +113,9 @@ export class TableToolbar extends LitElement { @property({ attribute: false }) declare onRefresh: (() => void) | undefined + @property({ attribute: false }) + declare onStressTest: (() => void) | undefined + createRenderRoot() { return this } @@ -124,14 +127,19 @@ export class TableToolbar extends LitElement { return html`

${this.title}

-
+
+ ${this.onRefresh + ? html`` + : nothing} + ${this.onStressTest + ? html`` + : nothing} - ${this.onRefresh - ? html`` - : nothing}
` diff --git a/examples/lit/composable-tables/src/components/users-table.ts b/examples/lit/composable-tables/src/components/users-table.ts index a2eeed1936..a13e7fe54c 100644 --- a/examples/lit/composable-tables/src/components/users-table.ts +++ b/examples/lit/composable-tables/src/components/users-table.ts @@ -92,26 +92,13 @@ export class UsersTable extends LitElement { return html`
-
- - -
{ + this.data = makeData(200_000) + }} > diff --git a/examples/lit/composable-tables/src/index.css b/examples/lit/composable-tables/src/index.css index 75b7db2d40..4d8fef0142 100644 --- a/examples/lit/composable-tables/src/index.css +++ b/examples/lit/composable-tables/src/index.css @@ -174,6 +174,12 @@ tfoot th { border-spacing: 0; } +.table-toolbar-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + .table-toolbar button { border: 1px solid #ccc; border-radius: 4px; diff --git a/examples/preact/basic-external-atoms/src/main.tsx b/examples/preact/basic-external-atoms/src/main.tsx index 1a038e3fe5..5ed17a896a 100644 --- a/examples/preact/basic-external-atoms/src/main.tsx +++ b/examples/preact/basic-external-atoms/src/main.tsx @@ -74,20 +74,23 @@ function App() { const pagination = useSelector(paginationAtom, (s) => s) // Create the table and pass your per-slice external atoms. - const table = useTable({ - _features, - _rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), + const table = useTable( + { + _features, + _rowModels: { + sortedRowModel: createSortedRowModel(sortFns), + paginatedRowModel: createPaginatedRowModel(), + }, + columns, + data, + atoms: { + sorting: sortingAtom, + pagination: paginationAtom, + }, + debugTable: pagination.pageIndex > 2, }, - columns, - data, - atoms: { - sorting: sortingAtom, - pagination: paginationAtom, - }, - debugTable: pagination.pageIndex > 2, - }) + (state) => state, // default selector + ) return (
diff --git a/examples/preact/basic-external-state/src/main.tsx b/examples/preact/basic-external-state/src/main.tsx index 307a99799c..9057e5a3d5 100644 --- a/examples/preact/basic-external-state/src/main.tsx +++ b/examples/preact/basic-external-state/src/main.tsx @@ -73,22 +73,25 @@ function App() { console.log('pagination', pagination) // Create the table and pass state + onChange handlers - const table = useTable({ - debugTable: true, - _features, - _rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), + const table = useTable( + { + debugTable: true, + _features, + _rowModels: { + sortedRowModel: createSortedRowModel(sortFns), + paginatedRowModel: createPaginatedRowModel(), + }, + columns, + data, + state: { + sorting, // connect our sorting state back down to the table + pagination, // connect our pagination state back down to the table + }, + onSortingChange: setSorting, // raise sorting state changes to our own state management + onPaginationChange: setPagination, // raise pagination state changes to our own state management }, - columns, - data, - state: { - sorting, // connect our sorting state back down to the table - pagination, // connect our pagination state back down to the table - }, - onSortingChange: setSorting, // raise sorting state changes to our own state management - onPaginationChange: setPagination, // raise pagination state changes to our own state management - }) + (state) => state, // default selector + ) return (
diff --git a/examples/preact/basic-use-app-table/src/main.tsx b/examples/preact/basic-use-app-table/src/main.tsx index 97d9af8cca..4676e181b7 100644 --- a/examples/preact/basic-use-app-table/src/main.tsx +++ b/examples/preact/basic-use-app-table/src/main.tsx @@ -103,12 +103,15 @@ function App() { // 7. Create the table instance with the required columns and data. // Features and row models are already defined in the createTableHook call above - const table = useAppTable({ - debugTable: true, - columns, - data, - // add additional table options here or in the createTableHook call above - }) + const table = useAppTable( + { + debugTable: true, + columns, + data, + // add additional table options here or in the createTableHook call above + }, + (state) => state, // default selector + ) // 8. Render your table markup from the table instance APIs return ( diff --git a/examples/preact/basic-use-table/src/main.tsx b/examples/preact/basic-use-table/src/main.tsx index 529b7b63b5..9ac3c50d32 100644 --- a/examples/preact/basic-use-table/src/main.tsx +++ b/examples/preact/basic-use-table/src/main.tsx @@ -96,14 +96,17 @@ function App() { const rerender = useReducer(() => ({}), {})[1] // 6. Create the table instance with required _features, columns, and data - const table = useTable({ - debugTable: true, - _features, // new required option in V9. Tell the table which features you are importing and using (better tree-shaking) - _rowModels: {}, // `Core` row model is now included by default, but you can still override it here - columns, - data, - // add additional table options here - }) + const table = useTable( + { + debugTable: true, + _features, // new required option in V9. Tell the table which features you are importing and using (better tree-shaking) + _rowModels: {}, // `Core` row model is now included by default, but you can still override it here + columns, + data, + // add additional table options here + }, + (state) => state, // default selector + ) // 7. Render your table markup from the table instance APIs return ( diff --git a/examples/preact/column-groups/src/main.tsx b/examples/preact/column-groups/src/main.tsx index 34ee34c7ce..6a78c373f8 100644 --- a/examples/preact/column-groups/src/main.tsx +++ b/examples/preact/column-groups/src/main.tsx @@ -66,13 +66,16 @@ function App() { const stressTest = () => setData(makeData(1_000)) const rerender = useReducer(() => ({}), {})[1] - const table = useTable({ - debugTable: true, - _features, - _rowModels: {}, - columns, - data, - }) + const table = useTable( + { + debugTable: true, + _features, + _rowModels: {}, + columns, + data, + }, + (state) => state, // default selector + ) return (
diff --git a/examples/preact/column-ordering/src/main.tsx b/examples/preact/column-ordering/src/main.tsx index 218004aee8..a63f5707a1 100644 --- a/examples/preact/column-ordering/src/main.tsx +++ b/examples/preact/column-ordering/src/main.tsx @@ -72,15 +72,18 @@ function App() { const refreshData = () => setData(() => makeData(20)) const stressTest = () => setData(() => makeData(1_000)) - const table = useTable({ - _features, - _rowModels: {}, - columns, - data, - debugTable: true, - debugHeaders: true, - debugColumns: true, - }) + const table = useTable( + { + _features, + _rowModels: {}, + columns, + data, + debugTable: true, + debugHeaders: true, + debugColumns: true, + }, + (state) => state, // default selector + ) const randomizeColumns = () => { table.setColumnOrder( @@ -89,108 +92,96 @@ function App() { } return ( - ({ - // subscribe to only the column order and visibility state changes at root level - columnOrder: state.columnOrder, - columnVisibility: state.columnVisibility, - })} - > - {(_state) => ( -
-
-
+
+
+
+ +
+ {table.getAllLeafColumns().map((column) => { + return ( +
- {table.getAllLeafColumns().map((column) => { - return ( -
- -
- ) - })} -
-
-
- - - -
-
-
- - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - + ) + })} + +
+
+ + + +
+
+
- {header.isPlaceholder ? null : ( - - )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - ))} - + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + ))} - - - {table.getFooterGroups().map((footerGroup) => ( - - {footerGroup.headers.map((header) => ( - - ))} - + + ))} + + + {table.getFooterGroups().map((footerGroup) => ( + + {footerGroup.headers.map((header) => ( + ))} - -
+ {header.isPlaceholder ? null : ( + + )} +
- -
+ +
- {header.isPlaceholder ? null : ( - - )} -
+ {header.isPlaceholder ? null : ( + + )} +
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + +
{JSON.stringify(table.state, null, 2)}
+ ) } diff --git a/examples/preact/column-pinning-split/src/main.tsx b/examples/preact/column-pinning-split/src/main.tsx index 2d09daa060..9846d6112d 100644 --- a/examples/preact/column-pinning-split/src/main.tsx +++ b/examples/preact/column-pinning-split/src/main.tsx @@ -78,15 +78,18 @@ function App() { const refreshData = () => setData(() => makeData(1_000)) const stressTest = () => setData(() => makeData(500_000)) - const table = useTable({ - _features, - _rowModels: {}, - columns, - data, - debugTable: true, - debugHeaders: true, - debugColumns: true, - }) + const table = useTable( + { + _features, + _rowModels: {}, + columns, + data, + debugTable: true, + debugHeaders: true, + debugColumns: true, + }, + (state) => state, // default selector + ) const randomizeColumns = () => { table.setColumnOrder( @@ -95,282 +98,270 @@ function App() { } return ( - ({ - columnVisibility: state.columnVisibility, - columnOrder: state.columnOrder, - columnPinning: state.columnPinning, - })} - > - {(_state) => ( -
-
-
+
+
+
+ +
+ {table.getAllLeafColumns().map((column) => { + return ( +
- {table.getAllLeafColumns().map((column) => { - return ( -
- -
- ) - })} -
-
-
- - - -
-
-

- This example takes advantage of the "splitting" APIs. (APIs that - have "left, "center", and "right" modifiers) -

-
- - - {table.getLeftHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - + ) + })} + +
+
+ + + +
+
+

+ This example takes advantage of the "splitting" APIs. (APIs that have + "left, "center", and "right" modifiers) +

+
+
-
- {header.isPlaceholder ? null : ( - - )} -
- {!header.isPlaceholder && header.column.getCanPin() && ( -
- {header.column.getIsPinned() !== 'left' ? ( - - ) : null} - {header.column.getIsPinned() ? ( - - ) : null} - {header.column.getIsPinned() !== 'right' ? ( - - ) : null} -
- )} -
+ + {table.getLeftHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ))} - - - {table - .getRowModel() - .rows.slice(0, 20) - .map((row) => { - return ( - - {row.getLeftVisibleCells().map((cell) => { - return ( - - ) - })} - - ) - })} - -
+
+ {header.isPlaceholder ? null : ( + + )} +
+ {!header.isPlaceholder && header.column.getCanPin() && ( +
+ {header.column.getIsPinned() !== 'left' ? ( + + ) : null} + {header.column.getIsPinned() ? ( + + ) : null} + {header.column.getIsPinned() !== 'right' ? ( + + ) : null} +
+ )} +
- -
- - - {table.getCenterHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} + + ))} + + + {table + .getRowModel() + .rows.slice(0, 20) + .map((row) => { + return ( + + {row.getLeftVisibleCells().map((cell) => { + return ( + + ) + })} + ) + })} + +
-
- {header.isPlaceholder ? null : ( - - )} -
- {!header.isPlaceholder && header.column.getCanPin() && ( -
- {header.column.getIsPinned() !== 'left' ? ( - - ) : null} - {header.column.getIsPinned() ? ( - - ) : null} - {header.column.getIsPinned() !== 'right' ? ( - - ) : null} -
- )} -
+ +
+ + + {table.getCenterHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ))} - - - {table - .getRowModel() - .rows.slice(0, 20) - .map((row) => { - return ( - - {row.getCenterVisibleCells().map((cell) => { - return ( - - ) - })} - - ) - })} - -
+
+ {header.isPlaceholder ? null : ( + + )} +
+ {!header.isPlaceholder && header.column.getCanPin() && ( +
+ {header.column.getIsPinned() !== 'left' ? ( + + ) : null} + {header.column.getIsPinned() ? ( + + ) : null} + {header.column.getIsPinned() !== 'right' ? ( + + ) : null} +
+ )} +
- -
- - - {table.getRightHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} + + ))} + + + {table + .getRowModel() + .rows.slice(0, 20) + .map((row) => { + return ( + + {row.getCenterVisibleCells().map((cell) => { + return ( + + ) + })} + ) + })} + +
-
- {header.isPlaceholder ? null : ( - - )} -
- {!header.isPlaceholder && header.column.getCanPin() && ( -
- {header.column.getIsPinned() !== 'left' ? ( - - ) : null} - {header.column.getIsPinned() ? ( - - ) : null} - {header.column.getIsPinned() !== 'right' ? ( - - ) : null} -
- )} -
+ +
+ + + {table.getRightHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ))} - - - {table - .getRowModel() - .rows.slice(0, 20) - .map((row) => { - return ( - - {row.getRightVisibleCells().map((cell) => { - return ( - - ) - })} - - ) - })} - -
+
+ {header.isPlaceholder ? null : ( + + )} +
+ {!header.isPlaceholder && header.column.getCanPin() && ( +
+ {header.column.getIsPinned() !== 'left' ? ( + + ) : null} + {header.column.getIsPinned() ? ( + + ) : null} + {header.column.getIsPinned() !== 'right' ? ( + + ) : null} +
+ )} +
- -
-
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + + {table + .getRowModel() + .rows.slice(0, 20) + .map((row) => { + return ( + + {row.getRightVisibleCells().map((cell) => { + return ( + + + + ) + })} + + ) + })} + + +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/preact/column-pinning-sticky/src/main.tsx b/examples/preact/column-pinning-sticky/src/main.tsx index e3048793bc..b89cba6501 100644 --- a/examples/preact/column-pinning-sticky/src/main.tsx +++ b/examples/preact/column-pinning-sticky/src/main.tsx @@ -116,7 +116,7 @@ function App() { debugColumns: true, columnResizeMode: 'onChange', }, - (state) => state, + (state) => state, // default selector ) const randomizeColumns = () => { @@ -126,196 +126,153 @@ function App() { } return ( - ({ - columnVisibility: state.columnVisibility, - columnOrder: state.columnOrder, - columnPinning: state.columnPinning, - columnSizing: state.columnSizing, - columnResizing: state.columnResizing, - })} - > - {(_topLevelState) => ( -
-
-
+
+
+
+ +
+ {table.getAllLeafColumns().map((column) => { + return ( +
- {table.getAllLeafColumns().map((column) => { - return ( -
- -
- ) - })} -
-
-
- - - -
-
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - const { column } = header + ) + })} + +
+
+ + + +
+
+
+
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + const { column } = header - return ( - ({ - isResizingColumn: - state.columnResizing.isResizingColumn === - column.id, - columnSize: state.columnSizing[column.id], - })} - > - {() => ( - - )} - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => { - const { column } = cell - return ( - ({ - isResizingColumn: - state.columnResizing.isResizingColumn === - column.id, - columnSize: state.columnSizing[column.id], - })} - > - {() => ( - + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => { + const { column } = cell + return ( + + ) + })} + + ))} + +
+
+ {header.isPlaceholder ? null : ( + <> + {' '} + + )} + {/* Demo getIndex behavior */} + {column.getIndex(column.getIsPinned() || 'center')} +
+ {!header.isPlaceholder && header.column.getCanPin() && ( +
+ {header.column.getIsPinned() !== 'left' ? ( + - ) : null} - {header.column.getIsPinned() ? ( - - ) : null} - {header.column.getIsPinned() !== 'right' ? ( - - ) : null} -
- )} -
header.column.resetSize()} - onMouseDown={header.getResizeHandler()} - onTouchStart={header.getResizeHandler()} - className={`resizer ${ - header.column.getIsResizing() - ? 'isResizing' - : '' - }`} - /> -
{ + header.column.pin('right') + }} + > + {'=>'} + + ) : null} + + )} +
header.column.resetSize()} + onMouseDown={header.getResizeHandler()} + onTouchStart={header.getResizeHandler()} + className={`resizer ${ + header.column.getIsResizing() ? 'isResizing' : '' + }`} + /> + + ) + })} +
+ +
+
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/preact/column-pinning/src/main.tsx b/examples/preact/column-pinning/src/main.tsx index 5a705add6f..303b21b4ab 100644 --- a/examples/preact/column-pinning/src/main.tsx +++ b/examples/preact/column-pinning/src/main.tsx @@ -80,11 +80,14 @@ function App() { const refreshData = () => setData(() => makeData(1_000)) const stressTest = () => setData(() => makeData(500_000)) - const table = useAppTable({ - debugTable: true, - columns, - data, - }) + const table = useAppTable( + { + debugTable: true, + columns, + data, + }, + (state) => state, // default selector + ) const randomizeColumns = () => { table.setColumnOrder( @@ -93,144 +96,132 @@ function App() { } return ( - ({ - columnVisibility: state.columnVisibility, - columnOrder: state.columnOrder, - columnPinning: state.columnPinning, - })} - > - {(_state) => ( -
-
-
+
+
+
+ +
+ {table.getAllLeafColumns().map((column) => { + return ( +
- {table.getAllLeafColumns().map((column) => { - return ( -
- -
- ) - })} -
-
-
- - - -
-
-

- This example using the non-split APIs. Columns are just reordered - within 1 table instead of being split into 3 different tables. -

-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - + ) + })} + +
+
+ + + +
+
+

+ This example using the non-split APIs. Columns are just reordered within + 1 table instead of being split into 3 different tables. +

+
+
-
- {header.isPlaceholder ? null : ( - - )} -
- {!header.isPlaceholder && header.column.getCanPin() && ( -
- {header.column.getIsPinned() !== 'left' ? ( - - ) : null} - {header.column.getIsPinned() ? ( - - ) : null} - {header.column.getIsPinned() !== 'right' ? ( - - ) : null} -
- )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ))} - - - {table - .getRowModel() - .rows.slice(0, 20) - .map((row) => { - return ( - - {row.getVisibleCells().map((cell) => { - return ( - - ) - })} - - ) - })} - -
+
+ {header.isPlaceholder ? null : ( + + )} +
+ {!header.isPlaceholder && header.column.getCanPin() && ( +
+ {header.column.getIsPinned() !== 'left' ? ( + + ) : null} + {header.column.getIsPinned() ? ( + + ) : null} + {header.column.getIsPinned() !== 'right' ? ( + + ) : null} +
+ )} +
- -
-
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + + {table + .getRowModel() + .rows.slice(0, 20) + .map((row) => { + return ( + + {row.getVisibleCells().map((cell) => { + return ( + + + + ) + })} + + ) + })} + + +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/preact/column-resizing-performant/src/main.tsx b/examples/preact/column-resizing-performant/src/main.tsx index 1650f6859a..4993831758 100644 --- a/examples/preact/column-resizing-performant/src/main.tsx +++ b/examples/preact/column-resizing-performant/src/main.tsx @@ -137,13 +137,9 @@ function App() { - state}> - {(state) => ( -
-            {JSON.stringify(state, null, 2)}
-          
- )} -
+
+        {JSON.stringify(table.state, null, 2)}
+      
({data.length.toLocaleString()} rows)
{/* Here in the equivalent element (surrounds all table head and data cells), we will define our CSS variables for column sizes */} diff --git a/examples/preact/column-resizing/src/main.tsx b/examples/preact/column-resizing/src/main.tsx index 9ee3b572c8..8b1c60fe68 100644 --- a/examples/preact/column-resizing/src/main.tsx +++ b/examples/preact/column-resizing/src/main.tsx @@ -90,7 +90,7 @@ function App() { debugHeaders: true, debugColumns: true, }, - (state) => state, + (state) => state, // default selector ) return ( @@ -338,9 +338,7 @@ function App() { - state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
+
{JSON.stringify(table.state, null, 2)}
) } diff --git a/examples/preact/column-sizing/src/main.tsx b/examples/preact/column-sizing/src/main.tsx index 3086f20950..cae3109dcc 100644 --- a/examples/preact/column-sizing/src/main.tsx +++ b/examples/preact/column-sizing/src/main.tsx @@ -71,7 +71,7 @@ function App() { debugHeaders: true, debugColumns: true, }, - (state) => state, + (state) => state, // default selector ) return ( @@ -267,9 +267,7 @@ function App() { - state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
+
{JSON.stringify(table.state, null, 2)}
) } diff --git a/examples/preact/column-visibility/src/main.tsx b/examples/preact/column-visibility/src/main.tsx index 2f71407dd0..13bf268788 100644 --- a/examples/preact/column-visibility/src/main.tsx +++ b/examples/preact/column-visibility/src/main.tsx @@ -66,106 +66,99 @@ function App() { const stressTest = () => setData(makeData(1_000)) const rerender = useReducer(() => ({}), {})[1] - const table = useTable({ - _features, - _rowModels: {}, - columns, - data, - debugTable: true, - debugHeaders: true, - debugColumns: true, - }) + const table = useTable( + { + _features, + _rowModels: {}, + columns, + data, + debugTable: true, + debugHeaders: true, + debugColumns: true, + }, + (state) => state, // default selector + ) return ( - ({ - columnVisibility: state.columnVisibility, - })} - > - {(_state) => ( -
-
- - -
-
-
-
+
+
+ + +
+
+
+
+ +
+ {table.getAllLeafColumns().map((column) => { + return ( +
- {table.getAllLeafColumns().map((column) => { - return ( -
- -
- ) - })} -
-
-
- - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - + ) + })} + +
+
- {header.isPlaceholder ? null : ( - - )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - ))} - + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + ))} - - - {table.getFooterGroups().map((footerGroup) => ( - - {footerGroup.headers.map((header) => ( - - ))} - + + ))} + + + {table.getFooterGroups().map((footerGroup) => ( + + {footerGroup.headers.map((header) => ( + ))} - -
+ {header.isPlaceholder ? null : ( + + )} +
- -
+ +
- {header.isPlaceholder ? null : ( - - )} -
+ {header.isPlaceholder ? null : ( + + )} +
-
- -
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + +
+ +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/preact/composable-tables/src/components/table-components.tsx b/examples/preact/composable-tables/src/components/table-components.tsx index 887929b5d1..33acb335d7 100644 --- a/examples/preact/composable-tables/src/components/table-components.tsx +++ b/examples/preact/composable-tables/src/components/table-components.tsx @@ -14,71 +14,66 @@ export function PaginationControls() { const table = useTableContext() return ( - - {/* whole pagination slice — no selector needed */} - {(pagination: PaginationState) => ( -
- - - - - - Page{' '} - - {(pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - - - - | Go to page: - { - const page = (e.target as HTMLInputElement).value - ? Number((e.target as HTMLInputElement).value) - 1 - : 0 - table.setPageIndex(page) - }} - /> - - -
- )} -
+
+ + + + + + Page{' '} + + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + + + + | Go to page: + { + const page = (e.target as HTMLInputElement).value + ? Number((e.target as HTMLInputElement).value) - 1 + : 0 + table.setPageIndex(page) + }} + /> + + +
) } @@ -113,15 +108,15 @@ export function TableToolbar({ return (

{title}

-
- - +
{onRefresh && } {onStressTest && ( )} + +
) diff --git a/examples/preact/composable-tables/src/index.css b/examples/preact/composable-tables/src/index.css index 75b7db2d40..4d8fef0142 100644 --- a/examples/preact/composable-tables/src/index.css +++ b/examples/preact/composable-tables/src/index.css @@ -174,6 +174,12 @@ tfoot th { border-spacing: 0; } +.table-toolbar-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + .table-toolbar button { border: 1px solid #ccc; border-radius: 4px; diff --git a/examples/preact/composable-tables/src/main.tsx b/examples/preact/composable-tables/src/main.tsx index bd0522f53e..473ca140e7 100644 --- a/examples/preact/composable-tables/src/main.tsx +++ b/examples/preact/composable-tables/src/main.tsx @@ -76,7 +76,7 @@ function UsersTable() { debugTable: true, // more table options }, - // (state) => state, // alternatively, subscribe to the entire state instead of using table.Subscribe or selectors down below + (state) => state, // default selector ) return ( @@ -264,12 +264,15 @@ function ProductsTable() { ) // Create the table using the same useAppTable hook - const table = useAppTable({ - debugTable: true, - columns, - data, - getRowId: (row) => row.id, - }) + const table = useAppTable( + { + debugTable: true, + columns, + data, + getRowId: (row) => row.id, + }, + (state) => state, // default selector + ) return ( setData(makeData(200_000)) const [density, setDensity] = useState('md') - const table = useTable({ - _features, - _rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), + const table = useTable( + { + _features, + _rowModels: { + filteredRowModel: createFilteredRowModel(filterFns), + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(sortFns), + }, + columns, + data, + debugTable: true, + state: { + density, // passing the density state to the table, TS is still happy :) + }, + onDensityChange: setDensity, // using the new onDensityChange option, TS is still happy :) }, - columns, - data, - debugTable: true, - state: { - density, // passing the density state to the table, TS is still happy :) - }, - onDensityChange: setDensity, // using the new onDensityChange option, TS is still happy :) - }) + (state) => state, // default selector + ) return (
@@ -320,9 +323,7 @@ function App() { Showing {table.getRowModel().rows.length.toLocaleString()} of{' '} {table.getRowCount().toLocaleString()} Rows
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
+
{JSON.stringify(table.state, null, 2)}
) } diff --git a/examples/preact/expanding/src/main.tsx b/examples/preact/expanding/src/main.tsx index 16ac1f9d7f..a89afd0872 100644 --- a/examples/preact/expanding/src/main.tsx +++ b/examples/preact/expanding/src/main.tsx @@ -115,154 +115,143 @@ function App() { const refreshData = () => setData(() => makeData(100, 5, 3)) const stressTest = () => setData(() => makeData(10_000, 5, 3)) - const table = useTable({ - _features, - _rowModels: { - expandedRowModel: createExpandedRowModel(), - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), + const table = useTable( + { + _features, + _rowModels: { + expandedRowModel: createExpandedRowModel(), + filteredRowModel: createFilteredRowModel(filterFns), + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(sortFns), + }, + columns, + data, + getSubRows: (row) => row.subRows, + // filterFromLeafRows: true, + // maxLeafRowFilterDepth: 0, + debugTable: true, }, - columns, - data, - getSubRows: (row) => row.subRows, - // filterFromLeafRows: true, - // maxLeafRowFilterDepth: 0, - debugTable: true, - }) + (state) => state, // default selector + ) return ( - ({ - expanded: state.expanded, - pagination: state.pagination, - rowSelection: state.rowSelection, - columnFilters: state.columnFilters, - sorting: state.sorting, - })} - > - {(state) => ( -
-
- - -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + ) + })} + + ) + })} + +
- {header.isPlaceholder ? null : ( +
+
+ + +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { - return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) : null} + + )} + ) })} - -
+ {header.isPlaceholder ? null : ( +
+ + {header.column.getCanFilter() ? (
- - {header.column.getCanFilter() ? ( -
- -
- ) : null} +
- )} -
- -
-
-
- - - - - -
Page
- - {(state.pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = (e.target as HTMLInputElement).value - ? Number((e.target as HTMLInputElement).value) - 1 - : 0 - table.setPageIndex(page) - }} - className="page-size-input" - /> - - -
-
{table.getRowModel().rows.length.toLocaleString()} Rows
-
- -
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - +
+ +
+
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = (e.target as HTMLInputElement).value + ? Number((e.target as HTMLInputElement).value) - 1 + : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
{table.getRowModel().rows.length.toLocaleString()} Rows
+
+ +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/preact/filters-faceted/src/main.tsx b/examples/preact/filters-faceted/src/main.tsx index 3cbda2d826..d9ceab7302 100644 --- a/examples/preact/filters-faceted/src/main.tsx +++ b/examples/preact/filters-faceted/src/main.tsx @@ -89,154 +89,144 @@ function App() { const stressTest = () => setData((_old) => makeData(200_000)) const rerender = useReducer(() => ({}), {})[1] - const table = useTable({ - _features, - _rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), // client-side filtering - paginatedRowModel: createPaginatedRowModel(), - facetedRowModel: createFacetedRowModel(), // client-side faceting - facetedMinMaxValues: createFacetedMinMaxValues(), // generate min/max values for range filter - facetedUniqueValues: createFacetedUniqueValues(), // generate unique values for select filter/autocomplete + const table = useTable( + { + _features, + _rowModels: { + filteredRowModel: createFilteredRowModel(filterFns), // client-side filtering + paginatedRowModel: createPaginatedRowModel(), + facetedRowModel: createFacetedRowModel(), // client-side faceting + facetedMinMaxValues: createFacetedMinMaxValues(), // generate min/max values for range filter + facetedUniqueValues: createFacetedUniqueValues(), // generate unique values for select filter/autocomplete + }, + columns, + data, + debugTable: true, + debugHeaders: true, + debugColumns: false, }, - columns, - data, - debugTable: true, - debugHeaders: true, - debugColumns: false, - }) + (state) => state, // default selector + ) return ( - ({ - columnFilters: state.columnFilters, - pagination: state.pagination, - })} - > - {(state) => ( -
-
- - -
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { +
+
+ + +
+
- {header.isPlaceholder ? null : ( - <> -
- -
- {header.column.getCanFilter() ? ( -
- -
- ) : null} - - )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) })} - -
- -
+ {header.isPlaceholder ? null : ( + <> +
+ +
+ {header.column.getCanFilter() ? ( +
+ +
+ ) : null} + + )} +
-
-
- - - - - -
Page
- - {(state.pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = (e.target as HTMLInputElement).value - ? Number((e.target as HTMLInputElement).value) - 1 - : 0 - table.setPageIndex(page) - }} - className="page-size-input" - /> - - -
-
- {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows -
-
- -
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + + + ) + })} + + ) + })} + + +
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = (e.target as HTMLInputElement).value + ? Number((e.target as HTMLInputElement).value) - 1 + : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
+ {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows +
+
+ +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/preact/filters-fuzzy/src/main.tsx b/examples/preact/filters-fuzzy/src/main.tsx index 16aaa92b89..648d3b55f3 100644 --- a/examples/preact/filters-fuzzy/src/main.tsx +++ b/examples/preact/filters-fuzzy/src/main.tsx @@ -114,23 +114,26 @@ function App() { const refreshData = () => setData((_old) => makeData(5_000)) const stressTest = () => setData((_old) => makeData(200_000)) - const table = useTable({ - _features, - _rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - fuzzy: fuzzyFilter, - }), - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), + const table = useTable( + { + _features, + _rowModels: { + filteredRowModel: createFilteredRowModel({ + ...filterFns, + fuzzy: fuzzyFilter, + }), + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(sortFns), + }, + columns, + data, + globalFilterFn: 'fuzzy', // apply fuzzy filter to the global filter (most common use case for fuzzy filter) + debugTable: true, + debugHeaders: true, + debugColumns: false, }, - columns, - data, - globalFilterFn: 'fuzzy', // apply fuzzy filter to the global filter (most common use case for fuzzy filter) - debugTable: true, - debugHeaders: true, - debugColumns: false, - }) + (state) => state, // default selector + ) // apply the fuzzy sort if the fullName column is being filtered useEffect(() => { @@ -142,159 +145,142 @@ function App() { }, [table.store.state.columnFilters[0]?.id]) return ( - ({ - columnFilters: state.columnFilters, - globalFilter: state.globalFilter, - pagination: state.pagination, - sorting: state.sorting, - })} - > - {(state) => ( -
-
- - -
-
- table.setGlobalFilter(String(value))} - className="summary-panel" - placeholder="Search all columns..." - /> -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { +
+
+ + +
+
+ table.setGlobalFilter(String(value))} + className="summary-panel" + placeholder="Search all columns..." + /> +
+
+
- {header.isPlaceholder ? null : ( - <> -
- - {{ - asc: ' 🔼', - desc: ' 🔽', - }[header.column.getIsSorted() as string] ?? null} -
- {header.column.getCanFilter() ? ( -
- -
- ) : null} - - )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) })} - -
- -
+ {header.isPlaceholder ? null : ( + <> +
+ + {{ + asc: ' 🔼', + desc: ' 🔽', + }[header.column.getIsSorted() as string] ?? null} +
+ {header.column.getCanFilter() ? ( +
+ +
+ ) : null} + + )} +
-
-
- - - - - -
Page
- - {(state.pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = (e.target as HTMLInputElement).value - ? Number((e.target as HTMLInputElement).value) - 1 - : 0 - table.setPageIndex(page) - }} - className="page-size-input" - /> - - -
-
- {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows -
-
- -
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + + + ) + })} + + ) + })} + + +
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = (e.target as HTMLInputElement).value + ? Number((e.target as HTMLInputElement).value) - 1 + : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
+ {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows +
+
+ +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/preact/filters/src/main.tsx b/examples/preact/filters/src/main.tsx index cf91bbb060..14e1135aa0 100644 --- a/examples/preact/filters/src/main.tsx +++ b/examples/preact/filters/src/main.tsx @@ -90,152 +90,142 @@ function App() { const refreshData = () => setData((_old) => makeData(5_000)) const stressTest = () => setData((_old) => makeData(200_000)) - const table = useTable({ - _features, - _rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), // client side filtering - paginatedRowModel: createPaginatedRowModel(), + const table = useTable( + { + _features, + _rowModels: { + filteredRowModel: createFilteredRowModel(filterFns), // client side filtering + paginatedRowModel: createPaginatedRowModel(), + }, + columns, + data, + debugTable: true, + debugColumns: true, }, - columns, - data, - debugTable: true, - debugColumns: true, - }) + (state) => state, // default selector + ) return ( - ({ - columnFilters: state.columnFilters, - pagination: state.pagination, - })} - > - {(state) => ( -
-
- - -
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { +
+
+ + +
+
- {header.isPlaceholder ? null : ( - <> -
- -
- {header.column.getCanFilter() ? ( -
- -
- ) : null} - - )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) })} - -
- -
+ {header.isPlaceholder ? null : ( + <> +
+ +
+ {header.column.getCanFilter() ? ( +
+ +
+ ) : null} + + )} +
-
-
- - - - - -
Page
- - {(state.pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = (e.target as HTMLInputElement).value - ? Number((e.target as HTMLInputElement).value) - 1 - : 0 - table.setPageIndex(page) - }} - className="page-size-input" - /> - - -
-
- {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows -
-
- -
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + + + ) + })} + + ) + })} + + +
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = (e.target as HTMLInputElement).value + ? Number((e.target as HTMLInputElement).value) - 1 + : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
+ {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows +
+
+ +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/preact/grouping/src/main.tsx b/examples/preact/grouping/src/main.tsx index c193d32e28..adb0ed9f0a 100644 --- a/examples/preact/grouping/src/main.tsx +++ b/examples/preact/grouping/src/main.tsx @@ -96,7 +96,7 @@ function App() { data, debugTable: true, }, - (state) => state, // subscribe to all state changes + (state) => state, // default selector ) return ( @@ -253,9 +253,7 @@ function App() {
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
+
{JSON.stringify(table.state, null, 2)}
) } diff --git a/examples/preact/pagination/src/main.tsx b/examples/preact/pagination/src/main.tsx index e89fb7d756..7ec2e4e4eb 100644 --- a/examples/preact/pagination/src/main.tsx +++ b/examples/preact/pagination/src/main.tsx @@ -79,134 +79,127 @@ function MyTable({ data: Array columns: ReturnType }) { - const table = useTable({ - _features, - _rowModels: { - paginatedRowModel: createPaginatedRowModel(), + const table = useTable( + { + _features, + _rowModels: { + paginatedRowModel: createPaginatedRowModel(), + }, + columns, + data, + debugTable: true, + // no need to pass pageCount or rowCount with client-side pagination as it is calculated automatically }, - columns, - data, - debugTable: true, - // no need to pass pageCount or rowCount with client-side pagination as it is calculated automatically - }) + (state) => state, // default selector + ) return ( - ({ - pagination: state.pagination, - })} - > - {(state) => ( -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { +
+
+
-
- -
-
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) })} - -
- -
+
+ +
+
-
-
- - - - - -
Page
- - {(state.pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = (e.target as HTMLInputElement).value - ? Number((e.target as HTMLInputElement).value) - 1 - : 0 - table.setPageIndex(page) - }} - className="page-size-input" - /> - - -
-
- Showing {table.getRowModel().rows.length.toLocaleString()} of{' '} - {table.getRowCount().toLocaleString()} Rows -
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + + + ) + })} + + ) + })} + + +
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = (e.target as HTMLInputElement).value + ? Number((e.target as HTMLInputElement).value) - 1 + : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
+ Showing {table.getRowModel().rows.length.toLocaleString()} of{' '} + {table.getRowCount().toLocaleString()} Rows +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/preact/row-pinning/src/main.tsx b/examples/preact/row-pinning/src/main.tsx index bda0fa833b..ae5eef2cbd 100644 --- a/examples/preact/row-pinning/src/main.tsx +++ b/examples/preact/row-pinning/src/main.tsx @@ -170,7 +170,7 @@ function App() { keepPinnedRows, debugAll: true, }, - (state) => state, // subscribe to all re-renders + (state) => state, // default selector ) return ( diff --git a/examples/preact/row-selection/src/main.tsx b/examples/preact/row-selection/src/main.tsx index 8ad15dd487..96fe2eb68f 100644 --- a/examples/preact/row-selection/src/main.tsx +++ b/examples/preact/row-selection/src/main.tsx @@ -48,17 +48,11 @@ function App() { id: 'select', header: () => { return ( - - {() => ( - - )} - + ) }, cell: ({ row }) => ( @@ -126,223 +120,185 @@ function App() { // enableRowSelection: row => row.original.age > 18, // or enable row selection conditionally per row debugTable: true, }, - // (state) => state, // uncomment to subscribe to the entire table state (this is how v8 used to work by default) + (state) => state, // default selector ) useTanStackTableDevtools(table, 'Row Selection Example') return ( <> - ({ - // Store mode: multiple slices — must use table.store + explicit selector - columnFilters: state.columnFilters, - globalFilter: state.globalFilter, - pagination: state.pagination, - })} - > - {(state) => ( -
-
- - -
-
- - table.setGlobalFilter((e.target as HTMLInputElement).value) - } - className="summary-panel" - placeholder="Search all columns..." - /> -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { +
+
+ + +
+
+ + table.setGlobalFilter((e.target as HTMLInputElement).value) + } + className="summary-panel" + placeholder="Search all columns..." + /> +
+
+
- {header.isPlaceholder ? null : ( - <> - - {header.column.getCanFilter() ? ( -
- -
- ) : null} - - )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { return ( - rowSelection?.[row.id]} // optional: narrow to this row id - > - {(_isRowSelected) => ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) })} - - - - - + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + ) + })} - -
- -
+ {header.isPlaceholder ? null : ( + <> + + {header.column.getCanFilter() ? ( +
+ +
+ ) : null} + )} - +
- - {(_rowSelection) => ( - - )} - - - Page Rows ( - {table.getRowModel().rows.length.toLocaleString()}) -
+ +
-
-
- - - - - -
Page
- - {( - table.store.state.pagination.pageIndex + 1 - ).toLocaleString()}{' '} - of {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = (e.target as HTMLInputElement).value - ? Number((e.target as HTMLInputElement).value) - 1 - : 0 - table.setPageIndex(page) - }} - className="page-size-input" + ) + })} + + + + + - - -
-
-
- ({ - numSelected: Object.keys(state.rowSelection).length, - })} - > - {({ numSelected }) => <>{numSelected.toLocaleString()} of } - - {table.getPreFilteredRowModel().rows.length.toLocaleString()}{' '} - Total Rows Selected -
-
-
-
- -
-
- -
-
- - state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
-
- )} - + + + Page Rows ({table.getRowModel().rows.length.toLocaleString()}) + + + + +
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = (e.target as HTMLInputElement).value + ? Number((e.target as HTMLInputElement).value) - 1 + : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
+
+ <> + {Object.keys(table.state.rowSelection).length.toLocaleString()}{' '} + of{' '} + + {table.getPreFilteredRowModel().rows.length.toLocaleString()} Total + Rows Selected +
+
+
+
+ +
+
+ +
+
+ +
{JSON.stringify(table.state, null, 2)}
+
+
) } diff --git a/examples/preact/sorting/src/main.tsx b/examples/preact/sorting/src/main.tsx index 4a5576f79b..ff6c07108d 100644 --- a/examples/preact/sorting/src/main.tsx +++ b/examples/preact/sorting/src/main.tsx @@ -111,7 +111,7 @@ function App() { // isMultiSortEvent: (e) => true, //Make all clicks multi-sort - default requires `shift` key // maxMultiSortColCount: 3, // only allow 3 columns to be sorted at once - default is Infinity }, - // (state) => ({ state }), // uncomment to subscribe to the entire table state (this is how it worked in v8 by default) + (state) => state, // default selector ) useTanStackTableDevtools(table, 'Sorting Example') @@ -122,80 +122,70 @@ function App() {
- - {(_state) => ( - <> -
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { + <> +
+
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + ) + })} + + ))} + + + {table + .getRowModel() + .rows.slice(0, 10) + .map((row) => { + return ( + + {row.getAllCells().map((cell) => { return ( - + ) })} - ))} - - - {table - .getRowModel() - .rows.slice(0, 10) - .map((row) => { - return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - - ) - })} - -
+ {header.isPlaceholder ? null : ( +
+ + {{ + asc: ' 🔼', + desc: ' 🔽', + }[header.column.getIsSorted() as string] ?? null} +
+ )} +
- {header.isPlaceholder ? null : ( -
- - {{ - asc: ' 🔼', - desc: ' 🔽', - }[header.column.getIsSorted() as string] ?? null} -
- )} -
+ +
- -
-
{table.getRowModel().rows.length.toLocaleString()} Rows
-
- -
- {/* Store mode: full state for debugging */} - state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
- - )} - + ) + })} + + +
{table.getRowModel().rows.length.toLocaleString()} Rows
+
+ +
+ {/* Store mode: full state for debugging */} +
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/preact/sub-components/src/main.tsx b/examples/preact/sub-components/src/main.tsx index 2c8a46c6a4..e632c10ff8 100644 --- a/examples/preact/sub-components/src/main.tsx +++ b/examples/preact/sub-components/src/main.tsx @@ -91,72 +91,71 @@ function Table({ getRowCanExpand, renderSubComponent, }: TableProps): JSX.Element { - const table = useTable({ - debugTable: true, - _features, - _rowModels: { - expandedRowModel: createExpandedRowModel(), + const table = useTable( + { + debugTable: true, + _features, + _rowModels: { + expandedRowModel: createExpandedRowModel(), + }, + columns, + data, + getRowCanExpand, }, - columns, - data, - getRowCanExpand, - }) + (state) => state, // default selector + ) return ( - state}> - {() => ( -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { +
+
+
- {header.isPlaceholder ? null : ( -
- -
- )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { return ( - - - {/* first row is a normal row */} - {row.getAllCells().map((cell) => { - return ( - - ) - })} - - {row.getIsExpanded() && ( - - {/* 2nd row is a custom 1 cell row */} - - + ) })} - -
- -
- {renderSubComponent({ row })} -
+ {header.isPlaceholder ? null : ( +
+ +
)} - +
-
-
{table.getRowModel().rows.length.toLocaleString()} Rows
-
- )} - + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + + {/* first row is a normal row */} + {row.getAllCells().map((cell) => { + return ( + + + + ) + })} + + {row.getIsExpanded() && ( + + {/* 2nd row is a custom 1 cell row */} + + {renderSubComponent({ row })} + + + )} + + ) + })} + + +
+
{table.getRowModel().rows.length.toLocaleString()} Rows
+
) } diff --git a/examples/preact/with-tanstack-query/src/main.tsx b/examples/preact/with-tanstack-query/src/main.tsx index 4e4e47a3b8..fb645ef10d 100644 --- a/examples/preact/with-tanstack-query/src/main.tsx +++ b/examples/preact/with-tanstack-query/src/main.tsx @@ -69,18 +69,21 @@ function App() { const defaultData = useMemo(() => [], []) - const table = useTable({ - _features, - _rowModels: {}, - columns, - data: dataQuery.data?.rows ?? defaultData, - rowCount: dataQuery.data?.rowCount, - atoms: { - pagination: paginationAtom, + const table = useTable( + { + _features, + _rowModels: {}, + columns, + data: dataQuery.data?.rows ?? defaultData, + rowCount: dataQuery.data?.rowCount, + atoms: { + pagination: paginationAtom, + }, + manualPagination: true, // we're doing manual "server-side" pagination + debugTable: true, }, - manualPagination: true, // we're doing manual "server-side" pagination - debugTable: true, - }) + (state) => state, // default selector + ) return (
diff --git a/examples/react/basic-external-atoms/src/main.tsx b/examples/react/basic-external-atoms/src/main.tsx index 1c824c1b1c..f22eef25ae 100644 --- a/examples/react/basic-external-atoms/src/main.tsx +++ b/examples/react/basic-external-atoms/src/main.tsx @@ -92,7 +92,7 @@ function App() { }, debugTable: true, }, - (state) => state, // subscribe to all state changes for re-rendering + (state) => state, // default selector ) return ( diff --git a/examples/react/basic-external-state/src/main.tsx b/examples/react/basic-external-state/src/main.tsx index d5ee468e57..031c3f290b 100644 --- a/examples/react/basic-external-state/src/main.tsx +++ b/examples/react/basic-external-state/src/main.tsx @@ -73,22 +73,25 @@ function App() { // console.log('pagination', pagination) // Create the table and pass state + onChange handlers - const table = useTable({ - debugTable: true, - _features, - _rowModels: { - sortedRowModel: createSortedRowModel(sortFns), - paginatedRowModel: createPaginatedRowModel(), + const table = useTable( + { + debugTable: true, + _features, + _rowModels: { + sortedRowModel: createSortedRowModel(sortFns), + paginatedRowModel: createPaginatedRowModel(), + }, + columns, + data, + state: { + sorting, // connect our sorting state back down to the table + pagination, // connect our pagination state back down to the table + }, + onSortingChange: setSorting, // raise sorting state changes to our own state management + onPaginationChange: setPagination, // raise pagination state changes to our own state management }, - columns, - data, - state: { - sorting, // connect our sorting state back down to the table - pagination, // connect our pagination state back down to the table - }, - onSortingChange: setSorting, // raise sorting state changes to our own state management - onPaginationChange: setPagination, // raise pagination state changes to our own state management - }) + (state) => state, // default selector + ) return (
diff --git a/examples/react/basic-use-app-table/src/main.tsx b/examples/react/basic-use-app-table/src/main.tsx index e148f36400..e00d6c98eb 100644 --- a/examples/react/basic-use-app-table/src/main.tsx +++ b/examples/react/basic-use-app-table/src/main.tsx @@ -103,12 +103,15 @@ function App() { // 7. Create the table instance with the required columns and data. // Features and row models are already defined in the createTableHook call above - const table = useAppTable({ - debugTable: true, - columns, - data, - // add additional table options here or in the createTableHook call above - }) + const table = useAppTable( + { + debugTable: true, + columns, + data, + // add additional table options here or in the createTableHook call above + }, + (state) => state, // default selector + ) // 8. Render your table markup from the table instance APIs return ( diff --git a/examples/react/basic-use-table/src/main.tsx b/examples/react/basic-use-table/src/main.tsx index fab6db677f..5d60ad4d8f 100644 --- a/examples/react/basic-use-table/src/main.tsx +++ b/examples/react/basic-use-table/src/main.tsx @@ -96,14 +96,17 @@ function App() { const rerender = React.useReducer(() => ({}), {})[1] // 6. Create the table instance with required _features, columns, and data - const table = useTable({ - debugTable: true, - _features, // new required option in V9. Tell the table which features you are importing and using (better tree-shaking) - _rowModels: {}, // `Core` row model is now included by default, but you can still override it here - columns, - data, - // add additional table options here - }) + const table = useTable( + { + debugTable: true, + _features, // new required option in V9. Tell the table which features you are importing and using (better tree-shaking) + _rowModels: {}, // `Core` row model is now included by default, but you can still override it here + columns, + data, + // add additional table options here + }, + (state) => state, // default selector + ) // 7. Render your table markup from the table instance APIs return ( diff --git a/examples/react/column-dnd/src/main.tsx b/examples/react/column-dnd/src/main.tsx index 89fd8b029a..cf28422cdf 100644 --- a/examples/react/column-dnd/src/main.tsx +++ b/examples/react/column-dnd/src/main.tsx @@ -146,7 +146,7 @@ function App() { columnOrder: columns.map((c) => c.id!), }, }, - (state) => state, + (state) => state, // default selector ) // reorder columns after drag & drop @@ -223,9 +223,7 @@ function App() { ))} - state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
+
{JSON.stringify(table.state, null, 2)}
) diff --git a/examples/react/column-groups/src/main.tsx b/examples/react/column-groups/src/main.tsx index 9a6cc40562..b753dfef29 100644 --- a/examples/react/column-groups/src/main.tsx +++ b/examples/react/column-groups/src/main.tsx @@ -66,13 +66,16 @@ function App() { const stressTest = () => setData(makeData(1_000)) const rerender = React.useReducer(() => ({}), {})[1] - const table = useTable({ - debugTable: true, - _features, - _rowModels: {}, - columns, - data, - }) + const table = useTable( + { + debugTable: true, + _features, + _rowModels: {}, + columns, + data, + }, + (state) => state, // default selector + ) return (
diff --git a/examples/react/column-ordering/src/main.tsx b/examples/react/column-ordering/src/main.tsx index 7189aa68f9..5e7ab7012d 100644 --- a/examples/react/column-ordering/src/main.tsx +++ b/examples/react/column-ordering/src/main.tsx @@ -72,15 +72,18 @@ function App() { const refreshData = () => setData(makeData(20)) const stressTest = () => setData(makeData(1_000)) - const table = useTable({ - _features, - _rowModels: {}, - columns, - data, - debugTable: true, - debugHeaders: true, - debugColumns: true, - }) + const table = useTable( + { + _features, + _rowModels: {}, + columns, + data, + debugTable: true, + debugHeaders: true, + debugColumns: true, + }, + (state) => state, // default selector + ) const randomizeColumns = () => { table.setColumnOrder( @@ -89,108 +92,96 @@ function App() { } return ( - ({ - // subscribe to only the column order and visibility state changes at root level - columnOrder: state.columnOrder, - columnVisibility: state.columnVisibility, - })} - > - {(_state) => ( -
-
-
+
+
+
+ +
+ {table.getAllLeafColumns().map((column) => { + return ( +
- {table.getAllLeafColumns().map((column) => { - return ( -
- -
- ) - })} -
-
-
- - - -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - + ) + })} + +
+
+ + + +
+
+
- {header.isPlaceholder ? null : ( - - )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - ))} - + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + ))} - - - {table.getFooterGroups().map((footerGroup) => ( - - {footerGroup.headers.map((header) => ( - - ))} - + + ))} + + + {table.getFooterGroups().map((footerGroup) => ( + + {footerGroup.headers.map((header) => ( + ))} - -
+ {header.isPlaceholder ? null : ( + + )} +
- -
+ +
- {header.isPlaceholder ? null : ( - - )} -
+ {header.isPlaceholder ? null : ( + + )} +
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + +
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/react/column-pinning-split/src/main.tsx b/examples/react/column-pinning-split/src/main.tsx index 5c46b6a97e..8c9232bb41 100644 --- a/examples/react/column-pinning-split/src/main.tsx +++ b/examples/react/column-pinning-split/src/main.tsx @@ -78,15 +78,18 @@ function App() { const refreshData = () => setData(makeData(1_000)) const stressTest = () => setData(makeData(500_000)) - const table = useTable({ - _features, - _rowModels: {}, - columns, - data, - debugTable: true, - debugHeaders: true, - debugColumns: true, - }) + const table = useTable( + { + _features, + _rowModels: {}, + columns, + data, + debugTable: true, + debugHeaders: true, + debugColumns: true, + }, + (state) => state, // default selector + ) const randomizeColumns = () => { table.setColumnOrder( @@ -95,282 +98,270 @@ function App() { } return ( - ({ - columnVisibility: state.columnVisibility, - columnOrder: state.columnOrder, - columnPinning: state.columnPinning, - })} - > - {(_state) => ( -
-
-
+
+
+
+ +
+ {table.getAllLeafColumns().map((column) => { + return ( +
- {table.getAllLeafColumns().map((column) => { - return ( -
- -
- ) - })} -
-
-
- - - -
-
-

- This example takes advantage of the "splitting" APIs. (APIs that - have "left, "center", and "right" modifiers) -

-
- - - {table.getLeftHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - + ) + })} + +
+
+ + + +
+
+

+ This example takes advantage of the "splitting" APIs. (APIs that have + "left, "center", and "right" modifiers) +

+
+
-
- {header.isPlaceholder ? null : ( - - )} -
- {!header.isPlaceholder && header.column.getCanPin() && ( -
- {header.column.getIsPinned() !== 'left' ? ( - - ) : null} - {header.column.getIsPinned() ? ( - - ) : null} - {header.column.getIsPinned() !== 'right' ? ( - - ) : null} -
- )} -
+ + {table.getLeftHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ))} - - - {table - .getRowModel() - .rows.slice(0, 20) - .map((row) => { - return ( - - {row.getLeftVisibleCells().map((cell) => { - return ( - - ) - })} - - ) - })} - -
+
+ {header.isPlaceholder ? null : ( + + )} +
+ {!header.isPlaceholder && header.column.getCanPin() && ( +
+ {header.column.getIsPinned() !== 'left' ? ( + + ) : null} + {header.column.getIsPinned() ? ( + + ) : null} + {header.column.getIsPinned() !== 'right' ? ( + + ) : null} +
+ )} +
- -
- - - {table.getCenterHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} + + ))} + + + {table + .getRowModel() + .rows.slice(0, 20) + .map((row) => { + return ( + + {row.getLeftVisibleCells().map((cell) => { + return ( + + ) + })} + ) + })} + +
-
- {header.isPlaceholder ? null : ( - - )} -
- {!header.isPlaceholder && header.column.getCanPin() && ( -
- {header.column.getIsPinned() !== 'left' ? ( - - ) : null} - {header.column.getIsPinned() ? ( - - ) : null} - {header.column.getIsPinned() !== 'right' ? ( - - ) : null} -
- )} -
+ +
+ + + {table.getCenterHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ))} - - - {table - .getRowModel() - .rows.slice(0, 20) - .map((row) => { - return ( - - {row.getCenterVisibleCells().map((cell) => { - return ( - - ) - })} - - ) - })} - -
+
+ {header.isPlaceholder ? null : ( + + )} +
+ {!header.isPlaceholder && header.column.getCanPin() && ( +
+ {header.column.getIsPinned() !== 'left' ? ( + + ) : null} + {header.column.getIsPinned() ? ( + + ) : null} + {header.column.getIsPinned() !== 'right' ? ( + + ) : null} +
+ )} +
- -
- - - {table.getRightHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} + + ))} + + + {table + .getRowModel() + .rows.slice(0, 20) + .map((row) => { + return ( + + {row.getCenterVisibleCells().map((cell) => { + return ( + + ) + })} + ) + })} + +
-
- {header.isPlaceholder ? null : ( - - )} -
- {!header.isPlaceholder && header.column.getCanPin() && ( -
- {header.column.getIsPinned() !== 'left' ? ( - - ) : null} - {header.column.getIsPinned() ? ( - - ) : null} - {header.column.getIsPinned() !== 'right' ? ( - - ) : null} -
- )} -
+ +
+ + + {table.getRightHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ))} - - - {table - .getRowModel() - .rows.slice(0, 20) - .map((row) => { - return ( - - {row.getRightVisibleCells().map((cell) => { - return ( - - ) - })} - - ) - })} - -
+
+ {header.isPlaceholder ? null : ( + + )} +
+ {!header.isPlaceholder && header.column.getCanPin() && ( +
+ {header.column.getIsPinned() !== 'left' ? ( + + ) : null} + {header.column.getIsPinned() ? ( + + ) : null} + {header.column.getIsPinned() !== 'right' ? ( + + ) : null} +
+ )} +
- -
-
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + + {table + .getRowModel() + .rows.slice(0, 20) + .map((row) => { + return ( + + {row.getRightVisibleCells().map((cell) => { + return ( + + + + ) + })} + + ) + })} + + +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/react/column-pinning-sticky/src/main.tsx b/examples/react/column-pinning-sticky/src/main.tsx index c8aa3a378e..abc772531d 100644 --- a/examples/react/column-pinning-sticky/src/main.tsx +++ b/examples/react/column-pinning-sticky/src/main.tsx @@ -116,7 +116,7 @@ function App() { debugColumns: true, columnResizeMode: 'onChange', }, - (state) => state, + (state) => state, // default selector ) const randomizeColumns = () => { @@ -126,196 +126,153 @@ function App() { } return ( - ({ - columnVisibility: state.columnVisibility, - columnOrder: state.columnOrder, - columnPinning: state.columnPinning, - columnSizing: state.columnSizing, - columnResizing: state.columnResizing, - })} - > - {(_topLevelState) => ( -
-
-
+
+
+
+ +
+ {table.getAllLeafColumns().map((column) => { + return ( +
- {table.getAllLeafColumns().map((column) => { - return ( -
- -
- ) - })} -
-
-
- - - -
-
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - const { column } = header + ) + })} + +
+
+ + + +
+
+
+
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + const { column } = header - return ( - ({ - isResizingColumn: - state.columnResizing.isResizingColumn === - column.id, - columnSize: state.columnSizing[column.id], - })} - > - {() => ( - - )} - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => { - const { column } = cell - return ( - ({ - isResizingColumn: - state.columnResizing.isResizingColumn === - column.id, - columnSize: state.columnSizing[column.id], - })} - > - {() => ( - + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => { + const { column } = cell + return ( + + ) + })} + + ))} + +
+
+ {header.isPlaceholder ? null : ( + <> + {' '} + + )} + {/* Demo getIndex behavior */} + {column.getIndex(column.getIsPinned() || 'center')} +
+ {!header.isPlaceholder && header.column.getCanPin() && ( +
+ {header.column.getIsPinned() !== 'left' ? ( + - ) : null} - {header.column.getIsPinned() ? ( - - ) : null} - {header.column.getIsPinned() !== 'right' ? ( - - ) : null} -
- )} -
header.column.resetSize()} - onMouseDown={header.getResizeHandler()} - onTouchStart={header.getResizeHandler()} - className={`resizer ${ - header.column.getIsResizing() - ? 'isResizing' - : '' - }`} - /> -
{ + header.column.pin('right') + }} + > + {'=>'} + + ) : null} + + )} +
header.column.resetSize()} + onMouseDown={header.getResizeHandler()} + onTouchStart={header.getResizeHandler()} + className={`resizer ${ + header.column.getIsResizing() ? 'isResizing' : '' + }`} + /> + + ) + })} +
+ +
+
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/react/column-pinning/src/main.tsx b/examples/react/column-pinning/src/main.tsx index 4fa88ae6db..9c301b9612 100644 --- a/examples/react/column-pinning/src/main.tsx +++ b/examples/react/column-pinning/src/main.tsx @@ -80,11 +80,14 @@ function App() { const refreshData = () => setData(makeData(1_000)) const stressTest = () => setData(makeData(500_000)) - const table = useAppTable({ - debugTable: true, - columns, - data, - }) + const table = useAppTable( + { + debugTable: true, + columns, + data, + }, + (state) => state, // default selector + ) const randomizeColumns = () => { table.setColumnOrder( @@ -93,144 +96,132 @@ function App() { } return ( - ({ - columnVisibility: state.columnVisibility, - columnOrder: state.columnOrder, - columnPinning: state.columnPinning, - })} - > - {(_state) => ( -
-
-
+
+
+
+ +
+ {table.getAllLeafColumns().map((column) => { + return ( +
- {table.getAllLeafColumns().map((column) => { - return ( -
- -
- ) - })} -
-
-
- - - -
-
-

- This example using the non-split APIs. Columns are just reordered - within 1 table instead of being split into 3 different tables. -

-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - + ) + })} + +
+
+ + + +
+
+

+ This example using the non-split APIs. Columns are just reordered within + 1 table instead of being split into 3 different tables. +

+
+
-
- {header.isPlaceholder ? null : ( - - )} -
- {!header.isPlaceholder && header.column.getCanPin() && ( -
- {header.column.getIsPinned() !== 'left' ? ( - - ) : null} - {header.column.getIsPinned() ? ( - - ) : null} - {header.column.getIsPinned() !== 'right' ? ( - - ) : null} -
- )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ))} - - - {table - .getRowModel() - .rows.slice(0, 20) - .map((row) => { - return ( - - {row.getVisibleCells().map((cell) => { - return ( - - ) - })} - - ) - })} - -
+
+ {header.isPlaceholder ? null : ( + + )} +
+ {!header.isPlaceholder && header.column.getCanPin() && ( +
+ {header.column.getIsPinned() !== 'left' ? ( + + ) : null} + {header.column.getIsPinned() ? ( + + ) : null} + {header.column.getIsPinned() !== 'right' ? ( + + ) : null} +
+ )} +
- -
-
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + + {table + .getRowModel() + .rows.slice(0, 20) + .map((row) => { + return ( + + {row.getVisibleCells().map((cell) => { + return ( + + + + ) + })} + + ) + })} + + +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/react/column-resizing-performant/src/main.tsx b/examples/react/column-resizing-performant/src/main.tsx index 0bc0822ef7..8d3335ac1a 100644 --- a/examples/react/column-resizing-performant/src/main.tsx +++ b/examples/react/column-resizing-performant/src/main.tsx @@ -140,13 +140,9 @@ function App() { - state}> - {(state) => ( -
-            {JSON.stringify(state, null, 2)}
-          
- )} -
+
+        {JSON.stringify(table.state, null, 2)}
+      
({data.length.toLocaleString()} rows)
{/* Here in the equivalent element (surrounds all table head and data cells), we will define our CSS variables for column sizes */} diff --git a/examples/react/column-resizing/src/main.tsx b/examples/react/column-resizing/src/main.tsx index 3e1e7e346e..dfa2822178 100644 --- a/examples/react/column-resizing/src/main.tsx +++ b/examples/react/column-resizing/src/main.tsx @@ -90,7 +90,7 @@ function App() { debugHeaders: true, debugColumns: true, }, - (state) => state, + (state) => state, // default selector ) return ( @@ -339,9 +339,7 @@ function App() { - state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
+
{JSON.stringify(table.state, null, 2)}
) } diff --git a/examples/react/column-sizing/src/main.tsx b/examples/react/column-sizing/src/main.tsx index 9b5ed3967e..d41d3ce5f8 100644 --- a/examples/react/column-sizing/src/main.tsx +++ b/examples/react/column-sizing/src/main.tsx @@ -71,7 +71,7 @@ function App() { debugHeaders: true, debugColumns: true, }, - (state) => state, + (state) => state, // default selector ) return ( @@ -272,9 +272,7 @@ function App() { - state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
+
{JSON.stringify(table.state, null, 2)}
) } diff --git a/examples/react/column-visibility/src/main.tsx b/examples/react/column-visibility/src/main.tsx index a65c570072..fee9f4733a 100644 --- a/examples/react/column-visibility/src/main.tsx +++ b/examples/react/column-visibility/src/main.tsx @@ -66,110 +66,103 @@ function App() { const stressTest = () => setData(makeData(1_000)) const rerender = React.useReducer(() => ({}), {})[1] - const table = useTable({ - _features, - _rowModels: {}, - columns, - data, - debugTable: true, - debugHeaders: true, - debugColumns: true, - }) + const table = useTable( + { + _features, + _rowModels: {}, + columns, + data, + debugTable: true, + debugHeaders: true, + debugColumns: true, + }, + (state) => state, // default selector + ) return ( - ({ - columnVisibility: state.columnVisibility, - })} - > - {(_state) => ( -
-
- - -
-
-
-
+
+
+ + +
+
+
+
+ +
+ {table.getAllLeafColumns().map((column) => { + return ( +
- {table.getAllLeafColumns().map((column) => { - return ( -
- -
- ) - })} -
-
-
- - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - + ) + })} + +
+
- {header.isPlaceholder ? null : ( - - )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - ))} - + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + ))} - - - {table.getFooterGroups().map((footerGroup) => ( - - {footerGroup.headers.map((header) => ( - - ))} - + + ))} + + + {table.getFooterGroups().map((footerGroup) => ( + + {footerGroup.headers.map((header) => ( + ))} - -
+ {header.isPlaceholder ? null : ( + + )} +
- -
+ +
- {header.isPlaceholder ? null : ( - - )} -
+ {header.isPlaceholder ? null : ( + + )} +
-
- -
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + +
+ +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/react/composable-tables/src/components/table-components.tsx b/examples/react/composable-tables/src/components/table-components.tsx index 169d21e67b..43164c38ef 100644 --- a/examples/react/composable-tables/src/components/table-components.tsx +++ b/examples/react/composable-tables/src/components/table-components.tsx @@ -5,7 +5,6 @@ * directly on the table object, e.g., */ import { useTableContext } from '../hooks/table' -import type { PaginationState } from '@tanstack/react-table' /** * Pagination controls for the table @@ -14,69 +13,64 @@ export function PaginationControls() { const table = useTableContext() return ( - - {/* whole pagination slice — no selector needed */} - {(pagination: PaginationState) => ( -
- - - - - - Page{' '} - - {(pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - - - - | Go to page: - { - const page = e.target.value ? Number(e.target.value) - 1 : 0 - table.setPageIndex(page) - }} - /> - - -
- )} -
+
+ + + + + + Page{' '} + + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + + + + | Go to page: + { + const page = e.target.value ? Number(e.target.value) - 1 : 0 + table.setPageIndex(page) + }} + /> + + +
) } @@ -100,21 +94,26 @@ export function RowCount() { export function TableToolbar({ title, onRefresh, + onStressTest, }: { title: string onRefresh?: () => void + onStressTest?: () => void }) { const table = useTableContext() return (

{title}

-
+
+ {onRefresh && } + {onStressTest && ( + + )} - {onRefresh && }
) diff --git a/examples/react/composable-tables/src/index.css b/examples/react/composable-tables/src/index.css index 75b7db2d40..4d8fef0142 100644 --- a/examples/react/composable-tables/src/index.css +++ b/examples/react/composable-tables/src/index.css @@ -174,6 +174,12 @@ tfoot th { border-spacing: 0; } +.table-toolbar-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + .table-toolbar button { border: 1px solid #ccc; border-radius: 4px; diff --git a/examples/react/composable-tables/src/main.tsx b/examples/react/composable-tables/src/main.tsx index 96d6a12817..63239f86f5 100644 --- a/examples/react/composable-tables/src/main.tsx +++ b/examples/react/composable-tables/src/main.tsx @@ -77,7 +77,7 @@ function UsersTable() { debugTable: true, // more table options }, - // (state) => state, // alternatively, subscribe to the entire state instead of using table.Subscribe or selectors down below + (state) => state, // default selector ) return ( @@ -93,13 +93,11 @@ function UsersTable() { {({ sorting, columnFilters }) => (
{/* Table toolbar using pre-bound component */} - -
- - -
+ {/* Table element */} @@ -267,12 +265,15 @@ function ProductsTable() { ) // Create the table using the same useAppTable hook - const table = useAppTable({ - debugTable: true, - columns, - data, - getRowId: (row) => row.id, - }) + const table = useAppTable( + { + debugTable: true, + columns, + data, + getRowId: (row) => row.id, + }, + (state) => state, // default selector + ) return ( (
{/* Table toolbar using the same pre-bound component */} - -
- - -
+ {/* Table element */}
diff --git a/examples/react/custom-plugin/src/main.tsx b/examples/react/custom-plugin/src/main.tsx index 210a4c0033..3d9c6fa2f6 100644 --- a/examples/react/custom-plugin/src/main.tsx +++ b/examples/react/custom-plugin/src/main.tsx @@ -152,21 +152,24 @@ function App() { const stressTest = () => setData(makeData(200_000)) const [density, setDensity] = React.useState('md') - const table = useTable({ - _features, - _rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), + const table = useTable( + { + _features, + _rowModels: { + filteredRowModel: createFilteredRowModel(filterFns), + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(sortFns), + }, + columns, + data, + debugTable: true, + state: { + density, // passing the density state to the table, TS is still happy :) + }, + onDensityChange: setDensity, // using the new onDensityChange option, TS is still happy :) }, - columns, - data, - debugTable: true, - state: { - density, // passing the density state to the table, TS is still happy :) - }, - onDensityChange: setDensity, // using the new onDensityChange option, TS is still happy :) - }) + (state) => state, // default selector + ) return (
@@ -322,9 +325,7 @@ function App() { Showing {table.getRowModel().rows.length.toLocaleString()} of{' '} {table.getRowCount().toLocaleString()} Rows
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
+
{JSON.stringify(table.state, null, 2)}
) } diff --git a/examples/react/expanding/src/main.tsx b/examples/react/expanding/src/main.tsx index 3a76258b32..6de98cc826 100644 --- a/examples/react/expanding/src/main.tsx +++ b/examples/react/expanding/src/main.tsx @@ -115,152 +115,141 @@ function App() { const refreshData = () => setData(makeData(100, 5, 3)) const stressTest = () => setData(makeData(10_000, 5, 3)) - const table = useTable({ - _features, - _rowModels: { - expandedRowModel: createExpandedRowModel(), - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), + const table = useTable( + { + _features, + _rowModels: { + expandedRowModel: createExpandedRowModel(), + filteredRowModel: createFilteredRowModel(filterFns), + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(sortFns), + }, + columns, + data, + getSubRows: (row) => row.subRows, + // filterFromLeafRows: true, + // maxLeafRowFilterDepth: 0, + debugTable: true, }, - columns, - data, - getSubRows: (row) => row.subRows, - // filterFromLeafRows: true, - // maxLeafRowFilterDepth: 0, - debugTable: true, - }) + (state) => state, // default selector + ) return ( - ({ - expanded: state.expanded, - pagination: state.pagination, - rowSelection: state.rowSelection, - columnFilters: state.columnFilters, - sorting: state.sorting, - })} - > - {(state) => ( -
-
- - -
-
-
- - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + ) + })} + + ) + })} + +
- {header.isPlaceholder ? null : ( +
+
+ + +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { - return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) : null} + + )} + ) })} - -
+ {header.isPlaceholder ? null : ( +
+ + {header.column.getCanFilter() ? (
- - {header.column.getCanFilter() ? ( -
- -
- ) : null} +
- )} -
- -
-
-
- - - - - -
Page
- - {(state.pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = e.target.value ? Number(e.target.value) - 1 : 0 - table.setPageIndex(page) - }} - className="page-size-input" - /> - - -
-
{table.getRowModel().rows.length.toLocaleString()} Rows
-
- -
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - +
+ +
+
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = e.target.value ? Number(e.target.value) - 1 : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
{table.getRowModel().rows.length.toLocaleString()} Rows
+
+ +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/react/filters-faceted/src/main.tsx b/examples/react/filters-faceted/src/main.tsx index 083d7029ca..d6e32ae333 100644 --- a/examples/react/filters-faceted/src/main.tsx +++ b/examples/react/filters-faceted/src/main.tsx @@ -88,152 +88,142 @@ function App() { const stressTest = () => setData(makeData(200_000)) const rerender = React.useReducer(() => ({}), {})[1] - const table = useTable({ - _features, - _rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), // client-side filtering - paginatedRowModel: createPaginatedRowModel(), - facetedRowModel: createFacetedRowModel(), // client-side faceting - facetedMinMaxValues: createFacetedMinMaxValues(), // generate min/max values for range filter - facetedUniqueValues: createFacetedUniqueValues(), // generate unique values for select filter/autocomplete + const table = useTable( + { + _features, + _rowModels: { + filteredRowModel: createFilteredRowModel(filterFns), // client-side filtering + paginatedRowModel: createPaginatedRowModel(), + facetedRowModel: createFacetedRowModel(), // client-side faceting + facetedMinMaxValues: createFacetedMinMaxValues(), // generate min/max values for range filter + facetedUniqueValues: createFacetedUniqueValues(), // generate unique values for select filter/autocomplete + }, + columns, + data, + debugTable: true, + debugHeaders: true, + debugColumns: false, }, - columns, - data, - debugTable: true, - debugHeaders: true, - debugColumns: false, - }) + (state) => state, // default selector + ) return ( - ({ - columnFilters: state.columnFilters, - pagination: state.pagination, - })} - > - {(state) => ( -
-
- - -
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { +
+
+ + +
+
- {header.isPlaceholder ? null : ( - <> -
- -
- {header.column.getCanFilter() ? ( -
- -
- ) : null} - - )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) })} - -
- -
+ {header.isPlaceholder ? null : ( + <> +
+ +
+ {header.column.getCanFilter() ? ( +
+ +
+ ) : null} + + )} +
-
-
- - - - - -
Page
- - {(state.pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = e.target.value ? Number(e.target.value) - 1 : 0 - table.setPageIndex(page) - }} - className="page-size-input" - /> - - -
-
- {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows -
-
- -
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + + + ) + })} + + ) + })} + + +
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = e.target.value ? Number(e.target.value) - 1 : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
+ {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows +
+
+ +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/react/filters-fuzzy/src/main.tsx b/examples/react/filters-fuzzy/src/main.tsx index 42ebd3ae4e..2ab2d82c4d 100644 --- a/examples/react/filters-fuzzy/src/main.tsx +++ b/examples/react/filters-fuzzy/src/main.tsx @@ -113,23 +113,26 @@ function App() { const refreshData = () => setData(makeData(5_000)) const stressTest = () => setData(makeData(200_000)) - const table = useTable({ - _features, - _rowModels: { - filteredRowModel: createFilteredRowModel({ - ...filterFns, - fuzzy: fuzzyFilter, - }), - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), + const table = useTable( + { + _features, + _rowModels: { + filteredRowModel: createFilteredRowModel({ + ...filterFns, + fuzzy: fuzzyFilter, + }), + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(sortFns), + }, + columns, + data, + globalFilterFn: 'fuzzy', // apply fuzzy filter to the global filter (most common use case for fuzzy filter) + debugTable: true, + debugHeaders: true, + debugColumns: false, }, - columns, - data, - globalFilterFn: 'fuzzy', // apply fuzzy filter to the global filter (most common use case for fuzzy filter) - debugTable: true, - debugHeaders: true, - debugColumns: false, - }) + (state) => state, // default selector + ) // apply the fuzzy sort if the fullName column is being filtered React.useEffect(() => { @@ -141,157 +144,140 @@ function App() { }, [table.store.state.columnFilters[0]?.id]) return ( - ({ - columnFilters: state.columnFilters, - globalFilter: state.globalFilter, - pagination: state.pagination, - sorting: state.sorting, - })} - > - {(state) => ( -
-
- - -
-
- table.setGlobalFilter(String(value))} - className="summary-panel" - placeholder="Search all columns..." - /> -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { +
+
+ + +
+
+ table.setGlobalFilter(String(value))} + className="summary-panel" + placeholder="Search all columns..." + /> +
+
+
- {header.isPlaceholder ? null : ( - <> -
- - {{ - asc: ' 🔼', - desc: ' 🔽', - }[header.column.getIsSorted() as string] ?? null} -
- {header.column.getCanFilter() ? ( -
- -
- ) : null} - - )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) })} - -
- -
+ {header.isPlaceholder ? null : ( + <> +
+ + {{ + asc: ' 🔼', + desc: ' 🔽', + }[header.column.getIsSorted() as string] ?? null} +
+ {header.column.getCanFilter() ? ( +
+ +
+ ) : null} + + )} +
-
-
- - - - - -
Page
- - {(state.pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = e.target.value ? Number(e.target.value) - 1 : 0 - table.setPageIndex(page) - }} - className="page-size-input" - /> - - -
-
- {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows -
-
- -
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + + + ) + })} + + ) + })} + + +
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = e.target.value ? Number(e.target.value) - 1 : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
+ {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows +
+
+ +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/react/filters/src/main.tsx b/examples/react/filters/src/main.tsx index bf6ab8458c..0568fbdb7f 100644 --- a/examples/react/filters/src/main.tsx +++ b/examples/react/filters/src/main.tsx @@ -89,150 +89,140 @@ function App() { const refreshData = () => setData(makeData(5_000)) const stressTest = () => setData(makeData(200_000)) - const table = useTable({ - _features, - _rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), // client side filtering - paginatedRowModel: createPaginatedRowModel(), + const table = useTable( + { + _features, + _rowModels: { + filteredRowModel: createFilteredRowModel(filterFns), // client side filtering + paginatedRowModel: createPaginatedRowModel(), + }, + columns, + data, + debugTable: true, + debugColumns: true, }, - columns, - data, - debugTable: true, - debugColumns: true, - }) + (state) => state, // default selector + ) return ( - ({ - columnFilters: state.columnFilters, - pagination: state.pagination, - })} - > - {(state) => ( -
-
- - -
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { +
+
+ + +
+
- {header.isPlaceholder ? null : ( - <> -
- -
- {header.column.getCanFilter() ? ( -
- -
- ) : null} - - )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) })} - -
- -
+ {header.isPlaceholder ? null : ( + <> +
+ +
+ {header.column.getCanFilter() ? ( +
+ +
+ ) : null} + + )} +
-
-
- - - - - -
Page
- - {(state.pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = e.target.value ? Number(e.target.value) - 1 : 0 - table.setPageIndex(page) - }} - className="page-size-input" - /> - - -
-
- {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows -
-
- -
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + + + ) + })} + + ) + })} + + +
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = e.target.value ? Number(e.target.value) - 1 : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
+ {table.getPrePaginatedRowModel().rows.length.toLocaleString()} Rows +
+
+ +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/react/grouping/src/main.tsx b/examples/react/grouping/src/main.tsx index 556f3e8516..a2d8a87275 100644 --- a/examples/react/grouping/src/main.tsx +++ b/examples/react/grouping/src/main.tsx @@ -96,7 +96,7 @@ function App() { data, debugTable: true, }, - (state) => state, // subscribe to all state changes + (state) => state, // default selector ) return ( @@ -251,9 +251,7 @@ function App() {
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
+
{JSON.stringify(table.state, null, 2)}
) } diff --git a/examples/react/kitchen-sink-hero-ui/src/main.tsx b/examples/react/kitchen-sink-hero-ui/src/main.tsx index 052a929b45..4ba0d93e87 100644 --- a/examples/react/kitchen-sink-hero-ui/src/main.tsx +++ b/examples/react/kitchen-sink-hero-ui/src/main.tsx @@ -1360,7 +1360,7 @@ function App() { columnResizeMode: 'onChange', debugTable: true, }, - (state) => state, + (state) => state, // default selector ) const columnSizeVars = React.useMemo(() => { diff --git a/examples/react/kitchen-sink-mantine/src/main.tsx b/examples/react/kitchen-sink-mantine/src/main.tsx index 74d1b55cc7..41f8113c39 100644 --- a/examples/react/kitchen-sink-mantine/src/main.tsx +++ b/examples/react/kitchen-sink-mantine/src/main.tsx @@ -1416,7 +1416,7 @@ function App() { columnResizeMode: 'onChange', debugTable: true, }, - (state) => state, + (state) => state, // default selector ) const columnSizeVars = React.useMemo(() => { diff --git a/examples/react/kitchen-sink-material-ui/src/main.tsx b/examples/react/kitchen-sink-material-ui/src/main.tsx index 2771b8c164..27a9388998 100644 --- a/examples/react/kitchen-sink-material-ui/src/main.tsx +++ b/examples/react/kitchen-sink-material-ui/src/main.tsx @@ -1436,7 +1436,7 @@ function App({ columnResizeMode: 'onChange', debugTable: true, }, - (state) => state, + (state) => state, // default selector ) const columnSizeVars = React.useMemo(() => { diff --git a/examples/react/kitchen-sink-react-aria/src/main.tsx b/examples/react/kitchen-sink-react-aria/src/main.tsx index 2b0204c232..2b44390216 100644 --- a/examples/react/kitchen-sink-react-aria/src/main.tsx +++ b/examples/react/kitchen-sink-react-aria/src/main.tsx @@ -1368,7 +1368,7 @@ function App() { columnResizeMode: 'onChange', debugTable: true, }, - (state) => state, + (state) => state, // default selector ) const columnSizeVars = React.useMemo(() => { diff --git a/examples/react/kitchen-sink-shadcn-base/src/main.tsx b/examples/react/kitchen-sink-shadcn-base/src/main.tsx index b2b80dcd0a..9779d72cfa 100644 --- a/examples/react/kitchen-sink-shadcn-base/src/main.tsx +++ b/examples/react/kitchen-sink-shadcn-base/src/main.tsx @@ -447,7 +447,7 @@ function App() { columnResizeMode: 'onChange', debugTable: true, }, - (state) => state, // subscribe to all re-renders + (state) => state, // default selector ) const columnSizeVars = React.useMemo(() => { diff --git a/examples/react/kitchen-sink-shadcn-radix/src/main.tsx b/examples/react/kitchen-sink-shadcn-radix/src/main.tsx index 6ac671247f..68d8c93bbd 100644 --- a/examples/react/kitchen-sink-shadcn-radix/src/main.tsx +++ b/examples/react/kitchen-sink-shadcn-radix/src/main.tsx @@ -446,7 +446,7 @@ function App() { columnResizeMode: 'onChange', debugTable: true, }, - (state) => state, // subscribe to all re-renders + (state) => state, // default selector ) const columnSizeVars = React.useMemo(() => { diff --git a/examples/react/lib-hero-ui/src/main.tsx b/examples/react/lib-hero-ui/src/main.tsx index 21c370ea8b..d1194e61f8 100644 --- a/examples/react/lib-hero-ui/src/main.tsx +++ b/examples/react/lib-hero-ui/src/main.tsx @@ -127,7 +127,7 @@ function App() { data, globalFilterFn: 'includesString', }, - (state) => state, + (state) => state, // default selector ) const pageIndex = table.state.pagination.pageIndex diff --git a/examples/react/lib-mantine/src/main.tsx b/examples/react/lib-mantine/src/main.tsx index 70925e1eaa..21de825897 100644 --- a/examples/react/lib-mantine/src/main.tsx +++ b/examples/react/lib-mantine/src/main.tsx @@ -105,7 +105,7 @@ function App() { data, globalFilterFn: 'includesString', }, - (state) => state, + (state) => state, // default selector ) return ( diff --git a/examples/react/lib-material-ui/src/main.tsx b/examples/react/lib-material-ui/src/main.tsx index 7f5482124f..91ff114d99 100644 --- a/examples/react/lib-material-ui/src/main.tsx +++ b/examples/react/lib-material-ui/src/main.tsx @@ -108,7 +108,7 @@ function App() { data, globalFilterFn: 'includesString', }, - (state) => state, + (state) => state, // default selector ) return ( diff --git a/examples/react/lib-react-aria/src/main.tsx b/examples/react/lib-react-aria/src/main.tsx index 27376364f8..62263774b9 100644 --- a/examples/react/lib-react-aria/src/main.tsx +++ b/examples/react/lib-react-aria/src/main.tsx @@ -137,7 +137,7 @@ function App() { data, globalFilterFn: 'includesString', }, - (state) => state, + (state) => state, // default selector ) const pageIndex = table.state.pagination.pageIndex diff --git a/examples/react/lib-shadcn-base/src/main.tsx b/examples/react/lib-shadcn-base/src/main.tsx index da82273e0e..3d0553547b 100644 --- a/examples/react/lib-shadcn-base/src/main.tsx +++ b/examples/react/lib-shadcn-base/src/main.tsx @@ -108,7 +108,7 @@ function App() { data, globalFilterFn: 'includesString', }, - (state) => state, + (state) => state, // default selector ) // 7. Render your table markup from the table instance APIs. return ( diff --git a/examples/react/lib-shadcn-radix/src/main.tsx b/examples/react/lib-shadcn-radix/src/main.tsx index da82273e0e..3d0553547b 100644 --- a/examples/react/lib-shadcn-radix/src/main.tsx +++ b/examples/react/lib-shadcn-radix/src/main.tsx @@ -108,7 +108,7 @@ function App() { data, globalFilterFn: 'includesString', }, - (state) => state, + (state) => state, // default selector ) // 7. Render your table markup from the table instance APIs. return ( diff --git a/examples/react/mantine-react-table/src/mantine-react-table/hooks/useMRT_TableInstance.ts b/examples/react/mantine-react-table/src/mantine-react-table/hooks/useMRT_TableInstance.ts index 93cbabf864..b63cba5075 100644 --- a/examples/react/mantine-react-table/src/mantine-react-table/hooks/useMRT_TableInstance.ts +++ b/examples/react/mantine-react-table/src/mantine-react-table/hooks/useMRT_TableInstance.ts @@ -314,7 +314,7 @@ export const useMRT_TableInstance = ( pagination: paginationAtom, }, }, - (state) => state, + (state) => state, // default selector ) as unknown as MRT_TableInstance // v9 spells the resize setter `setcolumnResizing` (lowercase 'c') because diff --git a/examples/react/material-react-table/src/material-react-table/hooks/useMRT_TableInstance.ts b/examples/react/material-react-table/src/material-react-table/hooks/useMRT_TableInstance.ts index f037cef710..1ac96e51ce 100644 --- a/examples/react/material-react-table/src/material-react-table/hooks/useMRT_TableInstance.ts +++ b/examples/react/material-react-table/src/material-react-table/hooks/useMRT_TableInstance.ts @@ -318,7 +318,7 @@ export const useMRT_TableInstance = ( pagination: paginationAtom, }, }, - (state) => state, + (state) => state, // default selector ) as unknown as MRT_TableInstance // v9 spells the resize setter `setcolumnResizing` (lowercase 'c') because diff --git a/examples/react/pagination/src/main.tsx b/examples/react/pagination/src/main.tsx index 1334e7e3c8..09e7be7aeb 100644 --- a/examples/react/pagination/src/main.tsx +++ b/examples/react/pagination/src/main.tsx @@ -79,132 +79,125 @@ function MyTable({ data: Array columns: ReturnType }) { - const table = useTable({ - _features, - _rowModels: { - paginatedRowModel: createPaginatedRowModel(), + const table = useTable( + { + _features, + _rowModels: { + paginatedRowModel: createPaginatedRowModel(), + }, + columns, + data, + debugTable: true, + // no need to pass pageCount or rowCount with client-side pagination as it is calculated automatically }, - columns, - data, - debugTable: true, - // no need to pass pageCount or rowCount with client-side pagination as it is calculated automatically - }) + (state) => state, // default selector + ) return ( - ({ - pagination: state.pagination, - })} - > - {(state) => ( -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { +
+
+
-
- -
-
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) })} - -
- -
+
+ +
+
-
-
- - - - - -
Page
- - {(state.pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = e.target.value ? Number(e.target.value) - 1 : 0 - table.setPageIndex(page) - }} - className="page-size-input" - /> - - -
-
- Showing {table.getRowModel().rows.length.toLocaleString()} of{' '} - {table.getRowCount().toLocaleString()} Rows -
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + + + ) + })} + + ) + })} + + +
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = e.target.value ? Number(e.target.value) - 1 : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
+ Showing {table.getRowModel().rows.length.toLocaleString()} of{' '} + {table.getRowCount().toLocaleString()} Rows +
+
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/react/row-dnd/src/main.tsx b/examples/react/row-dnd/src/main.tsx index 67a7fd36a0..2f47357523 100644 --- a/examples/react/row-dnd/src/main.tsx +++ b/examples/react/row-dnd/src/main.tsx @@ -134,7 +134,7 @@ function App() { data, getRowId: (row) => row.userId, // required because row indexes will change }, - (state) => state, + (state) => state, // default selector ) // reorder rows after drag & drop @@ -205,9 +205,7 @@ function App() { - state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
+
{JSON.stringify(table.state, null, 2)}
) diff --git a/examples/react/row-pinning/src/main.tsx b/examples/react/row-pinning/src/main.tsx index 02355633e0..27423d295b 100644 --- a/examples/react/row-pinning/src/main.tsx +++ b/examples/react/row-pinning/src/main.tsx @@ -170,7 +170,7 @@ function App() { keepPinnedRows, debugAll: true, }, - (state) => state, // subscribe to all re-renders + (state) => state, // default selector ) // console.log(table.getBottomRows) diff --git a/examples/react/row-selection/src/main.tsx b/examples/react/row-selection/src/main.tsx index b8fb8336f8..5145777069 100644 --- a/examples/react/row-selection/src/main.tsx +++ b/examples/react/row-selection/src/main.tsx @@ -43,18 +43,11 @@ function App() { id: 'select', header: () => { return ( - - {() => ( - - )} - + ) }, cell: ({ row }) => ( @@ -103,6 +96,7 @@ function App() { const refreshData = () => setData(makeData(1_000)) const stressTest = () => setData(makeData(200_000)) + // optionally, raise the selection state to your own atom const rowSelectionAtom = useCreateAtom({}) const table = useTable( @@ -122,228 +116,191 @@ function App() { // enableRowSelection: row => row.original.age > 18, // or enable row selection conditionally per row debugTable: true, }, - // (state) => state, // uncomment to subscribe to the entire table state (this is how v8 used to work by default) + (state) => state, // default selector ) useTanStackTableDevtools(table, 'Row Selection Example') return ( <> - ({ - // Store mode: multiple slices — must use table.store + explicit selector - columnFilters: state.columnFilters, - globalFilter: state.globalFilter, - pagination: state.pagination, - })} - > - {(state) => ( -
-
- - -
-
- table.setGlobalFilter(value)} - className="summary-panel" - placeholder="Search all columns..." - /> -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { +
+
+ + +
+
+ table.setGlobalFilter(value)} + className="summary-panel" + placeholder="Search all columns..." + /> +
+
+
- {header.isPlaceholder ? null : ( - <> - - {header.column.getCanFilter() ? ( -
- -
- ) : null} - - )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { return ( - rowSelection?.[row.id]} // optional: narrow to this row so the row re-renders only when this id toggles - > - {(_isRowSelected) => ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) })} - - - - - + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + ) + })} - -
- -
+ {header.isPlaceholder ? null : ( + <> + + {header.column.getCanFilter() ? ( +
+ +
+ ) : null} + )} - +
- a} - > - {() => ( - - )} - - - Page Rows ( - {table.getRowModel().rows.length.toLocaleString()}) -
+ +
-
-
- - - - - -
Page
- - {( - table.store.state.pagination.pageIndex + 1 - ).toLocaleString()}{' '} - of {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = e.target.value ? Number(e.target.value) - 1 : 0 - table.setPageIndex(page) - }} - className="page-size-input" + ) + })} + + + + + - - -
-
-
- ({ - numSelected: Object.keys(state.rowSelection).length, - })} - > - {({ numSelected }) => <>{numSelected.toLocaleString()} of } - - {table.getPreFilteredRowModel().rows.length.toLocaleString()}{' '} - Total Rows Selected -
-
-
-
- -
-
- -
-
- - state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
-
- )} - + + + Page Rows ({table.getRowModel().rows.length.toLocaleString()}) + + + + +
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = e.target.value ? Number(e.target.value) - 1 : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
+
+ <> + {Object.keys(table.state.rowSelection).length.toLocaleString()}{' '} + of{' '} + + {table.getPreFilteredRowModel().rows.length.toLocaleString()} Total + Rows Selected +
+
+
+
+ +
+
+ +
+
+ +
{JSON.stringify(table.state, null, 2)}
+
+
) } diff --git a/examples/react/sorting/src/main.tsx b/examples/react/sorting/src/main.tsx index 116416e808..6571f7f28b 100644 --- a/examples/react/sorting/src/main.tsx +++ b/examples/react/sorting/src/main.tsx @@ -93,90 +93,78 @@ function App() { // isMultiSortEvent: (e) => true, //Make all clicks multi-sort - default requires `shift` key // maxMultiSortColCount: 3, // only allow 3 columns to be sorted at once - default is Infinity }, - // (state) => state, // uncomment to subscribe to the entire table state (this is how v8 used to work by default) + (state) => state, // default selector ) return ( - - {/* omit selector to subscribe to the entire sorting slice */} - {() => ( -
-
- - -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { +
+
+ + +
+
+
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + ) + })} + + ))} + + + {table + .getRowModel() + .rows.slice(0, 10) + .map((row) => { + return ( + + {row.getAllCells().map((cell) => { return ( - + ) })} - ))} - - - {table - .getRowModel() - .rows.slice(0, 10) - .map((row) => { - return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - - ) - })} - -
+ {header.isPlaceholder ? null : ( +
+ + {{ + asc: ' 🔼', + desc: ' 🔽', + }[header.column.getIsSorted() as string] ?? null} +
+ )} +
- {header.isPlaceholder ? null : ( -
- - {{ - asc: ' 🔼', - desc: ' 🔽', - }[header.column.getIsSorted() as string] ?? null} -
- )} -
+ +
- -
-
{table.getRowModel().rows.length.toLocaleString()} Rows
-
- -
- {/* Store mode: dump full table state for debugging — selector required */} - state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
-
- )} - + ) + })} + + +
{table.getRowModel().rows.length.toLocaleString()} Rows
+
+ +
+ {/* Store mode: dump full table state for debugging */} +
{JSON.stringify(table.state, null, 2)}
+
) } diff --git a/examples/react/sub-components/src/main.tsx b/examples/react/sub-components/src/main.tsx index 0b8dc3c712..07021404f0 100644 --- a/examples/react/sub-components/src/main.tsx +++ b/examples/react/sub-components/src/main.tsx @@ -92,72 +92,71 @@ function Table({ getRowCanExpand, renderSubComponent, }: TableProps): React.JSX.Element { - const table = useTable({ - debugTable: true, - _features, - _rowModels: { - expandedRowModel: createExpandedRowModel(), + const table = useTable( + { + debugTable: true, + _features, + _rowModels: { + expandedRowModel: createExpandedRowModel(), + }, + columns, + data, + getRowCanExpand, }, - columns, - data, - getRowCanExpand, - }) + (state) => state, // default selector + ) return ( - state}> - {() => ( -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { +
+
+
- {header.isPlaceholder ? null : ( -
- -
- )} -
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { return ( - - - {/* first row is a normal row */} - {row.getAllCells().map((cell) => { - return ( - - ) - })} - - {row.getIsExpanded() && ( - - {/* 2nd row is a custom 1 cell row */} - - + ) })} - -
- -
- {renderSubComponent({ row })} -
+ {header.isPlaceholder ? null : ( +
+ +
)} - +
-
-
{table.getRowModel().rows.length.toLocaleString()} Rows
-
- )} - + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + + {/* first row is a normal row */} + {row.getAllCells().map((cell) => { + return ( + + + + ) + })} + + {row.getIsExpanded() && ( + + {/* 2nd row is a custom 1 cell row */} + + {renderSubComponent({ row })} + + + )} + + ) + })} + + +
+
{table.getRowModel().rows.length.toLocaleString()} Rows
+
) } diff --git a/examples/react/virtualized-columns-experimental/src/main.tsx b/examples/react/virtualized-columns-experimental/src/main.tsx index 81d33af486..92d5a2e558 100644 --- a/examples/react/virtualized-columns-experimental/src/main.tsx +++ b/examples/react/virtualized-columns-experimental/src/main.tsx @@ -45,13 +45,16 @@ function App() { }, [columns]) // The table does not live in the same scope as the virtualizers - const table = useTable({ - _features, - _rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, - columns, - data, - debugTable: true, - }) + const table = useTable( + { + _features, + _rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, + columns, + data, + debugTable: true, + }, + (state) => state, // default selector + ) return (
@@ -124,19 +127,15 @@ function TableContainer({ table }: TableContainerProps) { height: '800px', // should be a fixed height }} > - - {() => ( - // Even though we're still using sematic table tags, we must use CSS grid and flexbox for dynamic row heights - - - -
- )} -
+ {/* Even though we're still using sematic table tags, we must use CSS grid and flexbox for dynamic row heights */} + + + +
) } @@ -151,6 +150,7 @@ function TableHead({ table, columnVirtualizer }: TableHeadProps) { + {/* fake empty column to the left for virtualization scroll padding */} {virtualColumnIndexes.map((virtualColumnIndex) => { @@ -208,7 +211,9 @@ function TableHeadCell({ diff --git a/examples/react/virtualized-columns/src/main.tsx b/examples/react/virtualized-columns/src/main.tsx index cd96fa3665..c903b8ca44 100644 --- a/examples/react/virtualized-columns/src/main.tsx +++ b/examples/react/virtualized-columns/src/main.tsx @@ -44,13 +44,16 @@ function App() { setData(makeData(10_000, columns)) }, [columns]) - const table = useTable({ - _features: features, - _rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, - columns, - data, - debugTable: true, - }) + const table = useTable( + { + _features: features, + _rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, + columns, + data, + debugTable: true, + }, + (state) => state, // default selector + ) // All important CSS styles are included as inline styles for this example. This is not recommended for your code. return ( diff --git a/examples/react/virtualized-infinite-scrolling/src/main.tsx b/examples/react/virtualized-infinite-scrolling/src/main.tsx index 0f65e8bf74..66e579cc27 100644 --- a/examples/react/virtualized-infinite-scrolling/src/main.tsx +++ b/examples/react/virtualized-infinite-scrolling/src/main.tsx @@ -123,17 +123,20 @@ function App() { fetchMoreOnBottomReached(tableContainerRef.current) }, [fetchMoreOnBottomReached]) - const table = useTable({ - _features, - _rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, - data: flatData, - columns, - state: { - sorting, + const table = useTable( + { + _features, + _rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, + data: flatData, + columns, + state: { + sorting, + }, + manualSorting: true, + debugTable: true, }, - manualSorting: true, - debugTable: true, - }) + (state) => state, // default selector + ) // scroll to top of table when sorting changes const handleSortingChange: OnChangeFn = (updater) => { diff --git a/examples/react/virtualized-rows-experimental/src/main.tsx b/examples/react/virtualized-rows-experimental/src/main.tsx index e7d642f1ce..015550c59b 100644 --- a/examples/react/virtualized-rows-experimental/src/main.tsx +++ b/examples/react/virtualized-rows-experimental/src/main.tsx @@ -73,13 +73,16 @@ function App() { const refreshData = () => setData(makeData(50_000)) const stressTest = () => setData(makeData(500_000)) - const table = useTable({ - _features, - _rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, - columns, - data, - debugTable: true, - }) + const table = useTable( + { + _features, + _rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, + columns, + data, + debugTable: true, + }, + (state) => state, // default selector + ) // The virtualizer needs to know the scrollable container element const tableContainerRef = React.useRef(null) @@ -108,69 +111,65 @@ function App() { height: '800px', // should be a fixed height }} > - - {() => ( - // Even though we're still using sematic table tags, we must use CSS grid and flexbox for dynamic row heights - - + + {table.getHeaderGroups().map((headerGroup) => ( + - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - -
-
- - {{ - asc: ' 🔼', - desc: ' 🔽', - }[header.column.getIsSorted() as string] ?? null} -
-
- )} -
+ {headerGroup.headers.map((header) => { + return ( + +
+ + {{ + asc: ' 🔼', + desc: ' 🔽', + }[header.column.getIsSorted() as string] ?? null} +
+ + ) + })} + + ))} + + +
) diff --git a/examples/react/virtualized-rows-experimental/vite.config.js b/examples/react/virtualized-rows-experimental/vite.config.js index 1755f6bea8..273808b8d3 100644 --- a/examples/react/virtualized-rows-experimental/vite.config.js +++ b/examples/react/virtualized-rows-experimental/vite.config.js @@ -15,9 +15,9 @@ export default defineConfig({ }), react(), // React Compiler - comment out the next line to disable - babel({ - presets: [reactCompilerPreset()], - include: [/\/src\/.*\.[jt]sx?$/], - }), + // babel({ + // presets: [reactCompilerPreset()], + // include: [/\/src\/.*\.[jt]sx?$/], + // }), ], }) diff --git a/examples/react/virtualized-rows/src/main.tsx b/examples/react/virtualized-rows/src/main.tsx index 990d9b3301..e573e6d38c 100644 --- a/examples/react/virtualized-rows/src/main.tsx +++ b/examples/react/virtualized-rows/src/main.tsx @@ -83,13 +83,16 @@ function App() { setData(makeData(1_000_000)) }, []) - const table = useTable({ - _features: features, - _rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, - columns, - data, - debugTable: true, - }) + const table = useTable( + { + _features: features, + _rowModels: { sortedRowModel: createSortedRowModel(sortFns) }, + columns, + data, + debugTable: true, + }, + (state) => state, // default selector + ) // All important CSS styles are included as inline styles for this example. This is not recommended for your code. return ( @@ -116,65 +119,58 @@ function App() { height: '800px', // should be a fixed height }} > - state}> - {() => ( - // Even though we're still using sematic table tags, we must use CSS grid and flexbox for dynamic row heights - - + + {table.getHeaderGroups().map((headerGroup) => ( + - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - -
-
- - {{ - asc: ' 🔼', - desc: ' 🔽', - }[header.column.getIsSorted() as string] ?? null} -
-
- )} -
+ {headerGroup.headers.map((header) => { + return ( + +
+ + {{ + asc: ' 🔼', + desc: ' 🔽', + }[header.column.getIsSorted() as string] ?? null} +
+ + ) + })} + + ))} + + +
- state}> - {(state) =>
{JSON.stringify(state, null, 2)}
} -
+
{JSON.stringify(table.state, null, 2)}
) } diff --git a/examples/react/with-tanstack-form/src/main.tsx b/examples/react/with-tanstack-form/src/main.tsx index d4c632d724..83cc5ef19b 100644 --- a/examples/react/with-tanstack-form/src/main.tsx +++ b/examples/react/with-tanstack-form/src/main.tsx @@ -166,16 +166,19 @@ function App() { // Create table using form state as data source // The table gets fresh data on each render, cells handle their own field state - const table = useTable({ - _features, - _rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), + const table = useTable( + { + _features, + _rowModels: { + filteredRowModel: createFilteredRowModel(filterFns), + paginatedRowModel: createPaginatedRowModel(), + }, + columns, + data: form.state.values.data, + debugTable: true, }, - columns, - data: form.state.values.data, - debugTable: true, - }) + (state) => state, // default selector + ) const refreshData = () => { const data: Array = makeData(100) @@ -239,136 +242,122 @@ function App() {
{/* Table */} - ({ - pagination: state.pagination, - columnFilters: state.columnFilters, - })} - > - {(tableState) => ( - <> -
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + ) + })} + + ) + })} + +
- {header.isPlaceholder ? null : ( + <> +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { - return ( - - {row.getAllCells().map((cell) => { - return ( - - ) - })} - + ) : null} + + )} + ) })} - -
+ {header.isPlaceholder ? null : ( +
+ + {header.column.getCanFilter() ? (
- - {header.column.getCanFilter() ? ( -
- -
- ) : null} +
- )} -
- -
+
+ +
- {/* Pagination controls */} -
-
- - - - - -
Page
- - {(tableState.pagination.pageIndex + 1).toLocaleString()} of{' '} - {table.getPageCount().toLocaleString()} - -
- - | Go to page: - { - const page = e.target.value - ? Number(e.target.value) - 1 - : 0 - table.setPageIndex(page) - }} - className="page-size-input" - /> - - -
-
- Showing {table.getRowModel().rows.length.toLocaleString()} of{' '} - {table.getRowCount().toLocaleString()} Rows -
- - )} - + {/* Pagination controls */} +
+
+ + + + + +
Page
+ + {(table.state.pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = e.target.value ? Number(e.target.value) - 1 : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+
+ Showing {table.getRowModel().rows.length.toLocaleString()} of{' '} + {table.getRowCount().toLocaleString()} Rows +
+
) diff --git a/examples/react/with-tanstack-query/src/main.tsx b/examples/react/with-tanstack-query/src/main.tsx index ef65f3eb96..92f053b518 100644 --- a/examples/react/with-tanstack-query/src/main.tsx +++ b/examples/react/with-tanstack-query/src/main.tsx @@ -69,18 +69,21 @@ function App() { const defaultData = React.useMemo(() => [], []) - const table = useTable({ - _features, - _rowModels: {}, - columns, - data: dataQuery.data?.rows ?? defaultData, - rowCount: dataQuery.data?.rowCount, - atoms: { - pagination: paginationAtom, + const table = useTable( + { + _features, + _rowModels: {}, + columns, + data: dataQuery.data?.rows ?? defaultData, + rowCount: dataQuery.data?.rowCount, + atoms: { + pagination: paginationAtom, + }, + manualPagination: true, // we're doing manual "server-side" pagination + debugTable: true, }, - manualPagination: true, // we're doing manual "server-side" pagination - debugTable: true, - }) + (state) => state, // default selector + ) return (
diff --git a/examples/react/with-tanstack-router/src/components/table.tsx b/examples/react/with-tanstack-router/src/components/table.tsx index 7fb891cd4c..63da294708 100644 --- a/examples/react/with-tanstack-router/src/components/table.tsx +++ b/examples/react/with-tanstack-router/src/components/table.tsx @@ -64,7 +64,7 @@ export default function Table>({ onSortingChange, ...paginationOptions, }, - (state) => state, + (state) => state, // default selector ) // Sync controlled state with per-slice base atoms diff --git a/examples/solid/composable-tables/src/App.tsx b/examples/solid/composable-tables/src/App.tsx index 73ca151f45..e70d37e75e 100644 --- a/examples/solid/composable-tables/src/App.tsx +++ b/examples/solid/composable-tables/src/App.tsx @@ -88,13 +88,11 @@ function UsersTable() { return (
{/* Table toolbar using pre-bound component */} - -
- - -
+ {/* Table element */} @@ -297,13 +295,8 @@ function ProductsTable() { -
- - -
{/* Table element */}
diff --git a/examples/solid/composable-tables/src/components/table-components.tsx b/examples/solid/composable-tables/src/components/table-components.tsx index b3d1596dbf..cd1d799338 100644 --- a/examples/solid/composable-tables/src/components/table-components.tsx +++ b/examples/solid/composable-tables/src/components/table-components.tsx @@ -95,21 +95,26 @@ export function RowCount() { export function TableToolbar({ title, onRefresh, + onStressTest, }: { title: string onRefresh?: () => void + onStressTest?: () => void }) { const table = useTableContext() return (

{title}

-
+
+ {onRefresh && } + {onStressTest && ( + + )} - {onRefresh && }
) diff --git a/examples/solid/composable-tables/src/index.css b/examples/solid/composable-tables/src/index.css index 75b7db2d40..4d8fef0142 100644 --- a/examples/solid/composable-tables/src/index.css +++ b/examples/solid/composable-tables/src/index.css @@ -174,6 +174,12 @@ tfoot th { border-spacing: 0; } +.table-toolbar-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + .table-toolbar button { border: 1px solid #ccc; border-radius: 4px; diff --git a/examples/svelte/composable-tables/src/components/ProductsTable.svelte b/examples/svelte/composable-tables/src/components/ProductsTable.svelte index 1673bdc52e..eba8efbdf6 100644 --- a/examples/svelte/composable-tables/src/components/ProductsTable.svelte +++ b/examples/svelte/composable-tables/src/components/ProductsTable.svelte @@ -76,14 +76,12 @@
-
- - -
- +
diff --git a/examples/svelte/composable-tables/src/components/TableToolbar.svelte b/examples/svelte/composable-tables/src/components/TableToolbar.svelte index 01eabb9ae5..6780cbe769 100644 --- a/examples/svelte/composable-tables/src/components/TableToolbar.svelte +++ b/examples/svelte/composable-tables/src/components/TableToolbar.svelte @@ -3,31 +3,33 @@ Uses useTableContext to access the table instance. -->
-

{title - }

-
+

{title}

+
+ {#if onRefresh} + + {/if} + {#if onStressTest} + + {/if} - {#if onRefresh} - - {/if}
diff --git a/examples/svelte/composable-tables/src/components/UsersTable.svelte b/examples/svelte/composable-tables/src/components/UsersTable.svelte index eb5d18f3a6..ed85bff648 100644 --- a/examples/svelte/composable-tables/src/components/UsersTable.svelte +++ b/examples/svelte/composable-tables/src/components/UsersTable.svelte @@ -88,14 +88,12 @@
-
- - -
- +
diff --git a/examples/svelte/composable-tables/src/index.css b/examples/svelte/composable-tables/src/index.css index 75b7db2d40..4d8fef0142 100644 --- a/examples/svelte/composable-tables/src/index.css +++ b/examples/svelte/composable-tables/src/index.css @@ -174,6 +174,12 @@ tfoot th { border-spacing: 0; } +.table-toolbar-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + .table-toolbar button { border: 1px solid #ccc; border-radius: 4px; diff --git a/examples/svelte/row-selection/src/App.svelte b/examples/svelte/row-selection/src/App.svelte index 23bf52bdb1..a1078162f3 100644 --- a/examples/svelte/row-selection/src/App.svelte +++ b/examples/svelte/row-selection/src/App.svelte @@ -298,7 +298,10 @@ -{#snippet Filter(column: Column, table: SvelteTable)} +{#snippet Filter( + column: Column, + table: SvelteTable, +)} {@const firstValue = table .getPreFilteredRowModel() .flatRows[0]?.getValue(column.id)} diff --git a/examples/vue/composable-tables/src/components/ProductsTable.vue b/examples/vue/composable-tables/src/components/ProductsTable.vue index ed9564794b..362f1ffb0b 100644 --- a/examples/vue/composable-tables/src/components/ProductsTable.vue +++ b/examples/vue/composable-tables/src/components/ProductsTable.vue @@ -64,11 +64,8 @@ const table = useAppTable({ :is="table.TableToolbar" title="Products Table" :onRefresh="refreshData" + :onStressTest="stressTest" /> -
- - -
diff --git a/examples/vue/composable-tables/src/components/UsersTable.vue b/examples/vue/composable-tables/src/components/UsersTable.vue index 89711873b4..c876473efc 100644 --- a/examples/vue/composable-tables/src/components/UsersTable.vue +++ b/examples/vue/composable-tables/src/components/UsersTable.vue @@ -82,11 +82,8 @@ function tableSelector(state: ReturnType) { :is="table.TableToolbar" title="Users Table" :onRefresh="refreshData" + :onStressTest="stressTest" /> -
- - -
diff --git a/examples/vue/composable-tables/src/components/table-components.ts b/examples/vue/composable-tables/src/components/table-components.ts index 4c11f255c5..750869c0db 100644 --- a/examples/vue/composable-tables/src/components/table-components.ts +++ b/examples/vue/composable-tables/src/components/table-components.ts @@ -122,19 +122,17 @@ export const TableToolbar = defineComponent({ type: Function as PropType<() => void>, default: undefined, }, + onStressTest: { + type: Function as PropType<() => void>, + default: undefined, + }, }, setup(props) { const table = useTableContext() return () => h('div', { class: 'table-toolbar' }, [ h('h2', props.title), - h('div', [ - h( - 'button', - { onClick: () => table.resetColumnFilters() }, - 'Clear Filters', - ), - h('button', { onClick: () => table.resetSorting() }, 'Clear Sorting'), + h('div', { class: 'table-toolbar-actions' }, [ props.onRefresh ? h( 'button', @@ -142,6 +140,19 @@ export const TableToolbar = defineComponent({ 'Regenerate Data', ) : null, + props.onStressTest + ? h( + 'button', + { onClick: () => props.onStressTest?.() }, + 'Stress Test (200k rows)', + ) + : null, + h( + 'button', + { onClick: () => table.resetColumnFilters() }, + 'Clear Filters', + ), + h('button', { onClick: () => table.resetSorting() }, 'Clear Sorting'), ]), ]) }, diff --git a/examples/vue/composable-tables/src/index.css b/examples/vue/composable-tables/src/index.css index 75b7db2d40..4d8fef0142 100644 --- a/examples/vue/composable-tables/src/index.css +++ b/examples/vue/composable-tables/src/index.css @@ -174,6 +174,12 @@ tfoot th { border-spacing: 0; } +.table-toolbar-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + .table-toolbar button { border: 1px solid #ccc; border-radius: 4px; diff --git a/packages/angular-table/src/helpers/createTableHook.ts b/packages/angular-table/src/helpers/createTableHook.ts index 0d1ab14097..51f58cea7c 100644 --- a/packages/angular-table/src/helpers/createTableHook.ts +++ b/packages/angular-table/src/helpers/createTableHook.ts @@ -404,7 +404,10 @@ export function createTableHook< return injectFlexRenderContext>() } - function injectAppTable( + function injectAppTable< + TData extends RowData, + TSelected = TableState, + >( tableOptions: () => Omit< TableOptions, '_features' | '_rowModels' diff --git a/packages/angular-table/src/injectTable.ts b/packages/angular-table/src/injectTable.ts index b33d83f9d5..dad7dbc457 100644 --- a/packages/angular-table/src/injectTable.ts +++ b/packages/angular-table/src/injectTable.ts @@ -136,8 +136,7 @@ export function injectTable< TSelected = TableState, >( options: () => TableOptions, - selector: (state: TableState) => TSelected = (state) => - state as TSelected, + selector?: (state: TableState) => TSelected, ): AngularTable { assertInInjectionContext(injectTable) const injector = inject(Injector) @@ -175,18 +174,14 @@ export function injectTable< equal?: ValueEqualityFn }) { const source = props.source ?? table.store - return injectSelector( - source, - props.selector ?? ((state: unknown) => state), - { - compare: props.equal ?? shallow, - injector, - }, - ) + return injectSelector(source, props.selector as never, { + compare: props.equal ?? shallow, + injector, + }) } as AngularTableComputed Object.defineProperty(table, 'state', { - value: computed(() => selector(table.store.get())), + value: computed(() => selector?.(table.store.get()) ?? table.store.get()), }) Object.defineProperty(table, 'value', { diff --git a/packages/lit-table/src/TableController.ts b/packages/lit-table/src/TableController.ts index 0d652f5ef5..8c0e1de7e9 100644 --- a/packages/lit-table/src/TableController.ts +++ b/packages/lit-table/src/TableController.ts @@ -30,7 +30,7 @@ export type SubscribeSource = export type LitTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = {}, + TSelected = TableState, > = Table & { /** * Subscribe to a selected slice of table state, or to a single source (atom or store). @@ -144,10 +144,9 @@ export class TableController< ;(this.host = host).addController(this) } - public table( + public table>( tableOptions: TableOptions, - selector: (state: TableState) => TSelected = () => - ({}) as TSelected, + selector?: (state: TableState) => TSelected, ): LitTable { if (!this._table) { const mergedOptions: TableOptions = { @@ -206,7 +205,8 @@ export class TableController< Subscribe, FlexRender, get state() { - return selector(tableInstance.store.state) + return (selector?.(tableInstance.store.state) ?? + tableInstance.store.state) as TSelected }, } } diff --git a/packages/lit-table/src/createTableHook.ts b/packages/lit-table/src/createTableHook.ts index 9beeb978a4..8fdd161934 100644 --- a/packages/lit-table/src/createTableHook.ts +++ b/packages/lit-table/src/createTableHook.ts @@ -612,7 +612,10 @@ export function createTableHook< * } * ``` */ - function useAppTable( + function useAppTable< + TData extends RowData, + TSelected = TableState, + >( host: ReactiveControllerHost & HTMLElement, tableOptions: Omit< TableOptions, diff --git a/packages/preact-table/src/Subscribe.ts b/packages/preact-table/src/Subscribe.ts index d795af56ac..90016c297a 100644 --- a/packages/preact-table/src/Subscribe.ts +++ b/packages/preact-table/src/Subscribe.ts @@ -92,11 +92,9 @@ export function Subscribe< >( props: SubscribeProps, ): ComponentChildren { - const selectFn = props.selector ?? ((x: unknown) => x) - const selected = useSelector( props.source as never, - selectFn as Parameters[1], + props.selector as Parameters[1], { compare: shallow, }, diff --git a/packages/preact-table/src/createTableHook.tsx b/packages/preact-table/src/createTableHook.tsx index a69516737e..71dbd53eff 100644 --- a/packages/preact-table/src/createTableHook.tsx +++ b/packages/preact-table/src/createTableHook.tsx @@ -796,7 +796,10 @@ export function createTableHook< * * TFeatures is already known from the createTableHook call; TData is inferred from the data prop. */ - function useAppTable( + function useAppTable< + TData extends RowData, + TSelected = TableState, + >( tableOptions: Omit< TableOptions, '_features' | '_rowModels' diff --git a/packages/preact-table/src/useTable.ts b/packages/preact-table/src/useTable.ts index d1fd134619..238c0959f2 100644 --- a/packages/preact-table/src/useTable.ts +++ b/packages/preact-table/src/useTable.ts @@ -19,7 +19,7 @@ import type { SubscribePropsWithStore, SubscribeSource } from './Subscribe' export type PreactTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = {}, + TSelected = TableState, > = Table & { /** * A Preact HOC (Higher Order Component) that allows you to subscribe to the table state. @@ -79,11 +79,10 @@ export type PreactTable< export function useTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = {}, + TSelected = TableState, >( tableOptions: TableOptions, - selector: (state: TableState) => TSelected = () => - ({}) as TSelected, + selector?: (state: TableState) => TSelected, ): PreactTable { const [table] = useState(() => { const tableInstance = constructTable({ diff --git a/packages/react-table/src/Subscribe.ts b/packages/react-table/src/Subscribe.ts index 41d769f33d..b9a9c45445 100644 --- a/packages/react-table/src/Subscribe.ts +++ b/packages/react-table/src/Subscribe.ts @@ -138,12 +138,10 @@ export function Subscribe< >( props: SubscribeProps, ): ReturnType { - const selectFn = props.selector ?? ((x: unknown) => x) - const selected = useSelector( // Atom and store share the same selection protocol; union args need a widen for TS. props.source, - selectFn as Parameters[1], + props.selector as Parameters[1], { compare: shallow, }, diff --git a/packages/react-table/src/createTableHook.tsx b/packages/react-table/src/createTableHook.tsx index e873b8ed05..2095137adc 100644 --- a/packages/react-table/src/createTableHook.tsx +++ b/packages/react-table/src/createTableHook.tsx @@ -800,7 +800,10 @@ export function createTableHook< * * TFeatures is already known from the createTableHook call; TData is inferred from the data prop. */ - function useAppTable( + function useAppTable< + TData extends RowData, + TSelected = TableState, + >( tableOptions: Omit< TableOptions, '_features' | '_rowModels' diff --git a/packages/react-table/src/useTable.ts b/packages/react-table/src/useTable.ts index b86c1d0e88..187ad48c06 100644 --- a/packages/react-table/src/useTable.ts +++ b/packages/react-table/src/useTable.ts @@ -21,7 +21,7 @@ import type { FunctionComponent, ReactNode } from 'react' export type ReactTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = {}, + TSelected = TableState, > = Table & { /** * A React HOC (Higher Order Component) that allows you to subscribe to the table state. @@ -108,11 +108,10 @@ export type ReactTable< export function useTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = {}, + TSelected = TableState, >( tableOptions: TableOptions, - selector: (state: TableState) => TSelected = () => - ({}) as TSelected, + selector?: (state: TableState) => TSelected, ): ReactTable { const [table] = useState(() => { const tableInstance = constructTable({ diff --git a/packages/solid-table/src/createTable.ts b/packages/solid-table/src/createTable.ts index e969d9f7cd..be3e3b172d 100644 --- a/packages/solid-table/src/createTable.ts +++ b/packages/solid-table/src/createTable.ts @@ -27,7 +27,7 @@ export type SubscribeSource = export type SolidTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = {}, + TSelected = TableState, > = Table & { /** * Subscribe to the store (selector required) or a single source (atom or store). @@ -76,11 +76,10 @@ export type SolidTable< export function createTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = {}, + TSelected = TableState, >( tableOptions: TableOptions, - selector: (state: TableState) => TSelected = () => - ({}) as TSelected, + selector?: (state: TableState) => TSelected, ): SolidTable { const owner = getOwner()! @@ -130,8 +129,7 @@ export function createTable< children: ((state: Accessor) => JSX.Element) | JSX.Element }) => { const source = props.source ?? table.store - const selectFn = props.selector ?? ((x: unknown) => x) - const selected = useSelector(source as never, selectFn, { + const selected = useSelector(source as never, props.selector as never, { compare: shallow, }) return typeof props.children === 'function' diff --git a/packages/solid-table/src/createTableHook.tsx b/packages/solid-table/src/createTableHook.tsx index b639f92bbe..8747fb7a57 100644 --- a/packages/solid-table/src/createTableHook.tsx +++ b/packages/solid-table/src/createTableHook.tsx @@ -820,7 +820,10 @@ export function createTableHook< * * TFeatures is already known from the createTableHook call; TData is inferred from the data prop. */ - function createAppTable( + function createAppTable< + TData extends RowData, + TSelected = TableState, + >( tableOptions: Omit< TableOptions, '_features' | '_rowModels' diff --git a/packages/svelte-table/src/createTable.svelte.ts b/packages/svelte-table/src/createTable.svelte.ts index 9c24a4a1de..d050453066 100644 --- a/packages/svelte-table/src/createTable.svelte.ts +++ b/packages/svelte-table/src/createTable.svelte.ts @@ -14,7 +14,7 @@ import type { export type SvelteTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = {}, + TSelected = TableState, > = Table & { /** * The selected state of the table. This state may not match the structure of `table.store.state` because it is selected by the `selector` function that you pass as the 2nd argument to `createTable`. @@ -30,11 +30,10 @@ export type SvelteTable< export function createTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = {}, + TSelected = TableState, >( tableOptions: TableOptions, - selector: (state: TableState) => TSelected = () => - ({}) as TSelected, + selector?: (state: TableState) => TSelected, ): SvelteTable { // 1. Merge reactivity into options using mergeObjects (preserves getters) const mergedOptions = mergeObjects(tableOptions, { diff --git a/packages/svelte-table/src/createTableHook.svelte.ts b/packages/svelte-table/src/createTableHook.svelte.ts index 4f063f929b..be40ea02a1 100644 --- a/packages/svelte-table/src/createTableHook.svelte.ts +++ b/packages/svelte-table/src/createTableHook.svelte.ts @@ -523,7 +523,10 @@ export function createTableHook< * * TFeatures is already known from the createTableHook call; TData is inferred from the data prop. */ - function createAppTable( + function createAppTable< + TData extends RowData, + TSelected = TableState, + >( tableOptions: Omit< TableOptions, '_features' | '_rowModels' diff --git a/packages/vue-table/src/createTableHook.ts b/packages/vue-table/src/createTableHook.ts index 91f9f34a89..644b5eb632 100644 --- a/packages/vue-table/src/createTableHook.ts +++ b/packages/vue-table/src/createTableHook.ts @@ -392,7 +392,10 @@ export function createTableHook< }, }) - function useAppTable( + function useAppTable< + TData extends RowData, + TSelected = TableState, + >( tableOptions: Omit< TableOptionsWithReactiveData, '_features' | '_rowModels' diff --git a/packages/vue-table/src/useTable.ts b/packages/vue-table/src/useTable.ts index 22b8eb7010..eb129cac29 100644 --- a/packages/vue-table/src/useTable.ts +++ b/packages/vue-table/src/useTable.ts @@ -61,7 +61,7 @@ function getReactiveOptionDeps< export type VueTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = {}, + TSelected = TableState, > = Table & { /** * Store mode: `selector` required. Source mode: pass `source` (atom or store); omit @@ -107,13 +107,12 @@ export type VueTable< export function useTable< TFeatures extends TableFeatures, TData extends RowData, - TSelected = {}, + TSelected = TableState, >( tableOptions: | TableOptions | TableOptionsWithReactiveData, - selector: (state: TableState) => TSelected = () => - ({}) as TSelected, + selector?: (state: TableState) => TSelected, ): VueTable { const syncTableOptions = ( table: Table, @@ -209,8 +208,7 @@ export function useTable< | Array }) => { const source = props.source ?? table.store - const selectFn = props.selector ?? ((x: unknown) => x) - const selected = useSelector(source as never, selectFn as never, { + const selected = useSelector(source as never, props.selector as never, { compare: shallow, }) if (typeof props.children === 'function') { From fbaf0048b6ccb1b6f5f5aaa6c5e914af85438b0f Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Fri, 8 May 2026 12:24:41 -0500 Subject: [PATCH 2/2] add basic-subscribe example --- docs/config.json | 2 + .../reference/functions/injectTable.md | 6 +- docs/framework/angular/reference/index.md | 1 + .../interfaces/AngularTableComputed.md | 12 +- .../reference/type-aliases/AngularTable.md | 29 +- .../reference/type-aliases/SubscribeSource.md | 22 + .../reference/index/functions/Subscribe.md | 53 +- .../index/functions/createTableHook.md | 3 +- .../reference/index/functions/useTable.md | 9 +- docs/framework/react/reference/index/index.md | 1 + .../index/type-aliases/ReactTable.md | 11 +- .../index/type-aliases/SubscribeProps.md | 16 +- .../type-aliases/SubscribePropsWithSource.md | 18 +- .../SubscribePropsWithSourceIdentity.md | 26 +- .../SubscribePropsWithSourceWithSelector.md | 30 +- .../type-aliases/SubscribePropsWithStore.md | 20 +- .../index/type-aliases/SubscribeSource.md | 22 + examples/preact/basic-subscribe/index.html | 13 + examples/preact/basic-subscribe/package.json | 26 + examples/preact/basic-subscribe/src/index.css | 372 +++++++++++++ examples/preact/basic-subscribe/src/main.tsx | 478 +++++++++++++++++ .../preact/basic-subscribe/src/makeData.ts | 50 ++ .../preact/basic-subscribe/src/vite-env.d.ts | 1 + examples/preact/basic-subscribe/tsconfig.json | 25 + .../preact/basic-subscribe/vite.config.ts | 7 + examples/react/basic-subscribe/.gitignore | 5 + examples/react/basic-subscribe/README.md | 6 + examples/react/basic-subscribe/index.html | 13 + examples/react/basic-subscribe/package.json | 31 ++ examples/react/basic-subscribe/src/index.css | 372 +++++++++++++ examples/react/basic-subscribe/src/main.tsx | 489 ++++++++++++++++++ .../react/basic-subscribe/src/makeData.ts | 50 ++ .../react/basic-subscribe/src/vite-env.d.ts | 1 + examples/react/basic-subscribe/tsconfig.json | 21 + examples/react/basic-subscribe/vite.config.js | 23 + pnpm-lock.yaml | 80 +++ 36 files changed, 2177 insertions(+), 167 deletions(-) create mode 100644 docs/framework/angular/reference/type-aliases/SubscribeSource.md create mode 100644 docs/framework/react/reference/index/type-aliases/SubscribeSource.md create mode 100644 examples/preact/basic-subscribe/index.html create mode 100644 examples/preact/basic-subscribe/package.json create mode 100644 examples/preact/basic-subscribe/src/index.css create mode 100644 examples/preact/basic-subscribe/src/main.tsx create mode 100644 examples/preact/basic-subscribe/src/makeData.ts create mode 100644 examples/preact/basic-subscribe/src/vite-env.d.ts create mode 100644 examples/preact/basic-subscribe/tsconfig.json create mode 100644 examples/preact/basic-subscribe/vite.config.ts create mode 100644 examples/react/basic-subscribe/.gitignore create mode 100644 examples/react/basic-subscribe/README.md create mode 100644 examples/react/basic-subscribe/index.html create mode 100644 examples/react/basic-subscribe/package.json create mode 100644 examples/react/basic-subscribe/src/index.css create mode 100644 examples/react/basic-subscribe/src/main.tsx create mode 100644 examples/react/basic-subscribe/src/makeData.ts create mode 100644 examples/react/basic-subscribe/src/vite-env.d.ts create mode 100644 examples/react/basic-subscribe/tsconfig.json create mode 100644 examples/react/basic-subscribe/vite.config.js diff --git a/docs/config.json b/docs/config.json index 21120c3651..3a61fd255d 100644 --- a/docs/config.json +++ b/docs/config.json @@ -823,6 +823,7 @@ { "to": "framework/react/examples/basic-use-legacy-table", "label": "Basic (useLegacyTable)" }, { "to": "framework/react/examples/basic-external-state", "label": "Basic (External State)" }, { "to": "framework/react/examples/basic-external-atoms", "label": "Basic (External Atoms)" }, + { "to": "framework/react/examples/basic-subscribe", "label": "Basic (Subscribe)" }, { "to": "framework/react/examples/column-groups", "label": "Header Groups" } ] }, @@ -864,6 +865,7 @@ { "to": "framework/preact/examples/basic-use-app-table", "label": "Basic (useAppTable)" }, { "to": "framework/preact/examples/basic-external-state", "label": "Basic (External State)" }, { "to": "framework/preact/examples/basic-external-atoms", "label": "Basic (External Atoms)" }, + { "to": "framework/preact/examples/basic-subscribe", "label": "Basic (Subscribe)" }, { "to": "framework/preact/examples/column-groups", "label": "Header Groups" } ] }, diff --git a/docs/framework/angular/reference/functions/injectTable.md b/docs/framework/angular/reference/functions/injectTable.md index f6b3ded2f1..177051ff49 100644 --- a/docs/framework/angular/reference/functions/injectTable.md +++ b/docs/framework/angular/reference/functions/injectTable.md @@ -6,10 +6,10 @@ title: injectTable # Function: injectTable() ```ts -function injectTable(options, selector): AngularTable; +function injectTable(options, selector?): AngularTable; ``` -Defined in: [injectTable.ts:124](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L124) +Defined in: [injectTable.ts:133](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L133) Creates and returns an Angular-reactive table instance. @@ -42,7 +42,7 @@ The returned table is also signal-reactive: table state and table APIs are wired () => `TableOptions`\<`TFeatures`, `TData`\> -### selector +### selector? (`state`) => `TSelected` diff --git a/docs/framework/angular/reference/index.md b/docs/framework/angular/reference/index.md index 36769f83fe..378d414a33 100644 --- a/docs/framework/angular/reference/index.md +++ b/docs/framework/angular/reference/index.md @@ -38,6 +38,7 @@ title: "@tanstack/angular-table" - [FlexRenderContent](type-aliases/FlexRenderContent.md) - [FlexRenderInputContent](type-aliases/FlexRenderInputContent.md) - [RenderableComponent](type-aliases/RenderableComponent.md) +- [SubscribeSource](type-aliases/SubscribeSource.md) ## Variables diff --git a/docs/framework/angular/reference/interfaces/AngularTableComputed.md b/docs/framework/angular/reference/interfaces/AngularTableComputed.md index 99495d91b7..5b3c0a973b 100644 --- a/docs/framework/angular/reference/interfaces/AngularTableComputed.md +++ b/docs/framework/angular/reference/interfaces/AngularTableComputed.md @@ -5,7 +5,7 @@ title: AngularTableComputed # Interface: AngularTableComputed()\ -Defined in: [injectTable.ts:28](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L28) +Defined in: [injectTable.ts:40](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L40) Store mode: pass `selector` (required) to project from full table state. Source mode: pass `source` (atom or store); omit `selector` for the whole value @@ -24,7 +24,7 @@ inference. AngularTableComputed(props): Signal>; ``` -Defined in: [injectTable.ts:29](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L29) +Defined in: [injectTable.ts:41](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L41) Store mode: pass `selector` (required) to project from full table state. Source mode: pass `source` (atom or store); omit `selector` for the whole value @@ -51,7 +51,7 @@ inference. ##### source -`Atom`\<`TSourceValue`\> \| `ReadonlyAtom`\<`TSourceValue`\> +[`SubscribeSource`](../type-aliases/SubscribeSource.md)\<`TSourceValue`\> ### Returns @@ -63,7 +63,7 @@ inference. AngularTableComputed(props): Signal>; ``` -Defined in: [injectTable.ts:34](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L34) +Defined in: [injectTable.ts:46](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L46) Store mode: pass `selector` (required) to project from full table state. Source mode: pass `source` (atom or store); omit `selector` for the whole value @@ -94,7 +94,7 @@ inference. ##### source -`Atom`\<`TSourceValue`\> \| `ReadonlyAtom`\<`TSourceValue`\> +[`SubscribeSource`](../type-aliases/SubscribeSource.md)\<`TSourceValue`\> ### Returns @@ -106,7 +106,7 @@ inference. AngularTableComputed(props): Signal>; ``` -Defined in: [injectTable.ts:39](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L39) +Defined in: [injectTable.ts:51](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L51) Store mode: pass `selector` (required) to project from full table state. Source mode: pass `source` (atom or store); omit `selector` for the whole value diff --git a/docs/framework/angular/reference/type-aliases/AngularTable.md b/docs/framework/angular/reference/type-aliases/AngularTable.md index d58e3ccafa..44a6367d5f 100644 --- a/docs/framework/angular/reference/type-aliases/AngularTable.md +++ b/docs/framework/angular/reference/type-aliases/AngularTable.md @@ -9,42 +9,19 @@ title: AngularTable type AngularTable = Table & object; ``` -Defined in: [injectTable.ts:45](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L45) +Defined in: [injectTable.ts:57](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L57) ## Type Declaration -### computed() +### computed ```ts -computed: (props) => Signal>; +computed: AngularTableComputed; ``` Creates a computed that subscribe to changes in the table store with a custom selector. Default equality function is "shallow". -#### Type Parameters - -##### TSubSelected - -`TSubSelected` = \{ -\} - -#### Parameters - -##### props - -###### equal? - -`ValueEqualityFn`\<`TSubSelected`\> - -###### selector - -(`state`) => `TSubSelected` - -#### Returns - -`Signal`\<`Readonly`\<`TSubSelected`\>\> - ### state ```ts diff --git a/docs/framework/angular/reference/type-aliases/SubscribeSource.md b/docs/framework/angular/reference/type-aliases/SubscribeSource.md new file mode 100644 index 0000000000..5e0fe25a71 --- /dev/null +++ b/docs/framework/angular/reference/type-aliases/SubscribeSource.md @@ -0,0 +1,22 @@ +--- +id: SubscribeSource +title: SubscribeSource +--- + +# Type Alias: SubscribeSource\ + +```ts +type SubscribeSource = + | Atom + | ReadonlyAtom + | Store +| ReadonlyStore; +``` + +Defined in: [injectTable.ts:28](https://github.com/TanStack/table/blob/main/packages/angular-table/src/injectTable.ts#L28) + +## Type Parameters + +### TValue + +`TValue` diff --git a/docs/framework/react/reference/index/functions/Subscribe.md b/docs/framework/react/reference/index/functions/Subscribe.md index f1d2c735e4..c8f099b527 100644 --- a/docs/framework/react/reference/index/functions/Subscribe.md +++ b/docs/framework/react/reference/index/functions/Subscribe.md @@ -8,10 +8,10 @@ title: Subscribe ## Call Signature ```ts -function Subscribe(props): ReactNode | Promise; +function Subscribe(props): ReactNode | Promise; ``` -Defined in: [Subscribe.ts:148](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L148) +Defined in: [Subscribe.ts:125](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L125) A React component that allows you to subscribe to the table state. @@ -22,14 +22,6 @@ contextual typing works. This standalone component uses a union `props` type. ### Type Parameters -#### TFeatures - -`TFeatures` *extends* `TableFeatures` - -#### TData - -`TData` *extends* `RowData` - #### TSourceValue `TSourceValue` @@ -38,7 +30,7 @@ contextual typing works. This standalone component uses a union `props` type. #### props -[`SubscribePropsWithSourceIdentity`](../type-aliases/SubscribePropsWithSourceIdentity.md)\<`TFeatures`, `TData`, `TSourceValue`\> +[`SubscribePropsWithSourceIdentity`](../type-aliases/SubscribePropsWithSourceIdentity.md)\<`TSourceValue`\> ### Returns @@ -48,7 +40,7 @@ contextual typing works. This standalone component uses a union `props` type. ```tsx // As a standalone component — full store - ({ rowSelection: state.rowSelection })}> + ({ rowSelection: state.rowSelection })}> {({ rowSelection }) => (
Selected rows: {Object.keys(rowSelection).length}
)} @@ -57,7 +49,7 @@ contextual typing works. This standalone component uses a union `props` type. ```tsx // Entire source (atom or store) — no selector - + {(rowSelection) =>
...
}
``` @@ -65,7 +57,6 @@ contextual typing works. This standalone component uses a union `props` type. ```tsx // Project source value (e.g. one row’s selection) rowSelection?.[row.id]} > @@ -85,10 +76,10 @@ contextual typing works. This standalone component uses a union `props` type. ## Call Signature ```ts -function Subscribe(props): ReactNode | Promise; +function Subscribe(props): ReactNode | Promise; ``` -Defined in: [Subscribe.ts:155](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L155) +Defined in: [Subscribe.ts:128](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L128) A React component that allows you to subscribe to the table state. @@ -99,14 +90,6 @@ contextual typing works. This standalone component uses a union `props` type. ### Type Parameters -#### TFeatures - -`TFeatures` *extends* `TableFeatures` - -#### TData - -`TData` *extends* `RowData` - #### TSourceValue `TSourceValue` @@ -119,7 +102,7 @@ contextual typing works. This standalone component uses a union `props` type. #### props -[`SubscribePropsWithSourceWithSelector`](../type-aliases/SubscribePropsWithSourceWithSelector.md)\<`TFeatures`, `TData`, `TSourceValue`, `TSelected`\> +[`SubscribePropsWithSourceWithSelector`](../type-aliases/SubscribePropsWithSourceWithSelector.md)\<`TSourceValue`, `TSelected`\> ### Returns @@ -129,7 +112,7 @@ contextual typing works. This standalone component uses a union `props` type. ```tsx // As a standalone component — full store - ({ rowSelection: state.rowSelection })}> + ({ rowSelection: state.rowSelection })}> {({ rowSelection }) => (
Selected rows: {Object.keys(rowSelection).length}
)} @@ -138,7 +121,7 @@ contextual typing works. This standalone component uses a union `props` type. ```tsx // Entire source (atom or store) — no selector - + {(rowSelection) =>
...
}
``` @@ -146,7 +129,6 @@ contextual typing works. This standalone component uses a union `props` type. ```tsx // Project source value (e.g. one row’s selection) rowSelection?.[row.id]} > @@ -166,10 +148,10 @@ contextual typing works. This standalone component uses a union `props` type. ## Call Signature ```ts -function Subscribe(props): ReactNode | Promise; +function Subscribe(props): ReactNode | Promise; ``` -Defined in: [Subscribe.ts:168](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L168) +Defined in: [Subscribe.ts:131](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L131) A React component that allows you to subscribe to the table state. @@ -184,10 +166,6 @@ contextual typing works. This standalone component uses a union `props` type. `TFeatures` *extends* `TableFeatures` -#### TData - -`TData` *extends* `RowData` - #### TSelected `TSelected` @@ -196,7 +174,7 @@ contextual typing works. This standalone component uses a union `props` type. #### props -[`SubscribePropsWithStore`](../type-aliases/SubscribePropsWithStore.md)\<`TFeatures`, `TData`, `TSelected`\> +[`SubscribePropsWithStore`](../type-aliases/SubscribePropsWithStore.md)\<`TFeatures`, `TSelected`\> ### Returns @@ -206,7 +184,7 @@ contextual typing works. This standalone component uses a union `props` type. ```tsx // As a standalone component — full store - ({ rowSelection: state.rowSelection })}> + ({ rowSelection: state.rowSelection })}> {({ rowSelection }) => (
Selected rows: {Object.keys(rowSelection).length}
)} @@ -215,7 +193,7 @@ contextual typing works. This standalone component uses a union `props` type. ```tsx // Entire source (atom or store) — no selector - + {(rowSelection) =>
...
}
``` @@ -223,7 +201,6 @@ contextual typing works. This standalone component uses a union `props` type. ```tsx // Project source value (e.g. one row’s selection) rowSelection?.[row.id]} > diff --git a/docs/framework/react/reference/index/functions/createTableHook.md b/docs/framework/react/reference/index/functions/createTableHook.md index 51e54b22b1..90220cfbb7 100644 --- a/docs/framework/react/reference/index/functions/createTableHook.md +++ b/docs/framework/react/reference/index/functions/createTableHook.md @@ -110,8 +110,7 @@ TFeatures is already known from the createTableHook call; TData is inferred from ##### TSelected -`TSelected` = \{ -\} +`TSelected` = `TableState`\<`TFeatures`\> #### Parameters diff --git a/docs/framework/react/reference/index/functions/useTable.md b/docs/framework/react/reference/index/functions/useTable.md index 562026f411..3e61910931 100644 --- a/docs/framework/react/reference/index/functions/useTable.md +++ b/docs/framework/react/reference/index/functions/useTable.md @@ -6,10 +6,10 @@ title: useTable # Function: useTable() ```ts -function useTable(tableOptions, selector): ReactTable; +function useTable(tableOptions, selector?): ReactTable; ``` -Defined in: [useTable.ts:112](https://github.com/TanStack/table/blob/main/packages/react-table/src/useTable.ts#L112) +Defined in: [useTable.ts:108](https://github.com/TanStack/table/blob/main/packages/react-table/src/useTable.ts#L108) ## Type Parameters @@ -23,8 +23,7 @@ Defined in: [useTable.ts:112](https://github.com/TanStack/table/blob/main/packag ### TSelected -`TSelected` = \{ -\} +`TSelected` = `TableState`\<`TFeatures`\> ## Parameters @@ -32,7 +31,7 @@ Defined in: [useTable.ts:112](https://github.com/TanStack/table/blob/main/packag `TableOptions`\<`TFeatures`, `TData`\> -### selector +### selector? (`state`) => `TSelected` diff --git a/docs/framework/react/reference/index/index.md b/docs/framework/react/reference/index/index.md index b93f343e15..7a3e72dfef 100644 --- a/docs/framework/react/reference/index/index.md +++ b/docs/framework/react/reference/index/index.md @@ -36,6 +36,7 @@ title: index - [SubscribePropsWithSourceIdentity](type-aliases/SubscribePropsWithSourceIdentity.md) - [SubscribePropsWithSourceWithSelector](type-aliases/SubscribePropsWithSourceWithSelector.md) - [SubscribePropsWithStore](type-aliases/SubscribePropsWithStore.md) +- [SubscribeSource](type-aliases/SubscribeSource.md) ## Functions diff --git a/docs/framework/react/reference/index/type-aliases/ReactTable.md b/docs/framework/react/reference/index/type-aliases/ReactTable.md index 1620c2cb3b..ecf261b102 100644 --- a/docs/framework/react/reference/index/type-aliases/ReactTable.md +++ b/docs/framework/react/reference/index/type-aliases/ReactTable.md @@ -9,7 +9,7 @@ title: ReactTable type ReactTable = Table & object; ``` -Defined in: [useTable.ts:22](https://github.com/TanStack/table/blob/main/packages/react-table/src/useTable.ts#L22) +Defined in: [useTable.ts:21](https://github.com/TanStack/table/blob/main/packages/react-table/src/useTable.ts#L21) ## Type Declaration @@ -114,7 +114,7 @@ The **source** overloads are listed first so `TSourceValue` is inferred from `so ###### source -`Atom`\<`TSourceValue`\> \| `ReadonlyAtom`\<`TSourceValue`\> +[`SubscribeSource`](SubscribeSource.md)\<`TSourceValue`\> ##### Returns @@ -150,7 +150,7 @@ The **source** overloads are listed first so `TSourceValue` is inferred from `so ###### source -`Atom`\<`TSourceValue`\> \| `ReadonlyAtom`\<`TSourceValue`\> +[`SubscribeSource`](SubscribeSource.md)\<`TSourceValue`\> ##### Returns @@ -172,7 +172,7 @@ The **source** overloads are listed first so `TSourceValue` is inferred from `so ###### props -`Omit`\<[`SubscribePropsWithStore`](SubscribePropsWithStore.md)\<`TFeatures`, `TData`, `TSubSelected`\>, `"table"`\> +`Omit`\<[`SubscribePropsWithStore`](SubscribePropsWithStore.md)\<`TFeatures`, `TSubSelected`\>, `"source"`\> ##### Returns @@ -190,5 +190,4 @@ The **source** overloads are listed first so `TSourceValue` is inferred from `so ### TSelected -`TSelected` = \{ -\} +`TSelected` = `TableState`\<`TFeatures`\> diff --git a/docs/framework/react/reference/index/type-aliases/SubscribeProps.md b/docs/framework/react/reference/index/type-aliases/SubscribeProps.md index d416bed1ff..41ebae0626 100644 --- a/docs/framework/react/reference/index/type-aliases/SubscribeProps.md +++ b/docs/framework/react/reference/index/type-aliases/SubscribeProps.md @@ -3,16 +3,16 @@ id: SubscribeProps title: SubscribeProps --- -# Type Alias: SubscribeProps\ +# Type Alias: SubscribeProps\ ```ts -type SubscribeProps = - | SubscribePropsWithStore - | SubscribePropsWithSourceIdentity -| SubscribePropsWithSourceWithSelector; +type SubscribeProps = + | SubscribePropsWithStore + | SubscribePropsWithSourceIdentity +| SubscribePropsWithSourceWithSelector; ``` -Defined in: [Subscribe.ts:85](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L85) +Defined in: [Subscribe.ts:69](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L69) ## Type Parameters @@ -20,10 +20,6 @@ Defined in: [Subscribe.ts:85](https://github.com/TanStack/table/blob/main/packag `TFeatures` *extends* `TableFeatures` -### TData - -`TData` *extends* `RowData` - ### TSelected `TSelected` = `unknown` diff --git a/docs/framework/react/reference/index/type-aliases/SubscribePropsWithSource.md b/docs/framework/react/reference/index/type-aliases/SubscribePropsWithSource.md index 421c14fb64..e08ee8d1d1 100644 --- a/docs/framework/react/reference/index/type-aliases/SubscribePropsWithSource.md +++ b/docs/framework/react/reference/index/type-aliases/SubscribePropsWithSource.md @@ -3,15 +3,15 @@ id: SubscribePropsWithSource title: SubscribePropsWithSource --- -# Type Alias: SubscribePropsWithSource\ +# Type Alias: SubscribePropsWithSource\ ```ts -type SubscribePropsWithSource = - | SubscribePropsWithSourceIdentity -| SubscribePropsWithSourceWithSelector; +type SubscribePropsWithSource = + | SubscribePropsWithSourceIdentity +| SubscribePropsWithSourceWithSelector; ``` -Defined in: [Subscribe.ts:71](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L71) +Defined in: [Subscribe.ts:65](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L65) Subscribe to a single source — atom or store (identity or projected). Prefer [SubscribePropsWithSourceIdentity](SubscribePropsWithSourceIdentity.md) or [SubscribePropsWithSourceWithSelector](SubscribePropsWithSourceWithSelector.md) @@ -19,14 +19,6 @@ for clearer inference when `selector` is omitted. ## Type Parameters -### TFeatures - -`TFeatures` *extends* `TableFeatures` - -### TData - -`TData` *extends* `RowData` - ### TSourceValue `TSourceValue` diff --git a/docs/framework/react/reference/index/type-aliases/SubscribePropsWithSourceIdentity.md b/docs/framework/react/reference/index/type-aliases/SubscribePropsWithSourceIdentity.md index 2b23021cb4..311890ced1 100644 --- a/docs/framework/react/reference/index/type-aliases/SubscribePropsWithSourceIdentity.md +++ b/docs/framework/react/reference/index/type-aliases/SubscribePropsWithSourceIdentity.md @@ -3,13 +3,13 @@ id: SubscribePropsWithSourceIdentity title: SubscribePropsWithSourceIdentity --- -# Type Alias: SubscribePropsWithSourceIdentity\ +# Type Alias: SubscribePropsWithSourceIdentity\ ```ts -type SubscribePropsWithSourceIdentity = object; +type SubscribePropsWithSourceIdentity = object; ``` -Defined in: [Subscribe.ts:39](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L39) +Defined in: [Subscribe.ts:44](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L44) Subscribe to the full value of a source (e.g. `table.atoms.rowSelection` or `table.optionsStore`). Omitting `selector` is equivalent to the identity @@ -17,14 +17,6 @@ selector — children receive `TSourceValue`. ## Type Parameters -### TFeatures - -`TFeatures` *extends* `TableFeatures` - -### TData - -`TData` *extends* `RowData` - ### TSourceValue `TSourceValue` @@ -54,17 +46,7 @@ Defined in: [Subscribe.ts:46](https://github.com/TanStack/table/blob/main/packag ### source ```ts -source: Atom | ReadonlyAtom; +source: SubscribeSource; ``` Defined in: [Subscribe.ts:45](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L45) - -*** - -### table - -```ts -table: Table; -``` - -Defined in: [Subscribe.ts:44](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L44) diff --git a/docs/framework/react/reference/index/type-aliases/SubscribePropsWithSourceWithSelector.md b/docs/framework/react/reference/index/type-aliases/SubscribePropsWithSourceWithSelector.md index 9f23a762d6..5992d99ea1 100644 --- a/docs/framework/react/reference/index/type-aliases/SubscribePropsWithSourceWithSelector.md +++ b/docs/framework/react/reference/index/type-aliases/SubscribePropsWithSourceWithSelector.md @@ -3,10 +3,10 @@ id: SubscribePropsWithSourceWithSelector title: SubscribePropsWithSourceWithSelector --- -# Type Alias: SubscribePropsWithSourceWithSelector\ +# Type Alias: SubscribePropsWithSourceWithSelector\ ```ts -type SubscribePropsWithSourceWithSelector = object; +type SubscribePropsWithSourceWithSelector = object; ``` Defined in: [Subscribe.ts:54](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L54) @@ -16,14 +16,6 @@ receives the source value; children receive the projected `TSelected`. ## Type Parameters -### TFeatures - -`TFeatures` *extends* `TableFeatures` - -### TData - -`TData` *extends* `RowData` - ### TSourceValue `TSourceValue` @@ -40,7 +32,7 @@ receives the source value; children receive the projected `TSelected`. children: (state) => ReactNode | ReactNode; ``` -Defined in: [Subscribe.ts:63](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L63) +Defined in: [Subscribe.ts:57](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L57) *** @@ -50,7 +42,7 @@ Defined in: [Subscribe.ts:63](https://github.com/TanStack/table/blob/main/packag selector: (state) => TSelected; ``` -Defined in: [Subscribe.ts:62](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L62) +Defined in: [Subscribe.ts:56](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L56) #### Parameters @@ -67,17 +59,7 @@ Defined in: [Subscribe.ts:62](https://github.com/TanStack/table/blob/main/packag ### source ```ts -source: Atom | ReadonlyAtom; -``` - -Defined in: [Subscribe.ts:61](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L61) - -*** - -### table - -```ts -table: Table; +source: SubscribeSource; ``` -Defined in: [Subscribe.ts:60](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L60) +Defined in: [Subscribe.ts:55](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L55) diff --git a/docs/framework/react/reference/index/type-aliases/SubscribePropsWithStore.md b/docs/framework/react/reference/index/type-aliases/SubscribePropsWithStore.md index 2147a86ae3..56a55531c6 100644 --- a/docs/framework/react/reference/index/type-aliases/SubscribePropsWithStore.md +++ b/docs/framework/react/reference/index/type-aliases/SubscribePropsWithStore.md @@ -3,13 +3,13 @@ id: SubscribePropsWithStore title: SubscribePropsWithStore --- -# Type Alias: SubscribePropsWithStore\ +# Type Alias: SubscribePropsWithStore\ ```ts -type SubscribePropsWithStore = object; +type SubscribePropsWithStore = object; ``` -Defined in: [Subscribe.ts:17](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L17) +Defined in: [Subscribe.ts:23](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L23) Subscribe to `table.store` (full table state). The selector receives the full TableState. @@ -20,10 +20,6 @@ TableState. `TFeatures` *extends* `TableFeatures` -### TData - -`TData` *extends* `RowData` - ### TSelected `TSelected` @@ -36,7 +32,7 @@ TableState. children: (state) => ReactNode | ReactNode; ``` -Defined in: [Subscribe.ts:31](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L31) +Defined in: [Subscribe.ts:36](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L36) *** @@ -46,7 +42,7 @@ Defined in: [Subscribe.ts:31](https://github.com/TanStack/table/blob/main/packag selector: (state) => TSelected; ``` -Defined in: [Subscribe.ts:30](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L30) +Defined in: [Subscribe.ts:35](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L35) Select from full table state. Re-renders when the selected value changes (shallow compare). @@ -66,10 +62,10 @@ store without an explicit projection. *** -### table +### source ```ts -table: Table; +source: SubscribeSource>; ``` -Defined in: [Subscribe.ts:22](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L22) +Defined in: [Subscribe.ts:27](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L27) diff --git a/docs/framework/react/reference/index/type-aliases/SubscribeSource.md b/docs/framework/react/reference/index/type-aliases/SubscribeSource.md new file mode 100644 index 0000000000..ed0adc6acc --- /dev/null +++ b/docs/framework/react/reference/index/type-aliases/SubscribeSource.md @@ -0,0 +1,22 @@ +--- +id: SubscribeSource +title: SubscribeSource +--- + +# Type Alias: SubscribeSource\ + +```ts +type SubscribeSource = + | Atom + | ReadonlyAtom + | Store +| ReadonlyStore; +``` + +Defined in: [Subscribe.ts:13](https://github.com/TanStack/table/blob/main/packages/react-table/src/Subscribe.ts#L13) + +## Type Parameters + +### TValue + +`TValue` diff --git a/examples/preact/basic-subscribe/index.html b/examples/preact/basic-subscribe/index.html new file mode 100644 index 0000000000..fff0f71352 --- /dev/null +++ b/examples/preact/basic-subscribe/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + Preact + + +
+ + + diff --git a/examples/preact/basic-subscribe/package.json b/examples/preact/basic-subscribe/package.json new file mode 100644 index 0000000000..6dfbee7d66 --- /dev/null +++ b/examples/preact/basic-subscribe/package.json @@ -0,0 +1,26 @@ +{ + "name": "tanstack-preact-table-example-basic-subscribe", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "serve": "vite preview", + "start": "vite", + "lint": "eslint ./src", + "test:types": "tsc" + }, + "dependencies": { + "@faker-js/faker": "^10.4.0", + "@tanstack/preact-store": "^0.13.0", + "@tanstack/preact-table": "^9.0.0-alpha.44", + "preact": "^10.29.1" + }, + "devDependencies": { + "@preact/preset-vite": "^2.10.5", + "@tanstack/preact-devtools": "^0.10.2", + "@tanstack/preact-table-devtools": "^9.0.0-alpha.43", + "typescript": "6.0.3", + "vite": "^8.0.10" + } +} diff --git a/examples/preact/basic-subscribe/src/index.css b/examples/preact/basic-subscribe/src/index.css new file mode 100644 index 0000000000..0a099f6d9f --- /dev/null +++ b/examples/preact/basic-subscribe/src/index.css @@ -0,0 +1,372 @@ +html { + font-family: sans-serif; + font-size: 14px; +} + +table { + border: 1px solid lightgray; + border-collapse: collapse; + border-spacing: 0; +} + +tbody { + border-bottom: 1px solid lightgray; +} + +th { + border-bottom: 1px solid lightgray; + border-right: 1px solid lightgray; + padding: 2px 4px; +} + +tfoot { + color: gray; +} + +tfoot th { + font-weight: normal; +} + +button:disabled, +button[disabled] { + opacity: 0.5; + cursor: not-allowed; + pointer-events: none; +} + +/* Demo layout utilities for plain example styling. */ +.demo-root { + padding: 0.5rem; +} + +.spacer-xs { + height: 0.25rem; +} + +.spacer-sm { + height: 0.5rem; +} + +.spacer-md { + height: 1rem; +} + +.controls, +.button-row, +.inline-controls, +.pin-actions, +.filter-row, +.form-actions { + display: flex; + align-items: center; +} + +.button-row { + flex-wrap: wrap; + gap: 0.5rem; +} + +.controls { + gap: 0.5rem; +} + +.inline-controls, +.pin-actions { + gap: 0.25rem; +} + +.pin-actions { + justify-content: center; +} + +.filter-row { + gap: 0.5rem; +} + +.form-actions { + gap: 1rem; + margin-bottom: 1rem; +} + +.split-tables { + display: flex; + gap: 1rem; +} + +.table-row-group { + display: flex; + border-collapse: collapse; + border-spacing: 0; +} + +.vertical-options { + display: flex; + flex-direction: column; + gap: 0.5rem; + align-items: center; +} + +.column-toggle-panel { + display: inline-block; + border: 1px solid #000; + border-radius: 0.25rem; + box-shadow: 0 1px 3px rgb(0 0 0 / 0.2); +} + +.column-toggle-panel-header { + border-bottom: 1px solid #000; + padding: 0 0.25rem; +} + +.column-toggle-row, +.selection-cell { + padding: 0 0.25rem; +} + +.selection-cell { + display: block; +} + +.demo-button, +.pin-button, +.compact-input, +.filter-input, +.filter-select, +.page-size-input, +.text-input, +.number-input, +.wide-action-button, +.primary-action, +.secondary-action, +.success-action { + border: 1px solid currentColor; + border-radius: 0.25rem; +} + +.demo-button { + padding: 0.5rem; +} + +.demo-button-sm { + padding: 0.25rem; +} + +.demo-button-spaced { + margin-bottom: 0.5rem; +} + +.pin-button { + padding: 0 0.5rem; +} + +.outlined-table { + border: 2px solid #000; + border-collapse: collapse; + border-spacing: 0; +} + +.outlined-control { + border-color: #000; +} + +.nowrap { + white-space: nowrap; +} + +.demo-note { + margin-bottom: 0.5rem; + font-size: 0.875rem; +} + +.section-title { + font-size: 1.25rem; +} + +.scroll-container { + overflow-x: auto; +} + +.page-size-input { + width: 4rem; + padding: 0.25rem; +} + +.number-input { + width: 5rem; + padding: 0 0.25rem; +} + +.filter-input, +.filter-select { + width: 6rem; + box-shadow: 0 1px 3px rgb(0 0 0 / 0.2); +} + +.filter-select { + width: 9rem; +} + +.text-input { + width: 100%; + padding: 0 0.25rem; +} + +.compact-input { + padding: 0 0.25rem; +} + +.wide-action-button { + width: 16rem; +} + +.summary-panel { + border: 1px solid currentColor; + box-shadow: 0 1px 3px rgb(0 0 0 / 0.2); + padding: 0.5rem; +} + +.sortable-header, +.sortable { + cursor: pointer; + user-select: none; +} + +.primary-action, +.success-action, +.secondary-action { + color: #fff; +} + +.primary-action { + background: #3b82f6; +} + +.success-action { + background: #22c55e; +} + +.secondary-action { + background: #6b7280; +} + +.submit-button:disabled { + opacity: 0.5; +} + +.error-text { + color: #ef4444; + font-size: 0.75rem; +} + +.success-text { + color: #16a34a; +} + +.warning-text { + color: #ca8a04; +} + +.muted-text { + color: #9ca3af; +} + +.label-offset { + margin-left: 0.5rem; +} + +.cell-padding { + padding: 0.25rem; +} + +.table-spacer { + margin-bottom: 0.5rem; + border-collapse: collapse; + border-spacing: 0; +} + +.warning-panel { + margin-bottom: 1rem; + border: 1px solid #facc15; + border-radius: 0.25rem; + background: #fef9c3; + padding: 0.5rem; +} + +.code-block { + overflow: auto; + border-radius: 0.25rem; + background: #f3f4f6; + padding: 0.5rem; + font-size: 0.75rem; +} + +.router-root { + display: flex; + flex-direction: column; + gap: 0.5rem; + padding: 0.5rem; +} + +.page-title { + margin-bottom: 0.25rem; + font-size: 1.5rem; + font-weight: 600; +} + +.disabled-button:disabled { + color: #6b7280; + cursor: not-allowed; +} + +.pagination-controls { + margin-block: 0.5rem; +} + +.column-size-input { + width: 6rem; + margin-left: 0.5rem; + border: 1px solid currentColor; + border-radius: 0.25rem; + padding: 0.25rem; +} + +.form-status { + display: flex; + gap: 1rem; + font-size: 0.875rem; +} + +.centered-button-row { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.5rem; +} + +.split-gap { + gap: 1rem; +} + +.demo-link { + color: #2563eb; + text-decoration: underline; +} + +.capitalized-text { + text-transform: capitalize; +} + +.centered-text { + text-align: center; +} + +.centered-strong-text { + text-align: center; + font-weight: 600; +} + +.virtualized-title { + text-align: center; + font-size: 1.875rem; + font-weight: 700; +} diff --git a/examples/preact/basic-subscribe/src/main.tsx b/examples/preact/basic-subscribe/src/main.tsx new file mode 100644 index 0000000000..167ad3b406 --- /dev/null +++ b/examples/preact/basic-subscribe/src/main.tsx @@ -0,0 +1,478 @@ +import { useEffect, useMemo, useReducer, useRef, useState } from 'preact/hooks' +import { render } from 'preact' +import { + Subscribe, + columnFilteringFeature, + createColumnHelper, + createFilteredRowModel, + createPaginatedRowModel, + filterFns, + globalFilteringFeature, + rowPaginationFeature, + rowSelectionFeature, + tableFeatures, + useTable, +} from '@tanstack/preact-table' +import { + tableDevtoolsPlugin, + useTanStackTableDevtools, +} from '@tanstack/preact-table-devtools' +import { TanStackDevtools } from '@tanstack/preact-devtools' +import { useCreateAtom } from '@tanstack/preact-store' +import { makeData } from './makeData' +import type { + Column, + PreactTable, + RowSelectionState, +} from '@tanstack/preact-table' +import type { JSX } from 'preact' +import type { Person } from './makeData' +import './index.css' + +const _features = tableFeatures({ + rowPaginationFeature, + rowSelectionFeature, + columnFilteringFeature, + globalFilteringFeature, +}) + +const columnHelper = createColumnHelper() + +/** + * This is an example showing how to use advanced re-rendering optimizations with more fine-grained control over what is subscribed to. + * Subscribe/table.Subscribe is a higher-order component that allows you to subscribe to the table state or individual atoms/stores. + * This is useful for making sure that re-renders only happen at certain parts of the preact tree exactly where need to be. + * We recommend only using these patterns when you run into specific performance issues. + */ +function App() { + const rerender = useReducer(() => ({}), {})[1] + + const columns = useMemo( + () => + columnHelper.columns([ + columnHelper.display({ + id: 'select', + header: ({ table }) => { + return ( + // just import Subscribe component if "preact" table is not available in scope and pass in the table store as source + ({ + columnFilters: state.columnFilters, + globalFilter: state.globalFilter, + rowSelection: state.rowSelection, + })} + > + {() => ( + + )} + + ) + }, + cell: ({ row }) => ( + rowSelection[row.id]} // optimize to only re-render when the row selection changes for this row + > + {(isRowSelected) => ( +
+ {/* Select only this row's selection value so toggling one row only re-renders that row's checkbox. */} + +
+ )} +
+ ), + }), + columnHelper.accessor('firstName', { + header: 'First Name', + cell: (info) => info.getValue(), + footer: (props) => props.column.id, + }), + columnHelper.accessor((row) => row.lastName, { + id: 'lastName', + header: () => Last Name, + cell: (info) => info.getValue(), + footer: (props) => props.column.id, + }), + columnHelper.accessor('age', { + header: () => 'Age', + footer: (props) => props.column.id, + }), + columnHelper.accessor('visits', { + header: () => Visits, + footer: (props) => props.column.id, + }), + columnHelper.accessor('status', { + header: 'Status', + footer: (props) => props.column.id, + }), + columnHelper.accessor('progress', { + header: 'Profile Progress', + footer: (props) => props.column.id, + }), + ]), + [], + ) + + const [data, setData] = useState(() => makeData(1_000)) + const refreshData = () => setData(() => makeData(1_000)) + const stressTest = () => setData(() => makeData(200_000)) + + // optionally, raise the selection state to your own atom + const rowSelectionAtom = useCreateAtom({}) + + const table = useTable( + { + _features, + _rowModels: { + filteredRowModel: createFilteredRowModel(filterFns), + paginatedRowModel: createPaginatedRowModel(), + }, + atoms: { + rowSelection: rowSelectionAtom, + }, + columns, + data, + getRowId: (row) => row.id, + enableRowSelection: true, // enable row selection for all rows + // enableRowSelection: row => row.original.age > 18, // or enable row selection conditionally per row + debugTable: true, + }, + () => null, // subscribe to no table state by default; use table.Subscribe below for targeted updates + ) + + useTanStackTableDevtools(table, 'Basic Subscribe Example') + + return ( +
+
+ + +
+
+ + {(globalFilter) => ( + + table.setGlobalFilter((e.target as HTMLInputElement).value) + } + className="summary-panel" + placeholder="Search all columns..." + /> + )} + +
+
+
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + ) + })} + + ))} + + {/* Subscribe the row model to filtering and pagination only. Row selection is handled per row below. */} + ({ + columnFilters: state.columnFilters, + globalFilter: state.globalFilter, + pagination: state.pagination, + })} + > + {() => ( + <> + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + ) + })} + + ) + })} + + + + + + + + + )} + +
+ {header.isPlaceholder ? null : ( + <> + + {header.column.getCanFilter() ? ( +
+ +
+ ) : null} + + )} +
+ +
+ + {() => ( + + )} + + + Page Rows ( + {table.getRowModel().rows.length.toLocaleString()}) +
+
+ ({ + columnFilters: state.columnFilters, + globalFilter: state.globalFilter, + pagination: state.pagination, + })} + > + {({ pagination }) => ( +
+ + + + + +
Page
+ + {(pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const value = (e.target as HTMLInputElement).value + const page = value ? Number(value) - 1 : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+ )} +
+
+ + {(rowSelection) => ( +
+ <>{Object.keys(rowSelection).length.toLocaleString()} of + {table.getPreFilteredRowModel().rows.length.toLocaleString()} Total + Rows Selected +
+ )} +
+
+
+
+ +
+
+ +
+
+ + {/* subscribe to the entire table state */} + state}> + {(state) =>
{JSON.stringify(state, null, 2)}
} +
+
+
+ ) +} + +function Filter({ + column, + table, +}: { + column: Column + table: PreactTable +}) { + const firstValue = table + .getPreFilteredRowModel() + .flatRows[0]?.getValue(column.id) + + return ( + + {() => + typeof firstValue === 'number' ? ( +
+ + column.setFilterValue((old: any) => [ + (e.target as HTMLInputElement).value, + old?.[1], + ]) + } + placeholder={`Min`} + className="filter-input" + /> + + column.setFilterValue((old: any) => [ + old?.[0], + (e.target as HTMLInputElement).value, + ]) + } + placeholder={`Max`} + className="filter-input" + /> +
+ ) : ( + + column.setFilterValue((e.target as HTMLInputElement).value) + } + placeholder={`Search...`} + className="filter-select" + /> + ) + } +
+ ) +} + +function IndeterminateCheckbox({ + indeterminate, + className = '', + checked, + onChange, + disabled, + ...rest +}: { + indeterminate?: boolean + checked?: boolean + disabled?: boolean + onChange?: (event: Event) => void +} & Record) { + const ref = useRef(null!) + + useEffect(() => { + if (typeof indeterminate === 'boolean') { + ref.current.indeterminate = !checked && indeterminate + } + }, [ref, indeterminate, checked]) + + return ( + + ) +} + +const rootElement = document.getElementById('root') +if (!rootElement) throw new Error('Failed to find the root element') + +render( + <> + + + , + rootElement, +) diff --git a/examples/preact/basic-subscribe/src/makeData.ts b/examples/preact/basic-subscribe/src/makeData.ts new file mode 100644 index 0000000000..c34c43a03e --- /dev/null +++ b/examples/preact/basic-subscribe/src/makeData.ts @@ -0,0 +1,50 @@ +import { faker } from '@faker-js/faker' + +export type Person = { + id: string + firstName: string + lastName: string + age: number + visits: number + progress: number + status: 'relationship' | 'complicated' | 'single' + subRows?: Array +} + +const range = (len: number) => { + const arr: Array = [] + for (let i = 0; i < len; i++) { + arr.push(i) + } + return arr +} + +const newPerson = (): Person => { + return { + id: faker.string.uuid(), + 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: Array) { + const makeDataLevel = (depth = 0): Array => { + 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/preact/basic-subscribe/src/vite-env.d.ts b/examples/preact/basic-subscribe/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/preact/basic-subscribe/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/preact/basic-subscribe/tsconfig.json b/examples/preact/basic-subscribe/tsconfig.json new file mode 100644 index 0000000000..c1e0bb7b6c --- /dev/null +++ b/examples/preact/basic-subscribe/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "strictNullChecks": true, + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "noEmit": true, + "allowJs": true, + "checkJs": true, + "jsx": "react-jsx", + "jsxImportSource": "preact", + "skipLibCheck": true, + "paths": { + "react": ["./node_modules/preact/compat/"], + "react-dom": ["./node_modules/preact/compat/"] + } + }, + "include": [ + "node_modules/vite/client.d.ts", + "src/**/*", + "vite.config.js", + "vite.config.ts" + ], + "exclude": ["dist/**/*", "node_modules"] +} diff --git a/examples/preact/basic-subscribe/vite.config.ts b/examples/preact/basic-subscribe/vite.config.ts new file mode 100644 index 0000000000..c16c7375d0 --- /dev/null +++ b/examples/preact/basic-subscribe/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import preact from '@preact/preset-vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [preact({ babel: {} })], +}) diff --git a/examples/react/basic-subscribe/.gitignore b/examples/react/basic-subscribe/.gitignore new file mode 100644 index 0000000000..d451ff16c1 --- /dev/null +++ b/examples/react/basic-subscribe/.gitignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local diff --git a/examples/react/basic-subscribe/README.md b/examples/react/basic-subscribe/README.md new file mode 100644 index 0000000000..89dfe13876 --- /dev/null +++ b/examples/react/basic-subscribe/README.md @@ -0,0 +1,6 @@ +# Basic Subscribe Example + +To run this example: + +- `pnpm install` +- `pnpm start` diff --git a/examples/react/basic-subscribe/index.html b/examples/react/basic-subscribe/index.html new file mode 100644 index 0000000000..1551290f72 --- /dev/null +++ b/examples/react/basic-subscribe/index.html @@ -0,0 +1,13 @@ + + + + + + Vite App + + + +
+ + + diff --git a/examples/react/basic-subscribe/package.json b/examples/react/basic-subscribe/package.json new file mode 100644 index 0000000000..b6b52cc31d --- /dev/null +++ b/examples/react/basic-subscribe/package.json @@ -0,0 +1,31 @@ +{ + "name": "tanstack-react-table-example-basic-subscribe", + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build", + "serve": "vite preview", + "start": "vite", + "lint": "eslint ./src", + "test:types": "tsc" + }, + "dependencies": { + "@faker-js/faker": "^10.4.0", + "@tanstack/react-store": "^0.11.0", + "@tanstack/react-table": "^9.0.0-alpha.44", + "react": "^19.2.5", + "react-dom": "^19.2.5" + }, + "devDependencies": { + "@rolldown/plugin-babel": "^0.2.3", + "@rollup/plugin-replace": "^6.0.3", + "@tanstack/react-devtools": "^0.10.2", + "@tanstack/react-table-devtools": "^9.0.0-alpha.43", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "babel-plugin-react-compiler": "^1.0.0", + "typescript": "6.0.3", + "vite": "^8.0.10" + } +} diff --git a/examples/react/basic-subscribe/src/index.css b/examples/react/basic-subscribe/src/index.css new file mode 100644 index 0000000000..0a099f6d9f --- /dev/null +++ b/examples/react/basic-subscribe/src/index.css @@ -0,0 +1,372 @@ +html { + font-family: sans-serif; + font-size: 14px; +} + +table { + border: 1px solid lightgray; + border-collapse: collapse; + border-spacing: 0; +} + +tbody { + border-bottom: 1px solid lightgray; +} + +th { + border-bottom: 1px solid lightgray; + border-right: 1px solid lightgray; + padding: 2px 4px; +} + +tfoot { + color: gray; +} + +tfoot th { + font-weight: normal; +} + +button:disabled, +button[disabled] { + opacity: 0.5; + cursor: not-allowed; + pointer-events: none; +} + +/* Demo layout utilities for plain example styling. */ +.demo-root { + padding: 0.5rem; +} + +.spacer-xs { + height: 0.25rem; +} + +.spacer-sm { + height: 0.5rem; +} + +.spacer-md { + height: 1rem; +} + +.controls, +.button-row, +.inline-controls, +.pin-actions, +.filter-row, +.form-actions { + display: flex; + align-items: center; +} + +.button-row { + flex-wrap: wrap; + gap: 0.5rem; +} + +.controls { + gap: 0.5rem; +} + +.inline-controls, +.pin-actions { + gap: 0.25rem; +} + +.pin-actions { + justify-content: center; +} + +.filter-row { + gap: 0.5rem; +} + +.form-actions { + gap: 1rem; + margin-bottom: 1rem; +} + +.split-tables { + display: flex; + gap: 1rem; +} + +.table-row-group { + display: flex; + border-collapse: collapse; + border-spacing: 0; +} + +.vertical-options { + display: flex; + flex-direction: column; + gap: 0.5rem; + align-items: center; +} + +.column-toggle-panel { + display: inline-block; + border: 1px solid #000; + border-radius: 0.25rem; + box-shadow: 0 1px 3px rgb(0 0 0 / 0.2); +} + +.column-toggle-panel-header { + border-bottom: 1px solid #000; + padding: 0 0.25rem; +} + +.column-toggle-row, +.selection-cell { + padding: 0 0.25rem; +} + +.selection-cell { + display: block; +} + +.demo-button, +.pin-button, +.compact-input, +.filter-input, +.filter-select, +.page-size-input, +.text-input, +.number-input, +.wide-action-button, +.primary-action, +.secondary-action, +.success-action { + border: 1px solid currentColor; + border-radius: 0.25rem; +} + +.demo-button { + padding: 0.5rem; +} + +.demo-button-sm { + padding: 0.25rem; +} + +.demo-button-spaced { + margin-bottom: 0.5rem; +} + +.pin-button { + padding: 0 0.5rem; +} + +.outlined-table { + border: 2px solid #000; + border-collapse: collapse; + border-spacing: 0; +} + +.outlined-control { + border-color: #000; +} + +.nowrap { + white-space: nowrap; +} + +.demo-note { + margin-bottom: 0.5rem; + font-size: 0.875rem; +} + +.section-title { + font-size: 1.25rem; +} + +.scroll-container { + overflow-x: auto; +} + +.page-size-input { + width: 4rem; + padding: 0.25rem; +} + +.number-input { + width: 5rem; + padding: 0 0.25rem; +} + +.filter-input, +.filter-select { + width: 6rem; + box-shadow: 0 1px 3px rgb(0 0 0 / 0.2); +} + +.filter-select { + width: 9rem; +} + +.text-input { + width: 100%; + padding: 0 0.25rem; +} + +.compact-input { + padding: 0 0.25rem; +} + +.wide-action-button { + width: 16rem; +} + +.summary-panel { + border: 1px solid currentColor; + box-shadow: 0 1px 3px rgb(0 0 0 / 0.2); + padding: 0.5rem; +} + +.sortable-header, +.sortable { + cursor: pointer; + user-select: none; +} + +.primary-action, +.success-action, +.secondary-action { + color: #fff; +} + +.primary-action { + background: #3b82f6; +} + +.success-action { + background: #22c55e; +} + +.secondary-action { + background: #6b7280; +} + +.submit-button:disabled { + opacity: 0.5; +} + +.error-text { + color: #ef4444; + font-size: 0.75rem; +} + +.success-text { + color: #16a34a; +} + +.warning-text { + color: #ca8a04; +} + +.muted-text { + color: #9ca3af; +} + +.label-offset { + margin-left: 0.5rem; +} + +.cell-padding { + padding: 0.25rem; +} + +.table-spacer { + margin-bottom: 0.5rem; + border-collapse: collapse; + border-spacing: 0; +} + +.warning-panel { + margin-bottom: 1rem; + border: 1px solid #facc15; + border-radius: 0.25rem; + background: #fef9c3; + padding: 0.5rem; +} + +.code-block { + overflow: auto; + border-radius: 0.25rem; + background: #f3f4f6; + padding: 0.5rem; + font-size: 0.75rem; +} + +.router-root { + display: flex; + flex-direction: column; + gap: 0.5rem; + padding: 0.5rem; +} + +.page-title { + margin-bottom: 0.25rem; + font-size: 1.5rem; + font-weight: 600; +} + +.disabled-button:disabled { + color: #6b7280; + cursor: not-allowed; +} + +.pagination-controls { + margin-block: 0.5rem; +} + +.column-size-input { + width: 6rem; + margin-left: 0.5rem; + border: 1px solid currentColor; + border-radius: 0.25rem; + padding: 0.25rem; +} + +.form-status { + display: flex; + gap: 1rem; + font-size: 0.875rem; +} + +.centered-button-row { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.5rem; +} + +.split-gap { + gap: 1rem; +} + +.demo-link { + color: #2563eb; + text-decoration: underline; +} + +.capitalized-text { + text-transform: capitalize; +} + +.centered-text { + text-align: center; +} + +.centered-strong-text { + text-align: center; + font-weight: 600; +} + +.virtualized-title { + text-align: center; + font-size: 1.875rem; + font-weight: 700; +} diff --git a/examples/react/basic-subscribe/src/main.tsx b/examples/react/basic-subscribe/src/main.tsx new file mode 100644 index 0000000000..f4fd2d04cb --- /dev/null +++ b/examples/react/basic-subscribe/src/main.tsx @@ -0,0 +1,489 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import { + Subscribe, + columnFilteringFeature, + createColumnHelper, + createFilteredRowModel, + createPaginatedRowModel, + filterFns, + globalFilteringFeature, + rowPaginationFeature, + rowSelectionFeature, + tableFeatures, + useTable, +} from '@tanstack/react-table' +import { + tableDevtoolsPlugin, + useTanStackTableDevtools, +} from '@tanstack/react-table-devtools' +import { TanStackDevtools } from '@tanstack/react-devtools' +import { useCreateAtom } from '@tanstack/react-store' +import { makeData } from './makeData' +import type { HTMLProps } from 'react' +import type { Person } from './makeData' +import type { + Column, + ReactTable, + RowSelectionState, +} from '@tanstack/react-table' +import './index.css' + +const _features = tableFeatures({ + rowPaginationFeature, + rowSelectionFeature, + columnFilteringFeature, + globalFilteringFeature, +}) + +const columnHelper = createColumnHelper() + +/** + * This is an example showing how to use advanced re-rendering optimizations with more fine-grained control over what is subscribed to. + * Subscribe/table.Subscribe is a higher-order component that allows you to subscribe to the table state or individual atoms/stores. + * This is useful for making sure that re-renders only happen at certain parts of the react tree exactly where need to be. + * We recommend only using these patterns when you run into specific performance issues. + */ +function App() { + const rerender = React.useReducer(() => ({}), {})[1] + + const columns = React.useMemo( + () => + columnHelper.columns([ + columnHelper.display({ + id: 'select', + header: ({ table }) => { + return ( + // just import Subscribe component if "react" table is not available in scope and pass in the table store as source + ({ + columnFilters: state.columnFilters, + globalFilter: state.globalFilter, + rowSelection: state.rowSelection, + })} + > + {() => ( + + )} + + ) + }, + cell: ({ row }) => ( + rowSelection[row.id]} // optimize to only re-render when the row selection changes for this row + > + {(isRowSelected) => ( +
+ {/* Select only this row's selection value so toggling one row only re-renders that row's checkbox. */} + +
+ )} +
+ ), + }), + columnHelper.accessor('firstName', { + header: 'First Name', + cell: (info) => info.getValue(), + footer: (props) => props.column.id, + }), + columnHelper.accessor((row) => row.lastName, { + id: 'lastName', + header: () => Last Name, + cell: (info) => info.getValue(), + footer: (props) => props.column.id, + }), + columnHelper.accessor('age', { + header: () => 'Age', + footer: (props) => props.column.id, + }), + columnHelper.accessor('visits', { + header: () => Visits, + footer: (props) => props.column.id, + }), + columnHelper.accessor('status', { + header: 'Status', + footer: (props) => props.column.id, + }), + columnHelper.accessor('progress', { + header: 'Profile Progress', + footer: (props) => props.column.id, + }), + ]), + [], + ) + + const [data, setData] = React.useState(() => makeData(1_000)) + const refreshData = () => setData(makeData(1_000)) + const stressTest = () => setData(makeData(200_000)) + + // optionally, raise the selection state to your own atom + const rowSelectionAtom = useCreateAtom({}) + + const table = useTable( + { + _features, + _rowModels: { + filteredRowModel: createFilteredRowModel(filterFns), + paginatedRowModel: createPaginatedRowModel(), + }, + atoms: { + rowSelection: rowSelectionAtom, + }, + columns, + data, + getRowId: (row) => row.id, + enableRowSelection: true, // enable row selection for all rows + // enableRowSelection: row => row.original.age > 18, // or enable row selection conditionally per row + debugTable: true, + }, + () => null, // subscribe to no table state by default; use table.Subscribe below for targeted updates + ) + + useTanStackTableDevtools(table, 'Basic Subscribe Example') + + return ( +
+
+ + +
+
+ + {(globalFilter) => ( + table.setGlobalFilter(value)} + className="summary-panel" + placeholder="Search all columns..." + /> + )} + +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + ) + })} + + ))} + + {/* Subscribe the row model to filtering and pagination only. Row selection is handled per row below. */} + ({ + columnFilters: state.columnFilters, + globalFilter: state.globalFilter, + pagination: state.pagination, + })} + > + {() => ( + <> + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getAllCells().map((cell) => { + return ( + + ) + })} + + ) + })} + + + + + + + + + )} + +
+ {header.isPlaceholder ? null : ( + <> + + {header.column.getCanFilter() ? ( +
+ +
+ ) : null} + + )} +
+ +
+ + {() => ( + + )} + + + Page Rows ( + {table.getRowModel().rows.length.toLocaleString()}) +
+
+ ({ + columnFilters: state.columnFilters, + globalFilter: state.globalFilter, + pagination: state.pagination, + })} + > + {({ pagination }) => ( +
+ + + + + +
Page
+ + {(pagination.pageIndex + 1).toLocaleString()} of{' '} + {table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const page = e.target.value ? Number(e.target.value) - 1 : 0 + table.setPageIndex(page) + }} + className="page-size-input" + /> + + +
+ )} +
+
+ + {(rowSelection) => ( +
+ <>{Object.keys(rowSelection).length.toLocaleString()} of + {table.getPreFilteredRowModel().rows.length.toLocaleString()} Total + Rows Selected +
+ )} +
+
+
+
+ +
+
+ +
+
+ + {/* subscribe to the entire table state */} + state}> + {(state) =>
{JSON.stringify(state, null, 2)}
} +
+
+
+ ) +} + +function Filter({ + column, + table, +}: { + column: Column + table: ReactTable +}) { + const firstValue = table + .getPreFilteredRowModel() + .flatRows[0]?.getValue(column.id) + + return ( + + {() => + typeof firstValue === 'number' ? ( +
+ + column.setFilterValue((old: any) => [value, old?.[1]]) + } + placeholder={`Min`} + className="filter-input" + /> + + column.setFilterValue((old: any) => [old?.[0], value]) + } + placeholder={`Max`} + className="filter-input" + /> +
+ ) : ( + column.setFilterValue(value)} + placeholder={`Search...`} + className="filter-select" + /> + ) + } +
+ ) +} + +// A debounced input react component +function DebouncedInput({ + value: initialValue, + onChange, + debounce = 500, + ...props +}: { + value: string | number + onChange: (value: string | number) => void + debounce?: number +} & Omit, 'onChange'>) { + const [value, setValue] = React.useState(initialValue) + + React.useEffect(() => { + setValue(initialValue) + }, [initialValue]) + + React.useEffect(() => { + const timeout = setTimeout(() => { + onChange(value) + }, debounce) + + return () => clearTimeout(timeout) + }, [value]) + + return ( + setValue(e.target.value)} + /> + ) +} + +function IndeterminateCheckbox({ + indeterminate, + className = '', + ...rest +}: { indeterminate?: boolean } & HTMLProps) { + const ref = React.useRef(null!) + + React.useEffect(() => { + if (typeof indeterminate === 'boolean') { + ref.current.indeterminate = !rest.checked && indeterminate + } + }, [ref, indeterminate]) + + return ( + + ) +} + +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/basic-subscribe/src/makeData.ts b/examples/react/basic-subscribe/src/makeData.ts new file mode 100644 index 0000000000..c34c43a03e --- /dev/null +++ b/examples/react/basic-subscribe/src/makeData.ts @@ -0,0 +1,50 @@ +import { faker } from '@faker-js/faker' + +export type Person = { + id: string + firstName: string + lastName: string + age: number + visits: number + progress: number + status: 'relationship' | 'complicated' | 'single' + subRows?: Array +} + +const range = (len: number) => { + const arr: Array = [] + for (let i = 0; i < len; i++) { + arr.push(i) + } + return arr +} + +const newPerson = (): Person => { + return { + id: faker.string.uuid(), + 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: Array) { + const makeDataLevel = (depth = 0): Array => { + 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/basic-subscribe/src/vite-env.d.ts b/examples/react/basic-subscribe/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/react/basic-subscribe/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/react/basic-subscribe/tsconfig.json b/examples/react/basic-subscribe/tsconfig.json new file mode 100644 index 0000000000..33a3e872be --- /dev/null +++ b/examples/react/basic-subscribe/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "allowJs": true + }, + "include": ["src", "vite.config.js", "vite.config.ts"] +} diff --git a/examples/react/basic-subscribe/vite.config.js b/examples/react/basic-subscribe/vite.config.js new file mode 100644 index 0000000000..1755f6bea8 --- /dev/null +++ b/examples/react/basic-subscribe/vite.config.js @@ -0,0 +1,23 @@ +import { defineConfig } from 'vite' +import react, { reactCompilerPreset } from '@vitejs/plugin-react' +import babel from '@rolldown/plugin-babel' +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(), + // React Compiler - comment out the next line to disable + babel({ + presets: [reactCompilerPreset()], + include: [/\/src\/.*\.[jt]sx?$/], + }), + ], +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a02ce288d7..0ef3d7b3eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1660,6 +1660,37 @@ importers: specifier: ^8.0.10 version: 8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3) + examples/preact/basic-subscribe: + dependencies: + '@faker-js/faker': + specifier: ^10.4.0 + version: 10.4.0 + '@tanstack/preact-store': + specifier: ^0.13.0 + version: 0.13.0(preact@10.29.1) + '@tanstack/preact-table': + specifier: ^9.0.0-alpha.44 + version: link:../../../packages/preact-table + preact: + specifier: ^10.29.1 + version: 10.29.1 + devDependencies: + '@preact/preset-vite': + specifier: ^2.10.5 + version: 2.10.5(@babel/core@7.29.0)(preact@10.29.1)(rollup@4.60.2)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)) + '@tanstack/preact-devtools': + specifier: ^0.10.2 + version: 0.10.2(preact@10.29.1)(solid-js@1.9.12) + '@tanstack/preact-table-devtools': + specifier: ^9.0.0-alpha.43 + version: link:../../../packages/preact-table-devtools + typescript: + specifier: 6.0.3 + version: 6.0.3 + vite: + specifier: ^8.0.10 + version: 8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3) + examples/preact/basic-use-app-table: dependencies: '@tanstack/preact-table': @@ -2304,6 +2335,55 @@ importers: specifier: ^8.0.10 version: 8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3) + examples/react/basic-subscribe: + dependencies: + '@faker-js/faker': + specifier: ^10.4.0 + version: 10.4.0 + '@tanstack/react-store': + specifier: ^0.11.0 + version: 0.11.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@tanstack/react-table': + specifier: ^9.0.0-alpha.44 + version: link:../../../packages/react-table + react: + specifier: ^19.2.5 + version: 19.2.5 + react-dom: + specifier: ^19.2.5 + version: 19.2.5(react@19.2.5) + devDependencies: + '@rolldown/plugin-babel': + specifier: ^0.2.3 + version: 0.2.3(@babel/core@7.29.0)(@babel/plugin-transform-runtime@7.29.0(@babel/core@7.29.0))(@babel/runtime@7.29.2)(rolldown@1.0.0-rc.17)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)) + '@rollup/plugin-replace': + specifier: ^6.0.3 + version: 6.0.3(rollup@4.60.2) + '@tanstack/react-devtools': + specifier: ^0.10.2 + version: 0.10.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(solid-js@1.9.12) + '@tanstack/react-table-devtools': + specifier: ^9.0.0-alpha.43 + version: link:../../../packages/react-table-devtools + '@types/react': + specifier: ^19.2.14 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.1(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.0)(@babel/plugin-transform-runtime@7.29.0(@babel/core@7.29.0))(@babel/runtime@7.29.2)(rolldown@1.0.0-rc.17)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)))(babel-plugin-react-compiler@1.0.0)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)) + babel-plugin-react-compiler: + specifier: ^1.0.0 + version: 1.0.0 + typescript: + specifier: 6.0.3 + version: 6.0.3 + vite: + specifier: ^8.0.10 + version: 8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3) + examples/react/basic-use-app-table: dependencies: '@tanstack/react-table':