@tanstack/table-core 9.0.0-beta.43 → 9.0.0-beta.44

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.
Files changed (60) hide show
  1. package/dist/core/rows/coreRowsFeature.types.d.cts +12 -1
  2. package/dist/core/rows/coreRowsFeature.types.d.ts +12 -1
  3. package/dist/core/table/constructTable.cjs +1 -0
  4. package/dist/core/table/constructTable.cjs.map +1 -1
  5. package/dist/core/table/constructTable.js +1 -0
  6. package/dist/core/table/constructTable.js.map +1 -1
  7. package/dist/core/table/coreTablesFeature.types.d.cts +2 -1
  8. package/dist/core/table/coreTablesFeature.types.d.ts +2 -1
  9. package/dist/core/table/coreTablesFeature.utils.cjs +4 -1
  10. package/dist/core/table/coreTablesFeature.utils.cjs.map +1 -1
  11. package/dist/core/table/coreTablesFeature.utils.d.cts +2 -1
  12. package/dist/core/table/coreTablesFeature.utils.d.ts +2 -1
  13. package/dist/core/table/coreTablesFeature.utils.js +4 -1
  14. package/dist/core/table/coreTablesFeature.utils.js.map +1 -1
  15. package/dist/features/row-selection/rowSelectionFeature.cjs +13 -2
  16. package/dist/features/row-selection/rowSelectionFeature.cjs.map +1 -1
  17. package/dist/features/row-selection/rowSelectionFeature.js +13 -2
  18. package/dist/features/row-selection/rowSelectionFeature.js.map +1 -1
  19. package/dist/features/row-selection/rowSelectionFeature.types.d.cts +35 -5
  20. package/dist/features/row-selection/rowSelectionFeature.types.d.ts +35 -5
  21. package/dist/features/row-selection/rowSelectionFeature.utils.cjs +50 -3
  22. package/dist/features/row-selection/rowSelectionFeature.utils.cjs.map +1 -1
  23. package/dist/features/row-selection/rowSelectionFeature.utils.d.cts +8 -6
  24. package/dist/features/row-selection/rowSelectionFeature.utils.d.ts +8 -6
  25. package/dist/features/row-selection/rowSelectionFeature.utils.js +50 -3
  26. package/dist/features/row-selection/rowSelectionFeature.utils.js.map +1 -1
  27. package/dist/index.d.cts +2 -2
  28. package/dist/index.d.ts +2 -2
  29. package/dist/types/TableFeatures.d.cts +22 -2
  30. package/dist/types/TableFeatures.d.ts +22 -2
  31. package/package.json +1 -1
  32. package/skills/api-not-found/SKILL.md +1 -1
  33. package/skills/client-vs-server/SKILL.md +1 -1
  34. package/skills/column-faceting/SKILL.md +1 -1
  35. package/skills/column-filtering/SKILL.md +1 -1
  36. package/skills/column-ordering/SKILL.md +1 -1
  37. package/skills/column-pinning/SKILL.md +1 -1
  38. package/skills/column-resizing/SKILL.md +1 -1
  39. package/skills/column-sizing/SKILL.md +1 -1
  40. package/skills/column-visibility/SKILL.md +1 -1
  41. package/skills/core/SKILL.md +37 -2
  42. package/skills/custom-features/SKILL.md +30 -2
  43. package/skills/expanding/SKILL.md +1 -1
  44. package/skills/global-filtering/SKILL.md +1 -1
  45. package/skills/grouping/SKILL.md +1 -1
  46. package/skills/migrate-v8-to-v9/SKILL.md +1 -1
  47. package/skills/pagination/SKILL.md +1 -1
  48. package/skills/row-pinning/SKILL.md +1 -1
  49. package/skills/row-selection/SKILL.md +91 -3
  50. package/skills/sorting/SKILL.md +1 -1
  51. package/skills/table-features/SKILL.md +1 -1
  52. package/skills/typescript/SKILL.md +1 -1
  53. package/src/core/rows/coreRowsFeature.types.ts +12 -1
  54. package/src/core/table/constructTable.ts +4 -0
  55. package/src/core/table/coreTablesFeature.types.ts +2 -1
  56. package/src/core/table/coreTablesFeature.utils.ts +7 -1
  57. package/src/features/row-selection/rowSelectionFeature.ts +19 -1
  58. package/src/features/row-selection/rowSelectionFeature.types.ts +37 -3
  59. package/src/features/row-selection/rowSelectionFeature.utils.ts +114 -14
  60. package/src/types/TableFeatures.ts +32 -2
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: row-selection
3
3
  description: >
