@olenbetong/appframe-ds 1.0.3 → 1.2.0
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/CHANGELOG.md +13 -0
- package/package.json +8 -7
- package/scripts/copyAssets.mjs +27 -55
- package/src/AfLookup/Lookup.css +129 -125
- package/src/AfLookup/Lookup.tsx +184 -162
- package/src/AfLookup/LookupCombobox.css +78 -78
- package/src/AfLookup/LookupCombobox.tsx +237 -237
- package/src/AfLookup/LookupEdit.tsx +130 -130
- package/src/AfLookup/LookupGrid.tsx +124 -124
- package/src/AfLookup/index.ts +5 -5
- package/src/autocomplete/Autocomplete.css +256 -256
- package/src/autocomplete/Autocomplete.tsx +320 -320
- package/src/autocomplete/AutocompleteFrame.tsx +168 -168
- package/src/autocomplete/Combobox.tsx +286 -286
- package/src/autocomplete/index.ts +4 -4
- package/src/autocomplete/types.ts +204 -204
- package/src/autocomplete/utils.ts +183 -183
- package/src/binding/BoundNumericTextField.tsx +102 -102
- package/src/binding/BoundTextField.tsx +49 -49
- package/src/binding/CancelButton.tsx +30 -30
- package/src/binding/CancelIconButton.tsx +30 -30
- package/src/binding/DataEditToolbar.tsx +92 -92
- package/src/binding/DeleteButton.tsx +35 -35
- package/src/binding/DeleteIconButton.tsx +35 -35
- package/src/binding/RefreshButton.tsx +31 -31
- package/src/binding/RefreshIconButton.tsx +33 -33
- package/src/binding/RefreshRowIconButton.tsx +33 -33
- package/src/binding/SaveButton.tsx +37 -37
- package/src/binding/SaveIconButton.tsx +38 -38
- package/src/container/Container.tsx +49 -49
- package/src/container/index.ts +1 -1
- package/src/cutoff/CutOffPicker.css +12 -0
- package/src/cutoff/CutOffPicker.tsx +165 -0
- package/src/cutoff/context.tsx +194 -0
- package/src/cutoff/dsCutOffDates.ts +21 -0
- package/src/cutoff/index.ts +4 -0
- package/src/cutoff/utils.test.ts +59 -0
- package/src/cutoff/utils.ts +276 -0
- package/src/filter/ChipListInput.tsx +79 -79
- package/src/filter/FieldFilterPanel.css +150 -150
- package/src/filter/FieldFilterPanel.tsx +309 -309
- package/src/filter/FilterBuilder.css +152 -152
- package/src/filter/FilterBuilder.test.tsx +153 -153
- package/src/filter/FilterBuilder.tsx +435 -435
- package/src/filter/FilterEditor.css +231 -231
- package/src/filter/FilterEditor.test.tsx +259 -259
- package/src/filter/FilterEditor.tsx +75 -74
- package/src/filter/FilterGroupEditor.tsx +160 -160
- package/src/filter/FilterNameDialog.css +22 -22
- package/src/filter/FilterNameDialog.tsx +81 -81
- package/src/filter/FilterRow.tsx +117 -117
- package/src/filter/FilterShareDialog.css +28 -28
- package/src/filter/FilterShareDialog.tsx +201 -201
- package/src/filter/FilterStringField.tsx +78 -77
- package/src/filter/FilterValueEditor.tsx +220 -220
- package/src/filter/SavedFilterTree.css +107 -107
- package/src/filter/SavedFilterTree.test.tsx +94 -94
- package/src/filter/SavedFilterTree.tsx +218 -222
- package/src/filter/fieldFilter.test.ts +158 -158
- package/src/filter/fieldFilter.ts +139 -139
- package/src/filter/index.ts +27 -27
- package/src/filter/types.ts +38 -38
- package/src/filter/useDistinctValues.test.ts +102 -102
- package/src/filter/useDistinctValues.ts +230 -230
- package/src/global.d.ts +11 -11
- package/src/grid/AfGridColumnsPanel.tsx +118 -118
- package/src/grid/AfGridContext.tsx +51 -51
- package/src/grid/AfGridError.tsx +46 -46
- package/src/grid/AfHeaderFilterCell.tsx +147 -151
- package/src/grid/AfHeaderFilterPanel.tsx +111 -111
- package/src/grid/GridEditLookup.css +12 -0
- package/src/grid/GridEditLookup.tsx +121 -0
- package/src/grid/Toolbar.tsx +233 -233
- package/src/grid/editing.ts +100 -100
- package/src/grid/filter.ts +78 -78
- package/src/grid/formatters.ts +48 -48
- package/src/grid/index.css +245 -245
- package/src/grid/index.tsx +489 -484
- package/src/grid/license.ts +48 -48
- package/src/grid/localization.ts +369 -369
- package/src/grid/slots/index.tsx +307 -307
- package/src/grid/slots/slots.css +64 -64
- package/src/grid/theme.tsx +138 -138
- package/src/grid/useAfColumns.tsx +371 -371
- package/src/grid/useAfCurrentIndex.ts +63 -63
- package/src/grid/useAfData.ts +20 -20
- package/src/grid/useAfFilter.ts +160 -160
- package/src/grid/useAfFilterFields.ts +55 -55
- package/src/grid/useAfGridApi.ts +71 -71
- package/src/grid/useAfKeyBindings.ts +36 -36
- package/src/grid/useAfNewItemRow.ts +258 -258
- package/src/grid/useAfPagination.ts +53 -53
- package/src/grid/useAfPersistedState.ts +230 -230
- package/src/grid/useAfRowEditModel.ts +145 -145
- package/src/grid/useAfRowGrouping.ts +47 -47
- package/src/grid/useAfServerAggregation.ts +145 -145
- package/src/grid/useAfSortModel.ts +84 -84
- package/src/i18n.ts +13 -0
- package/src/index.ts +14 -12
- package/src/input/InputAdornments.css +75 -75
- package/src/input/InputAdornments.tsx +102 -102
- package/src/input/index.ts +1 -1
- package/src/layout/AppMenu.tsx +74 -75
- package/src/layout/AppMenuDrawer.css +70 -70
- package/src/layout/AppMenuDrawer.tsx +122 -122
- package/src/layout/ErrorBoundary.tsx +162 -162
- package/src/layout/ResponsiveDialog.css +86 -0
- package/src/layout/ResponsiveDialog.tsx +55 -0
- package/src/layout/index.tsx +69 -68
- package/src/link/Link.tsx +23 -0
- package/src/link/index.ts +1 -0
- package/src/mdi/Mdi.css +138 -138
- package/src/mdi/Mdi.test.tsx +91 -87
- package/src/mdi/Mdi.tsx +141 -141
- package/src/mdi/MdiConfirmCloseDialog.tsx +47 -47
- package/src/mdi/MdiContext.tsx +59 -59
- package/src/mdi/MdiDocumentContext.tsx +60 -60
- package/src/mdi/MdiPanel.tsx +61 -61
- package/src/mdi/MdiRestore.test.tsx +86 -82
- package/src/mdi/MdiTab.tsx +30 -30
- package/src/mdi/controller.test.ts +382 -382
- package/src/mdi/controller.ts +656 -656
- package/src/mdi/dockviewHost.ts +132 -132
- package/src/mdi/fakeHost.ts +200 -200
- package/src/mdi/host.ts +44 -44
- package/src/mdi/ids.test.ts +20 -20
- package/src/mdi/ids.ts +18 -18
- package/src/mdi/index.ts +31 -31
- package/src/mdi/layoutRestore.test.ts +156 -156
- package/src/mdi/layoutRestore.ts +311 -311
- package/src/mdi/layoutSpec.test.ts +143 -143
- package/src/mdi/layoutSpec.ts +214 -214
- package/src/mdi/layoutStore.test.ts +93 -93
- package/src/mdi/layoutStore.ts +99 -99
- package/src/mdi/memorySettingsStore.ts +21 -21
- package/src/mdi/registry.ts +34 -34
- package/src/mdi/testTypes.ts +37 -37
- package/src/mdi/theme.ts +22 -22
- package/src/mdi/types.ts +214 -214
- package/src/page/Page.css +53 -53
- package/src/page/PageBlock.css +64 -64
- package/src/page/PageBlock.tsx +92 -92
- package/src/page/PageHeader.css +88 -88
- package/src/page/PageHeader.tsx +115 -116
- package/src/page/PageTitle.css +5 -0
- package/src/page/PageTitle.tsx +21 -0
- package/src/page/index.ts +4 -3
- package/src/paper/Paper.tsx +52 -52
- package/src/progress/LinearProgress.css +93 -93
- package/src/progress/LinearProgress.tsx +104 -104
- package/src/progress/index.ts +1 -1
- package/src/report-downloader/ReportDownloader.css +67 -67
- package/src/report-downloader/components/StatusItem.tsx +87 -86
- package/src/report-downloader/components/StatusList.tsx +28 -28
- package/src/report-downloader/index.ts +41 -41
- package/src/report-downloader/status.tsx +92 -92
- package/src/sortable/DropIndicator.css +51 -51
- package/src/sortable/DropIndicator.tsx +27 -27
- package/src/sortable/SortableItemList.css +21 -21
- package/src/sortable/SortableItemList.tsx +264 -264
- package/src/sortable/index.ts +8 -8
- package/src/sortable/reorderSortOrder.ts +126 -126
- package/src/sortable/sortOrder.ts +101 -101
- package/src/sortable/types.ts +9 -9
- package/src/sortable/useSortableContainer.ts +53 -53
- package/src/sortable/useSortableItem.ts +95 -95
- package/src/sortable/useSortableList.ts +117 -117
- package/src/test/happy-dom-document-class.ts +24 -24
- package/src/test/setup.ts +12 -12
- package/src/theme/useDsColorScheme.ts +53 -53
- package/tsconfig.build.json +9 -9
- package/tsconfig.json +8 -8
- package/vitest.config.ts +10 -10
package/src/grid/index.tsx
CHANGED
|
@@ -1,484 +1,489 @@
|
|
|
1
|
-
import "./index.css";
|
|
2
|
-
|
|
3
|
-
import { Alert } from "@digdir/designsystemet-react";
|
|
4
|
-
import {
|
|
5
|
-
DataGridPremium as DataGrid,
|
|
6
|
-
type DataGridPremiumProps as DataGridProps,
|
|
7
|
-
GRID_AGGREGATION_FUNCTIONS,
|
|
8
|
-
GRID_ROOT_GROUP_ID,
|
|
9
|
-
type GridAggregationFunction,
|
|
10
|
-
type GridApi,
|
|
11
|
-
type GridFilterModel,
|
|
12
|
-
useGridApiRef,
|
|
13
|
-
} from "@mui/x-data-grid-premium";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
} from "./
|
|
43
|
-
import type
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
52
|
-
import {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
} from "./
|
|
59
|
-
import {
|
|
60
|
-
import {
|
|
61
|
-
import {
|
|
62
|
-
import {
|
|
63
|
-
|
|
64
|
-
export * from "./formatters.js";
|
|
65
|
-
export *
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
*
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*/
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
*
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
*
|
|
160
|
-
*/
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
*
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
*
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
*
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*/
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
* the
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
| "
|
|
230
|
-
| "
|
|
231
|
-
| "
|
|
232
|
-
| "
|
|
233
|
-
| "
|
|
234
|
-
| "
|
|
235
|
-
| "
|
|
236
|
-
| "
|
|
237
|
-
| "
|
|
238
|
-
| "
|
|
239
|
-
| "
|
|
240
|
-
| "
|
|
241
|
-
| "
|
|
242
|
-
| "
|
|
243
|
-
| "
|
|
244
|
-
| "
|
|
245
|
-
| "
|
|
246
|
-
| "
|
|
247
|
-
| "
|
|
248
|
-
| "
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
let
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
{
|
|
412
|
-
{
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
{
|
|
425
|
-
{...
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
1
|
+
import "./index.css";
|
|
2
|
+
|
|
3
|
+
import { Alert } from "@digdir/designsystemet-react";
|
|
4
|
+
import {
|
|
5
|
+
DataGridPremium as DataGrid,
|
|
6
|
+
type DataGridPremiumProps as DataGridProps,
|
|
7
|
+
GRID_AGGREGATION_FUNCTIONS,
|
|
8
|
+
GRID_ROOT_GROUP_ID,
|
|
9
|
+
type GridAggregationFunction,
|
|
10
|
+
type GridApi,
|
|
11
|
+
type GridFilterModel,
|
|
12
|
+
useGridApiRef,
|
|
13
|
+
} from "@mui/x-data-grid-premium";
|
|
14
|
+
import { type DataObject, uid } from "@olenbetong/appframe-data";
|
|
15
|
+
import { DataObjectProvider, useLoading } from "@olenbetong/appframe-react";
|
|
16
|
+
|
|
17
|
+
import clsx from "clsx";
|
|
18
|
+
import { useEffect, useMemo, useState } from "react";
|
|
19
|
+
import { FilterBuilder } from "../filter/index.js";
|
|
20
|
+
import { AfGridColumnsPanel } from "./AfGridColumnsPanel.js";
|
|
21
|
+
import { type AfGridContextValue, AfGridContextProvider } from "./AfGridContext.js";
|
|
22
|
+
import { AfGridError } from "./AfGridError.js";
|
|
23
|
+
import { registerLicense } from "./license.js";
|
|
24
|
+
import { AF_GRID_LOCALE_TEXT } from "./localization.js";
|
|
25
|
+
import { dsBaseSlots } from "./slots/index.js";
|
|
26
|
+
import { AfGridThemeProvider } from "./theme.js";
|
|
27
|
+
import {
|
|
28
|
+
type AfExcelExportOptions,
|
|
29
|
+
AfGridExcelExportMenuItem,
|
|
30
|
+
type AfGridExcelExportOptions,
|
|
31
|
+
AfGridToolbar,
|
|
32
|
+
AfGridToolbarExcelExportButton,
|
|
33
|
+
GridToolbarAddButton,
|
|
34
|
+
GridToolbarAddIfAllowedButton,
|
|
35
|
+
GridToolbarColumnsButton,
|
|
36
|
+
GridToolbarContainer,
|
|
37
|
+
GridToolbarFilterButton,
|
|
38
|
+
GridToolbarMoreOptionsButton,
|
|
39
|
+
GridToolbarRefreshButton,
|
|
40
|
+
GridToolbarResetLayoutButton,
|
|
41
|
+
} from "./Toolbar.js";
|
|
42
|
+
import type { AfGridColumn } from "./useAfColumns.js";
|
|
43
|
+
import { type AfGridColDef, useAfColumns } from "./useAfColumns.js";
|
|
44
|
+
import { useAfCurrentIndex } from "./useAfCurrentIndex.js";
|
|
45
|
+
import { useAfData } from "./useAfData.js";
|
|
46
|
+
import { useAfFilterFields } from "./useAfFilterFields.js";
|
|
47
|
+
import { useAfFilter } from "./useAfFilter.js";
|
|
48
|
+
import { type AfGridApi, useAfGridApi } from "./useAfGridApi.js";
|
|
49
|
+
import { useAfKeyBindings } from "./useAfKeyBindings.js";
|
|
50
|
+
import { type AfGridNewItemRowPosition, NEW_ITEM_ROW_ID, isNewItemRow, useAfNewItemRow } from "./useAfNewItemRow.js";
|
|
51
|
+
import { useAfPagination } from "./useAfPagination.js";
|
|
52
|
+
import {
|
|
53
|
+
isPersistedStateLoaded,
|
|
54
|
+
type StatePersistenceConfig,
|
|
55
|
+
settingsLoaded,
|
|
56
|
+
useAfPersistedState,
|
|
57
|
+
} from "./useAfPersistedState.js";
|
|
58
|
+
import { useAfRowEditModel } from "./useAfRowEditModel.js";
|
|
59
|
+
import { useAfRowGrouping } from "./useAfRowGrouping.js";
|
|
60
|
+
import { useAfServerAggregation } from "./useAfServerAggregation.js";
|
|
61
|
+
import { useAfSortModel } from "./useAfSortModel.js";
|
|
62
|
+
import { getLocalizedString } from "../i18n.js";
|
|
63
|
+
|
|
64
|
+
export * from "./formatters.js";
|
|
65
|
+
export * from "./GridEditLookup.js";
|
|
66
|
+
// MUI X as a namespace (`DataGrid.DataGridPremium`, `DataGrid.GridColDef`, `DataGrid.useGridApiRef`,
|
|
67
|
+
// ...), for grids that are not bound to a data object - from here rather than the app, so a
|
|
68
|
+
// platform app does not bundle a second MUI X. Same shape as appframe-mui exported it.
|
|
69
|
+
export * as DataGrid from "@mui/x-data-grid-premium";
|
|
70
|
+
export * as MuiDataGrid from "@mui/x-data-grid-premium";
|
|
71
|
+
|
|
72
|
+
registerLicense();
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The grid's criteria live in a `FilterTree` owned by `useAfFilter`, not in
|
|
76
|
+
* MUI's filter model, so the model is always empty. It is hoisted so the
|
|
77
|
+
* reference is stable and the grid does not treat it as a change.
|
|
78
|
+
*/
|
|
79
|
+
const EMPTY_FILTER_MODEL: GridFilterModel = { items: [] };
|
|
80
|
+
|
|
81
|
+
const privSysColumns: AfGridColDef[] = [
|
|
82
|
+
{ field: "PrimKey", editable: false, hide: true },
|
|
83
|
+
{ field: "Created", editable: false, hide: true },
|
|
84
|
+
{ field: "CreatedBy", editable: false, hide: true },
|
|
85
|
+
{ field: "Updated", editable: false, hide: true },
|
|
86
|
+
{ field: "UpdatedBy", editable: false, hide: true },
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
export const systemColumns = {
|
|
90
|
+
[Symbol.iterator]: () => privSysColumns[Symbol.iterator](),
|
|
91
|
+
PrimKey: privSysColumns[0],
|
|
92
|
+
Created: privSysColumns[1],
|
|
93
|
+
CreatedBy: privSysColumns[2],
|
|
94
|
+
Updated: privSysColumns[3],
|
|
95
|
+
UpdatedBy: privSysColumns[4],
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export type {
|
|
99
|
+
AfExcelExportOptions,
|
|
100
|
+
AfGridApi,
|
|
101
|
+
AfGridColDef,
|
|
102
|
+
AfGridColumn,
|
|
103
|
+
AfGridExcelExportOptions,
|
|
104
|
+
AfGridNewItemRowPosition,
|
|
105
|
+
};
|
|
106
|
+
export type { AfGridGrouping, AfGridGroupingProps } from "./useAfRowGrouping.js";
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
AF_GRID_LOCALE_TEXT,
|
|
110
|
+
NEW_ITEM_ROW_ID,
|
|
111
|
+
isNewItemRow,
|
|
112
|
+
useAfNewItemRow,
|
|
113
|
+
AfGridColumnsPanel,
|
|
114
|
+
AfGridExcelExportMenuItem,
|
|
115
|
+
AfGridThemeProvider,
|
|
116
|
+
AfGridToolbar,
|
|
117
|
+
AfGridToolbarExcelExportButton,
|
|
118
|
+
GridToolbarAddButton,
|
|
119
|
+
GridToolbarAddIfAllowedButton,
|
|
120
|
+
GridToolbarColumnsButton,
|
|
121
|
+
GridToolbarContainer,
|
|
122
|
+
GridToolbarFilterButton,
|
|
123
|
+
GridToolbarMoreOptionsButton,
|
|
124
|
+
GridToolbarRefreshButton,
|
|
125
|
+
GridToolbarResetLayoutButton,
|
|
126
|
+
useAfColumns,
|
|
127
|
+
useAfCurrentIndex,
|
|
128
|
+
useAfData,
|
|
129
|
+
useAfFilter,
|
|
130
|
+
useAfFilterFields,
|
|
131
|
+
useAfPagination,
|
|
132
|
+
useAfPersistedState,
|
|
133
|
+
useAfRowEditModel,
|
|
134
|
+
useAfRowGrouping,
|
|
135
|
+
useAfServerAggregation,
|
|
136
|
+
useAfSortModel,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export type AfGridProps = {
|
|
140
|
+
apiRef?: React.RefObject<GridApi>;
|
|
141
|
+
/**
|
|
142
|
+
* If set to true, the grid will automatically load data if the data object has not
|
|
143
|
+
* been loaded previously.
|
|
144
|
+
*/
|
|
145
|
+
autoLoad?: boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Bind the focused/selected row to the current index in the data object.
|
|
148
|
+
* Useful if you want child data to load as the user navigates the grid.
|
|
149
|
+
*/
|
|
150
|
+
bindIndex?: boolean;
|
|
151
|
+
columns: AfGridColumn[];
|
|
152
|
+
dataObject: DataObject<any>;
|
|
153
|
+
/**
|
|
154
|
+
* The layout version of the grid. Persisted grid state will only be restored
|
|
155
|
+
* if the version matches.
|
|
156
|
+
*/
|
|
157
|
+
layoutVersion?: string | number;
|
|
158
|
+
/**
|
|
159
|
+
* Field that contains a unique identifier for each row. Defaults to PrimKey.
|
|
160
|
+
*/
|
|
161
|
+
idField?: string;
|
|
162
|
+
/**
|
|
163
|
+
* A function that can be used to transform the data object data before it is
|
|
164
|
+
* passed to the grid, for example to create summary rows.
|
|
165
|
+
*/
|
|
166
|
+
getData?: (data: Readonly<Readonly<any>[]>) => any[];
|
|
167
|
+
/**
|
|
168
|
+
* Override the data object permission, and remove the delete button from the default row actions
|
|
169
|
+
*/
|
|
170
|
+
disableDelete?: boolean;
|
|
171
|
+
/**
|
|
172
|
+
* Override the data object permission, and remove the edit button from the default row actions
|
|
173
|
+
*/
|
|
174
|
+
disableEdit?: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Override the data object permission, and remove the Add button from the default toolbar.
|
|
177
|
+
*/
|
|
178
|
+
disableInsert?: boolean;
|
|
179
|
+
/**
|
|
180
|
+
* Use to disable server sorting. Required for example when using row grouping.
|
|
181
|
+
*/
|
|
182
|
+
disableServerSorting?: boolean;
|
|
183
|
+
/**
|
|
184
|
+
* The view the filter builder saves and loads saved filters for, and converts
|
|
185
|
+
* its filter against. Without it the filter builder can still be used, but
|
|
186
|
+
* saved filters are unavailable.
|
|
187
|
+
*/
|
|
188
|
+
filterViewName?: string;
|
|
189
|
+
/**
|
|
190
|
+
* Where to show the new item row: a non-data row pinned above or below the data
|
|
191
|
+
* that reads "Click here to add a new row" until the user starts editing it.
|
|
192
|
+
* Editing it moves the data object to the new record at index -1, and committing
|
|
193
|
+
* it saves the record. Only shown when the data object allows insert and
|
|
194
|
+
* `disableInsert` is not set. `none` turns it off, in which case the Add button
|
|
195
|
+
* appends an unsaved row to the data instead. Defaults to `bottom`.
|
|
196
|
+
*/
|
|
197
|
+
newItemRow?: AfGridNewItemRowPosition;
|
|
198
|
+
/**
|
|
199
|
+
* A unique ID used to persist the grid state. It is better to use the layoutVersion
|
|
200
|
+
* property if you would like to ignore any previously persisted state, as changing
|
|
201
|
+
* this property will leave orphan records for the previous id in the Indexed DB
|
|
202
|
+
* grid layout store.
|
|
203
|
+
*/
|
|
204
|
+
id: string;
|
|
205
|
+
/**
|
|
206
|
+
* Object almost matching the grid state, but with all values as booleans. This
|
|
207
|
+
* is used to configure which properties should be persisted.
|
|
208
|
+
*/
|
|
209
|
+
persistenceModel?: StatePersistenceConfig;
|
|
210
|
+
/**
|
|
211
|
+
* Ask the server for the grand total instead of adding up the rows the grid is holding.
|
|
212
|
+
*
|
|
213
|
+
* MUI aggregates over the loaded rows, which on a dynamically loading data object is the
|
|
214
|
+
* current page — a sum that looks like a total and is not one. With this on, the grid runs a
|
|
215
|
+
* second retrieve under the same filters that returns only the aggregates, so the footer shows
|
|
216
|
+
* the total over the whole result. Subtotals on grouped rows stay client side, since a group
|
|
217
|
+
* only exists over the rows the grid has.
|
|
218
|
+
*
|
|
219
|
+
* `sum`, `avg`, `min` and `max` are resolved server side; `size` and any custom aggregation
|
|
220
|
+
* function stay on the client. If the view cannot be aggregated the grid falls back to the
|
|
221
|
+
* client total rather than failing.
|
|
222
|
+
*
|
|
223
|
+
* Defaults to true when the data object loads dynamically, since that is exactly the case
|
|
224
|
+
* where the client total is wrong.
|
|
225
|
+
*/
|
|
226
|
+
serverAggregation?: boolean;
|
|
227
|
+
} & Omit<
|
|
228
|
+
DataGridProps,
|
|
229
|
+
| "apiRef"
|
|
230
|
+
| "columns"
|
|
231
|
+
| "loading"
|
|
232
|
+
| "rows"
|
|
233
|
+
| "filterMode"
|
|
234
|
+
| "filterModel"
|
|
235
|
+
| "onFilterModelChange"
|
|
236
|
+
| "sortingMode"
|
|
237
|
+
| "sortModel"
|
|
238
|
+
| "onSortModelChange"
|
|
239
|
+
| "rowGroupingModel"
|
|
240
|
+
| "onRowGroupingModelChange"
|
|
241
|
+
| "aggregationModel"
|
|
242
|
+
| "onAggregationModelChange"
|
|
243
|
+
| "editRowsModel"
|
|
244
|
+
| "editMode"
|
|
245
|
+
| "onRowEditCommit"
|
|
246
|
+
| "onEditRowsModelChange"
|
|
247
|
+
| "pagination"
|
|
248
|
+
| "paginationMode"
|
|
249
|
+
| "pageSize"
|
|
250
|
+
| "rowCount"
|
|
251
|
+
| "rowsPerPageOptions"
|
|
252
|
+
| "onPageChange"
|
|
253
|
+
| "onPageSizeChange"
|
|
254
|
+
>;
|
|
255
|
+
|
|
256
|
+
// This wrapper component prevents the actual grid from rendering until the persisted state has been loaded.
|
|
257
|
+
// This is to prevent the grid from flickering when the persisted state is restored, plus the grid sometimes
|
|
258
|
+
// resets the column order if not provided in the initial state
|
|
259
|
+
export function AfGrid(props: AfGridProps) {
|
|
260
|
+
let [loaded, setLoaded] = useState(isPersistedStateLoaded());
|
|
261
|
+
|
|
262
|
+
useEffect(() => {
|
|
263
|
+
settingsLoaded.then(() => setLoaded(true));
|
|
264
|
+
}, []);
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<AfGridThemeProvider>
|
|
268
|
+
{loaded ? <AfGrid_ {...props} /> : <div className="AfGrid-loading">{getLocalizedString("Loading...")}</div>}
|
|
269
|
+
</AfGridThemeProvider>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function AfGrid_(props: AfGridProps) {
|
|
274
|
+
let {
|
|
275
|
+
autoLoad = false,
|
|
276
|
+
apiRef: apiRefProp,
|
|
277
|
+
bindIndex,
|
|
278
|
+
columns: columnsProp,
|
|
279
|
+
disableServerSorting,
|
|
280
|
+
disableInsert,
|
|
281
|
+
disableDelete,
|
|
282
|
+
disableEdit = false,
|
|
283
|
+
dataObject,
|
|
284
|
+
filterViewName,
|
|
285
|
+
layoutVersion = 0,
|
|
286
|
+
getData,
|
|
287
|
+
idField = "PrimKey",
|
|
288
|
+
id,
|
|
289
|
+
// Follows the data object's `newRecordPosition` when it has one (older data
|
|
290
|
+
// packages have none and append), so the row sits where the record will land
|
|
291
|
+
newItemRow: newItemRowPosition = dataObject.options?.newRecordPosition === "start" ? "top" : "bottom",
|
|
292
|
+
persistenceModel,
|
|
293
|
+
initialState,
|
|
294
|
+
pinnedRows,
|
|
295
|
+
getRowClassName,
|
|
296
|
+
serverAggregation = dataObject.options?.dynamicLoading === true,
|
|
297
|
+
...other
|
|
298
|
+
} = props;
|
|
299
|
+
let apiRefPriv = useGridApiRef();
|
|
300
|
+
let apiRef = apiRefProp ?? apiRefPriv;
|
|
301
|
+
useAfGridApi(props, apiRef);
|
|
302
|
+
let newItemRow = useAfNewItemRow({
|
|
303
|
+
apiRef,
|
|
304
|
+
dataObject,
|
|
305
|
+
position: newItemRowPosition,
|
|
306
|
+
disableInsert,
|
|
307
|
+
pinnedRows,
|
|
308
|
+
getRowClassName,
|
|
309
|
+
});
|
|
310
|
+
let initialState_ = useAfPersistedState({
|
|
311
|
+
apiRef,
|
|
312
|
+
columns: columnsProp,
|
|
313
|
+
id,
|
|
314
|
+
layoutVersion,
|
|
315
|
+
persistenceModel,
|
|
316
|
+
initialState,
|
|
317
|
+
});
|
|
318
|
+
// Seeded from the restored layout, so a persisted grouping survives a reload.
|
|
319
|
+
let grouping = useAfRowGrouping(initialState_);
|
|
320
|
+
let loading = useLoading(dataObject);
|
|
321
|
+
let paging = useAfPagination(dataObject);
|
|
322
|
+
let sortModel = useAfSortModel({
|
|
323
|
+
dataObject,
|
|
324
|
+
columns: columnsProp,
|
|
325
|
+
disableServerSorting,
|
|
326
|
+
isGrouping: grouping.isGrouping,
|
|
327
|
+
});
|
|
328
|
+
let gridFilter = useAfFilter({ autoLoad, dataObject, id });
|
|
329
|
+
let [snackbar, setSnackbar] = useState<string | null>(null);
|
|
330
|
+
let [isFilterBuilderOpen, setFilterBuilderOpen] = useState(false);
|
|
331
|
+
let [isColumnsPanelOpen, setColumnsPanelOpen] = useState(false);
|
|
332
|
+
let editProps = useAfRowEditModel({ dataObject, disableEdit, idField, apiRef, newItemRow });
|
|
333
|
+
let data = useAfData(dataObject, { getData });
|
|
334
|
+
useAfCurrentIndex({
|
|
335
|
+
apiRef,
|
|
336
|
+
dataObject,
|
|
337
|
+
idField,
|
|
338
|
+
enabled: !!bindIndex,
|
|
339
|
+
newItemRowId: newItemRow.enabled ? NEW_ITEM_ROW_ID : null,
|
|
340
|
+
});
|
|
341
|
+
let columns = useAfColumns({
|
|
342
|
+
apiRef,
|
|
343
|
+
dataObject,
|
|
344
|
+
disableEdit,
|
|
345
|
+
disableDelete,
|
|
346
|
+
columnsProp,
|
|
347
|
+
idField,
|
|
348
|
+
});
|
|
349
|
+
let filterFields = useAfFilterFields(dataObject, columnsProp);
|
|
350
|
+
|
|
351
|
+
let keyEvents = useAfKeyBindings({ apiRef, dataObject });
|
|
352
|
+
|
|
353
|
+
useEffect(() => {
|
|
354
|
+
if (!snackbar) return;
|
|
355
|
+
|
|
356
|
+
let timeout = setTimeout(() => setSnackbar(null), 6000);
|
|
357
|
+
|
|
358
|
+
return () => clearTimeout(timeout);
|
|
359
|
+
}, [snackbar]);
|
|
360
|
+
|
|
361
|
+
let serverAggregates = useAfServerAggregation(
|
|
362
|
+
dataObject,
|
|
363
|
+
grouping.gridProps.aggregationModel,
|
|
364
|
+
serverAggregation && !grouping.isGrouping,
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
/*
|
|
368
|
+
* The grand total row asks for `groupId === GRID_ROOT_GROUP_ID`; every other call is a group
|
|
369
|
+
* subtotal, which only means anything over the rows the grid actually has. So the server value
|
|
370
|
+
* is substituted for the root and the built-in function is left to do the rest.
|
|
371
|
+
*/
|
|
372
|
+
let aggregationFunctions = useMemo(() => {
|
|
373
|
+
if (!serverAggregation) return undefined;
|
|
374
|
+
|
|
375
|
+
let base = other.aggregationFunctions ?? GRID_AGGREGATION_FUNCTIONS;
|
|
376
|
+
let wrapped: Record<string, GridAggregationFunction> = {};
|
|
377
|
+
|
|
378
|
+
for (let [name, fn] of Object.entries(base) as Array<[string, GridAggregationFunction]>) {
|
|
379
|
+
wrapped[name] = {
|
|
380
|
+
...fn,
|
|
381
|
+
apply: (params, api) => {
|
|
382
|
+
if (params.groupId === GRID_ROOT_GROUP_ID && params.field in serverAggregates.values) {
|
|
383
|
+
return serverAggregates.values[params.field];
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return fn.apply(params, api);
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return wrapped;
|
|
392
|
+
}, [serverAggregation, other.aggregationFunctions, serverAggregates.values]);
|
|
393
|
+
|
|
394
|
+
let gridContext = useMemo<AfGridContextValue>(
|
|
395
|
+
() => ({
|
|
396
|
+
id,
|
|
397
|
+
apiRef,
|
|
398
|
+
filterFields,
|
|
399
|
+
...gridFilter,
|
|
400
|
+
openFilterBuilder: () => setFilterBuilderOpen(true),
|
|
401
|
+
openColumnsPanel: () => setColumnsPanelOpen(true),
|
|
402
|
+
}),
|
|
403
|
+
[id, apiRef, filterFields, gridFilter],
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
return (
|
|
407
|
+
<DataObjectProvider dataObject={dataObject}>
|
|
408
|
+
<AfGridContextProvider value={gridContext}>
|
|
409
|
+
<div className="AfGrid-root" style={newItemRow.rootStyle} {...keyEvents} tabIndex={-1}>
|
|
410
|
+
<DataGrid
|
|
411
|
+
apiRef={apiRef}
|
|
412
|
+
rows={data}
|
|
413
|
+
columns={columns}
|
|
414
|
+
loading={loading}
|
|
415
|
+
getRowId={(row) => row[uid] ?? row[idField]}
|
|
416
|
+
{...editProps}
|
|
417
|
+
{...newItemRow.gridProps}
|
|
418
|
+
/*
|
|
419
|
+
* Our own filter tree drives the data object, so MUI never
|
|
420
|
+
* sees a filter model. The header filter cells are replaced
|
|
421
|
+
* by `renderHeaderFilter` on every column.
|
|
422
|
+
*/
|
|
423
|
+
filterMode="server"
|
|
424
|
+
filterModel={EMPTY_FILTER_MODEL}
|
|
425
|
+
{...sortModel}
|
|
426
|
+
headerFilters
|
|
427
|
+
{...paging}
|
|
428
|
+
showToolbar
|
|
429
|
+
{...other}
|
|
430
|
+
{...grouping.gridProps}
|
|
431
|
+
/*
|
|
432
|
+
* Spread rather than passed, because the grid merges props over its defaults with
|
|
433
|
+
* `Object.assign`: an explicit `undefined` here would wipe out the built-in
|
|
434
|
+
* aggregation functions rather than fall back to them, and the grid then throws on
|
|
435
|
+
* the first render.
|
|
436
|
+
*/
|
|
437
|
+
{...(aggregationFunctions ? { aggregationFunctions } : {})}
|
|
438
|
+
localeText={{ ...other.localeText, ...AF_GRID_LOCALE_TEXT }}
|
|
439
|
+
slots={{
|
|
440
|
+
...dsBaseSlots,
|
|
441
|
+
toolbar: () => <AfGridToolbar disableInsert={disableInsert} />,
|
|
442
|
+
...other.slots,
|
|
443
|
+
}}
|
|
444
|
+
initialState={initialState_}
|
|
445
|
+
slotProps={{
|
|
446
|
+
...other.slotProps,
|
|
447
|
+
headerFilterCell: {
|
|
448
|
+
...other.slotProps?.headerFilterCell,
|
|
449
|
+
headerClassName: clsx(other.slotProps?.headerFilterCell?.headerClassName, "AfGrid-headerCell"),
|
|
450
|
+
showRightBorder: true,
|
|
451
|
+
},
|
|
452
|
+
}}
|
|
453
|
+
onProcessRowUpdateError={(error) => {
|
|
454
|
+
other.onProcessRowUpdateError?.(error);
|
|
455
|
+
let message = "message" in (error as any) ? (error as any).message : String(error);
|
|
456
|
+
setSnackbar(message);
|
|
457
|
+
}}
|
|
458
|
+
/>
|
|
459
|
+
</div>
|
|
460
|
+
<AfGridError apiRef={apiRef} dataObject={dataObject} />
|
|
461
|
+
{isColumnsPanelOpen && <AfGridColumnsPanel open onClose={() => setColumnsPanelOpen(false)} />}
|
|
462
|
+
<FilterBuilder
|
|
463
|
+
variant="dialog"
|
|
464
|
+
open={isFilterBuilderOpen}
|
|
465
|
+
onClose={() => setFilterBuilderOpen(false)}
|
|
466
|
+
fields={filterFields}
|
|
467
|
+
viewName={filterViewName}
|
|
468
|
+
dataObject={dataObject}
|
|
469
|
+
/*
|
|
470
|
+
* The builder edits the same named slot and the same tree as the
|
|
471
|
+
* header filters, so a criterion added in one shows up in the
|
|
472
|
+
* other. Applying writes the slot directly, which skips the
|
|
473
|
+
* header filters' debounce.
|
|
474
|
+
*/
|
|
475
|
+
slotName={id}
|
|
476
|
+
value={gridFilter.filter}
|
|
477
|
+
onChange={gridFilter.setFilter}
|
|
478
|
+
/>
|
|
479
|
+
{!!snackbar && (
|
|
480
|
+
<div className="AfGrid-snackbar">
|
|
481
|
+
<Alert data-color="danger" data-size="sm" onClick={() => setSnackbar(null)}>
|
|
482
|
+
{snackbar}
|
|
483
|
+
</Alert>
|
|
484
|
+
</div>
|
|
485
|
+
)}
|
|
486
|
+
</AfGridContextProvider>
|
|
487
|
+
</DataObjectProvider>
|
|
488
|
+
);
|
|
489
|
+
}
|