@redsift/table-pro 12.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CONTRIBUTING.md +435 -0
  2. package/_internal/BaseComponents.js +3 -0
  3. package/_internal/BaseComponents.js.map +1 -0
  4. package/_internal/BaseIconButton.js +126 -0
  5. package/_internal/BaseIconButton.js.map +1 -0
  6. package/_internal/BaseTextField.js +26 -0
  7. package/_internal/BaseTextField.js.map +1 -0
  8. package/_internal/DataGrid.js +2 -0
  9. package/_internal/DataGrid.js.map +1 -0
  10. package/_internal/DataGrid2.js +507 -0
  11. package/_internal/DataGrid2.js.map +1 -0
  12. package/_internal/GridToolbarFilterSemanticField.js +2 -0
  13. package/_internal/GridToolbarFilterSemanticField.js.map +1 -0
  14. package/_internal/GridToolbarFilterSemanticField2.js +6962 -0
  15. package/_internal/GridToolbarFilterSemanticField2.js.map +1 -0
  16. package/_internal/Pagination.js +2 -0
  17. package/_internal/Pagination.js.map +1 -0
  18. package/_internal/ServerSideControlledPagination.js +324 -0
  19. package/_internal/ServerSideControlledPagination.js.map +1 -0
  20. package/_internal/StatefulDataGrid.js +2 -0
  21. package/_internal/StatefulDataGrid.js.map +1 -0
  22. package/_internal/StatefulDataGrid2.js +3237 -0
  23. package/_internal/StatefulDataGrid2.js.map +1 -0
  24. package/_internal/TextCell.js +2 -0
  25. package/_internal/TextCell.js.map +1 -0
  26. package/_internal/TextCell2.js +66 -0
  27. package/_internal/TextCell2.js.map +1 -0
  28. package/_internal/Toolbar.js +2 -0
  29. package/_internal/Toolbar.js.map +1 -0
  30. package/_internal/Toolbar2.js +102 -0
  31. package/_internal/Toolbar2.js.map +1 -0
  32. package/_internal/ToolbarWrapper.js +2 -0
  33. package/_internal/ToolbarWrapper.js.map +1 -0
  34. package/_internal/ToolbarWrapper2.js +53 -0
  35. package/_internal/ToolbarWrapper2.js.map +1 -0
  36. package/_internal/_rollupPluginBabelHelpers.js +93 -0
  37. package/_internal/_rollupPluginBabelHelpers.js.map +1 -0
  38. package/_internal/useControlledDatagridState.js +170 -0
  39. package/_internal/useControlledDatagridState.js.map +1 -0
  40. package/index.d.ts +1223 -0
  41. package/index.js +721 -0
  42. package/index.js.map +1 -0
  43. package/package.json +96 -0
  44. package/style/index.css +7 -0