4
- Maintain rowSelection ID state with stable getRowId, single, multi, and subrow rules, selected row models, and manual-pagination semantics. Load when selected IDs outlive loaded Row objects or data removal.
4
+ Maintain rowSelection ID state with stable getRowId, single, multi, subrow, and Shift-range rules, selected row models, handler anchors, and manual-pagination semantics. Load when implementing getToggleSelectedHandler, enableRowRangeSelection, selectChildren, or selected IDs that outlive loaded Row objects.
5
5
  metadata:
6
6
  {
7
7
  type: sub-skill,
8
8
  library: '@tanstack/table-core',
9
- library_version: '9.0.0-beta.43',
9
+ library_version: '9.0.0-beta.44',
10
10
  }
11
11
  requires: ['core', 'table-features']
12
12
  sources:
@@ -20,7 +20,11 @@ This skill builds on `core` and `table-features`. Selection is independent ID st
20
20
  ## Setup
21
21
 
22
22
  ```ts
23
- import { rowSelectionFeature, tableFeatures } from '@tanstack/table-core'
23
+ import {
24
+ rowSelectionFeature,
25
+ tableFeatures,
26
+ type Row,
27
+ } from '@tanstack/table-core'
24
28
 
25
29
  type Person = { id: string; name: string }
26
30
  export const features = tableFeatures({ rowSelectionFeature })
@@ -39,6 +43,28 @@ const loadedSelectedRows = table.getSelectedRowModel().rows
39
43
 
40
44
  Use IDs for database-wide intent and row models for currently loaded objects.
41
45
 
46
+ ### Inclusive Shift ranges through the row handler
47
+
48
+ ```ts
49
+ export function getSelectionHandler(row: Row<typeof features, Person>) {
50
+ return row.getToggleSelectedHandler()
51
+ }
52
+ ```
53
+
54
+ The handler establishes a table-local anchor on ordinary interactions and applies the checked value to the inclusive current display-order range on Shift interactions. Range behavior is enabled by default; set `enableRowRangeSelection: false` to preserve non-range handler behavior. Direct `row.toggleSelected()` and `table.setRowSelection()` calls do not move that anchor.
55
+
56
+ Pass the original checkbox click event to this handler. DOM `change` events often omit modifier keys, so use the framework's click binding for row checkboxes unless its change event exposes the original click through `nativeEvent` (as React does).
57
+
58
+ ### Limit a range to explicitly displayed rows
59
+
60
+ ```ts
61
+ export function getDisplayedRowsOnlyHandler(row: Row<typeof features, Person>) {
62
+ return row.getToggleSelectedHandler({ selectChildren: false })
63
+ }
64
+ ```
65
+
66
+ The default `selectChildren: true` recursively changes selectable descendants of parents encountered in the range. Set it to `false` when collapsed descendants outside the display-order interval must remain unchanged.
67
+
42
68
  ## Common Mistakes
43
69
 
44
70
  ### [HIGH] Expecting selection to clean itself
@@ -71,6 +97,68 @@ Under manual pagination, unloaded selected IDs have no `Row` object in the curre
71
97
 
72
98
  Source: `docs/framework/react/guide/row-selection.md#note-if-you-are-using-manualpagination`
73
99
 
