@tanstack/table-core 9.0.0-alpha.47 → 9.0.0-alpha.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/core/cells/coreCellsFeature.utils.cjs +11 -8
- package/dist/core/cells/coreCellsFeature.utils.cjs.map +1 -1
- package/dist/core/cells/coreCellsFeature.utils.d.cts +11 -8
- package/dist/core/cells/coreCellsFeature.utils.d.ts +11 -8
- package/dist/core/cells/coreCellsFeature.utils.js +11 -8
- package/dist/core/cells/coreCellsFeature.utils.js.map +1 -1
- package/dist/core/columns/coreColumnsFeature.types.d.cts +7 -6
- package/dist/core/columns/coreColumnsFeature.types.d.ts +7 -6
- package/dist/core/columns/coreColumnsFeature.utils.cjs +37 -27
- package/dist/core/columns/coreColumnsFeature.utils.cjs.map +1 -1
- package/dist/core/columns/coreColumnsFeature.utils.d.cts +37 -27
- package/dist/core/columns/coreColumnsFeature.utils.d.ts +37 -27
- package/dist/core/columns/coreColumnsFeature.utils.js +37 -27
- package/dist/core/columns/coreColumnsFeature.utils.js.map +1 -1
- package/dist/core/headers/coreHeadersFeature.types.d.cts +6 -4
- package/dist/core/headers/coreHeadersFeature.types.d.ts +6 -4
- package/dist/core/headers/coreHeadersFeature.utils.cjs +23 -18
- package/dist/core/headers/coreHeadersFeature.utils.cjs.map +1 -1
- package/dist/core/headers/coreHeadersFeature.utils.d.cts +23 -18
- package/dist/core/headers/coreHeadersFeature.utils.d.ts +23 -18
- package/dist/core/headers/coreHeadersFeature.utils.js +23 -18
- package/dist/core/headers/coreHeadersFeature.utils.js.map +1 -1
- package/dist/core/row-models/coreRowModelsFeature.utils.cjs +48 -36
- package/dist/core/row-models/coreRowModelsFeature.utils.cjs.map +1 -1
- package/dist/core/row-models/coreRowModelsFeature.utils.d.cts +48 -36
- package/dist/core/row-models/coreRowModelsFeature.utils.d.ts +48 -36
- package/dist/core/row-models/coreRowModelsFeature.utils.js +48 -36
- package/dist/core/row-models/coreRowModelsFeature.utils.js.map +1 -1
- package/dist/core/rows/coreRowsFeature.types.d.cts +4 -4
- package/dist/core/rows/coreRowsFeature.types.d.ts +4 -4
- package/dist/core/rows/coreRowsFeature.utils.cjs +34 -28
- package/dist/core/rows/coreRowsFeature.utils.cjs.map +1 -1
- package/dist/core/rows/coreRowsFeature.utils.d.cts +34 -28
- package/dist/core/rows/coreRowsFeature.utils.d.ts +34 -28
- package/dist/core/rows/coreRowsFeature.utils.js +34 -28
- package/dist/core/rows/coreRowsFeature.utils.js.map +1 -1
- package/dist/core/table/coreTablesFeature.types.d.cts +6 -2
- package/dist/core/table/coreTablesFeature.types.d.ts +6 -2
- package/dist/core/table/coreTablesFeature.utils.cjs +2 -2
- package/dist/core/table/coreTablesFeature.utils.cjs.map +1 -1
- package/dist/core/table/coreTablesFeature.utils.d.cts +2 -2
- package/dist/core/table/coreTablesFeature.utils.d.ts +2 -2
- package/dist/core/table/coreTablesFeature.utils.js +2 -2
- package/dist/core/table/coreTablesFeature.utils.js.map +1 -1
- package/dist/features/column-faceting/columnFacetingFeature.types.d.cts +16 -10
- package/dist/features/column-faceting/columnFacetingFeature.types.d.ts +16 -10
- package/dist/features/column-faceting/columnFacetingFeature.utils.cjs +26 -18
- package/dist/features/column-faceting/columnFacetingFeature.utils.cjs.map +1 -1
- package/dist/features/column-faceting/columnFacetingFeature.utils.d.cts +26 -18
- package/dist/features/column-faceting/columnFacetingFeature.utils.d.ts +26 -18
- package/dist/features/column-faceting/columnFacetingFeature.utils.js +26 -18
- package/dist/features/column-faceting/columnFacetingFeature.utils.js.map +1 -1
- package/dist/features/column-filtering/columnFilteringFeature.types.d.cts +21 -11
- package/dist/features/column-filtering/columnFilteringFeature.types.d.ts +21 -11
- package/dist/features/column-filtering/columnFilteringFeature.utils.cjs +39 -28
- package/dist/features/column-filtering/columnFilteringFeature.utils.cjs.map +1 -1
- package/dist/features/column-filtering/columnFilteringFeature.utils.d.cts +39 -28
- package/dist/features/column-filtering/columnFilteringFeature.utils.d.ts +39 -28
- package/dist/features/column-filtering/columnFilteringFeature.utils.js +39 -28
- package/dist/features/column-filtering/columnFilteringFeature.utils.js.map +1 -1
- package/dist/features/column-grouping/columnGroupingFeature.types.d.cts +18 -14
- package/dist/features/column-grouping/columnGroupingFeature.types.d.ts +18 -14
- package/dist/features/column-grouping/columnGroupingFeature.utils.cjs +55 -43
- package/dist/features/column-grouping/columnGroupingFeature.utils.cjs.map +1 -1
- package/dist/features/column-grouping/columnGroupingFeature.utils.d.cts +55 -43
- package/dist/features/column-grouping/columnGroupingFeature.utils.d.ts +55 -43
- package/dist/features/column-grouping/columnGroupingFeature.utils.js +55 -43
- package/dist/features/column-grouping/columnGroupingFeature.utils.js.map +1 -1
- package/dist/features/column-ordering/columnOrderingFeature.types.d.cts +14 -5
- package/dist/features/column-ordering/columnOrderingFeature.types.d.ts +14 -5
- package/dist/features/column-ordering/columnOrderingFeature.utils.cjs +30 -23
- package/dist/features/column-ordering/columnOrderingFeature.utils.cjs.map +1 -1
- package/dist/features/column-ordering/columnOrderingFeature.utils.d.cts +30 -23
- package/dist/features/column-ordering/columnOrderingFeature.utils.d.ts +30 -23
- package/dist/features/column-ordering/columnOrderingFeature.utils.js +30 -23
- package/dist/features/column-ordering/columnOrderingFeature.utils.js.map +1 -1
- package/dist/features/column-pinning/columnPinningFeature.types.d.cts +43 -31
- package/dist/features/column-pinning/columnPinningFeature.types.d.ts +43 -31
- package/dist/features/column-pinning/columnPinningFeature.utils.cjs +111 -91
- package/dist/features/column-pinning/columnPinningFeature.utils.cjs.map +1 -1
- package/dist/features/column-pinning/columnPinningFeature.utils.d.cts +111 -91
- package/dist/features/column-pinning/columnPinningFeature.utils.d.ts +111 -91
- package/dist/features/column-pinning/columnPinningFeature.utils.js +111 -91
- package/dist/features/column-pinning/columnPinningFeature.utils.js.map +1 -1
- package/dist/features/column-resizing/columnResizingFeature.types.d.cts +9 -9
- package/dist/features/column-resizing/columnResizingFeature.types.d.ts +9 -9
- package/dist/features/column-resizing/columnResizingFeature.utils.cjs +28 -19
- package/dist/features/column-resizing/columnResizingFeature.utils.cjs.map +1 -1
- package/dist/features/column-resizing/columnResizingFeature.utils.d.cts +28 -19
- package/dist/features/column-resizing/columnResizingFeature.utils.d.ts +28 -19
- package/dist/features/column-resizing/columnResizingFeature.utils.js +28 -19
- package/dist/features/column-resizing/columnResizingFeature.utils.js.map +1 -1
- package/dist/features/column-sizing/columnSizingFeature.types.d.cts +19 -12
- package/dist/features/column-sizing/columnSizingFeature.types.d.ts +19 -12
- package/dist/features/column-sizing/columnSizingFeature.utils.cjs +52 -41
- package/dist/features/column-sizing/columnSizingFeature.utils.cjs.map +1 -1
- package/dist/features/column-sizing/columnSizingFeature.utils.d.cts +52 -41
- package/dist/features/column-sizing/columnSizingFeature.utils.d.ts +52 -41
- package/dist/features/column-sizing/columnSizingFeature.utils.js +52 -41
- package/dist/features/column-sizing/columnSizingFeature.utils.js.map +1 -1
- package/dist/features/column-visibility/columnVisibilityFeature.types.d.cts +16 -13
- package/dist/features/column-visibility/columnVisibilityFeature.types.d.ts +16 -13
- package/dist/features/column-visibility/columnVisibilityFeature.utils.cjs +54 -42
- package/dist/features/column-visibility/columnVisibilityFeature.utils.cjs.map +1 -1
- package/dist/features/column-visibility/columnVisibilityFeature.utils.d.cts +54 -42
- package/dist/features/column-visibility/columnVisibilityFeature.utils.d.ts +54 -42
- package/dist/features/column-visibility/columnVisibilityFeature.utils.js +54 -42
- package/dist/features/column-visibility/columnVisibilityFeature.utils.js.map +1 -1
- package/dist/features/global-filtering/globalFilteringFeature.types.d.cts +10 -5
- package/dist/features/global-filtering/globalFilteringFeature.types.d.ts +10 -5
- package/dist/features/global-filtering/globalFilteringFeature.utils.cjs +20 -14
- package/dist/features/global-filtering/globalFilteringFeature.utils.cjs.map +1 -1
- package/dist/features/global-filtering/globalFilteringFeature.utils.d.cts +20 -14
- package/dist/features/global-filtering/globalFilteringFeature.utils.d.ts +20 -14
- package/dist/features/global-filtering/globalFilteringFeature.utils.js +20 -14
- package/dist/features/global-filtering/globalFilteringFeature.utils.js.map +1 -1
- package/dist/features/row-expanding/rowExpandingFeature.types.d.cts +16 -15
- package/dist/features/row-expanding/rowExpandingFeature.types.d.ts +16 -15
- package/dist/features/row-expanding/rowExpandingFeature.utils.cjs +56 -41
- package/dist/features/row-expanding/rowExpandingFeature.utils.cjs.map +1 -1
- package/dist/features/row-expanding/rowExpandingFeature.utils.d.cts +56 -41
- package/dist/features/row-expanding/rowExpandingFeature.utils.d.ts +56 -41
- package/dist/features/row-expanding/rowExpandingFeature.utils.js +56 -41
- package/dist/features/row-expanding/rowExpandingFeature.utils.js.map +1 -1
- package/dist/features/row-pagination/rowPaginationFeature.types.d.cts +19 -12
- package/dist/features/row-pagination/rowPaginationFeature.types.d.ts +19 -12
- package/dist/features/row-pagination/rowPaginationFeature.utils.cjs +49 -32
- package/dist/features/row-pagination/rowPaginationFeature.utils.cjs.map +1 -1
- package/dist/features/row-pagination/rowPaginationFeature.utils.d.cts +49 -32
- package/dist/features/row-pagination/rowPaginationFeature.utils.d.ts +49 -32
- package/dist/features/row-pagination/rowPaginationFeature.utils.js +49 -32
- package/dist/features/row-pagination/rowPaginationFeature.utils.js.map +1 -1
- package/dist/features/row-pinning/rowPinningFeature.types.d.cts +12 -8
- package/dist/features/row-pinning/rowPinningFeature.types.d.ts +12 -8
- package/dist/features/row-pinning/rowPinningFeature.utils.cjs +39 -29
- package/dist/features/row-pinning/rowPinningFeature.utils.cjs.map +1 -1
- package/dist/features/row-pinning/rowPinningFeature.utils.d.cts +39 -29
- package/dist/features/row-pinning/rowPinningFeature.utils.d.ts +39 -29
- package/dist/features/row-pinning/rowPinningFeature.utils.js +39 -29
- package/dist/features/row-pinning/rowPinningFeature.utils.js.map +1 -1
- package/dist/features/row-selection/rowSelectionFeature.types.d.cts +30 -24
- package/dist/features/row-selection/rowSelectionFeature.types.d.ts +30 -24
- package/dist/features/row-selection/rowSelectionFeature.utils.cjs +89 -69
- package/dist/features/row-selection/rowSelectionFeature.utils.cjs.map +1 -1
- package/dist/features/row-selection/rowSelectionFeature.utils.d.cts +89 -69
- package/dist/features/row-selection/rowSelectionFeature.utils.d.ts +89 -69
- package/dist/features/row-selection/rowSelectionFeature.utils.js +89 -69
- package/dist/features/row-selection/rowSelectionFeature.utils.js.map +1 -1
- package/dist/features/row-sorting/rowSortingFeature.types.d.cts +10 -8
- package/dist/features/row-sorting/rowSortingFeature.types.d.ts +10 -8
- package/dist/features/row-sorting/rowSortingFeature.utils.cjs +61 -44
- package/dist/features/row-sorting/rowSortingFeature.utils.cjs.map +1 -1
- package/dist/features/row-sorting/rowSortingFeature.utils.d.cts +61 -44
- package/dist/features/row-sorting/rowSortingFeature.utils.d.ts +61 -44
- package/dist/features/row-sorting/rowSortingFeature.utils.js +61 -44
- package/dist/features/row-sorting/rowSortingFeature.utils.js.map +1 -1
- package/dist/fns/aggregationFns.cjs +33 -9
- package/dist/fns/aggregationFns.cjs.map +1 -1
- package/dist/fns/aggregationFns.d.cts +33 -9
- package/dist/fns/aggregationFns.d.ts +33 -9
- package/dist/fns/aggregationFns.js +33 -9
- package/dist/fns/aggregationFns.js.map +1 -1
- package/dist/fns/filterFns.cjs +48 -17
- package/dist/fns/filterFns.cjs.map +1 -1
- package/dist/fns/filterFns.d.cts +42 -15
- package/dist/fns/filterFns.d.ts +42 -15
- package/dist/fns/filterFns.js +48 -17
- package/dist/fns/filterFns.js.map +1 -1
- package/dist/helpers/columnHelper.cjs +9 -4
- package/dist/helpers/columnHelper.cjs.map +1 -1
- package/dist/helpers/columnHelper.d.cts +9 -4
- package/dist/helpers/columnHelper.d.ts +9 -4
- package/dist/helpers/columnHelper.js +9 -4
- package/dist/helpers/columnHelper.js.map +1 -1
- package/dist/helpers/tableOptions.cjs.map +1 -1
- package/dist/helpers/tableOptions.d.cts +12 -9
- package/dist/helpers/tableOptions.d.ts +12 -9
- package/dist/helpers/tableOptions.js.map +1 -1
- package/dist/types/ColumnDef.d.cts +38 -0
- package/dist/types/ColumnDef.d.ts +38 -0
- package/dist/types/TableOptions.d.cts +15 -0
- package/dist/types/TableOptions.d.ts +15 -0
- package/dist/types/TableState.d.cts +12 -0
- package/dist/types/TableState.d.ts +12 -0
- package/package.json +5 -3
- package/skills/column-definitions/SKILL.md +333 -0
- package/skills/column-layout/SKILL.md +328 -0
- package/skills/column-layout/references/subsystems.md +220 -0
- package/skills/customizing-feature-behavior/SKILL.md +416 -0
- package/skills/filtering/SKILL.md +357 -0
- package/skills/filtering/references/faceting-and-fuzzy.md +191 -0
- package/skills/grouping/SKILL.md +448 -0
- package/skills/migrate-v8-to-v9/SKILL.md +495 -0
- package/skills/pagination/SKILL.md +385 -0
- package/skills/row-expanding/SKILL.md +348 -0
- package/skills/row-pinning/SKILL.md +269 -0
- package/skills/row-selection/SKILL.md +391 -0
- package/skills/setup/SKILL.md +400 -0
- package/skills/sorting/SKILL.md +344 -0
- package/skills/state-management/SKILL.md +388 -0
- package/src/core/cells/coreCellsFeature.utils.ts +11 -8
- package/src/core/columns/coreColumnsFeature.types.ts +7 -6
- package/src/core/columns/coreColumnsFeature.utils.ts +37 -27
- package/src/core/headers/coreHeadersFeature.types.ts +6 -4
- package/src/core/headers/coreHeadersFeature.utils.ts +23 -18
- package/src/core/row-models/coreRowModelsFeature.utils.ts +48 -36
- package/src/core/rows/coreRowsFeature.types.ts +4 -4
- package/src/core/rows/coreRowsFeature.utils.ts +34 -28
- package/src/core/table/coreTablesFeature.types.ts +6 -2
- package/src/core/table/coreTablesFeature.utils.ts +2 -2
- package/src/features/column-faceting/columnFacetingFeature.types.ts +16 -10
- package/src/features/column-faceting/columnFacetingFeature.utils.ts +26 -18
- package/src/features/column-filtering/columnFilteringFeature.types.ts +21 -11
- package/src/features/column-filtering/columnFilteringFeature.utils.ts +39 -28
- package/src/features/column-grouping/columnGroupingFeature.types.ts +18 -14
- package/src/features/column-grouping/columnGroupingFeature.utils.ts +55 -43
- package/src/features/column-ordering/columnOrderingFeature.types.ts +14 -5
- package/src/features/column-ordering/columnOrderingFeature.utils.ts +30 -23
- package/src/features/column-pinning/columnPinningFeature.types.ts +43 -31
- package/src/features/column-pinning/columnPinningFeature.utils.ts +111 -91
- package/src/features/column-resizing/columnResizingFeature.types.ts +9 -9
- package/src/features/column-resizing/columnResizingFeature.utils.ts +28 -19
- package/src/features/column-sizing/columnSizingFeature.types.ts +19 -12
- package/src/features/column-sizing/columnSizingFeature.utils.ts +52 -41
- package/src/features/column-visibility/columnVisibilityFeature.types.ts +16 -13
- package/src/features/column-visibility/columnVisibilityFeature.utils.ts +54 -42
- package/src/features/global-filtering/globalFilteringFeature.types.ts +10 -5
- package/src/features/global-filtering/globalFilteringFeature.utils.ts +20 -14
- package/src/features/row-expanding/rowExpandingFeature.types.ts +16 -15
- package/src/features/row-expanding/rowExpandingFeature.utils.ts +56 -41
- package/src/features/row-pagination/rowPaginationFeature.types.ts +19 -12
- package/src/features/row-pagination/rowPaginationFeature.utils.ts +49 -32
- package/src/features/row-pinning/rowPinningFeature.types.ts +12 -8
- package/src/features/row-pinning/rowPinningFeature.utils.ts +39 -29
- package/src/features/row-selection/rowSelectionFeature.types.ts +30 -24
- package/src/features/row-selection/rowSelectionFeature.utils.ts +89 -69
- package/src/features/row-sorting/rowSortingFeature.types.ts +10 -8
- package/src/features/row-sorting/rowSortingFeature.utils.ts +61 -44
- package/src/fns/aggregationFns.ts +33 -9
- package/src/fns/filterFns.ts +48 -17
- package/src/helpers/columnHelper.ts +9 -4
- package/src/helpers/tableOptions.ts +12 -9
- package/src/types/ColumnDef.ts +38 -0
- package/src/types/TableOptions.ts +15 -0
- package/src/types/TableState.ts +12 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Column-layout subsystems — full API surface
|
|
2
|
+
|
|
3
|
+
Detailed reference for the five UI-state-only column features extracted from `SKILL.md`. The SKILL keeps a 2-line summary table linking here; this file documents each subsystem in detail.
|
|
4
|
+
|
|
5
|
+
## Visibility — `columnVisibilityFeature`
|
|
6
|
+
|
|
7
|
+
State: `columnVisibility: Record<columnId, boolean>` — missing or `true` means visible.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
// Visibility toggle panel
|
|
11
|
+
{
|
|
12
|
+
table.getAllLeafColumns().map((column) => (
|
|
13
|
+
<label key={column.id}>
|
|
14
|
+
<input
|
|
15
|
+
type="checkbox"
|
|
16
|
+
checked={column.getIsVisible()}
|
|
17
|
+
disabled={!column.getCanHide()}
|
|
18
|
+
onChange={column.getToggleVisibilityHandler()}
|
|
19
|
+
/>
|
|
20
|
+
{column.id}
|
|
21
|
+
</label>
|
|
22
|
+
))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Body — use Visible variants, NOT getAllLeafColumns / getAllCells
|
|
26
|
+
;<tbody>
|
|
27
|
+
{table.getRowModel().rows.map((row) => (
|
|
28
|
+
<tr key={row.id}>
|
|
29
|
+
{row.getVisibleCells().map((cell) => (
|
|
30
|
+
<td key={cell.id}>
|
|
31
|
+
<table.FlexRender cell={cell} />
|
|
32
|
+
</td>
|
|
33
|
+
))}
|
|
34
|
+
</tr>
|
|
35
|
+
))}
|
|
36
|
+
</tbody>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Ordering — `columnOrderingFeature`
|
|
40
|
+
|
|
41
|
+
State: `columnOrder: string[]` of leaf column ids. Empty means definition order. **Scoped to UNPINNED columns** when pinning is active — pinned columns are sequenced inside `columnPinning.left/right`.
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
table.setColumnOrder(['firstName', 'lastName', 'age'])
|
|
45
|
+
column.getIndex('center') // ← position
|
|
46
|
+
column.getIsFirstColumn()
|
|
47
|
+
column.getIsLastColumn()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
For drag-and-drop with `@dnd-kit/core`, see the "Common Mistakes" entry on dnd libraries in the SKILL — `DndContext` must wrap from OUTSIDE the `<table>`.
|
|
51
|
+
|
|
52
|
+
## Pinning — `columnPinningFeature`
|
|
53
|
+
|
|
54
|
+
State: `columnPinning: { left: string[]; right: string[] }`. Two render strategies:
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
// Strategy A — split tables
|
|
58
|
+
<thead>
|
|
59
|
+
{table.getLeftHeaderGroups().map(/* … */)}
|
|
60
|
+
</thead>
|
|
61
|
+
// + getCenterHeaderGroups / getRightHeaderGroups
|
|
62
|
+
// + row.getLeftVisibleCells / getCenterVisibleCells / getRightVisibleCells
|
|
63
|
+
|
|
64
|
+
// Strategy B — single table + sticky CSS
|
|
65
|
+
<th
|
|
66
|
+
key={header.id}
|
|
67
|
+
style={{
|
|
68
|
+
position: header.column.getIsPinned() ? 'sticky' : undefined,
|
|
69
|
+
left: header.column.getIsPinned() === 'left' ? `${header.column.getStart('left')}px` : undefined,
|
|
70
|
+
right: header.column.getIsPinned() === 'right' ? `${header.column.getAfter('right')}px` : undefined,
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
...
|
|
74
|
+
</th>
|
|
75
|
+
|
|
76
|
+
// Toggle a pin programmatically
|
|
77
|
+
column.pin('left') // or 'right' | false
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Sizing — `columnSizingFeature`
|
|
81
|
+
|
|
82
|
+
State: `columnSizing: Record<columnId, number>` (pixels). Defaults via `defaultColumnSizing` ({ size: 150, minSize: 20, maxSize: Number.MAX_SAFE_INTEGER }) or `tableOptions.defaultColumn` globally.
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
columnHelper.accessor('firstName', {
|
|
86
|
+
size: 200,
|
|
87
|
+
minSize: 80,
|
|
88
|
+
maxSize: 400,
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// Reads
|
|
92
|
+
column.getSize() // committed size (clamped)
|
|
93
|
+
header.getSize() // same, for groups sums children
|
|
94
|
+
table.getTotalSize()
|
|
95
|
+
table.getCenterTotalSize()
|
|
96
|
+
column.resetSize() // drop the override
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Resizing — `columnResizingFeature`
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
// Wire BOTH onMouseDown AND onTouchStart on the resize handle
|
|
103
|
+
<div
|
|
104
|
+
onDoubleClick={() => header.column.resetSize()}
|
|
105
|
+
onMouseDown={header.getResizeHandler()}
|
|
106
|
+
onTouchStart={header.getResizeHandler()}
|
|
107
|
+
className={`resizer ${header.column.getIsResizing() ? 'isResizing' : ''}`}
|
|
108
|
+
/>
|
|
109
|
+
|
|
110
|
+
// Modes:
|
|
111
|
+
// columnResizeMode: 'onEnd' (default) — commit on drag release; safer for big React tables
|
|
112
|
+
// columnResizeMode: 'onChange' — commit live; needs the perf pattern in SKILL
|
|
113
|
+
// columnResizeDirection: 'ltr' (default) | 'rtl'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Additional MEDIUM-priority failure modes
|
|
117
|
+
|
|
118
|
+
### Trying to reorder pinned columns via `columnOrder`
|
|
119
|
+
|
|
120
|
+
Wrong:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
// Won't move 'actions' relative to 'firstName' while it's pinned right
|
|
124
|
+
const [columnPinning] = useState({
|
|
125
|
+
left: ['select'],
|
|
126
|
+
right: ['actions'],
|
|
127
|
+
})
|
|
128
|
+
table.setColumnOrder(['actions', 'select', 'firstName', 'lastName'])
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Correct:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
// Reorder the pinning state itself
|
|
135
|
+
table.setColumnPinning((old) => ({
|
|
136
|
+
left: ['select'],
|
|
137
|
+
right: ['summary', 'actions'], // 'summary' renders before 'actions'
|
|
138
|
+
}))
|
|
139
|
+
|
|
140
|
+
// columnOrder works normally for the unpinned center region
|
|
141
|
+
table.setColumnOrder(['firstName', 'lastName'])
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
After the pipeline's pinning split, the left/right partitions read directly from `state.columnPinning.left/right`. `columnOrder` only affects the center.
|
|
145
|
+
|
|
146
|
+
Source: docs/guide/column-ordering.md; packages/table-core/src/features/column-pinning/columnPinningFeature.utils.ts
|
|
147
|
+
|
|
148
|
+
### Using `react-dnd` / `react-beautiful-dnd` for column reorder in React 18+
|
|
149
|
+
|
|
150
|
+
Wrong:
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
// react-dnd in React 18 Strict Mode — flicker and stale drags
|
|
154
|
+
import { DndProvider } from 'react-dnd'
|
|
155
|
+
import { HTML5Backend } from 'react-dnd-html5-backend'
|
|
156
|
+
|
|
157
|
+
// or nesting DndContext inside <table>
|
|
158
|
+
;<table>
|
|
159
|
+
<DndContext onDragEnd={handleDragEnd}>
|
|
160
|
+
<thead>...</thead>
|
|
161
|
+
</DndContext>
|
|
162
|
+
</table>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Correct:
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
// @dnd-kit + wrap from OUTSIDE the table (DndContext renders divs)
|
|
169
|
+
<DndContext
|
|
170
|
+
collisionDetection={closestCenter}
|
|
171
|
+
modifiers={[restrictToHorizontalAxis]}
|
|
172
|
+
onDragEnd={handleDragEnd}
|
|
173
|
+
sensors={sensors}
|
|
174
|
+
>
|
|
175
|
+
<table>
|
|
176
|
+
<thead>
|
|
177
|
+
{table.getHeaderGroups().map((hg) => (
|
|
178
|
+
<tr key={hg.id}>
|
|
179
|
+
<SortableContext
|
|
180
|
+
items={table.store.state.columnOrder}
|
|
181
|
+
strategy={horizontalListSortingStrategy}
|
|
182
|
+
>
|
|
183
|
+
{hg.headers.map((h) => (
|
|
184
|
+
<DraggableHeader key={h.id} header={h} />
|
|
185
|
+
))}
|
|
186
|
+
</SortableContext>
|
|
187
|
+
</tr>
|
|
188
|
+
))}
|
|
189
|
+
</thead>
|
|
190
|
+
</table>
|
|
191
|
+
</DndContext>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
`react-dnd` has Strict Mode incompatibilities; `react-beautiful-dnd` is in maintenance. dnd-kit is the v9-recommended stack.
|
|
195
|
+
|
|
196
|
+
Source: examples/react/column-dnd/src/main.tsx
|
|
197
|
+
|
|
198
|
+
### Wiring `header.getResizeHandler()` to only `onMouseDown`
|
|
199
|
+
|
|
200
|
+
Wrong:
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
// Desktop only — mobile users can't resize
|
|
204
|
+
<div onMouseDown={header.getResizeHandler()} />
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Correct:
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
<div
|
|
211
|
+
onDoubleClick={() => header.column.resetSize()}
|
|
212
|
+
onMouseDown={header.getResizeHandler()}
|
|
213
|
+
onTouchStart={header.getResizeHandler()}
|
|
214
|
+
className={`resizer ${header.column.getIsResizing() ? 'isResizing' : ''}`}
|
|
215
|
+
/>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`header_getResizeHandler` branches internally on `isTouchStartEvent`. The same handler must be installed on both DOM events.
|
|
219
|
+
|
|
220
|
+
Source: docs/guide/column-resizing.md; examples/react/column-resizing/src/main.tsx
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: customizing-feature-behavior
|
|
3
|
+
description: >
|
|
4
|
+
Override per-column `sortFn`, `filterFn`, `aggregationFn` and table-level
|
|
5
|
+
`globalFilterFn` in TanStack Table v9. Covers built-in `filterFns` / `sortFns` /
|
|
6
|
+
`aggregationFns` registries (passed to `createFilteredRowModel(filterFns)` /
|
|
7
|
+
`createSortedRowModel(sortFns)` / `createGroupedRowModel(aggregationFns)`),
|
|
8
|
+
authoring custom functions with the `FilterFn` / `SortFn` / `AggregationFn`
|
|
9
|
+
signatures, chaining filter→sort via the `addMeta` callback +
|
|
10
|
+
`row.columnFiltersMeta`, `resolveFilterValue`, `autoRemove`, `invertSorting`,
|
|
11
|
+
`sortUndefined` ('first'|'last'|-1|1), and `sortDescFirst`. Distinguishes
|
|
12
|
+
`aggregationFn` (produces value) from `aggregatedCell` (renders value).
|
|
13
|
+
type: core
|
|
14
|
+
library: tanstack-table
|
|
15
|
+
library_version: '9.0.0-alpha.47'
|
|
16
|
+
requires:
|
|
17
|
+
- state-management
|
|
18
|
+
sources:
|
|
19
|
+
- TanStack/table:docs/guide/sorting.md
|
|
20
|
+
- TanStack/table:docs/guide/column-filtering.md
|
|
21
|
+
- TanStack/table:docs/guide/fuzzy-filtering.md
|
|
22
|
+
- TanStack/table:packages/table-core/src/fns/filterFns.ts
|
|
23
|
+
- TanStack/table:packages/table-core/src/fns/sortFns.ts
|
|
24
|
+
- TanStack/table:packages/table-core/src/fns/aggregationFns.ts
|
|
25
|
+
- TanStack/table:examples/react/filters-fuzzy/src/main.tsx
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
This skill builds on `tanstack-table/state-management`. Read it first for how feature plugins drive state slices.
|
|
29
|
+
|
|
30
|
+
## Setup
|
|
31
|
+
|
|
32
|
+
v9 customization happens in three places:
|
|
33
|
+
|
|
34
|
+
1. **Built-in function registries** — `filterFns`, `sortFns`, `aggregationFns` — passed as arguments to row-model factories so unused fns tree-shake away.
|
|
35
|
+
2. **Per-column overrides** — `columnDef.filterFn`, `columnDef.sortFn`, `columnDef.aggregationFn` (string name OR inline function).
|
|
36
|
+
3. **Table-level overrides** — `tableOptions.globalFilterFn`.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import {
|
|
40
|
+
tableFeatures,
|
|
41
|
+
rowSortingFeature,
|
|
42
|
+
columnFilteringFeature,
|
|
43
|
+
globalFilteringFeature,
|
|
44
|
+
columnGroupingFeature,
|
|
45
|
+
rowExpandingFeature,
|
|
46
|
+
createFilteredRowModel,
|
|
47
|
+
createSortedRowModel,
|
|
48
|
+
createGroupedRowModel,
|
|
49
|
+
createExpandedRowModel,
|
|
50
|
+
filterFns,
|
|
51
|
+
sortFns,
|
|
52
|
+
aggregationFns,
|
|
53
|
+
createColumnHelper,
|
|
54
|
+
} from '@tanstack/table-core'
|
|
55
|
+
import type { FilterFn, SortFn, AggregationFn } from '@tanstack/table-core'
|
|
56
|
+
import {
|
|
57
|
+
rankItem,
|
|
58
|
+
compareItems,
|
|
59
|
+
type RankingInfo,
|
|
60
|
+
} from '@tanstack/match-sorter-utils'
|
|
61
|
+
|
|
62
|
+
type Person = {
|
|
63
|
+
id: string
|
|
64
|
+
firstName: string
|
|
65
|
+
lastName: string
|
|
66
|
+
revenue: number
|
|
67
|
+
status: 'single' | 'complicated' | 'relationship'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const _features = tableFeatures({
|
|
71
|
+
rowSortingFeature,
|
|
72
|
+
columnFilteringFeature,
|
|
73
|
+
globalFilteringFeature,
|
|
74
|
+
columnGroupingFeature,
|
|
75
|
+
rowExpandingFeature,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// Module augmentation registers custom fn names so columnDef.filterFn typechecks.
|
|
79
|
+
declare module '@tanstack/table-core' {
|
|
80
|
+
interface FilterFns {
|
|
81
|
+
fuzzy: FilterFn<typeof _features, Person>
|
|
82
|
+
}
|
|
83
|
+
interface FilterMeta {
|
|
84
|
+
itemRank?: RankingInfo
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const fuzzyFilter: FilterFn<typeof _features, Person> = (
|
|
89
|
+
row,
|
|
90
|
+
columnId,
|
|
91
|
+
value,
|
|
92
|
+
addMeta,
|
|
93
|
+
) => {
|
|
94
|
+
const itemRank = rankItem(row.getValue(columnId), value)
|
|
95
|
+
addMeta?.({ itemRank })
|
|
96
|
+
return itemRank.passed
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const columnHelper = createColumnHelper<typeof _features, Person>()
|
|
100
|
+
|
|
101
|
+
const columns = columnHelper.columns([
|
|
102
|
+
columnHelper.accessor('firstName', {
|
|
103
|
+
filterFn: 'fuzzy', // ← refers to registered name
|
|
104
|
+
sortFn: 'alphanumeric',
|
|
105
|
+
}),
|
|
106
|
+
columnHelper.accessor('revenue', {
|
|
107
|
+
aggregationFn: 'sum',
|
|
108
|
+
aggregatedCell: (info) => `$${info.getValue<number>().toLocaleString()}`,
|
|
109
|
+
}),
|
|
110
|
+
])
|
|
111
|
+
|
|
112
|
+
const table = constructTable({
|
|
113
|
+
_features,
|
|
114
|
+
_rowModels: {
|
|
115
|
+
filteredRowModel: createFilteredRowModel({
|
|
116
|
+
...filterFns, // keep built-ins
|
|
117
|
+
fuzzy: fuzzyFilter, // add custom
|
|
118
|
+
}),
|
|
119
|
+
sortedRowModel: createSortedRowModel(sortFns),
|
|
120
|
+
groupedRowModel: createGroupedRowModel(aggregationFns),
|
|
121
|
+
expandedRowModel: createExpandedRowModel(),
|
|
122
|
+
},
|
|
123
|
+
columns,
|
|
124
|
+
data,
|
|
125
|
+
globalFilterFn: 'fuzzy',
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Core Patterns
|
|
130
|
+
|
|
131
|
+
### Pick a built-in `sortFn` by name + direction control
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
columnHelper.accessor('lastName', {
|
|
135
|
+
sortFn: 'alphanumeric',
|
|
136
|
+
sortDescFirst: false,
|
|
137
|
+
sortUndefined: 'last', // ABSOLUTE: always at end regardless of asc/desc
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Layered direction controls:
|
|
142
|
+
|
|
143
|
+
- `sortDescFirst: true/false` — first click sorts descending
|
|
144
|
+
- `sortUndefined: 'first' | 'last' | -1 | 1 | false` — string forms are absolute; numeric flips with `desc`
|
|
145
|
+
- `invertSorting: true` — for "lower-is-better" scales (rank 1 above rank 2 even when descending)
|
|
146
|
+
|
|
147
|
+
### Filter → sort handoff via `addMeta`
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
const fuzzyFilter: FilterFn<typeof _features, Person> = (
|
|
151
|
+
row,
|
|
152
|
+
columnId,
|
|
153
|
+
value,
|
|
154
|
+
addMeta,
|
|
155
|
+
) => {
|
|
156
|
+
const itemRank = rankItem(row.getValue(columnId), value)
|
|
157
|
+
addMeta?.({ itemRank })
|
|
158
|
+
return itemRank.passed
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Custom sortFn reads the meta the filter stashed
|
|
162
|
+
const fuzzySort: SortFn<typeof _features, Person> = (rowA, rowB, columnId) => {
|
|
163
|
+
let dir = 0
|
|
164
|
+
if (rowA.columnFiltersMeta[columnId]) {
|
|
165
|
+
dir = compareItems(
|
|
166
|
+
rowA.columnFiltersMeta[columnId].itemRank!,
|
|
167
|
+
rowB.columnFiltersMeta[columnId].itemRank!,
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
return dir === 0 ? sortFns.alphanumeric(rowA, rowB, columnId) : dir
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
columnHelper.accessor('fullName', { filterFn: 'fuzzy', sortFn: fuzzySort })
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`row.columnFiltersMeta` is keyed by the column id that produced the meta (or `'__global__'` for the global filter). The sortFn MUST look up the same column id its filterFn used.
|
|
177
|
+
|
|
178
|
+
### Custom `aggregationFn` for grouping
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import type { AggregationFn } from '@tanstack/table-core'
|
|
182
|
+
|
|
183
|
+
// Signature: (columnId, leafRows, childRows) → aggregated value
|
|
184
|
+
// leafRows = all descendant non-grouped rows
|
|
185
|
+
// childRows = immediate children (may be sub-aggregates at deeper levels)
|
|
186
|
+
const weightedMean: AggregationFn<typeof _features, Person> = (
|
|
187
|
+
columnId,
|
|
188
|
+
leafRows,
|
|
189
|
+
) => {
|
|
190
|
+
let totalWeight = 0
|
|
191
|
+
let weightedSum = 0
|
|
192
|
+
leafRows.forEach((row) => {
|
|
193
|
+
const v = row.getValue<number>(columnId)
|
|
194
|
+
const w = row.original.revenue
|
|
195
|
+
weightedSum += v * w
|
|
196
|
+
totalWeight += w
|
|
197
|
+
})
|
|
198
|
+
return totalWeight === 0 ? 0 : weightedSum / totalWeight
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const table = constructTable({
|
|
202
|
+
_features,
|
|
203
|
+
_rowModels: {
|
|
204
|
+
groupedRowModel: createGroupedRowModel({ ...aggregationFns, weightedMean }),
|
|
205
|
+
expandedRowModel: createExpandedRowModel(),
|
|
206
|
+
},
|
|
207
|
+
columns: columnHelper.columns([
|
|
208
|
+
columnHelper.accessor('revenue', {
|
|
209
|
+
aggregationFn: 'weightedMean',
|
|
210
|
+
aggregatedCell: (info) => `$${info.getValue<number>().toFixed(2)}`,
|
|
211
|
+
}),
|
|
212
|
+
]),
|
|
213
|
+
data,
|
|
214
|
+
})
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Common Mistakes
|
|
218
|
+
|
|
219
|
+
### [CRITICAL] Referencing a custom `filterFn` by string without registering it
|
|
220
|
+
|
|
221
|
+
Wrong:
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
// "fuzzy" string never registered
|
|
225
|
+
const table = useTable({
|
|
226
|
+
_features,
|
|
227
|
+
columns: [columnHelper.accessor('fullName', { filterFn: 'fuzzy' })],
|
|
228
|
+
_rowModels: {
|
|
229
|
+
filteredRowModel: createFilteredRowModel(filterFns), // ❌ no fuzzy
|
|
230
|
+
},
|
|
231
|
+
data,
|
|
232
|
+
})
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Correct:
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
declare module '@tanstack/react-table' {
|
|
239
|
+
interface FilterFns {
|
|
240
|
+
fuzzy: FilterFn<typeof _features, Person>
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const fuzzyFilter: FilterFn<typeof _features, Person> = (
|
|
245
|
+
row,
|
|
246
|
+
columnId,
|
|
247
|
+
value,
|
|
248
|
+
addMeta,
|
|
249
|
+
) => {
|
|
250
|
+
const itemRank = rankItem(row.getValue(columnId), value)
|
|
251
|
+
addMeta?.({ itemRank })
|
|
252
|
+
return itemRank.passed
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const table = useTable({
|
|
256
|
+
_features,
|
|
257
|
+
columns: [columnHelper.accessor('fullName', { filterFn: 'fuzzy' })],
|
|
258
|
+
_rowModels: {
|
|
259
|
+
filteredRowModel: createFilteredRowModel({
|
|
260
|
+
...filterFns,
|
|
261
|
+
fuzzy: fuzzyFilter,
|
|
262
|
+
}),
|
|
263
|
+
},
|
|
264
|
+
data,
|
|
265
|
+
})
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
String values are looked up in `table._rowModelFns.filterFns`. Unregistered names log `Could not find a valid 'column.filterFn' …` in dev and silently no-op in prod.
|
|
269
|
+
|
|
270
|
+
Source: examples/react/filters-fuzzy/src/main.tsx; packages/table-core/src/features/column-filtering/columnFilteringFeature.utils.ts
|
|
271
|
+
|
|
272
|
+
### [HIGH] Using v8 `sortingFn` / `sortingFns` names
|
|
273
|
+
|
|
274
|
+
Wrong:
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
columnHelper.accessor('age', {
|
|
278
|
+
sortingFn: 'alphanumeric', // v8 name — ignored
|
|
279
|
+
})
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Correct:
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
columnHelper.accessor('age', {
|
|
286
|
+
sortFn: 'alphanumeric',
|
|
287
|
+
})
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
v9 renamed every sorting API: `sortingFn` → `sortFn`, `sortingFns` → `sortFns`, type `SortingFn` → `SortFn`, `column.getSortingFn()` → `column.getSortFn()`. The default `sortFn` is `'auto'`, falling back to `sortFn_basic` if the lookup misses — so wrong names sort wrong instead of erroring.
|
|
291
|
+
|
|
292
|
+
Source: docs/framework/react/guide/migrating.md; packages/table-core/src/features/row-sorting/rowSortingFeature.types.ts
|
|
293
|
+
|
|
294
|
+
### [HIGH] Custom `sortFn` reads filter meta from a different column id
|
|
295
|
+
|
|
296
|
+
Wrong:
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
// filter on 'fullName', sort reads meta from 'firstName'
|
|
300
|
+
const fuzzySort: SortFn<typeof _features, Person> = (a, b, columnId) => {
|
|
301
|
+
const meta = a.columnFiltersMeta['firstName'] // ❌ wrong key
|
|
302
|
+
return meta
|
|
303
|
+
? compareItems(meta.itemRank, b.columnFiltersMeta['firstName'].itemRank)
|
|
304
|
+
: 0
|
|
305
|
+
}
|
|
306
|
+
columnHelper.accessor('fullName', { filterFn: 'fuzzy', sortFn: fuzzySort })
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Correct:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
const fuzzySort: SortFn<typeof _features, Person> = (rowA, rowB, columnId) => {
|
|
313
|
+
let dir = 0
|
|
314
|
+
if (rowA.columnFiltersMeta[columnId]) {
|
|
315
|
+
dir = compareItems(
|
|
316
|
+
rowA.columnFiltersMeta[columnId].itemRank!,
|
|
317
|
+
rowB.columnFiltersMeta[columnId].itemRank!,
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
return dir === 0 ? sortFns.alphanumeric(rowA, rowB, columnId) : dir
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
`row.columnFiltersMeta` is keyed by the column id that produced it. Always use the `columnId` argument the sortFn receives.
|
|
325
|
+
|
|
326
|
+
Source: examples/react/filters-fuzzy/src/main.tsx
|
|
327
|
+
|
|
328
|
+
### [MEDIUM] Returning a complex value from the accessor while using a built-in `sortFn`
|
|
329
|
+
|
|
330
|
+
Wrong:
|
|
331
|
+
|
|
332
|
+
```ts
|
|
333
|
+
// accessor returns object; alphanumeric sees "[object Object]"
|
|
334
|
+
columnHelper.accessor((row) => row.name, {
|
|
335
|
+
id: 'name',
|
|
336
|
+
sortFn: 'alphanumeric',
|
|
337
|
+
})
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Correct:
|
|
341
|
+
|
|
342
|
+
```ts
|
|
343
|
+
// Option A — return a primitive
|
|
344
|
+
columnHelper.accessor((row) => `${row.name.first} ${row.name.last}`, {
|
|
345
|
+
id: 'fullName',
|
|
346
|
+
sortFn: 'alphanumeric',
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
// Option B — custom sortFn that knows the shape
|
|
350
|
+
columnHelper.accessor((row) => row.name, {
|
|
351
|
+
id: 'name',
|
|
352
|
+
sortFn: (a, b, id) => {
|
|
353
|
+
const av = a.getValue<{ first: string }>(id).first
|
|
354
|
+
const bv = b.getValue<{ first: string }>(id).first
|
|
355
|
+
return av === bv ? 0 : av > bv ? 1 : -1
|
|
356
|
+
},
|
|
357
|
+
})
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Built-in sortFns (`alphanumeric`, `text`, `basic`) coerce via comparison operators. Object accessors collapse to `"[object Object]"` and every row ties.
|
|
361
|
+
|
|
362
|
+
Source: packages/table-core/src/fns/sortFns.ts
|
|
363
|
+
|
|
364
|
+
### [MEDIUM] Confusing `aggregationFn` with `aggregatedCell`
|
|
365
|
+
|
|
366
|
+
Wrong:
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
// rendering JSX inside the aggregation function
|
|
370
|
+
columnHelper.accessor('revenue', {
|
|
371
|
+
aggregationFn: (id, leaves) => <b>${leaves.reduce((a, r) => a + r.getValue(id), 0)}</b>,
|
|
372
|
+
})
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Correct:
|
|
376
|
+
|
|
377
|
+
```ts
|
|
378
|
+
columnHelper.accessor('revenue', {
|
|
379
|
+
aggregationFn: 'sum', // returns a value
|
|
380
|
+
aggregatedCell: (info) => <b>${info.getValue<number>().toLocaleString()}</b>, // renders it
|
|
381
|
+
})
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
`aggregationFn` produces the grouped-row value (signature `(columnId, leafRows, childRows)`). `aggregatedCell` renders it. Don't combine.
|
|
385
|
+
|
|
386
|
+
Source: packages/table-core/src/features/column-grouping/columnGroupingFeature.types.ts
|
|
387
|
+
|
|
388
|
+
### [CRITICAL] Reimplementing what built-in APIs provide
|
|
389
|
+
|
|
390
|
+
Wrong:
|
|
391
|
+
|
|
392
|
+
```ts
|
|
393
|
+
// Reimplements sorting state manually instead of using the API
|
|
394
|
+
const [sorting, setSorting] = useState([])
|
|
395
|
+
const sortedData = useMemo(() => [...data].sort(/* … */), [data, sorting])
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
Correct:
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
const table = useTable({
|
|
402
|
+
_features: tableFeatures({ rowSortingFeature }),
|
|
403
|
+
_rowModels: { sortedRowModel: createSortedRowModel(sortFns) },
|
|
404
|
+
columns,
|
|
405
|
+
data,
|
|
406
|
+
})
|
|
407
|
+
// table.setSorting(...), column.toggleSorting(), header.getToggleSortingHandler()
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Source: maintainer interview (Phase 4, 2026-05-17)
|
|
411
|
+
|
|
412
|
+
## See also
|
|
413
|
+
|
|
414
|
+
- `tanstack-table/filtering` — `filterFn` placement, fuzzy filter pattern, faceted UI
|
|
415
|
+
- `tanstack-table/sorting` — built-in `sortFns`, multi-sort, `sortUndefined`
|
|
416
|
+
- `tanstack-table/grouping` — `aggregationFn` signature details and built-ins
|