@jielga/tmdatagrid 2.0.0-beta.9 → 2.0.1
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 +1281 -768
- package/dist/index.js +4607 -3250
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/docs/adding-rows.md +132 -0
- package/docs/anatomy.md +119 -0
- package/docs/card-view.md +108 -0
- package/docs/cell-selection.md +194 -0
- package/docs/column-layout.md +182 -0
- package/docs/column-menu.md +66 -0
- package/docs/columns.md +269 -0
- package/docs/components.md +311 -0
- package/docs/draft-store.md +242 -0
- package/docs/editing.md +303 -0
- package/docs/editors.md +250 -0
- package/docs/export.md +319 -0
- package/docs/filtering.md +362 -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/migrating-to-2.md +163 -0
- package/docs/pagination.md +144 -0
- package/docs/persistence.md +114 -0
- package/docs/portfolio-rebalancer.md +94 -0
- package/docs/query-builder.md +179 -0
- package/docs/quick-search.md +84 -0
- package/docs/row-details.md +115 -0
- package/docs/row-interaction.md +149 -0
- package/docs/row-pinning.md +132 -0
- package/docs/row-selection.md +136 -0
- package/docs/row-styling.md +133 -0
- package/docs/scrolling.md +112 -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 +744 -0
- package/docs/toolbar.md +161 -0
- package/docs/use-tm-data-grid.md +361 -0
- package/package.json +22 -46
- package/skills/appearance/SKILL.md +72 -19
- package/skills/cell-selection/SKILL.md +49 -48
- package/skills/columns/SKILL.md +125 -70
- package/skills/columns/references/columns-api.md +59 -0
- package/skills/data/SKILL.md +112 -18
- package/skills/editing/SKILL.md +76 -42
- package/skills/editing/references/common-mistakes.md +77 -69
- package/skills/editing/references/editing-api.md +25 -20
- package/skills/editing/references/editors-and-validation.md +80 -18
- package/skills/filtering/SKILL.md +155 -41
- package/skills/getting-started/SKILL.md +116 -16
- package/skills/grouping/SKILL.md +31 -16
- package/skills/migrating-to-2/SKILL.md +244 -0
- package/skills/options/SKILL.md +24 -12
- package/skills/rows/SKILL.md +22 -18
- package/skills/rows/references/rows-api.md +10 -6
- package/skills/server-side/SKILL.md +170 -17
- package/skills/testing/SKILL.md +150 -32
- package/skills/testing-components/SKILL.md +230 -0
- package/skills/testing-editing/SKILL.md +240 -0
- package/src/{tmdatagrid/components → components}/TMDataGrid.tsx +38 -21
- package/src/{tmdatagrid/components → components}/TMDataGridCellEditor.tsx +54 -8
- 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 +5 -51
- package/src/{tmdatagrid/components → components}/TMDataGridDraftActions.tsx +41 -24
- package/src/{tmdatagrid/components → components}/TMDataGridEditColumn.tsx +12 -59
- package/src/{tmdatagrid/components → components}/TMDataGridEntryRows.tsx +164 -92
- 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 +15 -8
- package/src/components/TMDataGridFilterSurface.module.css +54 -0
- package/src/components/TMDataGridFilterSurface.tsx +167 -0
- package/src/{tmdatagrid/components → components}/TMDataGridFooter.tsx +53 -90
- package/src/{tmdatagrid/components → components}/TMDataGridGroupColumn.tsx +5 -69
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.module.css +12 -2
- package/src/{tmdatagrid/components → components}/TMDataGridHeaderCell.tsx +58 -21
- package/src/components/TMDataGridHeaderFilterRow.module.css +51 -0
- package/src/components/TMDataGridHeaderFilterRow.tsx +301 -0
- package/src/components/TMDataGridMenu.tsx +357 -0
- package/src/{tmdatagrid/components → components}/TMDataGridSelectColumn.tsx +11 -48
- package/src/{tmdatagrid/components → components}/TMDataGridTable.module.css +69 -56
- package/src/{tmdatagrid/components → components}/TMDataGridTable.tsx +240 -138
- package/src/{tmdatagrid/components → components}/TMDataGridToolbar.module.css +5 -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/{tmdatagrid/components → components}/editors/TMDataGridNumberEditor.tsx +3 -3
- 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 +16 -2
- 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/components/generatedColumns.tsx +187 -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}/columnOptions.ts +60 -8
- package/src/{tmdatagrid/core → core}/columnOrdering.ts +33 -14
- package/src/{tmdatagrid/core → core}/columnUtils.ts +44 -5
- package/src/core/controlledStateSync.ts +108 -0
- package/src/core/deletedRows.ts +34 -0
- package/src/core/dom.ts +74 -0
- package/src/{tmdatagrid/core → core}/editEngine.ts +1107 -460
- package/src/core/export.ts +704 -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}/grouping.ts +21 -0
- package/src/{tmdatagrid/core → core}/labels.ts +51 -6
- package/src/{tmdatagrid/core → core}/labelsSv.ts +27 -6
- package/src/core/pageReset.ts +120 -0
- package/src/core/pagination.ts +81 -0
- package/src/{tmdatagrid/core → core}/persistence.ts +22 -5
- package/src/{tmdatagrid/core → core}/summary.ts +20 -4
- package/src/{tmdatagrid/index.ts → index.ts} +69 -35
- package/src/{tmdatagrid/useTMDataGrid.tsx → useTMDataGrid.tsx} +428 -109
- package/src/useTMDataGridExport.ts +78 -0
- package/src/tmdatagrid/components/TMDataGridFilterPanel.module.css +0 -35
- package/src/tmdatagrid/components/TMDataGridFilterPanel.tsx +0 -354
- package/src/tmdatagrid/components/TMDataGridToolbar.tsx +0 -161
- package/src/tmdatagrid/core/cellExport.ts +0 -320
- /package/src/{tmdatagrid/TMDataGridContext.ts → TMDataGridContext.ts} +0 -0
- /package/src/{tmdatagrid/components → components}/TMDataGrid.module.css +0 -0
- /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}/capabilities.ts +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}/controlledState.ts +0 -0
- /package/src/{tmdatagrid/core → core}/draftCellContext.ts +0 -0
- /package/src/{tmdatagrid/core → core}/editorFocus.ts +0 -0
- /package/src/{tmdatagrid/core → core}/expanding.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}/resizePreview.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
- /package/src/{tmdatagrid/core → core}/useSettledTableState.ts +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { useCreateStore, useSelector } from "@tanstack/react-store";
|
|
2
2
|
import { shallow } from "@tanstack/store";
|
|
3
3
|
import {
|
|
4
|
+
type AccessorFn,
|
|
5
|
+
type AccessorFnColumnDef,
|
|
6
|
+
type AccessorKeyColumnDef,
|
|
4
7
|
aggregationFns,
|
|
5
8
|
type ColumnDef,
|
|
9
|
+
type ColumnHelper,
|
|
6
10
|
columnFacetingFeature,
|
|
7
11
|
columnFilteringFeature,
|
|
8
12
|
columnGroupingFeature,
|
|
@@ -19,11 +23,17 @@ import {
|
|
|
19
23
|
createFilteredRowModel,
|
|
20
24
|
createGroupedRowModel,
|
|
21
25
|
createPaginatedRowModel,
|
|
26
|
+
type DeepKeys,
|
|
27
|
+
type DeepValue,
|
|
28
|
+
type DisplayColumnDef,
|
|
22
29
|
filterFns,
|
|
30
|
+
type GroupColumnDef,
|
|
23
31
|
globalFilteringFeature,
|
|
32
|
+
type IdentifiedColumnDef,
|
|
24
33
|
metaHelper,
|
|
25
34
|
type Row,
|
|
26
35
|
type RowData,
|
|
36
|
+
rowAggregationFeature,
|
|
27
37
|
rowExpandingFeature,
|
|
28
38
|
rowPaginationFeature,
|
|
29
39
|
rowPinningFeature,
|
|
@@ -53,7 +63,7 @@ import {
|
|
|
53
63
|
type TMDataGridEditApi,
|
|
54
64
|
type TMDataGridEditCommitArgs,
|
|
55
65
|
type TMDataGridSaveDraftsArgs,
|
|
56
|
-
type
|
|
66
|
+
type TMDataGridSaveDraftsResponse,
|
|
57
67
|
type TMDataGridEditEngineContext,
|
|
58
68
|
type TMDataGridColumnEditOptions,
|
|
59
69
|
type TMDataGridEditMode,
|
|
@@ -69,6 +79,11 @@ import {
|
|
|
69
79
|
} from "./core/filterOperators";
|
|
70
80
|
import { getColumnDefaultOperator, isControlColumn } from "./core/columnUtils";
|
|
71
81
|
import type { TMDataGridColumnFilterOptions } from "./core/filterControls";
|
|
82
|
+
import {
|
|
83
|
+
resolveFilterOptions,
|
|
84
|
+
type TMDataGridFiltersOptions,
|
|
85
|
+
type TMDataGridFiltersSettings,
|
|
86
|
+
} from "./core/filterSurface";
|
|
72
87
|
import {
|
|
73
88
|
createFuzzyRankedSortedRowModel,
|
|
74
89
|
fuzzyGlobalFilterFn,
|
|
@@ -101,23 +116,34 @@ import {
|
|
|
101
116
|
stabilizeControlledState,
|
|
102
117
|
withoutUndefinedSlices,
|
|
103
118
|
} from "./core/controlledState";
|
|
104
|
-
import type { TMDataGridCellRange } from "./core/cellRange";
|
|
105
119
|
import {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
beginControlledStateSync,
|
|
121
|
+
deferControlledStateSyncPublishes,
|
|
122
|
+
endControlledStateSync,
|
|
123
|
+
} from "./core/controlledStateSync";
|
|
124
|
+
import { registerDeletedRows } from "./core/deletedRows";
|
|
109
125
|
import {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
} from "./
|
|
126
|
+
withPageReset,
|
|
127
|
+
type TMDataGridQueryTable,
|
|
128
|
+
} from "./core/pageReset";
|
|
129
|
+
import type { TMDataGridCellRange } from "./core/cellRange";
|
|
113
130
|
import {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
131
|
+
resolveExportOptions,
|
|
132
|
+
type TMDataGridExportOptions,
|
|
133
|
+
type TMDataGridExportPickerRequest,
|
|
134
|
+
type TMDataGridExportSettings,
|
|
135
|
+
type TMDataGridExportValueGetter,
|
|
136
|
+
} from "./core/export";
|
|
137
|
+
import { SELECT_COLUMN_ID } from "./components/TMDataGridSelectColumn";
|
|
138
|
+
import { GROUP_COLUMN_ID } from "./components/TMDataGridGroupColumn";
|
|
139
|
+
import { DETAILS_COLUMN_ID } from "./components/TMDataGridDetailsColumn";
|
|
140
|
+
import { EDIT_COLUMN_ID } from "./components/TMDataGridEditColumn";
|
|
117
141
|
import {
|
|
142
|
+
createDetailsColumn,
|
|
118
143
|
createEditColumn,
|
|
119
|
-
|
|
120
|
-
|
|
144
|
+
createGroupColumn,
|
|
145
|
+
createSelectColumn,
|
|
146
|
+
} from "./components/generatedColumns";
|
|
121
147
|
import {
|
|
122
148
|
createRowNumberColumn,
|
|
123
149
|
ROW_NUMBER_COLUMN_ID,
|
|
@@ -139,8 +165,11 @@ const PERSIST_DEBOUNCE_MS = 200;
|
|
|
139
165
|
* `type` and `options` are read by both stages, so one declaration of each
|
|
140
166
|
* feeds the filter panel and the cell editor, which is why they sit outside
|
|
141
167
|
* both namespaces.
|
|
168
|
+
*
|
|
169
|
+
* `TData` types the row that `options` and `edit.enabled` callbacks receive.
|
|
170
|
+
* `createTMDataGridColumnHelper<TData>()` fills it in.
|
|
142
171
|
*/
|
|
143
|
-
export type TMDataGridColumnMeta = {
|
|
172
|
+
export type TMDataGridColumnMeta<TData extends RowData = TMDataGridRowData> = {
|
|
144
173
|
/** Name shown in menus and the column manager. Falls back to a string header. */
|
|
145
174
|
label?: string;
|
|
146
175
|
/**
|
|
@@ -155,7 +184,7 @@ export type TMDataGridColumnMeta = {
|
|
|
155
184
|
* function of the table, column and, for editors, the row. See
|
|
156
185
|
* {@link TMDataGridOptionsSource}.
|
|
157
186
|
*/
|
|
158
|
-
options?: TMDataGridOptionsSource
|
|
187
|
+
options?: TMDataGridOptionsSource<TData>;
|
|
159
188
|
/** Share of the leftover width this column claims. Defaults to `1`. */
|
|
160
189
|
flex?: number;
|
|
161
190
|
align?: "left" | "right" | "center";
|
|
@@ -172,8 +201,8 @@ export type TMDataGridColumnMeta = {
|
|
|
172
201
|
*/
|
|
173
202
|
enableOrdering?: boolean;
|
|
174
203
|
/**
|
|
175
|
-
* How this column filters: the operator a fresh
|
|
176
|
-
* value control the filter panel renders for it.
|
|
204
|
+
* How this column filters: which operators it offers, the operator a fresh
|
|
205
|
+
* filter starts with, and the value control the filter panel renders for it.
|
|
177
206
|
*
|
|
178
207
|
* ```tsx
|
|
179
208
|
* meta: {
|
|
@@ -199,7 +228,26 @@ export type TMDataGridColumnMeta = {
|
|
|
199
228
|
*
|
|
200
229
|
* See {@link TMDataGridColumnEditOptions}.
|
|
201
230
|
*/
|
|
202
|
-
edit?: TMDataGridColumnEditOptions
|
|
231
|
+
edit?: TMDataGridColumnEditOptions<TData>;
|
|
232
|
+
/**
|
|
233
|
+
* `false` leaves the column out of every export and out of Ctrl+C - for a
|
|
234
|
+
* column of buttons, or one whose value means nothing outside the grid.
|
|
235
|
+
* Defaults to `true`.
|
|
236
|
+
*/
|
|
237
|
+
enableExport?: boolean;
|
|
238
|
+
/**
|
|
239
|
+
* The value an export writes for this column, in place of
|
|
240
|
+
* `row.getValue(column.id)`. The export otherwise writes the value, never
|
|
241
|
+
* what the cell renders, so this is where a status code becomes its label
|
|
242
|
+
* or a nested object becomes one field.
|
|
243
|
+
*
|
|
244
|
+
* ```tsx
|
|
245
|
+
* meta: {
|
|
246
|
+
* exportValue: ({ value }) => STATUS_LABELS[value as Status],
|
|
247
|
+
* }
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
exportValue?: TMDataGridExportValueGetter;
|
|
203
251
|
};
|
|
204
252
|
|
|
205
253
|
/** Grid-wide configuration passed through `options.meta`. */
|
|
@@ -232,6 +280,7 @@ export const tmDataGridFeatures = tableFeatures({
|
|
|
232
280
|
columnResizingFeature,
|
|
233
281
|
columnFacetingFeature,
|
|
234
282
|
columnGroupingFeature,
|
|
283
|
+
rowAggregationFeature,
|
|
235
284
|
// Registered for grouping's sake rather than for tree data: the grouped row
|
|
236
285
|
// model builds the parent rows, and this is what flattens the expanded ones
|
|
237
286
|
// back into the flat list the body virtualizes.
|
|
@@ -271,8 +320,57 @@ export type TMDataGridTable<TData extends RowData> = Table<
|
|
|
271
320
|
TData
|
|
272
321
|
>;
|
|
273
322
|
|
|
274
|
-
|
|
275
|
-
|
|
323
|
+
/** A column definition with `meta` typed against the row type. */
|
|
324
|
+
type WithRowTypedMeta<TDef, TData extends RowData> = TDef extends unknown
|
|
325
|
+
? Omit<TDef, "meta"> & { meta?: TMDataGridColumnMeta<TData> }
|
|
326
|
+
: never;
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* TanStack's column helper with `meta` typed against `TData`, so the
|
|
330
|
+
* `meta.options` and `meta.edit.enabled` callbacks receive
|
|
331
|
+
* `Row<TMDataGridFeatures, TData>`.
|
|
332
|
+
*/
|
|
333
|
+
export type TMDataGridColumnHelper<TData extends RowData> = {
|
|
334
|
+
accessor: <
|
|
335
|
+
TAccessor extends AccessorFn<TData> | DeepKeys<TData>,
|
|
336
|
+
TValue extends TAccessor extends AccessorFn<TData, infer TReturn>
|
|
337
|
+
? TReturn
|
|
338
|
+
: TAccessor extends DeepKeys<TData>
|
|
339
|
+
? DeepValue<TData, TAccessor>
|
|
340
|
+
: never,
|
|
341
|
+
>(
|
|
342
|
+
accessor: TAccessor,
|
|
343
|
+
column: WithRowTypedMeta<
|
|
344
|
+
TAccessor extends AccessorFn<TData>
|
|
345
|
+
? DisplayColumnDef<TMDataGridFeatures, TData, TValue>
|
|
346
|
+
: IdentifiedColumnDef<TMDataGridFeatures, TData, TValue>,
|
|
347
|
+
TData
|
|
348
|
+
>,
|
|
349
|
+
) => TAccessor extends AccessorFn<TData>
|
|
350
|
+
? AccessorFnColumnDef<TMDataGridFeatures, TData, TValue>
|
|
351
|
+
: AccessorKeyColumnDef<TMDataGridFeatures, TData, TValue>;
|
|
352
|
+
columns: ColumnHelper<TMDataGridFeatures, TData>["columns"];
|
|
353
|
+
display: (
|
|
354
|
+
column: WithRowTypedMeta<DisplayColumnDef<TMDataGridFeatures, TData>, TData>,
|
|
355
|
+
) => DisplayColumnDef<TMDataGridFeatures, TData, unknown>;
|
|
356
|
+
group: (
|
|
357
|
+
column: WithRowTypedMeta<
|
|
358
|
+
GroupColumnDef<TMDataGridFeatures, TData, unknown>,
|
|
359
|
+
TData
|
|
360
|
+
>,
|
|
361
|
+
) => GroupColumnDef<TMDataGridFeatures, TData, unknown>;
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
export function createTMDataGridColumnHelper<
|
|
365
|
+
TData extends RowData,
|
|
366
|
+
>(): TMDataGridColumnHelper<TData> {
|
|
367
|
+
// Same runtime helper. The features object registers one meta type for
|
|
368
|
+
// every table, so the row-typed meta exists only in this signature; the
|
|
369
|
+
// grid calls the callbacks with the rows of the table they belong to.
|
|
370
|
+
return createColumnHelper<
|
|
371
|
+
TMDataGridFeatures,
|
|
372
|
+
TData
|
|
373
|
+
>() as unknown as TMDataGridColumnHelper<TData>;
|
|
276
374
|
}
|
|
277
375
|
|
|
278
376
|
/** What the `renderDetails` render prop is handed for an expanded row. */
|
|
@@ -305,9 +403,20 @@ const EMPTY_IDS: ReadonlyArray<string> = [];
|
|
|
305
403
|
*/
|
|
306
404
|
export type TMDataGridUiState = {
|
|
307
405
|
filterPanelOpen: boolean;
|
|
308
|
-
|
|
309
|
-
|
|
406
|
+
/**
|
|
407
|
+
* Column whose *panel* row should take the focus. Cleared once the row has
|
|
408
|
+
* taken it, so pointing at the same column twice focuses twice.
|
|
409
|
+
*/
|
|
310
410
|
filterPanelColumnId: string | null;
|
|
411
|
+
/**
|
|
412
|
+
* Column whose *header filter* control should take the focus, under
|
|
413
|
+
* `filters.inHeader`. Cleared once taken, like the one above.
|
|
414
|
+
*
|
|
415
|
+
* Its own slot rather than a second reader of `filterPanelColumnId`: a grid
|
|
416
|
+
* can have header filters and a panel at once, and two controls racing to
|
|
417
|
+
* answer one id means whichever mounted last wins the caret.
|
|
418
|
+
*/
|
|
419
|
+
headerFilterColumnId: string | null;
|
|
311
420
|
/**
|
|
312
421
|
* Column being dragged by its header, if any. Held here rather than read from
|
|
313
422
|
* `dataTransfer`, which browsers keep unreadable until the drop.
|
|
@@ -346,13 +455,33 @@ export type TMDataGridUiState = {
|
|
|
346
455
|
* describe different places.
|
|
347
456
|
*/
|
|
348
457
|
cellRange: TMDataGridCellRange | null;
|
|
458
|
+
/**
|
|
459
|
+
* The export column picker, while it is open: which rows it exports and the
|
|
460
|
+
* options of the item that opened it. `null` while closed. Held here rather
|
|
461
|
+
* than in the menu item, which unmounts with the dropdown the moment it is
|
|
462
|
+
* clicked.
|
|
463
|
+
*/
|
|
464
|
+
exportPicker: TMDataGridExportPickerRequest | null;
|
|
349
465
|
};
|
|
350
466
|
|
|
351
467
|
export type TMDataGridUiActions = {
|
|
352
468
|
openFilterPanel: (columnId?: string | null) => void;
|
|
353
469
|
closeFilterPanel: () => void;
|
|
354
|
-
|
|
355
|
-
|
|
470
|
+
/** Opens the export column picker for `request`. See `TMDataGrid.Menu.Export`'s `columns="custom"`. */
|
|
471
|
+
openExportPicker: (request: TMDataGridExportPickerRequest) => void;
|
|
472
|
+
closeExportPicker: () => void;
|
|
473
|
+
/**
|
|
474
|
+
* Points at a column's row in the filter panel without opening anything.
|
|
475
|
+
* `openFilterPanel` does this as well as opening; this is the half a panel
|
|
476
|
+
* that is already showing needs.
|
|
477
|
+
*/
|
|
478
|
+
focusPanelFilter: (columnId: string | null) => void;
|
|
479
|
+
/**
|
|
480
|
+
* Points at a column's header filter control - what `openColumnFilter` does
|
|
481
|
+
* under `filters.inHeader`, where there is no panel to open. The header row
|
|
482
|
+
* scrolls the column into view and focuses it.
|
|
483
|
+
*/
|
|
484
|
+
focusHeaderFilter: (columnId: string | null) => void;
|
|
356
485
|
startColumnDrag: (columnId: string) => void;
|
|
357
486
|
endColumnDrag: () => void;
|
|
358
487
|
/**
|
|
@@ -397,13 +526,28 @@ export type TMDataGridApi<TData extends RowData> = {
|
|
|
397
526
|
ui: TMDataGridUiStore;
|
|
398
527
|
/**
|
|
399
528
|
* The edit engine - open forms, dirty/error projections, and the verbs
|
|
400
|
-
* (`begin`, `commit`, `cancel`, `
|
|
401
|
-
* out the same TanStack Form the inline editors write through
|
|
402
|
-
* or detail panel can share a row's draft
|
|
529
|
+
* (`begin`, `commit`, `cancel`, `saveDrafts`). `edit.getForm(rowId)` hands
|
|
530
|
+
* out the same TanStack Form the inline editors write through while a row
|
|
531
|
+
* is open, so a drawer or detail panel can share a row's draft; a
|
|
532
|
+
* committed row has no form until `begin` reopens it. Inert until
|
|
533
|
+
* `editing` is set.
|
|
403
534
|
*/
|
|
404
535
|
edit: TMDataGridEditApi<TData>;
|
|
405
536
|
/** Table-level feature switches, re-read from options on every render. */
|
|
406
537
|
features: TMDataGridFeatureFlags;
|
|
538
|
+
/**
|
|
539
|
+
* Where the filter controls live, the `filters` option with its defaults
|
|
540
|
+
* filled in. On the api rather than in a component's props because the
|
|
541
|
+
* pills, the column menu and `openColumnFilter` all have to agree with the
|
|
542
|
+
* table about which surface is on.
|
|
543
|
+
*/
|
|
544
|
+
filters: TMDataGridFiltersSettings;
|
|
545
|
+
/**
|
|
546
|
+
* How the grid exports, the `exportOptions` option with its defaults filled
|
|
547
|
+
* in. Read by `useTMDataGridExport`, the `TMDataGrid.Menu.Export*` items and
|
|
548
|
+
* the cell-range menu, so every export of the grid agrees on the format.
|
|
549
|
+
*/
|
|
550
|
+
exportOptions: TMDataGridExportSettings;
|
|
407
551
|
/** Every string the chrome renders, `labels` merged over the English defaults. */
|
|
408
552
|
labels: TMDataGridLabels;
|
|
409
553
|
/** The detail renderer, when row details are on. See `renderDetails`. */
|
|
@@ -592,25 +736,14 @@ export type TMDataGridEditingOptions<TData extends RowData> =
|
|
|
592
736
|
*
|
|
593
737
|
* Rows still open are not in the payload and stay open. Returning
|
|
594
738
|
* nothing saves the whole store and throwing saves none of it;
|
|
595
|
-
* return a {@link
|
|
739
|
+
* return a {@link TMDataGridSaveDraftsResponse} to save part of it.
|
|
596
740
|
*/
|
|
597
741
|
onSaveDrafts?: (
|
|
598
742
|
args: TMDataGridSaveDraftsArgs<TData>,
|
|
599
743
|
) =>
|
|
600
744
|
| void
|
|
601
|
-
|
|
|
602
|
-
| Promise<void |
|
|
603
|
-
/**
|
|
604
|
-
* @deprecated Renamed to {@link onSaveDrafts} - it fires when the
|
|
605
|
-
* draft store is saved, not when a row commits into it. Still
|
|
606
|
-
* honoured; removed in a later beta.
|
|
607
|
-
*/
|
|
608
|
-
onCommitDrafts?: (
|
|
609
|
-
args: TMDataGridSaveDraftsArgs<TData>,
|
|
610
|
-
) =>
|
|
611
|
-
| void
|
|
612
|
-
| TMDataGridSaveDraftsResult
|
|
613
|
-
| Promise<void | TMDataGridSaveDraftsResult>;
|
|
745
|
+
| TMDataGridSaveDraftsResponse
|
|
746
|
+
| Promise<void | TMDataGridSaveDraftsResponse>;
|
|
614
747
|
/**
|
|
615
748
|
* Keep committed entry rows pinned in the sticky entry block until
|
|
616
749
|
* the draft store is saved, out of the body's sort. Off by default:
|
|
@@ -626,8 +759,6 @@ export type TMDataGridEditingOptions<TData extends RowData> =
|
|
|
626
759
|
draft?: false;
|
|
627
760
|
/** Only `draft: true` has a store to save - see the other branch. */
|
|
628
761
|
onSaveDrafts?: never;
|
|
629
|
-
/** @deprecated See {@link onSaveDrafts}. */
|
|
630
|
-
onCommitDrafts?: never;
|
|
631
762
|
/** Parked entry rows exist only under `draft: true` - see there. */
|
|
632
763
|
newRowsSticky?: never;
|
|
633
764
|
}
|
|
@@ -693,12 +824,55 @@ export type UseTMDataGridOptions<TData extends RowData> = Omit<
|
|
|
693
824
|
* no extra flag.
|
|
694
825
|
*/
|
|
695
826
|
enablePagination?: boolean;
|
|
827
|
+
/**
|
|
828
|
+
* Sends the grid back to the first page whenever the query changes - a
|
|
829
|
+
* column filter, the quick search, the sort or the grouping. On by
|
|
830
|
+
* default. TanStack's own `autoResetPageIndex` is switched off by the
|
|
831
|
+
* grid: it fires on any change to the `data` array, which under
|
|
832
|
+
* `editing.draft` is every commit.
|
|
833
|
+
*
|
|
834
|
+
* Server-side, `pageIndex` is a position in a result set the grid does not
|
|
835
|
+
* own: narrowing the query leaves it pointing past the last page, and the
|
|
836
|
+
* next request comes back empty. The reset is applied in the same event as
|
|
837
|
+
* the change, so one request goes out, for the first page of the new query.
|
|
838
|
+
*/
|
|
839
|
+
resetPageOnQueryChange?: boolean;
|
|
696
840
|
/**
|
|
697
841
|
* The row-number gutter: a generated lane, outermost left, numbering the
|
|
698
842
|
* rows of the current view - sorted, filtered, continuing across pages,
|
|
699
843
|
* with group rows unnumbered. Off by default.
|
|
700
844
|
*/
|
|
701
845
|
enableRowNumbers?: boolean;
|
|
846
|
+
/**
|
|
847
|
+
* Where the grid puts its filter controls - a popup over the rows, a sidebar
|
|
848
|
+
* beside them, controls in the header row, or nowhere at all so you place
|
|
849
|
+
* `TMDataGrid.FilterPanel` yourself.
|
|
850
|
+
*
|
|
851
|
+
* ```tsx
|
|
852
|
+
* useTMDataGrid({ data, columns, filters: { surface: "sidebar", inHeader: true } });
|
|
853
|
+
* ```
|
|
854
|
+
*
|
|
855
|
+
* Defaults to `{ surface: "popup" }` - the floating panel the grid has
|
|
856
|
+
* always shown. See {@link TMDataGridFiltersOptions}.
|
|
857
|
+
*
|
|
858
|
+
* Read field by field, so a literal is fine here - unlike `labels` or
|
|
859
|
+
* `persist`, this one does not have to be referentially stable.
|
|
860
|
+
*/
|
|
861
|
+
filters?: TMDataGridFiltersOptions;
|
|
862
|
+
/**
|
|
863
|
+
* How the grid exports: the file format, the file name and whether the
|
|
864
|
+
* column labels go in as the first row. Defaults to `csvExcelFormat()`,
|
|
865
|
+
* `"export"` and `true`. See {@link TMDataGridExportOptions}.
|
|
866
|
+
*
|
|
867
|
+
* ```tsx
|
|
868
|
+
* useTMDataGrid({ data, columns, exportOptions: { format: csvFormat(), fileName: "employees" } });
|
|
869
|
+
* ```
|
|
870
|
+
*
|
|
871
|
+
* Read field by field like `filters`, so a literal is fine. A `format`
|
|
872
|
+
* built inline is rebuilt every render, which costs nothing but a small
|
|
873
|
+
* object; keep it at module scope when that bothers you.
|
|
874
|
+
*/
|
|
875
|
+
exportOptions?: TMDataGridExportOptions;
|
|
702
876
|
/**
|
|
703
877
|
* How the quick search (`TMDataGrid.Search`) matches. `"fuzzy"` - the
|
|
704
878
|
* default - forgives typos and skipped characters, and while it is the
|
|
@@ -962,6 +1136,7 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
962
1136
|
labels: labelsOverride,
|
|
963
1137
|
enableColumnOrdering,
|
|
964
1138
|
enablePagination,
|
|
1139
|
+
resetPageOnQueryChange,
|
|
965
1140
|
enableRowNumbers,
|
|
966
1141
|
selectionMode,
|
|
967
1142
|
showSelectedBackground,
|
|
@@ -969,6 +1144,8 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
969
1144
|
onHighlightedRowChange,
|
|
970
1145
|
cellSelection,
|
|
971
1146
|
onFocusedCellChange,
|
|
1147
|
+
filters: filterOptions,
|
|
1148
|
+
exportOptions: exportOptionsOverride,
|
|
972
1149
|
editing,
|
|
973
1150
|
renderDetails,
|
|
974
1151
|
renderDetailsEstHeight = DEFAULT_DETAILS_EST_HEIGHT,
|
|
@@ -1000,6 +1177,50 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1000
1177
|
// Resolved on the override's identity, so a module-scope dictionary costs one
|
|
1001
1178
|
// merge for the lifetime of the grid.
|
|
1002
1179
|
const labels = useMemo(() => mergeLabels(labelsOverride), [labelsOverride]);
|
|
1180
|
+
// Field by field for the same reason as `filters` below.
|
|
1181
|
+
const {
|
|
1182
|
+
format: exportFormat,
|
|
1183
|
+
fileName: exportFileName,
|
|
1184
|
+
includeHeaders: exportIncludeHeaders,
|
|
1185
|
+
columns: exportColumns,
|
|
1186
|
+
} = exportOptionsOverride ?? {};
|
|
1187
|
+
const exportOptions = useMemo(
|
|
1188
|
+
() =>
|
|
1189
|
+
resolveExportOptions({
|
|
1190
|
+
format: exportFormat,
|
|
1191
|
+
fileName: exportFileName,
|
|
1192
|
+
includeHeaders: exportIncludeHeaders,
|
|
1193
|
+
columns: exportColumns,
|
|
1194
|
+
}),
|
|
1195
|
+
[exportFormat, exportFileName, exportIncludeHeaders, exportColumns],
|
|
1196
|
+
);
|
|
1197
|
+
// Unpacked before the memo, so the api is keyed on the five fields rather
|
|
1198
|
+
// than on the object's identity - which is what lets `filters` be written as
|
|
1199
|
+
// a literal, the way it reads best, without republishing every render.
|
|
1200
|
+
const {
|
|
1201
|
+
surface: filterSurface,
|
|
1202
|
+
sidebarSide: filterSidebarSide,
|
|
1203
|
+
sidebarWidth: filterSidebarWidth,
|
|
1204
|
+
defaultOpen: filtersDefaultOpen,
|
|
1205
|
+
inHeader: filtersInHeader,
|
|
1206
|
+
} = filterOptions ?? {};
|
|
1207
|
+
const filters = useMemo(
|
|
1208
|
+
() =>
|
|
1209
|
+
resolveFilterOptions({
|
|
1210
|
+
surface: filterSurface,
|
|
1211
|
+
sidebarSide: filterSidebarSide,
|
|
1212
|
+
sidebarWidth: filterSidebarWidth,
|
|
1213
|
+
defaultOpen: filtersDefaultOpen,
|
|
1214
|
+
inHeader: filtersInHeader,
|
|
1215
|
+
}),
|
|
1216
|
+
[
|
|
1217
|
+
filterSurface,
|
|
1218
|
+
filterSidebarSide,
|
|
1219
|
+
filterSidebarWidth,
|
|
1220
|
+
filtersDefaultOpen,
|
|
1221
|
+
filtersInHeader,
|
|
1222
|
+
],
|
|
1223
|
+
);
|
|
1003
1224
|
|
|
1004
1225
|
const pinningEnabled = options.enableColumnPinning !== false;
|
|
1005
1226
|
const selectColumnEnabled = features.selectColumn;
|
|
@@ -1184,12 +1405,10 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1184
1405
|
const editContextRef = useRef<TMDataGridEditEngineContext>(null as never);
|
|
1185
1406
|
// The engine is erased; the row type comes back on the way out, which is
|
|
1186
1407
|
// what makes `edit.addRow(values)` check against `TData`.
|
|
1187
|
-
const [
|
|
1188
|
-
() =>
|
|
1189
|
-
createEditEngine(
|
|
1190
|
-
() => editContextRef.current,
|
|
1191
|
-
) as unknown as TMDataGridEditApi<TData>,
|
|
1408
|
+
const [engine] = useState(() =>
|
|
1409
|
+
createEditEngine(() => editContextRef.current),
|
|
1192
1410
|
);
|
|
1411
|
+
const edit = engine as unknown as TMDataGridEditApi<TData>;
|
|
1193
1412
|
|
|
1194
1413
|
// The rows as shown. Under `editing.draft` a committed row is a row like any
|
|
1195
1414
|
// other to the table: its draft replaces the consumer's record and a
|
|
@@ -1269,12 +1488,36 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1269
1488
|
[],
|
|
1270
1489
|
);
|
|
1271
1490
|
|
|
1491
|
+
// Filled immediately after the call below. The query-change wrappers close
|
|
1492
|
+
// over it rather than over `table`, since they are built as part of the
|
|
1493
|
+
// options the table is constructed from.
|
|
1494
|
+
const tableRef = useRef<TMDataGridTable<TData>>(null as never);
|
|
1495
|
+
// A narrower query invalidates the page the grid is on - see pageReset.ts.
|
|
1496
|
+
// On by default everywhere: TanStack's own `autoResetPageIndex` is switched
|
|
1497
|
+
// off below, since it also fires on every draft commit.
|
|
1498
|
+
const resetPage = resetPageOnQueryChange ?? true;
|
|
1499
|
+
const getQueryTable = useCallback(
|
|
1500
|
+
() => tableRef.current as unknown as TMDataGridQueryTable,
|
|
1501
|
+
[],
|
|
1502
|
+
);
|
|
1503
|
+
|
|
1504
|
+
// The sync of `state` into the table's atoms happens inside this call, in
|
|
1505
|
+
// the render body, and publishing from there makes React warn about the
|
|
1506
|
+
// consumer's component - see controlledStateSync.ts.
|
|
1507
|
+
beginControlledStateSync();
|
|
1272
1508
|
const table = useTable({
|
|
1273
1509
|
// The grid paints a running drag itself, one style write per frame - see
|
|
1274
1510
|
// the resize preview in TMDataGridTable - and takes the width into state
|
|
1275
1511
|
// once, when the pointer is released. `"onChange"` publishes a width on
|
|
1276
1512
|
// every pointer move instead, which re-renders the grid for each of them.
|
|
1277
1513
|
columnResizeMode: "onEnd",
|
|
1514
|
+
// TanStack resets both whenever the `data` array's identity changes, and
|
|
1515
|
+
// the rows as shown are a new array on every draft commit (see `shown`),
|
|
1516
|
+
// so a commit on page 3 would land on page 1 with every details panel
|
|
1517
|
+
// closed. Off here; the page reset the grid does want - on a query
|
|
1518
|
+
// change - is its own, below. A consumer's explicit option still wins.
|
|
1519
|
+
autoResetExpanded: false,
|
|
1520
|
+
autoResetPageIndex: false,
|
|
1278
1521
|
enableSorting: true,
|
|
1279
1522
|
enableColumnResizing: true,
|
|
1280
1523
|
// The quick search's matcher. Fuzzy by default (Q4); `"contains"` keeps
|
|
@@ -1291,6 +1534,37 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1291
1534
|
// `"reorder"` to keep the column and have it moved to the front instead.
|
|
1292
1535
|
groupedColumnMode: "remove",
|
|
1293
1536
|
...options,
|
|
1537
|
+
// The query slices, wrapped so a change also takes the grid back to the
|
|
1538
|
+
// first page - see pageReset.ts. Spread conditionally: the keys carry a
|
|
1539
|
+
// `makeStateUpdater` default, and an explicit `undefined` would overwrite
|
|
1540
|
+
// it and leave the slice unwritable.
|
|
1541
|
+
...(resetPage
|
|
1542
|
+
? {
|
|
1543
|
+
onColumnFiltersChange: withPageReset(
|
|
1544
|
+
"columnFilters",
|
|
1545
|
+
options.onColumnFiltersChange as never,
|
|
1546
|
+
getQueryTable,
|
|
1547
|
+
) as TableOptions<
|
|
1548
|
+
TMDataGridFeatures,
|
|
1549
|
+
TData
|
|
1550
|
+
>["onColumnFiltersChange"],
|
|
1551
|
+
onGlobalFilterChange: withPageReset(
|
|
1552
|
+
"globalFilter",
|
|
1553
|
+
options.onGlobalFilterChange as never,
|
|
1554
|
+
getQueryTable,
|
|
1555
|
+
) as TableOptions<TMDataGridFeatures, TData>["onGlobalFilterChange"],
|
|
1556
|
+
onSortingChange: withPageReset(
|
|
1557
|
+
"sorting",
|
|
1558
|
+
options.onSortingChange as never,
|
|
1559
|
+
getQueryTable,
|
|
1560
|
+
) as TableOptions<TMDataGridFeatures, TData>["onSortingChange"],
|
|
1561
|
+
onGroupingChange: withPageReset(
|
|
1562
|
+
"grouping",
|
|
1563
|
+
options.onGroupingChange as never,
|
|
1564
|
+
getQueryTable,
|
|
1565
|
+
) as TableOptions<TMDataGridFeatures, TData>["onGroupingChange"],
|
|
1566
|
+
}
|
|
1567
|
+
: {}),
|
|
1294
1568
|
// The rows as shown - see `shown` above. The consumer's own array passes
|
|
1295
1569
|
// through untouched while nothing is committed.
|
|
1296
1570
|
data: shown.rows,
|
|
@@ -1313,6 +1587,20 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1313
1587
|
(typeof options.enableRowPinning === "function"
|
|
1314
1588
|
? options.enableRowPinning(row)
|
|
1315
1589
|
: options.enableRowPinning === true),
|
|
1590
|
+
// A deletion-marked row is not selectable: it is on its way out, and a
|
|
1591
|
+
// bulk action over the selection must not see it. TanStack reads the
|
|
1592
|
+
// predicate on every call, so the mark is checked live against the
|
|
1593
|
+
// engine. Only under `editing.draft`, the one place marks exist; the
|
|
1594
|
+
// consumer's own option keeps the final say, predicate form included.
|
|
1595
|
+
...(editDraft
|
|
1596
|
+
? {
|
|
1597
|
+
enableRowSelection: (row: Row<TMDataGridFeatures, TData>) =>
|
|
1598
|
+
!engine.isRowDeleted(row.id) &&
|
|
1599
|
+
(typeof options.enableRowSelection === "function"
|
|
1600
|
+
? options.enableRowSelection(row)
|
|
1601
|
+
: options.enableRowSelection !== false),
|
|
1602
|
+
}
|
|
1603
|
+
: {}),
|
|
1316
1604
|
features: tmDataGridFeatures,
|
|
1317
1605
|
columns: columns as TableOptions<TMDataGridFeatures, TData>["columns"],
|
|
1318
1606
|
// The stabilized controlled state; `undefined` when nothing is controlled.
|
|
@@ -1334,14 +1622,14 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1334
1622
|
columnPinning: {
|
|
1335
1623
|
// The generated columns are structurally pinned, so they are re-applied
|
|
1336
1624
|
// on top of anything restored from storage.
|
|
1337
|
-
|
|
1625
|
+
start: [
|
|
1338
1626
|
...(rowNumbersEnabled && pinningEnabled ? [ROW_NUMBER_COLUMN_ID] : []),
|
|
1339
1627
|
...(selectColumnEnabled && pinningEnabled ? [SELECT_COLUMN_ID] : []),
|
|
1340
1628
|
...(groupColumnEnabled && pinningEnabled ? [GROUP_COLUMN_ID] : []),
|
|
1341
1629
|
...(detailsColumnEnabled && pinningEnabled ? [DETAILS_COLUMN_ID] : []),
|
|
1342
1630
|
...(
|
|
1343
|
-
persistedState.columnPinning?.
|
|
1344
|
-
options.initialState?.columnPinning?.
|
|
1631
|
+
persistedState.columnPinning?.start ??
|
|
1632
|
+
options.initialState?.columnPinning?.start ??
|
|
1345
1633
|
[]
|
|
1346
1634
|
).filter(
|
|
1347
1635
|
(id) =>
|
|
@@ -1353,10 +1641,10 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1353
1641
|
],
|
|
1354
1642
|
// The edit lane mirrors the generated columns on the left: structurally
|
|
1355
1643
|
// pinned, outermost, re-applied over anything restored.
|
|
1356
|
-
|
|
1644
|
+
end: [
|
|
1357
1645
|
...(
|
|
1358
|
-
persistedState.columnPinning?.
|
|
1359
|
-
options.initialState?.columnPinning?.
|
|
1646
|
+
persistedState.columnPinning?.end ??
|
|
1647
|
+
options.initialState?.columnPinning?.end ??
|
|
1360
1648
|
[]
|
|
1361
1649
|
).filter((id) => id !== EDIT_COLUMN_ID),
|
|
1362
1650
|
...(editColumnEnabled && pinningEnabled ? [EDIT_COLUMN_ID] : []),
|
|
@@ -1370,6 +1658,9 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1370
1658
|
},
|
|
1371
1659
|
},
|
|
1372
1660
|
}, selectSettledState);
|
|
1661
|
+
endControlledStateSync();
|
|
1662
|
+
tableRef.current = table as unknown as TMDataGridTable<TData>;
|
|
1663
|
+
deferControlledStateSyncPublishes(table.store);
|
|
1373
1664
|
|
|
1374
1665
|
// The engine's view of this render - see the engine's creation above.
|
|
1375
1666
|
editContextRef.current = {
|
|
@@ -1384,9 +1675,8 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1384
1675
|
editing?.isRowEditable as TMDataGridEditEngineContext["isRowEditable"],
|
|
1385
1676
|
onEditCommit:
|
|
1386
1677
|
editing?.onCommit as TMDataGridEditEngineContext["onEditCommit"],
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
editing?.onCommitDrafts) as TMDataGridEditEngineContext["onSaveDrafts"],
|
|
1678
|
+
onSaveDrafts:
|
|
1679
|
+
editing?.onSaveDrafts as TMDataGridEditEngineContext["onSaveDrafts"],
|
|
1390
1680
|
newRowDefaults:
|
|
1391
1681
|
editing?.newRowDefaults as TMDataGridEditEngineContext["newRowDefaults"],
|
|
1392
1682
|
onRowAdd: editing?.onRowAdd as TMDataGridEditEngineContext["onRowAdd"],
|
|
@@ -1407,6 +1697,29 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1407
1697
|
edit.cancelAll();
|
|
1408
1698
|
}, [editMode, editDraft, edit]);
|
|
1409
1699
|
|
|
1700
|
+
// A refetch that drops a record takes the engine's state for it along -
|
|
1701
|
+
// see forgetMissingRows. Not where the grid does not own the result set:
|
|
1702
|
+
// there a row missing from `data` is on another page or filtered away
|
|
1703
|
+
// server-side, not gone, and its draft has to wait for Save. Keyed on the
|
|
1704
|
+
// rows as shown, which is what the core row model is built from.
|
|
1705
|
+
// Through the ref, not `table`: `useTable` hands out a fresh copy of the
|
|
1706
|
+
// table every render, and listing it would run this on every one.
|
|
1707
|
+
const editingOn = editing !== undefined;
|
|
1708
|
+
const serverSideRows =
|
|
1709
|
+
options.manualPagination === true || options.manualFiltering === true;
|
|
1710
|
+
useEffect(() => {
|
|
1711
|
+
if (!editingOn || serverSideRows) return;
|
|
1712
|
+
engine.forgetMissingRows(tableRef.current.getCoreRowModel().rowsById);
|
|
1713
|
+
}, [shown.rows, editingOn, serverSideRows, engine]);
|
|
1714
|
+
|
|
1715
|
+
// Readers that hold the table and nothing else - `exportGrid` - still
|
|
1716
|
+
// leave a deletion-marked row out. Keyed on
|
|
1717
|
+
// the store, which every copy of the table shares, so once is enough.
|
|
1718
|
+
useEffect(
|
|
1719
|
+
() => registerDeletedRows(tableRef.current, engine.isRowDeleted),
|
|
1720
|
+
[engine],
|
|
1721
|
+
);
|
|
1722
|
+
|
|
1410
1723
|
// Editing without stable ids points every draft at whatever record slides
|
|
1411
1724
|
// into that index after a sort. Loud, once, in development.
|
|
1412
1725
|
useEffect(() => {
|
|
@@ -1433,42 +1746,16 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1433
1746
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1434
1747
|
}, []);
|
|
1435
1748
|
|
|
1436
|
-
//
|
|
1437
|
-
//
|
|
1438
|
-
// One: the tree column appears with the first grouped column and goes away
|
|
1439
|
-
// with the last, so an ungrouped grid looks exactly as it did before grouping
|
|
1749
|
+
// The tree column appears with the first grouped column and goes away with
|
|
1750
|
+
// the last, so an ungrouped grid looks exactly as it did before grouping
|
|
1440
1751
|
// existed. Driven from a subscription rather than by rebuilding the column
|
|
1441
1752
|
// array, because the array is what the table is built from - deriving it from
|
|
1442
1753
|
// table state would close the loop. Visibility is the one column property
|
|
1443
1754
|
// that can be changed after the fact without touching the definitions.
|
|
1444
1755
|
//
|
|
1445
|
-
// Two, and this one is a workaround. In table-core 9.0.0-beta.21 the
|
|
1446
|
-
// per-region column APIs do not list `grouping` among their memo
|
|
1447
|
-
// dependencies, even though they all derive from `getAllLeafColumns()`, which
|
|
1448
|
-
// does:
|
|
1449
|
-
//
|
|
1450
|
-
// | API | Declares |
|
|
1451
|
-
// | --- | --- |
|
|
1452
|
-
// | `getLeft/Center/RightVisibleLeafColumns` | columns, columnPinning, columnVisibility, columnOrder |
|
|
1453
|
-
// | `getLeft/Center/RightHeaderGroups` | columnPinning, columnOrder |
|
|
1454
|
-
// | `row.getLeft/Center/RightVisibleCells` | columnPinning, columnVisibility |
|
|
1455
|
-
//
|
|
1456
|
-
// So grouping a *second* column leaves every one of them returning the
|
|
1457
|
-
// previous list: the column TanStack removed keeps its header and its grid
|
|
1458
|
-
// track, and the row cells no longer line up with them. The first grouping
|
|
1459
|
-
// appears to work only because the visibility write above happens to touch a
|
|
1460
|
-
// dependency they share.
|
|
1461
|
-
//
|
|
1462
|
-
// Re-publishing `columnVisibility` and `columnOrder` - same contents, new
|
|
1463
|
-
// identity - invalidates all three families. `columnOrder` is the only
|
|
1464
|
-
// dependency the header groups declare, and `columnVisibility` the only one
|
|
1465
|
-
// the cells do, so both are needed. Remove this once the deps are fixed
|
|
1466
|
-
// upstream; the test that fails without it groups two columns and asserts the
|
|
1467
|
-
// second one leaves the grid.
|
|
1468
|
-
//
|
|
1469
1756
|
// Writing back into the store from its own subscriber is safe: the guard is
|
|
1470
|
-
// on `grouping`'s identity, and
|
|
1471
|
-
//
|
|
1757
|
+
// on `grouping`'s identity, and the write does not touch it, so the callback
|
|
1758
|
+
// it triggers short-circuits.
|
|
1472
1759
|
useEffect(() => {
|
|
1473
1760
|
if (!groupColumnEnabled) return;
|
|
1474
1761
|
let previousGrouping = table.store.state.grouping;
|
|
@@ -1502,8 +1789,8 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1502
1789
|
// Keeps the entry injected into a controlled `columnVisibility` (see
|
|
1503
1790
|
// requestedState) in sync with grouping.
|
|
1504
1791
|
groupingActiveRef.current = state.grouping.length > 0;
|
|
1505
|
-
// On a controlled slice the
|
|
1506
|
-
// consumer's handler; the next render must forward
|
|
1792
|
+
// On a controlled slice the write below round-trips through the
|
|
1793
|
+
// consumer's handler; the next render must forward it unstabilized.
|
|
1507
1794
|
// See republishControlledStateRef.
|
|
1508
1795
|
republishControlledStateRef.current = true;
|
|
1509
1796
|
|
|
@@ -1511,7 +1798,6 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1511
1798
|
...old,
|
|
1512
1799
|
[GROUP_COLUMN_ID]: state.grouping.length > 0,
|
|
1513
1800
|
}));
|
|
1514
|
-
table.setColumnOrder((old) => [...old]);
|
|
1515
1801
|
});
|
|
1516
1802
|
|
|
1517
1803
|
return () => subscription.unsubscribe();
|
|
@@ -1556,9 +1842,11 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1556
1842
|
|
|
1557
1843
|
const ui = useCreateStore<TMDataGridUiState, TMDataGridUiActions>(
|
|
1558
1844
|
{
|
|
1559
|
-
|
|
1560
|
-
|
|
1845
|
+
// `useCreateStore` builds the store once per mount, so `defaultOpen` is
|
|
1846
|
+
// read the way `initialState` is - a starting point, not a controller.
|
|
1847
|
+
filterPanelOpen: filters.defaultOpen,
|
|
1561
1848
|
filterPanelColumnId: null,
|
|
1849
|
+
headerFilterColumnId: null,
|
|
1562
1850
|
draggedColumnId: null,
|
|
1563
1851
|
// `useCreateStore` builds the store once per mount, so this is a genuine
|
|
1564
1852
|
// default rather than a value that would fight later clicks.
|
|
@@ -1566,6 +1854,7 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1566
1854
|
selectionAnchorRowId: null,
|
|
1567
1855
|
focusedCell: null,
|
|
1568
1856
|
cellRange: null,
|
|
1857
|
+
exportPicker: null,
|
|
1569
1858
|
},
|
|
1570
1859
|
({ setState }) => ({
|
|
1571
1860
|
openFilterPanel: (columnId = null) =>
|
|
@@ -1580,10 +1869,14 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1580
1869
|
filterPanelOpen: false,
|
|
1581
1870
|
filterPanelColumnId: null,
|
|
1582
1871
|
})),
|
|
1583
|
-
|
|
1584
|
-
setState((prev) => ({ ...prev,
|
|
1585
|
-
|
|
1586
|
-
setState((prev) => ({ ...prev,
|
|
1872
|
+
openExportPicker: (request) =>
|
|
1873
|
+
setState((prev) => ({ ...prev, exportPicker: request })),
|
|
1874
|
+
closeExportPicker: () =>
|
|
1875
|
+
setState((prev) => ({ ...prev, exportPicker: null })),
|
|
1876
|
+
focusPanelFilter: (columnId) =>
|
|
1877
|
+
setState((prev) => ({ ...prev, filterPanelColumnId: columnId })),
|
|
1878
|
+
focusHeaderFilter: (columnId) =>
|
|
1879
|
+
setState((prev) => ({ ...prev, headerFilterColumnId: columnId })),
|
|
1587
1880
|
startColumnDrag: (columnId) =>
|
|
1588
1881
|
setState((prev) => ({ ...prev, draggedColumnId: columnId })),
|
|
1589
1882
|
endColumnDrag: () =>
|
|
@@ -1672,12 +1965,12 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1672
1965
|
table.setColumnSizing({ ...initial?.columnSizing });
|
|
1673
1966
|
table.setColumnOrder([...(initial?.columnOrder ?? [])]);
|
|
1674
1967
|
table.setColumnPinning({
|
|
1675
|
-
|
|
1968
|
+
start: [
|
|
1676
1969
|
...(rowNumbersEnabled && pinningEnabled ? [ROW_NUMBER_COLUMN_ID] : []),
|
|
1677
1970
|
...(selectColumnEnabled && pinningEnabled ? [SELECT_COLUMN_ID] : []),
|
|
1678
1971
|
...(groupColumnEnabled && pinningEnabled ? [GROUP_COLUMN_ID] : []),
|
|
1679
1972
|
...(detailsColumnEnabled && pinningEnabled ? [DETAILS_COLUMN_ID] : []),
|
|
1680
|
-
...(initial?.columnPinning?.
|
|
1973
|
+
...(initial?.columnPinning?.start ?? []).filter(
|
|
1681
1974
|
(id) =>
|
|
1682
1975
|
id !== ROW_NUMBER_COLUMN_ID &&
|
|
1683
1976
|
id !== SELECT_COLUMN_ID &&
|
|
@@ -1685,8 +1978,8 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1685
1978
|
id !== GROUP_COLUMN_ID,
|
|
1686
1979
|
),
|
|
1687
1980
|
],
|
|
1688
|
-
|
|
1689
|
-
...(initial?.columnPinning?.
|
|
1981
|
+
end: [
|
|
1982
|
+
...(initial?.columnPinning?.end ?? []).filter(
|
|
1690
1983
|
(id) => id !== EDIT_COLUMN_ID,
|
|
1691
1984
|
),
|
|
1692
1985
|
...(editColumnEnabled && pinningEnabled ? [EDIT_COLUMN_ID] : []),
|
|
@@ -1709,6 +2002,8 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1709
2002
|
ui,
|
|
1710
2003
|
edit,
|
|
1711
2004
|
features,
|
|
2005
|
+
filters,
|
|
2006
|
+
exportOptions,
|
|
1712
2007
|
labels,
|
|
1713
2008
|
renderDetails,
|
|
1714
2009
|
renderDetailsEstHeight,
|
|
@@ -1720,20 +2015,44 @@ export function useTMDataGrid<TData extends RowData>({
|
|
|
1720
2015
|
}
|
|
1721
2016
|
|
|
1722
2017
|
/**
|
|
1723
|
-
*
|
|
1724
|
-
*
|
|
2018
|
+
* Gives a column an empty filter of its default operator, unless it already
|
|
2019
|
+
* has one - which is what makes a surface open on a row rather than on
|
|
2020
|
+
* nothing.
|
|
2021
|
+
*
|
|
2022
|
+
* @internal Shared by `openColumnFilter` and the toolbar's filter button.
|
|
1725
2023
|
*/
|
|
1726
|
-
export function
|
|
2024
|
+
export function seedColumnFilter<TData extends RowData>(
|
|
1727
2025
|
api: TMDataGridApi<TData>,
|
|
1728
2026
|
columnId: string,
|
|
1729
2027
|
): void {
|
|
1730
2028
|
const column = api.table.getColumn(columnId);
|
|
1731
|
-
if (column
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
2029
|
+
if (column === undefined || column.getFilterValue() !== undefined) return;
|
|
2030
|
+
const operator = getColumnDefaultOperator(column);
|
|
2031
|
+
column.setFilterValue({ operator, value: emptyValueForOperator(operator) });
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
/**
|
|
2035
|
+
* Sends the user to a column's filter control, seeding an empty filter when
|
|
2036
|
+
* the column has none yet - what "Filter" in the column menu and a click on a
|
|
2037
|
+
* filter pill both do.
|
|
2038
|
+
*
|
|
2039
|
+
* Which control that is follows the grid's `filters` option. Under
|
|
2040
|
+
* `inHeader` it is the column's header control, which is already on screen, so
|
|
2041
|
+
* the call focuses it and leaves the popup or sidebar closed. Otherwise it is
|
|
2042
|
+
* the panel's row for that column, and the call opens the surface on it.
|
|
2043
|
+
*/
|
|
2044
|
+
export function openColumnFilter<TData extends RowData>(
|
|
2045
|
+
api: TMDataGridApi<TData>,
|
|
2046
|
+
columnId: string,
|
|
2047
|
+
): void {
|
|
2048
|
+
seedColumnFilter(api, columnId);
|
|
2049
|
+
if (api.filters.inHeader) {
|
|
2050
|
+
// The header control is always visible, so there is nothing to open -
|
|
2051
|
+
// only a column to point at. The header row watching this focuses it and
|
|
2052
|
+
// scrolls it into view. The panel, if one is also showing, is left alone:
|
|
2053
|
+
// it reads the other slot.
|
|
2054
|
+
api.ui.actions.focusHeaderFilter(columnId);
|
|
2055
|
+
return;
|
|
1737
2056
|
}
|
|
1738
2057
|
api.ui.actions.openFilterPanel(columnId);
|
|
1739
2058
|
}
|