100
+ ### [HIGH] Bypassing the range-selection handler
101
+
102
+ Wrong:
103
+
104
+ ```ts
105
+ const onChange = (event: { target: { checked: boolean } }) =>
106
+ row.toggleSelected(event.target.checked)
107
+ ```
108
+
109
+ Correct:
110
+
111
+ ```ts
112
+ const onChange = row.getToggleSelectedHandler()
113
+ ```
114
+
115
+ Only successful interactions through `getToggleSelectedHandler()` establish or advance the Shift-range anchor. The handler also supports custom range-event detection through `isRowRangeSelectionEvent`.
116
+
117
+ Source: `docs/framework/react/guide/row-selection.md#shift-range-selection`
118
+
119
+ ### [HIGH] Binding a DOM change event that drops Shift
120
+
121
+ Wrong:
122
+
123
+ ```ts
124
+ checkbox.addEventListener('change', row.getToggleSelectedHandler())
125
+ ```
126
+
127
+ Correct:
128
+
129
+ ```ts
130
+ checkbox.addEventListener('click', row.getToggleSelectedHandler())
131
+ ```
132
+
133
+ The range modifier must be present on the event passed to the handler. Raw DOM `change` events do not reliably expose click modifier keys.
134
+
135
+ Source: `docs/framework/svelte/guide/row-selection.md#shift-range-selection`
136
+
137
+ ### [MEDIUM] Expecting ranges across unloaded server pages
138
+
139
+ Wrong:
140
+
141
+ ```ts
142
+ const options = {
143
+ manualPagination: true,
144
+ enableRowRangeSelection: true,
145
+ }
146
+ // Shift cannot select rows absent from data.
147
+ ```
148
+
149
+ Correct:
150
+
151
+ ```ts
152
+ const options = {
153
+ manualPagination: true,
154
+ getRowId: (row: Person) => row.id,
155
+ }
156
+ ```
157
+
158
+ Client-side ranges can cross pages because display order is pre-pagination. Manual/server pagination cannot include rows absent from the loaded `data`; select database-wide IDs in application state when that behavior is required.
159
+
160
+ Source: `docs/framework/react/guide/row-selection.md#shift-range-selection`, `https://github.com/TanStack/table/issues/4781`
161
+
74
162
  ## API Discovery
75
163
 
76
164
  Inspect `node_modules/@tanstack/table-core/src/features/row-selection/` for state, row-model variants, and selection enablement callbacks.
@@ -6,7 +6,7 @@ metadata:
6
6
  {
7
7
  type: sub-skill,
8
8
  library: '@tanstack/table-core',
9
- library_version: '9.0.0-beta.43',
9
+ library_version: '9.0.0-beta.44',
10
10
  }
11
11
  requires: ['core', 'table-features', 'client-vs-server']
12
12
  sources:
@@ -5,7 +5,7 @@ description: >
5
5
  metadata:
6
6
  type: sub-skill
7
7
  library: '@tanstack/table-core'
8
- library_version: '9.0.0-beta.43'
8
+ library_version: '9.0.0-beta.44'
9
9
  requires: ['core']
10
10
  sources:
11
11
  - 'TanStack/table:docs/guide/row-models.md'
@@ -5,7 +5,7 @@ description: >
5
5
  metadata:
6
6
  type: sub-skill
7
7
  library: '@tanstack/table-core'
8
- library_version: '9.0.0-beta.43'
8
+ library_version: '9.0.0-beta.44'
9
9
  requires: ['core', 'table-features']
10
10
  sources:
11
11
  - 'TanStack/table:docs/guide/helpers.md'
@@ -13,6 +13,15 @@ export interface Row_CoreProperties<
13
13
  Column<TFeatures, TData, unknown>,
14
14
  Cell<TFeatures, TData, unknown>
15
15
  >
16
+ /**
17
+ * Internal cache used while resolving the current display order.
18
+ *
19
+ * This value may be stale until display order is recomputed. Use
20
+ * `row.getDisplayIndex()` instead; it refreshes and validates the cached
21
+ * position before returning it.
22
+ *
23
+ * @internal
24
+ */
16
25
  _displayIndexCache: number
17
26
  _uniqueValuesCache: Record<string, unknown>
18
27
  _valuesCache: Record<string, unknown>