@@ -0,0 +1,3237 @@
1
+ import { _ as _objectSpread2, a as _objectWithoutProperties, b as _extends } from './_rollupPluginBabelHelpers.js';
2
+ import * as React from 'react';
3
+ import React__default, { useCallback, useEffect, useRef, useMemo, forwardRef, useState } from 'react';
4
+ import { createTheme, ThemeProvider as ThemeProvider$1 } from '@mui/material/styles';
5
+ import classNames from 'classnames';
6
+ import { LicenseInfo } from '@mui/x-license';
7
+ import { Icon, useTheme, RedsiftColorBlueN, RedsiftColorNeutralXDarkGrey, RedsiftColorNeutralWhite, ThemeProvider } from '@redsift/design-system';
8
+ import { getGridNumericOperators as getGridNumericOperators$1, GridFilterInputValue, GridFilterInputSingleSelect, GridFilterInputMultipleValue, GridFilterInputMultipleSingleSelect, getGridStringOperators as getGridStringOperators$1, getGridBooleanOperators, getGridDateOperators, getGridSingleSelectOperators, GridLogicOperator, useGridApiRef, gridFilteredSortedRowEntriesSelector, gridFilteredSortedRowIdsSelector, DataGridPro } from '@mui/x-data-grid-pro';
9
+ import { u as useControlledDatagridState, S as StyledDataGrid } from './useControlledDatagridState.js';
10
+ import Box from '@mui/material/Box';
11
+ import TextField from '@mui/material/TextField';
12
+ import { mdiSync } from '@redsift/icons';
13
+ import { decompressFromEncodedURIComponent, compressToEncodedURIComponent } from 'lz-string';
14
+ import { n as normalizeRowSelectionModel, o as onServerSideSelectionStatusChange, g as getSelectionCount, i as isRowSelected, S as ServerSideControlledPagination, C as ControlledPagination } from './ServerSideControlledPagination.js';
15
+ import { B as BaseButton, a as BaseCheckbox, c as BaseIconButton, b as BaseIcon } from './BaseIconButton.js';
16
+
17
+ const SUBMIT_FILTER_STROKE_TIME = 500;
18
+ const InputNumberInterval = props => {
19
+ var _item$value;
20
+ const {
21
+ item,
22
+ applyValue,
23
+ focusElementRef = null
24
+ } = props;
25
+ const filterTimeout = React.useRef();
26
+ const [filterValueState, setFilterValueState] = React.useState((_item$value = item.value) !== null && _item$value !== void 0 ? _item$value : '');
27
+ const [applying, setIsApplying] = React.useState(false);
28
+ React.useEffect(() => {
29
+ return () => {
30
+ clearTimeout(filterTimeout.current);
31
+ };
32
+ }, []);
33
+ React.useEffect(() => {
34
+ var _item$value2;
35
+ const itemValue = (_item$value2 = item.value) !== null && _item$value2 !== void 0 ? _item$value2 : [undefined, undefined];
36
+ setFilterValueState(itemValue);
37
+ }, [item.value]);
38
+ const updateFilterValue = (lowerBound, upperBound) => {
39
+ clearTimeout(filterTimeout.current);
40
+ setFilterValueState([lowerBound, upperBound]);
41
+ setIsApplying(true);
42
+ filterTimeout.current = setTimeout(() => {
43
+ setIsApplying(false);
44
+ applyValue(_objectSpread2(_objectSpread2({}, item), {}, {
45
+ value: [lowerBound, upperBound]
46
+ }));
47
+ }, SUBMIT_FILTER_STROKE_TIME);
48
+ };
49
+ const handleUpperFilterChange = event => {
50
+ const newUpperBound = event.target.value;
51
+ updateFilterValue(filterValueState[0], newUpperBound);
52
+ };
53
+ const handleLowerFilterChange = event => {
54
+ const newLowerBound = event.target.value;
55
+ updateFilterValue(newLowerBound, filterValueState[1]);
56
+ };
57
+ return /*#__PURE__*/React.createElement(Box, {
58
+ sx: {
59
+ display: 'inline-flex',
60
+ flexDirection: 'row',
61
+ alignItems: 'end',
62
+ height: 48,
63
+ pl: '20px'
64
+ }
65
+ }, /*#__PURE__*/React.createElement(TextField, {
66
+ name: "lower-bound-input",
67
+ placeholder: "From",
68
+ label: "From",
69
+ variant: "standard",
70
+ value: Number(filterValueState[0]),
71
+ onChange: handleLowerFilterChange,
72
+ type: "number",
73
+ inputRef: focusElementRef,
74
+ sx: {
75
+ mr: 2
76
+ }
77
+ }), /*#__PURE__*/React.createElement(TextField, {
78
+ name: "upper-bound-input",
79
+ placeholder: "To",
80
+ label: "To",
81
+ variant: "standard",
82
+ value: Number(filterValueState[1]),
83
+ onChange: handleUpperFilterChange,
84
+ type: "number",
85
+ InputProps: applying ? {
86
+ endAdornment: /*#__PURE__*/React.createElement(Icon, {
87
+ icon: mdiSync
88
+ })
89
+ } : {}
90
+ }));
91
+ };
92
+
93
+ const isBetweenOperator = {
94
+ label: 'is between',
95
+ value: 'isBetween',
96
+ getApplyFilterFn: filterItem => {
97
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
98
+ return null;
99
+ }
100
+ if (!Array.isArray(filterItem.value) || filterItem.value.length !== 2) {
101
+ return null;
102
+ }
103
+ if (filterItem.value[0] == null || filterItem.value[1] == null) {
104
+ return null;
105
+ }
106
+ if (typeof filterItem.value[0] !== 'number' || typeof filterItem.value[1] !== 'number') {
107
+ return null;
108
+ }
109
+ return value => {
110
+ return value !== null && value !== undefined && filterItem.value[0] <= value && value <= filterItem.value[1];
111
+ };
112
+ },
113
+ InputComponent: InputNumberInterval
114
+ };
115
+ const IS_BETWEEN = isBetweenOperator;
116
+
117
+ const getGridNumericOperators = () => [...getGridNumericOperators$1(), IS_BETWEEN];
118
+
119
+ const doesNotContain = {
120
+ label: 'does not contain',
121
+ value: 'doesNotContain',
122
+ getApplyFilterFn: filterItem => {
123
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
124
+ return null;
125
+ }
126
+ return value => {
127
+ if (filterItem.value.length === 0) {
128
+ return true;
129
+ }
130
+ if (value == null) {
131
+ return true;
132
+ }
133
+ if (String(value).indexOf(filterItem.value) !== -1) {
134
+ return false;
135
+ }
136
+ return true;
137
+ };
138
+ },
139
+ InputComponent: GridFilterInputValue
140
+ };
141
+ const DOES_NOT_CONTAIN = doesNotContain;
142
+
143
+ const doesNotEndWithOperator = {
144
+ label: 'does not end with',
145
+ value: 'doesNotEndWith',
146
+ getApplyFilterFn: filterItem => {
147
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
148
+ return null;
149
+ }
150
+ return value => {
151
+ if (value == null) {
152
+ return true;
153
+ }
154
+ return !String(value).endsWith(filterItem.value);
155
+ };
156
+ },
157
+ InputComponent: GridFilterInputValue
158
+ };
159
+ const DOES_NOT_END_WITH = doesNotEndWithOperator;
160
+
161
+ const doesNotEqual = {
162
+ label: 'does not equal',
163
+ value: 'doesNotEqual',
164
+ getApplyFilterFn: filterItem => {
165
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
166
+ return null;
167
+ }
168
+ return value => {
169
+ if (value == null) {
170
+ return true;
171
+ }
172
+ if (String(value) === filterItem.value) {
173
+ return false;
174
+ }
175
+ return true;
176
+ };
177
+ },
178
+ InputComponent: GridFilterInputValue
179
+ };
180
+ const DOES_NOT_EQUAL = doesNotEqual;
181
+
182
+ const doesNotHaveOperator = {
183
+ label: "doesn't have",
184
+ value: 'doesNotHave',
185
+ getApplyFilterFn: filterItem => {
186
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
187
+ return null;
188
+ }
189
+ return value => {
190
+ if (value == null) {
191
+ return true;
192
+ }
193
+ const cellValues = Array.isArray(value) ? value : [value];
194
+ return !cellValues.map(value => String(value)).includes(filterItem.value);
195
+ };
196
+ },
197
+ InputComponent: GridFilterInputValue
198
+ };
199
+ const DOES_NOT_HAVE = doesNotHaveOperator;
200
+ const DOES_NOT_HAVE_WITH_SELECT = _objectSpread2(_objectSpread2({}, DOES_NOT_HAVE), {}, {
201
+ InputComponent: GridFilterInputSingleSelect
202
+ });
203
+
204
+ const doesNotStartWithOperator = {
205
+ label: 'does not start with',
206
+ value: 'doesNotStartWith',
207
+ getApplyFilterFn: filterItem => {
208
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
209
+ return null;
210
+ }
211
+ return value => {
212
+ if (value == null) {
213
+ return true;
214
+ }
215
+ return !String(value).startsWith(filterItem.value);
216
+ };
217
+ },
218
+ InputComponent: GridFilterInputValue
219
+ };
220
+ const DOES_NOT_START_WITH = doesNotStartWithOperator;
221
+
222
+ const hasOperator = {
223
+ label: 'has',
224
+ value: 'has',
225
+ getApplyFilterFn: filterItem => {
226
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
227
+ return null;
228
+ }
229
+ return value => {
230
+ if (value == null) {
231
+ return false;
232
+ }
233
+ const cellValues = Array.isArray(value) ? value : [value];
234
+ return cellValues.map(value => String(value)).includes(filterItem.value);
235
+ };
236
+ },
237
+ InputComponent: GridFilterInputValue
238
+ };
239
+ const HAS = hasOperator;
240
+ const HAS_WITH_SELECT = _objectSpread2(_objectSpread2({}, HAS), {}, {
241
+ InputComponent: GridFilterInputSingleSelect
242
+ });
243
+
244
+ const hasOnlyOperator = {
245
+ label: 'has only',
246
+ value: 'hasOnly',
247
+ getApplyFilterFn: filterItem => {
248
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
249
+ return null;
250
+ }
251
+ return value => {
252
+ if (value == null) {
253
+ return false;
254
+ }
255
+ const cellValues = Array.isArray(value) ? value : [value];
256
+ return cellValues.length === 1 && String(cellValues[0]) === filterItem.value;
257
+ };
258
+ },
259
+ InputComponent: GridFilterInputValue
260
+ };
261
+ const HAS_ONLY = hasOnlyOperator;
262
+ const HAS_ONLY_WITH_SELECT = _objectSpread2(_objectSpread2({}, HAS_ONLY), {}, {
263
+ InputComponent: GridFilterInputSingleSelect
264
+ });
265
+
266
+ const isOperator = {
267
+ label: 'is',
268
+ value: 'is',
269
+ getApplyFilterFn: filterItem => {
270
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
271
+ return null;
272
+ }
273
+ return value => {
274
+ if (value == null) {
275
+ return false;
276
+ }
277
+ if (Array.isArray(value)) {
278
+ return false;
279
+ }
280
+ return String(value) === filterItem.value;
281
+ };
282
+ },
283
+ InputComponent: GridFilterInputValue
284
+ };
285
+ const IS = isOperator;
286
+ const IS_WITH_SELECT = _objectSpread2(_objectSpread2({}, IS), {}, {
287
+ InputComponent: GridFilterInputSingleSelect
288
+ });
289
+
290
+ const isNotOperator = {
291
+ label: 'is not',
292
+ value: 'isNot',
293
+ getApplyFilterFn: filterItem => {
294
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
295
+ return null;
296
+ }
297
+ return value => {
298
+ if (value == null) {
299
+ return true;
300
+ }
301
+ if (Array.isArray(value)) {
302
+ return true;
303
+ }
304
+ return String(value) !== filterItem.value;
305
+ };
306
+ },
307
+ InputComponent: GridFilterInputValue
308
+ };
309
+ const IS_NOT = isNotOperator;
310
+ const IS_NOT_WITH_SELECT = _objectSpread2(_objectSpread2({}, IS_NOT), {}, {
311
+ InputComponent: GridFilterInputSingleSelect
312
+ });
313
+
314
+ const containsAnyOfOperator = {
315
+ label: 'contains any of',
316
+ value: 'containsAnyOf',
317
+ getApplyFilterFn: filterItem => {
318
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
319
+ return null;
320
+ }
321
+ return value => {
322
+ if (filterItem.value.length === 0) {
323
+ return true;
324
+ }
325
+ if (value == null) {
326
+ return false;
327
+ }
328
+ const paramValues = Array.isArray(value) ? value : [value];
329
+ let match = false;
330
+ filterItem.value.forEach(filteredValue => {
331
+ paramValues.forEach(paramValue => {
332
+ if (String(paramValue).indexOf(filteredValue) !== -1) {
333
+ match = true;
334
+ }
335
+ });
336
+ });
337
+ return match;
338
+ };
339
+ },
340
+ InputComponent: GridFilterInputMultipleValue
341
+ };
342
+ const CONTAINS_ANY_OF = containsAnyOfOperator;
343
+
344
+ const doesNotContainAnyOfOperator = {
345
+ label: 'does not contain any of',
346
+ value: 'doesNotContainAnyOf',
347
+ getApplyFilterFn: filterItem => {
348
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
349
+ return null;
350
+ }
351
+ return value => {
352
+ if (filterItem.value.length === 0) {
353
+ return true;
354
+ }
355
+ if (value == null) {
356
+ return true;
357
+ }
358
+ const paramValues = Array.isArray(value) ? value : [value];
359
+ for (const filteredValue of filterItem.value) {
360
+ for (const paramValue of paramValues) {
361
+ if (String(paramValue).indexOf(filteredValue) !== -1) {
362
+ return false;
363
+ }
364
+ }
365
+ }
366
+ return true;
367
+ };
368
+ },
369
+ InputComponent: GridFilterInputMultipleValue
370
+ };
371
+ const DOES_NOT_CONTAIN_ANY_OF = doesNotContainAnyOfOperator;
372
+
373
+ const doesNotEndWithAnyOfOperator = {
374
+ label: 'does not end with any of',
375
+ value: 'doesNotEndWithAnyOf',
376
+ getApplyFilterFn: filterItem => {
377
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
378
+ return null;
379
+ }
380
+ return value => {
381
+ if (filterItem.value.length === 0) {
382
+ return true;
383
+ }
384
+ if (value == null) {
385
+ return true;
386
+ }
387
+ const paramValues = Array.isArray(value) ? value : [value];
388
+ for (const filteredValue of filterItem.value) {
389
+ for (const paramValue of paramValues) {
390
+ if (String(paramValue).endsWith(filteredValue)) {
391
+ return false;
392
+ }
393
+ }
394
+ }
395
+ return true;
396
+ };
397
+ },
398
+ InputComponent: GridFilterInputMultipleValue
399
+ };
400
+ const DOES_NOT_END_WITH_ANY_OF = doesNotEndWithAnyOfOperator;
401
+
402
+ const doesNotHaveAnyOf = {
403
+ label: "doesn't have any of",
404
+ value: 'doesNotHaveAnyOf',
405
+ getApplyFilterFn: filterItem => {
406
+ if (!filterItem.field || !filterItem.value || !Array.isArray(filterItem.value) || filterItem.value.length === 0) {
407
+ return null;
408
+ }
409
+ return value => {
410
+ if (value == null) {
411
+ return true;
412
+ }
413
+ const cellValues = Array.isArray(value) ? value : [value];
414
+
415
+ // Return true only if none of the filter values are in the cell values
416
+ return filterItem.value.every(filterVal => !cellValues.map(value => String(value)).includes(filterVal));
417
+ };
418
+ },
419
+ InputComponent: GridFilterInputMultipleValue
420
+ };
421
+ const DOES_NOT_HAVE_ANY_OF = doesNotHaveAnyOf;
422
+ const DOES_NOT_HAVE_ANY_OF_WITH_SELECT = _objectSpread2(_objectSpread2({}, DOES_NOT_HAVE_ANY_OF), {}, {
423
+ InputComponent: GridFilterInputMultipleSingleSelect
424
+ });
425
+
426
+ const doesNotStartWithAnyOfOperator = {
427
+ label: 'does not start with any of',
428
+ value: 'doesNotStartWithAnyOf',
429
+ getApplyFilterFn: filterItem => {
430
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
431
+ return null;
432
+ }
433
+ return value => {
434
+ if (filterItem.value.length === 0) {
435
+ return true;
436
+ }
437
+ if (value == null) {
438
+ return true;
439
+ }
440
+ const paramValues = Array.isArray(value) ? value : [value];
441
+ for (const filteredValue of filterItem.value) {
442
+ for (const paramValue of paramValues) {
443
+ if (String(paramValue).startsWith(filteredValue)) {
444
+ return false;
445
+ }
446
+ }
447
+ }
448
+ return true;
449
+ };
450
+ },
451
+ InputComponent: GridFilterInputMultipleValue
452
+ };
453
+ const DOES_NOT_START_WITH_ANY_OF = doesNotStartWithAnyOfOperator;
454
+
455
+ const endsWithAnyOfOperator = {
456
+ label: 'ends with any of',
457
+ value: 'endsWithAnyOf',
458
+ getApplyFilterFn: filterItem => {
459
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
460
+ return null;
461
+ }
462
+ return value => {
463
+ if (filterItem.value.length === 0) {
464
+ return true;
465
+ }
466
+ if (value == null) {
467
+ return false;
468
+ }
469
+ const paramValues = Array.isArray(value) ? value : [value];
470
+ let match = false;
471
+ filterItem.value.forEach(filteredValue => {
472
+ paramValues.forEach(paramValue => {
473
+ if (String(paramValue).endsWith(filteredValue)) {
474
+ match = true;
475
+ }
476
+ });
477
+ });
478
+ return match;
479
+ };
480
+ },
481
+ InputComponent: GridFilterInputMultipleValue
482
+ };
483
+ const ENDS_WITH_ANY_OF = endsWithAnyOfOperator;
484
+
485
+ const hasAllOfOperator = {
486
+ label: 'has all of',
487
+ value: 'hasAllOf',
488
+ getApplyFilterFn: filterItem => {
489
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
490
+ return null;
491
+ }
492
+ return value => {
493
+ if (filterItem.value.length === 0) {
494
+ return true;
495
+ }
496
+ if (value == null) {
497
+ return false;
498
+ }
499
+ const cellValues = Array.isArray(value) ? value : [value];
500
+ const cellStrings = cellValues.map(value => String(value));
501
+ const filterItemValues = Array.isArray(filterItem.value) ? filterItem.value : [filterItem.value];
502
+ return filterItemValues.every(v => cellStrings.includes(v));
503
+ };
504
+ },
505
+ InputComponent: GridFilterInputMultipleValue
506
+ };
507
+ const HAS_ALL_OF = hasAllOfOperator;
508
+ const HAS_ALL_OF_WITH_SELECT = _objectSpread2(_objectSpread2({}, HAS_ALL_OF), {}, {
509
+ InputComponent: GridFilterInputMultipleSingleSelect
510
+ });
511
+
512
+ const hasAnyOfOperator = {
513
+ label: 'has any of',
514
+ value: 'hasAnyOf',
515
+ getApplyFilterFn: filterItem => {
516
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
517
+ return null;
518
+ }
519
+ return value => {
520
+ if (filterItem.value.length === 0) {
521
+ return true;
522
+ }
523
+ if (value == null) {
524
+ return false;
525
+ }
526
+ const cellValues = Array.isArray(value) ? value : [value];
527
+ const filterItemValues = Array.isArray(filterItem.value) ? filterItem.value : [filterItem.value];
528
+ return filterItemValues.some(v => cellValues.map(value => String(value)).includes(v));
529
+ };
530
+ },
531
+ InputComponent: GridFilterInputMultipleValue
532
+ };
533
+ const HAS_ANY_OF = hasAnyOfOperator;
534
+ const HAS_ANY_OF_WITH_SELECT = _objectSpread2(_objectSpread2({}, HAS_ANY_OF), {}, {
535
+ InputComponent: GridFilterInputMultipleSingleSelect
536
+ });
537
+
538
+ const isAnyOfOperator = {
539
+ label: 'is any of',
540
+ value: 'isAnyOf',
541
+ getApplyFilterFn: filterItem => {
542
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
543
+ return null;
544
+ }
545
+ return value => {
546
+ if (filterItem.value.length === 0) {
547
+ return true;
548
+ }
549
+ if (value == null) {
550
+ return false;
551
+ }
552
+ const paramValues = Array.isArray(value) ? value : [value];
553
+ for (const paramValue of paramValues) {
554
+ if (filterItem.value.includes(String(paramValue))) {
555
+ return true;
556
+ }
557
+ }
558
+ return false;
559
+ };
560
+ },
561
+ InputComponent: GridFilterInputMultipleValue
562
+ };
563
+ const IS_ANY_OF = isAnyOfOperator;
564
+ const IS_ANY_OF_WITH_SELECT = _objectSpread2(_objectSpread2({}, IS_ANY_OF), {}, {
565
+ InputComponent: GridFilterInputMultipleSingleSelect
566
+ });
567
+
568
+ const isNotAnyOfOperator = {
569
+ label: 'is not any of',
570
+ value: 'isNotAnyOf',
571
+ getApplyFilterFn: filterItem => {
572
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
573
+ return null;
574
+ }
575
+ return value => {
576
+ if (filterItem.value.length === 0) {
577
+ return true;
578
+ }
579
+ if (value == null) {
580
+ return true;
581
+ }
582
+ const paramValues = Array.isArray(value) ? value : [value];
583
+ for (const paramValue of paramValues) {
584
+ if (filterItem.value.includes(String(paramValue))) {
585
+ return false;
586
+ }
587
+ }
588
+ return true;
589
+ };
590
+ },
591
+ InputComponent: GridFilterInputMultipleValue
592
+ };
593
+ const IS_NOT_ANY_OF = isNotAnyOfOperator;
594
+ const IS_NOT_ANY_OF_WITH_SELECT = _objectSpread2(_objectSpread2({}, IS_NOT_ANY_OF), {}, {
595
+ InputComponent: GridFilterInputMultipleSingleSelect
596
+ });
597
+
598
+ const startsWithAnyOfOperator = {
599
+ label: 'starts with any of',
600
+ value: 'startsWithAnyOf',
601
+ getApplyFilterFn: filterItem => {
602
+ if (!filterItem.field || !filterItem.value || !filterItem.operator) {
603
+ return null;
604
+ }
605
+ return value => {
606
+ if (filterItem.value.length === 0) {
607
+ return true;
608
+ }
609
+ if (value == null) {
610
+ return false;
611
+ }
612
+ const paramValues = Array.isArray(value) ? value : [value];
613
+ let match = false;
614
+ filterItem.value.forEach(filteredValue => {
615
+ paramValues.forEach(paramValue => {
616
+ if (String(paramValue).startsWith(filteredValue)) {
617
+ match = true;
618
+ }
619
+ });
620
+ });
621
+ return match;
622
+ };
623
+ },
624
+ InputComponent: GridFilterInputMultipleValue
625
+ };
626
+ const STARTS_WITH_ANY_OF = startsWithAnyOfOperator;
627
+
628
+ const getGridStringArrayOperators = () => [CONTAINS_ANY_OF, DOES_NOT_CONTAIN_ANY_OF, ENDS_WITH_ANY_OF, DOES_NOT_END_WITH_ANY_OF, IS_ANY_OF, IS_NOT_ANY_OF, STARTS_WITH_ANY_OF, DOES_NOT_START_WITH_ANY_OF];
629
+ const getGridStringArrayOperatorsWithSelect = () => [IS_WITH_SELECT, IS_NOT_WITH_SELECT, IS_ANY_OF_WITH_SELECT, IS_NOT_ANY_OF_WITH_SELECT, STARTS_WITH_ANY_OF, DOES_NOT_START_WITH_ANY_OF, ENDS_WITH_ANY_OF, DOES_NOT_END_WITH_ANY_OF, CONTAINS_ANY_OF, DOES_NOT_CONTAIN_ANY_OF];
630
+ const getGridStringArrayOperatorsWithSelectOnStringArrayColumns = () => [HAS_WITH_SELECT, HAS_ANY_OF_WITH_SELECT, HAS_ALL_OF_WITH_SELECT, HAS_ONLY_WITH_SELECT, DOES_NOT_HAVE_WITH_SELECT, DOES_NOT_HAVE_ANY_OF_WITH_SELECT];
631
+
632
+ const getGridStringOperators = () => [...getGridStringOperators$1().filter(operator => !['isAnyOf'].includes(operator.value)), DOES_NOT_CONTAIN, DOES_NOT_EQUAL, DOES_NOT_START_WITH, DOES_NOT_END_WITH, ...getGridStringArrayOperators()];
633
+
634
+ const isEmptyOperator = {
635
+ label: 'is empty',
636
+ value: 'isEmpty',
637
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
638
+ getApplyFilterFn: _filterItem => {
639
+ return value => {
640
+ if (value == null) {
641
+ return true;
642
+ }
643
+ if (Array.isArray(value)) {
644
+ return value.length === 0;
645
+ }
646
+ if (typeof value === 'string') {
647
+ return value.trim() === '';
648
+ }
649
+ return false;
650
+ };
651
+ },
652
+ requiresFilterValue: false
653
+ };
654
+ const isNotEmptyOperator = {
655
+ label: 'is not empty',
656
+ value: 'isNotEmpty',
657
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
658
+ getApplyFilterFn: _filterItem => {
659
+ return value => {
660
+ if (value == null) {
661
+ return false;
662
+ }
663
+ if (Array.isArray(value)) {
664
+ return value.length > 0;
665
+ }
666
+ if (typeof value === 'string') {
667
+ return value.trim() !== '';
668
+ }
669
+ return true;
670
+ };
671
+ },
672
+ requiresFilterValue: false
673
+ };
674
+ const ARRAY_IS_EMPTY = isEmptyOperator;
675
+ const ARRAY_IS_NOT_EMPTY = isNotEmptyOperator;
676
+
677
+ // istanbul ignore file
678
+ const operatorList = {
679
+ // Default types
680
+ string: getGridStringOperators$1(),
681
+ number: getGridNumericOperators$1(),
682
+ boolean: getGridBooleanOperators(),
683
+ date: getGridDateOperators(),
684
+ dateTime: getGridDateOperators(true),
685
+ singleSelect: getGridSingleSelectOperators(),
686
+ // Extended types (used by createColumn)
687
+ rsString: getGridStringOperators(),
688
+ rsNumber: getGridNumericOperators(),
689
+ rsSingleSelect: [CONTAINS_ANY_OF, ENDS_WITH_ANY_OF, IS_ANY_OF_WITH_SELECT, IS_NOT_WITH_SELECT, IS_WITH_SELECT, STARTS_WITH_ANY_OF],
690
+ rsSingleSelectWithShortOperatorList: [IS_WITH_SELECT, IS_NOT_WITH_SELECT, IS_ANY_OF_WITH_SELECT],
691
+ rsMultipleSelect: [HAS_WITH_SELECT, HAS_ANY_OF_WITH_SELECT, HAS_ALL_OF_WITH_SELECT, HAS_ONLY_WITH_SELECT, DOES_NOT_HAVE_WITH_SELECT, DOES_NOT_HAVE_ANY_OF_WITH_SELECT, ARRAY_IS_EMPTY, ARRAY_IS_NOT_EMPTY],
692
+ rsMultipleSelectWithShortOperatorList: [HAS_WITH_SELECT, DOES_NOT_HAVE_WITH_SELECT, HAS_ANY_OF_WITH_SELECT],
693
+ // Custom types
694
+ rsStringArray: getGridStringArrayOperators()
695
+ };
696
+
697
+ const PAGINATION_MODEL_KEY = 'paginationModel';
698
+ const FILTER_MODEL_KEY = 'filterModel';
699
+ const SORT_MODEL_KEY = 'sortModel';
700
+ const VISIBILITY_MODEL_KEY = 'visibilityModel';
701
+ const PINNED_COLUMNS = 'pinnedColumns';
702
+ const DIMENSION_MODEL_KEY = 'dimension';
703
+ const FILTER_SEARCH_KEY = 'searchModel';
704
+ const DENSITY_MODEL_KEY = 'densityModel';
705
+ const COLUMN_ORDER_MODEL_KEY = 'columnOrderModel';
706
+ const CATEGORIES = [PAGINATION_MODEL_KEY, FILTER_MODEL_KEY, SORT_MODEL_KEY, VISIBILITY_MODEL_KEY, DIMENSION_MODEL_KEY, FILTER_SEARCH_KEY, PINNED_COLUMNS, DENSITY_MODEL_KEY, COLUMN_ORDER_MODEL_KEY];
707
+ /**
708
+ * Build the localStorage key for a specific grid state category.
709
+ * Consumers can use this to read or clear individual state entries directly.
710
+ *
711
+ * @example
712
+ * ```ts
713
+ * const key = buildStorageKey({ id: pathname, version: 2, category: DENSITY_MODEL_KEY });
714
+ * localStorage.removeItem(key);
715
+ * ```
716
+ */
717
+ const buildStorageKey = _ref => {
718
+ let {
719
+ id,
720
+ version,
721
+ category
722
+ } = _ref;
723
+ return `${id}:${version}:${category}`;
724
+ };
725
+ const clearPreviousVersionStorage = (id, previousLocalStorageVersions) => {
726
+ for (const version of previousLocalStorageVersions) {
727
+ const keysToDelete = CATEGORIES.map(category => buildStorageKey({
728
+ id,
729
+ version,
730
+ category
731
+ }));
732
+ for (const keyToDelete of keysToDelete) {
733
+ try {
734
+ window.localStorage.removeItem(keyToDelete);
735
+ } catch (e) {
736
+ // Ignore
737
+ }
738
+ }
739
+ }
740
+ };
741
+
742
+ /**
743
+ * Clear localStorage keys for all versions from 0 to `maxVersion` (inclusive).
744
+ * Useful when the consumer wants to wipe all persisted grid state regardless of which version is active.
745
+ */
746
+ const clearAllVersionStorage = (id, maxVersion) => {
747
+ const versions = Array.from({
748
+ length: maxVersion + 1
749
+ }, (_, i) => i);
750
+ clearPreviousVersionStorage(id, versions);
751
+ };
752
+
753
+ /**
754
+ * Fully reset a `StatefulDataGrid` — clear localStorage for the given versions
755
+ * and strip all grid-owned URL params (`_*` and `v=`), preserving any non-grid params.
756
+ *
757
+ * Call from within a component where the router values are available:
758
+ * ```ts
759
+ * const { search, historyReplace } = useMyRouter();
760
+ * resetStatefulDataGridState({ id: 'myGrid', versions: [0, 1, 2], search, historyReplace });
761
+ * ```
762
+ */
763
+ const resetStatefulDataGridState = _ref2 => {
764
+ let {
765
+ id,
766
+ versions,
767
+ search,
768
+ historyReplace
769
+ } = _ref2;
770
+ clearPreviousVersionStorage(id, versions);
771
+ const params = new URLSearchParams(search);
772
+ const keysToDelete = [];
773
+ params.forEach((_value, key) => {
774
+ if (key.startsWith('_') || key === 'v') {
775
+ keysToDelete.push(key);
776
+ }
777
+ });
778
+ for (const key of keysToDelete) {
779
+ params.delete(key);
780
+ }
781
+ historyReplace(params.toString());
782
+ };
783
+
784
+ /** Maximum query string length (chars) before compression kicks in. */
785
+ const URL_BUDGET = 2000;
786
+
787
+ /** Prefix added to compressed values so the deserialiser can detect them. */
788
+ const COMPRESSED_PREFIX = '~';
789
+
790
+ /**
791
+ * Priority order for compressing URL params when the total query string exceeds the budget.
792
+ * Params listed first are compressed first (least valuable to read in the URL).
793
+ * The filter aggregate step uses the special key `_filters_aggregate`.
794
+ */
795
+ const COMPRESSION_PRIORITY = ['_columnOrder', '_columnVisibility', '_pinnedColumnsLeft', '_pinnedColumnsRight', '_filters_aggregate', '_aggregation', '_rowGrouping', '_quickFilterValues', '_pivot'];
796
+
797
+ /** Params that are always short and should never be compressed. */
798
+ const NEVER_COMPRESS = new Set(['_sortColumn', '_pagination', '_density', '_logicOperator', 'v', 'tab']);
799
+
800
+ /** Compress a string value and add the `~` prefix. */
801
+ const compressValue = value => {
802
+ return COMPRESSED_PREFIX + compressToEncodedURIComponent(value);
803
+ };
804
+
805
+ /** If the value starts with `~`, decompress it; otherwise return as-is. */
806
+ const decompressValue = value => {
807
+ if (!value.startsWith(COMPRESSED_PREFIX)) {
808
+ return value;
809
+ }
810
+ const decompressed = decompressFromEncodedURIComponent(value.slice(COMPRESSED_PREFIX.length));
811
+ return decompressed !== null && decompressed !== void 0 ? decompressed : value;
812
+ };
813
+
814
+ /** Check whether a value is compressed (starts with `~`). */
815
+ const isCompressed = value => {
816
+ return value.startsWith(COMPRESSED_PREFIX);
817
+ };
818
+
819
+ /**
820
+ * Calculate the total length of a query string built from a params map.
821
+ * Each entry contributes `key=value&` (the trailing `&` is omitted for the last entry
822
+ * but we include it for simplicity — the +1 per entry is close enough).
823
+ */
824
+ const calculateQueryStringLength = params => {
825
+ let total = 0;
826
+ for (const [key, value] of params) {
827
+ // key=value& — the last & is off by one but this is a budget heuristic
828
+ total += key.length + 1 + value.length + 1;
829
+ }
830
+ // Subtract the trailing & that doesn't actually exist
831
+ return Math.max(0, total - 1);
832
+ };
833
+
834
+ /**
835
+ * Given a set of filter param keys, aggregate them into a single `_filters` param
836
+ * by JSON-serialising the key-value entries and compressing the result.
837
+ *
838
+ * Returns null if aggregation doesn't save space.
839
+ */
840
+ const tryAggregateFilters = (params, filterKeys) => {
841
+ if (filterKeys.length === 0) return null;
842
+
843
+ // Build a record of key→value for all filter params
844
+ const filterRecord = {};
845
+ let originalLength = 0;
846
+ for (const key of filterKeys) {
847
+ const value = params.get(key);
848
+ if (value !== undefined) {
849
+ filterRecord[key] = value;
850
+ originalLength += key.length + 1 + value.length + 1; // key=value&
851
+ }
852
+ }
853
+
854
+ const json = JSON.stringify(filterRecord);
855
+ const compressed = compressValue(json);
856
+ const aggregatedLength = '_filters'.length + 1 + compressed.length; // _filters=~...
857
+
858
+ if (aggregatedLength < originalLength) {
859
+ return {
860
+ keysToRemove: filterKeys,
861
+ newKey: '_filters',
862
+ newValue: compressed
863
+ };
864
+ }
865
+ return null;
866
+ };
867
+
868
+ /**
869
+ * Identify which param keys are individual filter params (not system params).
870
+ * Filter params are those that start with `_` but are not well-known system params.
871
+ */
872
+ const getFilterParamKeys = params => {
873
+ const systemPrefixes = ['_sortColumn', '_pagination', '_density', '_logicOperator', '_quickFilterValues', '_columnVisibility', '_columnOrder', '_pinnedColumnsLeft', '_pinnedColumnsRight', '_rowGrouping', '_aggregation', '_pivot', '_filters'];
874
+ const filterKeys = [];
875
+ for (const key of params.keys()) {
876
+ if (!key.startsWith('_')) continue;
877
+ if (systemPrefixes.some(prefix => key === prefix || key.startsWith(prefix + '['))) {
878
+ // This includes _field[operator,type] style filter keys
879
+ if (key.includes('[')) {
880
+ filterKeys.push(key);
881
+ }
882
+ continue;
883
+ }
884
+ // Any other _prefixed key is a filter
885
+ filterKeys.push(key);
886
+ }
887
+
888
+ // Also include bracket-notation filter keys like _domain[contains,string]
889
+ for (const key of params.keys()) {
890
+ if (key.includes('[') && key.startsWith('_') && !filterKeys.includes(key)) {
891
+ filterKeys.push(key);
892
+ }
893
+ }
894
+ return filterKeys;
895
+ };
896
+
897
+ /**
898
+ * Apply budget-based compression to a set of URL params.
899
+ *
900
+ * Algorithm:
901
+ * 1. If total query string length ≤ budget → return as-is
902
+ * 2. Iterate params in COMPRESSION_PRIORITY order
903
+ * 3. For each param, try compressing — only apply if it saves space
904
+ * 4. For the `_filters_aggregate` step, try collapsing all individual filter
905
+ * params into a single `_filters=~blob`
906
+ * 5. Stop as soon as total ≤ budget
907
+ */
908
+ const applyBudgetCompression = function (params) {
909
+ let budget = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : URL_BUDGET;
910
+ const result = new Map(params);
911
+ let total = calculateQueryStringLength(result);
912
+ if (total <= budget) {
913
+ return result;
914
+ }
915
+ for (const priorityKey of COMPRESSION_PRIORITY) {
916
+ if (total <= budget) break;
917
+
918
+ // Special handling for filter aggregation
919
+ if (priorityKey === '_filters_aggregate') {
920
+ const filterKeys = getFilterParamKeys(result);
921
+ const aggregation = tryAggregateFilters(result, filterKeys);
922
+ if (aggregation) {
923
+ for (const key of aggregation.keysToRemove) {
924
+ result.delete(key);
925
+ }
926
+ result.set(aggregation.newKey, aggregation.newValue);
927
+ total = calculateQueryStringLength(result);
928
+ }
929
+ continue;
930
+ }
931
+
932
+ // Standard single-param compression
933
+ if (NEVER_COMPRESS.has(priorityKey)) continue;
934
+ const value = result.get(priorityKey);
935
+ if (value === undefined || isCompressed(value)) continue;
936
+ const compressed = compressValue(value);
937
+ if (compressed.length < value.length) {
938
+ result.set(priorityKey, compressed);
939
+ total = calculateQueryStringLength(result);
940
+ }
941
+ }
942
+ return result;
943
+ };
944
+
945
+ // Characters that need URL encoding in display format values
946
+ // These characters could cause CloudFront issues or parsing ambiguity
947
+ const CHARS_TO_ENCODE_IN_VALUES = {
948
+ ':': '%3A',
949
+ // Colon - reserved
950
+ ',': '%2C',
951
+ // Comma - used as list separator (only in list values)
952
+ '~': '%7E',
953
+ // Tilde - reserved
954
+ ';': '%3B',
955
+ // Semicolon - reserved
956
+ '[': '%5B',
957
+ // Square bracket - used in internal format
958
+ ']': '%5D',
959
+ // Square bracket - used in internal format
960
+ '(': '%28',
961
+ // Parenthesis - reserved
962
+ ')': '%29' // Parenthesis - reserved
963
+ };
964
+
965
+ /**
966
+ * Encodes special characters in a value for display format URLs.
967
+ * This ensures values containing colons, commas, etc. don't break parsing.
968
+ */
969
+ const encodeDisplayValue = value => {
970
+ let encoded = value;
971
+ for (const [char, replacement] of Object.entries(CHARS_TO_ENCODE_IN_VALUES)) {
972
+ encoded = encoded.split(char).join(replacement);
973
+ }
974
+ return encoded;
975
+ };
976
+
977
+ /**
978
+ * Decodes display format values back to their original form.
979
+ * Only reverses the specific encodings from encodeDisplayValue to ensure symmetry.
980
+ * Does NOT use decodeURIComponent, which would also decode literal percent-encoded
981
+ * sequences in the original data (e.g., a value containing literal "%3A" would be
982
+ * incorrectly decoded to ":").
983
+ */
984
+ const decodeDisplayValue = value => {
985
+ let decoded = value;
986
+ for (const [char, encoded] of Object.entries(CHARS_TO_ENCODE_IN_VALUES)) {
987
+ decoded = decoded.split(encoded).join(char);
988
+ }
989
+ return decoded;
990
+ };
991
+
992
+ /**
993
+ * Encodes array values for display format URLs.
994
+ * Each value is URL-encoded and joined with commas.
995
+ */
996
+ const encodeDisplayArrayValue = values => {
997
+ return values.map(v => encodeDisplayValue(v)).join(',');
998
+ };
999
+
1000
+ /**
1001
+ * Decodes comma-separated display format array values.
1002
+ * Handles URL-encoded commas within individual values.
1003
+ */
1004
+ const decodeDisplayArrayValue = value => {
1005
+ if (value === '') {
1006
+ return [];
1007
+ }
1008
+
1009
+ // Split by unencoded commas only (not %2C)
1010
+ // First, temporarily replace encoded commas, then split, then restore
1011
+ const TEMP_COMMA_PLACEHOLDER = '\x00COMMA\x00';
1012
+ const withPlaceholder = value.split('%2C').join(TEMP_COMMA_PLACEHOLDER);
1013
+ const parts = withPlaceholder.split(',');
1014
+ return parts.map(part => {
1015
+ const withCommasRestored = part.split(TEMP_COMMA_PLACEHOLDER).join('%2C');
1016
+ return decodeDisplayValue(withCommasRestored);
1017
+ });
1018
+ };
1019
+
1020
+ /**
1021
+ * Converts internal bracket notation to display-friendly dot notation.
1022
+ *
1023
+ * Display format (CloudFront-safe, no brackets):
1024
+ * - `field[operator]=value` → `field.operator=value` (value URL-encoded if needed)
1025
+ * - `field[isAnyOf]=list[a,b,c]` → `field.isAnyOf=a,b,c` (comma-separated, values URL-encoded)
1026
+ * - `_sortColumn=[field,desc]` → `_sortColumn=field.desc`
1027
+ * - `_pagination=[0,25,next]` → `_pagination=0.25.next`
1028
+ * - `_pinnedColumnsLeft=[a,b,c]` → `_pinnedColumnsLeft=a,b,c` (comma-separated)
1029
+ * - `_columnVisibility=[a,b,c]` → `_columnVisibility=a,b,c` (comma-separated)
1030
+ */
1031
+ const convertToDisplayFormat = search => {
1032
+ if (!search) return search;
1033
+ const cleanSearch = search.startsWith('?') ? search.slice(1) : search;
1034
+ const params = cleanSearch.split('&');
1035
+ const converted = params.map(param => {
1036
+ const eqIndex = param.indexOf('=');
1037
+ if (eqIndex !== -1) {
1038
+ const value = param.slice(eqIndex + 1);
1039
+ // Skip conversion for compressed values — already URL-safe
1040
+ if (isCompressed(value)) return param;
1041
+ }
1042
+
1043
+ // Handle _sortColumn=[field,direction]
1044
+ if (param.startsWith('_sortColumn=')) {
1045
+ const value = param.slice('_sortColumn='.length);
1046
+ if (value.startsWith('[') && value.endsWith(']')) {
1047
+ const inner = value.slice(1, -1);
1048
+ if (inner === '') {
1049
+ return '_sortColumn=';
1050
+ }
1051
+ const [field, direction] = inner.split(',');
1052
+ return `_sortColumn=${field}.${direction}`;
1053
+ }
1054
+ return param;
1055
+ }
1056
+
1057
+ // Handle _pagination=[page,pageSize,direction]
1058
+ if (param.startsWith('_pagination=')) {
1059
+ const value = param.slice('_pagination='.length);
1060
+ if (value.startsWith('[') && value.endsWith(']')) {
1061
+ const inner = value.slice(1, -1);
1062
+ if (inner === '') {
1063
+ return '_pagination=';
1064
+ }
1065
+ const [page, pageSize, direction] = inner.split(',');
1066
+ return `_pagination=${page}.${pageSize}.${direction}`;
1067
+ }
1068
+ return param;
1069
+ }
1070
+
1071
+ // Handle _pinnedColumnsLeft=[a,b,c] and _pinnedColumnsRight=[a,b,c]
1072
+ if (param.startsWith('_pinnedColumnsLeft=') || param.startsWith('_pinnedColumnsRight=')) {
1073
+ const eqIndex = param.indexOf('=');
1074
+ const key = param.slice(0, eqIndex);
1075
+ const value = param.slice(eqIndex + 1);
1076
+ if (value.startsWith('[') && value.endsWith(']')) {
1077
+ const inner = value.slice(1, -1);
1078
+ return `${key}=${inner}`;
1079
+ }
1080
+ return param;
1081
+ }
1082
+
1083
+ // Handle _columnVisibility=[a,b,c]
1084
+ if (param.startsWith('_columnVisibility=')) {
1085
+ const value = param.slice('_columnVisibility='.length);
1086
+ if (value.startsWith('[') && value.endsWith(']')) {
1087
+ const inner = value.slice(1, -1);
1088
+ return `_columnVisibility=${inner}`;
1089
+ }
1090
+ return param;
1091
+ }
1092
+
1093
+ // Handle _columnOrder=[a,b,c]
1094
+ if (param.startsWith('_columnOrder=')) {
1095
+ const value = param.slice('_columnOrder='.length);
1096
+ if (value.startsWith('[') && value.endsWith(']')) {
1097
+ const inner = value.slice(1, -1);
1098
+ return `_columnOrder=${inner}`;
1099
+ }
1100
+ return param;
1101
+ }
1102
+
1103
+ // Handle _rowGrouping=[a,b,c]
1104
+ if (param.startsWith('_rowGrouping=')) {
1105
+ const value = param.slice('_rowGrouping='.length);
1106
+ if (value.startsWith('[') && value.endsWith(']')) {
1107
+ const inner = value.slice(1, -1);
1108
+ return `_rowGrouping=${inner}`;
1109
+ }
1110
+ return param;
1111
+ }
1112
+
1113
+ // _aggregation and _pivot do not use bracket notation — pass through
1114
+ if (param.startsWith('_aggregation=') || param.startsWith('_pivot=')) {
1115
+ return param;
1116
+ }
1117
+
1118
+ // Handle _field[operator,type]=value or _field[operator,type]=list[a,b,c]
1119
+ const bracketMatch = param.match(/^_([^[]+)\[([^\]]+)\]=(.*)$/);
1120
+ if (bracketMatch) {
1121
+ const [, field, operatorAndType, value] = bracketMatch;
1122
+ const [operator] = operatorAndType.split(',');
1123
+
1124
+ // Convert list[a,b,c] to comma-separated with URL-encoded values
1125
+ if (value.startsWith('list[') && value.endsWith(']')) {
1126
+ const listContent = value.slice(5, -1);
1127
+ if (listContent === '') {
1128
+ return `${field}.${operator}=`;
1129
+ }
1130
+ const items = listContent.split(',');
1131
+ return `${field}.${operator}=${encodeDisplayArrayValue(items)}`;
1132
+ }
1133
+ // URL-encode special characters in the value
1134
+ return `${field}.${operator}=${encodeDisplayValue(value)}`;
1135
+ }
1136
+ return param;
1137
+ });
1138
+ return converted.join('&');
1139
+ };
1140
+
1141
+ /**
1142
+ * Converts display-friendly dot notation back to internal bracket notation.
1143
+ *
1144
+ * Internal format (server-side compatible):
1145
+ * - `field.operator=value` → `_field[operator,type]=value`
1146
+ * - `field.isAnyOf=a,b,c` → `_field[isAnyOf,type]=list[a,b,c]`
1147
+ * - `_sortColumn=field.desc` → `_sortColumn=[field,desc]`
1148
+ * - `_pagination=0.25.next` → `_pagination=[0,25,next]`
1149
+ * - `_pinnedColumnsLeft=a,b,c` → `_pinnedColumnsLeft=[a,b,c]`
1150
+ * - `_columnVisibility=a,b,c` → `_columnVisibility=[a,b,c]`
1151
+ */
1152
+ const convertFromDisplayFormat = (search, columns) => {
1153
+ if (!search) return search;
1154
+ const cleanSearch = search.startsWith('?') ? search.slice(1) : search;
1155
+ const params = cleanSearch.split('&');
1156
+ const converted = params.map(param => {
1157
+ const eqIndex = param.indexOf('=');
1158
+ if (eqIndex !== -1) {
1159
+ const value = param.slice(eqIndex + 1);
1160
+ // Skip conversion for compressed values — already URL-safe
1161
+ if (isCompressed(value)) return param;
1162
+ }
1163
+
1164
+ // Handle _sortColumn=field.direction or _sortColumn=
1165
+ if (param.startsWith('_sortColumn=')) {
1166
+ const value = param.slice('_sortColumn='.length);
1167
+ if (value === '') {
1168
+ return '_sortColumn=[]';
1169
+ }
1170
+ // If it already has brackets, leave it alone
1171
+ if (value.startsWith('[')) {
1172
+ return param;
1173
+ }
1174
+ const dotIndex = value.indexOf('.');
1175
+ if (dotIndex !== -1) {
1176
+ const field = value.slice(0, dotIndex);
1177
+ const direction = value.slice(dotIndex + 1);
1178
+ return `_sortColumn=[${field},${direction}]`;
1179
+ }
1180
+ return param;
1181
+ }
1182
+
1183
+ // Handle _pagination=page.pageSize.direction or _pagination=
1184
+ if (param.startsWith('_pagination=')) {
1185
+ const value = param.slice('_pagination='.length);
1186
+ if (value === '') {
1187
+ return '_pagination=[]';
1188
+ }
1189
+ // If it already has brackets, leave it alone
1190
+ if (value.startsWith('[')) {
1191
+ return param;
1192
+ }
1193
+ const parts = value.split('.');
1194
+ if (parts.length === 3) {
1195
+ return `_pagination=[${parts.join(',')}]`;
1196
+ }
1197
+ return param;
1198
+ }
1199
+
1200
+ // Handle _pinnedColumnsLeft=a,b,c and _pinnedColumnsRight=a,b,c
1201
+ if (param.startsWith('_pinnedColumnsLeft=') || param.startsWith('_pinnedColumnsRight=')) {
1202
+ const eqIndex = param.indexOf('=');
1203
+ const key = param.slice(0, eqIndex);
1204
+ const value = param.slice(eqIndex + 1);
1205
+ // If it already has brackets, leave it alone
1206
+ if (value.startsWith('[')) {
1207
+ return param;
1208
+ }
1209
+ return `${key}=[${value}]`;
1210
+ }
1211
+
1212
+ // Handle _columnVisibility=a,b,c
1213
+ if (param.startsWith('_columnVisibility=')) {
1214
+ const value = param.slice('_columnVisibility='.length);
1215
+ // If it already has brackets, leave it alone
1216
+ if (value.startsWith('[')) {
1217
+ return param;
1218
+ }
1219
+ return `_columnVisibility=[${value}]`;
1220
+ }
1221
+
1222
+ // Handle _columnOrder=a,b,c
1223
+ if (param.startsWith('_columnOrder=')) {
1224
+ const value = param.slice('_columnOrder='.length);
1225
+ if (value.startsWith('[')) {
1226
+ return param;
1227
+ }
1228
+ return `_columnOrder=[${value}]`;
1229
+ }
1230
+
1231
+ // Handle _rowGrouping=a,b,c
1232
+ if (param.startsWith('_rowGrouping=')) {
1233
+ const value = param.slice('_rowGrouping='.length);
1234
+ if (value.startsWith('[')) {
1235
+ return param;
1236
+ }
1237
+ return `_rowGrouping=[${value}]`;
1238
+ }
1239
+
1240
+ // _aggregation, _pivot, _filters — pass through (no bracket conversion needed)
1241
+ if (param.startsWith('_aggregation=') || param.startsWith('_pivot=') || param.startsWith('_filters=')) {
1242
+ return param;
1243
+ }
1244
+
1245
+ // Handle field.operator=value (dot notation for filters)
1246
+ const dotMatch = param.match(/^([^.]+)\.([a-zA-Z_]+)=(.*)$/);
1247
+ if (dotMatch) {
1248
+ const [, field, operator, rawValue] = dotMatch;
1249
+
1250
+ // Get column type for this field
1251
+ const column = columns.find(c => c.field === field);
1252
+ const columnType = (column === null || column === void 0 ? void 0 : column.type) || 'string';
1253
+
1254
+ // Check if this is a list operator
1255
+ const listOps = ['containsAnyOf', 'doesNotContainAnyOf', 'endsWithAnyOf', 'doesNotEndWithAnyOf', 'hasAllOf', 'hasAnyOf', 'doesNotHaveAnyOf', 'isAnyOf', 'isNotAnyOf', 'startsWithAnyOf', 'doesNotStartWithAnyOf', 'isBetween'];
1256
+ if (listOps.includes(operator)) {
1257
+ const items = decodeDisplayArrayValue(rawValue);
1258
+ return `_${field}[${operator},${columnType}]=list[${items.join(',')}]`;
1259
+ }
1260
+ // Decode URL-encoded special characters in the value
1261
+ const decodedValue = decodeDisplayValue(rawValue);
1262
+ return `_${field}[${operator},${columnType}]=${decodedValue}`;
1263
+ }
1264
+ return param;
1265
+ });
1266
+ return converted.join('&');
1267
+ };
1268
+
1269
+ /**
1270
+ * Detects if search string is in display format and converts it to internal format.
1271
+ */
1272
+ const getDecodedSearchFromUrl = (search, columns) => {
1273
+ const searchWithoutLeadingQuestion = search.startsWith('?') ? search.slice(1) : search;
1274
+
1275
+ // Check if it's using display format:
1276
+ // 1. Contains dot notation for filters like 'field.operator='
1277
+ // 2. Has empty _sortColumn= (display format) instead of _sortColumn=[] (internal format)
1278
+ // 3. Has _pinnedColumnsLeft=a,b without brackets
1279
+ // 4. Has _sortColumn=field.desc format
1280
+ // 5. Has _pagination=0.25.next format
1281
+ const hasDotNotationFilter = /[a-zA-Z_]+\.[a-zA-Z_]+=/.test(searchWithoutLeadingQuestion);
1282
+ const hasEmptySortColumn = /(_sortColumn)=(&|$)/.test(searchWithoutLeadingQuestion);
1283
+ const hasSortDotNotation = /_sortColumn=[^&[]+\.[^&]+/.test(searchWithoutLeadingQuestion);
1284
+ const hasPaginationDotNotation = /_pagination=[^&[]+\.[^&]+/.test(searchWithoutLeadingQuestion);
1285
+ const hasPinnedWithoutBrackets = /(_pinnedColumnsLeft|_pinnedColumnsRight)=[^&[]*(&|$)/.test(searchWithoutLeadingQuestion);
1286
+ const hasVisibilityWithoutBrackets = /_columnVisibility=[^&[]*(&|$)/.test(searchWithoutLeadingQuestion);
1287
+ const hasColumnOrderWithoutBrackets = /_columnOrder=[^&[]*(&|$)/.test(searchWithoutLeadingQuestion);
1288
+ const hasRowGroupingWithoutBrackets = /_rowGrouping=[^&[]*(&|$)/.test(searchWithoutLeadingQuestion);
1289
+ const hasBracketNotation = /\[.*\]=/.test(searchWithoutLeadingQuestion);
1290
+ const isDisplayFormat = (hasDotNotationFilter || hasEmptySortColumn || hasSortDotNotation || hasPaginationDotNotation || hasPinnedWithoutBrackets || hasVisibilityWithoutBrackets || hasColumnOrderWithoutBrackets || hasRowGroupingWithoutBrackets) && !hasBracketNotation;
1291
+ if (isDisplayFormat) {
1292
+ return '?' + convertFromDisplayFormat(searchWithoutLeadingQuestion, columns);
1293
+ }
1294
+
1295
+ // Already in internal bracket format or empty
1296
+ return search;
1297
+ };
1298
+
1299
+ /**
1300
+ * Builds a display-format query string from internal format.
1301
+ */
1302
+ const buildQueryParamsString = search => {
1303
+ const displaySearch = convertToDisplayFormat(search);
1304
+ return displaySearch.startsWith('?') ? displaySearch : `?${displaySearch}`;
1305
+ };
1306
+
1307
+ /**
1308
+ * Compares two search strings for equality, ignoring parameter order.
1309
+ * This prevents infinite update loops when the same parameters appear in different order.
1310
+ */
1311
+ const areSearchStringsEqual = (search1, search2) => {
1312
+ const strippedSearch1 = search1.startsWith('?') ? search1.slice(1) : search1;
1313
+ const strippedSearch2 = search2.startsWith('?') ? search2.slice(1) : search2;
1314
+
1315
+ // Quick check: if strings are identical, no need to parse
1316
+ if (strippedSearch1 === strippedSearch2) {
1317
+ return true;
1318
+ }
1319
+
1320
+ // Parse and sort parameters for order-independent comparison
1321
+ const params1 = strippedSearch1.split('&').filter(Boolean).sort();
1322
+ const params2 = strippedSearch2.split('&').filter(Boolean).sort();
1323
+ if (params1.length !== params2.length) {
1324
+ return false;
1325
+ }
1326
+ return params1.every((param, index) => param === params2[index]);
1327
+ };
1328
+ const decodeValue = value => {
1329
+ if (value === '') {
1330
+ return undefined;
1331
+ }
1332
+
1333
+ // Handle array values encoded as list[item1,item2,...]
1334
+ if (value.startsWith('list[')) {
1335
+ const arrayContent = value.slice(5, -1); // Remove 'list[' and ']'
1336
+ if (arrayContent === '') {
1337
+ return [];
1338
+ }
1339
+ return arrayContent.split(',').filter(item => item);
1340
+ }
1341
+ return value;
1342
+ };
1343
+ const encodeValue = value => {
1344
+ if (value === undefined || value === null) {
1345
+ return '';
1346
+ }
1347
+
1348
+ // Handle array values - encode as list[item1,item2,...]
1349
+ if (Array.isArray(value)) {
1350
+ return `list[${value.map(String).join(',')}]`;
1351
+ }
1352
+ return String(value);
1353
+ };
1354
+ const urlSearchParamsToString = searchParams => {
1355
+ let searchString = '';
1356
+ for (const [key, value] of searchParams) {
1357
+ searchString = searchString + `${key}=${value}&`;
1358
+ }
1359
+ return searchString.slice(0, searchString.length - 1);
1360
+ };
1361
+ const numberOperatorEncoder = {
1362
+ '=': 'eq',
1363
+ '!=': 'ne',
1364
+ '>': 'gt',
1365
+ '>=': 'gte',
1366
+ '<': 'lt',
1367
+ '<=': 'lte'
1368
+ };
1369
+ const numberOperatorDecoder = {
1370
+ eq: '=',
1371
+ ne: '!=',
1372
+ gt: '>',
1373
+ gte: '>=',
1374
+ lt: '<',
1375
+ lte: '<='
1376
+ };
1377
+ const isOperatorValueValid = (field, operator, columns) => {
1378
+ const column = columns.find(column => column.field === field);
1379
+ if (!column) {
1380
+ return false;
1381
+ }
1382
+ const columnType = (column === null || column === void 0 ? void 0 : column.type) || 'string';
1383
+ const operators = column.filterOperators || operatorList[columnType];
1384
+ if (!operators) {
1385
+ return false;
1386
+ }
1387
+ return !!operators.find(op => columnType === 'number' && Object.keys(numberOperatorEncoder).includes(op.value) ? numberOperatorEncoder[op.value] === operator : op.value === operator);
1388
+ };
1389
+ const listOperators = ['containsAnyOf', 'doesNotContainAnyOf', 'endsWithAnyOf', 'doesNotEndWithAnyOf', 'hasAllOf', 'hasAnyOf', 'doesNotHaveAnyOf', 'isAnyOf', 'isNotAnyOf', 'startsWithAnyOf', 'doesNotStartWithAnyOf', 'isBetween'];
1390
+
1391
+ // Check if the value doesn't break
1392
+ const isValueValid = (value, field, columns, operator) => {
1393
+ var _column$type;
1394
+ // every field accepts undefined as value for default
1395
+ if (value === undefined || value === '') {
1396
+ return true;
1397
+ }
1398
+
1399
+ // xxxAnyOf accepts as value only lists, and we are declaring them in the
1400
+ // URL as `list=[...]`
1401
+ if (listOperators.includes(operator)) {
1402
+ return Array.isArray(value) || value === '';
1403
+ }
1404
+
1405
+ // We are accepting arrays only if they are of the 'xxxAnyOf' type
1406
+ if (Array.isArray(value) && !listOperators.includes(operator)) {
1407
+ return false;
1408
+ }
1409
+ const column = columns.find(column => column.field === field);
1410
+ if (!column) {
1411
+ return false;
1412
+ }
1413
+ const type = (_column$type = column['type']) !== null && _column$type !== void 0 ? _column$type : 'string';
1414
+
1415
+ // Only date and rating fail with 500s, other set themselves as undefined
1416
+ if (type !== 'date' && type !== 'rating') {
1417
+ return true;
1418
+ }
1419
+
1420
+ // just checking that rating is a number.
1421
+ if (type === 'rating') {
1422
+ return !isNaN(Number(value));
1423
+ }
1424
+
1425
+ // format: YYYY-MM-DD
1426
+ // just verifying that the 3 values are numbers to avoid 500s,
1427
+ // If the value is invalid the form will appear as undefined
1428
+ if (type === 'date') {
1429
+ const dateSplitted = value.split('-');
1430
+ if (dateSplitted.length !== 3) {
1431
+ return false;
1432
+ }
1433
+ const [YYYY, MM, DD] = dateSplitted;
1434
+ return !isNaN(parseInt(YYYY)) && !isNaN(parseInt(MM)) && !isNaN(parseInt(DD));
1435
+ }
1436
+ return false;
1437
+ };
1438
+
1439
+ /** FILTERS */
1440
+
1441
+ // example:
1442
+ // unicodeDomain[contains]=a&unicodeDomain[contains]=dsa&logicOperator=and&tab=ignored
1443
+ const getFilterModelFromString = (searchString, columns) => {
1444
+ if (!searchString) {
1445
+ return 'invalid';
1446
+ }
1447
+ let logicOperator = GridLogicOperator.And;
1448
+ let quickFilterValues = [];
1449
+ const searchParams = new URLSearchParams();
1450
+ for (const [key, value] of new URLSearchParams(searchString)) {
1451
+ if (key.startsWith('_') && !['_logicOperator', '_sortColumn', '_pinnedColumnsLeft', '_pinnedColumnsRight', '_columnVisibility', '_pagination', '_quickFilterValues', '_columnOrder', '_rowGrouping', '_aggregation', '_pivot', '_density', '_filters'].includes(key)) {
1452
+ searchParams.set(key, value);
1453
+ }
1454
+ if (key === '_logicOperator') {
1455
+ logicOperator = value === GridLogicOperator.And || value === GridLogicOperator.Or ? value : GridLogicOperator.And;
1456
+ }
1457
+ if (key === '_quickFilterValues') {
1458
+ try {
1459
+ quickFilterValues = JSON.parse(decodeURIComponent(value));
1460
+ } catch {
1461
+ quickFilterValues = [];
1462
+ }
1463
+ }
1464
+ }
1465
+ let id = 5000;
1466
+ const fields = columns.map(column => column.field);
1467
+ let isInvalid = false;
1468
+ const items = [];
1469
+ searchParams.forEach((value, key) => {
1470
+ var _columns$find;
1471
+ if (isInvalid) {
1472
+ return;
1473
+ }
1474
+ const field = key.split('[')[0].slice(1);
1475
+ if (!fields.includes(field)) {
1476
+ return;
1477
+ }
1478
+ const columnType = (_columns$find = columns.find(column => column.field === field)) === null || _columns$find === void 0 ? void 0 : _columns$find.type;
1479
+ const left = key.split(']')[0];
1480
+ if (left.split('[').length < 2) {
1481
+ isInvalid = true;
1482
+ return;
1483
+ }
1484
+ const splitRight = key.split('[')[1].split(']')[0].split(',');
1485
+ const type = splitRight[1];
1486
+ if (type !== columnType) {
1487
+ isInvalid = true;
1488
+ return;
1489
+ }
1490
+ const operator = splitRight[0];
1491
+ if (!isOperatorValueValid(field, operator, columns) || Array.isArray(operator)) {
1492
+ isInvalid = true;
1493
+ return;
1494
+ }
1495
+ id += 1;
1496
+ const decodedValue = decodeValue(value);
1497
+ if (!isValueValid(decodedValue, field, columns, operator)) {
1498
+ isInvalid = true;
1499
+ return;
1500
+ }
1501
+ items.push({
1502
+ field,
1503
+ operator: columnType === 'number' && Object.keys(numberOperatorDecoder).includes(operator) ? numberOperatorDecoder[operator] : operator,
1504
+ id,
1505
+ value: listOperators.includes(operator) && decodedValue === '' ? [] : decodedValue,
1506
+ type
1507
+ });
1508
+ });
1509
+ if (isInvalid) {
1510
+ return 'invalid';
1511
+ }
1512
+ return {
1513
+ items,
1514
+ logicOperator,
1515
+ quickFilterValues
1516
+ };
1517
+ };
1518
+ const getSearchParamsFromFilterModel = filterModel => {
1519
+ var _filterModel$quickFil;
1520
+ const searchParams = new URLSearchParams();
1521
+ searchParams.set('_logicOperator', filterModel['logicOperator'] || '');
1522
+ filterModel['items'].forEach(item => {
1523
+ const {
1524
+ field,
1525
+ operator,
1526
+ value,
1527
+ type
1528
+ } = item;
1529
+ if (Object.keys(numberOperatorEncoder).includes(operator)) {
1530
+ searchParams.set(`_${field}[${numberOperatorEncoder[operator]},${encodeValue(type)}]`, encodeValue(value));
1531
+ } else {
1532
+ searchParams.set(`_${field}[${encodeValue(operator)},${encodeValue(type)}]`, encodeValue(value));
1533
+ }
1534
+ });
1535
+ if ((_filterModel$quickFil = filterModel.quickFilterValues) !== null && _filterModel$quickFil !== void 0 && _filterModel$quickFil.length) {
1536
+ searchParams.set('_quickFilterValues', encodeURIComponent(JSON.stringify(filterModel.quickFilterValues)));
1537
+ }
1538
+ return searchParams;
1539
+ };
1540
+
1541
+ // Rules:
1542
+ // - if we have something in the URL, use that info
1543
+ // - if we don't have that, use the localStorage and update the URL
1544
+ // - if we don't have that, return an empty FilterModel
1545
+ const getFilterModel = (search, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion) => {
1546
+ const defaultValue = initialState && initialState.filter && initialState.filter.filterModel ? initialState.filter.filterModel : {
1547
+ items: [],
1548
+ logicOperator: GridLogicOperator.And
1549
+ };
1550
+
1551
+ // Persist initialState-derived filters to localStorage so all three sources stay in sync
1552
+ const persistDefaultFilters = () => {
1553
+ const searchFromDefault = getSearchParamsFromFilterModel(defaultValue);
1554
+ const searchString = urlSearchParamsToString(searchFromDefault);
1555
+ if (searchString !== localStorageFilters) {
1556
+ setLocalStorageFilters(searchString);
1557
+ }
1558
+ };
1559
+ if (isNewVersion) {
1560
+ persistDefaultFilters();
1561
+ return defaultValue;
1562
+ }
1563
+ const filterModelFromSearch = getFilterModelFromString(search, columns);
1564
+ if (filterModelFromSearch !== 'invalid') {
1565
+ const searchFromFilterModel = getSearchParamsFromFilterModel(filterModelFromSearch);
1566
+ const searchString = urlSearchParamsToString(searchFromFilterModel);
1567
+ if (searchString !== localStorageFilters) {
1568
+ setLocalStorageFilters(searchString);
1569
+ }
1570
+ return filterModelFromSearch;
1571
+ }
1572
+ const filterModelFromLocalStorage = getFilterModelFromString(localStorageFilters, columns);
1573
+ if (filterModelFromLocalStorage !== 'invalid') {
1574
+ return filterModelFromLocalStorage;
1575
+ }
1576
+ persistDefaultFilters();
1577
+ return defaultValue;
1578
+ };
1579
+
1580
+ /** SORT */
1581
+
1582
+ const getSortingFromString = (searchString, columns) => {
1583
+ if (!searchString) {
1584
+ return 'invalid';
1585
+ }
1586
+ const searchParams = new URLSearchParams(searchString);
1587
+ const value = searchParams.get('_sortColumn');
1588
+ if (value === '' || value === null || value === '[]') {
1589
+ return [];
1590
+ }
1591
+ const fields = columns.map(column => column.field);
1592
+ const [column, order] = value.slice(1, value.length - 1).split(',');
1593
+ if (fields.includes(column) && (order === 'asc' || order === 'desc')) {
1594
+ return [{
1595
+ field: column,
1596
+ sort: order
1597
+ }];
1598
+ }
1599
+ return 'invalid';
1600
+ };
1601
+ const getSearchParamsFromSorting = sorting => {
1602
+ const searchParams = new URLSearchParams();
1603
+ searchParams.set('_sortColumn', sorting.length > 0 && sorting[0].sort ? `[${encodeValue(sorting[0].field)},${encodeValue(sorting[0].sort)}]` : '[]');
1604
+ return searchParams;
1605
+ };
1606
+
1607
+ // Rules:
1608
+ // - if we have something in the URL, use that info
1609
+ // - if we don't have that, use the localStorage and update the URL
1610
+ // - if we don't have that, return an empty SortModel
1611
+ const getSortModel = (search, columns, localStorageSorting, setLocalStorageSorting, initialState, isNewVersion) => {
1612
+ var _initialState$sorting;
1613
+ const defaultValue = initialState !== null && initialState !== void 0 && (_initialState$sorting = initialState.sorting) !== null && _initialState$sorting !== void 0 && _initialState$sorting.sortModel ? initialState.sorting.sortModel : [];
1614
+
1615
+ // Persist initialState-derived sorting to localStorage so all three sources stay in sync
1616
+ const persistDefaultSort = () => {
1617
+ const searchFromDefault = getSearchParamsFromSorting(defaultValue);
1618
+ const searchString = urlSearchParamsToString(searchFromDefault);
1619
+ if (searchString !== localStorageSorting) {
1620
+ setLocalStorageSorting(searchString);
1621
+ }
1622
+ };
1623
+ if (isNewVersion) {
1624
+ persistDefaultSort();
1625
+ return defaultValue;
1626
+ }
1627
+ const sorting = getSortingFromString(search, columns);
1628
+ if (sorting !== 'invalid') {
1629
+ const searchFromSortModel = getSearchParamsFromSorting(sorting);
1630
+ const searchString = urlSearchParamsToString(searchFromSortModel);
1631
+ if (searchString !== localStorageSorting) {
1632
+ setLocalStorageSorting(searchString);
1633
+ }
1634
+ return sorting;
1635
+ }
1636
+ const sortModelFromLocalStorage = getSortingFromString(localStorageSorting, columns);
1637
+ if (sortModelFromLocalStorage !== 'invalid') {
1638
+ return sortModelFromLocalStorage;
1639
+ }
1640
+ persistDefaultSort();
1641
+ return defaultValue;
1642
+ };
1643
+
1644
+ /** PAGINATION */
1645
+
1646
+ const getPaginationFromString = searchString => {
1647
+ if (!searchString) {
1648
+ return 'invalid';
1649
+ }
1650
+ const searchParams = new URLSearchParams(searchString);
1651
+ const value = searchParams.get('_pagination');
1652
+ if (value === '' || value === null || value === '[]') {
1653
+ return 'invalid';
1654
+ }
1655
+ const pagination = value.slice(1, value.length - 1).split(',');
1656
+ const page = parseFloat(pagination[0]);
1657
+ const pageSize = parseFloat(pagination[1]);
1658
+ const direction = pagination[2];
1659
+ if (!Number.isNaN(page) && !Number.isNaN(pageSize) && direction && ['next', 'back'].includes(direction)) {
1660
+ return {
1661
+ page,
1662
+ pageSize,
1663
+ direction: direction
1664
+ };
1665
+ }
1666
+ return 'invalid';
1667
+ };
1668
+ const getSearchParamsFromPagination = pagination => {
1669
+ const searchParams = new URLSearchParams();
1670
+ searchParams.set('_pagination', `[${pagination.page},${pagination.pageSize},${pagination.direction}]`);
1671
+ return searchParams;
1672
+ };
1673
+
1674
+ // Rules:
1675
+ // - if we have something in the URL, use that info
1676
+ // - if we don't have that, use the localStorage and update the URL
1677
+ // - if we don't have that, return an empty PaginationModel
1678
+ const getPaginationModel = (search, localStoragePagination, setLocalStoragePagination, initialState, isNewVersion) => {
1679
+ var _initialState$paginat, _initialState$paginat2, _initialState$paginat3;
1680
+ // Extract pageSize from MUI's nested format: initialState.pagination.paginationModel.pageSize
1681
+ const initialPageSize = (_initialState$paginat = initialState === null || initialState === void 0 ? void 0 : (_initialState$paginat2 = initialState.pagination) === null || _initialState$paginat2 === void 0 ? void 0 : (_initialState$paginat3 = _initialState$paginat2.paginationModel) === null || _initialState$paginat3 === void 0 ? void 0 : _initialState$paginat3.pageSize) !== null && _initialState$paginat !== void 0 ? _initialState$paginat : 25;
1682
+ const defaultValue = {
1683
+ page: 0,
1684
+ pageSize: initialPageSize,
1685
+ direction: 'next'
1686
+ };
1687
+
1688
+ // Persist initialState-derived pagination to localStorage so all three sources stay in sync
1689
+ const persistDefaultPagination = () => {
1690
+ const searchFromDefault = getSearchParamsFromPagination(defaultValue);
1691
+ const searchString = urlSearchParamsToString(searchFromDefault);
1692
+ if (searchString !== localStoragePagination) {
1693
+ setLocalStoragePagination(searchString);
1694
+ }
1695
+ };
1696
+ if (isNewVersion) {
1697
+ persistDefaultPagination();
1698
+ return defaultValue;
1699
+ }
1700
+ const pagination = getPaginationFromString(search);
1701
+ if (pagination !== 'invalid') {
1702
+ const searchFromPaginationModel = getSearchParamsFromPagination(pagination);
1703
+ const searchString = urlSearchParamsToString(searchFromPaginationModel);
1704
+ if (searchString !== localStoragePagination) {
1705
+ setLocalStoragePagination(searchString);
1706
+ }
1707
+ return pagination;
1708
+ }
1709
+ const paginationModelFromLocalStorage = getPaginationFromString(localStoragePagination);
1710
+ if (paginationModelFromLocalStorage !== 'invalid') {
1711
+ return paginationModelFromLocalStorage;
1712
+ }
1713
+ persistDefaultPagination();
1714
+ return defaultValue;
1715
+ };
1716
+
1717
+ /** COLUMN VISIBILITY */
1718
+
1719
+ const getColumnVisibilityFromString = (searchString, columns) => {
1720
+ if (!searchString) {
1721
+ return 'invalid';
1722
+ }
1723
+ const searchParams = new URLSearchParams(searchString);
1724
+ const value = searchParams.get('_columnVisibility');
1725
+ if (value === '' || value === null || value === '[]') {
1726
+ return 'invalid';
1727
+ }
1728
+ const parsedFields = value.slice(1, value.length - 1).split(',');
1729
+ const fields = columns.map(column => column.field);
1730
+ const visibility = {};
1731
+ for (const field of fields) {
1732
+ visibility[field] = false;
1733
+ }
1734
+ for (const parsedField of parsedFields) {
1735
+ if (fields.includes(parsedField)) {
1736
+ visibility[parsedField] = true;
1737
+ }
1738
+ }
1739
+ if (Object.values(visibility).filter(v => v === true).length === 0) {
1740
+ return 'invalid';
1741
+ }
1742
+ return visibility;
1743
+ };
1744
+ const getSearchParamsFromColumnVisibility = (columnVisibility, columns) => {
1745
+ const searchParams = new URLSearchParams();
1746
+ const fields = columns.map(column => column.field);
1747
+
1748
+ // if column visibility model is empty, show all columns
1749
+ if (Object.keys(columnVisibility).length == 0) {
1750
+ searchParams.set('_columnVisibility', `[${fields.join(',')}]`);
1751
+ return searchParams;
1752
+ }
1753
+ const finalColumnVisibility = columns.filter(c => {
1754
+ var _c$hideable;
1755
+ return !((_c$hideable = c === null || c === void 0 ? void 0 : c.hideable) !== null && _c$hideable !== void 0 ? _c$hideable : true);
1756
+ }).map(c => c.field).reduce((acc, colName) => {
1757
+ return _objectSpread2(_objectSpread2({}, acc), {}, {
1758
+ [colName]: true
1759
+ });
1760
+ }, columnVisibility);
1761
+ const visibleColumns = fields.filter(column => finalColumnVisibility[column] !== false);
1762
+ searchParams.set('_columnVisibility', `[${visibleColumns.join(',')}]`);
1763
+ return searchParams;
1764
+ };
1765
+
1766
+ // Rules:
1767
+ // - if we have something in the URL, use that info
1768
+ // - if we don't have that, use the localStorage and update the URL
1769
+ // - if we don't have that, return an empty ColumnVisibilityModel (which is all columns)
1770
+ // NOTE: the `defaultHidden` is a custom field and not standard DataGrid
1771
+ // The reason is the following bug: https://github.com/mui/mui-x/issues/8407
1772
+ const getColumnsVisibility = (search, columns, localStorageColumnsVisibility, setLocalStorageColumnsVisibility, initialState, isNewVersion) => {
1773
+ var _initialState$columns3;
1774
+ const defaultValue = {};
1775
+ for (const column of columns) {
1776
+ var _initialState$columns, _initialState$columns2;
1777
+ const field = column.field;
1778
+ if ((initialState === null || initialState === void 0 ? void 0 : (_initialState$columns = initialState.columns) === null || _initialState$columns === void 0 ? void 0 : (_initialState$columns2 = _initialState$columns.columnVisibilityModel) === null || _initialState$columns2 === void 0 ? void 0 : _initialState$columns2[field]) !== undefined) {
1779
+ defaultValue[field] = initialState.columns.columnVisibilityModel[field];
1780
+ } else {
1781
+ defaultValue[field] = column.defaultHidden !== true; // undefined will be true
1782
+ }
1783
+ }
1784
+
1785
+ // Persist initialState-derived column visibility to localStorage so all three sources stay in sync
1786
+ const persistDefaultVisibility = value => {
1787
+ const searchFromDefault = getSearchParamsFromColumnVisibility(value, columns);
1788
+ if (searchFromDefault.toString() !== localStorageColumnsVisibility) {
1789
+ setLocalStorageColumnsVisibility(searchFromDefault.toString());
1790
+ }
1791
+ };
1792
+ if (isNewVersion) {
1793
+ persistDefaultVisibility(defaultValue);
1794
+ return defaultValue;
1795
+ }
1796
+ const columnVisibility = getColumnVisibilityFromString(search, columns);
1797
+ if (columnVisibility !== 'invalid') {
1798
+ const searchColumnVisibility = getSearchParamsFromColumnVisibility(columnVisibility, columns);
1799
+ if (searchColumnVisibility.toString() !== localStorageColumnsVisibility) {
1800
+ setLocalStorageColumnsVisibility(searchColumnVisibility.toString());
1801
+ }
1802
+ return columnVisibility;
1803
+ }
1804
+ const columnVisibilityFromLocalStorage = getColumnVisibilityFromString(localStorageColumnsVisibility, columns);
1805
+ if (columnVisibilityFromLocalStorage !== 'invalid') {
1806
+ return columnVisibilityFromLocalStorage;
1807
+ }
1808
+ if (initialState !== null && initialState !== void 0 && (_initialState$columns3 = initialState.columns) !== null && _initialState$columns3 !== void 0 && _initialState$columns3.columnVisibilityModel) {
1809
+ persistDefaultVisibility(initialState.columns.columnVisibilityModel);
1810
+ return initialState.columns.columnVisibilityModel;
1811
+ }
1812
+ persistDefaultVisibility(defaultValue);
1813
+ return defaultValue;
1814
+ };
1815
+ const getPinnedColumnsFromString = (notParsed, tableColumns) => {
1816
+ if (!notParsed || notParsed.length === 1 && notParsed[0] === '?') {
1817
+ return 'invalid';
1818
+ }
1819
+ // remove the initial ? if present
1820
+ const parsed = notParsed[0] === '?' ? notParsed.slice(1) : notParsed;
1821
+ const pinnedColumns = {};
1822
+ for (const item of parsed.split('&')) {
1823
+ const fieldURL = item.split('=')[0];
1824
+ if (fieldURL !== '_pinnedColumnsLeft' && fieldURL !== '_pinnedColumnsRight') {
1825
+ continue;
1826
+ }
1827
+ const left = item.split(']')[0];
1828
+ if (left.split('[').length < 2) {
1829
+ continue;
1830
+ }
1831
+ const encodedValues = item.split('[')[1].split(']')[0];
1832
+ if (typeof encodedValues !== 'string') {
1833
+ continue;
1834
+ }
1835
+ const fields = [...tableColumns.map(column => column.field), '__check__'];
1836
+ const columns = encodedValues.split(',').map(value => decodeValue(value)).filter(val => typeof val === 'string' && fields.includes(val));
1837
+ if (fieldURL === '_pinnedColumnsLeft') {
1838
+ pinnedColumns['left'] = columns;
1839
+ }
1840
+ if (fieldURL === '_pinnedColumnsRight') {
1841
+ pinnedColumns['right'] = columns;
1842
+ }
1843
+ }
1844
+ return pinnedColumns.left && pinnedColumns.left.length > 0 || pinnedColumns.right && pinnedColumns.right.length > 0 ? {
1845
+ left: pinnedColumns.left || [],
1846
+ right: pinnedColumns.right || []
1847
+ } : 'invalid';
1848
+ };
1849
+ const getSearchParamsFromPinnedColumns = pinnedColumns => {
1850
+ var _pinnedColumns$left, _pinnedColumns$right;
1851
+ const searchParams = new URLSearchParams();
1852
+ const pinnedColumnLeft = ((_pinnedColumns$left = pinnedColumns.left) === null || _pinnedColumns$left === void 0 ? void 0 : _pinnedColumns$left.map(val => encodeValue(val))) || [];
1853
+ const pinnedColumnRight = ((_pinnedColumns$right = pinnedColumns.right) === null || _pinnedColumns$right === void 0 ? void 0 : _pinnedColumns$right.map(val => encodeValue(val))) || [];
1854
+ searchParams.set('_pinnedColumnsLeft', `[${pinnedColumnLeft.join(',')}]`);
1855
+ searchParams.set('_pinnedColumnsRight', `[${pinnedColumnRight.join(',')}]`);
1856
+ return searchParams;
1857
+ };
1858
+
1859
+ // Rules:
1860
+ // - if we have something in the URL, use that info
1861
+ // - if we don't have that, use the localStorage and update the URL
1862
+ // - if we don't have that, return an empty ColumnVisibilityModel (which is all columns)
1863
+ const getPinnedColumns = (search, columns, localStoragePinnedColumns, setLocalStoragePinnedColumns, initialState, isNewVersion) => {
1864
+ const defaultValue = initialState !== null && initialState !== void 0 && initialState.pinnedColumns ? {
1865
+ left: (initialState === null || initialState === void 0 ? void 0 : initialState.pinnedColumns['left']) || [],
1866
+ right: (initialState === null || initialState === void 0 ? void 0 : initialState.pinnedColumns['right']) || []
1867
+ } : {
1868
+ left: [],
1869
+ right: []
1870
+ };
1871
+ if (isNewVersion) {
1872
+ return defaultValue;
1873
+ }
1874
+ const pinnedColumns = getPinnedColumnsFromString(search, columns);
1875
+ if (pinnedColumns !== 'invalid') {
1876
+ const searchPinnedColumns = getSearchParamsFromPinnedColumns(pinnedColumns);
1877
+ if (searchPinnedColumns.toString() !== localStoragePinnedColumns) {
1878
+ setLocalStoragePinnedColumns(searchPinnedColumns.toString());
1879
+ }
1880
+ return pinnedColumns;
1881
+ }
1882
+ const pinnedColumnsFromLocalStorage = getPinnedColumnsFromString(localStoragePinnedColumns, columns);
1883
+ if (pinnedColumnsFromLocalStorage !== 'invalid') {
1884
+ return pinnedColumnsFromLocalStorage;
1885
+ }
1886
+ return defaultValue;
1887
+ };
1888
+ const getSearchParamsFromTab = search => {
1889
+ const searchParams = new URLSearchParams();
1890
+ const openTab = new URLSearchParams(search).get('tab');
1891
+ if (openTab) {
1892
+ searchParams.set('tab', openTab);
1893
+ }
1894
+ return searchParams;
1895
+ };
1896
+
1897
+ /** DENSITY */
1898
+
1899
+ const VALID_DENSITIES = ['compact', 'standard', 'comfortable'];
1900
+ const getDensityFromString = searchString => {
1901
+ if (!searchString) {
1902
+ return 'invalid';
1903
+ }
1904
+ const searchParams = new URLSearchParams(searchString);
1905
+ const value = searchParams.get('_density');
1906
+ if (value && VALID_DENSITIES.includes(value)) {
1907
+ return value;
1908
+ }
1909
+ return 'invalid';
1910
+ };
1911
+ const getSearchParamsFromDensity = density => {
1912
+ const searchParams = new URLSearchParams();
1913
+ searchParams.set('_density', density);
1914
+ return searchParams;
1915
+ };
1916
+ const getDensityModel = (search, localStorageDensity, setLocalStorageDensity, initialState, isNewVersion) => {
1917
+ // Default density: honour initialState.density if valid, otherwise fall back to 'compact'
1918
+ const initialDensity = initialState === null || initialState === void 0 ? void 0 : initialState.density;
1919
+ const defaultValue = initialDensity && VALID_DENSITIES.includes(initialDensity) ? initialDensity : 'compact';
1920
+
1921
+ // Persist initialState-derived density to localStorage so all three sources stay in sync
1922
+ const persistDefaultDensity = () => {
1923
+ const searchFromDefault = getSearchParamsFromDensity(defaultValue);
1924
+ const searchString = urlSearchParamsToString(searchFromDefault);
1925
+ if (searchString !== localStorageDensity) {
1926
+ setLocalStorageDensity(searchString);
1927
+ }
1928
+ };
1929
+ if (isNewVersion) {
1930
+ persistDefaultDensity();
1931
+ return defaultValue;
1932
+ }
1933
+ const density = getDensityFromString(search);
1934
+ if (density !== 'invalid') {
1935
+ const searchFromDensity = getSearchParamsFromDensity(density);
1936
+ const searchString = urlSearchParamsToString(searchFromDensity);
1937
+ if (searchString !== localStorageDensity) {
1938
+ setLocalStorageDensity(searchString);
1939
+ }
1940
+ return density;
1941
+ }
1942
+ const densityFromLocalStorage = getDensityFromString(localStorageDensity);
1943
+ if (densityFromLocalStorage !== 'invalid') {
1944
+ return densityFromLocalStorage;
1945
+ }
1946
+ persistDefaultDensity();
1947
+ return defaultValue;
1948
+ };
1949
+
1950
+ /** COLUMN ORDER */
1951
+
1952
+ const getColumnOrderFromString = searchString => {
1953
+ if (!searchString) return 'invalid';
1954
+ const searchParams = new URLSearchParams(searchString);
1955
+ const value = searchParams.get('_columnOrder');
1956
+ if (value === '' || value === null || value === '[]') return 'invalid';
1957
+
1958
+ // Handle bracket notation [a,b,c]
1959
+ const inner = value.startsWith('[') && value.endsWith(']') ? value.slice(1, -1) : value;
1960
+ if (!inner) return 'invalid';
1961
+ return inner.split(',').filter(Boolean);
1962
+ };
1963
+ const getSearchParamsFromColumnOrder = columnOrder => {
1964
+ const searchParams = new URLSearchParams();
1965
+ if (columnOrder.length > 0) {
1966
+ searchParams.set('_columnOrder', `[${columnOrder.join(',')}]`);
1967
+ }
1968
+ return searchParams;
1969
+ };
1970
+ const getColumnOrder = (search, columns, localStorageColumnOrder, setLocalStorageColumnOrder, initialState, isNewVersion) => {
1971
+ var _initialState$columns4, _initialState$columns5;
1972
+ const defaultValue = (_initialState$columns4 = initialState === null || initialState === void 0 ? void 0 : (_initialState$columns5 = initialState.columns) === null || _initialState$columns5 === void 0 ? void 0 : _initialState$columns5.orderedFields) !== null && _initialState$columns4 !== void 0 ? _initialState$columns4 : columns.map(c => c.field);
1973
+ const persistDefault = () => {
1974
+ const searchFromDefault = getSearchParamsFromColumnOrder(defaultValue);
1975
+ const searchString = urlSearchParamsToString(searchFromDefault);
1976
+ if (searchString !== localStorageColumnOrder) {
1977
+ setLocalStorageColumnOrder(searchString);
1978
+ }
1979
+ };
1980
+ if (isNewVersion) {
1981
+ persistDefault();
1982
+ return defaultValue;
1983
+ }
1984
+ const fromUrl = getColumnOrderFromString(search);
1985
+ if (fromUrl !== 'invalid') {
1986
+ const searchFromModel = getSearchParamsFromColumnOrder(fromUrl);
1987
+ const searchString = urlSearchParamsToString(searchFromModel);
1988
+ if (searchString !== localStorageColumnOrder) {
1989
+ setLocalStorageColumnOrder(searchString);
1990
+ }
1991
+ return fromUrl;
1992
+ }
1993
+ const fromLocalStorage = getColumnOrderFromString(localStorageColumnOrder);
1994
+ if (fromLocalStorage !== 'invalid') {
1995
+ return fromLocalStorage;
1996
+ }
1997
+ persistDefault();
1998
+ return defaultValue;
1999
+ };
2000
+ const getFinalSearch = _ref => {
2001
+ let {
2002
+ search,
2003
+ localStorageVersion,
2004
+ filterModel,
2005
+ sortModel,
2006
+ paginationModel,
2007
+ columnsVisibilityModel,
2008
+ pinnedColumnsModel,
2009
+ density,
2010
+ columnOrderModel,
2011
+ defaultColumnOrder,
2012
+ columns
2013
+ } = _ref;
2014
+ const filterModelSearch = getSearchParamsFromFilterModel(filterModel);
2015
+ const sortModelSearch = getSearchParamsFromSorting(sortModel);
2016
+ const paginationModelSearch = getSearchParamsFromPagination(paginationModel);
2017
+ const columnVisibilityModelSearch = getSearchParamsFromColumnVisibility(columnsVisibilityModel, columns);
2018
+ const pinnedColumnsModelSearch = getSearchParamsFromPinnedColumns(pinnedColumnsModel);
2019
+ const densitySearch = getSearchParamsFromDensity(density);
2020
+ // Only include _columnOrder in URL when it differs from the default
2021
+ const columnOrderSearch = columnOrderModel.length !== defaultColumnOrder.length || columnOrderModel.some((field, i) => field !== defaultColumnOrder[i]) ? getSearchParamsFromColumnOrder(columnOrderModel) : new URLSearchParams();
2022
+ const tabSearch = getSearchParamsFromTab(search);
2023
+ const searchParams = new URLSearchParams();
2024
+ for (const [key, value] of new URLSearchParams(search)) {
2025
+ if (!key.startsWith('_')) {
2026
+ searchParams.set(key, value);
2027
+ }
2028
+ }
2029
+ searchParams.set('v', `${localStorageVersion}`);
2030
+
2031
+ // Add quickFilterValues explicitly if present in filterModel
2032
+ if (filterModel.quickFilterValues && filterModel.quickFilterValues.length > 0) {
2033
+ // Encode array as JSON string to preserve all values in one param
2034
+ searchParams.set('_quickFilterValues', encodeURIComponent(JSON.stringify(filterModel.quickFilterValues)));
2035
+ }
2036
+ return new URLSearchParams([...searchParams, ...filterModelSearch, ...sortModelSearch, ...paginationModelSearch, ...tabSearch, ...pinnedColumnsModelSearch, ...columnVisibilityModelSearch, ...densitySearch, ...columnOrderSearch]);
2037
+ };
2038
+ /** Return the state of the table given the URL and the local storage state */
2039
+ const getModelsParsedOrUpdateLocalStorage = (search, localStorageVersion, columns, initialState, localStorage) => {
2040
+ var _initialState$columns6, _initialState$columns7;
2041
+ // Decompress any compressed params in the search string before processing
2042
+ const decompressedSearch = decompressSearchParams(search);
2043
+ // Convert from display format (dot notation) to internal format (bracket notation) if needed
2044
+ const decodedSearch = getDecodedSearchFromUrl(decompressedSearch, columns);
2045
+ const decodedParams = new URLSearchParams(decodedSearch);
2046
+ const currentVersion = decodedParams.get('v');
2047
+ // Only treat as "new version reset" when the URL has params with a stale/missing version.
2048
+ // An empty URL should fall through to the localStorage branch of each getter so persisted
2049
+ // state is restored instead of being clobbered by defaults.
2050
+ const hasUrlState = Array.from(decodedParams.keys()).length > 0;
2051
+ const isNewVersion = hasUrlState && (!currentVersion || Number(currentVersion) !== localStorageVersion);
2052
+ const {
2053
+ localStorageFilters,
2054
+ setLocalStorageFilters,
2055
+ localStorageSorting,
2056
+ setLocalStorageSorting,
2057
+ localStoragePagination,
2058
+ setLocalStoragePagination,
2059
+ localStorageColumnsVisibility,
2060
+ setLocalStorageColumnsVisibility,
2061
+ localStoragePinnedColumns,
2062
+ setLocalStoragePinnedColumns,
2063
+ localStorageDensity,
2064
+ setLocalStorageDensity,
2065
+ localStorageColumnOrder,
2066
+ setLocalStorageColumnOrder
2067
+ } = localStorage;
2068
+ const filterModel = getFilterModel(decodedSearch, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion);
2069
+ const sortModel = getSortModel(decodedSearch, columns, localStorageSorting, setLocalStorageSorting, initialState, isNewVersion);
2070
+ const paginationModel = getPaginationModel(decodedSearch, localStoragePagination, setLocalStoragePagination, initialState, isNewVersion);
2071
+ const columnVisibilityModel = getColumnsVisibility(decodedSearch, columns, localStorageColumnsVisibility, setLocalStorageColumnsVisibility, initialState, isNewVersion);
2072
+ const pinnedColumnsModel = getPinnedColumns(decodedSearch, columns, localStoragePinnedColumns, setLocalStoragePinnedColumns, initialState, isNewVersion);
2073
+ const density = getDensityModel(decodedSearch, localStorageDensity, setLocalStorageDensity, initialState, isNewVersion);
2074
+ const columnOrderModel = getColumnOrder(decodedSearch, columns, localStorageColumnOrder, setLocalStorageColumnOrder, initialState, isNewVersion);
2075
+ const defaultColumnOrder = (_initialState$columns6 = initialState === null || initialState === void 0 ? void 0 : (_initialState$columns7 = initialState.columns) === null || _initialState$columns7 === void 0 ? void 0 : _initialState$columns7.orderedFields) !== null && _initialState$columns6 !== void 0 ? _initialState$columns6 : columns.map(c => c.field);
2076
+ const finalSearch = getFinalSearch({
2077
+ localStorageVersion,
2078
+ search: decodedSearch,
2079
+ filterModel,
2080
+ sortModel,
2081
+ paginationModel,
2082
+ columnsVisibilityModel: columnVisibilityModel,
2083
+ pinnedColumnsModel,
2084
+ density,
2085
+ columnOrderModel,
2086
+ defaultColumnOrder,
2087
+ columns
2088
+ });
2089
+ const internalSearchString = urlSearchParamsToString(finalSearch);
2090
+ // Convert to display format for the URL
2091
+ const displaySearchString = buildQueryParamsString(internalSearchString);
2092
+ // Apply budget compression to the display format string
2093
+ const compressedSearchString = applyCompressionToDisplaySearch(displaySearchString);
2094
+ const compressedWithoutQuestion = compressedSearchString.startsWith('?') ? compressedSearchString.slice(1) : compressedSearchString;
2095
+
2096
+ // Compare with original search (without leading ?)
2097
+ const originalSearchWithoutQuestion = search.startsWith('?') ? search.slice(1) : search;
2098
+ const pendingSearch = !areSearchStringsEqual(compressedWithoutQuestion, originalSearchWithoutQuestion) ? compressedWithoutQuestion : null;
2099
+ return {
2100
+ filterModel,
2101
+ sortModel,
2102
+ paginationModel,
2103
+ columnVisibilityModel,
2104
+ pinnedColumnsModel,
2105
+ density,
2106
+ columnOrderModel,
2107
+ pendingSearch
2108
+ };
2109
+ };
2110
+ const updateUrl = (_ref2, search, localStorageVersion, historyReplace, columns) => {
2111
+ let {
2112
+ filterModel,
2113
+ sortModel,
2114
+ paginationModel,
2115
+ columnsModel: columnsVisibilityModel,
2116
+ pinnedColumnsModel,
2117
+ density,
2118
+ columnOrderModel,
2119
+ defaultColumnOrder
2120
+ } = _ref2;
2121
+ // Convert from display format to internal format if needed
2122
+ const decodedSearch = getDecodedSearchFromUrl(search, columns);
2123
+ const newSearch = getFinalSearch({
2124
+ search: decodedSearch,
2125
+ localStorageVersion,
2126
+ filterModel,
2127
+ sortModel,
2128
+ paginationModel,
2129
+ columnsVisibilityModel,
2130
+ pinnedColumnsModel,
2131
+ density,
2132
+ columnOrderModel,
2133
+ defaultColumnOrder,
2134
+ columns
2135
+ });
2136
+ const internalSearchString = urlSearchParamsToString(newSearch);
2137
+ // Convert to display format for the URL
2138
+ const displaySearchString = buildQueryParamsString(internalSearchString);
2139
+ // Apply budget compression to the display format string
2140
+ const compressedSearchString = applyCompressionToDisplaySearch(displaySearchString);
2141
+ const compressedWithoutQuestion = compressedSearchString.startsWith('?') ? compressedSearchString.slice(1) : compressedSearchString;
2142
+
2143
+ // Compare with original search (without leading ?)
2144
+ const originalSearchWithoutQuestion = search.startsWith('?') ? search.slice(1) : search;
2145
+ if (!areSearchStringsEqual(compressedWithoutQuestion, originalSearchWithoutQuestion)) {
2146
+ historyReplace(compressedWithoutQuestion);
2147
+ }
2148
+ };
2149
+
2150
+ // Note: this is a comparator to sort the filters, not pure comparison
2151
+ // do not use it for equivalence (e.g. with value `3` and undefined we
2152
+ // will get 0).
2153
+ const compareFilters = (firstFilter, secondFilter) => {
2154
+ if (firstFilter.field < secondFilter.field) {
2155
+ return -1;
2156
+ } else if (firstFilter.field > secondFilter.field) {
2157
+ return 1;
2158
+ }
2159
+ if (firstFilter.operator === undefined || secondFilter.operator === undefined) {
2160
+ return 0;
2161
+ }
2162
+ if (firstFilter.operator < secondFilter.operator) {
2163
+ return -1;
2164
+ } else if (firstFilter.operator > secondFilter.operator) {
2165
+ return 1;
2166
+ }
2167
+ if (firstFilter.value < secondFilter.value) {
2168
+ return -1;
2169
+ } else if (firstFilter.value > secondFilter.value) {
2170
+ return 1;
2171
+ }
2172
+ return 0;
2173
+ };
2174
+ const areFiltersEquivalent = (firstFilter, secondFilter) => {
2175
+ return firstFilter.field === secondFilter.field && firstFilter.operator === secondFilter.operator && firstFilter.value === secondFilter.value;
2176
+ };
2177
+ const areFilterModelsEquivalent = (filterModel, filterModelToMatch) => {
2178
+ const {
2179
+ items,
2180
+ logicOperator
2181
+ } = filterModel;
2182
+ const {
2183
+ items: itemsToMatch,
2184
+ logicOperator: logicOperatorToMatch
2185
+ } = filterModelToMatch;
2186
+ if (logicOperator !== logicOperatorToMatch) {
2187
+ return false;
2188
+ }
2189
+ if (items.length !== itemsToMatch.length) {
2190
+ return false;
2191
+ }
2192
+ items.sort(compareFilters);
2193
+ itemsToMatch.sort(compareFilters);
2194
+ for (let i = 0; i < items.length; i++) {
2195
+ const filter = items[i];
2196
+ const filterToCompare = itemsToMatch[i];
2197
+
2198
+ // compareFilters return 0 if and only if the filters have the same
2199
+ // field, operator, and value
2200
+ if (!areFiltersEquivalent(filter, filterToCompare)) {
2201
+ return false;
2202
+ }
2203
+ }
2204
+ return true;
2205
+ };
2206
+
2207
+ /**
2208
+ * Decompress any `~`-prefixed param values in a search string.
2209
+ * Also handles the `_filters` aggregate param: expands it back into individual filter params.
2210
+ */
2211
+ const decompressSearchParams = search => {
2212
+ if (!search || !search.includes('~')) return search;
2213
+ const cleanSearch = search.startsWith('?') ? search.slice(1) : search;
2214
+ const params = new URLSearchParams(cleanSearch);
2215
+ const result = new URLSearchParams();
2216
+ for (const [key, value] of params) {
2217
+ if (key === '_filters' && isCompressed(value)) {
2218
+ // Aggregate filter: decompress JSON back into individual filter params
2219
+ try {
2220
+ const json = decompressValue(value);
2221
+ const filterRecord = JSON.parse(json);
2222
+ for (const [filterKey, filterValue] of Object.entries(filterRecord)) {
2223
+ result.set(filterKey, filterValue);
2224
+ }
2225
+ } catch {
2226
+ // If decompression fails, keep the original
2227
+ result.set(key, value);
2228
+ }
2229
+ } else {
2230
+ result.set(key, decompressValue(value));
2231
+ }
2232
+ }
2233
+ return '?' + result.toString();
2234
+ };
2235
+
2236
+ /**
2237
+ * Apply budget compression to a display-format search string.
2238
+ * Parses the string into a Map, runs applyBudgetCompression, and reassembles.
2239
+ */
2240
+ const applyCompressionToDisplaySearch = displaySearch => {
2241
+ const cleanSearch = displaySearch.startsWith('?') ? displaySearch.slice(1) : displaySearch;
2242
+ if (!cleanSearch) return displaySearch;
2243
+ const params = new Map();
2244
+ for (const part of cleanSearch.split('&')) {
2245
+ const eqIndex = part.indexOf('=');
2246
+ if (eqIndex === -1) continue;
2247
+ const key = part.slice(0, eqIndex);
2248
+ const value = part.slice(eqIndex + 1);
2249
+ params.set(key, value);
2250
+ }
2251
+ const compressed = applyBudgetCompression(params);
2252
+ const parts = [];
2253
+ for (const [key, value] of compressed) {
2254
+ parts.push(`${key}=${value}`);
2255
+ }
2256
+ return '?' + parts.join('&');
2257
+ };
2258
+
2259
+ // Get and Set data from LocalStorage WITHOUT useState
2260
+ const useFetchState = (defaultValue, key) => {
2261
+ let stickyValue = null;
2262
+ try {
2263
+ stickyValue = window.localStorage.getItem(key);
2264
+ } catch (e) {
2265
+ console.error('StatefulDataGrid: error getting item from local storage: ', e);
2266
+ }
2267
+ const parsedValue = stickyValue !== null && stickyValue !== undefined && stickyValue !== 'undefined' ? JSON.parse(stickyValue) : defaultValue;
2268
+ const updateValue = useCallback(value => {
2269
+ try {
2270
+ window.localStorage.setItem(key, JSON.stringify(value));
2271
+ } catch (e) {
2272
+ console.error('StatefulDataGrid: error setting item into local storage: ', e);
2273
+ }
2274
+ }, [key]);
2275
+ return [parsedValue, updateValue];
2276
+ };
2277
+
2278
+ const useTableStates = (id, version) => {
2279
+ const [paginationModel, setPaginationModel] = useFetchState('', buildStorageKey({
2280
+ id,
2281
+ version,
2282
+ category: PAGINATION_MODEL_KEY
2283
+ }));
2284
+ const [sortModel, setSortModel] = useFetchState('', buildStorageKey({
2285
+ id,
2286
+ version,
2287
+ category: SORT_MODEL_KEY
2288
+ }));
2289
+ const [localStorageFilters, setLocalStorageFilters] = useFetchState('', buildStorageKey({
2290
+ id,
2291
+ version,
2292
+ category: FILTER_SEARCH_KEY
2293
+ }));
2294
+ const [visibilityModelLocalStorage, setVisibilityModelLocalStorage] = useFetchState('', buildStorageKey({
2295
+ id,
2296
+ version,
2297
+ category: VISIBILITY_MODEL_KEY
2298
+ }));
2299
+ const [pinnedColumns, setPinnedColumns] = useFetchState('_pinnedColumnsLeft=[]&_pinnedColumnsRight=[]', buildStorageKey({
2300
+ id,
2301
+ version,
2302
+ category: PINNED_COLUMNS
2303
+ }));
2304
+ const [dimensionModel, setDimensionModel] = useFetchState({}, buildStorageKey({
2305
+ id,
2306
+ version,
2307
+ category: DIMENSION_MODEL_KEY
2308
+ }));
2309
+ const [densityModel, setDensityModel] = useFetchState('', buildStorageKey({
2310
+ id,
2311
+ version,
2312
+ category: DENSITY_MODEL_KEY
2313
+ }));
2314
+ const [columnOrderModel, setColumnOrderModel] = useFetchState('', buildStorageKey({
2315
+ id,
2316
+ version,
2317
+ category: COLUMN_ORDER_MODEL_KEY
2318
+ }));
2319
+ return {
2320
+ paginationModel,
2321
+ setPaginationModel,
2322
+ sortModel,
2323
+ setSortModel,
2324
+ localStorageFilters,
2325
+ setLocalStorageFilters,
2326
+ visibilityModelLocalStorage,
2327
+ setVisibilityModelLocalStorage,
2328
+ pinnedColumns,
2329
+ setPinnedColumns,
2330
+ dimensionModel,
2331
+ setDimensionModel,
2332
+ densityModel,
2333
+ setDensityModel,
2334
+ columnOrderModel,
2335
+ setColumnOrderModel
2336
+ };
2337
+ };
2338
+
2339
+ /**
2340
+ * Deep-equal comparison for plain objects / arrays.
2341
+ * Used to stabilise parsed model references so that MUI v8 does not
2342
+ * reset pagination on every render.
2343
+ */
2344
+ function isDeepEqual(a, b) {
2345
+ if (a === b) return true;
2346
+ if (a == null || b == null) return false;
2347
+ if (typeof a !== typeof b) return false;
2348
+ if (typeof a !== 'object') return false;
2349
+ const aObj = a;
2350
+ const bObj = b;
2351
+ const aKeys = Object.keys(aObj);
2352
+ const bKeys = Object.keys(bObj);
2353
+ if (aKeys.length !== bKeys.length) return false;
2354
+ return aKeys.every(key => isDeepEqual(aObj[key], bObj[key]));
2355
+ }
2356
+ const useStatefulTable = props => {
2357
+ var _initialState$columns, _initialState$columns2;
2358
+ const {
2359
+ apiRef,
2360
+ initialState,
2361
+ columns: propsColumns,
2362
+ onColumnVisibilityModelChange: propsOnColumnVisibilityModelChange,
2363
+ onColumnWidthChange: propsOnColumnWidthChange,
2364
+ onFilterModelChange: propsOnFilterModelChange,
2365
+ onPaginationModelChange: propsOnPaginationModelChange,
2366
+ onPinnedColumnsChange: propsOnPinnedColumnsChange,
2367
+ onSortModelChange: propsOnSortModelChange,
2368
+ useRouter,
2369
+ localStorageVersion = 1,
2370
+ previousLocalStorageVersions = []
2371
+ } = props;
2372
+ const {
2373
+ search,
2374
+ pathname,
2375
+ historyReplace
2376
+ } = useRouter();
2377
+ const id = pathname;
2378
+
2379
+ // States and setters persisted in the local storage for this table
2380
+ const {
2381
+ paginationModel,
2382
+ setPaginationModel,
2383
+ sortModel,
2384
+ setSortModel,
2385
+ localStorageFilters,
2386
+ setLocalStorageFilters,
2387
+ visibilityModelLocalStorage,
2388
+ setVisibilityModelLocalStorage,
2389
+ pinnedColumns,
2390
+ setPinnedColumns,
2391
+ dimensionModel,
2392
+ setDimensionModel,
2393
+ densityModel,
2394
+ setDensityModel,
2395
+ columnOrderModel: localStorageColumnOrder,
2396
+ setColumnOrderModel: setLocalStorageColumnOrder
2397
+ } = useTableStates(id, localStorageVersion);
2398
+
2399
+ // clearing up old version keys, triggering only on first render
2400
+ useEffect(() => clearPreviousVersionStorage(id, previousLocalStorageVersions), [id, previousLocalStorageVersions]);
2401
+ const onColumnDimensionChange = useCallback(_ref => {
2402
+ let {
2403
+ newWidth,
2404
+ field
2405
+ } = _ref;
2406
+ setDimensionModel(_objectSpread2(_objectSpread2({}, dimensionModel), {}, {
2407
+ [field]: newWidth
2408
+ }));
2409
+ }, [dimensionModel, setDimensionModel]);
2410
+ const {
2411
+ filterModel: filterParsed,
2412
+ sortModel: sortModelParsed,
2413
+ paginationModel: paginationModelParsed,
2414
+ columnVisibilityModel: visibilityModel,
2415
+ pinnedColumnsModel,
2416
+ density: densityParsed,
2417
+ columnOrderModel: columnOrderParsed,
2418
+ pendingSearch
2419
+ } = getModelsParsedOrUpdateLocalStorage(search || '', localStorageVersion, propsColumns, initialState, {
2420
+ localStorageFilters,
2421
+ setLocalStorageFilters,
2422
+ localStorageSorting: sortModel,
2423
+ setLocalStorageSorting: setSortModel,
2424
+ localStoragePagination: paginationModel,
2425
+ setLocalStoragePagination: setPaginationModel,
2426
+ localStorageColumnsVisibility: visibilityModelLocalStorage,
2427
+ setLocalStorageColumnsVisibility: setVisibilityModelLocalStorage,
2428
+ localStoragePinnedColumns: pinnedColumns,
2429
+ setLocalStoragePinnedColumns: setPinnedColumns,
2430
+ localStorageDensity: densityModel,
2431
+ setLocalStorageDensity: setDensityModel,
2432
+ localStorageColumnOrder,
2433
+ setLocalStorageColumnOrder
2434
+ });
2435
+
2436
+ // Sync URL in an effect rather than during render to comply with React rules
2437
+ useEffect(() => {
2438
+ if (pendingSearch !== null) {
2439
+ historyReplace(pendingSearch);
2440
+ }
2441
+ }, [pendingSearch, historyReplace]);
2442
+
2443
+ // Stabilise parsed model references to prevent MUI v8 from resetting
2444
+ // pagination on every render due to new object identity.
2445
+ const filterParsedRef = useRef(filterParsed);
2446
+ if (!isDeepEqual(filterParsedRef.current, filterParsed)) {
2447
+ filterParsedRef.current = filterParsed;
2448
+ }
2449
+ const sortModelParsedRef = useRef(sortModelParsed);
2450
+ if (!isDeepEqual(sortModelParsedRef.current, sortModelParsed)) {
2451
+ sortModelParsedRef.current = sortModelParsed;
2452
+ }
2453
+ const paginationModelParsedRef = useRef(paginationModelParsed);
2454
+ if (!isDeepEqual(paginationModelParsedRef.current, paginationModelParsed)) {
2455
+ paginationModelParsedRef.current = paginationModelParsed;
2456
+ }
2457
+ const visibilityModelRef = useRef(visibilityModel);
2458
+ if (!isDeepEqual(visibilityModelRef.current, visibilityModel)) {
2459
+ visibilityModelRef.current = visibilityModel;
2460
+ }
2461
+ const pinnedColumnsModelRef = useRef(pinnedColumnsModel);
2462
+ if (!isDeepEqual(pinnedColumnsModelRef.current, pinnedColumnsModel)) {
2463
+ pinnedColumnsModelRef.current = pinnedColumnsModel;
2464
+ }
2465
+ const columnOrderParsedRef = useRef(columnOrderParsed);
2466
+ if (!isDeepEqual(columnOrderParsedRef.current, columnOrderParsed)) {
2467
+ columnOrderParsedRef.current = columnOrderParsed;
2468
+ }
2469
+ const columns = useMemo(() => propsColumns.map(column => {
2470
+ return _objectSpread2(_objectSpread2({}, column), {}, {
2471
+ width: dimensionModel[column.field] || column.width || 100
2472
+ });
2473
+ }), [propsColumns, dimensionModel]);
2474
+ if (apiRef.current) {
2475
+ /** Add resetPage method to apiRef. */
2476
+ apiRef.current.resetPage = () => {
2477
+ var _apiRef$current;
2478
+ (_apiRef$current = apiRef.current) === null || _apiRef$current === void 0 ? void 0 : _apiRef$current.setPage(0);
2479
+ };
2480
+ }
2481
+ const defaultColumnOrder = (_initialState$columns = initialState === null || initialState === void 0 ? void 0 : (_initialState$columns2 = initialState.columns) === null || _initialState$columns2 === void 0 ? void 0 : _initialState$columns2.orderedFields) !== null && _initialState$columns !== void 0 ? _initialState$columns : propsColumns.map(c => c.field);
2482
+
2483
+ // Subscribe to density changes via stateChange event (MUI v6 has no densityChange event)
2484
+ useEffect(() => {
2485
+ const api = apiRef.current;
2486
+ if (!(api !== null && api !== void 0 && api.subscribeEvent)) return;
2487
+ let prevDensity = densityParsed;
2488
+ const unsub = api.subscribeEvent('stateChange', () => {
2489
+ const currentDensity = api.state.density;
2490
+ if (currentDensity !== prevDensity) {
2491
+ prevDensity = currentDensity;
2492
+ updateUrl({
2493
+ filterModel: filterParsed,
2494
+ sortModel: sortModelParsed,
2495
+ paginationModel: paginationModelParsed,
2496
+ columnsModel: api.state.columns.columnVisibilityModel,
2497
+ pinnedColumnsModel: pinnedColumnsModel,
2498
+ density: currentDensity,
2499
+ columnOrderModel: columnOrderParsed,
2500
+ defaultColumnOrder
2501
+ }, search, localStorageVersion, historyReplace, columns);
2502
+ }
2503
+ });
2504
+ return unsub;
2505
+ }, [apiRef, densityParsed, filterParsed, sortModelParsed, paginationModelParsed, pinnedColumnsModel, columnOrderParsed, defaultColumnOrder, search, localStorageVersion, historyReplace, columns]);
2506
+
2507
+ // Subscribe to column order changes via columnOrderChange (drag-drop) and columnIndexChange (programmatic setColumnIndex)
2508
+ useEffect(() => {
2509
+ const api = apiRef.current;
2510
+ if (!(api !== null && api !== void 0 && api.subscribeEvent)) return;
2511
+ const handleColumnOrderChange = () => {
2512
+ const orderedFields = api.state.columns.orderedFields;
2513
+ if (orderedFields && !isDeepEqual(orderedFields, columnOrderParsed)) {
2514
+ updateUrl({
2515
+ filterModel: filterParsed,
2516
+ sortModel: sortModelParsed,
2517
+ paginationModel: paginationModelParsed,
2518
+ columnsModel: api.state.columns.columnVisibilityModel,
2519
+ pinnedColumnsModel,
2520
+ density: densityParsed,
2521
+ columnOrderModel: orderedFields,
2522
+ defaultColumnOrder
2523
+ }, search, localStorageVersion, historyReplace, columns);
2524
+ }
2525
+ };
2526
+ const unsub1 = api.subscribeEvent('columnOrderChange', handleColumnOrderChange);
2527
+ const unsub2 = api.subscribeEvent('columnIndexChange', handleColumnOrderChange);
2528
+ return () => {
2529
+ unsub1();
2530
+ unsub2();
2531
+ };
2532
+ }, [apiRef, columnOrderParsed, defaultColumnOrder, filterParsed, sortModelParsed, paginationModelParsed, pinnedColumnsModel, densityParsed, search, localStorageVersion, historyReplace, columns]);
2533
+
2534
+ // Helper to build the current DataGridModel for updateUrl calls
2535
+ const buildModel = function () {
2536
+ var _apiRef$current$state, _apiRef$current2;
2537
+ let overrides = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2538
+ return _objectSpread2({
2539
+ filterModel: filterParsed,
2540
+ sortModel: sortModelParsed,
2541
+ paginationModel: paginationModelParsed,
2542
+ columnsModel: (_apiRef$current$state = (_apiRef$current2 = apiRef.current) === null || _apiRef$current2 === void 0 ? void 0 : _apiRef$current2.state.columns.columnVisibilityModel) !== null && _apiRef$current$state !== void 0 ? _apiRef$current$state : {},
2543
+ pinnedColumnsModel: pinnedColumnsModel,
2544
+ density: densityParsed,
2545
+ columnOrderModel: columnOrderParsed,
2546
+ defaultColumnOrder
2547
+ }, overrides);
2548
+ };
2549
+
2550
+ // Track last emitted values for deep-equal guards to avoid feedback loops.
2551
+ // Initialised from the current parsed values; updated only when we actually fire.
2552
+ const lastEmittedFilterRef = useRef(filterParsed);
2553
+ const lastEmittedSortRef = useRef(sortModelParsed);
2554
+ const lastEmittedPaginationRef = useRef(paginationModelParsed);
2555
+ return {
2556
+ apiRef,
2557
+ columns,
2558
+ density: densityParsed,
2559
+ columnOrderModel: columnOrderParsedRef.current,
2560
+ onFilterModelChange: (model, details) => {
2561
+ const filterModel = _objectSpread2(_objectSpread2({}, model), {}, {
2562
+ items: model.items.map(item => {
2563
+ var _apiRef$current3;
2564
+ const column = (_apiRef$current3 = apiRef.current) === null || _apiRef$current3 === void 0 ? void 0 : _apiRef$current3.getColumn(item.field);
2565
+ item.type = (column === null || column === void 0 ? void 0 : column.type) || 'string';
2566
+ return item;
2567
+ }),
2568
+ quickFilterValues: model.quickFilterValues || []
2569
+ });
2570
+ if (isDeepEqual(filterModel, lastEmittedFilterRef.current)) return;
2571
+ lastEmittedFilterRef.current = filterModel;
2572
+ updateUrl(buildModel({
2573
+ filterModel
2574
+ }), search, localStorageVersion, historyReplace, columns);
2575
+ propsOnFilterModelChange === null || propsOnFilterModelChange === void 0 ? void 0 : propsOnFilterModelChange(filterModel, details);
2576
+ },
2577
+ filterModel: filterParsedRef.current,
2578
+ onSortModelChange: (model, details) => {
2579
+ if (isDeepEqual(model, lastEmittedSortRef.current)) return;
2580
+ lastEmittedSortRef.current = model;
2581
+ updateUrl(buildModel({
2582
+ sortModel: model
2583
+ }), search, localStorageVersion, historyReplace, columns);
2584
+ propsOnSortModelChange === null || propsOnSortModelChange === void 0 ? void 0 : propsOnSortModelChange(model, details);
2585
+ },
2586
+ sortModel: sortModelParsedRef.current,
2587
+ onPinnedColumnsChange: (pinnedColumns, details) => {
2588
+ updateUrl(buildModel({
2589
+ pinnedColumnsModel: pinnedColumns
2590
+ }), search, localStorageVersion, historyReplace, columns);
2591
+ propsOnPinnedColumnsChange === null || propsOnPinnedColumnsChange === void 0 ? void 0 : propsOnPinnedColumnsChange(pinnedColumns, details);
2592
+ },
2593
+ pinnedColumns: pinnedColumnsModelRef.current,
2594
+ paginationModel: paginationModelParsedRef.current,
2595
+ onPaginationModelChange: (model, details) => {
2596
+ const paginationModel = _objectSpread2(_objectSpread2({}, model), {}, {
2597
+ direction: paginationModelParsed.page < model.page ? 'next' : 'back'
2598
+ });
2599
+ if (isDeepEqual(paginationModel, lastEmittedPaginationRef.current)) return;
2600
+ lastEmittedPaginationRef.current = paginationModel;
2601
+ updateUrl(buildModel({
2602
+ paginationModel
2603
+ }), search, localStorageVersion, historyReplace, columns);
2604
+ propsOnPaginationModelChange === null || propsOnPaginationModelChange === void 0 ? void 0 : propsOnPaginationModelChange(paginationModel, details);
2605
+ },
2606
+ columnVisibilityModel: visibilityModelRef.current,
2607
+ onColumnVisibilityModelChange: (columnsVisibilityModel, details) => {
2608
+ updateUrl(buildModel({
2609
+ columnsModel: columnsVisibilityModel
2610
+ }), search, localStorageVersion, historyReplace, columns);
2611
+ propsOnColumnVisibilityModelChange === null || propsOnColumnVisibilityModelChange === void 0 ? void 0 : propsOnColumnVisibilityModelChange(columnsVisibilityModel, details);
2612
+ },
2613
+ onColumnWidthChange: (params, event, details) => {
2614
+ propsOnColumnWidthChange === null || propsOnColumnWidthChange === void 0 ? void 0 : propsOnColumnWidthChange(params, event, details);
2615
+ onColumnDimensionChange({
2616
+ newWidth: params.width,
2617
+ field: params.colDef.field
2618
+ });
2619
+ }
2620
+ };
2621
+ };
2622
+
2623
+ const _excluded = ["apiRef", "autoHeight", "className", "columns", "slots", "slotProps", "filterModel", "columnVisibilityModel", "pinnedColumns", "sortModel", "paginationModel", "height", "hideToolbar", "initialState", "isRowSelectable", "license", "localStorageVersion", "previousLocalStorageVersions", "onFilterModelChange", "rowSelectionModel", "onColumnWidthChange", "onPaginationModelChange", "onRowSelectionModelChange", "onColumnVisibilityModelChange", "onPinnedColumnsChange", "onSortModelChange", "pagination", "paginationPlacement", "paginationProps", "rows", "pageSizeOptions", "sx", "theme", "useRouter", "paginationMode", "rowCount", "density", "dataSource", "filterMode", "sortingMode"];
2624
+ const COMPONENT_NAME = 'DataGrid';
2625
+ const CLASSNAME = 'redsift-datagrid';
2626
+
2627
+ /**
2628
+ * StatefulDataGrid extends DataGrid with automatic state persistence to localStorage.
2629
+ * Preserves filters, sorting, pagination, column visibility, and column widths
2630
+ * across page refreshes.
2631
+ *
2632
+ * Use when users need persistent table preferences. Requires unique `localStorageKey`.
2633
+ *
2634
+ * @example
2635
+ * // Basic stateful grid (persists to localStorage)
2636
+ * <StatefulDataGrid
2637
+ * localStorageKey="users-table"
2638
+ * columns={columns}
2639
+ * rows={users}
2640
+ * pagination
2641
+ * />
2642
+ *
2643
+ * @example
2644
+ * // With version migration (clears old preferences)
2645
+ * <StatefulDataGrid
2646
+ * localStorageKey="reports-table"
2647
+ * localStorageVersion="v2"
2648
+ * previousLocalStorageVersions={['v1']}
2649
+ * columns={columns}
2650
+ * rows={reports}
2651
+ * />
2652
+ *
2653
+ * @example
2654
+ * // With URL state sync (shareable filtered views)
2655
+ * <StatefulDataGrid
2656
+ * localStorageKey="domains-table"
2657
+ * useRouter={true}
2658
+ * columns={columns}
2659
+ * rows={domains}
2660
+ * pagination
2661
+ * />
2662
+ *
2663
+ * @example
2664
+ * // All features combined
2665
+ * <StatefulDataGrid
2666
+ * localStorageKey="audit-log"
2667
+ * localStorageVersion="v3"
2668
+ * previousLocalStorageVersions={['v1', 'v2']}
2669
+ * useRouter={true}
2670
+ * columns={columns}
2671
+ * rows={logs}
2672
+ * pagination
2673
+ * pageSize={50}
2674
+ * checkboxSelection
2675
+ * />
2676
+ */
2677
+
2678
+ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
2679
+ const datagridRef = ref || useRef();
2680
+ const {
2681
+ apiRef: propsApiRef,
2682
+ autoHeight,
2683
+ className,
2684
+ columns,
2685
+ slots,
2686
+ slotProps,
2687
+ filterModel: propsFilterModel,
2688
+ columnVisibilityModel: propsColumnVisibilityModel,
2689
+ pinnedColumns: propsPinnedColumns,
2690
+ sortModel: propsSortModel,
2691
+ paginationModel: propsPaginationModel,
2692
+ height: propsHeight,
2693
+ hideToolbar,
2694
+ initialState,
2695
+ isRowSelectable,
2696
+ license = process.env.MUI_LICENSE_KEY,
2697
+ localStorageVersion,
2698
+ previousLocalStorageVersions,
2699
+ onFilterModelChange: propsOnFilterModelChange,
2700
+ rowSelectionModel: propsRowSelectionModel,
2701
+ onColumnWidthChange: propsOnColumnWidthChange,
2702
+ onPaginationModelChange: propsOnPaginationModelChange,
2703
+ onRowSelectionModelChange: propsOnRowSelectionModelChange,
2704
+ onColumnVisibilityModelChange: propsOnColumnVisibilityModelChange,
2705
+ onPinnedColumnsChange: propsOnPinnedColumnsChange,
2706
+ onSortModelChange: propsOnSortModelChange,
2707
+ pagination,
2708
+ paginationPlacement = 'both',
2709
+ paginationProps,
2710
+ rows,
2711
+ pageSizeOptions,
2712
+ sx,
2713
+ theme: propsTheme,
2714
+ useRouter,
2715
+ paginationMode = 'client',
2716
+ rowCount,
2717
+ density: _density,
2718
+ dataSource,
2719
+ filterMode: propsFilterMode,
2720
+ sortingMode: propsSortingMode
2721
+ } = props,
2722
+ forwardedProps = _objectWithoutProperties(props, _excluded);
2723
+ const theme = useTheme(propsTheme);
2724
+ const _apiRef = useGridApiRef();
2725
+ const apiRef = propsApiRef !== null && propsApiRef !== void 0 ? propsApiRef : _apiRef;
2726
+ LicenseInfo.setLicenseKey(license);
2727
+ const height = propsHeight !== null && propsHeight !== void 0 ? propsHeight : autoHeight ? undefined : '500px';
2728
+
2729
+ // When dataSource is present, MUI manages filter/sort/pagination internally.
2730
+ // We must not pass controlled models — only initialState (one-time) and
2731
+ // write-only onChange handlers for URL/localStorage persistence.
2732
+ const isDataSourceMode = Boolean(dataSource);
2733
+ const effectivePaginationMode = isDataSourceMode ? 'server' : paginationMode;
2734
+ const effectiveFilterMode = isDataSourceMode ? 'server' : propsFilterMode;
2735
+ const effectiveSortingMode = isDataSourceMode ? 'server' : propsSortingMode;
2736
+ const {
2737
+ onColumnVisibilityModelChange: controlledOnColumnVisibilityModelChange,
2738
+ onFilterModelChange: controlledOnFilterModelChange,
2739
+ onPaginationModelChange: controlledOnPaginationModelChange,
2740
+ onPinnedColumnsChange: controlledOnPinnedColumnsChange,
2741
+ onSortModelChange: controlledOnSortModelChange
2742
+ } = useControlledDatagridState({
2743
+ initialState,
2744
+ pageSizeOptions,
2745
+ propsColumnVisibilityModel,
2746
+ propsFilterModel,
2747
+ propsOnColumnVisibilityModelChange,
2748
+ propsOnFilterModelChange,
2749
+ propsOnPinnedColumnsChange,
2750
+ propsOnSortModelChange,
2751
+ propsPaginationModel,
2752
+ propsPinnedColumns,
2753
+ propsSortModel,
2754
+ propsOnPaginationModelChange
2755
+ });
2756
+ const {
2757
+ columnVisibilityModel,
2758
+ density: controlledDensity,
2759
+ filterModel,
2760
+ onColumnVisibilityModelChange,
2761
+ onFilterModelChange,
2762
+ onPaginationModelChange,
2763
+ onPinnedColumnsChange,
2764
+ onSortModelChange,
2765
+ paginationModel,
2766
+ pinnedColumns,
2767
+ sortModel,
2768
+ onColumnWidthChange,
2769
+ columnOrderModel
2770
+ } = useStatefulTable({
2771
+ apiRef: apiRef,
2772
+ initialState,
2773
+ columns,
2774
+ onColumnVisibilityModelChange: controlledOnColumnVisibilityModelChange,
2775
+ onColumnWidthChange: propsOnColumnWidthChange,
2776
+ onFilterModelChange: controlledOnFilterModelChange,
2777
+ onPaginationModelChange: controlledOnPaginationModelChange,
2778
+ onPinnedColumnsChange: controlledOnPinnedColumnsChange,
2779
+ onSortModelChange: controlledOnSortModelChange,
2780
+ useRouter: useRouter,
2781
+ localStorageVersion,
2782
+ previousLocalStorageVersions
2783
+ });
2784
+
2785
+ // In dataSource mode, track pagination locally for the custom pagination slots
2786
+ // (rendered outside DataGridPro). MUI owns the actual pagination state internally.
2787
+ const [dataSourcePaginationModel, setDataSourcePaginationModel] = useState(paginationModel);
2788
+
2789
+ // The pagination model to use for display in pagination slots
2790
+ const activePaginationModel = isDataSourceMode ? dataSourcePaginationModel : paginationModel;
2791
+
2792
+ // Wrap onPaginationModelChange to also track state locally in dataSource mode
2793
+ const wrappedOnPaginationModelChange = useCallback((model, details) => {
2794
+ if (isDataSourceMode) {
2795
+ setDataSourcePaginationModel(model);
2796
+ }
2797
+ onPaginationModelChange(model, details);
2798
+ }, [isDataSourceMode, onPaginationModelChange]);
2799
+
2800
+ // In dataSource mode, pagination changes from our custom pagination slots
2801
+ // (rendered outside MUI's pagination state) route through apiRef so MUI's
2802
+ // internal page state updates and dataSource.getRows() refetches with the
2803
+ // new params. The `paginationModelChange` subscription below picks up the
2804
+ // resulting state change and propagates it to URL/localStorage and local
2805
+ // React state via wrappedOnPaginationModelChange.
2806
+ const dataSourcePaginationChange = useCallback(model => {
2807
+ var _apiRef$current;
2808
+ (_apiRef$current = apiRef.current) === null || _apiRef$current === void 0 ? void 0 : _apiRef$current.setPaginationModel(model);
2809
+ }, [apiRef]);
2810
+
2811
+ // In dataSource mode, subscribe to MUI's `paginationModelChange` event so
2812
+ // URL state stays in sync with MUI's internal pagination regardless of how
2813
+ // it changed (slot click, apiRef.setPaginationModel from consumer code,
2814
+ // MUI internal updates, etc.). Relying on MUI's `onPaginationModelChange`
2815
+ // prop callback alone is not sufficient: in pivot/GroupedData strategy mode
2816
+ // and with `paginationModel` seeded via `initialState` (rather than as a
2817
+ // controlled prop), the prop callback can be missed under certain
2818
+ // re-render orderings. The event fires reliably whenever the internal
2819
+ // state changes via `setState('setPaginationModel')`, see
2820
+ // `useGridStateInitialization.setState` → `publishEvent(changeEvent, …)`.
2821
+ // The deep-equal guard inside `useStatefulTable.onPaginationModelChange`
2822
+ // dedupes any duplicate emits, so overlap with the prop callback is safe.
2823
+ useEffect(() => {
2824
+ if (!isDataSourceMode) return;
2825
+ const api = apiRef.current;
2826
+ if (!(api !== null && api !== void 0 && api.subscribeEvent)) return;
2827
+ return api.subscribeEvent('paginationModelChange', model => {
2828
+ wrappedOnPaginationModelChange({
2829
+ page: model.page,
2830
+ pageSize: model.pageSize
2831
+ }, {
2832
+ reason: 'paginationModelChange'
2833
+ });
2834
+ });
2835
+ }, [isDataSourceMode, apiRef, wrappedOnPaginationModelChange]);
2836
+ const [rowSelectionModel, setRowSelectionModel] = useState(() => normalizeRowSelectionModel(propsRowSelectionModel));
2837
+ useEffect(() => {
2838
+ setRowSelectionModel(normalizeRowSelectionModel(propsRowSelectionModel));
2839
+ }, [propsRowSelectionModel]);
2840
+ const onRowSelectionModelChange = (selectionModel, details) => {
2841
+ setRowSelectionModel(selectionModel);
2842
+ propsOnRowSelectionModelChange === null || propsOnRowSelectionModelChange === void 0 ? void 0 : propsOnRowSelectionModelChange(selectionModel, details);
2843
+ };
2844
+ const selectionStatusRef = useRef({
2845
+ type: 'none',
2846
+ numberOfSelectedRows: 0,
2847
+ numberOfSelectedRowsInPage: 0,
2848
+ page: paginationModel.page,
2849
+ pageSize: paginationModel.pageSize
2850
+ });
2851
+
2852
+ // Version counter to force re-renders when selectionStatus ref changes
2853
+ const [, forceSelectionUpdate] = useState(0);
2854
+
2855
+ // The checkboxSelectionVisibleOnly should only be applied to client-side pagination,
2856
+ // for server-side pagination it produces inconsistent behavior when selecting all rows in pages 2 and beyond
2857
+ const checkboxSelectionVisibleOnly = Boolean(pagination) && Boolean(effectivePaginationMode != 'server');
2858
+
2859
+ // Track when the grid API is ready to ensure top pagination renders correctly
2860
+ const [gridReady, setGridReady] = useState(false);
2861
+
2862
+ // Force re-render when the grid API becomes ready (for top pagination)
2863
+ useEffect(() => {
2864
+ if (apiRef.current && !gridReady) {
2865
+ setGridReady(true);
2866
+ }
2867
+ });
2868
+
2869
+ // Sync persisted density via apiRef — initialState only applies on mount,
2870
+ // so this handles SPA back/forward navigation where controlledDensity changes after mount
2871
+ useEffect(() => {
2872
+ if (apiRef.current) {
2873
+ apiRef.current.setDensity(controlledDensity);
2874
+ }
2875
+ }, [controlledDensity, apiRef]);
2876
+
2877
+ // in server-side pagination we want to update the selection status
2878
+ // every time we navigate between pages, resize our page or select something
2879
+ useEffect(() => {
2880
+ if (effectivePaginationMode == 'server') {
2881
+ onServerSideSelectionStatusChange(rowSelectionModel, apiRef, selectionStatusRef, forceSelectionUpdate, isRowSelectable, activePaginationModel.page, activePaginationModel.pageSize);
2882
+ }
2883
+ }, [rowSelectionModel, activePaginationModel.page, activePaginationModel.pageSize, rows]);
2884
+
2885
+ // In dataSource mode MUI provides rows internally; skip the guard.
2886
+ if (!isDataSourceMode && !Array.isArray(rows)) {
2887
+ return null;
2888
+ }
2889
+
2890
+ // Compute selection status synchronously during render for client-side pagination.
2891
+ // This value is passed directly to ToolbarWrapper and ControlledPagination, so slots
2892
+ // receive the fresh value in the same render cycle — no extra re-render needed.
2893
+ // The ref is kept in sync for the onRowSelectionModelChange callback's deselect logic.
2894
+ let selectionStatus = selectionStatusRef.current;
2895
+ if (pagination && effectivePaginationMode !== 'server' && getSelectionCount(rowSelectionModel) > 0) {
2896
+ try {
2897
+ // Use manual page slicing instead of gridPaginatedVisibleSorted* selectors.
2898
+ // MUI's paginated selectors use apiRef internal state which may be stale when
2899
+ // paginationModel prop changes — our React state is always up to date.
2900
+ const allFilteredEntries = gridFilteredSortedRowEntriesSelector(apiRef);
2901
+ const pageStart = activePaginationModel.page * activePaginationModel.pageSize;
2902
+ const pageEntries = allFilteredEntries.slice(pageStart, pageStart + activePaginationModel.pageSize);
2903
+ const selectableRowsInPage = isRowSelectable ? pageEntries.filter(_ref => {
2904
+ let {
2905
+ model
2906
+ } = _ref;
2907
+ return isRowSelectable({
2908
+ row: model
2909
+ });
2910
+ }).map(_ref2 => {
2911
+ let {
2912
+ id
2913
+ } = _ref2;
2914
+ return id;
2915
+ }) : pageEntries.map(_ref3 => {
2916
+ let {
2917
+ id
2918
+ } = _ref3;
2919
+ return id;
2920
+ });
2921
+ const numberOfSelectableRowsInPage = selectableRowsInPage.length;
2922
+ const selectableRowsInTable = isRowSelectable ? allFilteredEntries.filter(_ref4 => {
2923
+ let {
2924
+ model
2925
+ } = _ref4;
2926
+ return isRowSelectable({
2927
+ row: model
2928
+ });
2929
+ }).map(_ref5 => {
2930
+ let {
2931
+ id
2932
+ } = _ref5;
2933
+ return id;
2934
+ }) : gridFilteredSortedRowIdsSelector(apiRef);
2935
+ const numberOfSelectableRowsInTable = selectableRowsInTable.length;
2936
+ const numberOfSelectedRows = getSelectionCount(rowSelectionModel);
2937
+ const selectedOnCurrentPage = selectableRowsInPage.filter(id => isRowSelected(rowSelectionModel, id));
2938
+ if (numberOfSelectedRows === numberOfSelectableRowsInTable && numberOfSelectableRowsInPage < numberOfSelectableRowsInTable) {
2939
+ selectionStatus = {
2940
+ type: 'table',
2941
+ numberOfSelectedRows
2942
+ };
2943
+ } else if (selectedOnCurrentPage.length === numberOfSelectableRowsInPage && numberOfSelectableRowsInPage > 0 && numberOfSelectableRowsInPage < numberOfSelectableRowsInTable) {
2944
+ selectionStatus = {
2945
+ type: 'page',
2946
+ numberOfSelectedRows: selectedOnCurrentPage.length
2947
+ };
2948
+ } else if (numberOfSelectedRows > 0) {
2949
+ selectionStatus = {
2950
+ type: 'other',
2951
+ numberOfSelectedRows
2952
+ };
2953
+ } else {
2954
+ selectionStatus = {
2955
+ type: 'none',
2956
+ numberOfSelectedRows: 0
2957
+ };
2958
+ }
2959
+ } catch {
2960
+ // apiRef may not be initialized on first render
2961
+ }
2962
+ } else if (pagination && effectivePaginationMode !== 'server') {
2963
+ selectionStatus = {
2964
+ type: 'none',
2965
+ numberOfSelectedRows: 0
2966
+ };
2967
+ }
2968
+ selectionStatusRef.current = selectionStatus;
2969
+ const muiTheme = useMemo(() => createTheme({
2970
+ palette: {
2971
+ mode: theme,
2972
+ primary: {
2973
+ main: RedsiftColorBlueN
2974
+ },
2975
+ background: {
2976
+ default: theme === 'dark' ? RedsiftColorNeutralXDarkGrey : RedsiftColorNeutralWhite,
2977
+ paper: theme === 'dark' ? RedsiftColorNeutralXDarkGrey : RedsiftColorNeutralWhite
2978
+ }
2979
+ }
2980
+ }), [theme]);
2981
+ return /*#__PURE__*/React__default.createElement(ThemeProvider, {
2982
+ value: {
2983
+ theme
2984
+ }
2985
+ }, /*#__PURE__*/React__default.createElement(ThemeProvider$1, {
2986
+ theme: muiTheme
2987
+ }, /*#__PURE__*/React__default.createElement(StyledDataGrid, {
2988
+ ref: datagridRef,
2989
+ className: classNames(StatefulDataGrid.className, className),
2990
+ $height: height
2991
+ }, pagination && ['top', 'both'].includes(paginationPlacement) && gridReady ? effectivePaginationMode == 'server' ? /*#__PURE__*/React__default.createElement(ServerSideControlledPagination, {
2992
+ displaySelection: true,
2993
+ displayRowsPerPage: ['top', 'both'].includes(paginationPlacement),
2994
+ displayPagination: ['top', 'both'].includes(paginationPlacement),
2995
+ selectionStatus: selectionStatus,
2996
+ paginationModel: activePaginationModel,
2997
+ onPaginationModelChange: isDataSourceMode ? dataSourcePaginationChange : onPaginationModelChange,
2998
+ pageSizeOptions: pageSizeOptions,
2999
+ paginationProps: paginationProps,
3000
+ rowCount: rowCount
3001
+ }) : /*#__PURE__*/React__default.createElement(ControlledPagination, {
3002
+ displaySelection: true,
3003
+ displayRowsPerPage: ['top', 'both'].includes(paginationPlacement),
3004
+ displayPagination: ['top', 'both'].includes(paginationPlacement),
3005
+ selectionStatus: selectionStatus,
3006
+ apiRef: apiRef,
3007
+ isRowSelectable: isRowSelectable,
3008
+ paginationModel: activePaginationModel,
3009
+ onPaginationModelChange: onPaginationModelChange,
3010
+ pageSizeOptions: pageSizeOptions,
3011
+ paginationProps: paginationProps
3012
+ }) : null, /*#__PURE__*/React__default.createElement(DataGridPro, _extends({}, forwardedProps, {
3013
+ apiRef: apiRef,
3014
+ dataSource: dataSource,
3015
+ columns: columns,
3016
+ onColumnVisibilityModelChange: onColumnVisibilityModelChange,
3017
+ onPinnedColumnsChange: onPinnedColumnsChange,
3018
+ pageSizeOptions: pageSizeOptions,
3019
+ onColumnWidthChange: onColumnWidthChange
3020
+ // In dataSource mode: models are uncontrolled (MUI owns them),
3021
+ // onChange handlers are write-only for URL/localStorage persistence,
3022
+ // and initialState seeds MUI on mount from the persisted URL state.
3023
+ // columnVisibilityModel / pinnedColumns are also uncontrolled here
3024
+ // to avoid a controlled re-render race with consumer-side
3025
+ // microtask-deferred history updates (otherwise user toggles flip
3026
+ // back when MUI re-emits with the stale controlled value).
3027
+ // Consumers needing programmatic changes should use the apiRef
3028
+ // imperative API.
3029
+ }, isDataSourceMode ? {
3030
+ onFilterModelChange: onFilterModelChange,
3031
+ onSortModelChange: onSortModelChange,
3032
+ onPaginationModelChange: wrappedOnPaginationModelChange,
3033
+ initialState: _objectSpread2(_objectSpread2({}, initialState), {}, {
3034
+ density: controlledDensity,
3035
+ columns: _objectSpread2(_objectSpread2({}, initialState === null || initialState === void 0 ? void 0 : initialState.columns), {}, {
3036
+ orderedFields: columnOrderModel,
3037
+ columnVisibilityModel
3038
+ }),
3039
+ pinnedColumns,
3040
+ filter: {
3041
+ filterModel
3042
+ },
3043
+ sorting: {
3044
+ sortModel
3045
+ },
3046
+ pagination: {
3047
+ paginationModel
3048
+ }
3049
+ })
3050
+ } : {
3051
+ columnVisibilityModel,
3052
+ pinnedColumns,
3053
+ filterModel,
3054
+ sortModel,
3055
+ paginationModel,
3056
+ onFilterModelChange: onFilterModelChange,
3057
+ onSortModelChange: onSortModelChange,
3058
+ onPaginationModelChange: wrappedOnPaginationModelChange,
3059
+ initialState: _objectSpread2(_objectSpread2({}, initialState), {}, {
3060
+ density: controlledDensity,
3061
+ columns: _objectSpread2(_objectSpread2({}, initialState === null || initialState === void 0 ? void 0 : initialState.columns), {}, {
3062
+ orderedFields: columnOrderModel
3063
+ })
3064
+ })
3065
+ }, {
3066
+ isRowSelectable: isRowSelectable,
3067
+ pagination: pagination,
3068
+ paginationMode: effectivePaginationMode,
3069
+ filterMode: effectiveFilterMode,
3070
+ sortingMode: effectiveSortingMode,
3071
+ keepNonExistentRowsSelected: effectivePaginationMode == 'server',
3072
+ rows: isDataSourceMode ? [] : rows,
3073
+ rowCount: rowCount,
3074
+ autoHeight: autoHeight,
3075
+ checkboxSelectionVisibleOnly: checkboxSelectionVisibleOnly,
3076
+ disableRowSelectionExcludeModel: true,
3077
+ showToolbar: !hideToolbar,
3078
+ slots: _objectSpread2(_objectSpread2({
3079
+ baseButton: BaseButton,
3080
+ baseCheckbox: BaseCheckbox,
3081
+ baseIconButton: BaseIconButton,
3082
+ columnFilteredIcon: props => /*#__PURE__*/React__default.createElement(BaseIcon, _extends({}, props, {
3083
+ displayName: "columnFilteredIcon"
3084
+ })),
3085
+ columnSelectorIcon: props => /*#__PURE__*/React__default.createElement(BaseIcon, _extends({}, props, {
3086
+ displayName: "columnSelectorIcon"
3087
+ })),
3088
+ columnSortedAscendingIcon: props => /*#__PURE__*/React__default.createElement(BaseIcon, _extends({}, props, {
3089
+ displayName: "columnSortedAscendingIcon"
3090
+ })),
3091
+ columnSortedDescendingIcon: props => /*#__PURE__*/React__default.createElement(BaseIcon, _extends({}, props, {
3092
+ displayName: "columnSortedDescendingIcon"
3093
+ })),
3094
+ densityCompactIcon: props => /*#__PURE__*/React__default.createElement(BaseIcon, _extends({}, props, {
3095
+ displayName: "densityCompactIcon"
3096
+ })),
3097
+ densityStandardIcon: props => /*#__PURE__*/React__default.createElement(BaseIcon, _extends({}, props, {
3098
+ displayName: "densityStandardIcon"
3099
+ })),
3100
+ densityComfortableIcon: props => /*#__PURE__*/React__default.createElement(BaseIcon, _extends({}, props, {
3101
+ displayName: "densityComfortableIcon"
3102
+ })),
3103
+ detailPanelCollapseIcon: props => /*#__PURE__*/React__default.createElement(BaseIcon, _extends({}, props, {
3104
+ displayName: "detailPanelCollapseIcon"
3105
+ })),
3106
+ detailPanelExpandIcon: props => /*#__PURE__*/React__default.createElement(BaseIcon, _extends({}, props, {
3107
+ displayName: "detailPanelExpandIcon"
3108
+ })),
3109
+ exportIcon: props => /*#__PURE__*/React__default.createElement(BaseIcon, _extends({}, props, {
3110
+ displayName: "exportIcon"
3111
+ })),
3112
+ openFilterButtonIcon: props => /*#__PURE__*/React__default.createElement(BaseIcon, _extends({}, props, {
3113
+ displayName: "openFilterButtonIcon"
3114
+ }))
3115
+ }, slots), {}, {
3116
+ pagination: props => {
3117
+ return pagination ? effectivePaginationMode == 'server' ? /*#__PURE__*/React__default.createElement(ServerSideControlledPagination, _extends({}, props, {
3118
+ displaySelection: false,
3119
+ displayRowsPerPage: ['bottom', 'both'].includes(paginationPlacement),
3120
+ displayPagination: ['bottom', 'both'].includes(paginationPlacement),
3121
+ selectionStatus: selectionStatus,
3122
+ paginationModel: activePaginationModel
3123
+ // In dataSource mode route through apiRef so MUI's internal pagination
3124
+ // state stays in sync with the displayed model and dataSource.getRows()
3125
+ // re-fetches with the new params. MUI then fires onPaginationModelChange
3126
+ // via the prop, which updates URL/localStorage via useStatefulTable.
3127
+ // Without this the bottom slot only updated local React state + URL,
3128
+ // leaving MUI on its previous internal page (no refetch, stale display).
3129
+ ,
3130
+ onPaginationModelChange: isDataSourceMode ? dataSourcePaginationChange : wrappedOnPaginationModelChange,
3131
+ pageSizeOptions: pageSizeOptions,
3132
+ paginationProps: paginationProps,
3133
+ rowCount: rowCount
3134
+ })) : /*#__PURE__*/React__default.createElement(ControlledPagination, _extends({}, props, {
3135
+ displaySelection: false,
3136
+ displayRowsPerPage: ['bottom', 'both'].includes(paginationPlacement),
3137
+ displayPagination: ['bottom', 'both'].includes(paginationPlacement),
3138
+ selectionStatus: selectionStatus,
3139
+ apiRef: apiRef,
3140
+ isRowSelectable: isRowSelectable,
3141
+ paginationModel: activePaginationModel,
3142
+ onPaginationModelChange: wrappedOnPaginationModelChange,
3143
+ pageSizeOptions: pageSizeOptions,
3144
+ paginationProps: paginationProps
3145
+ })) : null;
3146
+ }
3147
+ }),
3148
+ slotProps: _objectSpread2({}, slotProps),
3149
+ rowSelectionModel: rowSelectionModel,
3150
+ onRowSelectionModelChange: (newSelectionModel, details) => {
3151
+ if (pagination && effectivePaginationMode != 'server') {
3152
+ // Use manual page slicing instead of gridPaginatedVisibleSorted* selectors
3153
+ // to avoid stale apiRef pagination state.
3154
+ const allFilteredEntries = gridFilteredSortedRowEntriesSelector(apiRef);
3155
+ const pageStart = activePaginationModel.page * activePaginationModel.pageSize;
3156
+ const pageEntries = allFilteredEntries.slice(pageStart, pageStart + activePaginationModel.pageSize);
3157
+ const selectableRowsInPage = isRowSelectable ? pageEntries.filter(_ref6 => {
3158
+ let {
3159
+ model
3160
+ } = _ref6;
3161
+ return isRowSelectable({
3162
+ row: model
3163
+ });
3164
+ }).map(_ref7 => {
3165
+ let {
3166
+ id
3167
+ } = _ref7;
3168
+ return id;
3169
+ }) : pageEntries.map(_ref8 => {
3170
+ let {
3171
+ id
3172
+ } = _ref8;
3173
+ return id;
3174
+ });
3175
+ const numberOfSelectableRowsInPage = selectableRowsInPage.length;
3176
+ const selectableRowsInTable = isRowSelectable ? allFilteredEntries.filter(_ref9 => {
3177
+ let {
3178
+ model
3179
+ } = _ref9;
3180
+ return isRowSelectable({
3181
+ row: model
3182
+ });
3183
+ }).map(_ref10 => {
3184
+ let {
3185
+ id
3186
+ } = _ref10;
3187
+ return id;
3188
+ }) : gridFilteredSortedRowIdsSelector(apiRef);
3189
+ const numberOfSelectableRowsInTable = selectableRowsInTable.length;
3190
+ const numberOfSelectedRows = getSelectionCount(newSelectionModel);
3191
+ if (selectionStatusRef.current.type === 'table' && numberOfSelectedRows === numberOfSelectableRowsInTable - numberOfSelectableRowsInPage || selectionStatusRef.current.type === 'table' && numberOfSelectedRows === numberOfSelectableRowsInTable || selectionStatusRef.current.type === 'page' && numberOfSelectedRows === numberOfSelectableRowsInPage) {
3192
+ setTimeout(() => {
3193
+ var _apiRef$current2;
3194
+ (_apiRef$current2 = apiRef.current) === null || _apiRef$current2 === void 0 ? void 0 : _apiRef$current2.selectRows([], true, true);
3195
+ }, 0);
3196
+ }
3197
+ if (numberOfSelectedRows === numberOfSelectableRowsInPage && numberOfSelectableRowsInPage < numberOfSelectableRowsInTable) {
3198
+ selectionStatusRef.current = {
3199
+ type: 'page',
3200
+ numberOfSelectedRows
3201
+ };
3202
+ } else if (numberOfSelectedRows === numberOfSelectableRowsInTable && numberOfSelectableRowsInPage < numberOfSelectableRowsInTable) {
3203
+ selectionStatusRef.current = {
3204
+ type: 'table',
3205
+ numberOfSelectedRows
3206
+ };
3207
+ } else if (numberOfSelectedRows > 0) {
3208
+ selectionStatusRef.current = {
3209
+ type: 'other',
3210
+ numberOfSelectedRows
3211
+ };
3212
+ } else {
3213
+ selectionStatusRef.current = {
3214
+ type: 'none',
3215
+ numberOfSelectedRows
3216
+ };
3217
+ }
3218
+ forceSelectionUpdate(v => v + 1);
3219
+ }
3220
+ onRowSelectionModelChange === null || onRowSelectionModelChange === void 0 ? void 0 : onRowSelectionModelChange(newSelectionModel, details);
3221
+ },
3222
+ sx: _objectSpread2(_objectSpread2({}, sx), {}, {
3223
+ '.MuiDataGrid-columnHeaders': {
3224
+ flexDirection: 'column',
3225
+ alignItems: 'normal'
3226
+ },
3227
+ '.MuiDataGrid-selectedRowCount': {
3228
+ margin: 'none'
3229
+ }
3230
+ })
3231
+ })))));
3232
+ });
3233
+ StatefulDataGrid.className = CLASSNAME;
3234
+ StatefulDataGrid.displayName = COMPONENT_NAME;
3235
+
3236
+ export { convertToDisplayFormat as $, ARRAY_IS_EMPTY as A, IS as B, CONTAINS_ANY_OF as C, DOES_NOT_CONTAIN as D, ENDS_WITH_ANY_OF as E, IS_NOT as F, getGridStringOperators as G, HAS_WITH_SELECT as H, IS_ANY_OF as I, getGridStringArrayOperators as J, getGridStringArrayOperatorsWithSelect as K, getGridStringArrayOperatorsWithSelectOnStringArrayColumns as L, FILTER_MODEL_KEY as M, SORT_MODEL_KEY as N, PINNED_COLUMNS as O, PAGINATION_MODEL_KEY as P, DIMENSION_MODEL_KEY as Q, FILTER_SEARCH_KEY as R, STARTS_WITH_ANY_OF as S, DENSITY_MODEL_KEY as T, COLUMN_ORDER_MODEL_KEY as U, VISIBILITY_MODEL_KEY as V, CATEGORIES as W, buildStorageKey as X, clearPreviousVersionStorage as Y, clearAllVersionStorage as Z, resetStatefulDataGridState as _, DOES_NOT_EQUAL as a, convertFromDisplayFormat as a0, getDecodedSearchFromUrl as a1, buildQueryParamsString as a2, areSearchStringsEqual as a3, decodeValue as a4, encodeValue as a5, urlSearchParamsToString as a6, numberOperatorEncoder as a7, numberOperatorDecoder as a8, isOperatorValueValid as a9, isValueValid as aa, getFilterModelFromString as ab, getSearchParamsFromFilterModel as ac, getSortingFromString as ad, getSearchParamsFromSorting as ae, getPaginationFromString as af, getSearchParamsFromPagination as ag, getColumnVisibilityFromString as ah, getSearchParamsFromColumnVisibility as ai, getPinnedColumnsFromString as aj, getSearchParamsFromPinnedColumns as ak, getSearchParamsFromTab as al, getDensityFromString as am, getSearchParamsFromDensity as an, getDensityModel as ao, getColumnOrderFromString as ap, getSearchParamsFromColumnOrder as aq, getFinalSearch as ar, getModelsParsedOrUpdateLocalStorage as as, updateUrl as at, areFilterModelsEquivalent as au, StatefulDataGrid as av, DOES_NOT_START_WITH as b, DOES_NOT_END_WITH as c, IS_NOT_ANY_OF as d, DOES_NOT_CONTAIN_ANY_OF as e, DOES_NOT_START_WITH_ANY_OF as f, DOES_NOT_END_WITH_ANY_OF as g, IS_BETWEEN as h, IS_WITH_SELECT as i, IS_NOT_WITH_SELECT as j, IS_ANY_OF_WITH_SELECT as k, IS_NOT_ANY_OF_WITH_SELECT as l, ARRAY_IS_NOT_EMPTY as m, DOES_NOT_HAVE_WITH_SELECT as n, operatorList as o, HAS_ANY_OF_WITH_SELECT as p, HAS_ALL_OF_WITH_SELECT as q, DOES_NOT_HAVE_ANY_OF_WITH_SELECT as r, HAS_ONLY_WITH_SELECT as s, HAS as t, DOES_NOT_HAVE as u, HAS_ANY_OF as v, HAS_ALL_OF as w, DOES_NOT_HAVE_ANY_OF as x, HAS_ONLY as y, getGridNumericOperators as z };
3237
+ //# sourceMappingURL=StatefulDataGrid2.js.map