@tanstack/table-core 9.0.0-beta.76 → 9.0.0-beta.78
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/dist/features/column-faceting/columnFacetingFeature.d.ts +6 -0
- package/dist/features/column-faceting/columnFacetingFeature.js +13 -35
- package/dist/features/column-faceting/columnFacetingFeature.utils.js +6 -2
- package/dist/features/column-filtering/columnFilteringFeature.types.d.ts +5 -0
- package/dist/features/column-filtering/columnFilteringFeature.utils.d.ts +6 -1
- package/dist/features/column-filtering/columnFilteringFeature.utils.js +9 -2
- package/dist/features/column-filtering/filterFns.d.ts +3 -1
- package/dist/features/column-filtering/filterFns.js +4 -1
- package/dist/features/column-filtering/filterRowsUtils.js +8 -0
- package/dist/features/column-visibility/columnVisibilityFeature.utils.d.ts +3 -1
- package/dist/features/column-visibility/columnVisibilityFeature.utils.js +9 -2
- package/dist/features/row-expanding/rowExpandingFeature.utils.d.ts +15 -6
- package/dist/features/row-expanding/rowExpandingFeature.utils.js +38 -15
- package/dist/features/row-pagination/createPaginatedRowModel.js +3 -0
- package/dist/features/row-sorting/rowSortingFeature.types.d.ts +9 -3
- package/dist/features/row-sorting/rowSortingFeature.utils.d.ts +8 -5
- package/dist/features/row-sorting/rowSortingFeature.utils.js +15 -8
- package/package.json +1 -1
- package/skills/aggregation/SKILL.md +1 -1
- package/skills/api-not-found/SKILL.md +1 -1
- package/skills/cell-selection/SKILL.md +1 -1
- package/skills/cell-spanning/SKILL.md +1 -1
- package/skills/client-vs-server/SKILL.md +1 -1
- package/skills/column-faceting/SKILL.md +1 -1
- package/skills/column-filtering/SKILL.md +1 -1
- package/skills/column-ordering/SKILL.md +1 -1
- package/skills/column-pinning/SKILL.md +1 -1
- package/skills/column-resizing/SKILL.md +1 -1
- package/skills/column-sizing/SKILL.md +1 -1
- package/skills/column-visibility/SKILL.md +1 -1
- package/skills/core/SKILL.md +1 -1
- package/skills/custom-features/SKILL.md +1 -1
- package/skills/expanding/SKILL.md +1 -1
- package/skills/global-filtering/SKILL.md +1 -1
- package/skills/grouping/SKILL.md +1 -1
- package/skills/migrate-v8-to-v9/SKILL.md +1 -1
- package/skills/pagination/SKILL.md +1 -1
- package/skills/row-pinning/SKILL.md +1 -1
- package/skills/row-selection/SKILL.md +1 -1
- package/skills/sorting/SKILL.md +1 -1
- package/skills/table-features/SKILL.md +1 -1
- package/skills/typescript/SKILL.md +1 -1
|
@@ -2,6 +2,12 @@ import { TableFeature } from "../../types/TableFeatures.js";
|
|
|
2
2
|
//#region src/features/column-faceting/columnFacetingFeature.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Feature that derives faceted row models, unique values, and min/max values for filters.
|
|
5
|
+
*
|
|
6
|
+
* These APIs are deliberately not memoized at this layer: the stock
|
|
7
|
+
* `createFaceted*` factories memoize internally (like every other stock row
|
|
8
|
+
* model), and an extra memo layer here would freeze custom factories whose
|
|
9
|
+
* data changes independently of the faceted row model. Custom factories own
|
|
10
|
+
* their memoization.
|
|
5
11
|
*/
|
|
6
12
|
declare const columnFacetingFeature: TableFeature;
|
|
7
13
|
//#endregion
|
|
@@ -1,51 +1,29 @@
|
|
|
1
|
-
import { assignPrototypeAPIs, assignTableAPIs
|
|
1
|
+
import { assignPrototypeAPIs, assignTableAPIs } from "../../utils.js";
|
|
2
2
|
import { column_getFacetedMinMaxValues, column_getFacetedRowModel, column_getFacetedUniqueValues, table_getGlobalFacetedMinMaxValues, table_getGlobalFacetedRowModel, table_getGlobalFacetedUniqueValues } from "./columnFacetingFeature.utils.js";
|
|
3
3
|
|
|
4
4
|
//#region src/features/column-faceting/columnFacetingFeature.ts
|
|
5
5
|
/**
|
|
6
6
|
* Feature that derives faceted row models, unique values, and min/max values for filters.
|
|
7
|
+
*
|
|
8
|
+
* These APIs are deliberately not memoized at this layer: the stock
|
|
9
|
+
* `createFaceted*` factories memoize internally (like every other stock row
|
|
10
|
+
* model), and an extra memo layer here would freeze custom factories whose
|
|
11
|
+
* data changes independently of the faceted row model. Custom factories own
|
|
12
|
+
* their memoization.
|
|
7
13
|
*/
|
|
8
14
|
const columnFacetingFeature = {
|
|
9
15
|
assignColumnPrototype: (prototype, table) => {
|
|
10
16
|
assignPrototypeAPIs("columnFacetingFeature", prototype, table, {
|
|
11
|
-
column_getFacetedRowModel: {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
table.atoms.columnFilters?.get(),
|
|
15
|
-
table.atoms.globalFilter?.get(),
|
|
16
|
-
table.getFilteredRowModel().rows
|
|
17
|
-
],
|
|
18
|
-
fn: (column) => column_getFacetedRowModel(column, column.table)
|
|
19
|
-
},
|
|
20
|
-
column_getFacetedMinMaxValues: {
|
|
21
|
-
memoDeps: (column) => [callMemoOrStaticFn(column, "getFacetedRowModel", column_getFacetedRowModel, column.table).flatRows],
|
|
22
|
-
fn: (column) => column_getFacetedMinMaxValues(column, column.table)
|
|
23
|
-
},
|
|
24
|
-
column_getFacetedUniqueValues: {
|
|
25
|
-
memoDeps: (column) => [callMemoOrStaticFn(column, "getFacetedRowModel", column_getFacetedRowModel, column.table).flatRows],
|
|
26
|
-
fn: (column) => column_getFacetedUniqueValues(column, column.table)
|
|
27
|
-
}
|
|
17
|
+
column_getFacetedRowModel: { fn: (column) => column_getFacetedRowModel(column, column.table) },
|
|
18
|
+
column_getFacetedMinMaxValues: { fn: (column) => column_getFacetedMinMaxValues(column, column.table) },
|
|
19
|
+
column_getFacetedUniqueValues: { fn: (column) => column_getFacetedUniqueValues(column, column.table) }
|
|
28
20
|
});
|
|
29
21
|
},
|
|
30
22
|
constructTableAPIs: (table) => {
|
|
31
23
|
assignTableAPIs("columnFacetingFeature", table, {
|
|
32
|
-
table_getGlobalFacetedRowModel: {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
table.atoms.columnFilters?.get(),
|
|
36
|
-
table.atoms.globalFilter?.get(),
|
|
37
|
-
table.getFilteredRowModel().rows
|
|
38
|
-
],
|
|
39
|
-
fn: () => table_getGlobalFacetedRowModel(table)
|
|
40
|
-
},
|
|
41
|
-
table_getGlobalFacetedMinMaxValues: {
|
|
42
|
-
memoDeps: () => [callMemoOrStaticFn(table, "getGlobalFacetedRowModel", table_getGlobalFacetedRowModel).flatRows],
|
|
43
|
-
fn: () => table_getGlobalFacetedMinMaxValues(table)
|
|
44
|
-
},
|
|
45
|
-
table_getGlobalFacetedUniqueValues: {
|
|
46
|
-
memoDeps: () => [callMemoOrStaticFn(table, "getGlobalFacetedRowModel", table_getGlobalFacetedRowModel).flatRows],
|
|
47
|
-
fn: () => table_getGlobalFacetedUniqueValues(table)
|
|
48
|
-
}
|
|
24
|
+
table_getGlobalFacetedRowModel: { fn: () => table_getGlobalFacetedRowModel(table) },
|
|
25
|
+
table_getGlobalFacetedMinMaxValues: { fn: () => table_getGlobalFacetedMinMaxValues(table) },
|
|
26
|
+
table_getGlobalFacetedUniqueValues: { fn: () => table_getGlobalFacetedUniqueValues(table) }
|
|
49
27
|
});
|
|
50
28
|
}
|
|
51
29
|
};
|
|
@@ -51,9 +51,13 @@ function column_getFacetedRowModel(column, table) {
|
|
|
51
51
|
function column_getFacetedUniqueValues(column, table) {
|
|
52
52
|
const facetedUniqueValues = table._rowModels.facetedUniqueValues ??= makeObjectMap();
|
|
53
53
|
let facetedUniqueValuesFn = facetedUniqueValues[column.id];
|
|
54
|
-
if (!facetedUniqueValuesFn) facetedUniqueValuesFn = facetedUniqueValues[column.id] = table.options.features.facetedUniqueValues?.(table, column.id) ?? (
|
|
54
|
+
if (!facetedUniqueValuesFn) facetedUniqueValuesFn = facetedUniqueValues[column.id] = table.options.features.facetedUniqueValues?.(table, column.id) ?? createStableEmptyMapFn();
|
|
55
55
|
return facetedUniqueValuesFn();
|
|
56
56
|
}
|
|
57
|
+
function createStableEmptyMapFn() {
|
|
58
|
+
const emptyMap = /* @__PURE__ */ new Map();
|
|
59
|
+
return () => emptyMap;
|
|
60
|
+
}
|
|
57
61
|
/**
|
|
58
62
|
* Computes min and max numeric facet values for the global filter context.
|
|
59
63
|
*
|
|
@@ -99,7 +103,7 @@ function table_getGlobalFacetedRowModel(table) {
|
|
|
99
103
|
* ```
|
|
100
104
|
*/
|
|
101
105
|
function table_getGlobalFacetedUniqueValues(table) {
|
|
102
|
-
if (!table._rowModels.globalFacetedUniqueValues) table._rowModels.globalFacetedUniqueValues = table.options.features.facetedUniqueValues?.(table, "__global__") ?? (
|
|
106
|
+
if (!table._rowModels.globalFacetedUniqueValues) table._rowModels.globalFacetedUniqueValues = table.options.features.facetedUniqueValues?.(table, "__global__") ?? createStableEmptyMapFn();
|
|
103
107
|
const facetedUniqueValuesFn = table._rowModels.globalFacetedUniqueValues;
|
|
104
108
|
return facetedUniqueValuesFn();
|
|
105
109
|
}
|
|
@@ -39,6 +39,11 @@ interface FilterFn<in out TFeatures extends TableFeatures, in out TData extends
|
|
|
39
39
|
/**
|
|
40
40
|
* Removes the filter from `state.columnFilters` when the filter value fails
|
|
41
41
|
* this test (e.g. empty strings for text filters).
|
|
42
|
+
*
|
|
43
|
+
* When provided, this test is authoritative: values it keeps stay in filter
|
|
44
|
+
* state even when they are empty strings, which the default heuristic would
|
|
45
|
+
* otherwise remove. An `undefined` filter value always clears the filter
|
|
46
|
+
* regardless.
|
|
42
47
|
*/
|
|
43
48
|
autoRemove?: ColumnFilterAutoRemoveTestFn<TFeatures, TData>;
|
|
44
49
|
/**
|
|
@@ -133,7 +133,12 @@ declare function table_resetColumnFilters<TFeatures extends TableFeatures, TData
|
|
|
133
133
|
/**
|
|
134
134
|
* Returns whether a filter value should be removed from filter state.
|
|
135
135
|
*
|
|
136
|
-
*
|
|
136
|
+
* `undefined` always removes: it is the universal "clear this filter"
|
|
137
|
+
* sentinel used by `setFilterValue(undefined)` and functional updaters. For
|
|
138
|
+
* any other value, a filter function's `autoRemove` hook is authoritative
|
|
139
|
+
* when provided, so custom filter functions can keep values (such as empty
|
|
140
|
+
* strings) that the default heuristic would drop. Without an `autoRemove`
|
|
141
|
+
* hook, empty strings are removed.
|
|
137
142
|
*
|
|
138
143
|
* @example
|
|
139
144
|
* ```ts
|
|
@@ -199,7 +199,12 @@ function table_resetColumnFilters(table, defaultState) {
|
|
|
199
199
|
/**
|
|
200
200
|
* Returns whether a filter value should be removed from filter state.
|
|
201
201
|
*
|
|
202
|
-
*
|
|
202
|
+
* `undefined` always removes: it is the universal "clear this filter"
|
|
203
|
+
* sentinel used by `setFilterValue(undefined)` and functional updaters. For
|
|
204
|
+
* any other value, a filter function's `autoRemove` hook is authoritative
|
|
205
|
+
* when provided, so custom filter functions can keep values (such as empty
|
|
206
|
+
* strings) that the default heuristic would drop. Without an `autoRemove`
|
|
207
|
+
* hook, empty strings are removed.
|
|
203
208
|
*
|
|
204
209
|
* @example
|
|
205
210
|
* ```ts
|
|
@@ -207,7 +212,9 @@ function table_resetColumnFilters(table, defaultState) {
|
|
|
207
212
|
* ```
|
|
208
213
|
*/
|
|
209
214
|
function shouldAutoRemoveFilter(filterFn, value, column) {
|
|
210
|
-
|
|
215
|
+
if (typeof value === "undefined") return true;
|
|
216
|
+
if (filterFn?.autoRemove) return !!filterFn.autoRemove(value, column);
|
|
217
|
+
return typeof value === "string" && !value;
|
|
211
218
|
}
|
|
212
219
|
|
|
213
220
|
//#endregion
|
|
@@ -143,7 +143,9 @@ declare const filterFn_betweenInclusive: CreatedFilterFn<any, any>;
|
|
|
143
143
|
* Keeps rows whose numeric value is inside an inclusive `[min, max]` range.
|
|
144
144
|
*
|
|
145
145
|
* Filter values are normalized so blank endpoints become open-ended and
|
|
146
|
-
* reversed endpoints are swapped.
|
|
146
|
+
* reversed endpoints are swapped. Only real numbers can fall inside the
|
|
147
|
+
* range: non-numeric row values (`null`, `undefined`, strings, booleans)
|
|
148
|
+
* never match.
|
|
147
149
|
*/
|
|
148
150
|
declare const filterFn_inNumberRange: CreatedFilterFn<any, any>;
|
|
149
151
|
/**
|
|
@@ -207,10 +207,13 @@ const filterFn_betweenInclusive = constructFilterFn({
|
|
|
207
207
|
* Keeps rows whose numeric value is inside an inclusive `[min, max]` range.
|
|
208
208
|
*
|
|
209
209
|
* Filter values are normalized so blank endpoints become open-ended and
|
|
210
|
-
* reversed endpoints are swapped.
|
|
210
|
+
* reversed endpoints are swapped. Only real numbers can fall inside the
|
|
211
|
+
* range: non-numeric row values (`null`, `undefined`, strings, booleans)
|
|
212
|
+
* never match.
|
|
211
213
|
*/
|
|
212
214
|
const filterFn_inNumberRange = constructFilterFn({
|
|
213
215
|
filter: (dataValue, filterValue) => {
|
|
216
|
+
if (typeof dataValue !== "number" || Number.isNaN(dataValue)) return false;
|
|
214
217
|
const [min, max] = filterValue;
|
|
215
218
|
return dataValue >= min && dataValue <= max;
|
|
216
219
|
},
|
|
@@ -67,6 +67,7 @@ function filterRowModelFromRoot(rowsToFilter, filterRow, table) {
|
|
|
67
67
|
filteredRows.push(row);
|
|
68
68
|
newFilteredFlatRows.push(row);
|
|
69
69
|
newFilteredRowsById[row.id] = row;
|
|
70
|
+
if (row.subRows.length && depth >= maxDepth) addSubRowsToFlatArrays(row.subRows, newFilteredFlatRows, newFilteredRowsById);
|
|
70
71
|
}
|
|
71
72
|
return filteredRows;
|
|
72
73
|
};
|
|
@@ -76,6 +77,13 @@ function filterRowModelFromRoot(rowsToFilter, filterRow, table) {
|
|
|
76
77
|
rowsById: newFilteredRowsById
|
|
77
78
|
};
|
|
78
79
|
}
|
|
80
|
+
function addSubRowsToFlatArrays(subRows, flatRows, rowsById) {
|
|
81
|
+
for (const subRow of subRows) {
|
|
82
|
+
flatRows.push(subRow);
|
|
83
|
+
rowsById[subRow.id] = subRow;
|
|
84
|
+
if (subRow.subRows.length) addSubRowsToFlatArrays(subRow.subRows, flatRows, rowsById);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
79
87
|
|
|
80
88
|
//#endregion
|
|
81
89
|
export { filterRows };
|
|
@@ -23,7 +23,9 @@ declare function getDefaultColumnVisibilityState(): ColumnVisibilityState;
|
|
|
23
23
|
* Updates this column's visibility when hiding is allowed.
|
|
24
24
|
*
|
|
25
25
|
* Passing `visible` stores that value. Omitting it flips the column's current
|
|
26
|
-
* visibility state.
|
|
26
|
+
* visibility state. Group columns update their hideable leaf columns because
|
|
27
|
+
* visibility state is keyed by leaf column ids. Columns that cannot hide stay
|
|
28
|
+
* unchanged.
|
|
27
29
|
*
|
|
28
30
|
* @example
|
|
29
31
|
* ```ts
|
|
@@ -20,7 +20,9 @@ function getDefaultColumnVisibilityState() {
|
|
|
20
20
|
* Updates this column's visibility when hiding is allowed.
|
|
21
21
|
*
|
|
22
22
|
* Passing `visible` stores that value. Omitting it flips the column's current
|
|
23
|
-
* visibility state.
|
|
23
|
+
* visibility state. Group columns update their hideable leaf columns because
|
|
24
|
+
* visibility state is keyed by leaf column ids. Columns that cannot hide stay
|
|
25
|
+
* unchanged.
|
|
24
26
|
*
|
|
25
27
|
* @example
|
|
26
28
|
* ```ts
|
|
@@ -30,7 +32,12 @@ function getDefaultColumnVisibilityState() {
|
|
|
30
32
|
function column_toggleVisibility(column, visible) {
|
|
31
33
|
if (column_getCanHide(column)) table_setColumnVisibility(column.table, (old) => {
|
|
32
34
|
const next = Object.assign(makeObjectMap(), old);
|
|
33
|
-
|
|
35
|
+
const nextVisible = visible ?? !callMemoOrStaticFn(column, "getIsVisible", column_getIsVisible);
|
|
36
|
+
const leafColumns = column.getLeafColumns();
|
|
37
|
+
for (let i = 0; i < leafColumns.length; i++) {
|
|
38
|
+
const leafColumn = leafColumns[i];
|
|
39
|
+
if (column_getCanHide(leafColumn)) next[leafColumn.id] = nextVisible;
|
|
40
|
+
}
|
|
34
41
|
return next;
|
|
35
42
|
});
|
|
36
43
|
}
|
|
@@ -48,6 +48,9 @@ declare function table_setExpanded<TFeatures extends TableFeatures, TData extend
|
|
|
48
48
|
* an empty map. Omitting the value toggles based on whether all rows are
|
|
49
49
|
* currently expanded.
|
|
50
50
|
*
|
|
51
|
+
* The call is a no-op (no `onExpandedChange`) when no row can expand or when
|
|
52
|
+
* the requested state matches the current state exactly.
|
|
53
|
+
*
|
|
51
54
|
* @example
|
|
52
55
|
* ```ts
|
|
53
56
|
* table_toggleAllRowsExpanded(table)
|
|
@@ -100,10 +103,12 @@ declare function table_getToggleAllRowsExpandedHandler<TFeatures extends TableFe
|
|
|
100
103
|
*/
|
|
101
104
|
declare function table_getIsSomeRowsExpanded<TFeatures extends TableFeatures, TData extends RowData>(table: Table<TFeatures, TData>): boolean;
|
|
102
105
|
/**
|
|
103
|
-
* Checks whether every row in the current row model is expanded.
|
|
106
|
+
* Checks whether every expandable row in the current row model is expanded.
|
|
104
107
|
*
|
|
105
108
|
* The special expanded-all value `true` returns true immediately. Empty
|
|
106
|
-
* expanded state returns false.
|
|
109
|
+
* expanded state returns false. Rows that cannot expand are ignored, so a
|
|
110
|
+
* materialized expanded-all map (which only contains expandable row ids)
|
|
111
|
+
* still counts as all rows expanded.
|
|
107
112
|
*
|
|
108
113
|
* @example
|
|
109
114
|
* ```ts
|
|
@@ -114,8 +119,8 @@ declare function table_getIsAllRowsExpanded<TFeatures extends TableFeatures, TDa
|
|
|
114
119
|
/**
|
|
115
120
|
* Computes the deepest expanded row id depth.
|
|
116
121
|
*
|
|
117
|
-
* Row ids are split on `.`; expanded-all state scans the current row model
|
|
118
|
-
* while explicit expanded state scans its expanded id keys.
|
|
122
|
+
* Row ids are split on `.`; expanded-all state scans the current row model's
|
|
123
|
+
* expandable rows, while explicit expanded state scans its expanded id keys.
|
|
119
124
|
*
|
|
120
125
|
* @example
|
|
121
126
|
* ```ts
|
|
@@ -127,8 +132,12 @@ declare function table_getExpandedDepth<TFeatures extends TableFeatures, TData e
|
|
|
127
132
|
* Expands or collapses this row.
|
|
128
133
|
*
|
|
129
134
|
* Omitting `expanded` toggles the row. If the current state is expanded-all,
|
|
130
|
-
* the function first materializes that state into a row-id map
|
|
131
|
-
* the row-specific change.
|
|
135
|
+
* the function first materializes that state into a row-id map (containing
|
|
136
|
+
* only expandable row ids) before applying the row-specific change.
|
|
137
|
+
*
|
|
138
|
+
* The call is a no-op (no `onExpandedChange`) when the requested state matches
|
|
139
|
+
* the current state, or when expanding a row that cannot expand. Collapsing is
|
|
140
|
+
* always allowed so stale expanded ids can be cleaned up.
|
|
132
141
|
*
|
|
133
142
|
* @example
|
|
134
143
|
* ```ts
|
|
@@ -52,14 +52,24 @@ function table_setExpanded(table, updater) {
|
|
|
52
52
|
* an empty map. Omitting the value toggles based on whether all rows are
|
|
53
53
|
* currently expanded.
|
|
54
54
|
*
|
|
55
|
+
* The call is a no-op (no `onExpandedChange`) when no row can expand or when
|
|
56
|
+
* the requested state matches the current state exactly.
|
|
57
|
+
*
|
|
55
58
|
* @example
|
|
56
59
|
* ```ts
|
|
57
60
|
* table_toggleAllRowsExpanded(table)
|
|
58
61
|
* ```
|
|
59
62
|
*/
|
|
60
63
|
function table_toggleAllRowsExpanded(table, expanded) {
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
const currentExpanded = table.atoms.expanded?.get() ?? {};
|
|
65
|
+
if (expanded ?? !table_getIsAllRowsExpanded(table)) {
|
|
66
|
+
if (currentExpanded === true) return;
|
|
67
|
+
if (!table_getCanSomeRowsExpand(table)) return;
|
|
68
|
+
table_setExpanded(table, true);
|
|
69
|
+
} else {
|
|
70
|
+
if (currentExpanded !== true && !Object.keys(currentExpanded).length) return;
|
|
71
|
+
table_setExpanded(table, makeObjectMap());
|
|
72
|
+
}
|
|
63
73
|
}
|
|
64
74
|
/**
|
|
65
75
|
* Resets `expanded` to the configured initial state or feature default.
|
|
@@ -119,10 +129,12 @@ function table_getIsSomeRowsExpanded(table) {
|
|
|
119
129
|
return expanded === true || Object.values(expanded).some(Boolean);
|
|
120
130
|
}
|
|
121
131
|
/**
|
|
122
|
-
* Checks whether every row in the current row model is expanded.
|
|
132
|
+
* Checks whether every expandable row in the current row model is expanded.
|
|
123
133
|
*
|
|
124
134
|
* The special expanded-all value `true` returns true immediately. Empty
|
|
125
|
-
* expanded state returns false.
|
|
135
|
+
* expanded state returns false. Rows that cannot expand are ignored, so a
|
|
136
|
+
* materialized expanded-all map (which only contains expandable row ids)
|
|
137
|
+
* still counts as all rows expanded.
|
|
126
138
|
*
|
|
127
139
|
* @example
|
|
128
140
|
* ```ts
|
|
@@ -133,14 +145,16 @@ function table_getIsAllRowsExpanded(table) {
|
|
|
133
145
|
const expanded = table.atoms.expanded?.get() ?? {};
|
|
134
146
|
if (expanded === true) return true;
|
|
135
147
|
if (!Object.keys(expanded).length) return false;
|
|
136
|
-
|
|
148
|
+
const expandableRows = table.getRowModel().flatRows.filter((row) => row_getCanExpand(row));
|
|
149
|
+
if (!expandableRows.length) return false;
|
|
150
|
+
if (expandableRows.some((row) => !row_getIsExpanded(row))) return false;
|
|
137
151
|
return true;
|
|
138
152
|
}
|
|
139
153
|
/**
|
|
140
154
|
* Computes the deepest expanded row id depth.
|
|
141
155
|
*
|
|
142
|
-
* Row ids are split on `.`; expanded-all state scans the current row model
|
|
143
|
-
* while explicit expanded state scans its expanded id keys.
|
|
156
|
+
* Row ids are split on `.`; expanded-all state scans the current row model's
|
|
157
|
+
* expandable rows, while explicit expanded state scans its expanded id keys.
|
|
144
158
|
*
|
|
145
159
|
* @example
|
|
146
160
|
* ```ts
|
|
@@ -149,7 +163,8 @@ function table_getIsAllRowsExpanded(table) {
|
|
|
149
163
|
*/
|
|
150
164
|
function table_getExpandedDepth(table) {
|
|
151
165
|
let maxDepth = 0;
|
|
152
|
-
|
|
166
|
+
const expanded = table.atoms.expanded?.get();
|
|
167
|
+
(expanded === true ? Object.values(table.getRowModel().rowsById).filter((row) => row_getCanExpand(row)).map((row) => row.id) : Object.keys(expanded ?? {})).forEach((id) => {
|
|
153
168
|
const splitId = id.split(".");
|
|
154
169
|
maxDepth = Math.max(maxDepth, splitId.length);
|
|
155
170
|
});
|
|
@@ -159,8 +174,12 @@ function table_getExpandedDepth(table) {
|
|
|
159
174
|
* Expands or collapses this row.
|
|
160
175
|
*
|
|
161
176
|
* Omitting `expanded` toggles the row. If the current state is expanded-all,
|
|
162
|
-
* the function first materializes that state into a row-id map
|
|
163
|
-
* the row-specific change.
|
|
177
|
+
* the function first materializes that state into a row-id map (containing
|
|
178
|
+
* only expandable row ids) before applying the row-specific change.
|
|
179
|
+
*
|
|
180
|
+
* The call is a no-op (no `onExpandedChange`) when the requested state matches
|
|
181
|
+
* the current state, or when expanding a row that cannot expand. Collapsing is
|
|
182
|
+
* always allowed so stale expanded ids can be cleaned up.
|
|
164
183
|
*
|
|
165
184
|
* @example
|
|
166
185
|
* ```ts
|
|
@@ -168,19 +187,23 @@ function table_getExpandedDepth(table) {
|
|
|
168
187
|
* ```
|
|
169
188
|
*/
|
|
170
189
|
function row_toggleExpanded(row, expanded) {
|
|
190
|
+
const currentExpanded = row.table.atoms.expanded?.get() ?? {};
|
|
191
|
+
const currentExists = currentExpanded === true || isExpandedRowId(currentExpanded, row.id);
|
|
192
|
+
const targetExpanded = expanded ?? !currentExists;
|
|
193
|
+
if (targetExpanded === currentExists) return;
|
|
194
|
+
if (targetExpanded && !row_getCanExpand(row)) return;
|
|
171
195
|
table_setExpanded(row.table, (old) => {
|
|
172
196
|
const exists = old === true ? true : isExpandedRowId(old, row.id);
|
|
173
197
|
let oldExpanded = makeObjectMap();
|
|
174
|
-
if (old === true) Object.
|
|
175
|
-
oldExpanded[
|
|
198
|
+
if (old === true) Object.values(row.table.getRowModel().rowsById).forEach((rowModelRow) => {
|
|
199
|
+
if (row_getCanExpand(rowModelRow)) oldExpanded[rowModelRow.id] = true;
|
|
176
200
|
});
|
|
177
201
|
else oldExpanded = Object.assign(makeObjectMap(), old);
|
|
178
|
-
|
|
179
|
-
if (!exists && expanded) {
|
|
202
|
+
if (!exists && targetExpanded) {
|
|
180
203
|
oldExpanded[row.id] = true;
|
|
181
204
|
return oldExpanded;
|
|
182
205
|
}
|
|
183
|
-
if (exists && !
|
|
206
|
+
if (exists && !targetExpanded) {
|
|
184
207
|
const rest = makeObjectMap();
|
|
185
208
|
const rowIds = Object.keys(oldExpanded);
|
|
186
209
|
for (let i = 0; i < rowIds.length; i++) {
|
|
@@ -45,7 +45,10 @@ function _createPaginatedRowModel(table) {
|
|
|
45
45
|
rowsById
|
|
46
46
|
};
|
|
47
47
|
paginatedRowModel.flatRows = [];
|
|
48
|
+
const seenFlatRows = /* @__PURE__ */ new Set();
|
|
48
49
|
const handleRow = (row) => {
|
|
50
|
+
if (seenFlatRows.has(row.id)) return;
|
|
51
|
+
seenFlatRows.add(row.id);
|
|
49
52
|
paginatedRowModel.flatRows.push(row);
|
|
50
53
|
if (row.subRows.length) row.subRows.forEach(handleRow);
|
|
51
54
|
};
|
|
@@ -89,11 +89,15 @@ interface ColumnDef_RowSorting<in out TFeatures extends TableFeatures, in out TD
|
|
|
89
89
|
/**
|
|
90
90
|
* The priority of undefined values when sorting this column.
|
|
91
91
|
* - `false`
|
|
92
|
-
* - Undefined values will be
|
|
92
|
+
* - Undefined values will be passed to the sorting function like any other value with no special handling; the sorting function is responsible for handling them
|
|
93
93
|
* - `-1`
|
|
94
94
|
* - Undefined values will be sorted with higher priority (ascending) (if ascending, undefined will appear on the beginning of the list)
|
|
95
95
|
* - `1`
|
|
96
96
|
* - Undefined values will be sorted with lower priority (descending) (if ascending, undefined will appear on the end of the list)
|
|
97
|
+
* - `'first'`
|
|
98
|
+
* - Undefined values will be pushed to the beginning of the list regardless of sort direction
|
|
99
|
+
* - `'last'`
|
|
100
|
+
* - Undefined values will be pushed to the end of the list regardless of sort direction
|
|
97
101
|
*/
|
|
98
102
|
sortUndefined?: false | -1 | 1 | 'first' | 'last';
|
|
99
103
|
}
|
|
@@ -127,9 +131,11 @@ interface Column_RowSorting<in out TFeatures extends TableFeatures, in out TData
|
|
|
127
131
|
*/
|
|
128
132
|
getIsSorted: () => false | SortDirection;
|
|
129
133
|
/**
|
|
130
|
-
* Returns the next sorting order.
|
|
134
|
+
* Returns the next sorting order. Pass `multi` to resolve the order for a
|
|
135
|
+
* multi-sort toggle, where `enableMultiRemove` governs whether the cycle can
|
|
136
|
+
* remove the sort.
|
|
131
137
|
*/
|
|
132
|
-
getNextSortingOrder: () => SortDirection | false;
|
|
138
|
+
getNextSortingOrder: (multi?: boolean) => SortDirection | false;
|
|
133
139
|
/**
|
|
134
140
|
* Finds this column's position in the ordered sorting state.
|
|
135
141
|
*/
|
|
@@ -67,10 +67,13 @@ declare function table_autoResetSorting<TFeatures extends TableFeatures, TData e
|
|
|
67
67
|
*/
|
|
68
68
|
declare function column_getAutoSortFn<TFeatures extends TableFeatures, TData extends RowData, TValue extends CellData = CellData>(column: Column<TFeatures, TData, TValue>): SortFn<TFeatures, TData>;
|
|
69
69
|
/**
|
|
70
|
-
* Chooses the default first sort direction from
|
|
70
|
+
* Chooses the default first sort direction from sampled filtered row values.
|
|
71
71
|
*
|
|
72
|
-
*
|
|
73
|
-
* types
|
|
72
|
+
* The first non-nullish value among the sampled rows decides: string columns
|
|
73
|
+
* start ascending so alphabetical order is natural; other value types (or
|
|
74
|
+
* columns with no non-nullish sample) start descending. Sampling past leading
|
|
75
|
+
* nullish values keeps the toggle cycle stable when sorting or a data swap
|
|
76
|
+
* moves an empty value into the first row.
|
|
74
77
|
*
|
|
75
78
|
* @example
|
|
76
79
|
* ```ts
|
|
@@ -95,8 +98,8 @@ declare function column_getSortFn<TFeatures extends TableFeatures, TData extends
|
|
|
95
98
|
* Applies the next sorting state for this column.
|
|
96
99
|
*
|
|
97
100
|
* The toggle can add, replace, flip, or remove this column's sort entry. Multi
|
|
98
|
-
* sorting respects `enableMultiSort`, `
|
|
99
|
-
* argument.
|
|
101
|
+
* sorting respects `enableMultiSort`, `enableMultiRemove`,
|
|
102
|
+
* `maxMultiSortColCount`, and the `multi` argument.
|
|
100
103
|
*
|
|
101
104
|
* @example
|
|
102
105
|
* ```ts
|
|
@@ -103,10 +103,13 @@ function column_getAutoSortFn(column) {
|
|
|
103
103
|
return sortFn_basic;
|
|
104
104
|
}
|
|
105
105
|
/**
|
|
106
|
-
* Chooses the default first sort direction from
|
|
106
|
+
* Chooses the default first sort direction from sampled filtered row values.
|
|
107
107
|
*
|
|
108
|
-
*
|
|
109
|
-
* types
|
|
108
|
+
* The first non-nullish value among the sampled rows decides: string columns
|
|
109
|
+
* start ascending so alphabetical order is natural; other value types (or
|
|
110
|
+
* columns with no non-nullish sample) start descending. Sampling past leading
|
|
111
|
+
* nullish values keeps the toggle cycle stable when sorting or a data swap
|
|
112
|
+
* moves an empty value into the first row.
|
|
110
113
|
*
|
|
111
114
|
* @example
|
|
112
115
|
* ```ts
|
|
@@ -114,8 +117,12 @@ function column_getAutoSortFn(column) {
|
|
|
114
117
|
* ```
|
|
115
118
|
*/
|
|
116
119
|
function column_getAutoSortDir(column) {
|
|
117
|
-
const
|
|
118
|
-
|
|
120
|
+
const firstRows = column.table.getFilteredRowModel().flatRows.slice(0, 10);
|
|
121
|
+
for (let i = 0; i < firstRows.length; i++) {
|
|
122
|
+
const value = firstRows[i].getValue(column.id);
|
|
123
|
+
if (value == null) continue;
|
|
124
|
+
return typeof value === "string" ? "asc" : "desc";
|
|
125
|
+
}
|
|
119
126
|
return "desc";
|
|
120
127
|
}
|
|
121
128
|
/**
|
|
@@ -142,8 +149,8 @@ function column_getSortFn(column) {
|
|
|
142
149
|
* Applies the next sorting state for this column.
|
|
143
150
|
*
|
|
144
151
|
* The toggle can add, replace, flip, or remove this column's sort entry. Multi
|
|
145
|
-
* sorting respects `enableMultiSort`, `
|
|
146
|
-
* argument.
|
|
152
|
+
* sorting respects `enableMultiSort`, `enableMultiRemove`,
|
|
153
|
+
* `maxMultiSortColCount`, and the `multi` argument.
|
|
147
154
|
*
|
|
148
155
|
* @example
|
|
149
156
|
* ```ts
|
|
@@ -151,7 +158,7 @@ function column_getSortFn(column) {
|
|
|
151
158
|
* ```
|
|
152
159
|
*/
|
|
153
160
|
function column_toggleSorting(column, desc, multi) {
|
|
154
|
-
const nextSortingOrder = column_getNextSortingOrder(column);
|
|
161
|
+
const nextSortingOrder = column_getNextSortingOrder(column, multi && column_getCanMultiSort(column));
|
|
155
162
|
const hasManualValue = typeof desc !== "undefined";
|
|
156
163
|
table_setSorting(column.table, (old) => {
|
|
157
164
|
const existingIndex = old.findIndex((d) => d.id === column.id);
|
package/package.json
CHANGED
package/skills/core/SKILL.md
CHANGED
|
@@ -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.
|
|
8
|
+
library_version: '9.0.0-beta.78'
|
|
9
9
|
requires: ['core', 'table-features', 'typescript']
|
|
10
10
|
sources:
|
|
11
11
|
- 'TanStack/table:docs/framework/react/guide/custom-features.md'
|
package/skills/grouping/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ description: >
|
|
|
5
5
|
metadata:
|
|
6
6
|
type: lifecycle
|
|
7
7
|
library: '@tanstack/table-core'
|
|
8
|
-
library_version: '9.0.0-beta.
|
|
8
|
+
library_version: '9.0.0-beta.78'
|
|
9
9
|
requires: ['core', 'table-features', 'typescript']
|
|
10
10
|
sources:
|
|
11
11
|
- 'TanStack/table:docs/framework/react/guide/migrating.md'
|
package/skills/sorting/SKILL.md
CHANGED