@tanstack/table-core 9.0.0-beta.37 → 9.0.0-beta.42
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 +1 -0
- package/dist/core/headers/buildHeaderGroups.cjs.map +1 -1
- package/dist/core/headers/buildHeaderGroups.d.cts +1 -1
- package/dist/core/headers/buildHeaderGroups.d.ts +1 -1
- package/dist/core/headers/buildHeaderGroups.js.map +1 -1
- package/dist/core/headers/coreHeadersFeature.utils.cjs +7 -7
- package/dist/core/headers/coreHeadersFeature.utils.cjs.map +1 -1
- package/dist/core/headers/coreHeadersFeature.utils.js +7 -7
- package/dist/core/headers/coreHeadersFeature.utils.js.map +1 -1
- package/dist/core/table/coreTablesFeature.utils.cjs +1 -1
- package/dist/core/table/coreTablesFeature.utils.cjs.map +1 -1
- package/dist/core/table/coreTablesFeature.utils.js +1 -1
- package/dist/core/table/coreTablesFeature.utils.js.map +1 -1
- package/dist/features/column-ordering/columnOrderingFeature.types.d.cts +5 -5
- package/dist/features/column-ordering/columnOrderingFeature.types.d.ts +5 -5
- package/dist/features/column-ordering/columnOrderingFeature.utils.cjs +6 -6
- package/dist/features/column-ordering/columnOrderingFeature.utils.cjs.map +1 -1
- package/dist/features/column-ordering/columnOrderingFeature.utils.d.cts +3 -3
- package/dist/features/column-ordering/columnOrderingFeature.utils.d.ts +3 -3
- package/dist/features/column-ordering/columnOrderingFeature.utils.js +6 -6
- package/dist/features/column-ordering/columnOrderingFeature.utils.js.map +1 -1
- package/dist/features/column-pinning/columnPinningFeature.cjs +44 -39
- package/dist/features/column-pinning/columnPinningFeature.cjs.map +1 -1
- package/dist/features/column-pinning/columnPinningFeature.d.cts +6 -1
- package/dist/features/column-pinning/columnPinningFeature.d.ts +6 -1
- package/dist/features/column-pinning/columnPinningFeature.js +45 -40
- package/dist/features/column-pinning/columnPinningFeature.js.map +1 -1
- package/dist/features/column-pinning/columnPinningFeature.types.d.cts +49 -38
- package/dist/features/column-pinning/columnPinningFeature.types.d.ts +49 -38
- package/dist/features/column-pinning/columnPinningFeature.utils.cjs +154 -146
- package/dist/features/column-pinning/columnPinningFeature.utils.cjs.map +1 -1
- package/dist/features/column-pinning/columnPinningFeature.utils.d.cts +81 -73
- package/dist/features/column-pinning/columnPinningFeature.utils.d.ts +81 -73
- package/dist/features/column-pinning/columnPinningFeature.utils.js +141 -133
- package/dist/features/column-pinning/columnPinningFeature.utils.js.map +1 -1
- package/dist/features/column-sizing/columnSizingFeature.cjs +4 -4
- package/dist/features/column-sizing/columnSizingFeature.cjs.map +1 -1
- package/dist/features/column-sizing/columnSizingFeature.js +5 -5
- package/dist/features/column-sizing/columnSizingFeature.js.map +1 -1
- package/dist/features/column-sizing/columnSizingFeature.types.d.cts +18 -12
- package/dist/features/column-sizing/columnSizingFeature.types.d.ts +18 -12
- package/dist/features/column-sizing/columnSizingFeature.utils.cjs +24 -20
- package/dist/features/column-sizing/columnSizingFeature.utils.cjs.map +1 -1
- package/dist/features/column-sizing/columnSizingFeature.utils.d.cts +18 -14
- package/dist/features/column-sizing/columnSizingFeature.utils.d.ts +18 -14
- package/dist/features/column-sizing/columnSizingFeature.utils.js +24 -20
- package/dist/features/column-sizing/columnSizingFeature.utils.js.map +1 -1
- package/dist/features/column-visibility/columnVisibilityFeature.utils.cjs +16 -16
- package/dist/features/column-visibility/columnVisibilityFeature.utils.cjs.map +1 -1
- package/dist/features/column-visibility/columnVisibilityFeature.utils.d.cts +3 -3
- package/dist/features/column-visibility/columnVisibilityFeature.utils.d.ts +3 -3
- package/dist/features/column-visibility/columnVisibilityFeature.utils.js +16 -16
- package/dist/features/column-visibility/columnVisibilityFeature.utils.js.map +1 -1
- package/dist/static-functions.cjs +16 -16
- package/dist/static-functions.d.cts +3 -3
- package/dist/static-functions.d.ts +3 -3
- package/dist/static-functions.js +3 -3
- package/package.json +1 -1
- package/skills/api-not-found/SKILL.md +113 -0
- package/skills/client-vs-server/SKILL.md +164 -0
- package/skills/column-faceting/SKILL.md +91 -0
- package/skills/column-filtering/SKILL.md +82 -0
- package/skills/column-ordering/SKILL.md +75 -0
- package/skills/column-pinning/SKILL.md +89 -0
- package/skills/column-resizing/SKILL.md +91 -0
- package/skills/column-sizing/SKILL.md +72 -0
- package/skills/column-visibility/SKILL.md +75 -0
- package/skills/core/SKILL.md +140 -0
- package/skills/custom-features/SKILL.md +207 -0
- package/skills/expanding/SKILL.md +80 -0
- package/skills/global-filtering/SKILL.md +84 -0
- package/skills/grouping/SKILL.md +50 -394
- package/skills/migrate-v8-to-v9/SKILL.md +230 -390
- package/skills/pagination/SKILL.md +35 -344
- package/skills/row-pinning/SKILL.md +47 -238
- package/skills/row-selection/SKILL.md +39 -351
- package/skills/sorting/SKILL.md +35 -299
- package/skills/table-features/SKILL.md +153 -0
- package/skills/typescript/SKILL.md +126 -0
- package/src/core/headers/buildHeaderGroups.ts +1 -1
- package/src/core/headers/coreHeadersFeature.utils.ts +7 -7
- package/src/core/table/coreTablesFeature.utils.ts +1 -1
- package/src/features/column-ordering/columnOrderingFeature.types.ts +5 -5
- package/src/features/column-ordering/columnOrderingFeature.utils.ts +9 -9
- package/src/features/column-pinning/columnPinningFeature.ts +64 -59
- package/src/features/column-pinning/columnPinningFeature.types.ts +49 -38
- package/src/features/column-pinning/columnPinningFeature.utils.ts +163 -155
- package/src/features/column-sizing/columnSizingFeature.ts +6 -6
- package/src/features/column-sizing/columnSizingFeature.types.ts +18 -12
- package/src/features/column-sizing/columnSizingFeature.utils.ts +31 -27
- package/src/features/column-visibility/columnVisibilityFeature.utils.ts +15 -15
- package/skills/column-definitions/SKILL.md +0 -330
- package/skills/column-layout/SKILL.md +0 -326
- package/skills/column-layout/references/subsystems.md +0 -220
- package/skills/customizing-feature-behavior/SKILL.md +0 -423
- package/skills/filtering/SKILL.md +0 -375
- package/skills/filtering/references/faceting-and-fuzzy.md +0 -218
- package/skills/row-expanding/SKILL.md +0 -356
- package/skills/setup/SKILL.md +0 -390
- package/skills/state-management/SKILL.md +0 -403
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: column-layout
|
|
3
|
-
description: >
|
|
4
|
-
The five UI-state-only column features in TanStack Table v9 that shape how
|
|
5
|
-
columns render — visibility, ordering, pinning, sizing, resizing. None
|
|
6
|
-
require a row model. Covers `columnVisibilityFeature` (getVisibleLeafColumns,
|
|
7
|
-
row.getVisibleCells), `columnOrderingFeature` (columnOrder string[], column.getIndex),
|
|
8
|
-
`columnPinningFeature` (left/right ColumnPinningState, column.pin, column.getStart /
|
|
9
|
-
getAfter, split-table getLeft*/getCenter*/getRight* APIs, sticky-CSS pattern;
|
|
10
|
-
`enableColumnPinning` table-level option distinct from per-column `enablePinning`),
|
|
11
|
-
`columnSizingFeature` (defaultColumnSizing, column.getSize, table.getTotalSize),
|
|
12
|
-
`columnResizingFeature` (columnResizeMode 'onEnd'/'onChange', columnResizeDirection
|
|
13
|
-
'ltr'/'rtl', header.getResizeHandler for mouse + touch, CSS-variable
|
|
14
|
-
performant resize pattern). Pipeline: Column Pinning → columnOrder → Grouping.
|
|
15
|
-
type: core
|
|
16
|
-
library: tanstack-table
|
|
17
|
-
library_version: '9.0.0-alpha.48'
|
|
18
|
-
requires:
|
|
19
|
-
- state-management
|
|
20
|
-
sources:
|
|
21
|
-
- TanStack/table:docs/guide/column-visibility.md
|
|
22
|
-
- TanStack/table:docs/guide/column-ordering.md
|
|
23
|
-
- TanStack/table:docs/guide/column-pinning.md
|
|
24
|
-
- TanStack/table:docs/guide/column-sizing.md
|
|
25
|
-
- TanStack/table:docs/guide/column-resizing.md
|
|
26
|
-
- TanStack/table:examples/react/column-visibility/src/main.tsx
|
|
27
|
-
- TanStack/table:examples/react/column-resizing/src/main.tsx
|
|
28
|
-
- TanStack/table:examples/react/column-resizing-performant/src/main.tsx
|
|
29
|
-
- TanStack/table:examples/react/column-pinning-split/src/main.tsx
|
|
30
|
-
- TanStack/table:examples/react/column-pinning-sticky/src/main.tsx
|
|
31
|
-
- TanStack/table:examples/react/column-dnd/src/main.tsx
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
This skill builds on `tanstack-table/state-management`. Read it first for the atom model — these are UI-state-only features (no row model).
|
|
35
|
-
|
|
36
|
-
## Setup
|
|
37
|
-
|
|
38
|
-
All five features are opt-in via `tableFeatures({...})`. The reorder pipeline is fixed: **(1) Column Pinning splits into left/center/right → (2) `columnOrder` reorders the center → (3) Grouping (`groupedColumnMode: 'reorder' | 'remove'`) may move grouped columns to the front.**
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
import {
|
|
42
|
-
tableFeatures,
|
|
43
|
-
columnVisibilityFeature,
|
|
44
|
-
columnOrderingFeature,
|
|
45
|
-
columnPinningFeature,
|
|
46
|
-
columnSizingFeature,
|
|
47
|
-
columnResizingFeature,
|
|
48
|
-
constructTable,
|
|
49
|
-
} from '@tanstack/table-core'
|
|
50
|
-
|
|
51
|
-
const features = tableFeatures({
|
|
52
|
-
columnVisibilityFeature,
|
|
53
|
-
columnOrderingFeature,
|
|
54
|
-
columnPinningFeature,
|
|
55
|
-
columnSizingFeature,
|
|
56
|
-
columnResizingFeature, // explicit — formerly part of v8 ColumnSizing
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
const table = constructTable({
|
|
60
|
-
features,
|
|
61
|
-
columns,
|
|
62
|
-
data,
|
|
63
|
-
initialState: {
|
|
64
|
-
columnVisibility: {},
|
|
65
|
-
columnOrder: [],
|
|
66
|
-
columnPinning: { left: [], right: [] },
|
|
67
|
-
columnSizing: {},
|
|
68
|
-
},
|
|
69
|
-
})
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Subsystems
|
|
73
|
-
|
|
74
|
-
| Feature | State slice | Key APIs |
|
|
75
|
-
| ------------------------- | ---------------------- | ---------------------------------------------------- |
|
|
76
|
-
| `columnVisibilityFeature` | `columnVisibility` | `column.toggleVisibility()`, `row.getVisibleCells()` |
|
|
77
|
-
| `columnOrderingFeature` | `columnOrder` | `table.setColumnOrder()`, `column.getIndex()` |
|
|
78
|
-
| `columnPinningFeature` | `columnPinning` (l/r) | `column.pin()`, `column.getStart()`, `getAfter()` |
|
|
79
|
-
| `columnSizingFeature` | `columnSizing` | `column.getSize()`, `table.getTotalSize()` |
|
|
80
|
-
| `columnResizingFeature` | (transient drag state) | `header.getResizeHandler()`, `columnResizeMode` |
|
|
81
|
-
|
|
82
|
-
Full API surface, render strategies, and additional MEDIUM-priority failure modes (reorder-pinned-via-columnOrder, react-dnd/react-beautiful-dnd avoidance, touch-resize handler) in [subsystems.md](references/subsystems.md).
|
|
83
|
-
|
|
84
|
-
## Core Patterns
|
|
85
|
-
|
|
86
|
-
### Performant `'onChange'` resize (React)
|
|
87
|
-
|
|
88
|
-
```tsx
|
|
89
|
-
// From examples/react/column-resizing-performant/src/main.tsx
|
|
90
|
-
const columnSizeVars = React.useMemo(() => {
|
|
91
|
-
const headers = table.getFlatHeaders()
|
|
92
|
-
const colSizes: { [key: string]: number } = {}
|
|
93
|
-
for (const header of headers) {
|
|
94
|
-
colSizes[`--header-${header.id}-size`] = header.getSize()
|
|
95
|
-
colSizes[`--col-${header.column.id}-size`] = header.column.getSize()
|
|
96
|
-
}
|
|
97
|
-
return colSizes
|
|
98
|
-
}, [table.state.columnResizing, table.state.columnSizing])
|
|
99
|
-
|
|
100
|
-
<div className="divTable" style={{ ...columnSizeVars, width: table.getTotalSize() }}>
|
|
101
|
-
{table.store.state.columnResizing.isResizingColumn
|
|
102
|
-
? <MemoizedTableBody table={table} />
|
|
103
|
-
: <TableBody table={table} />}
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
// Body cells use the CSS variable (no per-cell getSize() call)
|
|
107
|
-
<div className="td" style={{ width: `calc(var(--col-${cell.column.id}-size) * 1px)` }}>
|
|
108
|
-
{cell.renderValue()}
|
|
109
|
-
</div>
|
|
110
|
-
|
|
111
|
-
export const MemoizedTableBody = React.memo(
|
|
112
|
-
TableBody,
|
|
113
|
-
(prev, next) => prev.table.options.data === next.table.options.data,
|
|
114
|
-
)
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## Common Mistakes
|
|
118
|
-
|
|
119
|
-
### [HIGH] Rendering body cells with `row.getAllCells()` while visibility is registered
|
|
120
|
-
|
|
121
|
-
Wrong:
|
|
122
|
-
|
|
123
|
-
```tsx
|
|
124
|
-
// Toggling visibility has no effect on rendered cells
|
|
125
|
-
{
|
|
126
|
-
table.getAllLeafColumns().map((column) => <th key={column.id}>...</th>)
|
|
127
|
-
}
|
|
128
|
-
{
|
|
129
|
-
row.getAllCells().map((cell) => (
|
|
130
|
-
<td key={cell.id}>
|
|
131
|
-
<table.FlexRender cell={cell} />
|
|
132
|
-
</td>
|
|
133
|
-
))
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
Correct:
|
|
138
|
-
|
|
139
|
-
```tsx
|
|
140
|
-
// Header groups already respect visibility; use them for headers.
|
|
141
|
-
// For body cells, swap getAllCells → getVisibleCells.
|
|
142
|
-
<thead>
|
|
143
|
-
{table.getHeaderGroups().map((headerGroup) => (
|
|
144
|
-
<tr key={headerGroup.id}>
|
|
145
|
-
{headerGroup.headers.map((header) => (
|
|
146
|
-
<th key={header.id} colSpan={header.colSpan}>
|
|
147
|
-
{header.isPlaceholder ? null : <table.FlexRender header={header} />}
|
|
148
|
-
</th>
|
|
149
|
-
))}
|
|
150
|
-
</tr>
|
|
151
|
-
))}
|
|
152
|
-
</thead>
|
|
153
|
-
<tbody>
|
|
154
|
-
{table.getRowModel().rows.map((row) => (
|
|
155
|
-
<tr key={row.id}>
|
|
156
|
-
{row.getVisibleCells().map((cell) => (
|
|
157
|
-
<td key={cell.id}><table.FlexRender cell={cell} /></td>
|
|
158
|
-
))}
|
|
159
|
-
</tr>
|
|
160
|
-
))}
|
|
161
|
-
</tbody>
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
The `getAll*` accessors do NOT consult `columnVisibility` state. Only `Visible` variants and header-group APIs filter by visibility.
|
|
165
|
-
|
|
166
|
-
Source: docs/guide/column-visibility.md; examples/react/column-visibility/src/main.tsx
|
|
167
|
-
|
|
168
|
-
### [HIGH] `columnResizeMode: 'onChange'` + `column.getSize()` per cell + un-memoized body
|
|
169
|
-
|
|
170
|
-
Wrong:
|
|
171
|
-
|
|
172
|
-
```tsx
|
|
173
|
-
const table = useTable({
|
|
174
|
-
features: tableFeatures({ columnSizingFeature, columnResizingFeature }),
|
|
175
|
-
columnResizeMode: 'onChange',
|
|
176
|
-
})
|
|
177
|
-
<td style={{ width: cell.column.getSize() }}>{cell.renderValue()}</td>
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
Correct:
|
|
181
|
-
|
|
182
|
-
```tsx
|
|
183
|
-
// See "Performant 'onChange' resize" above — CSS variables + memoized body
|
|
184
|
-
const columnSizeVars = React.useMemo(() => {
|
|
185
|
-
/* … */
|
|
186
|
-
}, [table.state.columnResizing, table.state.columnSizing])
|
|
187
|
-
|
|
188
|
-
{
|
|
189
|
-
table.store.state.columnResizing.isResizingColumn ? (
|
|
190
|
-
<MemoizedTableBody table={table} />
|
|
191
|
-
) : (
|
|
192
|
-
<TableBody table={table} />
|
|
193
|
-
)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
;<div
|
|
197
|
-
className="td"
|
|
198
|
-
style={{ width: `calc(var(--col-${cell.column.id}-size) * 1px)` }}
|
|
199
|
-
/>
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
`'onChange'` commits a new `columnSizing` map on every pointer move. Per-cell `getSize()` blows the 16ms frame budget. The CSS-variable pattern caches widths once per resize batch.
|
|
203
|
-
|
|
204
|
-
Source: docs/guide/column-resizing.md; examples/react/column-resizing-performant/src/main.tsx
|
|
205
|
-
|
|
206
|
-
### [HIGH] Using v8 `enablePinning` at the table level
|
|
207
|
-
|
|
208
|
-
Wrong:
|
|
209
|
-
|
|
210
|
-
```ts
|
|
211
|
-
// v8 syntax — no longer disables pinning at table level in v9
|
|
212
|
-
const table = useTable({
|
|
213
|
-
features: tableFeatures({ columnPinningFeature }),
|
|
214
|
-
enablePinning: false, // ignored
|
|
215
|
-
})
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
Correct:
|
|
219
|
-
|
|
220
|
-
```ts
|
|
221
|
-
// v9 split: two distinct table-level options
|
|
222
|
-
const table = useTable({
|
|
223
|
-
features: tableFeatures({ columnPinningFeature, rowPinningFeature }),
|
|
224
|
-
enableColumnPinning: false,
|
|
225
|
-
enableRowPinning: false,
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
// Per-column opt-out is still spelled `enablePinning`:
|
|
229
|
-
columnHelper.accessor('id', {
|
|
230
|
-
enablePinning: false, // this column can't be pinned
|
|
231
|
-
})
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
v9 split `enablePinning` into `enableColumnPinning` and `enableRowPinning`. The bare name now refers ONLY to per-column opt-out.
|
|
235
|
-
|
|
236
|
-
Source: packages/table-core/src/features/column-pinning/columnPinningFeature.types.ts
|
|
237
|
-
|
|
238
|
-
### [HIGH] Defining `columns` inline (infinite loop once a layout feature commits state)
|
|
239
|
-
|
|
240
|
-
Wrong:
|
|
241
|
-
|
|
242
|
-
```tsx
|
|
243
|
-
function App() {
|
|
244
|
-
const columns = [
|
|
245
|
-
columnHelper.accessor('firstName', {
|
|
246
|
-
/* … */
|
|
247
|
-
}),
|
|
248
|
-
columnHelper.accessor('lastName', {
|
|
249
|
-
/* … */
|
|
250
|
-
}),
|
|
251
|
-
]
|
|
252
|
-
const table = useTable({
|
|
253
|
-
features: tableFeatures({ columnPinningFeature, columnResizingFeature }),
|
|
254
|
-
columns,
|
|
255
|
-
data,
|
|
256
|
-
})
|
|
257
|
-
}
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
Correct:
|
|
261
|
-
|
|
262
|
-
```tsx
|
|
263
|
-
const defaultColumns = columnHelper.columns([
|
|
264
|
-
columnHelper.accessor('firstName', {
|
|
265
|
-
/* … */
|
|
266
|
-
}),
|
|
267
|
-
columnHelper.accessor('lastName', {
|
|
268
|
-
/* … */
|
|
269
|
-
}),
|
|
270
|
-
])
|
|
271
|
-
function App() {
|
|
272
|
-
const [columns] = React.useState(() => [...defaultColumns])
|
|
273
|
-
const table = useTable({ features, columns, data })
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// or: useMemo
|
|
277
|
-
const columns = React.useMemo(
|
|
278
|
-
() =>
|
|
279
|
-
columnHelper.columns([
|
|
280
|
-
/* … */
|
|
281
|
-
]),
|
|
282
|
-
[],
|
|
283
|
-
)
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
Layout features commit state on every interaction. An inline `columns` array gets a new identity each render → table rebuild → another render. FAQ Pitfall 1.
|
|
287
|
-
|
|
288
|
-
Source: docs/faq.md; examples/react/column-pinning-split/src/main.tsx; examples/react/column-dnd/src/main.tsx
|
|
289
|
-
|
|
290
|
-
### [CRITICAL] Reimplementing visibility / pinning / resize logic manually
|
|
291
|
-
|
|
292
|
-
Wrong:
|
|
293
|
-
|
|
294
|
-
```ts
|
|
295
|
-
// Hand-rolled hide/show with a separate set
|
|
296
|
-
const [hidden, setHidden] = useState(new Set<string>())
|
|
297
|
-
const visibleColumns = useMemo(
|
|
298
|
-
() => columns.filter((c) => !hidden.has(c.id)),
|
|
299
|
-
[columns, hidden],
|
|
300
|
-
)
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
Correct:
|
|
304
|
-
|
|
305
|
-
```ts
|
|
306
|
-
const table = useTable({
|
|
307
|
-
features: tableFeatures({ columnVisibilityFeature }),
|
|
308
|
-
columns,
|
|
309
|
-
data,
|
|
310
|
-
})
|
|
311
|
-
column.toggleVisibility()
|
|
312
|
-
column.getIsVisible()
|
|
313
|
-
table.getVisibleLeafColumns()
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
Source: maintainer interview (Phase 4, 2026-05-17)
|
|
317
|
-
|
|
318
|
-
## See also
|
|
319
|
-
|
|
320
|
-
- `tanstack-table/state-management` — `state.columnVisibility` / `columnOrder` / `columnPinning` / `columnSizing` slices
|
|
321
|
-
- `tanstack-table/row-pinning` — analogous pinning for rows (different render pipeline)
|
|
322
|
-
- `tanstack-table/grouping` — `groupedColumnMode` interacts with `columnOrder`
|
|
323
|
-
|
|
324
|
-
## References
|
|
325
|
-
|
|
326
|
-
- [subsystems.md](references/subsystems.md) — full API surface per UI-state subsystem (visibility, ordering, pinning, sizing, resizing) plus MEDIUM-priority failure modes: reorder-pinned-via-`columnOrder`, react-dnd / react-beautiful-dnd avoidance, touch-resize handler
|
|
@@ -1,220 +0,0 @@
|
|
|
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
|