@jielga/tmdatagrid 2.0.0-beta.2 → 2.0.0-beta.21
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 +5 -212
- package/dist/index.d.ts +1664 -632
- package/dist/index.js +5226 -3223
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/docs/anatomy.md +102 -0
- package/docs/cell-selection.md +154 -0
- package/docs/column-layout.md +204 -0
- package/docs/columns.md +262 -0
- package/docs/components.md +304 -0
- package/docs/editing.md +603 -0
- package/docs/editors.md +250 -0
- package/docs/export.md +326 -0
- package/docs/filtering.md +358 -0
- package/docs/getting-started.md +123 -0
- package/docs/grouping.md +165 -0
- package/docs/loading-and-empty.md +92 -0
- package/docs/localization.md +79 -0
- package/docs/menu.md +143 -0
- package/docs/pagination.md +144 -0
- package/docs/persistence.md +111 -0
- package/docs/portfolio-rebalancer.md +94 -0
- package/docs/query-builder.md +175 -0
- package/docs/quick-search.md +83 -0
- package/docs/row-details.md +113 -0
- package/docs/row-interaction.md +148 -0
- package/docs/row-pinning.md +132 -0
- package/docs/row-selection.md +134 -0
- package/docs/row-styling.md +133 -0
- package/docs/scrolling.md +111 -0
- package/docs/server-query.md +246 -0
- package/docs/server-side.md +206 -0
- package/docs/sorting.md +101 -0
- package/docs/styling.md +126 -0
- package/docs/summary-row.md +76 -0
- package/docs/testing.md +309 -0
- package/docs/toolbar.md +161 -0
- package/docs/use-tm-data-grid.md +361 -0
- package/package.json +21 -45
- package/skills/appearance/SKILL.md +70 -17
- package/skills/cell-selection/SKILL.md +70 -76
- package/skills/columns/SKILL.md +131 -32
- package/skills/data/SKILL.md +100 -23
- package/skills/editing/SKILL.md +217 -96
- package/skills/editing/references/common-mistakes.md +111 -24
- package/skills/editing/references/editing-api.md +63 -39
- package/skills/editing/references/editors-and-validation.md +77 -19
- package/skills/filtering/SKILL.md +148 -40
- package/skills/getting-started/SKILL.md +18 -16
- package/skills/grouping/SKILL.md +32 -15
- package/skills/options/SKILL.md +39 -9
- package/skills/rows/SKILL.md +22 -18
- package/skills/server-side/SKILL.md +170 -17
- package/skills/testing/SKILL.md +10 -7
- package/src/{tmdatagrid/TMDataGridContext.ts → TMDataGridContext.ts} +7 -19
- package/src/{tmdatagrid/components → components}/TMDataGrid.module.css +7 -1
- package/src/{tmdatagrid/components → components}/TMDataGrid.tsx +39 -23
- package/src/{tmdatagrid/components → components}/TMDataGridCellEditor.tsx +106 -38
- package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.module.css +5 -1
- package/src/{tmdatagrid/components → components}/TMDataGridColumnsPanel.tsx +47 -55
- package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.tsx +4 -4
- package/src/components/TMDataGridDraftActions.tsx +307 -0
- package/src/{tmdatagrid/components → components}/TMDataGridEditColumn.tsx +58 -50
- package/src/{tmdatagrid/components → components}/TMDataGridEntryRows.tsx +150 -115
- package/src/components/TMDataGridExportPicker.module.css +77 -0
- package/src/components/TMDataGridExportPicker.tsx +234 -0
- package/src/components/TMDataGridFilterPanel.module.css +54 -0
- package/src/components/TMDataGridFilterPanel.tsx +348 -0
- package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.tsx +7 -5
- package/src/components/TMDataGridFilterSurface.module.css +54 -0
- package/src/components/TMDataGridFilterSurface.tsx +167 -0
- package/src/{tmdatagrid/components → components}/TMDataGridFooter.tsx +53 -13
- package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.tsx +4 -3
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.module.css +10 -0
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.tsx +100 -28
- package/src/components/TMDataGridHeaderFilterRow.module.css +51 -0
- package/src/components/TMDataGridHeaderFilterRow.tsx +301 -0
- package/src/components/TMDataGridMenu.tsx +354 -0
- package/src/{tmdatagrid/components → components}/TMDataGridSelectColumn.tsx +12 -7
- package/src/{tmdatagrid/components → components}/TMDataGridTable.module.css +90 -67
- package/src/{tmdatagrid/components → components}/TMDataGridTable.tsx +678 -156
- package/src/components/TMDataGridToolbar.module.css +21 -0
- package/src/components/TMDataGridToolbar.tsx +181 -0
- package/src/{tmdatagrid/components → components}/editors/TMDataGridBooleanEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridDateEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridMultiSelectEditor.tsx +3 -3
- package/src/components/editors/TMDataGridNumberEditor.tsx +70 -0
- package/src/{tmdatagrid/components → components}/editors/TMDataGridSelectEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/TMDataGridStringEditor.tsx +3 -3
- package/src/{tmdatagrid/components → components}/editors/editorShared.ts +17 -31
- package/src/{tmdatagrid/components → components}/filters/DgAutocompleteFilter.tsx +5 -4
- package/src/{tmdatagrid/components → components}/filters/DgDateRangeFilter.tsx +22 -5
- package/src/{tmdatagrid/components → components}/filters/DgRangeSliderFilter.tsx +5 -1
- package/src/{tmdatagrid/components → components}/filters/DgTriStateFilter.tsx +5 -1
- package/src/{tmdatagrid/components → components}/filters/TMDataGridFilterValueInput.tsx +44 -28
- package/src/components/filters/controlLayout.ts +32 -0
- package/src/components/filters/filterControlFor.ts +65 -0
- package/src/{tmdatagrid/components → components}/icons.ts +1 -0
- package/src/{tmdatagrid/components → components}/sticky.module.css +44 -0
- package/src/components/useHideableColumns.ts +52 -0
- package/src/{tmdatagrid/core → core}/autosize.ts +5 -2
- package/src/{tmdatagrid/core → core}/capabilities.ts +14 -6
- package/src/{tmdatagrid/core → core}/columnOptions.ts +46 -0
- package/src/{tmdatagrid/core → core}/columnOrdering.ts +33 -14
- package/src/{tmdatagrid/core → core}/columnUtils.ts +44 -5
- package/src/core/controlledState.ts +179 -0
- package/src/core/controlledStateSync.ts +108 -0
- package/src/core/deletedRows.ts +34 -0
- package/src/core/dom.ts +74 -0
- package/src/core/editEngine.ts +2476 -0
- package/src/{tmdatagrid/core → core}/editorFocus.ts +8 -4
- package/src/core/export.ts +843 -0
- package/src/{tmdatagrid/core → core}/filterControls.ts +38 -1
- package/src/{tmdatagrid/core → core}/filterOperators.ts +64 -1
- package/src/core/filterSurface.ts +99 -0
- package/src/{tmdatagrid/core → core}/labels.ts +66 -8
- package/src/{tmdatagrid/core → core}/labelsSv.ts +26 -3
- package/src/core/pageReset.ts +120 -0
- package/src/{tmdatagrid/core → core}/persistence.ts +22 -5
- package/src/core/resizePreview.ts +141 -0
- package/src/core/summary.ts +59 -0
- package/src/core/useSettledTableState.ts +36 -0
- package/src/{tmdatagrid/index.ts → index.ts} +75 -12
- package/src/{tmdatagrid/useTMDataGrid.tsx → useTMDataGrid.tsx} +734 -135
- package/src/useTMDataGridExport.ts +78 -0
- package/src/tmdatagrid/components/TMDataGridEditActions.tsx +0 -162
- package/src/tmdatagrid/components/TMDataGridFilterPanel.module.css +0 -35
- package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +0 -354
- package/src/tmdatagrid/components/TMDataGridToolbar.module.css +0 -12
- package/src/tmdatagrid/components/TMDataGridToolbar.tsx +0 -162
- package/src/tmdatagrid/components/editors/TMDataGridNumberEditor.tsx +0 -40
- package/src/tmdatagrid/core/cellExport.ts +0 -320
- package/src/tmdatagrid/core/editEngine.ts +0 -1006
- package/src/tmdatagrid/core/summary.ts +0 -35
- /package/src/{tmdatagrid/components → components}/TMDataGridDetailsColumn.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridFilterPills.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridFooter.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.module.css +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridRowNumberColumn.tsx +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGridSearch.tsx +0 -0
- /package/src/{tmdatagrid/core → core}/cellNavigation.ts +0 -0
- /package/src/{tmdatagrid/core → core}/cellRange.ts +0 -0
- /package/src/{tmdatagrid/core → core}/draftCellContext.ts +0 -0
- /package/src/{tmdatagrid/core → core}/expanding.ts +0 -0
- /package/src/{tmdatagrid/core → core}/grouping.ts +0 -0
- /package/src/{tmdatagrid/core → core}/matchHighlight.ts +0 -0
- /package/src/{tmdatagrid/core → core}/quickSearch.ts +0 -0
- /package/src/{tmdatagrid/core → core}/rowPinning.ts +0 -0
- /package/src/{tmdatagrid/core → core}/rowSelection.ts +0 -0
- /package/src/{tmdatagrid/core → core}/sizes.ts +0 -0
|
@@ -34,8 +34,36 @@ export type TMDataGridFilterControlArgs = {
|
|
|
34
34
|
options: ReadonlyArray<TMDataGridOption>;
|
|
35
35
|
size: TMDataGridSize;
|
|
36
36
|
labels: TMDataGridLabels;
|
|
37
|
+
/**
|
|
38
|
+
* How much room the control has, and whether it names itself. The same
|
|
39
|
+
* vocabulary as `TMDataGrid.FilterPanel`'s own `layout` prop, plus the one
|
|
40
|
+
* value only a header cell can be in.
|
|
41
|
+
*
|
|
42
|
+
* | Layout | Where | Field |
|
|
43
|
+
* | --- | --- | --- |
|
|
44
|
+
* | `"row"` | A filter row laid out side by side | Labelled, fixed width |
|
|
45
|
+
* | `"stacked"` | A filter row in a narrow host - the sidebar | Labelled, full width |
|
|
46
|
+
* | `"header"` | One header cell, under `filters.inHeader` | `aria-label`, full width |
|
|
47
|
+
*
|
|
48
|
+
* Every built-in control honours it. A custom control that ignores it still
|
|
49
|
+
* works - it will simply look the same everywhere.
|
|
50
|
+
*/
|
|
51
|
+
layout: TMDataGridFilterControlLayout;
|
|
37
52
|
};
|
|
38
53
|
|
|
54
|
+
/** How much room a filter control has. See `layout`. */
|
|
55
|
+
export type TMDataGridFilterControlLayout = "row" | "stacked" | "header";
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The two a filter *panel* can be in - {@link TMDataGridFilterControlLayout}
|
|
59
|
+
* without the header cell, which is not a panel. `TMDataGrid.FilterPanel`'s
|
|
60
|
+
* `layout` prop.
|
|
61
|
+
*/
|
|
62
|
+
export type TMDataGridFilterPanelLayout = Exclude<
|
|
63
|
+
TMDataGridFilterControlLayout,
|
|
64
|
+
"header"
|
|
65
|
+
>;
|
|
66
|
+
|
|
39
67
|
/**
|
|
40
68
|
* `meta.filter.control` - replaces the built-in value control for this column.
|
|
41
69
|
* Rendered as JSX, never invoked as a bare function, so hooks are legal
|
|
@@ -58,10 +86,19 @@ export type TMDataGridFilterControlComponent =
|
|
|
58
86
|
* declaration of each feeds the filter panel and the cell editor alike.
|
|
59
87
|
*/
|
|
60
88
|
export type TMDataGridColumnFilterOptions = {
|
|
89
|
+
/**
|
|
90
|
+
* The operators this column offers, a subset of the type's own. For a
|
|
91
|
+
* column backed by an endpoint that answers only some of them - `contains`
|
|
92
|
+
* and `equals`, say - so the panel and the header funnel never offer an
|
|
93
|
+
* operator the query cannot express. Kept in the type's order; one the type
|
|
94
|
+
* does not offer is ignored, and a list that leaves nothing falls back to
|
|
95
|
+
* the type's full set.
|
|
96
|
+
*/
|
|
97
|
+
operators?: readonly TMDataGridFilterOperator[];
|
|
61
98
|
/**
|
|
62
99
|
* The operator a fresh filter on this column starts with, instead of the
|
|
63
100
|
* type's default - a salary column can open on `"between"`. Must be one of
|
|
64
|
-
* the
|
|
101
|
+
* the operators the column offers.
|
|
65
102
|
*/
|
|
66
103
|
defaultOperator?: TMDataGridFilterOperator;
|
|
67
104
|
/**
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ColumnFiltersState,
|
|
3
|
+
Row,
|
|
4
|
+
RowData,
|
|
5
|
+
TableFeatures,
|
|
6
|
+
} from "@tanstack/react-table";
|
|
7
|
+
import type { TMDataGridTable } from "../useTMDataGrid";
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
10
|
* The value shape stored in `columnFilters` for every TMDataGrid column.
|
|
@@ -210,6 +216,25 @@ export function isTMDataGridFilterValue(
|
|
|
210
216
|
);
|
|
211
217
|
}
|
|
212
218
|
|
|
219
|
+
/**
|
|
220
|
+
* The three value shapes an operator can take. A set is not a range, even
|
|
221
|
+
* though both are arrays.
|
|
222
|
+
*/
|
|
223
|
+
export type TMDataGridFilterValueShape = "scalar" | "set" | "range";
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Which shape an operator's value takes. A typed value survives an operator or
|
|
227
|
+
* column change only within its shape, which is the rule both the panel and
|
|
228
|
+
* the header controls use when the operator changes.
|
|
229
|
+
*/
|
|
230
|
+
export function filterValueShape(
|
|
231
|
+
operator: TMDataGridFilterOperator,
|
|
232
|
+
): TMDataGridFilterValueShape {
|
|
233
|
+
if (operatorTakesArrayValue(operator)) return "set";
|
|
234
|
+
if (operatorTakesRangeValue(operator)) return "range";
|
|
235
|
+
return "scalar";
|
|
236
|
+
}
|
|
237
|
+
|
|
213
238
|
/**
|
|
214
239
|
* A filter only narrows the row set once it has something to compare against.
|
|
215
240
|
* Half-typed filters stay in state (so the panel keeps rendering their row) but
|
|
@@ -225,6 +250,44 @@ export function isFilterActive(value: unknown): boolean {
|
|
|
225
250
|
: typeof value.value === "string" && value.value.trim() !== "";
|
|
226
251
|
}
|
|
227
252
|
|
|
253
|
+
/** One column's filter, typed - what `columnFilters` holds per entry. */
|
|
254
|
+
export type TMDataGridColumnFilter = {
|
|
255
|
+
id: string;
|
|
256
|
+
value: TMDataGridFilterValue;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* The column filters that are actually narrowing the grid, typed.
|
|
261
|
+
*
|
|
262
|
+
* `ColumnFiltersState` types `value` as `unknown`, so the first line of a
|
|
263
|
+
* server-side mapping layer is otherwise a cast back to the shape the grid
|
|
264
|
+
* itself wrote, wrapped in the same "drop the half-typed ones" filter every
|
|
265
|
+
* consumer writes:
|
|
266
|
+
*
|
|
267
|
+
* ```ts
|
|
268
|
+
* const predicates = activeColumnFilters(table).map((filter) =>
|
|
269
|
+
* toPredicate(filter.id, filter.value),
|
|
270
|
+
* );
|
|
271
|
+
* ```
|
|
272
|
+
*
|
|
273
|
+
* Takes the table, or a `columnFilters` array where the consumer owns the
|
|
274
|
+
* slice. Reading it from the table reads the current value and does not
|
|
275
|
+
* subscribe; inside a component, subscribe to `columnFilters` the way the
|
|
276
|
+
* grid's own chrome does.
|
|
277
|
+
*/
|
|
278
|
+
export function activeColumnFilters<TData extends RowData>(
|
|
279
|
+
source: TMDataGridTable<TData> | ColumnFiltersState,
|
|
280
|
+
): Array<TMDataGridColumnFilter> {
|
|
281
|
+
const columnFilters = Array.isArray(source)
|
|
282
|
+
? source
|
|
283
|
+
: source.store.state.columnFilters;
|
|
284
|
+
return columnFilters.flatMap((entry) =>
|
|
285
|
+
isTMDataGridFilterValue(entry.value) && isFilterActive(entry.value)
|
|
286
|
+
? [{ id: entry.id, value: entry.value }]
|
|
287
|
+
: [],
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
228
291
|
/**
|
|
229
292
|
* One-line description of a single filter, as shown on a filter pill.
|
|
230
293
|
*
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the grid puts its filter controls.
|
|
3
|
+
*
|
|
4
|
+
* | Surface | Where it renders |
|
|
5
|
+
* | --- | --- |
|
|
6
|
+
* | `"popup"` | Floating over the first body rows, under the header |
|
|
7
|
+
* | `"sidebar"` | Beside the table, inside the grid frame |
|
|
8
|
+
* | `"none"` | Nowhere - the grid renders no panel of its own |
|
|
9
|
+
*
|
|
10
|
+
* Header filters are not one of these: they are a second row of controls in
|
|
11
|
+
* the header, always visible, and they coexist with any of the three. See
|
|
12
|
+
* {@link TMDataGridFiltersOptions.inHeader}.
|
|
13
|
+
*/
|
|
14
|
+
export type TMDataGridFilterSurface = "popup" | "sidebar" | "none";
|
|
15
|
+
|
|
16
|
+
/** Which side of the table the sidebar surface renders on. */
|
|
17
|
+
export type TMDataGridFilterSidebarSide = "left" | "right";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* `filters` on `useTMDataGrid` - everything about where the filter controls
|
|
21
|
+
* are, as opposed to what they do.
|
|
22
|
+
*
|
|
23
|
+
* Named for the option key, the way `editing` has `TMDataGridEditingOptions`.
|
|
24
|
+
* Not to be confused with `TMDataGridColumnFilterOptions`, which is one
|
|
25
|
+
* column's `meta.filter`.
|
|
26
|
+
*
|
|
27
|
+
* ```tsx
|
|
28
|
+
* useTMDataGrid({ data, columns, filters: { surface: "sidebar" } });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export type TMDataGridFiltersOptions = {
|
|
32
|
+
/**
|
|
33
|
+
* Which surface `TMDataGrid.Table` renders and `TMDataGrid.FilterButton`
|
|
34
|
+
* toggles. Defaults to `"popup"`.
|
|
35
|
+
*
|
|
36
|
+
* Under `"none"` the table renders no panel and the filter button renders
|
|
37
|
+
* nothing. That is what a grid running header filters alone wants, and it is
|
|
38
|
+
* also what frees a hand-placed `<TMDataGrid.FilterPanel />` to be the only
|
|
39
|
+
* panel on the page - mounted, it is always visible, so drive it off
|
|
40
|
+
* `ui.state.filterPanelOpen` if it belongs behind a control of your own.
|
|
41
|
+
*/
|
|
42
|
+
surface?: TMDataGridFilterSurface;
|
|
43
|
+
/** Which side the `"sidebar"` surface sits on. Defaults to `"right"`. */
|
|
44
|
+
sidebarSide?: TMDataGridFilterSidebarSide;
|
|
45
|
+
/** Width of the `"sidebar"` surface, any CSS length. Defaults to `"280px"`. */
|
|
46
|
+
sidebarWidth?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Whether the popup or the sidebar starts open. Read once, at mount, like
|
|
49
|
+
* `initialState`.
|
|
50
|
+
*
|
|
51
|
+
* Defaults to `true` under `"sidebar"` and `false` everywhere else: a
|
|
52
|
+
* sidebar is a layout choice, so asking for one and getting an empty strip
|
|
53
|
+
* until the funnel is clicked is not what it reads like, while a popup that
|
|
54
|
+
* greets you open is in the way.
|
|
55
|
+
*
|
|
56
|
+
* Under `"none"` it is simply the starting value of
|
|
57
|
+
* `ui.state.filterPanelOpen`, which a control of your own can read.
|
|
58
|
+
*/
|
|
59
|
+
defaultOpen?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* A second header row holding one value control per filterable column,
|
|
62
|
+
* always visible. Off by default.
|
|
63
|
+
*
|
|
64
|
+
* Independent of `surface` - a grid may have header filters and a popup at
|
|
65
|
+
* once. What it does change is the column chrome: the header's funnel
|
|
66
|
+
* indicator and the column menu's "Filter" item both come off, because
|
|
67
|
+
* their only job was to reveal a control that is now already on screen.
|
|
68
|
+
*
|
|
69
|
+
* A header cell has room for a value and an operator button, not for the
|
|
70
|
+
* panel's column / operator / value triple. Everything else about a filter
|
|
71
|
+
* is unchanged - the same operators, the same `meta.filter.control`, the
|
|
72
|
+
* same `columnFilters` state.
|
|
73
|
+
*/
|
|
74
|
+
inHeader?: boolean;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** {@link TMDataGridFiltersOptions} with every default filled in. */
|
|
78
|
+
export type TMDataGridFiltersSettings = Required<TMDataGridFiltersOptions>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Fills the defaults in. Field by field rather than by spreading, so an
|
|
82
|
+
* explicit `undefined` - which is what destructuring an absent option group
|
|
83
|
+
* hands over - reads as "not set" rather than overwriting the default with it.
|
|
84
|
+
*
|
|
85
|
+
* `defaultOpen` is the one default that is not a constant: it follows the
|
|
86
|
+
* surface, so there is no flat table of defaults to export.
|
|
87
|
+
*/
|
|
88
|
+
export function resolveFilterOptions(
|
|
89
|
+
options: TMDataGridFiltersOptions = {},
|
|
90
|
+
): TMDataGridFiltersSettings {
|
|
91
|
+
const surface = options.surface ?? "popup";
|
|
92
|
+
return {
|
|
93
|
+
surface,
|
|
94
|
+
sidebarSide: options.sidebarSide ?? "right",
|
|
95
|
+
sidebarWidth: options.sidebarWidth ?? "280px",
|
|
96
|
+
defaultOpen: options.defaultOpen ?? surface === "sidebar",
|
|
97
|
+
inHeader: options.inHeader ?? false,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -17,14 +17,18 @@ export type TMDataGridLabels = {
|
|
|
17
17
|
operators: Record<TMDataGridFilterOperator, string>;
|
|
18
18
|
|
|
19
19
|
// Toolbar
|
|
20
|
-
/** "Manage columns" - the burger button and the header menu item. */
|
|
21
|
-
manageColumns: string;
|
|
22
20
|
/** "Filters" - the funnel button and the filter panel's title. */
|
|
23
21
|
filters: string;
|
|
24
22
|
searchPlaceholder: string;
|
|
25
23
|
searchLabel: string;
|
|
26
24
|
clearSearch: string;
|
|
27
25
|
|
|
26
|
+
// Grid menu
|
|
27
|
+
/** Tooltip and `aria-label` of `TMDataGrid.Menu`'s burger trigger. */
|
|
28
|
+
menuButton: string;
|
|
29
|
+
/** "Manage columns" - the header menu's submenu. */
|
|
30
|
+
manageColumns: string;
|
|
31
|
+
|
|
28
32
|
// Columns panel
|
|
29
33
|
columnsSearchPlaceholder: string;
|
|
30
34
|
columnsNoMatch: (search: string) => string;
|
|
@@ -58,6 +62,8 @@ export type TMDataGridLabels = {
|
|
|
58
62
|
clearAllFilters: string;
|
|
59
63
|
closeFilters: string;
|
|
60
64
|
removeFilter: string;
|
|
65
|
+
/** Names the operator button in a column's header filter control. */
|
|
66
|
+
filterOperatorFor: (column: string) => string;
|
|
61
67
|
|
|
62
68
|
// Filter pills
|
|
63
69
|
activeFilters: string;
|
|
@@ -85,6 +91,8 @@ export type TMDataGridLabels = {
|
|
|
85
91
|
// Footer / pager
|
|
86
92
|
rowsPerPage: string;
|
|
87
93
|
pageRange: (args: { from: number; to: number; total: number }) => string;
|
|
94
|
+
/** `pageCount` is `-1` when a manual grid declares an unknown total. */
|
|
95
|
+
pageNumber: (args: { page: number; pageCount: number }) => string;
|
|
88
96
|
groupedAllRows: (total: number) => string;
|
|
89
97
|
pagingSuspendedHint: string;
|
|
90
98
|
previousPage: string;
|
|
@@ -111,10 +119,15 @@ export type TMDataGridLabels = {
|
|
|
111
119
|
cancelRowEdit: string;
|
|
112
120
|
/** Save tooltip when field errors block the row, with no row message. */
|
|
113
121
|
editRowErrors: string;
|
|
114
|
-
/** `
|
|
122
|
+
/** `DraftActions`' Save, with the count of rows in the draft store. */
|
|
115
123
|
saveAllEdits: (rows: number) => string;
|
|
116
|
-
/** `
|
|
124
|
+
/** `DraftActions`' Discard. */
|
|
117
125
|
discardAllEdits: string;
|
|
126
|
+
/**
|
|
127
|
+
* `DraftActions`' note about rows still open - edited but not committed, so
|
|
128
|
+
* not part of the save. Shown only while there are any.
|
|
129
|
+
*/
|
|
130
|
+
editRowsStillOpen: (rows: number) => string;
|
|
118
131
|
/** The entry row's ✓ - commit the add. */
|
|
119
132
|
confirmNewRow: string;
|
|
120
133
|
/** The entry row's ✕ - drop the entry. */
|
|
@@ -135,9 +148,27 @@ export type TMDataGridLabels = {
|
|
|
135
148
|
// Cell selection menu
|
|
136
149
|
cellCount: (count: number) => string;
|
|
137
150
|
copy: string;
|
|
138
|
-
|
|
151
|
+
exportCells: string;
|
|
152
|
+
/** @deprecated Use `exportCells`. Read as its fallback for one beta. */
|
|
153
|
+
exportCsv?: string;
|
|
139
154
|
includeHeaders: string;
|
|
140
155
|
|
|
156
|
+
// Export
|
|
157
|
+
exportAll: string;
|
|
158
|
+
exportSelected: (count: number) => string;
|
|
159
|
+
/** Title of the column picker `columns="custom"` opens; `format` is the file extension in upper case, "CSV". */
|
|
160
|
+
exportPickerTitle: (format: string) => string;
|
|
161
|
+
/** The line under the title; `selected` is the selected row count, or `null` for an export of all rows. */
|
|
162
|
+
exportPickerHint: (selected: number | null) => string;
|
|
163
|
+
exportPickerConfirm: string;
|
|
164
|
+
exportPickerCancel: string;
|
|
165
|
+
/** The select-all row over the picker's list. */
|
|
166
|
+
exportPickerSelectAll: string;
|
|
167
|
+
/** The "3 of 12" count beside it. */
|
|
168
|
+
exportPickerCount: (checked: number, total: number) => string;
|
|
169
|
+
/** Marks a column the grid hides at the moment. */
|
|
170
|
+
exportPickerHidden: string;
|
|
171
|
+
|
|
141
172
|
// Generated checkbox lane
|
|
142
173
|
selectColumnLabel: string;
|
|
143
174
|
selectAllRows: string;
|
|
@@ -172,16 +203,18 @@ export type TMDataGridLabelsOverride = Partial<
|
|
|
172
203
|
export const TMDATAGRID_LABELS_EN: TMDataGridLabels = {
|
|
173
204
|
operators: FILTER_OPERATOR_LABELS,
|
|
174
205
|
|
|
175
|
-
manageColumns: "Manage columns",
|
|
176
206
|
filters: "Filters",
|
|
177
207
|
searchPlaceholder: "Search",
|
|
178
208
|
searchLabel: "Search rows",
|
|
179
209
|
clearSearch: "Clear search",
|
|
180
210
|
|
|
211
|
+
menuButton: "Menu",
|
|
212
|
+
manageColumns: "Manage columns",
|
|
213
|
+
|
|
181
214
|
columnsSearchPlaceholder: "Search",
|
|
182
215
|
columnsNoMatch: (search) => `No columns match “${search}”`,
|
|
183
216
|
columnsShowHideAll: "Show/Hide All",
|
|
184
|
-
columnsReset: "
|
|
217
|
+
columnsReset: "Reset layout",
|
|
185
218
|
columnsResetHint:
|
|
186
219
|
"Resets column visibility, order, widths, pinning and grouping",
|
|
187
220
|
rowNumberColumnLabel: "Row number",
|
|
@@ -200,6 +233,7 @@ export const TMDATAGRID_LABELS_EN: TMDataGridLabels = {
|
|
|
200
233
|
clearAllFilters: "Clear all",
|
|
201
234
|
closeFilters: "Close filters",
|
|
202
235
|
removeFilter: "Remove filter",
|
|
236
|
+
filterOperatorFor: (column) => `${column} filter operator`,
|
|
203
237
|
|
|
204
238
|
activeFilters: "Active filters",
|
|
205
239
|
clearFilter: (column) => `Clear ${column} filter`,
|
|
@@ -224,6 +258,8 @@ export const TMDATAGRID_LABELS_EN: TMDataGridLabels = {
|
|
|
224
258
|
|
|
225
259
|
rowsPerPage: "Rows per page:",
|
|
226
260
|
pageRange: ({ from, to, total }) => `${from}–${to} of ${total}`,
|
|
261
|
+
pageNumber: ({ page, pageCount }) =>
|
|
262
|
+
pageCount < 0 ? `Page ${page}` : `Page ${page} of ${pageCount}`,
|
|
227
263
|
groupedAllRows: (total) => `Grouped · all ${total} rows`,
|
|
228
264
|
pagingSuspendedHint:
|
|
229
265
|
"Paging is off while the rows are grouped: the whole tree is rendered and virtualized. Ungroup to page again.",
|
|
@@ -243,6 +279,8 @@ export const TMDATAGRID_LABELS_EN: TMDataGridLabels = {
|
|
|
243
279
|
editRowErrors: "Fix the marked cells",
|
|
244
280
|
saveAllEdits: (rows) => (rows === 1 ? "Save 1 row" : `Save ${rows} rows`),
|
|
245
281
|
discardAllEdits: "Discard",
|
|
282
|
+
editRowsStillOpen: (rows) =>
|
|
283
|
+
rows === 1 ? "1 row still being edited" : `${rows} rows still being edited`,
|
|
246
284
|
confirmNewRow: "Add row",
|
|
247
285
|
discardNewRow: "Discard new row",
|
|
248
286
|
deleteRow: "Delete row",
|
|
@@ -254,9 +292,25 @@ export const TMDATAGRID_LABELS_EN: TMDataGridLabels = {
|
|
|
254
292
|
|
|
255
293
|
cellCount: (count) => (count === 1 ? "1 cell" : `${count} cells`),
|
|
256
294
|
copy: "Copy",
|
|
257
|
-
|
|
295
|
+
exportCells: "Export cells",
|
|
258
296
|
includeHeaders: "Include headers",
|
|
259
297
|
|
|
298
|
+
exportAll: "Export all rows",
|
|
299
|
+
exportSelected: (count) =>
|
|
300
|
+
count === 1 ? "Export 1 selected row" : `Export ${count} selected rows`,
|
|
301
|
+
exportPickerTitle: (format) => `Export as ${format}`,
|
|
302
|
+
exportPickerHint: (selected) =>
|
|
303
|
+
selected === null
|
|
304
|
+
? "Select the columns to export"
|
|
305
|
+
: selected === 1
|
|
306
|
+
? "Select the columns to export for the selected row"
|
|
307
|
+
: `Select the columns to export for the ${selected} selected rows`,
|
|
308
|
+
exportPickerConfirm: "Export",
|
|
309
|
+
exportPickerCancel: "Cancel",
|
|
310
|
+
exportPickerSelectAll: "Select all",
|
|
311
|
+
exportPickerCount: (checked, total) => `${checked} of ${total}`,
|
|
312
|
+
exportPickerHidden: "Hidden",
|
|
313
|
+
|
|
260
314
|
selectColumnLabel: "Checkbox selection",
|
|
261
315
|
selectAllRows: "Select all rows",
|
|
262
316
|
selectRow: "Select row",
|
|
@@ -282,6 +336,10 @@ export function mergeLabels(
|
|
|
282
336
|
return {
|
|
283
337
|
...TMDATAGRID_LABELS_EN,
|
|
284
338
|
...override,
|
|
339
|
+
exportCells:
|
|
340
|
+
override.exportCells ??
|
|
341
|
+
override.exportCsv ??
|
|
342
|
+
TMDATAGRID_LABELS_EN.exportCells,
|
|
285
343
|
operators: {
|
|
286
344
|
...TMDATAGRID_LABELS_EN.operators,
|
|
287
345
|
...override.operators,
|
|
@@ -32,16 +32,18 @@ export const TMDATAGRID_LABELS_SV: TMDataGridLabels = {
|
|
|
32
32
|
isNotEmpty: "är inte tom",
|
|
33
33
|
},
|
|
34
34
|
|
|
35
|
-
manageColumns: "Hantera kolumner",
|
|
36
35
|
filters: "Filter",
|
|
37
36
|
searchPlaceholder: "Sök",
|
|
38
37
|
searchLabel: "Sök rader",
|
|
39
38
|
clearSearch: "Rensa sökningen",
|
|
40
39
|
|
|
40
|
+
menuButton: "Meny",
|
|
41
|
+
manageColumns: "Hantera kolumner",
|
|
42
|
+
|
|
41
43
|
columnsSearchPlaceholder: "Sök",
|
|
42
44
|
columnsNoMatch: (search) => `Inga kolumner matchar ”${search}”`,
|
|
43
45
|
columnsShowHideAll: "Visa/dölj alla",
|
|
44
|
-
columnsReset: "
|
|
46
|
+
columnsReset: "Återställ layout",
|
|
45
47
|
columnsResetHint:
|
|
46
48
|
"Återställer kolumnernas synlighet, ordning, bredder, fästning och gruppering",
|
|
47
49
|
rowNumberColumnLabel: "Radnummer",
|
|
@@ -60,6 +62,7 @@ export const TMDATAGRID_LABELS_SV: TMDataGridLabels = {
|
|
|
60
62
|
clearAllFilters: "Rensa alla",
|
|
61
63
|
closeFilters: "Stäng filter",
|
|
62
64
|
removeFilter: "Ta bort filter",
|
|
65
|
+
filterOperatorFor: (column) => `Filteroperator för ${column}`,
|
|
63
66
|
|
|
64
67
|
activeFilters: "Aktiva filter",
|
|
65
68
|
clearFilter: (column) => `Rensa filtret för ${column}`,
|
|
@@ -84,6 +87,8 @@ export const TMDATAGRID_LABELS_SV: TMDataGridLabels = {
|
|
|
84
87
|
|
|
85
88
|
rowsPerPage: "Rader per sida:",
|
|
86
89
|
pageRange: ({ from, to, total }) => `${from}–${to} av ${total}`,
|
|
90
|
+
pageNumber: ({ page, pageCount }) =>
|
|
91
|
+
pageCount < 0 ? `Sida ${page}` : `Sida ${page} av ${pageCount}`,
|
|
87
92
|
groupedAllRows: (total) => `Grupperad · alla ${total} rader`,
|
|
88
93
|
pagingSuspendedHint:
|
|
89
94
|
"Sidindelningen är avstängd medan raderna är grupperade: hela trädet renderas och virtualiseras. Avgruppera för att bläddra igen.",
|
|
@@ -103,6 +108,8 @@ export const TMDATAGRID_LABELS_SV: TMDataGridLabels = {
|
|
|
103
108
|
editRowErrors: "Åtgärda de markerade cellerna",
|
|
104
109
|
saveAllEdits: (rows) => (rows === 1 ? "Spara 1 rad" : `Spara ${rows} rader`),
|
|
105
110
|
discardAllEdits: "Förkasta",
|
|
111
|
+
editRowsStillOpen: (rows) =>
|
|
112
|
+
rows === 1 ? "1 rad redigeras fortfarande" : `${rows} rader redigeras fortfarande`,
|
|
106
113
|
confirmNewRow: "Lägg till rad",
|
|
107
114
|
discardNewRow: "Släng ny rad",
|
|
108
115
|
deleteRow: "Ta bort rad",
|
|
@@ -114,9 +121,25 @@ export const TMDATAGRID_LABELS_SV: TMDataGridLabels = {
|
|
|
114
121
|
|
|
115
122
|
cellCount: (count) => (count === 1 ? "1 cell" : `${count} celler`),
|
|
116
123
|
copy: "Kopiera",
|
|
117
|
-
|
|
124
|
+
exportCells: "Exportera celler",
|
|
118
125
|
includeHeaders: "Inkludera rubriker",
|
|
119
126
|
|
|
127
|
+
exportAll: "Exportera alla rader",
|
|
128
|
+
exportSelected: (count) =>
|
|
129
|
+
count === 1 ? "Exportera 1 vald rad" : `Exportera ${count} valda rader`,
|
|
130
|
+
exportPickerTitle: (format) => `Exportera som ${format}`,
|
|
131
|
+
exportPickerHint: (selected) =>
|
|
132
|
+
selected === null
|
|
133
|
+
? "Välj kolumner att exportera"
|
|
134
|
+
: selected === 1
|
|
135
|
+
? "Välj kolumner att exportera för den valda raden"
|
|
136
|
+
: `Välj kolumner att exportera för de ${selected} valda raderna`,
|
|
137
|
+
exportPickerConfirm: "Exportera",
|
|
138
|
+
exportPickerCancel: "Avbryt",
|
|
139
|
+
exportPickerSelectAll: "Markera alla",
|
|
140
|
+
exportPickerCount: (checked, total) => `${checked} av ${total}`,
|
|
141
|
+
exportPickerHidden: "Dold",
|
|
142
|
+
|
|
120
143
|
selectColumnLabel: "Kryssrutemarkering",
|
|
121
144
|
selectAllRows: "Markera alla rader",
|
|
122
145
|
selectRow: "Markera rad",
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import {
|
|
2
|
+
functionalUpdate,
|
|
3
|
+
makeStateUpdater,
|
|
4
|
+
type TableState,
|
|
5
|
+
type Updater,
|
|
6
|
+
} from "@tanstack/react-table";
|
|
7
|
+
import type { TMDataGridFeatures } from "../useTMDataGrid";
|
|
8
|
+
import { sameStateValue } from "./controlledState";
|
|
9
|
+
import { isFilterActive, isTMDataGridFilterValue } from "./filterOperators";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Sends a server-paged grid back to the first page when the query changes.
|
|
13
|
+
*
|
|
14
|
+
* Under `manualPagination` the grid does not own the result set, so a column
|
|
15
|
+
* filter, the quick search or a sort leaves `pageIndex` pointing into a page
|
|
16
|
+
* range that no longer exists: the next request asks the server for page 8 of
|
|
17
|
+
* a result set that now has three, and the consumer gets an empty grid with
|
|
18
|
+
* no error.
|
|
19
|
+
*
|
|
20
|
+
* TanStack's `autoResetPageIndex` does not cover it. It defaults to
|
|
21
|
+
* `!manualPagination`, so it is off exactly when this is needed, and it fires
|
|
22
|
+
* on a data change - which server-side is the response landing, one request
|
|
23
|
+
* too late.
|
|
24
|
+
*
|
|
25
|
+
* The reset rides on the slice's own change callback rather than on an effect
|
|
26
|
+
* over table state, so both writes land in the same render and the query goes
|
|
27
|
+
* out once. From an effect the request for the stale page would leave first.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The state a query is built from, plus grouping: paging is suspended while
|
|
32
|
+
* a column is grouped, and the page it hands back on the way out was
|
|
33
|
+
* measured over the ungrouped rows before the group was made.
|
|
34
|
+
*/
|
|
35
|
+
export type TMDataGridQuerySlice =
|
|
36
|
+
| "columnFilters"
|
|
37
|
+
| "globalFilter"
|
|
38
|
+
| "sorting"
|
|
39
|
+
| "grouping";
|
|
40
|
+
|
|
41
|
+
/** What the reset reads and writes - the table, narrowed to that. */
|
|
42
|
+
export type TMDataGridQueryTable = {
|
|
43
|
+
store: { state: TableState<TMDataGridFeatures> };
|
|
44
|
+
setPageIndex: (index: number) => void;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type QueryHandler = (updater: never) => void;
|
|
48
|
+
|
|
49
|
+
type ColumnFilters = TableState<TMDataGridFeatures>["columnFilters"];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The entries the server could answer differently. Only the grid's own
|
|
53
|
+
* `{ operator, value }` shape can be recognised as "still empty" - anything
|
|
54
|
+
* else, a custom filter control's raw value or state restored from a URL,
|
|
55
|
+
* narrows the grid as far as this module can know, so it always counts.
|
|
56
|
+
*/
|
|
57
|
+
function querySignificantFilters(filters: ColumnFilters): ColumnFilters {
|
|
58
|
+
return filters.filter(
|
|
59
|
+
(entry) =>
|
|
60
|
+
!isTMDataGridFilterValue(entry.value) || isFilterActive(entry.value),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether the server would answer the next state differently.
|
|
66
|
+
*
|
|
67
|
+
* A column filter with an empty value matches every row, so it is not part of
|
|
68
|
+
* the query: opening the filter panel seeds a row on the first filterable
|
|
69
|
+
* column, and the user then types into it. Neither moves the result set, and
|
|
70
|
+
* neither may throw away the page the user is on.
|
|
71
|
+
*/
|
|
72
|
+
function changesTheQuery(
|
|
73
|
+
slice: TMDataGridQuerySlice,
|
|
74
|
+
previous: unknown,
|
|
75
|
+
next: unknown,
|
|
76
|
+
): boolean {
|
|
77
|
+
if (slice === "columnFilters") {
|
|
78
|
+
return !sameStateValue(
|
|
79
|
+
querySignificantFilters(previous as ColumnFilters),
|
|
80
|
+
querySignificantFilters(next as ColumnFilters),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return !sameStateValue(previous, next);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Wraps one slice's change callback. `handler` is the consumer's, where they
|
|
88
|
+
* control the slice; without one the write is the same `makeStateUpdater`
|
|
89
|
+
* TanStack would have defaulted to, which this option replaces.
|
|
90
|
+
*/
|
|
91
|
+
export function withPageReset(
|
|
92
|
+
slice: TMDataGridQuerySlice,
|
|
93
|
+
handler: QueryHandler | undefined,
|
|
94
|
+
getTable: () => TMDataGridQueryTable,
|
|
95
|
+
): QueryHandler {
|
|
96
|
+
return (updater) => {
|
|
97
|
+
const table = getTable();
|
|
98
|
+
const previous = table.store.state[slice];
|
|
99
|
+
// Resolved here rather than read back off the table afterwards: where the
|
|
100
|
+
// consumer owns the slice the write is their `setState`, so the table
|
|
101
|
+
// still holds the old value when this returns. The updater therefore runs
|
|
102
|
+
// twice per change - once here, once in the write path - which is safe
|
|
103
|
+
// for every updater the grid and table-core produce, and a requirement on
|
|
104
|
+
// the consumer's: an updater has to be a pure derivation of the previous
|
|
105
|
+
// value. The decision is also best-effort under batching: two writes to
|
|
106
|
+
// one slice in the same tick both read the same `previous`, so the second
|
|
107
|
+
// decision is computed from a stale base. A wrong reset decision at
|
|
108
|
+
// worst, never a wrong write.
|
|
109
|
+
const next = functionalUpdate(updater as Updater<unknown>, previous);
|
|
110
|
+
|
|
111
|
+
(handler ?? (makeStateUpdater(slice, table as never) as QueryHandler))(
|
|
112
|
+
updater,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (!changesTheQuery(slice, previous, next)) return;
|
|
116
|
+
// Already on the first page: writing anyway would publish the store and
|
|
117
|
+
// re-render the grid for a value that did not move.
|
|
118
|
+
if (table.store.state.pagination.pageIndex !== 0) table.setPageIndex(0);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -188,12 +188,26 @@ const SLICE_GUARDS: Record<
|
|
|
188
188
|
isRecordOf(value, (entry) => typeof entry === "boolean"),
|
|
189
189
|
columnSizing: (value) => isRecordOf(value, isFiniteNumber),
|
|
190
190
|
columnOrder: (value) => isArrayOf(value, isString),
|
|
191
|
+
// Both the current logical shape and the physical one 1.x wrote.
|
|
191
192
|
columnPinning: (value) =>
|
|
192
|
-
isArrayOf(prop(value, "
|
|
193
|
-
|
|
193
|
+
(isArrayOf(prop(value, "start"), isString) &&
|
|
194
|
+
isArrayOf(prop(value, "end"), isString)) ||
|
|
195
|
+
(isArrayOf(prop(value, "left"), isString) &&
|
|
196
|
+
isArrayOf(prop(value, "right"), isString)),
|
|
194
197
|
grouping: (value) => isArrayOf(value, isString),
|
|
195
198
|
};
|
|
196
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Settings saved by 1.x used TanStack's old physical `columnPinning` keys.
|
|
202
|
+
* Rewrite them once on read, so nothing downstream sees `left` / `right`.
|
|
203
|
+
*/
|
|
204
|
+
function normalizeColumnPinning(value: unknown): unknown {
|
|
205
|
+
const left = prop(value, "left");
|
|
206
|
+
const right = prop(value, "right");
|
|
207
|
+
if (left === undefined || right === undefined) return value;
|
|
208
|
+
return { start: left, end: right };
|
|
209
|
+
}
|
|
210
|
+
|
|
197
211
|
function isValidSlice(slice: keyof GridState, value: unknown): boolean {
|
|
198
212
|
const guard = SLICE_GUARDS[slice as keyof typeof SLICE_GUARDS];
|
|
199
213
|
return guard === undefined || guard(value);
|
|
@@ -219,7 +233,10 @@ function readSlices(
|
|
|
219
233
|
for (const slice of resolved.slices) {
|
|
220
234
|
if (!(slice in record)) continue;
|
|
221
235
|
if (!isValidSlice(slice, record[slice])) continue;
|
|
222
|
-
restored[slice] =
|
|
236
|
+
restored[slice] =
|
|
237
|
+
slice === "columnPinning"
|
|
238
|
+
? normalizeColumnPinning(record[slice])
|
|
239
|
+
: record[slice];
|
|
223
240
|
}
|
|
224
241
|
return restored as Partial<GridState>;
|
|
225
242
|
} catch {
|
|
@@ -323,8 +340,8 @@ function realignToColumns(
|
|
|
323
340
|
}
|
|
324
341
|
if (restored.columnPinning) {
|
|
325
342
|
realigned.columnPinning = {
|
|
326
|
-
|
|
327
|
-
|
|
343
|
+
start: keepIds(restored.columnPinning.start ?? []),
|
|
344
|
+
end: keepIds(restored.columnPinning.end ?? []),
|
|
328
345
|
};
|
|
329
346
|
}
|
|
330
347
|
if (restored.sorting) realigned.sorting = keepEntries(restored.sorting);
|