@mui/x-data-grid 7.22.0 → 7.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/colDef/gridBooleanOperators.js +4 -6
  3. package/components/GridRow.js +1 -1
  4. package/components/cell/GridBooleanCell.js +2 -1
  5. package/components/panel/filterPanel/GridFilterInputBoolean.d.ts +1 -0
  6. package/components/panel/filterPanel/GridFilterInputBoolean.js +14 -5
  7. package/components/virtualization/GridVirtualScrollbar.js +7 -1
  8. package/hooks/features/columnHeaders/useGridColumnHeaders.js +1 -2
  9. package/hooks/features/editing/useGridCellEditing.js +23 -4
  10. package/hooks/features/editing/useGridRowEditing.js +23 -2
  11. package/hooks/features/focus/useGridFocus.js +1 -1
  12. package/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +3 -4
  13. package/hooks/features/listView/useGridListView.d.ts +1 -1
  14. package/hooks/features/listView/useGridListView.js +8 -2
  15. package/hooks/features/rowSelection/useGridRowSelection.d.ts +1 -1
  16. package/hooks/features/rowSelection/useGridRowSelection.js +31 -17
  17. package/hooks/features/rows/useGridRowSpanning.js +3 -1
  18. package/hooks/features/scroll/useGridScroll.js +3 -7
  19. package/hooks/features/virtualization/useGridVirtualScroller.js +1 -1
  20. package/index.js +1 -1
  21. package/internals/constants.d.ts +3 -0
  22. package/internals/constants.js +3 -0
  23. package/internals/index.d.ts +1 -0
  24. package/internals/index.js +2 -1
  25. package/internals/utils/gridRowGroupingUtils.d.ts +2 -0
  26. package/internals/utils/gridRowGroupingUtils.js +9 -0
  27. package/internals/utils/index.d.ts +1 -0
  28. package/internals/utils/index.js +2 -1
  29. package/modern/colDef/gridBooleanOperators.js +4 -6
  30. package/modern/components/GridRow.js +1 -1
  31. package/modern/components/cell/GridBooleanCell.js +2 -1
  32. package/modern/components/panel/filterPanel/GridFilterInputBoolean.js +14 -5
  33. package/modern/components/virtualization/GridVirtualScrollbar.js +7 -1
  34. package/modern/hooks/features/columnHeaders/useGridColumnHeaders.js +1 -2
  35. package/modern/hooks/features/editing/useGridCellEditing.js +23 -4
  36. package/modern/hooks/features/editing/useGridRowEditing.js +23 -2
  37. package/modern/hooks/features/focus/useGridFocus.js +1 -1
  38. package/modern/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +3 -4
  39. package/modern/hooks/features/listView/useGridListView.js +8 -2
  40. package/modern/hooks/features/rowSelection/useGridRowSelection.js +31 -17
  41. package/modern/hooks/features/rows/useGridRowSpanning.js +3 -1
  42. package/modern/hooks/features/scroll/useGridScroll.js +3 -7
  43. package/modern/hooks/features/virtualization/useGridVirtualScroller.js +1 -1
  44. package/modern/index.js +1 -1
  45. package/modern/internals/constants.js +3 -0
  46. package/modern/internals/index.js +2 -1
  47. package/modern/internals/utils/gridRowGroupingUtils.js +9 -0
  48. package/modern/internals/utils/index.js +2 -1
  49. package/node/colDef/gridBooleanOperators.js +3 -5
  50. package/node/components/GridRow.js +2 -2
  51. package/node/components/cell/GridBooleanCell.js +2 -1
  52. package/node/components/panel/filterPanel/GridFilterInputBoolean.js +16 -5
  53. package/node/components/virtualization/GridVirtualScrollbar.js +7 -1
  54. package/node/hooks/features/columnHeaders/useGridColumnHeaders.js +1 -2
  55. package/node/hooks/features/editing/useGridCellEditing.js +23 -4
  56. package/node/hooks/features/editing/useGridRowEditing.js +23 -2
  57. package/node/hooks/features/focus/useGridFocus.js +1 -1
  58. package/node/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +4 -5
  59. package/node/hooks/features/listView/useGridListView.js +8 -2
  60. package/node/hooks/features/rowSelection/useGridRowSelection.js +31 -17
  61. package/node/hooks/features/rows/useGridRowSpanning.js +3 -1
  62. package/node/hooks/features/scroll/useGridScroll.js +3 -7
  63. package/node/hooks/features/virtualization/useGridVirtualScroller.js +1 -1
  64. package/node/index.js +1 -1
  65. package/node/internals/constants.js +9 -0
  66. package/node/internals/index.js +12 -0
  67. package/node/internals/utils/gridRowGroupingUtils.js +17 -0
  68. package/node/internals/utils/index.js +11 -0
  69. package/package.json +1 -1
  70. package/constants/gridDetailPanelToggleField.d.ts +0 -1
  71. package/constants/gridDetailPanelToggleField.js +0 -2
  72. package/modern/constants/gridDetailPanelToggleField.js +0 -2
  73. package/node/constants/gridDetailPanelToggleField.js +0 -8
