@redsift/table 12.5.13-muiv6 → 12.5.13

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.
@@ -1,1248 +1,18 @@
1
- import { _ as _objectSpread2, a as _objectWithoutProperties, b as _extends$2 } from './_rollupPluginBabelHelpers.js';
1
+ import { _ as _objectSpread2, a as _objectWithoutProperties, b as _extends } from './_rollupPluginBabelHelpers.js';
2
2
  import * as React from 'react';
3
- import React__default, { useCallback, useEffect, useMemo, useRef, forwardRef, useState } 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';
4
5
  import classNames from 'classnames';
5
- import { Icon, useTheme as useTheme$1, ThemeProvider, RedsiftColorBlueN, RedsiftColorNeutralXDarkGrey, RedsiftColorNeutralWhite } from '@redsift/design-system';
6
- import { getGridNumericOperators as getGridNumericOperators$1, GridFilterInputValue, GridFilterInputSingleSelect, GridFilterInputMultipleValue, GridFilterInputMultipleSingleSelect, getGridStringOperators as getGridStringOperators$1, getGridBooleanOperators, getGridDateOperators, getGridSingleSelectOperators, GridLogicOperator, useGridApiRef, gridPaginatedVisibleSortedGridRowEntriesSelector, gridPaginatedVisibleSortedGridRowIdsSelector, gridFilteredSortedRowEntriesSelector, gridFilteredSortedRowIdsSelector, DataGridPro } from '@mui/x-data-grid-pro';
7
- import { L as LicenseInfo, u as useControlledDatagridState, T as ThemeProvider$1, S as StyledDataGrid, B as BottomPagination, b as baseGridSlots } from './useControlledDatagridState.js';
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, DataGridPremium } from '@mui/x-data-grid-premium';
9
+ import { u as useControlledDatagridState, S as StyledDataGrid, B as BottomPagination, b as baseGridSlots, a as BelowToolbar } from './useControlledDatagridState.js';
10
+ import Box from '@mui/material/Box';
11
+ import TextField from '@mui/material/TextField';
8
12
  import { mdiSync } from '@redsift/icons';
9
- import { s as styled, a as styleFunctionSx, e as extendSxProp, _ as _objectWithoutPropertiesLoose, b as _extends, g as generateUtilityClasses, c as createTheme, T as THEME_ID, C as ClassNameGenerator, P as PropTypes, d as generateUtilityClass, f as styled$1, h as _extends$1, u as useDefaultProps, i as _objectWithoutPropertiesLoose$1, j as capitalize, k as composeClasses, r as rootShouldForwardProp, l as refType } from './Portal.js';
10
- import { u as useTheme, a as useFormControl, b as formControlState, c as clsx$1, i as isAdornedStart, d as isFilled, F as FormControlContext, e as useId, h as Select, I as Input, j as FilledInput, O as OutlinedInput, o as onServerSideSelectionStatusChange } from './ControlledPagination.js';
11
- import { j as jsxRuntimeExports } from './jsx-runtime.js';
12
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';
13
15
  import { T as Toolbar } from './Toolbar2.js';