@@ -56,7 +65,9 @@ export interface Row_Row<
56
65
  > extends Row_CoreProperties<TFeatures, TData> {
57
66
  /**
58
67
  * Returns the zero-based index of the row in the current display order
59
- * before pagination, or `-1` if the row is not in that model.
68
+ * before pagination, or `-1` if the row is not in that model. Use this for
69
+ * display row-number columns instead of `row.index` or the internal
70
+ * `_displayIndexCache` field.
60
71
  */
61
72
  getDisplayIndex: () => number
62
73
  /**
@@ -166,6 +166,10 @@ export function constructTable<
166
166
  ),
167
167
  )
168
168
 
169
+ for (let i = 0; i < featuresList.length; i++) {
170
+ featuresList[i]!.initTableInstanceData?.(table)
171
+ }
172
+
169
173
  if (
170
174
  process.env.NODE_ENV === 'development' &&
171
175
  (tableOptions.debugAll || tableOptions.debugTable)
@@ -229,7 +229,8 @@ export interface Table_Table<
229
229
  *
230
230
  * Prefer feature-specific reset APIs, such as `resetPagination`, when a state
231
231
  * slice may be owned by an external atom or needs that feature's blank/default
232
- * reset behavior.
232
+ * reset behavior. After resetting internal atoms, this also invokes feature
233
+ * reset hooks for mutable, transient table-instance data.
233
234
  */
234
235
  reset: () => void
235
236
  /**
@@ -40,7 +40,8 @@ export function table_syncExternalStateToBaseAtoms<
40
40
  }
41
41
 
42
42
  /**
43
- * Resets all internal table base atoms to `table.initialState`.
43
+ * Resets all internal table base atoms to `table.initialState`, then clears
44
+ * transient instance data through registered feature reset hooks.
44
45
  *
45
46
  * This resets internally owned state slices in a single reactivity batch. Use
46
47
  * feature-specific reset APIs when a slice may be externally owned.
@@ -62,6 +63,11 @@ export function table_reset<
62
63
  ;(table.baseAtoms as any)[key].set(snap[key])
63
64
  }
64
65
  })
66
+
67
+ const features = Object.values(table._features)
68
+ for (let i = 0; i < features.length; i++) {
69
+ features[i]!.resetTableInstanceData?.(table)
70
+ }
65
71
  }
66
72
 
67
73
  /**
@@ -35,6 +35,16 @@ import type { TableFeature } from '../../types/TableFeatures'
35
35
  * Feature that adds row selection state and APIs for row and page selection.
36
36
  */
37
37
  export const rowSelectionFeature: TableFeature = {
38
+ initTableInstanceData: (table) => {
39
+ // @ts-ignore - _lastSelectedRowId is row selection table instance data
40
+ table._lastSelectedRowId = null
41
+ },
42
+
43
+ resetTableInstanceData: (table) => {
44
+ // @ts-ignore - _lastSelectedRowId is row selection table instance data
45
+ table._lastSelectedRowId = null
46
+ },
47
+
38
48
  getInitialState: (initialState) => {
39
49
  return {
40
50
  rowSelection: getDefaultRowSelectionState(),
@@ -47,7 +57,15 @@ export const rowSelectionFeature: TableFeature = {
47
57
  onRowSelectionChange: makeStateUpdater('rowSelection', table),
48
58
  enableRowSelection: true,
49
59
  enableMultiRowSelection: true,
60
+ enableRowRangeSelection: true,
50
61
  enableSubRowSelection: true,
62
+ isRowRangeSelectionEvent: (event) => {
63
+ const rangeEvent = event as {
64
+ shiftKey?: boolean
65
+ nativeEvent?: { shiftKey?: boolean }
66
+ }
67
+ return Boolean(rangeEvent.shiftKey || rangeEvent.nativeEvent?.shiftKey)
68
+ },
51
69
  }
52
70
  },
53
71
 
@@ -85,7 +103,7 @@ export const rowSelectionFeature: TableFeature = {
85
103
  fn: (row) => row_getCanMultiSelect(row),
86
104
  },
87
105
  row_getToggleSelectedHandler: {
88
- fn: (row) => row_getToggleSelectedHandler(row),
106
+ fn: (row, opts) => row_getToggleSelectedHandler(row, opts),
89
107
  },
90
108
  })
91
109
  },
@@ -5,6 +5,17 @@ import type { Row } from '../../types/Row'
5
5
 
6
6
  export type RowSelectionState = Record<string, true>
7
7
 
8
+ /**
9
+ * Controls how toggling a row affects its descendants.
10
+ */
11
+ export interface ToggleSelectedOptions {
12
+ /**
13
+ * Whether selectable child rows should be toggled recursively. Defaults to
14
+ * `true`.
15
+ */
16
+ selectChildren?: boolean
17
+ }
18
+
8
19
  export interface TableState_RowSelection {
9
20
  rowSelection: RowSelectionState
10
21
  }
@@ -13,6 +24,11 @@ export interface TableOptions_RowSelection<
13
24
  in out TFeatures extends TableFeatures,
14
25
  in out TData extends RowData,