@@ -382,7 +382,7 @@ export const useGridVirtualScroller = () => {
382
382
  flexShrink: 0
383
383
  };
384
384
  if (rootProps.autoHeight && currentPage.rows.length === 0) {
385
- size.height = getMinimalContentHeight(apiRef); // Give room to show the overlay when there no rows.
385
+ size.flexBasis = getMinimalContentHeight(apiRef); // Give room to show the overlay when there no rows.
386
386
  }
387
387
  return size;
388
388
  }, [apiRef, columnsTotalWidth, contentHeight, needsHorizontalScrollbar, rootProps.autoHeight, currentPage.rows.length]);
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v7.22.0
2
+ * @mui/x-data-grid v7.22.2
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -0,0 +1,3 @@
1
+ export declare const GRID_TREE_DATA_GROUPING_FIELD = "__tree_data_group__";
2
+ export declare const GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD = "__row_group_by_columns_group__";
3
+ export declare const GRID_DETAIL_PANEL_TOGGLE_FIELD = "__detail_panel_toggle__";
@@ -0,0 +1,3 @@
1
+ export const GRID_TREE_DATA_GROUPING_FIELD = '__tree_data_group__';
2
+ export const GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD = '__row_group_by_columns_group__';
3
+ export const GRID_DETAIL_PANEL_TOGGLE_FIELD = '__detail_panel_toggle__';
@@ -92,4 +92,5 @@ export type { GridApiCommunity } from '../models/api/gridApiCommunity';
92
92
  export type { GridApiCaches } from '../models/gridApiCaches';
93
93
  export { serializeCellValue } from '../hooks/features/export/serializers/csvSerializer';
94
94
  export * from './utils';
95
+ export * from './constants';
95
96
  export type { Localization } from '../utils/getGridLocalization';
@@ -72,4 +72,5 @@ export * from "../utils/cellBorderUtils.js";
72
72
  export { useGridPrivateApiContext } from "../hooks/utils/useGridPrivateApiContext.js";
73
73
  export * from "../hooks/utils/index.js";
74
74
  export { serializeCellValue } from "../hooks/features/export/serializers/csvSerializer.js";
75
- export * from "./utils/index.js";
75
+ export * from "./utils/index.js";
76
+ export * from "./constants.js";
@@ -0,0 +1,2 @@
1
+ export declare const getRowGroupingCriteriaFromGroupingField: (groupingColDefField: string) => string | null;
2
+ export declare const isGroupingColumn: (field: string) => boolean;
@@ -0,0 +1,9 @@
1
+ import { GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD } from "../constants.js";
2
+ export const getRowGroupingCriteriaFromGroupingField = groupingColDefField => {
3
+ const match = groupingColDefField.match(/^__row_group_by_columns_group_(.*)__$/);
4
+ if (!match) {
5
+ return null;
6
+ }
7
+ return match[1];
8
+ };
9
+ export const isGroupingColumn = field => field === GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD || getRowGroupingCriteriaFromGroupingField(field) !== null;
@@ -1,3 +1,4 @@
1
1
  export * from './computeSlots';
2
2
  export * from './useProps';
3
3
  export * from './propValidation';
4
+ export * from './gridRowGroupingUtils';
@@ -1,3 +1,4 @@
1
1
  export * from "./computeSlots.js";
2
2
  export * from "./useProps.js";