14
- import { T as ToolbarWrapper } from './ToolbarWrapper2.js';
15
-
16
- function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
17
-
18
- const _excluded$6 = ["className", "component"];
19
- function createBox(options = {}) {
20
- const {
21
- themeId,
22
- defaultTheme,
23
- defaultClassName = 'MuiBox-root',
24
- generateClassName
25
- } = options;
26
- const BoxRoot = styled('div', {
27
- shouldForwardProp: prop => prop !== 'theme' && prop !== 'sx' && prop !== 'as'
28
- })(styleFunctionSx);
29
- const Box = /*#__PURE__*/React.forwardRef(function Box(inProps, ref) {
30
- const theme = useTheme(defaultTheme);
31
- const _extendSxProp = extendSxProp(inProps),
32
- {
33
- className,
34
- component = 'div'
35
- } = _extendSxProp,
36
- other = _objectWithoutPropertiesLoose(_extendSxProp, _excluded$6);
37
- return /*#__PURE__*/jsxRuntimeExports.jsx(BoxRoot, _extends({
38
- as: component,
39
- ref: ref,
40
- className: clsx(className, generateClassName ? generateClassName(defaultClassName) : defaultClassName),
41
- theme: themeId ? theme[themeId] || theme : theme
42
- }, other));
43
- });
44
- return Box;
45
- }
46
-
47
- function isMuiElement(element, muiNames) {
48
- var _muiName, _element$type;
49
- return /*#__PURE__*/React.isValidElement(element) && muiNames.indexOf( // For server components `muiName` is avaialble in element.type._payload.value.muiName
50
- // relevant info - https://github.com/facebook/react/blob/2807d781a08db8e9873687fccc25c0f12b4fb3d4/packages/react/src/ReactLazy.js#L45
51
- // eslint-disable-next-line no-underscore-dangle
52
- (_muiName = element.type.muiName) != null ? _muiName : (_element$type = element.type) == null || (_element$type = _element$type._payload) == null || (_element$type = _element$type.value) == null ? void 0 : _element$type.muiName) !== -1;
53
- }
54
-
55
- const boxClasses = generateUtilityClasses('MuiBox', ['root']);
56
- var boxClasses$1 = boxClasses;
57
-
58
- const defaultTheme = createTheme();
59
- const Box = createBox({
60
- themeId: THEME_ID,
61
- defaultTheme,
62
- defaultClassName: boxClasses$1.root,
63
- generateClassName: ClassNameGenerator.generate
64
- });
65
- process.env.NODE_ENV !== "production" ? Box.propTypes /* remove-proptypes */ = {
66
- // ┌────────────────────────────── Warning ──────────────────────────────┐
67
- // │ These PropTypes are generated from the TypeScript type definitions. │
68
- // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
69
- // └─────────────────────────────────────────────────────────────────────┘
70
- /**
71
- * @ignore
72
- */
73
- children: PropTypes.node,
74
- /**
75
- * The component used for the root node.
76
- * Either a string to use a HTML element or a component.
77
- */
78
- component: PropTypes.elementType,
79
- /**
80
- * The system prop that allows defining system overrides as well as additional CSS styles.
81
- */
82
- sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object])
83
- } : void 0;
84
- var Box$1 = Box;
85
-
86
- function getFormLabelUtilityClasses(slot) {
87
- return generateUtilityClass('MuiFormLabel', slot);
88
- }
89
- const formLabelClasses = generateUtilityClasses('MuiFormLabel', ['root', 'colorSecondary', 'focused', 'disabled', 'error', 'filled', 'required', 'asterisk']);
90
- var formLabelClasses$1 = formLabelClasses;
91
-
92
- const _excluded$5 = ["children", "className", "color", "component", "disabled", "error", "filled", "focused", "required"];
93
- const useUtilityClasses$4 = ownerState => {
94
- const {
95
- classes,
96
- color,
97
- focused,
98
- disabled,
99
- error,
100
- filled,
101
- required
102
- } = ownerState;
103
- const slots = {
104
- root: ['root', `color${capitalize(color)}`, disabled && 'disabled', error && 'error', filled && 'filled', focused && 'focused', required && 'required'],
105
- asterisk: ['asterisk', error && 'error']
106
- };
107
- return composeClasses(slots, getFormLabelUtilityClasses, classes);
108
- };
109
- const FormLabelRoot = styled$1('label', {
110
- name: 'MuiFormLabel',
111
- slot: 'Root',
112
- overridesResolver: ({
113
- ownerState
114
- }, styles) => {
115
- return _extends$1({}, styles.root, ownerState.color === 'secondary' && styles.colorSecondary, ownerState.filled && styles.filled);
116
- }
117
- })(({
118
- theme,
119
- ownerState
120
- }) => _extends$1({
121
- color: (theme.vars || theme).palette.text.secondary
122
- }, theme.typography.body1, {
123
- lineHeight: '1.4375em',
124
- padding: 0,
125
- position: 'relative',
126
- [`&.${formLabelClasses$1.focused}`]: {
127
- color: (theme.vars || theme).palette[ownerState.color].main
128
- },
129
- [`&.${formLabelClasses$1.disabled}`]: {
130
- color: (theme.vars || theme).palette.text.disabled
131
- },
132
- [`&.${formLabelClasses$1.error}`]: {
133
- color: (theme.vars || theme).palette.error.main
134
- }
135
- }));
136
- const AsteriskComponent = styled$1('span', {
137
- name: 'MuiFormLabel',
138
- slot: 'Asterisk',
139
- overridesResolver: (props, styles) => styles.asterisk
140
- })(({
141
- theme
142
- }) => ({
143
- [`&.${formLabelClasses$1.error}`]: {
144
- color: (theme.vars || theme).palette.error.main
145
- }
146
- }));
147
- const FormLabel = /*#__PURE__*/React.forwardRef(function FormLabel(inProps, ref) {
148
- const props = useDefaultProps({
149
- props: inProps,
150
- name: 'MuiFormLabel'
151
- });
152
- const {
153
- children,
154
- className,
155
- component = 'label'
156
- } = props,
157
- other = _objectWithoutPropertiesLoose$1(props, _excluded$5);
158
- const muiFormControl = useFormControl();
159
- const fcs = formControlState({
160
- props,
161
- muiFormControl,
162
- states: ['color', 'required', 'focused', 'disabled', 'error', 'filled']
163
- });
164
- const ownerState = _extends$1({}, props, {
165
- color: fcs.color || 'primary',
166
- component,
167
- disabled: fcs.disabled,
168
- error: fcs.error,
169
- filled: fcs.filled,
170
- focused: fcs.focused,
171
- required: fcs.required
172
- });
173
- const classes = useUtilityClasses$4(ownerState);
174
- return /*#__PURE__*/jsxRuntimeExports.jsxs(FormLabelRoot, _extends$1({
175
- as: component,
176
- ownerState: ownerState,
177
- className: clsx$1(classes.root, className),
178
- ref: ref
179
- }, other, {
180
- children: [children, fcs.required && /*#__PURE__*/jsxRuntimeExports.jsxs(AsteriskComponent, {
181
- ownerState: ownerState,
182
- "aria-hidden": true,
183
- className: classes.asterisk,
184
- children: ["\u2009", '*']
185
- })]
186
- }));
187
- });
188
- process.env.NODE_ENV !== "production" ? FormLabel.propTypes /* remove-proptypes */ = {
189
- // ┌────────────────────────────── Warning ──────────────────────────────┐
190
- // │ These PropTypes are generated from the TypeScript type definitions. │
191
- // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
192
- // └─────────────────────────────────────────────────────────────────────┘
193
- /**
194
- * The content of the component.
195
- */
196
- children: PropTypes.node,
197
- /**
198
- * Override or extend the styles applied to the component.
199
- */
200
- classes: PropTypes.object,
201
- /**
202
- * @ignore
203
- */
204
- className: PropTypes.string,
205
- /**
206
- * The color of the component.
207
- * It supports both default and custom theme colors, which can be added as shown in the
208
- * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors).
209
- */
210
- color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'warning']), PropTypes.string]),
211
- /**
212
- * The component used for the root node.
213
- * Either a string to use a HTML element or a component.
214
- */
215
- component: PropTypes.elementType,
216
- /**
217
- * If `true`, the label should be displayed in a disabled state.
218
- */
219
- disabled: PropTypes.bool,
220
- /**
221
- * If `true`, the label is displayed in an error state.
222
- */
223
- error: PropTypes.bool,
224
- /**
225
- * If `true`, the label should use filled classes key.
226
- */
227
- filled: PropTypes.bool,
228
- /**
229
- * If `true`, the input of this label is focused (used by `FormGroup` components).
230
- */
231
- focused: PropTypes.bool,
232
- /**
233
- * If `true`, the label will indicate that the `input` is required.
234
- */
235
- required: PropTypes.bool,
236
- /**
237
- * The system prop that allows defining system overrides as well as additional CSS styles.
238
- */
239
- sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object])
240
- } : void 0;
241
- var FormLabel$1 = FormLabel;
242
-
243
- function getInputLabelUtilityClasses(slot) {
244
- return generateUtilityClass('MuiInputLabel', slot);
245
- }
246
- generateUtilityClasses('MuiInputLabel', ['root', 'focused', 'disabled', 'error', 'required', 'asterisk', 'formControl', 'sizeSmall', 'shrink', 'animated', 'standard', 'filled', 'outlined']);
247
-
248
- const _excluded$4 = ["disableAnimation", "margin", "shrink", "variant", "className"];
249
- const useUtilityClasses$3 = ownerState => {
250
- const {
251
- classes,
252
- formControl,
253
- size,
254
- shrink,
255
- disableAnimation,
256
- variant,
257
- required
258
- } = ownerState;
259
- const slots = {
260
- root: ['root', formControl && 'formControl', !disableAnimation && 'animated', shrink && 'shrink', size && size !== 'normal' && `size${capitalize(size)}`, variant],
261
- asterisk: [required && 'asterisk']
262
- };
263
- const composedClasses = composeClasses(slots, getInputLabelUtilityClasses, classes);
264
- return _extends$1({}, classes, composedClasses);
265
- };
266
- const InputLabelRoot = styled$1(FormLabel$1, {
267
- shouldForwardProp: prop => rootShouldForwardProp(prop) || prop === 'classes',
268
- name: 'MuiInputLabel',
269
- slot: 'Root',
270
- overridesResolver: (props, styles) => {
271
- const {
272
- ownerState
273
- } = props;
274
- return [{
275
- [`& .${formLabelClasses$1.asterisk}`]: styles.asterisk
276
- }, styles.root, ownerState.formControl && styles.formControl, ownerState.size === 'small' && styles.sizeSmall, ownerState.shrink && styles.shrink, !ownerState.disableAnimation && styles.animated, ownerState.focused && styles.focused, styles[ownerState.variant]];
277
- }
278
- })(({
279
- theme,
280
- ownerState
281
- }) => _extends$1({
282
- display: 'block',
283
- transformOrigin: 'top left',
284
- whiteSpace: 'nowrap',
285
- overflow: 'hidden',
286
- textOverflow: 'ellipsis',
287
- maxWidth: '100%'
288
- }, ownerState.formControl && {
289
- position: 'absolute',
290
- left: 0,
291
- top: 0,
292
- // slight alteration to spec spacing to match visual spec result
293
- transform: 'translate(0, 20px) scale(1)'
294
- }, ownerState.size === 'small' && {
295
- // Compensation for the `Input.inputSizeSmall` style.
296
- transform: 'translate(0, 17px) scale(1)'
297
- }, ownerState.shrink && {
298
- transform: 'translate(0, -1.5px) scale(0.75)',
299
- transformOrigin: 'top left',
300
- maxWidth: '133%'
301
- }, !ownerState.disableAnimation && {
302
- transition: theme.transitions.create(['color', 'transform', 'max-width'], {
303
- duration: theme.transitions.duration.shorter,
304
- easing: theme.transitions.easing.easeOut
305
- })
306
- }, ownerState.variant === 'filled' && _extends$1({
307
- // Chrome's autofill feature gives the input field a yellow background.
308
- // Since the input field is behind the label in the HTML tree,
309
- // the input field is drawn last and hides the label with an opaque background color.
310
- // zIndex: 1 will raise the label above opaque background-colors of input.
311
- zIndex: 1,
312
- pointerEvents: 'none',
313
- transform: 'translate(12px, 16px) scale(1)',
314
- maxWidth: 'calc(100% - 24px)'
315
- }, ownerState.size === 'small' && {
316
- transform: 'translate(12px, 13px) scale(1)'
317
- }, ownerState.shrink && _extends$1({
318
- userSelect: 'none',
319
- pointerEvents: 'auto',
320
- transform: 'translate(12px, 7px) scale(0.75)',
321
- maxWidth: 'calc(133% - 24px)'
322
- }, ownerState.size === 'small' && {
323
- transform: 'translate(12px, 4px) scale(0.75)'
324
- })), ownerState.variant === 'outlined' && _extends$1({
325
- // see comment above on filled.zIndex
326
- zIndex: 1,
327
- pointerEvents: 'none',
328
- transform: 'translate(14px, 16px) scale(1)',
329
- maxWidth: 'calc(100% - 24px)'
330
- }, ownerState.size === 'small' && {
331
- transform: 'translate(14px, 9px) scale(1)'
332
- }, ownerState.shrink && {
333
- userSelect: 'none',
334
- pointerEvents: 'auto',
335
- // Theoretically, we should have (8+5)*2/0.75 = 34px
336
- // but it feels a better when it bleeds a bit on the left, so 32px.
337
- maxWidth: 'calc(133% - 32px)',
338
- transform: 'translate(14px, -9px) scale(0.75)'
339
- })));
340
- const InputLabel = /*#__PURE__*/React.forwardRef(function InputLabel(inProps, ref) {
341
- const props = useDefaultProps({
342
- name: 'MuiInputLabel',
343
- props: inProps
344
- });
345
- const {
346
- disableAnimation = false,
347
- shrink: shrinkProp,
348
- className
349
- } = props,
350
- other = _objectWithoutPropertiesLoose$1(props, _excluded$4);
351
- const muiFormControl = useFormControl();
352
- let shrink = shrinkProp;
353
- if (typeof shrink === 'undefined' && muiFormControl) {
354
- shrink = muiFormControl.filled || muiFormControl.focused || muiFormControl.adornedStart;
355
- }
356
- const fcs = formControlState({
357
- props,
358
- muiFormControl,
359
- states: ['size', 'variant', 'required', 'focused']
360
- });
361
- const ownerState = _extends$1({}, props, {
362
- disableAnimation,
363
- formControl: muiFormControl,
364
- shrink,
365
- size: fcs.size,
366
- variant: fcs.variant,
367
- required: fcs.required,
368
- focused: fcs.focused
369
- });
370
- const classes = useUtilityClasses$3(ownerState);
371
- return /*#__PURE__*/jsxRuntimeExports.jsx(InputLabelRoot, _extends$1({
372
- "data-shrink": shrink,
373
- ownerState: ownerState,
374
- ref: ref,
375
- className: clsx$1(classes.root, className)
376
- }, other, {
377
- classes: classes
378
- }));
379
- });
380
- process.env.NODE_ENV !== "production" ? InputLabel.propTypes /* remove-proptypes */ = {
381
- // ┌────────────────────────────── Warning ──────────────────────────────┐
382
- // │ These PropTypes are generated from the TypeScript type definitions. │
383
- // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
384
- // └─────────────────────────────────────────────────────────────────────┘
385
- /**
386
- * The content of the component.
387
- */
388
- children: PropTypes.node,
389
- /**
390
- * Override or extend the styles applied to the component.
391
- */
392
- classes: PropTypes.object,
393
- /**
394
- * @ignore
395
- */
396
- className: PropTypes.string,
397
- /**
398
- * The color of the component.
399
- * It supports both default and custom theme colors, which can be added as shown in the
400
- * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors).
401
- */
402
- color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'warning']), PropTypes.string]),
403
- /**
404
- * If `true`, the transition animation is disabled.
405
- * @default false
406
- */
407
- disableAnimation: PropTypes.bool,
408
- /**
409
- * If `true`, the component is disabled.
410
- */
411
- disabled: PropTypes.bool,
412
- /**
413
- * If `true`, the label is displayed in an error state.
414
- */
415
- error: PropTypes.bool,
416
- /**
417
- * If `true`, the `input` of this label is focused.
418
- */
419
- focused: PropTypes.bool,
420
- /**
421
- * If `dense`, will adjust vertical spacing. This is normally obtained via context from
422
- * FormControl.
423
- */
424
- margin: PropTypes.oneOf(['dense']),
425
- /**
426
- * if `true`, the label will indicate that the `input` is required.
427
- */
428
- required: PropTypes.bool,
429
- /**
430
- * If `true`, the label is shrunk.
431
- */
432
- shrink: PropTypes.bool,
433
- /**
434
- * The size of the component.
435
- * @default 'normal'
436
- */
437
- size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['normal', 'small']), PropTypes.string]),
438
- /**
439
- * The system prop that allows defining system overrides as well as additional CSS styles.
440
- */
441
- sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
442
- /**
443
- * The variant to use.
444
- */
445
- variant: PropTypes.oneOf(['filled', 'outlined', 'standard'])
446
- } : void 0;
447
- var InputLabel$1 = InputLabel;
448
-
449
- function getFormControlUtilityClasses(slot) {
450
- return generateUtilityClass('MuiFormControl', slot);
451
- }
452
- generateUtilityClasses('MuiFormControl', ['root', 'marginNone', 'marginNormal', 'marginDense', 'fullWidth', 'disabled']);
453
-
454
- const _excluded$3 = ["children", "className", "color", "component", "disabled", "error", "focused", "fullWidth", "hiddenLabel", "margin", "required", "size", "variant"];
455
- const useUtilityClasses$2 = ownerState => {
456
- const {
457
- classes,
458
- margin,
459
- fullWidth
460
- } = ownerState;
461
- const slots = {
462
- root: ['root', margin !== 'none' && `margin${capitalize(margin)}`, fullWidth && 'fullWidth']
463
- };
464
- return composeClasses(slots, getFormControlUtilityClasses, classes);
465
- };
466
- const FormControlRoot = styled$1('div', {
467
- name: 'MuiFormControl',
468
- slot: 'Root',
469
- overridesResolver: ({
470
- ownerState
471
- }, styles) => {
472
- return _extends$1({}, styles.root, styles[`margin${capitalize(ownerState.margin)}`], ownerState.fullWidth && styles.fullWidth);
473
- }
474
- })(({
475
- ownerState
476
- }) => _extends$1({
477
- display: 'inline-flex',
478
- flexDirection: 'column',
479
- position: 'relative',
480
- // Reset fieldset default style.
481
- minWidth: 0,
482
- padding: 0,
483
- margin: 0,
484
- border: 0,
485
- verticalAlign: 'top'
486
- }, ownerState.margin === 'normal' && {
487
- marginTop: 16,
488
- marginBottom: 8
489
- }, ownerState.margin === 'dense' && {
490
- marginTop: 8,
491
- marginBottom: 4
492
- }, ownerState.fullWidth && {
493
- width: '100%'
494
- }));
495
-
496
- /**
497
- * Provides context such as filled/focused/error/required for form inputs.
498
- * Relying on the context provides high flexibility and ensures that the state always stays
499
- * consistent across the children of the `FormControl`.
500
- * This context is used by the following components:
501
- *
502
- * - FormLabel
503
- * - FormHelperText
504
- * - Input
505
- * - InputLabel
506
- *
507
- * You can find one composition example below and more going to [the demos](/material-ui/react-text-field/#components).
508
- *
509
- * ```jsx
510
- * <FormControl>
511
- * <InputLabel htmlFor="my-input">Email address</InputLabel>
512
- * <Input id="my-input" aria-describedby="my-helper-text" />
513
- * <FormHelperText id="my-helper-text">We'll never share your email.</FormHelperText>
514
- * </FormControl>
515
- * ```
516
- *
517
- * ⚠️ Only one `InputBase` can be used within a FormControl because it creates visual inconsistencies.
518
- * For instance, only one input can be focused at the same time, the state shouldn't be shared.
519
- */
520
- const FormControl = /*#__PURE__*/React.forwardRef(function FormControl(inProps, ref) {
521
- const props = useDefaultProps({
522
- props: inProps,
523
- name: 'MuiFormControl'
524
- });
525
- const {
526
- children,
527
- className,
528
- color = 'primary',
529
- component = 'div',
530
- disabled = false,
531
- error = false,
532
- focused: visuallyFocused,
533
- fullWidth = false,
534
- hiddenLabel = false,
535
- margin = 'none',
536
- required = false,
537
- size = 'medium',
538
- variant = 'outlined'
539
- } = props,
540
- other = _objectWithoutPropertiesLoose$1(props, _excluded$3);
541
- const ownerState = _extends$1({}, props, {
542
- color,
543
- component,
544
- disabled,
545
- error,
546
- fullWidth,
547
- hiddenLabel,
548
- margin,
549
- required,
550
- size,
551
- variant
552
- });
553
- const classes = useUtilityClasses$2(ownerState);
554
- const [adornedStart, setAdornedStart] = React.useState(() => {
555
- // We need to iterate through the children and find the Input in order
556
- // to fully support server-side rendering.
557
- let initialAdornedStart = false;
558
- if (children) {
559
- React.Children.forEach(children, child => {
560
- if (!isMuiElement(child, ['Input', 'Select'])) {
561
- return;
562
- }
563
- const input = isMuiElement(child, ['Select']) ? child.props.input : child;
564
- if (input && isAdornedStart(input.props)) {
565
- initialAdornedStart = true;
566
- }
567
- });
568
- }
569
- return initialAdornedStart;
570
- });
571
- const [filled, setFilled] = React.useState(() => {
572
- // We need to iterate through the children and find the Input in order
573
- // to fully support server-side rendering.
574
- let initialFilled = false;
575
- if (children) {
576
- React.Children.forEach(children, child => {
577
- if (!isMuiElement(child, ['Input', 'Select'])) {
578
- return;
579
- }
580
- if (isFilled(child.props, true) || isFilled(child.props.inputProps, true)) {
581
- initialFilled = true;
582
- }
583
- });
584
- }
585
- return initialFilled;
586
- });
587
- const [focusedState, setFocused] = React.useState(false);
588
- if (disabled && focusedState) {
589
- setFocused(false);
590
- }
591
- const focused = visuallyFocused !== undefined && !disabled ? visuallyFocused : focusedState;
592
- let registerEffect;
593
- if (process.env.NODE_ENV !== 'production') {
594
- // eslint-disable-next-line react-hooks/rules-of-hooks
595
- const registeredInput = React.useRef(false);
596
- registerEffect = () => {
597
- if (registeredInput.current) {
598
- console.error(['MUI: There are multiple `InputBase` components inside a FormControl.', 'This creates visual inconsistencies, only use one `InputBase`.'].join('\n'));
599
- }
600
- registeredInput.current = true;
601
- return () => {
602
- registeredInput.current = false;
603
- };
604
- };
605
- }
606
- const childContext = React.useMemo(() => {
607
- return {
608
- adornedStart,
609
- setAdornedStart,
610
- color,
611
- disabled,
612
- error,
613
- filled,
614
- focused,
615
- fullWidth,
616
- hiddenLabel,
617
- size,
618
- onBlur: () => {
619
- setFocused(false);
620
- },
621
- onEmpty: () => {
622
- setFilled(false);
623
- },
624
- onFilled: () => {
625
- setFilled(true);
626
- },
627
- onFocus: () => {
628
- setFocused(true);
629
- },
630
- registerEffect,
631
- required,
632
- variant
633
- };
634
- }, [adornedStart, color, disabled, error, filled, focused, fullWidth, hiddenLabel, registerEffect, required, size, variant]);
635
- return /*#__PURE__*/jsxRuntimeExports.jsx(FormControlContext.Provider, {
636
- value: childContext,
637
- children: /*#__PURE__*/jsxRuntimeExports.jsx(FormControlRoot, _extends$1({
638
- as: component,
639
- ownerState: ownerState,
640
- className: clsx$1(classes.root, className),
641
- ref: ref
642
- }, other, {
643
- children: children
644
- }))
645
- });
646
- });
647
- process.env.NODE_ENV !== "production" ? FormControl.propTypes /* remove-proptypes */ = {
648
- // ┌────────────────────────────── Warning ──────────────────────────────┐
649
- // │ These PropTypes are generated from the TypeScript type definitions. │
650
- // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
651
- // └─────────────────────────────────────────────────────────────────────┘
652
- /**
653
- * The content of the component.
654
- */
655
- children: PropTypes.node,
656
- /**
657
- * Override or extend the styles applied to the component.
658
- */
659
- classes: PropTypes.object,
660
- /**
661
- * @ignore
662
- */
663
- className: PropTypes.string,
664
- /**
665
- * The color of the component.
666
- * It supports both default and custom theme colors, which can be added as shown in the
667
- * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors).
668
- * @default 'primary'
669
- */
670
- color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['primary', 'secondary', 'error', 'info', 'success', 'warning']), PropTypes.string]),
671
- /**
672
- * The component used for the root node.
673
- * Either a string to use a HTML element or a component.
674
- */
675
- component: PropTypes.elementType,
676
- /**
677
- * If `true`, the label, input and helper text should be displayed in a disabled state.
678
- * @default false
679
- */
680
- disabled: PropTypes.bool,
681
- /**
682
- * If `true`, the label is displayed in an error state.
683
- * @default false
684
- */
685
- error: PropTypes.bool,
686
- /**
687
- * If `true`, the component is displayed in focused state.
688
- */
689
- focused: PropTypes.bool,
690
- /**
691
- * If `true`, the component will take up the full width of its container.
692
- * @default false
693
- */
694
- fullWidth: PropTypes.bool,
695
- /**
696
- * If `true`, the label is hidden.
697
- * This is used to increase density for a `FilledInput`.
698
- * Be sure to add `aria-label` to the `input` element.
699
- * @default false
700
- */
701
- hiddenLabel: PropTypes.bool,
702
- /**
703
- * If `dense` or `normal`, will adjust vertical spacing of this and contained components.
704
- * @default 'none'
705
- */
706
- margin: PropTypes.oneOf(['dense', 'none', 'normal']),
707
- /**
708
- * If `true`, the label will indicate that the `input` is required.
709
- * @default false
710
- */
711
- required: PropTypes.bool,
712
- /**
713
- * The size of the component.
714
- * @default 'medium'
715
- */
716
- size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['medium', 'small']), PropTypes.string]),
717
- /**
718
- * The system prop that allows defining system overrides as well as additional CSS styles.
719
- */
720
- sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
721
- /**
722
- * The variant to use.
723
- * @default 'outlined'
724
- */
725
- variant: PropTypes.oneOf(['filled', 'outlined', 'standard'])
726
- } : void 0;
727
- var FormControl$1 = FormControl;
728
-
729
- function getFormHelperTextUtilityClasses(slot) {
730
- return generateUtilityClass('MuiFormHelperText', slot);
731
- }
732
- const formHelperTextClasses = generateUtilityClasses('MuiFormHelperText', ['root', 'error', 'disabled', 'sizeSmall', 'sizeMedium', 'contained', 'focused', 'filled', 'required']);
733
- var formHelperTextClasses$1 = formHelperTextClasses;
734
-
735
- var _span;
736
- const _excluded$2 = ["children", "className", "component", "disabled", "error", "filled", "focused", "margin", "required", "variant"];
737
- const useUtilityClasses$1 = ownerState => {
738
- const {
739
- classes,
740
- contained,
741
- size,
742
- disabled,
743
- error,
744
- filled,
745
- focused,
746
- required
747
- } = ownerState;
748
- const slots = {
749
- root: ['root', disabled && 'disabled', error && 'error', size && `size${capitalize(size)}`, contained && 'contained', focused && 'focused', filled && 'filled', required && 'required']
750
- };
751
- return composeClasses(slots, getFormHelperTextUtilityClasses, classes);
752
- };
753
- const FormHelperTextRoot = styled$1('p', {
754
- name: 'MuiFormHelperText',
755
- slot: 'Root',
756
- overridesResolver: (props, styles) => {
757
- const {
758
- ownerState
759
- } = props;
760
- return [styles.root, ownerState.size && styles[`size${capitalize(ownerState.size)}`], ownerState.contained && styles.contained, ownerState.filled && styles.filled];
761
- }
762
- })(({
763
- theme,
764
- ownerState
765
- }) => _extends$1({
766
- color: (theme.vars || theme).palette.text.secondary
767
- }, theme.typography.caption, {
768
- textAlign: 'left',
769
- marginTop: 3,
770
- marginRight: 0,
771
- marginBottom: 0,
772
- marginLeft: 0,
773
- [`&.${formHelperTextClasses$1.disabled}`]: {
774
- color: (theme.vars || theme).palette.text.disabled
775
- },
776
- [`&.${formHelperTextClasses$1.error}`]: {
777
- color: (theme.vars || theme).palette.error.main
778
- }
779
- }, ownerState.size === 'small' && {
780
- marginTop: 4
781
- }, ownerState.contained && {
782
- marginLeft: 14,
783
- marginRight: 14
784
- }));
785
- const FormHelperText = /*#__PURE__*/React.forwardRef(function FormHelperText(inProps, ref) {
786
- const props = useDefaultProps({
787
- props: inProps,
788
- name: 'MuiFormHelperText'
789
- });
790
- const {
791
- children,
792
- className,
793
- component = 'p'
794
- } = props,
795
- other = _objectWithoutPropertiesLoose$1(props, _excluded$2);
796
- const muiFormControl = useFormControl();
797
- const fcs = formControlState({
798
- props,
799
- muiFormControl,
800
- states: ['variant', 'size', 'disabled', 'error', 'filled', 'focused', 'required']
801
- });
802
- const ownerState = _extends$1({}, props, {
803
- component,
804
- contained: fcs.variant === 'filled' || fcs.variant === 'outlined',
805
- variant: fcs.variant,
806
- size: fcs.size,
807
- disabled: fcs.disabled,
808
- error: fcs.error,
809
- filled: fcs.filled,
810
- focused: fcs.focused,
811
- required: fcs.required
812
- });
813
- const classes = useUtilityClasses$1(ownerState);
814
- return /*#__PURE__*/jsxRuntimeExports.jsx(FormHelperTextRoot, _extends$1({
815
- as: component,
816
- ownerState: ownerState,
817
- className: clsx$1(classes.root, className),
818
- ref: ref
819
- }, other, {
820
- children: children === ' ' ? // notranslate needed while Google Translate will not fix zero-width space issue
821
- _span || (_span = /*#__PURE__*/jsxRuntimeExports.jsx("span", {
822
- className: "notranslate",
823
- children: "\u200B"
824
- })) : children
825
- }));
826
- });
827
- process.env.NODE_ENV !== "production" ? FormHelperText.propTypes /* remove-proptypes */ = {
828
- // ┌────────────────────────────── Warning ──────────────────────────────┐
829
- // │ These PropTypes are generated from the TypeScript type definitions. │
830
- // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
831
- // └─────────────────────────────────────────────────────────────────────┘
832
- /**
833
- * The content of the component.
834
- *
835
- * If `' '` is provided, the component reserves one line height for displaying a future message.
836
- */
837
- children: PropTypes.node,
838
- /**
839
- * Override or extend the styles applied to the component.
840
- */
841
- classes: PropTypes.object,
842
- /**
843
- * @ignore
844
- */
845
- className: PropTypes.string,
846
- /**
847
- * The component used for the root node.
848
- * Either a string to use a HTML element or a component.
849
- */
850
- component: PropTypes.elementType,
851
- /**
852
- * If `true`, the helper text should be displayed in a disabled state.
853
- */
854
- disabled: PropTypes.bool,
855
- /**
856
- * If `true`, helper text should be displayed in an error state.
857
- */
858
- error: PropTypes.bool,
859
- /**
860
- * If `true`, the helper text should use filled classes key.
861
- */
862
- filled: PropTypes.bool,
863
- /**
864
- * If `true`, the helper text should use focused classes key.
865
- */
866
- focused: PropTypes.bool,
867
- /**
868
- * If `dense`, will adjust vertical spacing. This is normally obtained via context from
869
- * FormControl.
870
- */
871
- margin: PropTypes.oneOf(['dense']),
872
- /**
873
- * If `true`, the helper text should use required classes key.
874
- */
875
- required: PropTypes.bool,
876
- /**
877
- * The system prop that allows defining system overrides as well as additional CSS styles.
878
- */
879
- sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
880
- /**
881
- * The variant to use.
882
- */
883
- variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['filled', 'outlined', 'standard']), PropTypes.string])
884
- } : void 0;
885
- var FormHelperText$1 = FormHelperText;
886
-
887
- function getTextFieldUtilityClass(slot) {
888
- return generateUtilityClass('MuiTextField', slot);
889
- }
890
- generateUtilityClasses('MuiTextField', ['root']);
891
-
892
- const _excluded$1 = ["autoComplete", "autoFocus", "children", "className", "color", "defaultValue", "disabled", "error", "FormHelperTextProps", "fullWidth", "helperText", "id", "InputLabelProps", "inputProps", "InputProps", "inputRef", "label", "maxRows", "minRows", "multiline", "name", "onBlur", "onChange", "onFocus", "placeholder", "required", "rows", "select", "SelectProps", "type", "value", "variant"];
893
- const variantComponent = {
894
- standard: Input,
895
- filled: FilledInput,
896
- outlined: OutlinedInput
897
- };
898
- const useUtilityClasses = ownerState => {
899
- const {
900
- classes
901
- } = ownerState;
902
- const slots = {
903
- root: ['root']
904
- };
905
- return composeClasses(slots, getTextFieldUtilityClass, classes);
906
- };
907
- const TextFieldRoot = styled$1(FormControl$1, {
908
- name: 'MuiTextField',
909
- slot: 'Root',
910
- overridesResolver: (props, styles) => styles.root
911
- })({});
912
-
913
- /**
914
- * The `TextField` is a convenience wrapper for the most common cases (80%).
915
- * It cannot be all things to all people, otherwise the API would grow out of control.
916
- *
917
- * ## Advanced Configuration
918
- *
919
- * It's important to understand that the text field is a simple abstraction
920
- * on top of the following components:
921
- *
922
- * - [FormControl](/material-ui/api/form-control/)
923
- * - [InputLabel](/material-ui/api/input-label/)
924
- * - [FilledInput](/material-ui/api/filled-input/)
925
- * - [OutlinedInput](/material-ui/api/outlined-input/)
926
- * - [Input](/material-ui/api/input/)
927
- * - [FormHelperText](/material-ui/api/form-helper-text/)
928
- *
929
- * If you wish to alter the props applied to the `input` element, you can do so as follows:
930
- *
931
- * ```jsx
932
- * const inputProps = {
933
- * step: 300,
934
- * };
935
- *
936
- * return <TextField id="time" type="time" inputProps={inputProps} />;
937
- * ```
938
- *
939
- * For advanced cases, please look at the source of TextField by clicking on the
940
- * "Edit this page" button above. Consider either:
941
- *
942
- * - using the upper case props for passing values directly to the components
943
- * - using the underlying components directly as shown in the demos
944
- */
945
- const TextField = /*#__PURE__*/React.forwardRef(function TextField(inProps, ref) {
946
- const props = useDefaultProps({
947
- props: inProps,
948
- name: 'MuiTextField'
949
- });
950
- const {
951
- autoComplete,
952
- autoFocus = false,
953
- children,
954
- className,
955
- color = 'primary',
956
- defaultValue,
957
- disabled = false,
958
- error = false,
959
- FormHelperTextProps,
960
- fullWidth = false,
961
- helperText,
962
- id: idOverride,
963
- InputLabelProps,
964
- inputProps,
965
- InputProps,
966
- inputRef,
967
- label,
968
- maxRows,
969
- minRows,
970
- multiline = false,
971
- name,
972
- onBlur,
973
- onChange,
974
- onFocus,
975
- placeholder,
976
- required = false,
977
- rows,
978
- select = false,
979
- SelectProps,
980
- type,
981
- value,
982
- variant = 'outlined'
983
- } = props,
984
- other = _objectWithoutPropertiesLoose$1(props, _excluded$1);
985
- const ownerState = _extends$1({}, props, {
986
- autoFocus,
987
- color,
988
- disabled,
989
- error,
990
- fullWidth,
991
- multiline,
992
- required,
993
- select,
994
- variant
995
- });
996
- const classes = useUtilityClasses(ownerState);
997
- if (process.env.NODE_ENV !== 'production') {
998
- if (select && !children) {
999
- console.error('MUI: `children` must be passed when using the `TextField` component with `select`.');
1000
- }
1001
- }
1002
- const InputMore = {};
1003
- if (variant === 'outlined') {
1004
- if (InputLabelProps && typeof InputLabelProps.shrink !== 'undefined') {
1005
- InputMore.notched = InputLabelProps.shrink;
1006
- }
1007
- InputMore.label = label;
1008
- }
1009
- if (select) {
1010
- // unset defaults from textbox inputs
1011
- if (!SelectProps || !SelectProps.native) {
1012
- InputMore.id = undefined;
1013
- }
1014
- InputMore['aria-describedby'] = undefined;
1015
- }
1016
- const id = useId(idOverride);
1017
- const helperTextId = helperText && id ? `${id}-helper-text` : undefined;
1018
- const inputLabelId = label && id ? `${id}-label` : undefined;
1019
- const InputComponent = variantComponent[variant];
1020
- const InputElement = /*#__PURE__*/jsxRuntimeExports.jsx(InputComponent, _extends$1({
1021
- "aria-describedby": helperTextId,
1022
- autoComplete: autoComplete,
1023
- autoFocus: autoFocus,
1024
- defaultValue: defaultValue,
1025
- fullWidth: fullWidth,
1026
- multiline: multiline,
1027
- name: name,
1028
- rows: rows,
1029
- maxRows: maxRows,
1030
- minRows: minRows,
1031
- type: type,
1032
- value: value,
1033
- id: id,
1034
- inputRef: inputRef,
1035
- onBlur: onBlur,
1036
- onChange: onChange,
1037
- onFocus: onFocus,
1038
- placeholder: placeholder,
1039
- inputProps: inputProps
1040
- }, InputMore, InputProps));
1041
- return /*#__PURE__*/jsxRuntimeExports.jsxs(TextFieldRoot, _extends$1({
1042
- className: clsx$1(classes.root, className),
1043
- disabled: disabled,
1044
- error: error,
1045
- fullWidth: fullWidth,
1046
- ref: ref,
1047
- required: required,
1048
- color: color,
1049
- variant: variant,
1050
- ownerState: ownerState
1051
- }, other, {
1052
- children: [label != null && label !== '' && /*#__PURE__*/jsxRuntimeExports.jsx(InputLabel$1, _extends$1({
1053
- htmlFor: id,
1054
- id: inputLabelId
1055
- }, InputLabelProps, {
1056
- children: label
1057
- })), select ? /*#__PURE__*/jsxRuntimeExports.jsx(Select, _extends$1({
1058
- "aria-describedby": helperTextId,
1059
- id: id,
1060
- labelId: inputLabelId,
1061
- value: value,
1062
- input: InputElement
1063
- }, SelectProps, {
1064
- children: children
1065
- })) : InputElement, helperText && /*#__PURE__*/jsxRuntimeExports.jsx(FormHelperText$1, _extends$1({
1066
- id: helperTextId
1067
- }, FormHelperTextProps, {
1068
- children: helperText
1069
- }))]
1070
- }));
1071
- });
1072
- process.env.NODE_ENV !== "production" ? TextField.propTypes /* remove-proptypes */ = {
1073
- // ┌────────────────────────────── Warning ──────────────────────────────┐
1074
- // │ These PropTypes are generated from the TypeScript type definitions. │
1075
- // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
1076
- // └─────────────────────────────────────────────────────────────────────┘
1077
- /**
1078
- * This prop helps users to fill forms faster, especially on mobile devices.
1079
- * The name can be confusing, as it's more like an autofill.
1080
- * You can learn more about it [following the specification](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill).
1081
- */
1082
- autoComplete: PropTypes.string,
1083
- /**
1084
- * If `true`, the `input` element is focused during the first mount.
1085
- * @default false
1086
- */
1087
- autoFocus: PropTypes.bool,
1088
- /**
1089
- * @ignore
1090
- */
1091
- children: PropTypes.node,
1092
- /**
1093
- * Override or extend the styles applied to the component.
1094
- */
1095
- classes: PropTypes.object,
1096
- /**
1097
- * @ignore
1098
- */
1099
- className: PropTypes.string,
1100
- /**
1101
- * The color of the component.
1102
- * It supports both default and custom theme colors, which can be added as shown in the
1103
- * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors).
1104
- * @default 'primary'
1105
- */
1106
- color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['primary', 'secondary', 'error', 'info', 'success', 'warning']), PropTypes.string]),
1107
- /**
1108
- * The default value. Use when the component is not controlled.
1109
- */
1110
- defaultValue: PropTypes.any,
1111
- /**
1112
- * If `true`, the component is disabled.
1113
- * @default false
1114
- */
1115
- disabled: PropTypes.bool,
1116
- /**
1117
- * If `true`, the label is displayed in an error state.
1118
- * @default false
1119
- */
1120
- error: PropTypes.bool,
1121
- /**
1122
- * Props applied to the [`FormHelperText`](/material-ui/api/form-helper-text/) element.
1123
- */
1124
- FormHelperTextProps: PropTypes.object,
1125
- /**
1126
- * If `true`, the input will take up the full width of its container.
1127
- * @default false
1128
- */
1129
- fullWidth: PropTypes.bool,
1130
- /**
1131
- * The helper text content.
1132
- */
1133
- helperText: PropTypes.node,
1134
- /**
1135
- * The id of the `input` element.
1136
- * Use this prop to make `label` and `helperText` accessible for screen readers.
1137
- */
1138
- id: PropTypes.string,
1139
- /**
1140
- * Props applied to the [`InputLabel`](/material-ui/api/input-label/) element.
1141
- * Pointer events like `onClick` are enabled if and only if `shrink` is `true`.
1142
- */
1143
- InputLabelProps: PropTypes.object,
1144
- /**
1145
- * [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes) applied to the `input` element.
1146
- */
1147
- inputProps: PropTypes.object,
1148
- /**
1149
- * Props applied to the Input element.
1150
- * It will be a [`FilledInput`](/material-ui/api/filled-input/),
1151
- * [`OutlinedInput`](/material-ui/api/outlined-input/) or [`Input`](/material-ui/api/input/)
1152
- * component depending on the `variant` prop value.
1153
- */
1154
- InputProps: PropTypes.object,
1155
- /**
1156
- * Pass a ref to the `input` element.
1157
- */
1158
- inputRef: refType,
1159
- /**
1160
- * The label content.
1161
- */
1162
- label: PropTypes.node,
1163
- /**
1164
- * If `dense` or `normal`, will adjust vertical spacing of this and contained components.
1165
- * @default 'none'
1166
- */
1167
- margin: PropTypes.oneOf(['dense', 'none', 'normal']),
1168
- /**
1169
- * Maximum number of rows to display when multiline option is set to true.
1170
- */
1171
- maxRows: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
1172
- /**
1173
- * Minimum number of rows to display when multiline option is set to true.
1174
- */
1175
- minRows: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
1176
- /**
1177
- * If `true`, a `textarea` element is rendered instead of an input.
1178
- * @default false
1179
- */
1180
- multiline: PropTypes.bool,
1181
- /**
1182
- * Name attribute of the `input` element.
1183
- */
1184
- name: PropTypes.string,
1185
- /**
1186
- * @ignore
1187
- */
1188
- onBlur: PropTypes.func,
1189
- /**
1190
- * Callback fired when the value is changed.
1191
- *
1192
- * @param {object} event The event source of the callback.
1193
- * You can pull out the new value by accessing `event.target.value` (string).
1194
- */
1195
- onChange: PropTypes.func,
1196
- /**
1197
- * @ignore
1198
- */
1199
- onFocus: PropTypes.func,
1200
- /**
1201
- * The short hint displayed in the `input` before the user enters a value.
1202
- */
1203
- placeholder: PropTypes.string,
1204
- /**
1205
- * If `true`, the label is displayed as required and the `input` element is required.
1206
- * @default false
1207
- */
1208
- required: PropTypes.bool,
1209
- /**
1210
- * Number of rows to display when multiline option is set to true.
1211
- */
1212
- rows: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
1213
- /**
1214
- * Render a [`Select`](/material-ui/api/select/) element while passing the Input element to `Select` as `input` parameter.
1215
- * If this option is set you must pass the options of the select as children.
1216
- * @default false
1217
- */
1218
- select: PropTypes.bool,
1219
- /**
1220
- * Props applied to the [`Select`](/material-ui/api/select/) element.
1221
- */
1222
- SelectProps: PropTypes.object,
1223
- /**
1224
- * The size of the component.
1225
- */
1226
- size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['medium', 'small']), PropTypes.string]),
1227
- /**
1228
- * The system prop that allows defining system overrides as well as additional CSS styles.
1229
- */
1230
- sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
1231
- /**
1232
- * Type of the `input` element. It should be [a valid HTML5 input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types).
1233
- */
1234
- type: PropTypes /* @typescript-to-proptypes-ignore */.string,
1235
- /**
1236
- * The value of the `input` element, required for a controlled component.
1237
- */
1238
- value: PropTypes.any,
1239
- /**
1240
- * The variant to use.
1241
- * @default 'outlined'
1242
- */
1243
- variant: PropTypes.oneOf(['filled', 'outlined', 'standard'])
1244
- } : void 0;
1245
- var TextField$1 = TextField;
1246
16
 