15
26
  > {
27
+ /**
28
+ * Enables inclusive row range selection through
29
+ * `row.getToggleSelectedHandler()`. Defaults to `true`.
30
+ */
31
+ enableRowRangeSelection?: boolean
16
32
  /**
17
33
  * Allows rows to be selected alongside other rows.
18
34
  *
@@ -32,6 +48,14 @@ export interface TableOptions_RowSelection<
32
48
  * grouping features and defaults to `true`.
33
49
  */
34
50
  enableSubRowSelection?: boolean | ((row: Row<TFeatures, TData>) => boolean)
51
+ /**
52
+ * Determines whether a row-selection handler event should select or
53
+ * deselect the inclusive range from the most recent handler interaction.
54
+ *
55
+ * By default, events with `shiftKey` directly on the event or on
56
+ * `event.nativeEvent` are treated as range-selection events.
57
+ */
58
+ isRowRangeSelectionEvent?: (event: unknown) => boolean
35
59
  /**
36
60
  * Called with an updater when row selection state changes. Pair this with
37
61
  * `state.rowSelection` when using external state; external atoms can own the
@@ -44,7 +68,6 @@ export interface TableOptions_RowSelection<
44
68
  // row: Row<TFeatures, TData>
45
69
  // ) => boolean)
46
70
  // isAdditiveSelectEvent?: (e: unknown) => boolean
47
- // isInclusiveSelectEvent?: (e: unknown) => boolean
48
71
  // selectRowsFn?: (
49
72
  // table: Table<TFeatures, TData>,
50
73
  // rowModel: RowModel<TFeatures, TData>
@@ -78,18 +101,29 @@ export interface Row_RowSelection {
78
101
  getIsSomeSelected: () => boolean
79
102
  /**
80
103
  * Creates a checkbox-style handler that toggles this row's selected state.
104
+ * Pass the original checkbox click event, or a framework event whose
105
+ * `nativeEvent` is that click, so Shift range selection can detect the
106
+ * modifier key.
81
107
  */
82
- getToggleSelectedHandler: () => (event: unknown) => void
108
+ getToggleSelectedHandler: (
109
+ opts?: ToggleSelectedOptions,
110
+ ) => (event: unknown) => void
83
111
  /**
84
112
  * Selects/deselects the row.
85
113
  */
86
- toggleSelected: (value?: boolean, opts?: { selectChildren?: boolean }) => void
114
+ toggleSelected: (value?: boolean, opts?: ToggleSelectedOptions) => void
87
115
  }
88
116
 
89
117
  export interface Table_RowSelection<
90
118
  in out TFeatures extends TableFeatures,
91
119
  in out TData extends RowData,