3
- export * from "./propValidation.js";
3
+ export * from "./propValidation.js";
4
+ export * from "./gridRowGroupingUtils.js";
@@ -1,14 +1,12 @@
1
- import { GridFilterInputBoolean } from "../components/panel/filterPanel/GridFilterInputBoolean.js";
1
+ import { GridFilterInputBoolean, sanitizeFilterItemValue } from "../components/panel/filterPanel/GridFilterInputBoolean.js";
2
2
  export const getGridBooleanOperators = () => [{
3
3
  value: 'is',
4
4
  getApplyFilterFn: filterItem => {
5
- if (!filterItem.value) {
5
+ const sanitizedValue = sanitizeFilterItemValue(filterItem.value);
6
+ if (sanitizedValue === undefined) {
6
7
  return null;
7
8
  }
8
- const valueAsBoolean = String(filterItem.value) === 'true';
9
- return value => {
10
- return Boolean(value) === valueAsBoolean;
11
- };
9
+ return value => Boolean(value) === sanitizedValue;
12
10
  },
13
11
  InputComponent: GridFilterInputBoolean
14
12
  }];
@@ -16,7 +16,7 @@ import { useGridVisibleRows } from "../hooks/utils/useGridVisibleRows.js";
16
16
  import { findParentElementFromClassName, isEventTargetInPortal } from "../utils/domUtils.js";
17
17
  import { GRID_CHECKBOX_SELECTION_COL_DEF } from "../colDef/gridCheckboxSelectionColDef.js";
18
18
  import { GRID_ACTIONS_COLUMN_TYPE } from "../colDef/gridActionsColDef.js";
19
- import { GRID_DETAIL_PANEL_TOGGLE_FIELD } from "../constants/gridDetailPanelToggleField.js";
19
+ import { GRID_DETAIL_PANEL_TOGGLE_FIELD } from "../internals/constants.js";
20
20
  import { gridSortModelSelector } from "../hooks/features/sorting/gridSortingSelector.js";
21
21
  import { gridRowMaximumTreeDepthSelector } from "../hooks/features/rows/gridRowsSelector.js";
22
22
  import { gridEditRowsStateSelector } from "../hooks/features/editing/gridEditingSelectors.js";
@@ -10,6 +10,7 @@ import { getDataGridUtilityClass } from "../../constants/gridClasses.js";
10
10
  import { useGridRootProps } from "../../hooks/utils/useGridRootProps.js";
11
11
  import { useGridApiContext } from "../../hooks/utils/useGridApiContext.js";
12
12
  import { isAutogeneratedRowNode } from "../../hooks/features/rows/gridRowsUtils.js";
13
+ import { GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD } from "../../internals/constants.js";
13
14
  import { jsx as _jsx } from "react/jsx-runtime";
14
15
  const useUtilityClasses = ownerState => {
15
16
  const {
@@ -116,7 +117,7 @@ process.env.NODE_ENV !== "production" ? GridBooleanCellRaw.propTypes = {
116
117
  const GridBooleanCell = /*#__PURE__*/React.memo(GridBooleanCellRaw);
117
118
  export { GridBooleanCell };
118
119
  export const renderBooleanCell = params => {
119
- if (params.field !== '__row_group_by_columns_group__' && isAutogeneratedRowNode(params.rowNode)) {
120
+ if (params.field !== GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD && isAutogeneratedRowNode(params.rowNode)) {
120
121
  return '';
121
122
  }
122
123
  return /*#__PURE__*/_jsx(GridBooleanCell, _extends({}, params));
@@ -7,6 +7,15 @@ import { refType, unstable_useId as useId } from '@mui/utils';
7
7
  import { styled } from '@mui/material/styles';
8
8
  import { useGridRootProps } from "../../../hooks/utils/useGridRootProps.js";
9
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
+ export const sanitizeFilterItemValue = value => {
11
+ if (String(value).toLowerCase() === 'true') {
12
+ return true;
13
+ }
14
+ if (String(value).toLowerCase() === 'false') {
15
+ return false;
16
+ }
17
+ return undefined;
18
+ };
10
19
  const BooleanOperatorContainer = styled('div')({
11
20
  display: 'flex',
12
21
  alignItems: 'center',
@@ -27,7 +36,7 @@ function GridFilterInputBoolean(props) {
27
36
  variant = 'standard'
28
37
  } = props,
29
38
  others = _objectWithoutPropertiesLoose(props, _excluded);
30
- const [filterValueState, setFilterValueState] = React.useState(item.value || '');
39
+ const [filterValueState, setFilterValueState] = React.useState(sanitizeFilterItemValue(item.value));
31
40
  const rootProps = useGridRootProps();
32
41
  const labelId = useId();
33
42
  const selectId = useId();
@@ -35,14 +44,14 @@ function GridFilterInputBoolean(props) {
35
44
  const isSelectNative = baseSelectProps.native ?? false;
36
45
  const baseSelectOptionProps = rootProps.slotProps?.baseSelectOption || {};
37
46
  const onFilterChange = React.useCallback(event => {
38
- const value = event.target.value;
47
+ const value = sanitizeFilterItemValue(event.target.value);
39
48
  setFilterValueState(value);
40
49
  applyValue(_extends({}, item, {
41
- value: Boolean(value)
50
+ value
42
51
  }));
43
52
  }, [applyValue, item]);
44
53
  React.useEffect(() => {
45
- setFilterValueState(item.value || '');
54
+ setFilterValueState(sanitizeFilterItemValue(item.value));
46
55
  }, [item.value]);
47
56
  const label = labelProp ?? apiRef.current.getLocaleText('filterPanelInputLabel');
48
57
  return /*#__PURE__*/_jsxs(BooleanOperatorContainer, {
@@ -57,7 +66,7 @@ function GridFilterInputBoolean(props) {
57
66
  labelId: labelId,
58
67
  id: selectId,
59
68
  label: label,
60
- value: filterValueState,
69
+ value: filterValueState === undefined ? '' : String(filterValueState),
61
70
  onChange: onFilterChange,
62
71
  variant: variant,
63
72
  notched: variant === 'outlined' ? true : undefined,
@@ -70,9 +70,13 @@ const GridVirtualScrollbar = /*#__PURE__*/React.forwardRef(function GridVirtualS
70
70
  const onScrollerScroll = useEventCallback(() => {
71
71
  const scroller = apiRef.current.virtualScrollerRef.current;
72
72
  const scrollbar = scrollbarRef.current;
73
+ if (!scrollbar) {
74
+ return;
75
+ }
73
76
  if (scroller[propertyScroll] === lastPosition.current) {
74
77
  return;
75
78
  }
79
+ lastPosition.current = scroller[propertyScroll];
76
80
  if (isLocked.current) {
77
81
  isLocked.current = false;
78
82
  return;
@@ -80,11 +84,13 @@ const GridVirtualScrollbar = /*#__PURE__*/React.forwardRef(function GridVirtualS
80
84
  isLocked.current = true;
81
85
  const value = scroller[propertyScroll] / contentSize;
82
86
  scrollbar[propertyScroll] = value * scrollbarInnerSize;
83
- lastPosition.current = scroller[propertyScroll];
84
87
  });
85
88
  const onScrollbarScroll = useEventCallback(() => {
86
89
  const scroller = apiRef.current.virtualScrollerRef.current;
87
90
  const scrollbar = scrollbarRef.current;
91
+ if (!scrollbar) {
92
+ return;
93
+ }
88
94
  if (isLocked.current) {
89
95
  isLocked.current = false;
90
96
  return;
@@ -223,7 +223,6 @@ export const useGridColumnHeaders = props => {
223
223
  return null;
224
224
  }
225
225
  const {
226
- renderedColumns,
227
226
  firstColumnToRender,
228
227
  lastColumnToRender
229
228
  } = columnsToRender;
@@ -292,7 +291,7 @@ export const useGridColumnHeaders = props => {
292
291
  pinnedPosition: pinnedPosition,
293
292
  style: style,
294
293
  indexInSection: indexInSection,
295
- sectionLength: renderedColumns.length,
294
+ sectionLength: rowStructure.length,
296
295
  gridHasFiller: gridHasFiller
297
296
  }, index);
298
297
  });
@@ -241,26 +241,45 @@ export const useGridCellEditing = (apiRef, props) => {
241
241
  mode: GridCellModes.Edit
242
242
  }, other));
243
243
  }, [throwIfNotEditable, throwIfNotInMode, updateFieldInCellModesModel]);
244
- const updateStateToStartCellEditMode = useEventCallback(params => {
244
+ const updateStateToStartCellEditMode = useEventCallback(async params => {
245
245
  const {
246
246
  id,
247
247
  field,
248
248
  deleteValue,
249
249
  initialValue
250
250
  } = params;
251
- let newValue = apiRef.current.getCellValue(id, field);
251
+ const value = apiRef.current.getCellValue(id, field);
252
+ let newValue = value;
252
253
  if (deleteValue) {
253
254
  newValue = getDefaultCellValue(apiRef.current.getColumn(field));
254
255
  } else if (initialValue) {
255
256
  newValue = initialValue;
256
257
  }
257
- const newProps = {
258
+ const column = apiRef.current.getColumn(field);
259
+ const shouldProcessEditCellProps = !!column.preProcessEditCellProps && deleteValue;
260
+ let newProps = {
258
261
  value: newValue,
259
262
  error: false,
260
- isProcessingProps: false
263
+ isProcessingProps: shouldProcessEditCellProps
261
264
  };
262
265
  updateOrDeleteFieldState(id, field, newProps);
263
266
  apiRef.current.setCellFocus(id, field);
267
+ if (shouldProcessEditCellProps) {
268
+ newProps = await Promise.resolve(column.preProcessEditCellProps({
269
+ id,
270
+ row: apiRef.current.getRow(id),
271
+ props: newProps,
272
+ hasChanged: newValue !== value
273
+ }));
274
+ // Check if still in edit mode before updating
275
+ if (apiRef.current.getCellMode(id, field) === GridCellModes.Edit) {
276
+ const editingState = gridEditRowsStateSelector(apiRef.current.state);
277
+ updateOrDeleteFieldState(id, field, _extends({}, newProps, {
278
+ value: editingState[id][field].value,
279
+ isProcessingProps: false
280
+ }));
281
+ }
282
+ }
264
283
  });
265
284
  const stopCellEditMode = React.useCallback(params => {
266
285
  const {
@@ -318,10 +318,11 @@ export const useGridRowEditing = (apiRef, props) => {
318
318
  if (!cellParams.isEditable) {
319
319
  return acc;
320
320
  }
321
+ const column = apiRef.current.getColumn(field);
321
322
  let newValue = apiRef.current.getCellValue(id, field);
322
323
  if (fieldToFocus === field && (deleteValue || initialValue)) {
323
324
  if (deleteValue) {
324
- newValue = getDefaultCellValue(apiRef.current.getColumn(field));
325
+ newValue = getDefaultCellValue(column);
325
326
  } else if (initialValue) {
326
327
  newValue = initialValue;
327
328
  }
@@ -329,7 +330,7 @@ export const useGridRowEditing = (apiRef, props) => {
329
330
  acc[field] = {
330
331
  value: newValue,
331
332
  error: false,
332
- isProcessingProps: false
333
+ isProcessingProps: !!column.preProcessEditCellProps && deleteValue
333
334
  };
334
335
  return acc;
335
336
  }, {});
@@ -337,6 +338,26 @@ export const useGridRowEditing = (apiRef, props) => {
337
338
  if (fieldToFocus) {
338
339
  apiRef.current.setCellFocus(id, fieldToFocus);
339
340
  }
341
+ columnFields.filter(field => !!apiRef.current.getColumn(field).preProcessEditCellProps && deleteValue).forEach(field => {
342
+ const column = apiRef.current.getColumn(field);
343
+ const value = apiRef.current.getCellValue(id, field);
344
+ const newValue = deleteValue ? getDefaultCellValue(column) : initialValue ?? value;
345
+ Promise.resolve(column.preProcessEditCellProps({
346
+ id,
347
+ row: apiRef.current.getRow(id),
348
+ props: newProps[field],
349
+ hasChanged: newValue !== value
350
+ })).then(processedProps => {
351
+ // Check if still in edit mode before updating
352
+ if (apiRef.current.getRowMode(id) === GridRowModes.Edit) {
353
+ const editingState = gridEditRowsStateSelector(apiRef.current.state);
354
+ updateOrDeleteFieldState(id, field, _extends({}, processedProps, {
355
+ value: editingState[id][field].value,
356
+ isProcessingProps: false
357
+ }));
358
+ }
359
+ });
360
+ });
340
361
  });
341
362
  const stopRowEditMode = React.useCallback(params => {
342
363
  const {
@@ -335,7 +335,7 @@ export const useGridFocus = (apiRef, props) => {
335
335
  paginationMode: props.paginationMode
336
336
  });
337
337
  const nextRow = currentPage.rows[clamp(lastFocusedRowIndex, 0, currentPage.rows.length - 1)];
338
- nextRowId = nextRow.id ?? null;
338
+ nextRowId = nextRow?.id ?? null;
339
339
  }
340
340
  apiRef.current.setState(state => _extends({}, state, {
341
341
  focus: {
@@ -1,5 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { useRtl } from '@mui/system/RtlProvider';
3
+ import { GRID_TREE_DATA_GROUPING_FIELD, GRID_DETAIL_PANEL_TOGGLE_FIELD } from "../../../internals/constants.js";
4
+ import { isGroupingColumn } from "../../../internals/utils/gridRowGroupingUtils.js";
3
5
  import { gridVisibleColumnDefinitionsSelector, gridVisibleColumnFieldsSelector } from "../columns/gridColumnsSelector.js";
4
6
  import { useGridLogger } from "../../utils/useGridLogger.js";
5
7
  import { useGridApiEventHandler } from "../../utils/useGridApiEventHandler.js";
@@ -9,7 +11,6 @@ import { GRID_CHECKBOX_SELECTION_COL_DEF } from "../../../colDef/gridCheckboxSel
9
11
  import { gridClasses } from "../../../constants/gridClasses.js";
10
12
  import { GridCellModes } from "../../../models/gridEditRowModel.js";
11
13
  import { isNavigationKey } from "../../../utils/keyboardUtils.js";
12
- import { GRID_DETAIL_PANEL_TOGGLE_FIELD } from "../../../constants/gridDetailPanelToggleField.js";
13
14
  import { gridFocusColumnGroupHeaderSelector } from "../focus/index.js";
14
15
  import { gridColumnGroupsHeaderMaxDepthSelector } from "../columnGrouping/gridColumnGroupsSelector.js";
15
16
  import { gridHeaderFilteringEditFieldSelector, gridHeaderFilteringMenuSelector } from "../headerFiltering/gridHeaderFilteringSelectors.js";
@@ -458,9 +459,7 @@ export const useGridKeyboardNavigation = (apiRef, props) => {
458
459
  break;
459
460
  }
460
461
  const colDef = params.colDef;
461
- if (colDef &&
462
- // `GRID_TREE_DATA_GROUPING_FIELD` from the Pro package
463
- colDef.field === '__tree_data_group__') {
462
+ if (colDef && (colDef.field === GRID_TREE_DATA_GROUPING_FIELD || isGroupingColumn(colDef.field))) {
464
463
  break;
465
464
  }
466
465
  if (!event.shiftKey && rowIndexBefore < lastRowIndexInPage) {
@@ -1,11 +1,12 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
+ import { warnOnce } from '@mui/x-internals/warning';
3
4
  import { gridDimensionsSelector } from "../dimensions/index.js";
4
5
  import { useGridApiEventHandler } from "../../utils/useGridApiEventHandler.js";
5
6
  export const listViewStateInitializer = (state, props, apiRef) => _extends({}, state, {
6
- listViewColumn: _extends({}, props.unstable_listColumn, {
7
+ listViewColumn: props.unstable_listColumn ? _extends({}, props.unstable_listColumn, {
7
8
  computedWidth: getListColumnWidth(apiRef)
8
- })
9
+ }) : undefined
9
10
  });
10
11
  export function useGridListView(apiRef, props) {
11
12
  /*
@@ -48,6 +49,11 @@ export function useGridListView(apiRef, props) {
48
49
  });
49
50
  }
50
51
  }, [apiRef, props.unstable_listColumn]);
52
+ React.useEffect(() => {
53
+ if (props.unstable_listView && !props.unstable_listColumn) {
54
+ warnOnce(['MUI X: The `unstable_listColumn` prop must be set if `unstable_listView` is enabled.', 'To fix, pass a column definition to the `unstable_listColumn` prop, e.g. `{ field: "example", renderCell: (params) => <div>{params.row.id}</div> }`.', 'For more details, see https://mui.com/x/react-data-grid/list-view/']);
55
+ }
56
+ }, [props.unstable_listView, props.unstable_listColumn]);
51
57
  }
52
58
  function getListColumnWidth(apiRef) {
53
59
  return gridDimensionsSelector(apiRef.current.state).viewportInnerSize.width;
@@ -4,7 +4,7 @@ import { GridSignature, useGridApiEventHandler } from "../../utils/useGridApiEve
4
4
  import { useGridApiMethod } from "../../utils/useGridApiMethod.js";
5
5
  import { useGridLogger } from "../../utils/useGridLogger.js";
6
6
  import { useGridSelector } from "../../utils/useGridSelector.js";
7
- import { gridRowMaximumTreeDepthSelector, gridRowTreeSelector } from "../rows/gridRowsSelector.js";
7
+ import { gridRowsLookupSelector, gridRowMaximumTreeDepthSelector, gridRowTreeSelector } from "../rows/gridRowsSelector.js";
8
8
  import { gridRowSelectionStateSelector, selectedGridRowsSelector, selectedIdsLookupSelector } from "./gridRowSelectionSelector.js";
9
9
  import { gridPaginatedVisibleSortedGridRowIdsSelector } from "../pagination/index.js";
10
10
  import { gridFocusCellSelector } from "../focus/gridFocusStateSelector.js";
@@ -13,7 +13,7 @@ import { GRID_CHECKBOX_SELECTION_COL_DEF, GRID_ACTIONS_COLUMN_TYPE } from "../..
13
13
  import { GridCellModes } from "../../../models/gridEditRowModel.js";
14
14
  import { isKeyboardEvent, isNavigationKey } from "../../../utils/keyboardUtils.js";
15
15
  import { useGridVisibleRows } from "../../utils/useGridVisibleRows.js";
16
- import { GRID_DETAIL_PANEL_TOGGLE_FIELD } from "../../../constants/gridDetailPanelToggleField.js";
16
+ import { GRID_DETAIL_PANEL_TOGGLE_FIELD } from "../../../internals/constants.js";
17
17
  import { gridClasses } from "../../../constants/gridClasses.js";
18
18
  import { isEventTargetInPortal } from "../../../utils/domUtils.js";
19
19
  import { isMultipleRowSelectionEnabled, findRowsToSelect, findRowsToDeselect } from "./utils.js";
@@ -171,30 +171,33 @@ export const useGridRowSelection = (apiRef, props) => {
171
171
  let newSelection;
172
172
  if (resetSelection) {
173
173
  if (isSelected) {
174
- newSelection = selectableIds;
174
+ newSelection = new Set(selectableIds);
175
175
  if (applyAutoSelection) {
176
176
  const addRow = rowId => {
177
- newSelection.push(rowId);
177
+ newSelection.add(rowId);
178
178
  };
179
179
  selectableIds.forEach(id => {
180
180
  findRowsToSelect(apiRef, tree, id, props.rowSelectionPropagation?.descendants ?? false, props.rowSelectionPropagation?.parents ?? false, addRow);
181
181
  });
182
182
  }
183
183
  } else {
184
- newSelection = [];
184
+ newSelection = new Set();
185
+ }
186
+ const currentLookup = selectedIdsLookupSelector(apiRef);
187
+ if (newSelection.size === Object.keys(currentLookup).length && Array.from(newSelection).every(id => currentLookup[id] === id)) {
188
+ return;
185
189
  }
186
190
  } else {
187
- // We clone the existing object to avoid mutating the same object returned by the selector to others part of the project
188
- const selectionLookup = _extends({}, selectedIdsLookupSelector(apiRef));
191
+ newSelection = new Set(Object.values(selectedIdsLookupSelector(apiRef)));
189
192
  const addRow = rowId => {
190
- selectionLookup[rowId] = rowId;
193
+ newSelection.add(rowId);
191
194
  };
192
195
  const removeRow = rowId => {
193
- delete selectionLookup[rowId];
196
+ newSelection.delete(rowId);
194
197
  };
195
198
  selectableIds.forEach(id => {
196
199
  if (isSelected) {
197
- selectionLookup[id] = id;
200
+ newSelection.add(id);
198
201
  if (applyAutoSelection) {
199
202
  findRowsToSelect(apiRef, tree, id, props.rowSelectionPropagation?.descendants ?? false, props.rowSelectionPropagation?.parents ?? false, addRow);
200
203
  }
@@ -205,11 +208,10 @@ export const useGridRowSelection = (apiRef, props) => {
205
208
  }
206
209
  }
207
210
  });
208
- newSelection = Object.values(selectionLookup);
209
211
  }
210
- const isSelectionValid = newSelection.length < 2 || canHaveMultipleSelection;
212
+ const isSelectionValid = newSelection.size < 2 || canHaveMultipleSelection;
211
213
  if (isSelectionValid) {
212
- apiRef.current.setRowSelectionModel(newSelection);
214
+ apiRef.current.setRowSelectionModel(Array.from(newSelection));
213
215
  }
214
216
  }, [logger, applyAutoSelection, canHaveMultipleSelection, apiRef, tree, props.rowSelectionPropagation?.descendants, props.rowSelectionPropagation?.parents]);
215
217
  const selectRowRange = React.useCallback(({
@@ -252,13 +254,20 @@ export const useGridRowSelection = (apiRef, props) => {
252
254
  return;
253
255
  }
254
256
  const currentSelection = gridRowSelectionStateSelector(apiRef.current.state);
257
+ const rowsLookup = gridRowsLookupSelector(apiRef);
255
258
  const filteredRowsLookup = gridFilteredRowsLookupSelector(apiRef);
256
259
 
257
260
  // We clone the existing object to avoid mutating the same object returned by the selector to others part of the project
258
261
  const selectionLookup = _extends({}, selectedIdsLookupSelector(apiRef));
262
+ const isNonExistent = id => {
263
+ if (props.filterMode === 'server') {
264
+ return !rowsLookup[id];
265
+ }
266
+ return filteredRowsLookup[id] !== true;
267
+ };
259
268
  let hasChanged = false;
260
269
  currentSelection.forEach(id => {
261
- if (filteredRowsLookup[id] !== true) {
270
+ if (isNonExistent(id)) {
262
271
  if (props.keepNonExistentRowsSelected) {
263
272
  return;
264
273
  }
@@ -284,15 +293,20 @@ export const useGridRowSelection = (apiRef, props) => {
284
293
  }
285
294
  }
286
295
  });
287
- if (hasChanged || isNestedData && !sortModelUpdated) {
296
+
297
+ // For nested data, on row tree updation (filtering, adding rows, etc.) when the selection is
298
+ // not empty, we need to re-run scanning of the tree to propagate the selection changes
299
+ // Example: A parent whose de-selected children are filtered out should now be selected
300
+ const shouldReapplyPropagation = isNestedData && props.rowSelectionPropagation?.parents && Object.keys(selectionLookup).length > 0;
301
+ if (hasChanged || shouldReapplyPropagation && !sortModelUpdated) {
288
302
  const newSelection = Object.values(selectionLookup);
289
- if (isNestedData) {
303
+ if (shouldReapplyPropagation) {
290
304
  apiRef.current.selectRows(newSelection, true, true);
291
305
  } else {
292
306
  apiRef.current.setRowSelectionModel(newSelection);
293
307
  }
294
308
  }
295
- }, [apiRef, isNestedData, props.rowSelectionPropagation?.parents, props.keepNonExistentRowsSelected, tree]);
309
+ }, [apiRef, isNestedData, props.rowSelectionPropagation?.parents, props.keepNonExistentRowsSelected, props.filterMode, tree]);
296
310
  const handleSingleRowSelection = React.useCallback((id, event) => {
297
311
  const hasCtrlKey = event.metaKey || event.ctrlKey;
298
312
 
@@ -1,11 +1,13 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
3
  import useLazyRef from '@mui/utils/useLazyRef';
4
+ import { GRID_DETAIL_PANEL_TOGGLE_FIELD } from "../../../internals/constants.js";
4
5
  import { gridVisibleColumnDefinitionsSelector } from "../columns/gridColumnsSelector.js";
5
6
  import { useGridVisibleRows } from "../../utils/useGridVisibleRows.js";
6
7
  import { gridRenderContextSelector } from "../virtualization/gridVirtualizationSelectors.js";
7
8
  import { useGridSelector } from "../../utils/useGridSelector.js";
8
9
  import { getUnprocessedRange, isRowRangeUpdated, isRowContextInitialized, getCellValue } from "./gridRowSpanningUtils.js";
10
+ import { GRID_CHECKBOX_SELECTION_FIELD } from "../../../colDef/gridCheckboxSelectionColDef.js";
9
11
  const EMPTY_STATE = {
10
12
  spannedCells: {},
11
13
  hiddenCells: {},
@@ -15,7 +17,7 @@ const EMPTY_RANGE = {
15
17
  firstRowIndex: 0,
16
18
  lastRowIndex: 0
17
19
  };
18
- const skippedFields = new Set(['__check__', '__reorder__', '__detail_panel_toggle__']);
20
+ const skippedFields = new Set([GRID_CHECKBOX_SELECTION_FIELD, '__reorder__', GRID_DETAIL_PANEL_TOGGLE_FIELD]);
19
21
  /**
20
22
  * Default number of rows to process during state initialization to avoid flickering.
21
23
  * Number `20` is arbitrarily chosen to be large enough to cover most of the cases without
@@ -48,8 +48,6 @@ export const useGridScroll = (apiRef, props) => {
48
48
  const logger = useGridLogger(apiRef, 'useGridScroll');
49
49
  const colRef = apiRef.current.columnHeadersContainerRef;
50
50
  const virtualScrollerRef = apiRef.current.virtualScrollerRef;
51
- const virtualScrollbarHorizontalRef = apiRef.current.virtualScrollbarHorizontalRef;
52
- const virtualScrollbarVerticalRef = apiRef.current.virtualScrollbarVerticalRef;
53
51
  const visibleSortedRows = useGridSelector(apiRef, gridExpandedSortedRowEntriesSelector);
54
52
  const scrollToIndexes = React.useCallback(params => {
55
53
  const dimensions = gridDimensionsSelector(apiRef.current.state);
@@ -103,20 +101,18 @@ export const useGridScroll = (apiRef, props) => {
103
101
  return false;
104
102
  }, [logger, apiRef, virtualScrollerRef, props.pagination, visibleSortedRows, props.unstable_listView]);
105
103
  const scroll = React.useCallback(params => {
106
- if (virtualScrollerRef.current && virtualScrollbarHorizontalRef.current && params.left !== undefined && colRef.current) {
104
+ if (virtualScrollerRef.current && params.left !== undefined && colRef.current) {
107
105
  const direction = isRtl ? -1 : 1;
108
106
  colRef.current.scrollLeft = params.left;
109
107
  virtualScrollerRef.current.scrollLeft = direction * params.left;
110
- virtualScrollbarHorizontalRef.current.scrollLeft = direction * params.left;
111
108
  logger.debug(`Scrolling left: ${params.left}`);
112
109
  }
113
- if (virtualScrollerRef.current && virtualScrollbarVerticalRef.current && params.top !== undefined) {
110
+ if (virtualScrollerRef.current && params.top !== undefined) {
114
111
  virtualScrollerRef.current.scrollTop = params.top;
115
- virtualScrollbarVerticalRef.current.scrollTop = params.top;
116
112
  logger.debug(`Scrolling top: ${params.top}`);
117
113
  }
118
114
  logger.debug(`Scrolling, updating container, and viewport`);
119
- }, [virtualScrollerRef, virtualScrollbarHorizontalRef, virtualScrollbarVerticalRef, isRtl, colRef, logger]);
115
+ }, [virtualScrollerRef, isRtl, colRef, logger]);
120
116
  const getScrollPosition = React.useCallback(() => {
121
117
  if (!virtualScrollerRef?.current) {
122
118
  return {
@@ -382,7 +382,7 @@ export const useGridVirtualScroller = () => {
382
382
  flexShrink: 0
383
383
  };
384
384
  if (rootProps.autoHeight && currentPage.rows.length === 0) {
385
- size.height = getMinimalContentHeight(apiRef); // Give room to show the overlay when there no rows.
385
+ size.flexBasis = getMinimalContentHeight(apiRef); // Give room to show the overlay when there no rows.
386
386
  }
387
387
  return size;
388
388
  }, [apiRef, columnsTotalWidth, contentHeight, needsHorizontalScrollbar, rootProps.autoHeight, currentPage.rows.length]);
package/modern/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v7.22.0
2
+ * @mui/x-data-grid v7.22.2
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -0,0 +1,3 @@
1
+ export const GRID_TREE_DATA_GROUPING_FIELD = '__tree_data_group__';
2
+ export const GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD = '__row_group_by_columns_group__';
3
+ export const GRID_DETAIL_PANEL_TOGGLE_FIELD = '__detail_panel_toggle__';
@@ -72,4 +72,5 @@ export * from "../utils/cellBorderUtils.js";
72
72
  export { useGridPrivateApiContext } from "../hooks/utils/useGridPrivateApiContext.js";
73
73
  export * from "../hooks/utils/index.js";
74
74
  export { serializeCellValue } from "../hooks/features/export/serializers/csvSerializer.js";
75
- export * from "./utils/index.js";
75
+ export * from "./utils/index.js";
76
+ export * from "./constants.js";
@@ -0,0 +1,9 @@
1
+ import { GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD } from "../constants.js";
2
+ export const getRowGroupingCriteriaFromGroupingField = groupingColDefField => {
3
+ const match = groupingColDefField.match(/^__row_group_by_columns_group_(.*)__$/);
4
+ if (!match) {
5
+ return null;
6
+ }
7
+ return match[1];
8
+ };
9
+ export const isGroupingColumn = field => field === GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD || getRowGroupingCriteriaFromGroupingField(field) !== null;
@@ -1,3 +1,4 @@
1
1
  export * from "./computeSlots.js";
2
2
  export * from "./useProps.js";
3
- export * from "./propValidation.js";
3
+ export * from "./propValidation.js";
4
+ export * from "./gridRowGroupingUtils.js";
@@ -8,13 +8,11 @@ var _GridFilterInputBoolean = require("../components/panel/filterPanel/GridFilte
8
8
  const getGridBooleanOperators = () => [{
9
9
  value: 'is',
10
10
  getApplyFilterFn: filterItem => {
11
- if (!filterItem.value) {
11
+ const sanitizedValue = (0, _GridFilterInputBoolean.sanitizeFilterItemValue)(filterItem.value);
12
+ if (sanitizedValue === undefined) {
12
13
  return null;
13
14
  }
14
- const valueAsBoolean = String(filterItem.value) === 'true';
15
- return value => {
16
- return Boolean(value) === valueAsBoolean;
17
- };
15
+ return value => Boolean(value) === sanitizedValue;
18
16
  },
19
17
  InputComponent: _GridFilterInputBoolean.GridFilterInputBoolean
20
18
  }];