1247
17
  const SUBMIT_FILTER_STROKE_TIME = 500;
1248
18
  const InputNumberInterval = props => {
@@ -1284,7 +54,7 @@ const InputNumberInterval = props => {
1284
54
  const newLowerBound = event.target.value;
1285
55
  updateFilterValue(newLowerBound, filterValueState[1]);
1286
56
  };
1287
- return /*#__PURE__*/React.createElement(Box$1, {
57
+ return /*#__PURE__*/React.createElement(Box, {
1288
58
  sx: {
1289
59
  display: 'inline-flex',
1290
60
  flexDirection: 'row',
@@ -1292,7 +62,7 @@ const InputNumberInterval = props => {
1292
62
  height: 48,
1293
63
  pl: '20px'
1294
64
  }
1295
- }, /*#__PURE__*/React.createElement(TextField$1, {
65
+ }, /*#__PURE__*/React.createElement(TextField, {
1296
66
  name: "lower-bound-input",
1297
67
  placeholder: "From",
1298
68
  label: "From",
@@ -1304,7 +74,7 @@ const InputNumberInterval = props => {
1304
74
  sx: {
1305
75
  mr: 2
1306
76
  }
1307
- }), /*#__PURE__*/React.createElement(TextField$1, {
77
+ }), /*#__PURE__*/React.createElement(TextField, {
1308
78
  name: "upper-bound-input",
1309
79
  placeholder: "To",
1310
80
  label: "To",
@@ -1336,8 +106,8 @@ const isBetweenOperator = {
1336
106
  if (typeof filterItem.value[0] !== 'number' || typeof filterItem.value[1] !== 'number') {
1337
107
  return null;
1338
108
  }
1339
- return params => {
1340
- return params.value !== null && params.value !== undefined && filterItem.value[0] <= params.value && params.value <= filterItem.value[1];
109
+ return value => {
110
+ return value !== null && value !== undefined && filterItem.value[0] <= value && value <= filterItem.value[1];
1341
111
  };
1342
112
  },
1343
113
  InputComponent: InputNumberInterval
@@ -1353,14 +123,14 @@ const doesNotContain = {
1353
123
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1354
124
  return null;
1355
125
  }
1356
- return params => {
126
+ return value => {
1357
127
  if (filterItem.value.length === 0) {
1358
128
  return true;
1359
129
  }
1360
- if (params.value == null) {
130
+ if (value == null) {
1361
131
  return true;
1362
132
  }
1363
- if (String(params.value).indexOf(filterItem.value) !== -1) {
133
+ if (String(value).indexOf(filterItem.value) !== -1) {
1364
134
  return false;
1365
135
  }
1366
136
  return true;
@@ -1377,11 +147,11 @@ const doesNotEndWithOperator = {
1377
147
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1378
148
  return null;
1379
149
  }
1380
- return params => {
1381
- if (params.value == null) {
150
+ return value => {
151
+ if (value == null) {
1382
152
  return true;
1383
153
  }
1384
- return !String(params.value).endsWith(filterItem.value);
154
+ return !String(value).endsWith(filterItem.value);
1385
155
  };
1386
156
  },
1387
157
  InputComponent: GridFilterInputValue
@@ -1395,11 +165,11 @@ const doesNotEqual = {
1395
165
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1396
166
  return null;
1397
167
  }
1398
- return params => {
1399
- if (params.value == null) {
168
+ return value => {
169
+ if (value == null) {
1400
170
  return true;
1401
171
  }
1402
- if (String(params.value) === filterItem.value) {
172
+ if (String(value) === filterItem.value) {
1403
173
  return false;
1404
174
  }
1405
175
  return true;
@@ -1416,11 +186,11 @@ const doesNotHaveOperator = {
1416
186
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1417
187
  return null;
1418
188
  }
1419
- return params => {
1420
- if (params.value == null) {
189
+ return value => {
190
+ if (value == null) {
1421
191
  return true;
1422
192
  }
1423
- const cellValues = Array.isArray(params.value) ? params.value : [params.value];
193
+ const cellValues = Array.isArray(value) ? value : [value];
1424
194
  return !cellValues.map(value => String(value)).includes(filterItem.value);
1425
195
  };
1426
196
  },
@@ -1438,11 +208,11 @@ const doesNotStartWithOperator = {
1438
208
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1439
209
  return null;
1440
210
  }
1441
- return params => {
1442
- if (params.value == null) {
211
+ return value => {
212
+ if (value == null) {
1443
213
  return true;
1444
214
  }
1445
- return !String(params.value).startsWith(filterItem.value);
215
+ return !String(value).startsWith(filterItem.value);
1446
216
  };
1447
217
  },
1448
218
  InputComponent: GridFilterInputValue
@@ -1456,11 +226,11 @@ const hasOperator = {
1456
226
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1457
227
  return null;
1458
228
  }
1459
- return params => {
1460
- if (params.value == null) {
229
+ return value => {
230
+ if (value == null) {
1461
231
  return false;
1462
232
  }
1463
- const cellValues = Array.isArray(params.value) ? params.value : [params.value];
233
+ const cellValues = Array.isArray(value) ? value : [value];
1464
234
  return cellValues.map(value => String(value)).includes(filterItem.value);
1465
235
  };
1466
236
  },
@@ -1478,11 +248,11 @@ const hasOnlyOperator = {
1478
248
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1479
249
  return null;
1480
250
  }
1481
- return params => {
1482
- if (params.value == null) {
251
+ return value => {
252
+ if (value == null) {
1483
253
  return false;
1484
254
  }
1485
- const cellValues = Array.isArray(params.value) ? params.value : [params.value];
255
+ const cellValues = Array.isArray(value) ? value : [value];
1486
256
  return cellValues.length === 1 && String(cellValues[0]) === filterItem.value;
1487
257
  };
1488
258
  },
@@ -1500,14 +270,14 @@ const isOperator = {
1500
270
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1501
271
  return null;
1502
272
  }
1503
- return params => {
1504
- if (params.value == null) {
273
+ return value => {
274
+ if (value == null) {
1505
275
  return false;
1506
276
  }
1507
- if (Array.isArray(params.value)) {
277
+ if (Array.isArray(value)) {
1508
278
  return false;
1509
279
  }
1510
- return String(params.value) === filterItem.value;
280
+ return String(value) === filterItem.value;
1511
281
  };
1512
282
  },
1513
283
  InputComponent: GridFilterInputValue
@@ -1524,14 +294,14 @@ const isNotOperator = {
1524
294
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1525
295
  return null;
1526
296
  }
1527
- return params => {
1528
- if (params.value == null) {
297
+ return value => {
298
+ if (value == null) {
1529
299
  return true;
1530
300
  }
1531
- if (Array.isArray(params.value)) {
301
+ if (Array.isArray(value)) {
1532
302
  return true;
1533
303
  }
1534
- return String(params.value) !== filterItem.value;
304
+ return String(value) !== filterItem.value;
1535
305
  };
1536
306
  },
1537
307
  InputComponent: GridFilterInputValue
@@ -1548,14 +318,14 @@ const containsAnyOfOperator = {
1548
318
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1549
319
  return null;
1550
320
  }
1551
- return params => {
321
+ return value => {
1552
322
  if (filterItem.value.length === 0) {
1553
323
  return true;
1554
324
  }
1555
- if (params.value == null) {
325
+ if (value == null) {
1556
326
  return false;
1557
327
  }
1558
- const paramValues = Array.isArray(params.value) ? params.value : [params.value];
328
+ const paramValues = Array.isArray(value) ? value : [value];
1559
329
  let match = false;
1560
330
  filterItem.value.forEach(filteredValue => {
1561
331
  paramValues.forEach(paramValue => {
@@ -1578,14 +348,14 @@ const doesNotContainAnyOfOperator = {
1578
348
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1579
349
  return null;
1580
350
  }
1581
- return params => {
351
+ return value => {
1582
352
  if (filterItem.value.length === 0) {
1583
353
  return true;
1584
354
  }
1585
- if (params.value == null) {
355
+ if (value == null) {
1586
356
  return true;
1587
357
  }
1588
- const paramValues = Array.isArray(params.value) ? params.value : [params.value];
358
+ const paramValues = Array.isArray(value) ? value : [value];
1589
359
  for (const filteredValue of filterItem.value) {
1590
360
  for (const paramValue of paramValues) {
1591
361
  if (String(paramValue).indexOf(filteredValue) !== -1) {
@@ -1607,14 +377,14 @@ const doesNotEndWithAnyOfOperator = {
1607
377
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1608
378
  return null;
1609
379
  }
1610
- return params => {
380
+ return value => {
1611
381
  if (filterItem.value.length === 0) {
1612
382
  return true;
1613
383
  }
1614
- if (params.value == null) {
384
+ if (value == null) {
1615
385
  return true;
1616
386
  }
1617
- const paramValues = Array.isArray(params.value) ? params.value : [params.value];
387
+ const paramValues = Array.isArray(value) ? value : [value];
1618
388
  for (const filteredValue of filterItem.value) {
1619
389
  for (const paramValue of paramValues) {
1620
390
  if (String(paramValue).endsWith(filteredValue)) {
@@ -1636,11 +406,11 @@ const doesNotHaveAnyOf = {
1636
406
  if (!filterItem.field || !filterItem.value || !Array.isArray(filterItem.value) || filterItem.value.length === 0) {
1637
407
  return null;
1638
408
  }
1639
- return params => {
1640
- if (params.value == null) {
409
+ return value => {
410
+ if (value == null) {
1641
411
  return true;
1642
412
  }
1643
- const cellValues = Array.isArray(params.value) ? params.value : [params.value];
413
+ const cellValues = Array.isArray(value) ? value : [value];
1644
414
 
1645
415
  // Return true only if none of the filter values are in the cell values
1646
416
  return filterItem.value.every(filterVal => !cellValues.map(value => String(value)).includes(filterVal));
@@ -1660,14 +430,14 @@ const doesNotStartWithAnyOfOperator = {
1660
430
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1661
431
  return null;
1662
432
  }
1663
- return params => {
433
+ return value => {
1664
434
  if (filterItem.value.length === 0) {
1665
435
  return true;
1666
436
  }
1667
- if (params.value == null) {
437
+ if (value == null) {
1668
438
  return true;
1669
439
  }
1670
- const paramValues = Array.isArray(params.value) ? params.value : [params.value];
440
+ const paramValues = Array.isArray(value) ? value : [value];
1671
441
  for (const filteredValue of filterItem.value) {
1672
442
  for (const paramValue of paramValues) {
1673
443
  if (String(paramValue).startsWith(filteredValue)) {
@@ -1689,14 +459,14 @@ const endsWithAnyOfOperator = {
1689
459
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1690
460
  return null;
1691
461
  }
1692
- return params => {
462
+ return value => {
1693
463
  if (filterItem.value.length === 0) {
1694
464
  return true;
1695
465
  }
1696
- if (params.value == null) {
466
+ if (value == null) {
1697
467
  return false;
1698
468
  }
1699
- const paramValues = Array.isArray(params.value) ? params.value : [params.value];
469
+ const paramValues = Array.isArray(value) ? value : [value];
1700
470
  let match = false;
1701
471
  filterItem.value.forEach(filteredValue => {
1702
472
  paramValues.forEach(paramValue => {
@@ -1719,14 +489,14 @@ const hasAllOfOperator = {
1719
489
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1720
490
  return null;
1721
491
  }
1722
- return params => {
492
+ return value => {
1723
493
  if (filterItem.value.length === 0) {
1724
494
  return true;
1725
495
  }
1726
- if (params.value == null) {
496
+ if (value == null) {
1727
497
  return false;
1728
498
  }
1729
- const cellValues = Array.isArray(params.value) ? params.value : [params.value];
499
+ const cellValues = Array.isArray(value) ? value : [value];
1730
500
  const cellStrings = cellValues.map(value => String(value));
1731
501
  const filterItemValues = Array.isArray(filterItem.value) ? filterItem.value : [filterItem.value];
1732
502
  return filterItemValues.every(v => cellStrings.includes(v));
@@ -1746,14 +516,14 @@ const hasAnyOfOperator = {
1746
516
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1747
517
  return null;
1748
518
  }
1749
- return params => {
519
+ return value => {
1750
520
  if (filterItem.value.length === 0) {
1751
521
  return true;
1752
522
  }
1753
- if (params.value == null) {
523
+ if (value == null) {
1754
524
  return false;
1755
525
  }
1756
- const cellValues = Array.isArray(params.value) ? params.value : [params.value];
526
+ const cellValues = Array.isArray(value) ? value : [value];
1757
527
  const filterItemValues = Array.isArray(filterItem.value) ? filterItem.value : [filterItem.value];
1758
528
  return filterItemValues.some(v => cellValues.map(value => String(value)).includes(v));
1759
529
  };
@@ -1772,14 +542,14 @@ const isAnyOfOperator = {
1772
542
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1773
543
  return null;
1774
544
  }
1775
- return params => {
545
+ return value => {
1776
546
  if (filterItem.value.length === 0) {
1777
547
  return true;
1778
548
  }
1779
- if (params.value == null) {
549
+ if (value == null) {
1780
550
  return false;
1781
551
  }
1782
- const paramValues = Array.isArray(params.value) ? params.value : [params.value];
552
+ const paramValues = Array.isArray(value) ? value : [value];
1783
553
  for (const paramValue of paramValues) {
1784
554
  if (filterItem.value.includes(String(paramValue))) {
1785
555
  return true;
@@ -1802,14 +572,14 @@ const isNotAnyOfOperator = {
1802
572
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1803
573
  return null;
1804
574
  }
1805
- return params => {
575
+ return value => {
1806
576
  if (filterItem.value.length === 0) {
1807
577
  return true;
1808
578
  }
1809
- if (params.value == null) {
579
+ if (value == null) {
1810
580
  return true;
1811
581
  }
1812
- const paramValues = Array.isArray(params.value) ? params.value : [params.value];
582
+ const paramValues = Array.isArray(value) ? value : [value];
1813
583
  for (const paramValue of paramValues) {
1814
584
  if (filterItem.value.includes(String(paramValue))) {
1815
585
  return false;
@@ -1832,14 +602,14 @@ const startsWithAnyOfOperator = {
1832
602
  if (!filterItem.field || !filterItem.value || !filterItem.operator) {
1833
603
  return null;
1834
604
  }
1835
- return params => {
605
+ return value => {
1836
606
  if (filterItem.value.length === 0) {
1837
607
  return true;
1838
608
  }
1839
- if (params.value == null) {
609
+ if (value == null) {
1840
610
  return false;
1841
611
  }
1842
- const paramValues = Array.isArray(params.value) ? params.value : [params.value];
612
+ const paramValues = Array.isArray(value) ? value : [value];
1843
613
  let match = false;
1844
614
  filterItem.value.forEach(filteredValue => {
1845
615
  paramValues.forEach(paramValue => {
@@ -1866,15 +636,15 @@ const isEmptyOperator = {
1866
636
  value: 'isEmpty',
1867
637
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
1868
638
  getApplyFilterFn: _filterItem => {
1869
- return params => {
1870
- if (params.value == null) {
639
+ return value => {
640
+ if (value == null) {
1871
641
  return true;
1872
642
  }
1873
- if (Array.isArray(params.value)) {
1874
- return params.value.length === 0;
643
+ if (Array.isArray(value)) {
644
+ return value.length === 0;
1875
645
  }
1876
- if (typeof params.value === 'string') {
1877
- return params.value.trim() === '';
646
+ if (typeof value === 'string') {
647
+ return value.trim() === '';
1878
648
  }
1879
649
  return false;
1880
650
  };
@@ -1886,15 +656,15 @@ const isNotEmptyOperator = {
1886
656
  value: 'isNotEmpty',
1887
657
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
1888
658
  getApplyFilterFn: _filterItem => {
1889
- return params => {
1890
- if (params.value == null) {
659
+ return value => {
660
+ if (value == null) {
1891
661
  return false;
1892
662
  }
1893
- if (Array.isArray(params.value)) {
1894
- return params.value.length > 0;
663
+ if (Array.isArray(value)) {
664
+ return value.length > 0;
1895
665
  }
1896
- if (typeof params.value === 'string') {
1897
- return params.value.trim() !== '';
666
+ if (typeof value === 'string') {
667
+ return value.trim() !== '';
1898
668
  }
1899
669
  return true;
1900
670
  };
@@ -1933,14 +703,20 @@ const DIMENSION_MODEL_KEY = 'dimension';
1933
703
  const FILTER_SEARCH_KEY = 'searchModel';
1934
704
  const DENSITY_MODEL_KEY = 'densityModel';
1935
705
  const COLUMN_ORDER_MODEL_KEY = 'columnOrderModel';
1936
- 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];
706
+ const ROW_GROUPING_MODEL_KEY = 'rowGroupingModel';
707
+ const AGGREGATION_MODEL_KEY = 'aggregationModel';
708
+ /** Storage category key for the pivot column/row/value configuration. Consumer interop — use with `buildStorageKey`. */
709
+ const PIVOT_MODEL_KEY = 'pivotModel';
710
+ /** Storage category key for whether pivoting is active. Consumer interop — use with `buildStorageKey`. */
711
+ const PIVOT_ACTIVE_KEY = 'pivotActive';
712
+ 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, ROW_GROUPING_MODEL_KEY, AGGREGATION_MODEL_KEY, PIVOT_MODEL_KEY, PIVOT_ACTIVE_KEY];
1937
713
  /**
1938
714
  * Build the localStorage key for a specific grid state category.
1939
715
  * Consumers can use this to read or clear individual state entries directly.
1940
716
  *
1941
717
  * @example
1942
718
  * ```ts
1943
- * const key = buildStorageKey({ id: pathname, version: 2, category: 'filterModel' });
719
+ * const key = buildStorageKey({ id: pathname, version: 2, category: PIVOT_ACTIVE_KEY });
1944
720
  * localStorage.removeItem(key);
1945
721
  * ```
1946
722
  */
@@ -1990,6 +766,22 @@ const clearPreviousVersionStorage = (id, previousLocalStorageVersions) => {
1990
766
  id,
1991
767
  version,
1992
768
  category: COLUMN_ORDER_MODEL_KEY
769
+ }), buildStorageKey({
770
+ id,
771
+ version,
772
+ category: ROW_GROUPING_MODEL_KEY
773
+ }), buildStorageKey({
774
+ id,
775
+ version,
776
+ category: AGGREGATION_MODEL_KEY
777
+ }), buildStorageKey({
778
+ id,
779
+ version,
780
+ category: PIVOT_MODEL_KEY
781
+ }), buildStorageKey({
782
+ id,
783
+ version,
784
+ category: PIVOT_ACTIVE_KEY
1993
785
  })];
1994
786
  for (const keyToDelete of keysToDelete) {
1995
787
  try {
@@ -2064,13 +856,14 @@ const resetStatefulDataGridState = _ref2 => {
2064
856
  * default (all columns visible). Clears the persisted `visibilityModel`
2065
857
  * localStorage entry for the given versions and strips the `_columnVisibility`
2066
858
  * param from the live URL, leaving every other piece of grid state (filters,
2067
- * sort, pagination, pinned columns, …) untouched.
859
+ * sort, pivot, pagination, pinned columns, …) untouched.
2068
860
  *
2069
861
  * This is the visibility-scoped counterpart to `resetStatefulDataGridState`.
2070
- * Reach for it at transition points where a stale visibility snapshot would
2071
- * otherwise re-seed and keep base columns hidden. Because it leaves the other
2072
- * params alone it will not clobber a filter the caller is writing in the same
2073
- * transition.
862
+ * Reach for it at transition points — for example drilling down from pivot
863
+ * mode to a flat record view — where a stale pivot-era visibility snapshot
864
+ * would otherwise re-seed and keep base columns hidden. Because it leaves the
865
+ * other params alone it will not clobber a filter the caller is writing in the
866
+ * same transition.
2074
867
  *
2075
868
  * Like `resetStatefulDataGridState`, the URL strip reads `window.location.search`
2076
869
  * directly so it operates on the live URL (the caller's captured snapshot may
@@ -2120,7 +913,7 @@ const COMPRESSED_PREFIX = '~';
2120
913
  * Params listed first are compressed first (least valuable to read in the URL).
2121
914
  * The filter aggregate step uses the special key `_filters_aggregate`.
2122
915
  */
2123
- const COMPRESSION_PRIORITY = ['_columnOrder', '_columnVisibility', '_pinnedColumnsLeft', '_pinnedColumnsRight', '_filters_aggregate', '_quickFilterValues'];
916
+ const COMPRESSION_PRIORITY = ['_columnOrder', '_columnVisibility', '_pinnedColumnsLeft', '_pinnedColumnsRight', '_filters_aggregate', '_aggregation', '_rowGrouping', '_quickFilterValues', '_pivot'];
2124
917
 
2125
918
  /** Params that are always short and should never be compressed. */
2126
919
  const NEVER_COMPRESS = new Set(['_sortColumn', '_pagination', '_density', '_logicOperator', 'v', 'tab']);
@@ -2361,6 +1154,13 @@ const convertToDisplayFormat = search => {
2361
1154
  const cleanSearch = search.startsWith('?') ? search.slice(1) : search;
2362
1155
  const params = cleanSearch.split('&');
2363
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
+
2364
1164
  // Handle _sortColumn=[field,direction]
2365
1165
  if (param.startsWith('_sortColumn=')) {
2366
1166
  const value = param.slice('_sortColumn='.length);
@@ -2415,7 +1215,6 @@ const convertToDisplayFormat = search => {
2415
1215
  // Handle _columnOrder=[a,b,c]
2416
1216
  if (param.startsWith('_columnOrder=')) {
2417
1217
  const value = param.slice('_columnOrder='.length);
2418
- if (value.startsWith('~')) return param; // compressed — skip
2419
1218
  if (value.startsWith('[') && value.endsWith(']')) {
2420
1219
  const inner = value.slice(1, -1);
2421
1220
  return `_columnOrder=${inner}`;
@@ -2423,6 +1222,21 @@ const convertToDisplayFormat = search => {
2423
1222
  return param;
2424
1223
  }
2425
1224
 
1225
+ // Handle _rowGrouping=[a,b,c]
1226
+ if (param.startsWith('_rowGrouping=')) {
1227
+ const value = param.slice('_rowGrouping='.length);
1228
+ if (value.startsWith('[') && value.endsWith(']')) {
1229
+ const inner = value.slice(1, -1);
1230
+ return `_rowGrouping=${inner}`;
1231
+ }
1232
+ return param;
1233
+ }
1234
+
1235
+ // _aggregation and _pivot do not use bracket notation — pass through
1236
+ if (param.startsWith('_aggregation=') || param.startsWith('_pivot=')) {
1237
+ return param;
1238
+ }
1239
+
2426
1240
  // Handle _field[operator,type]=value or _field[operator,type]=list[a,b,c]
2427
1241
  const bracketMatch = param.match(/^_([^[]+)\[([^\]]+)\]=(.*)$/);
2428
1242
  if (bracketMatch) {
@@ -2462,6 +1276,13 @@ const convertFromDisplayFormat = (search, columns) => {
2462
1276
  const cleanSearch = search.startsWith('?') ? search.slice(1) : search;
2463
1277
  const params = cleanSearch.split('&');
2464
1278
  const converted = params.map(param => {
1279
+ const eqIndex = param.indexOf('=');
1280
+ if (eqIndex !== -1) {
1281
+ const value = param.slice(eqIndex + 1);
1282
+ // Skip conversion for compressed values — already URL-safe
1283
+ if (isCompressed(value)) return param;
1284
+ }
1285
+
2465
1286
  // Handle _sortColumn=field.direction or _sortColumn=
2466
1287
  if (param.startsWith('_sortColumn=')) {
2467
1288
  const value = param.slice('_sortColumn='.length);
@@ -2513,8 +1334,8 @@ const convertFromDisplayFormat = (search, columns) => {
2513
1334
  // Handle _columnVisibility display `a,b,!c` → internal `[a,b,!c]`.
2514
1335
  if (param.startsWith('_columnVisibility=')) {
2515
1336
  const value = param.slice('_columnVisibility='.length);
2516
- // If it already has brackets, leave it alone
2517
- if (value.startsWith('[') || value.startsWith('~')) {
1337
+ // Already internal (bracketed) — leave it alone.
1338
+ if (value.startsWith('[')) {
2518
1339
  return param;
2519
1340
  }
2520
1341
  return `_columnVisibility=[${value}]`;
@@ -2523,13 +1344,26 @@ const convertFromDisplayFormat = (search, columns) => {
2523
1344
  // Handle _columnOrder=a,b,c
2524
1345
  if (param.startsWith('_columnOrder=')) {
2525
1346
  const value = param.slice('_columnOrder='.length);
2526
- // If it already has brackets or is compressed, leave it alone
2527
- if (value.startsWith('[') || value.startsWith('~')) {
1347
+ if (value.startsWith('[')) {
2528
1348
  return param;
2529
1349
  }
2530
1350
  return `_columnOrder=[${value}]`;
2531
1351
  }
2532
1352
 
1353
+ // Handle _rowGrouping=a,b,c
1354
+ if (param.startsWith('_rowGrouping=')) {
1355
+ const value = param.slice('_rowGrouping='.length);
1356
+ if (value.startsWith('[')) {
1357
+ return param;
1358
+ }
1359
+ return `_rowGrouping=[${value}]`;
1360
+ }
1361
+
1362
+ // _aggregation, _pivot, _filters — pass through (no bracket conversion needed)
1363
+ if (param.startsWith('_aggregation=') || param.startsWith('_pivot=') || param.startsWith('_filters=')) {
1364
+ return param;
1365
+ }
1366
+
2533
1367
  // Handle field.operator=value (dot notation for filters)
2534
1368
  const dotMatch = param.match(/^([^.]+)\.([a-zA-Z_]+)=(.*)$/);
2535
1369
  if (dotMatch) {
@@ -2572,9 +1406,10 @@ const getDecodedSearchFromUrl = (search, columns) => {
2572
1406
  const hasPaginationDotNotation = /_pagination=[^&[]+\.[^&]+/.test(searchWithoutLeadingQuestion);
2573
1407
  const hasPinnedWithoutBrackets = /(_pinnedColumnsLeft|_pinnedColumnsRight)=[^&[]*(&|$)/.test(searchWithoutLeadingQuestion);
2574
1408
  const hasVisibilityWithoutBrackets = /_columnVisibility=[^&[]*(&|$)/.test(searchWithoutLeadingQuestion);
2575
- const hasColumnOrderWithoutBrackets = /_columnOrder=[^&[~]*(&|$)/.test(searchWithoutLeadingQuestion);
1409
+ const hasColumnOrderWithoutBrackets = /_columnOrder=[^&[]*(&|$)/.test(searchWithoutLeadingQuestion);
1410
+ const hasRowGroupingWithoutBrackets = /_rowGrouping=[^&[]*(&|$)/.test(searchWithoutLeadingQuestion);
2576
1411
  const hasBracketNotation = /\[.*\]=/.test(searchWithoutLeadingQuestion);
2577
- const isDisplayFormat = (hasDotNotationFilter || hasEmptySortColumn || hasSortDotNotation || hasPaginationDotNotation || hasPinnedWithoutBrackets || hasVisibilityWithoutBrackets || hasColumnOrderWithoutBrackets) && !hasBracketNotation;
1412
+ const isDisplayFormat = (hasDotNotationFilter || hasEmptySortColumn || hasSortDotNotation || hasPaginationDotNotation || hasPinnedWithoutBrackets || hasVisibilityWithoutBrackets || hasColumnOrderWithoutBrackets || hasRowGroupingWithoutBrackets) && !hasBracketNotation;
2578
1413
  if (isDisplayFormat) {
2579
1414
  return '?' + convertFromDisplayFormat(searchWithoutLeadingQuestion, columns);
2580
1415
  }
@@ -2730,7 +1565,7 @@ const isValueValid = (value, field, columns, operator) => {
2730
1565
 
2731
1566
  // example:
2732
1567
  // unicodeDomain[contains]=a&unicodeDomain[contains]=dsa&logicOperator=and&tab=ignored
2733
- const getFilterModelFromString = (searchString, columns) => {
1568
+ const getFilterModelFromString = (searchString, columns, previousModel) => {
2734
1569
  if (!searchString) {
2735
1570
  return 'invalid';
2736
1571
  }
@@ -2743,7 +1578,7 @@ const getFilterModelFromString = (searchString, columns) => {
2743
1578
  let hasFilterMeta = false;
2744
1579
  const searchParams = new URLSearchParams();
2745
1580
  for (const [key, value] of new URLSearchParams(searchString)) {
2746
- if (key.startsWith('_') && !['_logicOperator', '_sortColumn', '_pinnedColumnsLeft', '_pinnedColumnsRight', '_columnVisibility', '_pagination', '_quickFilterValues', '_columnOrder', '_density', '_filters'].includes(key)) {
1581
+ if (key.startsWith('_') && !['_logicOperator', '_sortColumn', '_pinnedColumnsLeft', '_pinnedColumnsRight', '_columnVisibility', '_pagination', '_quickFilterValues', '_columnOrder', '_rowGrouping', '_aggregation', '_pivot', '_density', '_filters'].includes(key)) {
2747
1582
  searchParams.set(key, value);
2748
1583
  }
2749
1584
  if (key === '_logicOperator') {
@@ -2760,6 +1595,21 @@ const getFilterModelFromString = (searchString, columns) => {
2760
1595
  }
2761
1596
  }
2762
1597
  let id = 5000;
1598
+
1599
+ // Positional id-reuse map keyed by `${field}|${operator}` (decoded operator, matching
1600
+ // the item we push below). FIFO per key so duplicate field+operator items map by order.
1601
+ // Reusing the id of a previously parsed/emitted item keeps `item.id` stable across the
1602
+ // URL write→parse roundtrip (the URL never carries the id), so MUI's GridFilterForm
1603
+ // React key does not change and the filter panel input keeps focus. (DS-81)
1604
+ const previousIdsByKey = new Map();
1605
+ if (previousModel) {
1606
+ for (const prev of previousModel.items) {
1607
+ if (prev.id === undefined) continue;
1608
+ const key = `${prev.field}|${prev.operator}`;
1609
+ const bucket = previousIdsByKey.get(key);
1610
+ if (bucket) bucket.push(prev.id);else previousIdsByKey.set(key, [prev.id]);
1611
+ }
1612
+ }
2763
1613
  const fields = columns.map(column => column.field);
2764
1614
  let isInvalid = false;
2765
1615
  const items = [];
@@ -2795,10 +1645,13 @@ const getFilterModelFromString = (searchString, columns) => {
2795
1645
  isInvalid = true;
2796
1646
  return;
2797
1647
  }
1648
+ const finalOperator = columnType === 'number' && Object.keys(numberOperatorDecoder).includes(operator) ? numberOperatorDecoder[operator] : operator;
1649
+ const reuseBucket = previousIdsByKey.get(`${field}|${finalOperator}`);
1650
+ const stableId = reuseBucket && reuseBucket.length > 0 ? reuseBucket.shift() : id;
2798
1651
  items.push({
2799
1652
  field,
2800
- operator: columnType === 'number' && Object.keys(numberOperatorDecoder).includes(operator) ? numberOperatorDecoder[operator] : operator,
2801
- id,
1653
+ operator: finalOperator,
1654
+ id: stableId,
2802
1655
  value: listOperators.includes(operator) && decodedValue === '' ? [] : decodedValue,
2803
1656
  type
2804
1657
  });
@@ -2892,7 +1745,7 @@ const getSearchParamsFromFilterModel = filterModel => {
2892
1745
  // - if we have something in the URL, use that info
2893
1746
  // - if we don't have that, use the localStorage and update the URL
2894
1747
  // - if we don't have that, return an empty FilterModel
2895
- const getFilterModel = (search, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion) => {
1748
+ const getFilterModel = (search, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion, previousModel) => {
2896
1749
  const defaultValue = initialState && initialState.filter && initialState.filter.filterModel ? initialState.filter.filterModel : {
2897
1750
  items: [],
2898
1751
  logicOperator: GridLogicOperator.And
@@ -2910,7 +1763,7 @@ const getFilterModel = (search, columns, localStorageFilters, setLocalStorageFil
2910
1763
  persistDefaultFilters();
2911
1764
  return defaultValue;
2912
1765
  }
2913
- const filterModelFromSearch = getFilterModelFromString(search, columns);
1766
+ const filterModelFromSearch = getFilterModelFromString(search, columns, previousModel);
2914
1767
  if (filterModelFromSearch !== 'invalid') {
2915
1768
  const searchFromFilterModel = getSearchParamsFromFilterModel(filterModelFromSearch);
2916
1769
  const searchString = urlSearchParamsToString(searchFromFilterModel);
@@ -3093,8 +1946,8 @@ const getColumnVisibilityFromString = (searchString, columns) => {
3093
1946
  // by 12.5.5-muiv8-alpha.5/alpha.6 (replaced by the URL-safe `!` form below — see
3094
1947
  // getSearchParamsFromColumnVisibility). Still parsed so any localStorage entry
3095
1948
  // persisted by those alphas keeps working; never written anymore. The two sets are
3096
- // split on the fixed `];h:[` separator, so a `[` or `]` inside a field name does
3097
- // not break parsing.
1949
+ // split on the fixed `];h:[` separator, so a `[` or `]` inside a pivot field name
1950
+ // (e.g. `["gmail"]>->email_volume`) does not break parsing.
3098
1951
  if (value.startsWith('v:[') && value.endsWith(']') && value.includes('];h:[')) {
3099
1952
  const inner = value.slice('v:['.length, -1);
3100
1953
  const separatorIndex = inner.indexOf('];h:[');
@@ -3184,14 +2037,14 @@ const getSearchParamsFromColumnVisibility = (columnVisibility, columns) => {
3184
2037
  }, columnVisibility);
3185
2038
 
3186
2039
  // Serialise a single comma list over the static columns plus any extra keys present
3187
- // in the model (dynamically-generated fields, preserved in model order). Hidden
3188
- // fields are prefixed `!`; visible fields are bare. This URL-safe form round-trips
3189
- // idempotently through `URLSearchParams.toString()` percent-encoding — the earlier
3190
- // `v:[..];h:[..]` form did NOT (its `:`/`;` were percent-encoded and mis-parsed on
3191
- // read-back, driving an unbounded `history.replace` loop, ODM-3033). `!` (not `~`,
3192
- // the compression sentinel — compression.ts COMPRESSED_PREFIX) is used so a leading
3193
- // hidden field can't be mistaken for a compressed value. The hidden set is still
3194
- // explicit, so deselected dynamic columns survive reloads.
2040
+ // in the model (dynamically-generated pivot fields, preserved in model order).
2041
+ // Hidden fields are prefixed `!`; visible fields are bare. This URL-safe form
2042
+ // round-trips idempotently through `URLSearchParams.toString()` percent-encoding —
2043
+ // the earlier `v:[..];h:[..]` form did NOT (its `:`/`;` were percent-encoded and
2044
+ // mis-parsed on read-back, driving an unbounded `history.replace` loop, ODM-3033).
2045
+ // `!` (not `~`, which is the compression sentinel — compression.ts COMPRESSED_PREFIX)
2046
+ // is used so a leading hidden field can't be mistaken for a compressed value.
2047
+ // The hidden set is still explicit, so deselected dynamic columns survive reloads.
3195
2048
  const allFields = [...fields];
3196
2049
  for (const field of Object.keys(finalColumnVisibility)) {
3197
2050
  if (!allFields.includes(field)) {
@@ -3354,8 +2207,7 @@ const getSearchParamsFromDensity = density => {
3354
2207
  return searchParams;
3355
2208
  };
3356
2209
  const getDensityModel = (search, localStorageDensity, setLocalStorageDensity, initialState, isNewVersion) => {
3357
- // MUI v6 GridInitialStatePro does not expose density on its type, but consumers
3358
- // can still pass it through. Honour it when provided, otherwise fall back to 'compact'.
2210
+ // Default density: honour initialState.density if valid, otherwise fall back to 'compact'
3359
2211
  const initialDensity = initialState === null || initialState === void 0 ? void 0 : initialState.density;
3360
2212
  const defaultValue = initialDensity && VALID_DENSITIES.includes(initialDensity) ? initialDensity : 'compact';
3361
2213
 
@@ -3408,29 +2260,6 @@ const getSearchParamsFromColumnOrder = columnOrder => {
3408
2260
  }
3409
2261
  return searchParams;
3410
2262
  };
3411
-
3412
- /**
3413
- * Builds the `v=<version>` search param the grid uses to detect stale URLs.
3414
- *
3415
- * Use this when constructing a deep link or share URL outside the grid (e.g. via
3416
- * `getSearchParamsFromFilterModel` + `buildQueryParamsString`). The grid itself
3417
- * always includes `v` in URLs it writes via `getFinalSearch`, but external helpers
3418
- * do not — include this when you want a stale `localStorageVersion` bump to invalidate
3419
- * the link. Omitting `v` is also safe: a missing `v` is treated as "current version"
3420
- * and the URL state is applied as-is.
3421
- *
3422
- * @example
3423
- * const params = new URLSearchParams([
3424
- * ...getSearchParamsFromFilterModel(filterModel),
3425
- * ...getSearchParamsFromVersion(1),
3426
- * ]);
3427
- * const url = `/my-grid${buildQueryParamsString(urlSearchParamsToString(params))}`;
3428
- */
3429
- const getSearchParamsFromVersion = version => {
3430
- const searchParams = new URLSearchParams();
3431
- searchParams.set('v', String(version));
3432
- return searchParams;
3433
- };
3434
2263
  const getColumnOrder = (search, columns, localStorageColumnOrder, setLocalStorageColumnOrder, initialState, isNewVersion) => {
3435
2264
  var _initialState$columns4, _initialState$columns5;
3436
2265
  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);
@@ -3461,46 +2290,345 @@ const getColumnOrder = (search, columns, localStorageColumnOrder, setLocalStorag
3461
2290
  persistDefault();
3462
2291
  return defaultValue;
3463
2292
  };
3464
- const getFinalSearch = _ref => {
3465
- let {
3466
- search,
3467
- localStorageVersion,
3468
- filterModel,
3469
- sortModel,
3470
- paginationModel,
3471
- columnsVisibilityModel,
3472
- pinnedColumnsModel,
3473
- density,
3474
- columnOrderModel,
3475
- defaultColumnOrder,
3476
- columns
3477
- } = _ref;
3478
- const filterModelSearch = getSearchParamsFromFilterModel(filterModel);
3479
- const sortModelSearch = getSearchParamsFromSorting(sortModel);
3480
- const paginationModelSearch = getSearchParamsFromPagination(paginationModel);
3481
- const columnVisibilityModelSearch = getSearchParamsFromColumnVisibility(columnsVisibilityModel, columns);
3482
- const pinnedColumnsModelSearch = getSearchParamsFromPinnedColumns(pinnedColumnsModel);
3483
- const densitySearch = getSearchParamsFromDensity(density);
3484
- // Only include _columnOrder in URL when it differs from the default
3485
- const columnOrderSearch = columnOrderModel.length !== defaultColumnOrder.length || columnOrderModel.some((field, i) => field !== defaultColumnOrder[i]) ? getSearchParamsFromColumnOrder(columnOrderModel) : new URLSearchParams();
3486
- const tabSearch = getSearchParamsFromTab(search);
2293
+
2294
+ /** ROW GROUPING */
2295
+
2296
+ const getRowGroupingFromString = searchString => {
2297
+ if (!searchString) return 'invalid';
2298
+ const searchParams = new URLSearchParams(searchString);
2299
+ const value = searchParams.get('_rowGrouping');
2300
+ if (value === '' || value === null || value === '[]') return 'invalid';
2301
+ const inner = value.startsWith('[') && value.endsWith(']') ? value.slice(1, -1) : value;
2302
+ if (!inner) return 'invalid';
2303
+ return inner.split(',').filter(Boolean);
2304
+ };
2305
+ const getSearchParamsFromRowGrouping = rowGrouping => {
3487
2306
  const searchParams = new URLSearchParams();
3488
- for (const [key, value] of new URLSearchParams(search)) {
3489
- if (!key.startsWith('_')) {
3490
- searchParams.set(key, value);
2307
+ if (rowGrouping.length > 0) {
2308
+ searchParams.set('_rowGrouping', `[${rowGrouping.join(',')}]`);
2309
+ }
2310
+ return searchParams;
2311
+ };
2312
+ const getRowGroupingModel = (search, localStorageRowGrouping, setLocalStorageRowGrouping, initialState, isNewVersion) => {
2313
+ var _initialState$rowGrou, _initialState$rowGrou2;
2314
+ const defaultValue = (_initialState$rowGrou = initialState === null || initialState === void 0 ? void 0 : (_initialState$rowGrou2 = initialState.rowGrouping) === null || _initialState$rowGrou2 === void 0 ? void 0 : _initialState$rowGrou2.model) !== null && _initialState$rowGrou !== void 0 ? _initialState$rowGrou : [];
2315
+ const persistDefault = () => {
2316
+ const searchFromDefault = getSearchParamsFromRowGrouping(defaultValue);
2317
+ const searchString = urlSearchParamsToString(searchFromDefault);
2318
+ if (searchString !== localStorageRowGrouping) {
2319
+ setLocalStorageRowGrouping(searchString);
2320
+ }
2321
+ };
2322
+ if (isNewVersion) {
2323
+ persistDefault();
2324
+ return defaultValue;
2325
+ }
2326
+ const fromUrl = getRowGroupingFromString(search);
2327
+ if (fromUrl !== 'invalid') {
2328
+ const searchFromModel = getSearchParamsFromRowGrouping(fromUrl);
2329
+ const searchString = urlSearchParamsToString(searchFromModel);
2330
+ if (searchString !== localStorageRowGrouping) {
2331
+ setLocalStorageRowGrouping(searchString);
3491
2332
  }
2333
+ return fromUrl;
3492
2334
  }
3493
- searchParams.set('v', `${localStorageVersion}`);
2335
+ const fromLocalStorage = getRowGroupingFromString(localStorageRowGrouping);
2336
+ if (fromLocalStorage !== 'invalid') {
2337
+ return fromLocalStorage;
2338
+ }
2339
+ persistDefault();
2340
+ return defaultValue;
2341
+ };
3494
2342
 
3495
- // Add quickFilterValues explicitly if present in filterModel
3496
- if (filterModel.quickFilterValues && filterModel.quickFilterValues.length > 0) {
3497
- // Encode array as JSON string to preserve all values in one param
3498
- searchParams.set('_quickFilterValues', encodeURIComponent(JSON.stringify(filterModel.quickFilterValues)));
2343
+ /** AGGREGATION */
2344
+
2345
+ const getAggregationFromString = searchString => {
2346
+ if (!searchString) return 'invalid';
2347
+ const searchParams = new URLSearchParams(searchString);
2348
+ const value = searchParams.get('_aggregation');
2349
+ if (value === '' || value === null) return 'invalid';
2350
+
2351
+ // Format: field1.sum,field2.avg or [field1.sum,field2.avg]
2352
+ const inner = value.startsWith('[') && value.endsWith(']') ? value.slice(1, -1) : value;
2353
+ if (!inner) return 'invalid';
2354
+ const model = {};
2355
+ for (const entry of inner.split(',')) {
2356
+ const dotIndex = entry.lastIndexOf('.');
2357
+ if (dotIndex <= 0) return 'invalid';
2358
+ const field = entry.slice(0, dotIndex);
2359
+ const aggFunc = entry.slice(dotIndex + 1);
2360
+ if (!field || !aggFunc) return 'invalid';
2361
+ model[field] = aggFunc;
3499
2362
  }
3500
- return new URLSearchParams([...searchParams, ...filterModelSearch, ...sortModelSearch, ...paginationModelSearch, ...tabSearch, ...pinnedColumnsModelSearch, ...columnVisibilityModelSearch, ...densitySearch, ...columnOrderSearch]);
2363
+ return Object.keys(model).length > 0 ? model : 'invalid';
2364
+ };
2365
+ const getSearchParamsFromAggregation = aggregation => {
2366
+ const searchParams = new URLSearchParams();
2367
+ const entries = Object.entries(aggregation);
2368
+ if (entries.length > 0) {
2369
+ const value = entries.map(_ref => {
2370
+ let [field, aggFunc] = _ref;
2371
+ return `${field}.${aggFunc}`;
2372
+ }).join(',');
2373
+ searchParams.set('_aggregation', value);
2374
+ }
2375
+ return searchParams;
2376
+ };
2377
+ const getAggregationModel = (search, localStorageAggregation, setLocalStorageAggregation, initialState, isNewVersion) => {
2378
+ var _initialState$aggrega, _initialState$aggrega2;
2379
+ const defaultValue = (_initialState$aggrega = initialState === null || initialState === void 0 ? void 0 : (_initialState$aggrega2 = initialState.aggregation) === null || _initialState$aggrega2 === void 0 ? void 0 : _initialState$aggrega2.model) !== null && _initialState$aggrega !== void 0 ? _initialState$aggrega : {};
2380
+ const persistDefault = () => {
2381
+ const searchFromDefault = getSearchParamsFromAggregation(defaultValue);
2382
+ const searchString = urlSearchParamsToString(searchFromDefault);
2383
+ if (searchString !== localStorageAggregation) {
2384
+ setLocalStorageAggregation(searchString);
2385
+ }
2386
+ };
2387
+ if (isNewVersion) {
2388
+ persistDefault();
2389
+ return defaultValue;
2390
+ }
2391
+ const fromUrl = getAggregationFromString(search);
2392
+ if (fromUrl !== 'invalid') {
2393
+ const searchFromModel = getSearchParamsFromAggregation(fromUrl);
2394
+ const searchString = urlSearchParamsToString(searchFromModel);
2395
+ if (searchString !== localStorageAggregation) {
2396
+ setLocalStorageAggregation(searchString);
2397
+ }
2398
+ return fromUrl;
2399
+ }
2400
+ const fromLocalStorage = getAggregationFromString(localStorageAggregation);
2401
+ if (fromLocalStorage !== 'invalid') {
2402
+ return fromLocalStorage;
2403
+ }
2404
+ persistDefault();
2405
+ return defaultValue;
2406
+ };
2407
+
2408
+ /** PIVOT */
2409
+
2410
+ /** Convert MUI's GridPivotModel → our simplified PivotModel */
2411
+ const fromGridPivotModel = model => ({
2412
+ columns: model.columns.map(c => c.field),
2413
+ rows: model.rows.map(r => r.field),
2414
+ values: model.values.map(_ref2 => {
2415
+ let {
2416
+ field,
2417
+ aggFunc
2418
+ } = _ref2;
2419
+ return {
2420
+ field,
2421
+ aggFunc
2422
+ };
2423
+ })
2424
+ });
2425
+
2426
+ /**
2427
+ * Pivot format: `cols:f1,f2;rows:f3;vals:f4.sum,f5.avg`
2428
+ */
2429
+ const getPivotFromString = searchString => {
2430
+ if (!searchString) return 'invalid';
2431
+ const searchParams = new URLSearchParams(searchString);
2432
+ const value = searchParams.get('_pivot');
2433
+ if (value === '' || value === null) return 'invalid';
2434
+ const model = {
2435
+ columns: [],
2436
+ rows: [],
2437
+ values: []
2438
+ };
2439
+ for (const segment of value.split(';')) {
2440
+ const colonIndex = segment.indexOf(':');
2441
+ if (colonIndex <= 0) return 'invalid';
2442
+ const key = segment.slice(0, colonIndex);
2443
+ const content = segment.slice(colonIndex + 1);
2444
+ if (key === 'cols') {
2445
+ model.columns = content ? content.split(',').filter(Boolean) : [];
2446
+ } else if (key === 'rows') {
2447
+ model.rows = content ? content.split(',').filter(Boolean) : [];
2448
+ } else if (key === 'vals') {
2449
+ if (!content) continue;
2450
+ for (const entry of content.split(',')) {
2451
+ const dotIndex = entry.lastIndexOf('.');
2452
+ if (dotIndex <= 0) return 'invalid';
2453
+ model.values.push({
2454
+ field: entry.slice(0, dotIndex),
2455
+ aggFunc: entry.slice(dotIndex + 1)
2456
+ });
2457
+ }
2458
+ }
2459
+ }
2460
+
2461
+ // At least one section must have content
2462
+ if (model.columns.length === 0 && model.rows.length === 0 && model.values.length === 0) {
2463
+ return 'invalid';
2464
+ }
2465
+ return model;
2466
+ };
2467
+ const getSearchParamsFromPivot = pivot => {
2468
+ const searchParams = new URLSearchParams();
2469
+ const hasContent = pivot.columns.length > 0 || pivot.rows.length > 0 || pivot.values.length > 0;
2470
+ if (hasContent) {
2471
+ const parts = [];
2472
+ parts.push(`cols:${pivot.columns.join(',')}`);
2473
+ parts.push(`rows:${pivot.rows.join(',')}`);
2474
+ if (pivot.values.length > 0) {
2475
+ parts.push(`vals:${pivot.values.map(v => `${v.field}.${v.aggFunc}`).join(',')}`);
2476
+ }
2477
+ searchParams.set('_pivot', parts.join(';'));
2478
+ }
2479
+ return searchParams;
2480
+ };
2481
+ const getPivotModel = (search, localStoragePivot, setLocalStoragePivot, initialState, isNewVersion) => {
2482
+ var _initialState$pivotin;
2483
+ const defaultValue = initialState !== null && initialState !== void 0 && (_initialState$pivotin = initialState.pivoting) !== null && _initialState$pivotin !== void 0 && _initialState$pivotin.model ? fromGridPivotModel(initialState.pivoting.model) : {
2484
+ columns: [],
2485
+ rows: [],
2486
+ values: []
2487
+ };
2488
+ const persistDefault = () => {
2489
+ const searchFromDefault = getSearchParamsFromPivot(defaultValue);
2490
+ const searchString = urlSearchParamsToString(searchFromDefault);
2491
+ if (searchString !== localStoragePivot) {
2492
+ setLocalStoragePivot(searchString);
2493
+ }
2494
+ };
2495
+ if (isNewVersion) {
2496
+ persistDefault();
2497
+ return defaultValue;
2498
+ }
2499
+ const fromUrl = getPivotFromString(search);
2500
+ if (fromUrl !== 'invalid') {
2501
+ const searchFromModel = getSearchParamsFromPivot(fromUrl);
2502
+ const searchString = urlSearchParamsToString(searchFromModel);
2503
+ if (searchString !== localStoragePivot) {
2504
+ setLocalStoragePivot(searchString);
2505
+ }
2506
+ return fromUrl;
2507
+ }
2508
+ const fromLocalStorage = getPivotFromString(localStoragePivot);
2509
+ if (fromLocalStorage !== 'invalid') {
2510
+ return fromLocalStorage;
2511
+ }
2512
+ persistDefault();
2513
+ return defaultValue;
2514
+ };
2515
+
2516
+ /** PIVOT ACTIVE */
2517
+
2518
+ const getPivotActiveFromString = searchString => {
2519
+ if (!searchString) return 'invalid';
2520
+ const searchParams = new URLSearchParams(searchString);
2521
+ const value = searchParams.get('_pivotActive');
2522
+ if (value === 'true') return true;
2523
+ if (value === 'false') return false;
2524
+ return 'invalid';
2525
+ };
2526
+ const getSearchParamsFromPivotActive = active => {
2527
+ const searchParams = new URLSearchParams();
2528
+ searchParams.set('_pivotActive', String(active));
2529
+ return searchParams;
2530
+ };
2531
+
2532
+ /**
2533
+ * Builds the `v=<version>` search param the grid uses to detect stale URLs.
2534
+ *
2535
+ * Use this when constructing a deep link or share URL outside the grid (e.g. via
2536
+ * `getSearchParamsFromFilterModel` + `buildQueryParamsString`). The grid itself
2537
+ * always includes `v` in URLs it writes via `getFinalSearch`, but external helpers
2538
+ * do not — include this when you want a stale `localStorageVersion` bump to invalidate
2539
+ * the link. Omitting `v` is also safe: a missing `v` is treated as "current version"
2540
+ * and the URL state is applied as-is.
2541
+ *
2542
+ * @example
2543
+ * const params = new URLSearchParams([
2544
+ * ...getSearchParamsFromFilterModel(filterModel),
2545
+ * ...getSearchParamsFromVersion(1),
2546
+ * ]);
2547
+ * const url = `/my-grid${buildQueryParamsString(urlSearchParamsToString(params))}`;
2548
+ */
2549
+ const getSearchParamsFromVersion = version => {
2550
+ const searchParams = new URLSearchParams();
2551
+ searchParams.set('v', String(version));
2552
+ return searchParams;
2553
+ };
2554
+ const getPivotActive = (search, localStoragePivotActive, setLocalStoragePivotActive, initialState, isNewVersion) => {
2555
+ var _initialState$pivotin2, _initialState$pivotin3;
2556
+ const defaultValue = (_initialState$pivotin2 = initialState === null || initialState === void 0 ? void 0 : (_initialState$pivotin3 = initialState.pivoting) === null || _initialState$pivotin3 === void 0 ? void 0 : _initialState$pivotin3.enabled) !== null && _initialState$pivotin2 !== void 0 ? _initialState$pivotin2 : false;
2557
+ const persistDefault = () => {
2558
+ const searchFromDefault = getSearchParamsFromPivotActive(defaultValue);
2559
+ const searchString = urlSearchParamsToString(searchFromDefault);
2560
+ if (searchString !== localStoragePivotActive) {
2561
+ setLocalStoragePivotActive(searchString);
2562
+ }
2563
+ };
2564
+ if (isNewVersion) {
2565
+ persistDefault();
2566
+ return defaultValue;
2567
+ }
2568
+ const fromUrl = getPivotActiveFromString(search);
2569
+ if (fromUrl !== 'invalid') {
2570
+ const searchFromModel = getSearchParamsFromPivotActive(fromUrl);
2571
+ const searchString = urlSearchParamsToString(searchFromModel);
2572
+ if (searchString !== localStoragePivotActive) {
2573
+ setLocalStoragePivotActive(searchString);
2574
+ }
2575
+ return fromUrl;
2576
+ }
2577
+ const fromLocalStorage = getPivotActiveFromString(localStoragePivotActive);
2578
+ if (fromLocalStorage !== 'invalid') {
2579
+ return fromLocalStorage;
2580
+ }
2581
+ persistDefault();
2582
+ return defaultValue;
2583
+ };
2584
+ const getFinalSearch = _ref3 => {
2585
+ let {
2586
+ search,
2587
+ localStorageVersion,
2588
+ filterModel,
2589
+ sortModel,
2590
+ paginationModel,
2591
+ columnsVisibilityModel,
2592
+ pinnedColumnsModel,
2593
+ density,
2594
+ columnOrderModel,
2595
+ defaultColumnOrder,
2596
+ rowGroupingModel,
2597
+ aggregationModel,
2598
+ pivotModel,
2599
+ pivotActive,
2600
+ columns
2601
+ } = _ref3;
2602
+ const filterModelSearch = getSearchParamsFromFilterModel(filterModel);
2603
+ const sortModelSearch = getSearchParamsFromSorting(sortModel);
2604
+ const paginationModelSearch = getSearchParamsFromPagination(paginationModel);
2605
+ const columnVisibilityModelSearch = getSearchParamsFromColumnVisibility(columnsVisibilityModel, columns);
2606
+ const pinnedColumnsModelSearch = getSearchParamsFromPinnedColumns(pinnedColumnsModel);
2607
+ const densitySearch = getSearchParamsFromDensity(density);
2608
+ // Only include _columnOrder in URL when it differs from the default
2609
+ const columnOrderSearch = columnOrderModel.length !== defaultColumnOrder.length || columnOrderModel.some((field, i) => field !== defaultColumnOrder[i]) ? getSearchParamsFromColumnOrder(columnOrderModel) : new URLSearchParams();
2610
+ const rowGroupingSearch = getSearchParamsFromRowGrouping(rowGroupingModel);
2611
+ const aggregationSearch = getSearchParamsFromAggregation(aggregationModel);
2612
+ const pivotSearch = getSearchParamsFromPivot(pivotModel);
2613
+ const pivotActiveSearch = getSearchParamsFromPivotActive(pivotActive);
2614
+ const tabSearch = getSearchParamsFromTab(search);
2615
+ const searchParams = new URLSearchParams();
2616
+ for (const [key, value] of new URLSearchParams(search)) {
2617
+ if (!key.startsWith('_')) {
2618
+ searchParams.set(key, value);
2619
+ }
2620
+ }
2621
+ searchParams.set('v', `${localStorageVersion}`);
2622
+
2623
+ // Add quickFilterValues explicitly if present in filterModel
2624
+ if (filterModel.quickFilterValues && filterModel.quickFilterValues.length > 0) {
2625
+ // Encode array as JSON string to preserve all values in one param
2626
+ searchParams.set('_quickFilterValues', encodeURIComponent(JSON.stringify(filterModel.quickFilterValues)));
2627
+ }
2628
+ return new URLSearchParams([...searchParams, ...filterModelSearch, ...sortModelSearch, ...paginationModelSearch, ...tabSearch, ...pinnedColumnsModelSearch, ...columnVisibilityModelSearch, ...densitySearch, ...columnOrderSearch, ...rowGroupingSearch, ...aggregationSearch, ...pivotSearch, ...pivotActiveSearch]);
3501
2629
  };
3502
2630
  /** Return the state of the table given the URL and the local storage state */
3503
- const getModelsParsedOrUpdateLocalStorage = (search, localStorageVersion, columns, initialState, localStorage) => {
2631
+ const getModelsParsedOrUpdateLocalStorage = (search, localStorageVersion, columns, initialState, localStorage, previousFilterModel) => {
3504
2632
  var _initialState$columns6, _initialState$columns7;
3505
2633
  // Decompress any compressed params in the search string before processing
3506
2634
  const decompressedSearch = decompressSearchParams(search);
@@ -3537,15 +2665,27 @@ const getModelsParsedOrUpdateLocalStorage = (search, localStorageVersion, column
3537
2665
  localStorageDensity,
3538
2666
  setLocalStorageDensity,
3539
2667
  localStorageColumnOrder,
3540
- setLocalStorageColumnOrder
2668
+ setLocalStorageColumnOrder,
2669
+ localStorageRowGrouping,
2670
+ setLocalStorageRowGrouping,
2671
+ localStorageAggregation,
2672
+ setLocalStorageAggregation,
2673
+ localStoragePivot,
2674
+ setLocalStoragePivot,
2675
+ localStoragePivotActive,
2676
+ setLocalStoragePivotActive
3541
2677
  } = localStorage;
3542
- const filterModel = getFilterModel(decodedSearch, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion);
2678
+ const filterModel = getFilterModel(decodedSearch, columns, localStorageFilters, setLocalStorageFilters, initialState, isNewVersion, previousFilterModel);
3543
2679
  const sortModel = getSortModel(decodedSearch, columns, localStorageSorting, setLocalStorageSorting, initialState, isNewVersion);
3544
2680
  const paginationModel = getPaginationModel(decodedSearch, localStoragePagination, setLocalStoragePagination, initialState, isNewVersion);
3545
2681
  const columnVisibilityModel = getColumnsVisibility(decodedSearch, columns, localStorageColumnsVisibility, setLocalStorageColumnsVisibility, initialState, isNewVersion);
3546
2682
  const pinnedColumnsModel = getPinnedColumns(decodedSearch, columns, localStoragePinnedColumns, setLocalStoragePinnedColumns, initialState, isNewVersion);
3547
2683
  const density = getDensityModel(decodedSearch, localStorageDensity, setLocalStorageDensity, initialState, isNewVersion);
3548
2684
  const columnOrderModel = getColumnOrder(decodedSearch, columns, localStorageColumnOrder, setLocalStorageColumnOrder, initialState, isNewVersion);
2685
+ const rowGroupingModel = getRowGroupingModel(decodedSearch, localStorageRowGrouping, setLocalStorageRowGrouping, initialState, isNewVersion);
2686
+ const aggregationModel = getAggregationModel(decodedSearch, localStorageAggregation, setLocalStorageAggregation, initialState, isNewVersion);
2687
+ const pivotModel = getPivotModel(decodedSearch, localStoragePivot, setLocalStoragePivot, initialState, isNewVersion);
2688
+ const pivotActive = getPivotActive(decodedSearch, localStoragePivotActive, setLocalStoragePivotActive, initialState, isNewVersion);
3549
2689
  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);
3550
2690
  const finalSearch = getFinalSearch({
3551
2691
  localStorageVersion,
@@ -3558,6 +2698,10 @@ const getModelsParsedOrUpdateLocalStorage = (search, localStorageVersion, column
3558
2698
  density,
3559
2699
  columnOrderModel,
3560
2700
  defaultColumnOrder,
2701
+ rowGroupingModel,
2702
+ aggregationModel,
2703
+ pivotModel,
2704
+ pivotActive,
3561
2705
  columns
3562
2706
  });
3563
2707
  const internalSearchString = urlSearchParamsToString(finalSearch);
@@ -3578,10 +2722,14 @@ const getModelsParsedOrUpdateLocalStorage = (search, localStorageVersion, column
3578
2722
  pinnedColumnsModel,
3579
2723
  density,
3580
2724
  columnOrderModel,
2725
+ rowGroupingModel,
2726
+ aggregationModel,
2727
+ pivotModel,
2728
+ pivotActive,
3581
2729
  pendingSearch
3582
2730
  };
3583
2731
  };
3584
- const updateUrl = (_ref2, search, localStorageVersion, historyReplace, columns) => {
2732
+ const updateUrl = (_ref4, search, localStorageVersion, historyReplace, columns) => {
3585
2733
  let {
3586
2734
  filterModel,
3587
2735
  sortModel,
@@ -3590,8 +2738,12 @@ const updateUrl = (_ref2, search, localStorageVersion, historyReplace, columns)
3590
2738
  pinnedColumnsModel,
3591
2739
  density,
3592
2740
  columnOrderModel,
3593
- defaultColumnOrder
3594
- } = _ref2;
2741
+ defaultColumnOrder,
2742
+ rowGroupingModel,
2743
+ aggregationModel,
2744
+ pivotModel,
2745
+ pivotActive
2746
+ } = _ref4;
3595
2747
  // Convert from display format to internal format if needed
3596
2748
  const decodedSearch = getDecodedSearchFromUrl(search, columns);
3597
2749
  const newSearch = getFinalSearch({
@@ -3605,6 +2757,10 @@ const updateUrl = (_ref2, search, localStorageVersion, historyReplace, columns)
3605
2757
  density,
3606
2758
  columnOrderModel,
3607
2759
  defaultColumnOrder,
2760
+ rowGroupingModel,
2761
+ aggregationModel,
2762
+ pivotModel,
2763
+ pivotActive,
3608
2764
  columns
3609
2765
  });
3610
2766
  const internalSearchString = urlSearchParamsToString(newSearch);
@@ -3797,6 +2953,26 @@ const useTableStates = (id, version) => {
3797
2953
  version,
3798
2954
  category: COLUMN_ORDER_MODEL_KEY
3799
2955
  }));
2956
+ const [rowGroupingModel, setRowGroupingModel] = useFetchState('', buildStorageKey({
2957
+ id,
2958
+ version,
2959
+ category: ROW_GROUPING_MODEL_KEY
2960
+ }));
2961
+ const [aggregationModel, setAggregationModel] = useFetchState('', buildStorageKey({
2962
+ id,
2963
+ version,
2964
+ category: AGGREGATION_MODEL_KEY
2965
+ }));
2966
+ const [pivotModel, setPivotModel] = useFetchState('', buildStorageKey({
2967
+ id,
2968
+ version,
2969
+ category: PIVOT_MODEL_KEY
2970
+ }));
2971
+ const [pivotActive, setPivotActive] = useFetchState('', buildStorageKey({
2972
+ id,
2973
+ version,
2974
+ category: PIVOT_ACTIVE_KEY
2975
+ }));
3800
2976
  return {
3801
2977
  paginationModel,
3802
2978
  setPaginationModel,
@@ -3813,10 +2989,55 @@ const useTableStates = (id, version) => {
3813
2989
  densityModel,
3814
2990
  setDensityModel,
3815
2991
  columnOrderModel,
3816
- setColumnOrderModel
2992
+ setColumnOrderModel,
2993
+ rowGroupingModel,
2994
+ setRowGroupingModel,
2995
+ aggregationModel,
2996
+ setAggregationModel,
2997
+ pivotModel,
2998
+ setPivotModel,
2999
+ pivotActive,
3000
+ setPivotActive
3817
3001
  };
3818
3002
  };
3819
3003
 
3004
+ /** Convert our simplified PivotModel → MUI's GridPivotModel */
3005
+ const toGridPivotModel = model => ({
3006
+ columns: model.columns.map(field => ({
3007
+ field
3008
+ })),
3009
+ rows: model.rows.map(field => ({
3010
+ field
3011
+ })),
3012
+ values: model.values.map(_ref => {
3013
+ let {
3014
+ field,
3015
+ aggFunc
3016
+ } = _ref;
3017
+ return {
3018
+ field,
3019
+ aggFunc
3020
+ };
3021
+ })
3022
+ });
3023
+
3024
+ /**
3025
+ * Deep-equal comparison for plain objects / arrays.
3026
+ * Used to stabilise parsed model references so that MUI v8 does not
3027
+ * reset pagination on every render.
3028
+ */
3029
+ function isDeepEqual(a, b) {
3030
+ if (a === b) return true;
3031
+ if (a == null || b == null) return false;
3032
+ if (typeof a !== typeof b) return false;
3033
+ if (typeof a !== 'object') return false;
3034
+ const aObj = a;
3035
+ const bObj = b;
3036
+ const aKeys = Object.keys(aObj);
3037
+ const bKeys = Object.keys(bObj);
3038
+ if (aKeys.length !== bKeys.length) return false;
3039
+ return aKeys.every(key => isDeepEqual(aObj[key], bObj[key]));
3040
+ }
3820
3041
  const useStatefulTable = props => {
3821
3042
  var _initialState$columns, _initialState$columns2;
3822
3043
  const {
@@ -3829,6 +3050,9 @@ const useStatefulTable = props => {
3829
3050
  onPaginationModelChange: propsOnPaginationModelChange,
3830
3051
  onPinnedColumnsChange: propsOnPinnedColumnsChange,
3831
3052
  onSortModelChange: propsOnSortModelChange,
3053
+ onRowGroupingModelChange: propsOnRowGroupingModelChange,
3054
+ onAggregationModelChange: propsOnAggregationModelChange,
3055
+ onPivotModelChange: propsOnPivotModelChange,
3832
3056
  useRouter,
3833
3057
  localStorageVersion = 1,
3834
3058
  previousLocalStorageVersions = []
@@ -3857,20 +3081,33 @@ const useStatefulTable = props => {
3857
3081
  densityModel,
3858
3082
  setDensityModel,
3859
3083
  columnOrderModel: localStorageColumnOrder,
3860
- setColumnOrderModel: setLocalStorageColumnOrder
3084
+ setColumnOrderModel: setLocalStorageColumnOrder,
3085
+ rowGroupingModel: localStorageRowGrouping,
3086
+ setRowGroupingModel: setLocalStorageRowGrouping,
3087
+ aggregationModel: localStorageAggregation,
3088
+ setAggregationModel: setLocalStorageAggregation,
3089
+ pivotModel: localStoragePivot,
3090
+ setPivotModel: setLocalStoragePivot,
3091
+ pivotActive: localStoragePivotActive,
3092
+ setPivotActive: setLocalStoragePivotActive
3861
3093
  } = useTableStates(id, localStorageVersion);
3862
3094
 
3863
3095
  // clearing up old version keys, triggering only on first render
3864
3096
  useEffect(() => clearPreviousVersionStorage(id, previousLocalStorageVersions), [id, previousLocalStorageVersions]);
3865
- const onColumnDimensionChange = useCallback(_ref => {
3097
+ const onColumnDimensionChange = useCallback(_ref2 => {
3866
3098
  let {
3867
3099
  newWidth,
3868
3100
  field
3869
- } = _ref;
3101
+ } = _ref2;
3870
3102
  setDimensionModel(_objectSpread2(_objectSpread2({}, dimensionModel), {}, {
3871
3103
  [field]: newWidth
3872
3104
  }));
3873
3105
  }, [dimensionModel, setDimensionModel]);
3106
+
3107
+ // Source of ids for content-matching in the parser, so item.id is stable across the
3108
+ // URL write→parse roundtrip (DS-81). Holds MUI's just-emitted model (random placeholder
3109
+ // id) after onFilterModelChange, then the stabilised parsed model each render.
3110
+ const filterIdSourceRef = useRef(undefined);
3874
3111
  const {
3875
3112
  filterModel: filterParsed,
3876
3113
  sortModel: sortModelParsed,
@@ -3879,6 +3116,10 @@ const useStatefulTable = props => {
3879
3116
  pinnedColumnsModel,
3880
3117
  density: densityParsed,
3881
3118
  columnOrderModel: columnOrderParsed,
3119
+ rowGroupingModel: rowGroupingParsed,
3120
+ aggregationModel: aggregationParsed,
3121
+ pivotModel: pivotParsed,
3122
+ pivotActive: pivotActiveParsed,
3882
3123
  pendingSearch
3883
3124
  } = getModelsParsedOrUpdateLocalStorage(search || '', localStorageVersion, propsColumns, initialState, {
3884
3125
  localStorageFilters,
@@ -3894,8 +3135,16 @@ const useStatefulTable = props => {
3894
3135
  localStorageDensity: densityModel,
3895
3136
  setLocalStorageDensity: setDensityModel,
3896
3137
  localStorageColumnOrder,
3897
- setLocalStorageColumnOrder
3898
- });
3138
+ setLocalStorageColumnOrder,
3139
+ localStorageRowGrouping,
3140
+ setLocalStorageRowGrouping,
3141
+ localStorageAggregation,
3142
+ setLocalStorageAggregation,
3143
+ localStoragePivot,
3144
+ setLocalStoragePivot,
3145
+ localStoragePivotActive: localStoragePivotActive,
3146
+ setLocalStoragePivotActive: setLocalStoragePivotActive
3147
+ }, filterIdSourceRef.current);
3899
3148
 
3900
3149
  // Sync URL in an effect rather than during render to comply with React rules
3901
3150
  useEffect(() => {
@@ -3903,6 +3152,49 @@ const useStatefulTable = props => {
3903
3152
  historyReplace(pendingSearch);
3904
3153
  }
3905
3154
  }, [pendingSearch, historyReplace]);
3155
+
3156
+ // Stabilise parsed model references to prevent MUI v8 from resetting
3157
+ // pagination on every render due to new object identity.
3158
+ const filterParsedRef = useRef(filterParsed);
3159
+ if (!isDeepEqual(filterParsedRef.current, filterParsed)) {
3160
+ filterParsedRef.current = filterParsed;
3161
+ }
3162
+ // Keep the id source in sync with the stabilised parsed model so re-parses triggered
3163
+ // by unrelated state (pagination/sort/visibility) reuse the same ids (DS-81). The parse
3164
+ // call above reads the ref before this write (read-before-write within the render).
3165
+ filterIdSourceRef.current = filterParsed;
3166
+ const sortModelParsedRef = useRef(sortModelParsed);
3167
+ if (!isDeepEqual(sortModelParsedRef.current, sortModelParsed)) {
3168
+ sortModelParsedRef.current = sortModelParsed;
3169
+ }
3170
+ const paginationModelParsedRef = useRef(paginationModelParsed);
3171
+ if (!isDeepEqual(paginationModelParsedRef.current, paginationModelParsed)) {
3172
+ paginationModelParsedRef.current = paginationModelParsed;
3173
+ }
3174
+ const visibilityModelRef = useRef(visibilityModel);
3175
+ if (!isDeepEqual(visibilityModelRef.current, visibilityModel)) {
3176
+ visibilityModelRef.current = visibilityModel;
3177
+ }
3178
+ const pinnedColumnsModelRef = useRef(pinnedColumnsModel);
3179
+ if (!isDeepEqual(pinnedColumnsModelRef.current, pinnedColumnsModel)) {
3180
+ pinnedColumnsModelRef.current = pinnedColumnsModel;
3181
+ }
3182
+ const columnOrderParsedRef = useRef(columnOrderParsed);
3183
+ if (!isDeepEqual(columnOrderParsedRef.current, columnOrderParsed)) {
3184
+ columnOrderParsedRef.current = columnOrderParsed;
3185
+ }
3186
+ const rowGroupingParsedRef = useRef(rowGroupingParsed);
3187
+ if (!isDeepEqual(rowGroupingParsedRef.current, rowGroupingParsed)) {
3188
+ rowGroupingParsedRef.current = rowGroupingParsed;
3189
+ }
3190
+ const aggregationParsedRef = useRef(aggregationParsed);
3191
+ if (!isDeepEqual(aggregationParsedRef.current, aggregationParsed)) {
3192
+ aggregationParsedRef.current = aggregationParsed;
3193
+ }
3194
+ const pivotParsedRef = useRef(pivotParsed);
3195
+ if (!isDeepEqual(pivotParsedRef.current, pivotParsed)) {
3196
+ pivotParsedRef.current = pivotParsed;
3197
+ }
3906
3198
  const columns = useMemo(() => propsColumns.map(column => {
3907
3199
  return _objectSpread2(_objectSpread2({}, column), {}, {
3908
3200
  width: dimensionModel[column.field] || column.width || 100
@@ -3911,56 +3203,61 @@ const useStatefulTable = props => {
3911
3203
  if (apiRef.current) {
3912
3204
  /** Add resetPage method to apiRef. */
3913
3205
  apiRef.current.resetPage = () => {
3914
- apiRef.current.setPage(0);
3206
+ var _apiRef$current;
3207
+ (_apiRef$current = apiRef.current) === null || _apiRef$current === void 0 ? void 0 : _apiRef$current.setPage(0);
3915
3208
  };
3916
3209
  }
3917
3210
  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);
3918
3211
 
3919
- // Helper to build the current DataGridModel for updateUrl calls
3920
- const buildModel = function () {
3921
- var _apiRef$current$state, _apiRef$current;
3922
- let overrides = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
3923
- return _objectSpread2({
3924
- filterModel: filterParsed,
3925
- sortModel: sortModelParsed,
3926
- paginationModel: paginationModelParsed,
3927
- columnsModel: (_apiRef$current$state = (_apiRef$current = apiRef.current) === null || _apiRef$current === void 0 ? void 0 : _apiRef$current.state.columns.columnVisibilityModel) !== null && _apiRef$current$state !== void 0 ? _apiRef$current$state : {},
3928
- pinnedColumnsModel: pinnedColumnsModel,
3929
- density: densityParsed,
3930
- columnOrderModel: columnOrderParsed,
3931
- defaultColumnOrder
3932
- }, overrides);
3933
- };
3934
-
3935
3212
  // Subscribe to density changes via stateChange event (MUI v6 has no densityChange event)
3936
3213
  useEffect(() => {
3937
3214
  const api = apiRef.current;
3938
3215
  if (!(api !== null && api !== void 0 && api.subscribeEvent)) return;
3939
3216
  let prevDensity = densityParsed;
3940
3217
  const unsub = api.subscribeEvent('stateChange', () => {
3941
- const currentDensity = api.state.density.value;
3218
+ const currentDensity = api.state.density;
3942
3219
  if (currentDensity !== prevDensity) {
3943
3220
  prevDensity = currentDensity;
3944
- updateUrl(buildModel({
3221
+ updateUrl({
3222
+ filterModel: filterParsed,
3223
+ sortModel: sortModelParsed,
3224
+ paginationModel: paginationModelParsed,
3945
3225
  columnsModel: api.state.columns.columnVisibilityModel,
3946
- density: currentDensity
3947
- }), search, localStorageVersion, historyReplace, columns);
3226
+ pinnedColumnsModel: pinnedColumnsModel,
3227
+ density: currentDensity,
3228
+ columnOrderModel: columnOrderParsed,
3229
+ defaultColumnOrder,
3230
+ rowGroupingModel: rowGroupingParsed,
3231
+ aggregationModel: aggregationParsed,
3232
+ pivotModel: pivotParsed,
3233
+ pivotActive: pivotActiveParsed
3234
+ }, search, localStorageVersion, historyReplace, columns);
3948
3235
  }
3949
3236
  });
3950
3237
  return unsub;
3951
- }, [apiRef, densityParsed, filterParsed, sortModelParsed, paginationModelParsed, pinnedColumnsModel, columnOrderParsed, defaultColumnOrder, search, localStorageVersion, historyReplace, columns]);
3238
+ }, [apiRef, densityParsed, filterParsed, sortModelParsed, paginationModelParsed, pinnedColumnsModel, columnOrderParsed, defaultColumnOrder, rowGroupingParsed, aggregationParsed, pivotParsed, pivotActiveParsed, search, localStorageVersion, historyReplace, columns]);
3952
3239
 
3953
3240
  // Subscribe to column order changes via columnOrderChange (drag-drop) and columnIndexChange (programmatic setColumnIndex)
3954
3241
  useEffect(() => {
3955
3242
  const api = apiRef.current;
3956
3243
  if (!(api !== null && api !== void 0 && api.subscribeEvent)) return;
3957
- const isDeepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
3958
3244
  const handleColumnOrderChange = () => {
3959
3245
  const orderedFields = api.state.columns.orderedFields;
3960
3246
  if (orderedFields && !isDeepEqual(orderedFields, columnOrderParsed)) {
3961
- updateUrl(buildModel({
3962
- columnOrderModel: orderedFields
3963
- }), search, localStorageVersion, historyReplace, columns);
3247
+ updateUrl({
3248
+ filterModel: filterParsed,
3249
+ sortModel: sortModelParsed,
3250
+ paginationModel: paginationModelParsed,
3251
+ columnsModel: api.state.columns.columnVisibilityModel,
3252
+ pinnedColumnsModel,
3253
+ density: densityParsed,
3254
+ columnOrderModel: orderedFields,
3255
+ defaultColumnOrder,
3256
+ rowGroupingModel: rowGroupingParsed,
3257
+ aggregationModel: aggregationParsed,
3258
+ pivotModel: pivotParsed,
3259
+ pivotActive: pivotActiveParsed
3260
+ }, search, localStorageVersion, historyReplace, columns);
3964
3261
  }
3965
3262
  };
3966
3263
  const unsub1 = api.subscribeEvent('columnOrderChange', handleColumnOrderChange);
@@ -3969,69 +3266,124 @@ const useStatefulTable = props => {
3969
3266
  unsub1();
3970
3267
  unsub2();
3971
3268
  };
3972
- }, [apiRef, columnOrderParsed, defaultColumnOrder, filterParsed, sortModelParsed, paginationModelParsed, pinnedColumnsModel, densityParsed, search, localStorageVersion, historyReplace, columns]);
3269
+ }, [apiRef, columnOrderParsed, defaultColumnOrder, filterParsed, sortModelParsed, paginationModelParsed, pinnedColumnsModel, densityParsed, rowGroupingParsed, aggregationParsed, pivotParsed, pivotActiveParsed, search, localStorageVersion, historyReplace, columns]);
3270
+
3271
+ // Helper to build the current DataGridModel for updateUrl calls
3272
+ const buildModel = function () {
3273
+ var _apiRef$current$state, _apiRef$current2;
3274
+ let overrides = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
3275
+ return _objectSpread2({
3276
+ filterModel: filterParsed,
3277
+ sortModel: sortModelParsed,
3278
+ paginationModel: paginationModelParsed,
3279
+ 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 : {},
3280
+ pinnedColumnsModel: pinnedColumnsModel,
3281
+ density: densityParsed,
3282
+ columnOrderModel: columnOrderParsed,
3283
+ defaultColumnOrder,
3284
+ rowGroupingModel: rowGroupingParsed,
3285
+ aggregationModel: aggregationParsed,
3286
+ pivotModel: pivotParsed,
3287
+ pivotActive: pivotActiveParsed
3288
+ }, overrides);
3289
+ };
3290
+
3291
+ // Stable GridPivotModel identity — only recompute when the simplified value changes.
3292
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3293
+ const pivotModelMui = useMemo(() => toGridPivotModel(pivotParsed), [JSON.stringify(pivotParsed)]);
3973
3294
 
3974
3295
  // Track last emitted values for deep-equal guards to avoid feedback loops.
3296
+ // Initialised from the current parsed values; updated only when we actually fire.
3975
3297
  const lastEmittedFilterRef = useRef(filterParsed);
3976
3298
  const lastEmittedSortRef = useRef(sortModelParsed);
3977
3299
  const lastEmittedPaginationRef = useRef(paginationModelParsed);
3978
- const isModelDeepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
3300
+ const lastEmittedPivotRef = useRef(pivotParsed);
3979
3301
  return {
3980
3302
  apiRef,
3981
3303
  columns,
3982
3304
  density: densityParsed,
3983
- columnOrderModel: columnOrderParsed,
3305
+ columnOrderModel: columnOrderParsedRef.current,
3306
+ rowGroupingModel: rowGroupingParsedRef.current,
3307
+ aggregationModel: aggregationParsedRef.current,
3308
+ pivotModel: pivotModelMui,
3309
+ pivotActive: pivotActiveParsed,
3984
3310
  onFilterModelChange: (model, details) => {
3985
3311
  const filterModel = _objectSpread2(_objectSpread2({}, model), {}, {
3986
3312
  items: model.items.map(item => {
3987
- const column = apiRef.current.getColumn(item.field);
3988
- item.type = column.type || 'string';
3313
+ var _apiRef$current3;
3314
+ const column = (_apiRef$current3 = apiRef.current) === null || _apiRef$current3 === void 0 ? void 0 : _apiRef$current3.getColumn(item.field);
3315
+ item.type = (column === null || column === void 0 ? void 0 : column.type) || 'string';
3989
3316
  return item;
3990
3317
  }),
3991
3318
  quickFilterValues: model.quickFilterValues || []
3992
3319
  });
3993
- if (isModelDeepEqual(filterModel, lastEmittedFilterRef.current)) return;
3320
+ if (isDeepEqual(filterModel, lastEmittedFilterRef.current)) return;
3994
3321
  lastEmittedFilterRef.current = filterModel;
3995
- propsOnFilterModelChange === null || propsOnFilterModelChange === void 0 ? void 0 : propsOnFilterModelChange(filterModel, details);
3322
+ // Capture MUI's just-emitted model (carrying the placeholder's random id) so the
3323
+ // next render's URL re-parse reuses that id and the GridFilterForm key — and thus
3324
+ // the value input's focus — survives the write→parse roundtrip (DS-81).
3325
+ filterIdSourceRef.current = filterModel;
3996
3326
  updateUrl(buildModel({
3997
3327
  filterModel
3998
3328
  }), search, localStorageVersion, historyReplace, columns);
3329
+ propsOnFilterModelChange === null || propsOnFilterModelChange === void 0 ? void 0 : propsOnFilterModelChange(filterModel, details);
3999
3330
  },
4000
- filterModel: filterParsed,
3331
+ filterModel: filterParsedRef.current,
4001
3332
  onSortModelChange: (model, details) => {
4002
- if (isModelDeepEqual(model, lastEmittedSortRef.current)) return;
3333
+ if (isDeepEqual(model, lastEmittedSortRef.current)) return;
4003
3334
  lastEmittedSortRef.current = model;
4004
- propsOnSortModelChange === null || propsOnSortModelChange === void 0 ? void 0 : propsOnSortModelChange(model, details);
4005
3335
  updateUrl(buildModel({
4006
3336
  sortModel: model
4007
3337
  }), search, localStorageVersion, historyReplace, columns);
3338
+ propsOnSortModelChange === null || propsOnSortModelChange === void 0 ? void 0 : propsOnSortModelChange(model, details);
4008
3339
  },
4009
- sortModel: sortModelParsed,
3340
+ sortModel: sortModelParsedRef.current,
4010
3341
  onPinnedColumnsChange: (pinnedColumns, details) => {
3342
+ var _apiRef$current$state2, _apiRef$current4, _apiRef$current4$stat, _apiRef$current4$stat2;
3343
+ // While pivot mode is active, MUI Premium emits synthetic pinned-column
3344
+ // models (e.g. `__row_group_by_columns_group__` for the grouping column)
3345
+ // that must not be persisted to the URL / localStorage. Read the live
3346
+ // grid state from apiRef rather than the parsed URL value because the
3347
+ // URL lags by a tick during pivot enable/disable transitions. Consumer
3348
+ // callbacks are always forwarded so observers can still react.
3349
+ const pivotActiveLive = (_apiRef$current$state2 = (_apiRef$current4 = apiRef.current) === null || _apiRef$current4 === void 0 ? void 0 : (_apiRef$current4$stat = _apiRef$current4.state) === null || _apiRef$current4$stat === void 0 ? void 0 : (_apiRef$current4$stat2 = _apiRef$current4$stat.pivoting) === null || _apiRef$current4$stat2 === void 0 ? void 0 : _apiRef$current4$stat2.active) !== null && _apiRef$current$state2 !== void 0 ? _apiRef$current$state2 : pivotActiveParsed;
3350
+ if (!pivotActiveLive) {
3351
+ updateUrl(buildModel({
3352
+ pinnedColumnsModel: pinnedColumns
3353
+ }), search, localStorageVersion, historyReplace, columns);
3354
+ }
4011
3355
  propsOnPinnedColumnsChange === null || propsOnPinnedColumnsChange === void 0 ? void 0 : propsOnPinnedColumnsChange(pinnedColumns, details);
4012
- updateUrl(buildModel({
4013
- pinnedColumnsModel: pinnedColumns
4014
- }), search, localStorageVersion, historyReplace, columns);
4015
3356
  },
4016
- pinnedColumns: pinnedColumnsModel,
4017
- paginationModel: paginationModelParsed,
3357
+ pinnedColumns: pinnedColumnsModelRef.current,
3358
+ paginationModel: paginationModelParsedRef.current,
4018
3359
  onPaginationModelChange: (model, details) => {
4019
3360
  const paginationModel = _objectSpread2(_objectSpread2({}, model), {}, {
4020
3361
  direction: paginationModelParsed.page < model.page ? 'next' : 'back'
4021
3362
  });
4022
- if (isModelDeepEqual(paginationModel, lastEmittedPaginationRef.current)) return;
3363
+ if (isDeepEqual(paginationModel, lastEmittedPaginationRef.current)) return;
4023
3364
  lastEmittedPaginationRef.current = paginationModel;
4024
- propsOnPaginationModelChange === null || propsOnPaginationModelChange === void 0 ? void 0 : propsOnPaginationModelChange(paginationModel, details);
4025
3365
  updateUrl(buildModel({
4026
3366
  paginationModel
4027
3367
  }), search, localStorageVersion, historyReplace, columns);
3368
+ propsOnPaginationModelChange === null || propsOnPaginationModelChange === void 0 ? void 0 : propsOnPaginationModelChange(paginationModel, details);
4028
3369
  },
4029
- columnVisibilityModel: visibilityModel,
3370
+ columnVisibilityModel: visibilityModelRef.current,
4030
3371
  onColumnVisibilityModelChange: (columnsVisibilityModel, details) => {
3372
+ var _apiRef$current$state3, _apiRef$current5, _apiRef$current5$stat, _apiRef$current5$stat2;
3373
+ // While pivot mode is active, MUI Premium emits synthetic visibility
3374
+ // models that whitelist only the pivot value fields (hiding every base
3375
+ // column). Persisting that to the URL would re-hide all base columns
3376
+ // on the next load (see getColumnVisibilityFromString whitelist logic).
3377
+ // Read the live grid state rather than the parsed URL value because the
3378
+ // URL lags by a tick during pivot enable/disable transitions. Consumer
3379
+ // callbacks are always forwarded.
3380
+ const pivotActiveLive = (_apiRef$current$state3 = (_apiRef$current5 = apiRef.current) === null || _apiRef$current5 === void 0 ? void 0 : (_apiRef$current5$stat = _apiRef$current5.state) === null || _apiRef$current5$stat === void 0 ? void 0 : (_apiRef$current5$stat2 = _apiRef$current5$stat.pivoting) === null || _apiRef$current5$stat2 === void 0 ? void 0 : _apiRef$current5$stat2.active) !== null && _apiRef$current$state3 !== void 0 ? _apiRef$current$state3 : pivotActiveParsed;
3381
+ if (!pivotActiveLive) {
3382
+ updateUrl(buildModel({
3383
+ columnsModel: columnsVisibilityModel
3384
+ }), search, localStorageVersion, historyReplace, columns);
3385
+ }
4031
3386
  propsOnColumnVisibilityModelChange === null || propsOnColumnVisibilityModelChange === void 0 ? void 0 : propsOnColumnVisibilityModelChange(columnsVisibilityModel, details);
4032
- updateUrl(buildModel({
4033
- columnsModel: columnsVisibilityModel
4034
- }), search, localStorageVersion, historyReplace, columns);
4035
3387
  },
4036
3388
  onColumnWidthChange: (params, event, details) => {
4037
3389
  propsOnColumnWidthChange === null || propsOnColumnWidthChange === void 0 ? void 0 : propsOnColumnWidthChange(params, event, details);
@@ -4039,11 +3391,38 @@ const useStatefulTable = props => {
4039
3391
  newWidth: params.width,
4040
3392
  field: params.colDef.field
4041
3393
  });
3394
+ },
3395
+ onRowGroupingModelChange: (model, details) => {
3396
+ updateUrl(buildModel({
3397
+ rowGroupingModel: model
3398
+ }), search, localStorageVersion, historyReplace, columns);
3399
+ propsOnRowGroupingModelChange === null || propsOnRowGroupingModelChange === void 0 ? void 0 : propsOnRowGroupingModelChange(model, details);
3400
+ },
3401
+ onAggregationModelChange: (model, details) => {
3402
+ updateUrl(buildModel({
3403
+ aggregationModel: model
3404
+ }), search, localStorageVersion, historyReplace, columns);
3405
+ propsOnAggregationModelChange === null || propsOnAggregationModelChange === void 0 ? void 0 : propsOnAggregationModelChange(model, details);
3406
+ },
3407
+ onPivotModelChange: model => {
3408
+ const simplified = fromGridPivotModel(model);
3409
+ if (isDeepEqual(simplified, lastEmittedPivotRef.current)) return;
3410
+ lastEmittedPivotRef.current = simplified;
3411
+ updateUrl(buildModel({
3412
+ pivotModel: simplified
3413
+ }), search, localStorageVersion, historyReplace, columns);
3414
+ propsOnPivotModelChange === null || propsOnPivotModelChange === void 0 ? void 0 : propsOnPivotModelChange(model);
3415
+ },
3416
+ onPivotActiveChange: active => {
3417
+ if (active === pivotActiveParsed) return;
3418
+ updateUrl(buildModel({
3419
+ pivotActive: active
3420
+ }), search, localStorageVersion, historyReplace, columns);
4042
3421
  }
4043
3422
  };
4044
3423
  };
4045
3424
 
4046
- 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", "selectionBannerPlacement", "paginationProps", "rows", "pageSizeOptions", "sx", "theme", "useRouter", "paginationMode", "rowCount"];
3425
+ 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", "onRowGroupingModelChange", "onAggregationModelChange", "onPivotModelChange", "pagination", "paginationPlacement", "selectionBannerPlacement", "paginationProps", "rows", "pageSizeOptions", "sx", "theme", "useRouter", "paginationMode", "rowCount", "density", "dataSource", "filterMode", "sortingMode"];
4047
3426
  const COMPONENT_NAME = 'DataGrid';
4048
3427
  const CLASSNAME = 'redsift-datagrid';
4049
3428
 
@@ -4099,6 +3478,7 @@ const CLASSNAME = 'redsift-datagrid';
4099
3478
  */
4100
3479
 
4101
3480
  const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
3481
+ var _ref7;
4102
3482
  const datagridRef = ref || useRef();
4103
3483
  const {
4104
3484
  apiRef: propsApiRef,
@@ -4127,6 +3507,9 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4127
3507
  onColumnVisibilityModelChange: propsOnColumnVisibilityModelChange,
4128
3508
  onPinnedColumnsChange: propsOnPinnedColumnsChange,
4129
3509
  onSortModelChange: propsOnSortModelChange,
3510
+ onRowGroupingModelChange: propsOnRowGroupingModelChange,
3511
+ onAggregationModelChange: propsOnAggregationModelChange,
3512
+ onPivotModelChange: propsOnPivotModelChange,
4130
3513
  pagination,
4131
3514
  paginationPlacement = 'both',
4132
3515
  selectionBannerPlacement = 'top',
@@ -4137,15 +3520,26 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4137
3520
  theme: propsTheme,
4138
3521
  useRouter,
4139
3522
  paginationMode = 'client',
4140
- rowCount
3523
+ rowCount,
3524
+ density: _density,
3525
+ dataSource,
3526
+ filterMode: propsFilterMode,
3527
+ sortingMode: propsSortingMode
4141
3528
  } = props,
4142
3529
  forwardedProps = _objectWithoutProperties(props, _excluded);
4143
- const theme = useTheme$1(propsTheme);
3530
+ const theme = useTheme(propsTheme);
4144
3531
  const _apiRef = useGridApiRef();
4145
3532
  const apiRef = propsApiRef !== null && propsApiRef !== void 0 ? propsApiRef : _apiRef;
4146
- const RenderedToolbar = slots !== null && slots !== void 0 && slots.toolbar ? slots.toolbar : Toolbar;
4147
3533
  LicenseInfo.setLicenseKey(license);
4148
3534
  const height = propsHeight !== null && propsHeight !== void 0 ? propsHeight : autoHeight ? undefined : '500px';
3535
+
3536
+ // When dataSource is present, MUI manages filter/sort/pagination internally.
3537
+ // We must not pass controlled models — only initialState (one-time) and
3538
+ // write-only onChange handlers for URL/localStorage persistence.
3539
+ const isDataSourceMode = Boolean(dataSource);
3540
+ const effectivePaginationMode = isDataSourceMode ? 'server' : paginationMode;
3541
+ const effectiveFilterMode = isDataSourceMode ? 'server' : propsFilterMode;
3542
+ const effectiveSortingMode = isDataSourceMode ? 'server' : propsSortingMode;
4149
3543
  const {
4150
3544
  onColumnVisibilityModelChange: controlledOnColumnVisibilityModelChange,
4151
3545
  onFilterModelChange: controlledOnFilterModelChange,
@@ -4179,7 +3573,15 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4179
3573
  pinnedColumns,
4180
3574
  sortModel,
4181
3575
  onColumnWidthChange,
4182
- columnOrderModel
3576
+ columnOrderModel,
3577
+ rowGroupingModel,
3578
+ aggregationModel,
3579
+ pivotModel,
3580
+ pivotActive,
3581
+ onRowGroupingModelChange,
3582
+ onAggregationModelChange,
3583
+ onPivotModelChange,
3584
+ onPivotActiveChange
4183
3585
  } = useStatefulTable({
4184
3586
  apiRef: apiRef,
4185
3587
  initialState,
@@ -4190,6 +3592,9 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4190
3592
  onPaginationModelChange: controlledOnPaginationModelChange,
4191
3593
  onPinnedColumnsChange: controlledOnPinnedColumnsChange,
4192
3594
  onSortModelChange: controlledOnSortModelChange,
3595
+ onRowGroupingModelChange: propsOnRowGroupingModelChange,
3596
+ onAggregationModelChange: propsOnAggregationModelChange,
3597
+ onPivotModelChange: propsOnPivotModelChange,
4193
3598
  useRouter: useRouter,
4194
3599
  localStorageVersion,
4195
3600
  previousLocalStorageVersions
@@ -4225,9 +3630,61 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4225
3630
  return column;
4226
3631
  });
4227
3632
  }, [columns, columnOrderModel]);
4228
- const [rowSelectionModel, setRowSelectionModel] = useState(propsRowSelectionModel !== null && propsRowSelectionModel !== void 0 ? propsRowSelectionModel : []);
3633
+
3634
+ // In dataSource mode, track pagination locally for the custom pagination slots
3635
+ // (rendered outside DataGridPremium). MUI owns the actual pagination state internally.
3636
+ const [dataSourcePaginationModel, setDataSourcePaginationModel] = useState(paginationModel);
3637
+
3638
+ // The pagination model to use for display in pagination slots
3639
+ const activePaginationModel = isDataSourceMode ? dataSourcePaginationModel : paginationModel;
3640
+
3641
+ // Wrap onPaginationModelChange to also track state locally in dataSource mode
3642
+ const wrappedOnPaginationModelChange = useCallback((model, details) => {
3643
+ if (isDataSourceMode) {
3644
+ setDataSourcePaginationModel(model);
3645
+ }
3646
+ onPaginationModelChange(model, details);
3647
+ }, [isDataSourceMode, onPaginationModelChange]);
3648
+
3649
+ // In dataSource mode, pagination changes from our custom pagination slots
3650
+ // (rendered outside MUI's pagination state) route through apiRef so MUI's
3651
+ // internal page state updates and dataSource.getRows() refetches with the
3652
+ // new params. The `paginationModelChange` subscription below picks up the
3653
+ // resulting state change and propagates it to URL/localStorage and local
3654
+ // React state via wrappedOnPaginationModelChange.
3655
+ const dataSourcePaginationChange = useCallback(model => {
3656
+ var _apiRef$current;
3657
+ (_apiRef$current = apiRef.current) === null || _apiRef$current === void 0 ? void 0 : _apiRef$current.setPaginationModel(model);
3658
+ }, [apiRef]);
3659
+
3660
+ // In dataSource mode, subscribe to MUI's `paginationModelChange` event so
3661
+ // URL state stays in sync with MUI's internal pagination regardless of how
3662
+ // it changed (slot click, apiRef.setPaginationModel from consumer code,
3663
+ // MUI internal updates, etc.). Relying on MUI's `onPaginationModelChange`
3664
+ // prop callback alone is not sufficient: in pivot/GroupedData strategy mode
3665
+ // and with `paginationModel` seeded via `initialState` (rather than as a
3666
+ // controlled prop), the prop callback can be missed under certain
3667
+ // re-render orderings. The event fires reliably whenever the internal
3668
+ // state changes via `setState('setPaginationModel')`, see
3669
+ // `useGridStateInitialization.setState` → `publishEvent(changeEvent, …)`.
3670
+ // The deep-equal guard inside `useStatefulTable.onPaginationModelChange`
3671
+ // dedupes any duplicate emits, so overlap with the prop callback is safe.
3672
+ useEffect(() => {
3673
+ if (!isDataSourceMode) return;
3674
+ const api = apiRef.current;
3675
+ if (!(api !== null && api !== void 0 && api.subscribeEvent)) return;
3676
+ return api.subscribeEvent('paginationModelChange', model => {
3677
+ wrappedOnPaginationModelChange({
3678
+ page: model.page,
3679
+ pageSize: model.pageSize
3680
+ }, {
3681
+ reason: 'paginationModelChange'
3682
+ });
3683
+ });
3684
+ }, [isDataSourceMode, apiRef, wrappedOnPaginationModelChange]);
3685
+ const [rowSelectionModel, setRowSelectionModel] = useState(() => normalizeRowSelectionModel(propsRowSelectionModel));
4229
3686
  useEffect(() => {
4230
- setRowSelectionModel(propsRowSelectionModel !== null && propsRowSelectionModel !== void 0 ? propsRowSelectionModel : []);
3687
+ setRowSelectionModel(normalizeRowSelectionModel(propsRowSelectionModel));
4231
3688
  }, [propsRowSelectionModel]);
4232
3689
  const onRowSelectionModelChange = (selectionModel, details) => {
4233
3690
  setRowSelectionModel(selectionModel);
@@ -4241,25 +3698,49 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4241
3698
  pageSize: paginationModel.pageSize
4242
3699
  });
4243
3700
 
3701
+ // Version counter to force re-renders when selectionStatus ref changes
3702
+ const [, forceSelectionUpdate] = useState(0);
3703
+
4244
3704
  // The checkboxSelectionVisibleOnly should only be applied to client-side pagination,
4245
3705
  // for server-side pagination it produces inconsistent behavior when selecting all rows in pages 2 and beyond
4246
- const checkboxSelectionVisibleOnly = Boolean(pagination) && Boolean(paginationMode != 'server');
3706
+ const checkboxSelectionVisibleOnly = Boolean(pagination) && Boolean(effectivePaginationMode != 'server');
4247
3707
 
4248
3708
  // Banner and pager placements are independent. `belowToolbar` (for either) renders
4249
- // in the toolbar-slot row, and only applies when pagination is on.
3709
+ // in a row inside the toolbar slot, and only applies when pagination is on.
4250
3710
  const bannerAtTop = selectionBannerPlacement === 'top';
4251
3711
  const bannerAtBottom = selectionBannerPlacement === 'bottom';
4252
3712
  const bannerBelowToolbar = selectionBannerPlacement === 'belowToolbar' && Boolean(pagination);
4253
3713
  const pagerBelowToolbar = paginationPlacement === 'belowToolbar' && Boolean(pagination);
3714
+ const belowToolbarActive = bannerBelowToolbar || pagerBelowToolbar;
3715
+
3716
+ // Track when the grid API is ready to ensure top pagination renders correctly
3717
+ const [gridReady, setGridReady] = useState(false);
3718
+
3719
+ // Force re-render when the grid API becomes ready (for top pagination)
3720
+ useEffect(() => {
3721
+ if (apiRef.current && !gridReady) {
3722
+ setGridReady(true);
3723
+ }
3724
+ });
3725
+
3726
+ // Sync persisted density via apiRef — initialState only applies on mount,
3727
+ // so this handles SPA back/forward navigation where controlledDensity changes after mount
3728
+ useEffect(() => {
3729
+ if (apiRef.current) {
3730
+ apiRef.current.setDensity(controlledDensity);
3731
+ }
3732
+ }, [controlledDensity, apiRef]);
4254
3733
 
4255
3734
  // in server-side pagination we want to update the selection status
4256
3735
  // every time we navigate between pages, resize our page or select something
4257
3736
  useEffect(() => {
4258
- if (paginationMode == 'server') {
4259
- onServerSideSelectionStatusChange(Array.isArray(rowSelectionModel) ? rowSelectionModel : [rowSelectionModel], apiRef, selectionStatusRef, isRowSelectable, paginationModel.page, paginationModel.pageSize);
3737
+ if (effectivePaginationMode == 'server') {
3738
+ onServerSideSelectionStatusChange(rowSelectionModel, apiRef, selectionStatusRef, forceSelectionUpdate, isRowSelectable, activePaginationModel.page, activePaginationModel.pageSize);
4260
3739
  }
4261
- }, [rowSelectionModel, paginationModel.page, paginationModel.pageSize, rows]);
4262
- if (!Array.isArray(rows)) {
3740
+ }, [rowSelectionModel, activePaginationModel.page, activePaginationModel.pageSize, rows]);
3741
+
3742
+ // In dataSource mode MUI provides rows internally; skip the guard.
3743
+ if (!isDataSourceMode && !Array.isArray(rows)) {
4263
3744
  return null;
4264
3745
  }
4265
3746
 
@@ -4268,9 +3749,15 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4268
3749
  // receive the fresh value in the same render cycle — no extra re-render needed.
4269
3750
  // The ref is kept in sync for the onRowSelectionModelChange callback's deselect logic.
4270
3751
  let selectionStatus = selectionStatusRef.current;
4271
- if (pagination && paginationMode !== 'server' && Array.isArray(rowSelectionModel) && rowSelectionModel.length > 0) {
3752
+ if (pagination && effectivePaginationMode !== 'server' && getSelectionCount(rowSelectionModel) > 0) {
4272
3753
  try {
4273
- const selectableRowsInPage = isRowSelectable ? gridPaginatedVisibleSortedGridRowEntriesSelector(apiRef).filter(_ref2 => {
3754
+ // Use manual page slicing instead of gridPaginatedVisibleSorted* selectors.
3755
+ // MUI's paginated selectors use apiRef internal state which may be stale when
3756
+ // paginationModel prop changes — our React state is always up to date.
3757
+ const allFilteredEntries = gridFilteredSortedRowEntriesSelector(apiRef);
3758
+ const pageStart = activePaginationModel.page * activePaginationModel.pageSize;
3759
+ const pageEntries = allFilteredEntries.slice(pageStart, pageStart + activePaginationModel.pageSize);
3760
+ const selectableRowsInPage = isRowSelectable ? pageEntries.filter(_ref2 => {
4274
3761
  let {
4275
3762
  model
4276
3763
  } = _ref2;
@@ -4282,24 +3769,29 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4282
3769
  id
4283
3770
  } = _ref3;
4284
3771
  return id;
4285
- }) : gridPaginatedVisibleSortedGridRowIdsSelector(apiRef);
3772
+ }) : pageEntries.map(_ref4 => {
3773
+ let {
3774
+ id
3775
+ } = _ref4;
3776
+ return id;
3777
+ });
4286
3778
  const numberOfSelectableRowsInPage = selectableRowsInPage.length;
4287
- const selectableRowsInTable = isRowSelectable ? gridFilteredSortedRowEntriesSelector(apiRef).filter(_ref4 => {
3779
+ const selectableRowsInTable = isRowSelectable ? allFilteredEntries.filter(_ref5 => {
4288
3780
  let {
4289
3781
  model
4290
- } = _ref4;
3782
+ } = _ref5;
4291
3783
  return isRowSelectable({
4292
3784
  row: model
4293
3785
  });
4294
- }).map(_ref5 => {
3786
+ }).map(_ref6 => {
4295
3787
  let {
4296
3788
  id
4297
- } = _ref5;
3789
+ } = _ref6;
4298
3790
  return id;
4299
3791
  }) : gridFilteredSortedRowIdsSelector(apiRef);
4300
3792
  const numberOfSelectableRowsInTable = selectableRowsInTable.length;
4301
- const numberOfSelectedRows = rowSelectionModel.length;
4302
- const selectedOnCurrentPage = selectableRowsInPage.filter(id => rowSelectionModel.includes(id));
3793
+ const numberOfSelectedRows = getSelectionCount(rowSelectionModel);
3794
+ const selectedOnCurrentPage = selectableRowsInPage.filter(id => isRowSelected(rowSelectionModel, id));
4303
3795
  if (numberOfSelectedRows === numberOfSelectableRowsInTable && numberOfSelectableRowsInPage < numberOfSelectableRowsInTable) {
4304
3796
  selectionStatus = {
4305
3797
  type: 'table',
@@ -4324,7 +3816,7 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4324
3816
  } catch {
4325
3817
  // apiRef may not be initialized on first render
4326
3818
  }
4327
- } else if (pagination && paginationMode !== 'server') {
3819
+ } else if (pagination && effectivePaginationMode !== 'server') {
4328
3820
  selectionStatus = {
4329
3821
  type: 'none',
4330
3822
  numberOfSelectedRows: 0
@@ -4343,6 +3835,48 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4343
3835
  }
4344
3836
  }
4345
3837
  }), [theme]);
3838
+
3839
+ // Per-render data for the stable `BelowToolbar`/`BottomPagination` slots, injected via
3840
+ // `slotProps` so the slot identities stay constant and their subtrees are re-rendered, not
3841
+ // remounted (remounting the toolbar dropped quick-search focus on every keystroke). Typed as
3842
+ // ToolbarWrapper props so mistakes are caught here; cast to MUI's slot types at the injection
3843
+ // sites below. See ../DataGrid/defaultSlots.
3844
+ const belowToolbarSlotProps = {
3845
+ RenderedToolbar: (_ref7 = slots === null || slots === void 0 ? void 0 : slots.toolbar) !== null && _ref7 !== void 0 ? _ref7 : Toolbar,
3846
+ hideToolbar,
3847
+ filterModel,
3848
+ onFilterModelChange,
3849
+ pagination,
3850
+ paginationMode: effectivePaginationMode,
3851
+ displaySelection: bannerBelowToolbar,
3852
+ displayPagination: pagerBelowToolbar,
3853
+ displayRowsPerPage: pagerBelowToolbar,
3854
+ selectionStatus,
3855
+ apiRef,
3856
+ isRowSelectable,
3857
+ paginationModel: activePaginationModel,
3858
+ onPaginationModelChange: isDataSourceMode ? dataSourcePaginationChange : onPaginationModelChange,
3859
+ pageSizeOptions: pageSizeOptions,
3860
+ paginationProps,
3861
+ rowCount
3862
+ };
3863
+ const bottomPaginationSlotProps = {
3864
+ pagination,
3865
+ paginationMode: effectivePaginationMode,
3866
+ displaySelection: bannerAtBottom,
3867
+ displayRowsPerPage: ['bottom', 'both'].includes(paginationPlacement),
3868
+ displayPagination: ['bottom', 'both'].includes(paginationPlacement),
3869
+ selectionStatus,
3870
+ paginationModel: activePaginationModel,
3871
+ // Bottom pager routes non-dataSource changes through the wrapped handler (the
3872
+ // belowToolbar/top pagers use the unwrapped one) — preserved from the previous inline slot.
3873
+ onPaginationModelChange: isDataSourceMode ? dataSourcePaginationChange : wrappedOnPaginationModelChange,
3874
+ apiRef,
3875
+ isRowSelectable,
3876
+ pageSizeOptions: pageSizeOptions,
3877
+ paginationProps,
3878
+ rowCount
3879
+ };
4346
3880
  return /*#__PURE__*/React__default.createElement(ThemeProvider, {
4347
3881
  value: {
4348
3882
  theme
@@ -4353,117 +3887,172 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4353
3887
  ref: datagridRef,
4354
3888
  className: classNames(StatefulDataGrid.className, className),
4355
3889
  $height: height
4356
- }, /*#__PURE__*/React__default.createElement(DataGridPro, _extends$2({}, forwardedProps, {
3890
+ }, pagination && gridReady && (bannerAtTop || ['top', 'both'].includes(paginationPlacement)) ? effectivePaginationMode == 'server' ? /*#__PURE__*/React__default.createElement(ServerSideControlledPagination, {
3891
+ displaySelection: bannerAtTop,
3892
+ displayRowsPerPage: ['top', 'both'].includes(paginationPlacement),
3893
+ displayPagination: ['top', 'both'].includes(paginationPlacement),
3894
+ selectionStatus: selectionStatus,
3895
+ paginationModel: activePaginationModel,
3896
+ onPaginationModelChange: isDataSourceMode ? dataSourcePaginationChange : onPaginationModelChange,
3897
+ pageSizeOptions: pageSizeOptions,
3898
+ paginationProps: paginationProps,
3899
+ rowCount: rowCount
3900
+ }) : /*#__PURE__*/React__default.createElement(ControlledPagination, {
3901
+ displaySelection: bannerAtTop,
3902
+ displayRowsPerPage: ['top', 'both'].includes(paginationPlacement),
3903
+ displayPagination: ['top', 'both'].includes(paginationPlacement),
3904
+ selectionStatus: selectionStatus,
3905
+ apiRef: apiRef,
3906
+ isRowSelectable: isRowSelectable,
3907
+ paginationModel: activePaginationModel,
3908
+ onPaginationModelChange: onPaginationModelChange,
3909
+ pageSizeOptions: pageSizeOptions,
3910
+ paginationProps: paginationProps
3911
+ }) : null, /*#__PURE__*/React__default.createElement(DataGridPremium, _extends({}, forwardedProps, {
4357
3912
  apiRef: apiRef,
3913
+ dataSource: dataSource,
4358
3914
  columns: orderedColumns,
4359
- columnVisibilityModel: columnVisibilityModel,
4360
- density: controlledDensity,
4361
- filterModel: filterModel,
4362
3915
  onColumnVisibilityModelChange: onColumnVisibilityModelChange,
4363
- onFilterModelChange: onFilterModelChange,
4364
- onPaginationModelChange: onPaginationModelChange,
4365
3916
  onPinnedColumnsChange: onPinnedColumnsChange,
4366
- onSortModelChange: onSortModelChange,
4367
- paginationModel: paginationModel,
4368
- pinnedColumns: pinnedColumns,
4369
- sortModel: sortModel,
4370
3917
  pageSizeOptions: pageSizeOptions,
4371
3918
  onColumnWidthChange: onColumnWidthChange,
3919
+ onRowGroupingModelChange: onRowGroupingModelChange,
3920
+ onAggregationModelChange: onAggregationModelChange,
3921
+ onPivotModelChange: onPivotModelChange,
3922
+ pivotActive: pivotActive,
3923
+ onPivotActiveChange: onPivotActiveChange
3924
+ // In dataSource mode: models are uncontrolled (MUI owns them),
3925
+ // onChange handlers are write-only for URL/localStorage persistence,
3926
+ // and initialState seeds MUI on mount from the persisted URL state.
3927
+ // columnVisibilityModel / pinnedColumns / rowGroupingModel /
3928
+ // aggregationModel / pivotModel are also uncontrolled here to
3929
+ // avoid a controlled re-render race with consumer-side
3930
+ // microtask-deferred history updates (otherwise user toggles
3931
+ // flip back when MUI re-emits with the stale controlled value).
3932
+ // pivotModel specifically also carries `hidden`/`sort` field
3933
+ // metadata that our simplified URL representation strips — so
3934
+ // controlling it would prevent users from unchecking fields in
3935
+ // the pivot panel (the controlled prop would immediately re-add
3936
+ // them). Consumers needing programmatic changes should use the
3937
+ // apiRef imperative API.
3938
+ }, isDataSourceMode ? {
3939
+ onFilterModelChange: onFilterModelChange,
3940
+ onSortModelChange: onSortModelChange,
3941
+ onPaginationModelChange: wrappedOnPaginationModelChange,
3942
+ initialState: _objectSpread2(_objectSpread2({}, initialState), {}, {
3943
+ density: controlledDensity,
3944
+ columns: _objectSpread2(_objectSpread2({}, initialState === null || initialState === void 0 ? void 0 : initialState.columns), {}, {
3945
+ orderedFields: columnOrderModel,
3946
+ columnVisibilityModel
3947
+ }),
3948
+ pinnedColumns,
3949
+ rowGrouping: _objectSpread2(_objectSpread2({}, initialState === null || initialState === void 0 ? void 0 : initialState.rowGrouping), {}, {
3950
+ model: rowGroupingModel
3951
+ }),
3952
+ aggregation: _objectSpread2(_objectSpread2({}, initialState === null || initialState === void 0 ? void 0 : initialState.aggregation), {}, {
3953
+ model: aggregationModel
3954
+ }),
3955
+ filter: {
3956
+ filterModel
3957
+ },
3958
+ sorting: {
3959
+ sortModel
3960
+ },
3961
+ pagination: {
3962
+ paginationModel
3963
+ },
3964
+ pivoting: _objectSpread2(_objectSpread2({}, initialState === null || initialState === void 0 ? void 0 : initialState.pivoting), {}, {
3965
+ model: pivotModel,
3966
+ enabled: pivotActive
3967
+ })
3968
+ })
3969
+ } : {
3970
+ columnVisibilityModel,
3971
+ pinnedColumns,
3972
+ rowGroupingModel,
3973
+ aggregationModel,
3974
+ filterModel,
3975
+ sortModel,
3976
+ paginationModel,
3977
+ pivotModel,
3978
+ onFilterModelChange: onFilterModelChange,
3979
+ onSortModelChange: onSortModelChange,
3980
+ onPaginationModelChange: wrappedOnPaginationModelChange,
4372
3981
  initialState: _objectSpread2(_objectSpread2({}, initialState), {}, {
3982
+ density: controlledDensity,
4373
3983
  columns: _objectSpread2(_objectSpread2({}, initialState === null || initialState === void 0 ? void 0 : initialState.columns), {}, {
4374
3984
  orderedFields: columnOrderModel
4375
3985
  })
4376
- }),
3986
+ })
3987
+ }, {
4377
3988
  isRowSelectable: isRowSelectable,
4378
3989
  pagination: pagination,
4379
- paginationMode: paginationMode,
4380
- keepNonExistentRowsSelected: paginationMode == 'server',
4381
- rows: rows,
3990
+ paginationMode: effectivePaginationMode,
3991
+ filterMode: effectiveFilterMode,
3992
+ sortingMode: effectiveSortingMode,
3993
+ keepNonExistentRowsSelected: effectivePaginationMode == 'server',
3994
+ rows: isDataSourceMode ? [] : rows,
4382
3995
  rowCount: rowCount,
4383
3996
  autoHeight: autoHeight,
4384
3997
  checkboxSelectionVisibleOnly: checkboxSelectionVisibleOnly,
4385
- slots: _objectSpread2(_objectSpread2(_objectSpread2({}, baseGridSlots), slots), {}, {
4386
- // The toolbar is already a stable module-level component (ToolbarWrapper) with its
4387
- // per-render data injected via `slotProps.toolbar` below. `BottomPagination` is
4388
- // likewise stable; a fresh inline function here would remount the slot's subtree
4389
- // every render.
4390
- toolbar: ToolbarWrapper,
3998
+ disableRowSelectionExcludeModel: true,
3999
+ showToolbar: !hideToolbar || belowToolbarActive,
4000
+ slots: _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({}, baseGridSlots), slots), belowToolbarActive ? {
4001
+ toolbar: BelowToolbar
4002
+ } : {}), {}, {
4391
4003
  pagination: BottomPagination
4392
4004
  }),
4393
- slotProps: _objectSpread2(_objectSpread2({}, slotProps), {}, {
4394
- toolbar: _objectSpread2({
4395
- hideToolbar,
4396
- RenderedToolbar,
4397
- filterModel,
4398
- onFilterModelChange,
4399
- pagination,
4400
- displaySelection: bannerAtTop || bannerBelowToolbar,
4401
- displayPagination: ['top', 'both'].includes(paginationPlacement) || pagerBelowToolbar,
4402
- displayRowsPerPage: pagerBelowToolbar,
4403
- selectionStatus,
4404
- apiRef,
4405
- isRowSelectable,
4406
- paginationModel,
4407
- onPaginationModelChange,
4408
- pageSizeOptions,
4409
- paginationProps,
4410
- paginationMode,
4411
- rowCount
4412
- }, slotProps === null || slotProps === void 0 ? void 0 : slotProps.toolbar),
4413
- // Per-render data for the stable `BottomPagination` slot. Consumer slot props are
4414
- // spread first; the DS layout data wins. Cast at the MUI boundary because these are
4415
- // ToolbarWrapper props, not the MUI slot prop types.
4416
- pagination: _objectSpread2(_objectSpread2({}, slotProps === null || slotProps === void 0 ? void 0 : slotProps.pagination), {}, {
4417
- pagination,
4418
- paginationMode,
4419
- displaySelection: bannerAtBottom,
4420
- displayRowsPerPage: ['bottom', 'both'].includes(paginationPlacement),
4421
- displayPagination: ['bottom', 'both'].includes(paginationPlacement),
4422
- selectionStatus,
4423
- paginationModel,
4424
- onPaginationModelChange,
4425
- apiRef,
4426
- isRowSelectable,
4427
- pageSizeOptions,
4428
- paginationProps,
4429
- rowCount
4430
- })
4005
+ slotProps: _objectSpread2(_objectSpread2(_objectSpread2({}, slotProps), belowToolbarActive ? {
4006
+ toolbar: _objectSpread2(_objectSpread2({}, slotProps === null || slotProps === void 0 ? void 0 : slotProps.toolbar), belowToolbarSlotProps)
4007
+ } : {}), {}, {
4008
+ pagination: _objectSpread2(_objectSpread2({}, slotProps === null || slotProps === void 0 ? void 0 : slotProps.pagination), bottomPaginationSlotProps)
4431
4009
  }),
4432
4010
  rowSelectionModel: rowSelectionModel,
4433
4011
  onRowSelectionModelChange: (newSelectionModel, details) => {
4434
- if (pagination && paginationMode != 'server') {
4435
- const selectableRowsInPage = isRowSelectable ? gridPaginatedVisibleSortedGridRowEntriesSelector(apiRef).filter(_ref6 => {
4012
+ if (pagination && effectivePaginationMode != 'server') {
4013
+ // Use manual page slicing instead of gridPaginatedVisibleSorted* selectors
4014
+ // to avoid stale apiRef pagination state.
4015
+ const allFilteredEntries = gridFilteredSortedRowEntriesSelector(apiRef);
4016
+ const pageStart = activePaginationModel.page * activePaginationModel.pageSize;
4017
+ const pageEntries = allFilteredEntries.slice(pageStart, pageStart + activePaginationModel.pageSize);
4018
+ const selectableRowsInPage = isRowSelectable ? pageEntries.filter(_ref8 => {
4436
4019
  let {
4437
4020
  model
4438
- } = _ref6;
4021
+ } = _ref8;
4439
4022
  return isRowSelectable({
4440
4023
  row: model
4441
4024
  });
4442
- }).map(_ref7 => {
4025
+ }).map(_ref9 => {
4026
+ let {
4027
+ id
4028
+ } = _ref9;
4029
+ return id;
4030
+ }) : pageEntries.map(_ref10 => {
4443
4031
  let {
4444
4032
  id
4445
- } = _ref7;
4033
+ } = _ref10;
4446
4034
  return id;
4447
- }) : gridPaginatedVisibleSortedGridRowIdsSelector(apiRef);
4035
+ });
4448
4036
  const numberOfSelectableRowsInPage = selectableRowsInPage.length;
4449
- const selectableRowsInTable = isRowSelectable ? gridFilteredSortedRowEntriesSelector(apiRef).filter(_ref8 => {
4037
+ const selectableRowsInTable = isRowSelectable ? allFilteredEntries.filter(_ref11 => {
4450
4038
  let {
4451
4039
  model
4452
- } = _ref8;
4040
+ } = _ref11;
4453
4041
  return isRowSelectable({
4454
4042
  row: model
4455
4043
  });
4456
- }).map(_ref9 => {
4044
+ }).map(_ref12 => {
4457
4045
  let {
4458
4046
  id
4459
- } = _ref9;
4047
+ } = _ref12;
4460
4048
  return id;
4461
4049
  }) : gridFilteredSortedRowIdsSelector(apiRef);
4462
4050
  const numberOfSelectableRowsInTable = selectableRowsInTable.length;
4463
- const numberOfSelectedRows = newSelectionModel.length;
4051
+ const numberOfSelectedRows = getSelectionCount(newSelectionModel);
4464
4052
  if (selectionStatusRef.current.type === 'table' && numberOfSelectedRows === numberOfSelectableRowsInTable - numberOfSelectableRowsInPage || selectionStatusRef.current.type === 'table' && numberOfSelectedRows === numberOfSelectableRowsInTable || selectionStatusRef.current.type === 'page' && numberOfSelectedRows === numberOfSelectableRowsInPage) {
4465
4053
  setTimeout(() => {
4466
- apiRef.current.selectRows([], true, true);
4054
+ var _apiRef$current2;
4055
+ (_apiRef$current2 = apiRef.current) === null || _apiRef$current2 === void 0 ? void 0 : _apiRef$current2.selectRows([], true, true);
4467
4056
  }, 0);
4468
4057
  }
4469
4058
  if (numberOfSelectedRows === numberOfSelectableRowsInPage && numberOfSelectableRowsInPage < numberOfSelectableRowsInTable) {
@@ -4487,6 +4076,7 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4487
4076
  numberOfSelectedRows
4488
4077
  };
4489
4078
  }
4079
+ forceSelectionUpdate(v => v + 1);
4490
4080
  }
4491
4081
  onRowSelectionModelChange === null || onRowSelectionModelChange === void 0 ? void 0 : onRowSelectionModelChange(newSelectionModel, details);
4492
4082
  },
@@ -4504,5 +4094,5 @@ const StatefulDataGrid = /*#__PURE__*/forwardRef((props, ref) => {
4504
4094
  StatefulDataGrid.className = CLASSNAME;
4505
4095
  StatefulDataGrid.displayName = COMPONENT_NAME;
4506
4096
 
4507
- export { clearAllVersionStorage as $, ARRAY_IS_EMPTY as A, Box$1 as B, CONTAINS_ANY_OF as C, DOES_NOT_CONTAIN as D, ENDS_WITH_ANY_OF as E, IS as F, IS_NOT as G, HAS_WITH_SELECT as H, IS_ANY_OF as I, getGridStringOperators as J, getGridStringArrayOperators as K, getGridStringArrayOperatorsWithSelect as L, getGridStringArrayOperatorsWithSelectOnStringArrayColumns as M, FILTER_MODEL_KEY as N, SORT_MODEL_KEY as O, PAGINATION_MODEL_KEY as P, PINNED_COLUMNS as Q, DIMENSION_MODEL_KEY as R, STARTS_WITH_ANY_OF as S, TextField$1 as T, FILTER_SEARCH_KEY as U, VISIBILITY_MODEL_KEY as V, DENSITY_MODEL_KEY as W, COLUMN_ORDER_MODEL_KEY as X, CATEGORIES as Y, buildStorageKey as Z, clearPreviousVersionStorage as _, DOES_NOT_EQUAL as a, resetStatefulDataGridState as a0, resetColumnVisibility as a1, convertToDisplayFormat as a2, convertFromDisplayFormat as a3, getDecodedSearchFromUrl as a4, buildQueryParamsString as a5, areSearchStringsEqual as a6, decodeValue as a7, encodeValue as a8, urlSearchParamsToString as a9, numberOperatorEncoder as aa, numberOperatorDecoder as ab, isOperatorValueValid as ac, isValueValid as ad, getFilterModelFromString as ae, normalizeDateValue as af, getSearchParamsFromFilterModel as ag, getSortingFromString as ah, getSearchParamsFromSorting as ai, getPaginationFromString as aj, getSearchParamsFromPagination as ak, getColumnVisibilityFromString as al, getSearchParamsFromColumnVisibility as am, getPinnedColumnsFromString as an, getSearchParamsFromPinnedColumns as ao, getSearchParamsFromTab as ap, getDensityFromString as aq, getSearchParamsFromDensity as ar, getColumnOrderFromString as as, getSearchParamsFromColumnOrder as at, getSearchParamsFromVersion as au, getFinalSearch as av, getModelsParsedOrUpdateLocalStorage as aw, updateUrl as ax, areFilterModelsEquivalent as ay, StatefulDataGrid as az, 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 };
4097
+ export { buildStorageKey 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, ROW_GROUPING_MODEL_KEY as W, AGGREGATION_MODEL_KEY as X, PIVOT_MODEL_KEY as Y, PIVOT_ACTIVE_KEY as Z, CATEGORIES as _, DOES_NOT_EQUAL as a, clearPreviousVersionStorage as a0, clearAllVersionStorage as a1, resetStatefulDataGridState as a2, resetColumnVisibility as a3, convertToDisplayFormat as a4, convertFromDisplayFormat as a5, getDecodedSearchFromUrl as a6, buildQueryParamsString as a7, areSearchStringsEqual as a8, decodeValue as a9, getSearchParamsFromAggregation as aA, fromGridPivotModel as aB, getPivotFromString as aC, getSearchParamsFromPivot as aD, getPivotActiveFromString as aE, getSearchParamsFromPivotActive as aF, getSearchParamsFromVersion as aG, getFinalSearch as aH, getModelsParsedOrUpdateLocalStorage as aI, updateUrl as aJ, areFilterModelsEquivalent as aK, StatefulDataGrid as aL, encodeValue as aa, urlSearchParamsToString as ab, numberOperatorEncoder as ac, numberOperatorDecoder as ad, isOperatorValueValid as ae, isValueValid as af, getFilterModelFromString as ag, normalizeDateValue as ah, getSearchParamsFromFilterModel as ai, getSortingFromString as aj, getSearchParamsFromSorting as ak, getPaginationFromString as al, getSearchParamsFromPagination as am, getColumnVisibilityFromString as an, getSearchParamsFromColumnVisibility as ao, getPinnedColumnsFromString as ap, getSearchParamsFromPinnedColumns as aq, getSearchParamsFromTab as ar, getDensityFromString as as, getSearchParamsFromDensity as at, getDensityModel as au, getColumnOrderFromString as av, getSearchParamsFromColumnOrder as aw, getRowGroupingFromString as ax, getSearchParamsFromRowGrouping as ay, getAggregationFromString as az, 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 };
4508
4098
  //# sourceMappingURL=StatefulDataGrid2.js.map