92
120
  > {
121
+ /**
122
+ * The most recent row interacted with through the row selection handler.
123
+ *
124
+ * @internal
125
+ */
126
+ _lastSelectedRowId: string | null
93
127
  /**
94
128
  * Builds a selected-row model from rows after filtering.
95
129
  */
@@ -10,7 +10,10 @@ import type { TableFeatures } from '../../types/TableFeatures'
10
10
  import type { RowModel } from '../../core/row-models/coreRowModelsFeature.types'
11
11
  import type { Table_Internal } from '../../types/Table'
12
12
  import type { Row } from '../../types/Row'
13
- import type { RowSelectionState } from './rowSelectionFeature.types'
13
+ import type {
14
+ RowSelectionState,
15
+ ToggleSelectedOptions,
16
+ } from './rowSelectionFeature.types'
14
17
 
15
18
  // State APIs
16
19
 
@@ -66,6 +69,8 @@ export function table_resetRowSelection<
66
69
  TFeatures extends TableFeatures,
67
70
  TData extends RowData,
68
71
  >(table: Table_Internal<TFeatures, TData>, defaultState?: boolean) {
72
+ // @ts-ignore - _lastSelectedRowId is part of the RowSelection feature
73
+ table._lastSelectedRowId = null
69
74
  table_setRowSelection(
70
75
  table,
71
76
  defaultState
@@ -98,6 +103,8 @@ export function table_toggleAllRowsSelected<
98
103
  value?: boolean,
99
104
  opts?: { deselectAll?: boolean },
100
105
  ) {
106
+ // @ts-ignore - _lastSelectedRowId is part of the RowSelection feature
107
+ table._lastSelectedRowId = null
101
108
  table_setRowSelection(table, (old) => {
102
109
  value =
103
110
  typeof value !== 'undefined'
@@ -153,6 +160,8 @@ export function table_toggleAllPageRowsSelected<
153
160
  value?: boolean,
154
161
  opts?: { deselectAll?: boolean },
155
162
  ) {
163
+ // @ts-ignore - _lastSelectedRowId is part of the RowSelection feature
164
+ table._lastSelectedRowId = null
156
165
  table_setRowSelection(table, (old) => {
157
166
  const resolvedValue =
158
167
  typeof value !== 'undefined'
@@ -492,13 +501,7 @@ export function table_getToggleAllPageRowsSelectedHandler<
492
501
  export function row_toggleSelected<
493
502
  TFeatures extends TableFeatures,
494
503
  TData extends RowData,
495
- >(
496
- row: Row<TFeatures, TData>,
497
- value?: boolean,
498
- opts?: {
499
- selectChildren?: boolean
500
- },
501
- ) {
504
+ >(row: Row<TFeatures, TData>, value?: boolean, opts?: ToggleSelectedOptions) {
502
505
  const isSelected = row_getIsSelected(row)
503
506
 
504
507
  table_setRowSelection(row.table, (old) => {
@@ -643,7 +646,11 @@ export function row_getCanMultiSelect<
643
646
  * Creates a checkbox-style handler that selects or deselects this row.
644
647
  *
645
648
  * The handler is a no-op when the row cannot be selected and reads
646
- * `event.target.checked`.
649
+ * `event.target.checked`. Shift events select or deselect the inclusive range
650
+ * from the most recent selectable row handled by this table. The event's
651
+ * optional `persist()` method is called before it is read. Pass
652
+ * `selectChildren: false` to limit changes to rows explicitly present in the
653
+ * display-order interval.
647
654
  *
648
655
  * @example
649
656
  * ```ts
@@ -653,18 +660,111 @@ export function row_getCanMultiSelect<
653
660
  export function row_getToggleSelectedHandler<
654
661
  TFeatures extends TableFeatures,
655
662
  TData extends RowData,
656
- >(row: Row<TFeatures, TData>) {
663
+ >(row: Row<TFeatures, TData>, opts?: ToggleSelectedOptions) {
657
664
  const canSelect = row_getCanSelect(row)
658
665
 
659
666
  return (e: unknown) => {
660
667
  if (!canSelect) return
661
- row_toggleSelected(
662
- row,
663
- ((e as MouseEvent).target as HTMLInputElement).checked,
664
- )
668
+
669
+ const event = e as {
670
+ persist?: () => void
671
+ target: { checked: boolean }
672
+ }
673
+ event.persist?.()
674
+
675
+ const table = row.table
676
+ const checked = event.target.checked
677
+ // @ts-ignore - _lastSelectedRowId is part of the RowSelection feature
678
+ const anchorId = table._lastSelectedRowId
679
+ const canSelectRange =
680
+ table.options.enableRowRangeSelection !== false &&
681
+ anchorId !== null &&
682
+ row_getCanMultiSelect(row) &&
683
+ (table.options.isRowRangeSelectionEvent?.(e) ?? false)
684
+
685
+ if (
686
+ !canSelectRange ||
687
+ !selectRowRange(row, anchorId, checked, opts?.selectChildren ?? true)
688
+ ) {
689
+ row_toggleSelected(row, checked, opts)
690
+ }
691
+
692
+ // @ts-ignore - _lastSelectedRowId is part of the RowSelection feature
693
+ table._lastSelectedRowId = row.id
665
694
  }
666
695
  }
667
696
 
697
+ /**
698
+ * Resolves and mutates an inclusive interval in the table's latest logical
699
+ * display order.
700
+ *
701
+ * The anchor is resolved without throwing from the pre-pagination row model,
702
+ * then the core row model. Both endpoint display indexes must still identify
703
+ * those rows in the current order and both endpoints must support
704
+ * multi-selection. Eligible interval rows are applied through one row
705
+ * selection updater; non-selectable and non-multi-selectable rows are skipped.
706
+ * Returns `false` when the interaction should fall back to an ordinary toggle.
707
+ */
708
+ function selectRowRange<TFeatures extends TableFeatures, TData extends RowData>(
709
+ row: Row<TFeatures, TData>,
710
+ anchorId: string,
711
+ value: boolean,
712
+ includeChildren: boolean,
713
+ ): boolean {
714
+ const table = row.table
715
+ const rows = table.getRowsInDisplayOrder()
716
+ const anchorRow =
717
+ table.getPrePaginatedRowModel().rowsById[anchorId] ??
718
+ table.getCoreRowModel().rowsById[anchorId]
719
+
720
+ if (!anchorRow) {
721
+ return false
722
+ }
723
+
724
+ const anchorIndex = anchorRow.getDisplayIndex()
725
+ const rowIndex = row.getDisplayIndex()
726
+ const anchorAtIndex = rows[anchorIndex]
727
+ const rowAtIndex = rows[rowIndex]
728
+
729
+ if (
730
+ anchorIndex < 0 ||
731
+ rowIndex < 0 ||
732
+ anchorIndex >= rows.length ||
733
+ rowIndex >= rows.length ||
734
+ anchorAtIndex?.id !== anchorRow.id ||
735
+ rowAtIndex?.id !== row.id ||
736
+ !row_getCanMultiSelect(anchorRow) ||
737
+ !row_getCanMultiSelect(row)
738
+ ) {
739
+ return false
740
+ }
741
+
742
+ const start = Math.min(anchorIndex, rowIndex)
743
+ const end = Math.max(anchorIndex, rowIndex)
744
+
745
+ table_setRowSelection(table, (old) => {
746
+ const rowSelection = Object.assign(makeObjectMap<true>(), old)
747
+
748
+ for (let index = start; index <= end; index++) {
749
+ const rangeRow = rows[index]!
750
+ if (!row_getCanSelect(rangeRow) || !row_getCanMultiSelect(rangeRow)) {
751
+ continue
752
+ }
753
+ mutateRowIsSelected(
754
+ rowSelection,
755
+ rangeRow.id,
756
+ value,
757
+ includeChildren,
758
+ table,
759
+ )
760
+ }
761
+
762
+ return rowSelection
763
+ })
764
+
765
+ return true
766
+ }
767
+
668
768
  const mutateRowIsSelected = <
669
769
  TFeatures extends TableFeatures,
670
770
  TData extends RowData,
@@ -368,8 +368,9 @@ export interface TableFeature {
368
368
  *
369
369
  * The table is a singleton, unlike rows, columns, headers, and cells, so
370
370
  * table APIs are assigned directly instead of through a shared prototype.
371
- * This runs while the table is being constructed, after options and initial
372
- * state have been resolved.
371
+ * This hook is exclusively for assigning table methods. It runs after
372
+ * options, state atoms, and the store have been created and after every
373
+ * feature's `initTableInstanceData` hook has completed.
373
374
  */
374
375
  constructTableAPIs?: <TFeatures extends TableFeatures, TData extends RowData>(
375
376
  table: Table_Internal<TFeatures, TData>,
@@ -409,6 +410,22 @@ export interface TableFeature {
409
410
  * override feature defaults.
410
411
  */
411
412
  getInitialState?: (initialState: Partial<TableState_All>) => TableState_All
413
+ /**
414
+ * Initializes mutable, non-reactive data owned by this feature on the table
415
+ * instance.
416
+ *
417
+ * This runs once during table construction after options, state atoms, and
418
+ * the store are available, and before any feature's `constructTableAPIs`
419
+ * hook runs. Use `constructTableAPIs` exclusively for assigning table
420
+ * methods. Table resets do not rerun this hook; use
421
+ * `resetTableInstanceData` to clear transient instance data instead.
422
+ */
423
+ initTableInstanceData?: <
424
+ TFeatures extends TableFeatures,
425
+ TData extends RowData,
426
+ >(
427
+ table: Table_Internal<TFeatures, TData>,
428
+ ) => void
412
429
  /**
413
430
  * Initializes instance-specific data on each column.
414
431
  *
@@ -438,4 +455,17 @@ export interface TableFeature {
438
455
  >(
439
456
  row: Row<TFeatures, TData>,
440
457
  ) => void
458
+ /**
459
+ * Resets mutable, non-reactive table-instance data owned by this feature.
460
+ *
461
+ * This runs after internally owned state atoms have been restored to
462
+ * `table.initialState` by `table.reset()`. It is intended for transient
463
+ * feature data, not table state slices or externally controlled state.
464
+ */
465
+ resetTableInstanceData?: <
466
+ TFeatures extends TableFeatures,
467
+ TData extends RowData,
468
+ >(
469
+ table: Table_Internal<TFeatures, TData>,
470
+ ) => void
441
471
  }