@mui/x-data-grid 8.8.0 → 8.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +131 -24
  2. package/DataGrid/index.d.ts +0 -1
  3. package/DataGrid/useDataGridComponent.js +8 -0
  4. package/components/GridScrollArea.js +1 -1
  5. package/components/containers/GridRootStyles.js +39 -0
  6. package/constants/gridClasses.d.ts +12 -0
  7. package/constants/gridClasses.js +3 -1
  8. package/esm/DataGrid/index.d.ts +0 -1
  9. package/esm/DataGrid/useDataGridComponent.js +8 -0
  10. package/esm/components/GridScrollArea.js +1 -1
  11. package/esm/components/containers/GridRootStyles.js +39 -0
  12. package/esm/constants/gridClasses.d.ts +12 -0
  13. package/esm/constants/gridClasses.js +3 -1
  14. package/esm/hooks/core/pipeProcessing/gridPipeProcessingApi.d.ts +5 -0
  15. package/esm/hooks/core/pipeProcessing/useGridPipeProcessing.js +20 -5
  16. package/esm/hooks/core/useGridProps.d.ts +2 -2
  17. package/esm/hooks/core/useGridProps.js +3 -1
  18. package/esm/hooks/features/columnGrouping/gridColumnGroupsInterfaces.d.ts +4 -1
  19. package/esm/hooks/features/columnGrouping/gridColumnGroupsUtils.js +35 -32
  20. package/esm/hooks/features/columnGrouping/useGridColumnGrouping.js +18 -13
  21. package/esm/hooks/features/columns/gridColumnsInterfaces.d.ts +3 -3
  22. package/esm/hooks/features/columns/gridColumnsSelector.js +6 -2
  23. package/esm/hooks/features/columns/gridColumnsUtils.js +6 -4
  24. package/esm/hooks/features/columns/useGridColumns.js +8 -10
  25. package/esm/hooks/features/export/utils.js +1 -1
  26. package/esm/hooks/features/filter/useGridFilter.js +3 -3
  27. package/esm/hooks/features/keyboardNavigation/useGridKeyboardNavigation.d.ts +1 -1
  28. package/esm/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +5 -9
  29. package/esm/hooks/features/listView/gridListViewSelectors.d.ts +8 -1
  30. package/esm/hooks/features/listView/gridListViewSelectors.js +7 -0
  31. package/esm/hooks/features/rowSelection/useGridRowSelection.js +5 -4
  32. package/esm/hooks/features/rows/gridRowsUtils.d.ts +1 -1
  33. package/esm/hooks/features/rows/gridRowsUtils.js +5 -2
  34. package/esm/hooks/features/rows/useGridRows.js +4 -3
  35. package/esm/hooks/features/scroll/useGridScroll.d.ts +1 -1
  36. package/esm/hooks/features/scroll/useGridScroll.js +2 -3
  37. package/esm/hooks/features/virtualization/useGridVirtualScroller.js +3 -6
  38. package/esm/hooks/utils/index.d.ts +1 -0
  39. package/esm/hooks/utils/index.js +2 -1
  40. package/esm/hooks/utils/useGridInitializeState.d.ts +1 -1
  41. package/esm/hooks/utils/useGridInitializeState.js +6 -1
  42. package/esm/hooks/utils/useRunOncePerLoop.d.ts +1 -0
  43. package/esm/hooks/utils/useRunOncePerLoop.js +26 -0
  44. package/esm/index.js +1 -1
  45. package/esm/models/api/gridRowApi.d.ts +5 -0
  46. package/esm/models/events/gridEventLookup.d.ts +2 -1
  47. package/esm/models/gridApiCaches.d.ts +8 -2
  48. package/esm/models/gridStateCommunity.d.ts +2 -2
  49. package/hooks/core/pipeProcessing/gridPipeProcessingApi.d.ts +5 -0
  50. package/hooks/core/pipeProcessing/useGridPipeProcessing.js +20 -5
  51. package/hooks/core/useGridProps.d.ts +2 -2
  52. package/hooks/core/useGridProps.js +3 -1
  53. package/hooks/features/columnGrouping/gridColumnGroupsInterfaces.d.ts +4 -1
  54. package/hooks/features/columnGrouping/gridColumnGroupsUtils.js +35 -32
  55. package/hooks/features/columnGrouping/useGridColumnGrouping.js +18 -13
  56. package/hooks/features/columns/gridColumnsInterfaces.d.ts +3 -3
  57. package/hooks/features/columns/gridColumnsSelector.js +6 -2
  58. package/hooks/features/columns/gridColumnsUtils.js +6 -4
  59. package/hooks/features/columns/useGridColumns.js +8 -10
  60. package/hooks/features/export/utils.js +1 -1
  61. package/hooks/features/filter/useGridFilter.js +3 -3
  62. package/hooks/features/keyboardNavigation/useGridKeyboardNavigation.d.ts +1 -1
  63. package/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +5 -9
  64. package/hooks/features/listView/gridListViewSelectors.d.ts +8 -1
  65. package/hooks/features/listView/gridListViewSelectors.js +8 -1
  66. package/hooks/features/rowSelection/useGridRowSelection.js +5 -4
  67. package/hooks/features/rows/gridRowsUtils.d.ts +1 -1
  68. package/hooks/features/rows/gridRowsUtils.js +5 -2
  69. package/hooks/features/rows/useGridRows.js +4 -3
  70. package/hooks/features/scroll/useGridScroll.d.ts +1 -1
  71. package/hooks/features/scroll/useGridScroll.js +2 -3
  72. package/hooks/features/virtualization/useGridVirtualScroller.js +3 -6
  73. package/hooks/utils/index.d.ts +1 -0
  74. package/hooks/utils/index.js +12 -0
  75. package/hooks/utils/useGridInitializeState.d.ts +1 -1
  76. package/hooks/utils/useGridInitializeState.js +6 -1
  77. package/hooks/utils/useRunOncePerLoop.d.ts +1 -0
  78. package/hooks/utils/useRunOncePerLoop.js +33 -0
  79. package/index.js +1 -1
  80. package/models/api/gridRowApi.d.ts +5 -0
  81. package/models/events/gridEventLookup.d.ts +2 -1
  82. package/models/gridApiCaches.d.ts +8 -2
  83. package/models/gridStateCommunity.d.ts +2 -2
  84. package/package.json +1 -1
@@ -49,7 +49,8 @@ export const useGridPipeProcessing = apiRef => {
49
49
  cache.current[group] = {
50
50
  processors: new Map(),
51
51
  processorsAsArray: [],
52
- appliers: {}
52
+ appliers: {},
53
+ processorsUpdated: false
53
54
  };
54
55
  }
55
56
  const groupCache = cache.current[group];
@@ -57,19 +58,20 @@ export const useGridPipeProcessing = apiRef => {
57
58
  if (oldProcessor !== processor) {
58
59
  groupCache.processors.set(id, processor);
59
60
  groupCache.processorsAsArray = Array.from(cache.current[group].processors.values()).filter(processorValue => processorValue !== null);
60
- runAppliers(groupCache);
61
+ groupCache.processorsUpdated = true;
61
62
  }
62
63
  return () => {
63
64
  cache.current[group].processors.set(id, null);
64
65
  cache.current[group].processorsAsArray = Array.from(cache.current[group].processors.values()).filter(processorValue => processorValue !== null);
65
66
  };
66
- }, [runAppliers]);
67
+ }, []);
67
68
  const registerPipeApplier = React.useCallback((group, id, applier) => {
68
69
  if (!cache.current[group]) {
69
70
  cache.current[group] = {
70
71
  processors: new Map(),
71
72
  processorsAsArray: [],
72
- appliers: {}
73
+ appliers: {},
74
+ processorsUpdated: false
73
75
  };
74
76
  }
75
77
  cache.current[group].appliers[id] = applier;
@@ -82,6 +84,18 @@ export const useGridPipeProcessing = apiRef => {
82
84
  const requestPipeProcessorsApplication = React.useCallback(group => {
83
85
  runAppliers(cache.current[group]);
84
86
  }, [runAppliers]);
87
+ const runAppliersForPendingProcessors = React.useCallback(() => {
88
+ for (const group in cache.current) {
89
+ if (!Object.prototype.hasOwnProperty.call(cache.current, group)) {
90
+ continue;
91
+ }
92
+ const groupCache = cache.current[group];
93
+ if (groupCache.processorsUpdated) {
94
+ groupCache.processorsUpdated = false;
95
+ runAppliers(groupCache);
96
+ }
97
+ }
98
+ }, [runAppliers]);
85
99
  const applyPipeProcessors = React.useCallback((...args) => {
86
100
  const [group, value, context] = args;
87
101
  if (!cache.current[group]) {
@@ -97,7 +111,8 @@ export const useGridPipeProcessing = apiRef => {
97
111
  const preProcessingPrivateApi = {
98
112
  registerPipeProcessor,
99
113
  registerPipeApplier,
100
- requestPipeProcessorsApplication
114
+ requestPipeProcessorsApplication,
115
+ runAppliersForPendingProcessors
101
116
  };
102
117
  const preProcessingPublicApi = {
103
118
  unstable_applyPipeProcessors: applyPipeProcessors
@@ -1,8 +1,8 @@
1
1
  import type { RefObject } from '@mui/x-internals/types';
2
- import type { DataGridProps } from "../../models/props/DataGridProps.js";
2
+ import type { DataGridProcessedProps } from "../../models/props/DataGridProps.js";
3
3
  import type { GridPrivateApiCommon } from "../../models/api/gridApiCommon.js";
4
4
  import type { GridStateInitializer } from "../utils/useGridInitializeState.js";
5
- type Props = Pick<DataGridProps, 'getRowId'>;
5
+ type Props = Pick<DataGridProcessedProps, 'getRowId' | 'listView'>;
6
6
  export declare const propsStateInitializer: GridStateInitializer<Props>;
7
7
  export declare const useGridProps: <PrivateApi extends GridPrivateApiCommon>(apiRef: RefObject<PrivateApi>, props: Props) => void;
8
8
  export {};
@@ -5,6 +5,7 @@ import * as React from 'react';
5
5
  export const propsStateInitializer = (state, props) => {
6
6
  return _extends({}, state, {
7
7
  props: {
8
+ listView: props.listView,
8
9
  getRowId: props.getRowId
9
10
  }
10
11
  });
@@ -13,8 +14,9 @@ export const useGridProps = (apiRef, props) => {
13
14
  React.useEffect(() => {
14
15
  apiRef.current.setState(state => _extends({}, state, {
15
16
  props: {
17
+ listView: props.listView,
16
18
  getRowId: props.getRowId
17
19
  }
18
20
  }));
19
- }, [apiRef, props.getRowId]);
21
+ }, [apiRef, props.listView, props.getRowId]);
20
22
  };
@@ -1,4 +1,4 @@
1
- import { GridColumnGroup } from "../../../models/gridColumnGrouping.js";
1
+ import { GridColumnGroup, GridColumnGroupingModel } from "../../../models/gridColumnGrouping.js";
2
2
  export type GridColumnGroupLookup = {
3
3
  [groupId: string]: Omit<GridColumnGroup, 'children'>;
4
4
  };
@@ -13,4 +13,7 @@ export interface GridColumnsGroupingState {
13
13
  [columnField: string]: GridColumnGroup['groupId'][];
14
14
  };
15
15
  maxDepth: number;
16
+ }
17
+ export interface GridColumnGroupingInternalCache {
18
+ lastColumnGroupingModel?: GridColumnGroupingModel;
16
19
  }
@@ -1,4 +1,3 @@
1
- import { isDeepEqual } from '@mui/x-internals/isDeepEqual';
2
1
  import { isLeaf } from "../../../models/gridColumnGrouping.js";
3
2
  // This is the recurrence function that help writing `unwrapGroupingColumnModel()`
4
3
  const recurrentUnwrapGroupingColumnModel = (columnGroupNode, parents, unwrappedGroupingModelToComplete) => {
@@ -37,45 +36,49 @@ export const unwrapGroupingColumnModel = columnGroupingModel => {
37
36
  export const getColumnGroupsHeaderStructure = (orderedColumns, unwrappedGroupingModel, pinnedFields) => {
38
37
  const getParents = field => unwrappedGroupingModel[field] ?? [];
39
38
  const groupingHeaderStructure = [];
40
- const maxDepth = Math.max(...orderedColumns.map(field => getParents(field).length));
41
- const haveSameParents = (field1, field2, depth) => isDeepEqual(getParents(field1).slice(0, depth + 1), getParents(field2).slice(0, depth + 1));
42
- const haveDifferentContainers = (field1, field2) => {
43
- if (pinnedFields?.left && pinnedFields.left.includes(field1) && !pinnedFields.left.includes(field2)) {
44
- return true;
45
- }
46
- if (pinnedFields?.right && !pinnedFields.right.includes(field1) && pinnedFields.right.includes(field2)) {
47
- return true;
39
+ const maxDepth = Math.max(0, ...orderedColumns.map(field => getParents(field).length));
40
+ const haveSameParents = (field1, field2, depth) => {
41
+ const a = getParents(field1);
42
+ const b = getParents(field2);
43
+ for (let i = 0; i <= depth; i += 1) {
44
+ if (a[i] !== b[i]) {
45
+ return false;
46
+ }
48
47
  }
49
- return false;
48
+ return true;
49
+ };
50
+ const haveDifferentContainers = (field1, field2) => {
51
+ const left = pinnedFields?.left;
52
+ const right = pinnedFields?.right;
53
+ const inLeft1 = !!left?.includes(field1);
54
+ const inLeft2 = !!left?.includes(field2);
55
+ const inRight1 = !!right?.includes(field1);
56
+ const inRight2 = !!right?.includes(field2);
57
+ return inLeft1 !== inLeft2 || inRight1 !== inRight2;
50
58
  };
51
59
  for (let depth = 0; depth < maxDepth; depth += 1) {
52
- const depthStructure = orderedColumns.reduce((structure, newField) => {
53
- const groupId = getParents(newField)[depth] ?? null;
54
- if (structure.length === 0) {
55
- return [{
56
- columnFields: [newField],
60
+ const depthStructure = [];
61
+ for (let i = 0; i < orderedColumns.length; i += 1) {
62
+ const field = orderedColumns[i];
63
+ const groupId = getParents(field)[depth] ?? null;
64
+ if (depthStructure.length === 0) {
65
+ depthStructure.push({
66
+ columnFields: [field],
57
67
  groupId
58
- }];
68
+ });
69
+ continue;
59
70
  }
60
- const lastGroup = structure[structure.length - 1];
71
+ const lastGroup = depthStructure[depthStructure.length - 1];
61
72
  const prevField = lastGroup.columnFields[lastGroup.columnFields.length - 1];
62
- const prevGroupId = lastGroup.groupId;
63
- if (prevGroupId !== groupId || !haveSameParents(prevField, newField, depth) ||
64
- // Fix for https://github.com/mui/mui-x/issues/7041
65
- haveDifferentContainers(prevField, newField)) {
66
- // It's a new group
67
- return [...structure, {
68
- columnFields: [newField],
73
+ if (lastGroup.groupId !== groupId || !haveSameParents(prevField, field, depth) || haveDifferentContainers(prevField, field)) {
74
+ depthStructure.push({
75
+ columnFields: [field],
69
76
  groupId
70
- }];
77
+ });
78
+ } else {
79
+ lastGroup.columnFields.push(field);
71
80
  }
72
-
73
- // It extends the previous group
74
- return [...structure.slice(0, structure.length - 1), {
75
- columnFields: [...lastGroup.columnFields, newField],
76
- groupId
77
- }];
78
- }, []);
81
+ }
79
82
  groupingHeaderStructure.push(depthStructure);
80
83
  }
81
84
  return groupingHeaderStructure;
@@ -11,10 +11,11 @@ import { getColumnGroupsHeaderStructure, unwrapGroupingColumnModel } from "./gri
11
11
  import { useGridEvent } from "../../utils/useGridEvent.js";
12
12
  import { gridColumnFieldsSelector, gridVisibleColumnFieldsSelector } from "../columns/index.js";
13
13
  const createGroupLookup = columnGroupingModel => {
14
- let groupLookup = {};
15
- columnGroupingModel.forEach(node => {
14
+ const groupLookup = {};
15
+ for (let i = 0; i < columnGroupingModel.length; i += 1) {
16
+ const node = columnGroupingModel[i];
16
17
  if (isLeaf(node)) {
17
- return;
18
+ continue;
18
19
  }
19
20
  const {
20
21
  groupId,
@@ -24,10 +25,8 @@ const createGroupLookup = columnGroupingModel => {
24
25
  if (!groupId) {
25
26
  throw new Error('MUI X: An element of the columnGroupingModel does not have either `field` or `groupId`.');
26
27
  }
27
- if (process.env.NODE_ENV !== 'production') {
28
- if (!children) {
29
- console.warn(`MUI X: group groupId=${groupId} has no children.`);
30
- }
28
+ if (process.env.NODE_ENV !== 'production' && !children) {
29
+ console.warn(`MUI X: group groupId=${groupId} has no children.`);
31
30
  }
32
31
  const groupParam = _extends({}, other, {
33
32
  groupId
@@ -36,13 +35,15 @@ const createGroupLookup = columnGroupingModel => {
36
35
  if (subTreeLookup[groupId] !== undefined || groupLookup[groupId] !== undefined) {
37
36
  throw new Error(`MUI X: The groupId ${groupId} is used multiple times in the columnGroupingModel.`);
38
37
  }
39
- groupLookup = _extends({}, groupLookup, subTreeLookup, {
40
- [groupId]: groupParam
41
- });
42
- });
43
- return _extends({}, groupLookup);
38
+ Object.assign(groupLookup, subTreeLookup);
39
+ groupLookup[groupId] = groupParam;
40
+ }
41
+ return groupLookup;
44
42
  };
45
43
  export const columnGroupsStateInitializer = (state, props, apiRef) => {
44
+ apiRef.current.caches.columnGrouping = {
45
+ lastColumnGroupingModel: props.columnGroupingModel
46
+ };
46
47
  if (!props.columnGroupingModel) {
47
48
  return state;
48
49
  }
@@ -97,6 +98,7 @@ export const useGridColumnGrouping = (apiRef, props) => {
97
98
  });
98
99
  }, [apiRef, props.columnGroupingModel]);
99
100
  const updateColumnGroupingState = React.useCallback(columnGroupingModel => {
101
+ apiRef.current.caches.columnGrouping.lastColumnGroupingModel = columnGroupingModel;
100
102
  // @ts-expect-error Move this logic to `Pro` package
101
103
  const pinnedColumns = apiRef.current.getPinnedColumns?.() ?? {};
102
104
  const columnFields = gridColumnFieldsSelector(apiRef);
@@ -128,6 +130,9 @@ export const useGridColumnGrouping = (apiRef, props) => {
128
130
  * EFFECTS
129
131
  */
130
132
  React.useEffect(() => {
133
+ if (props.columnGroupingModel === apiRef.current.caches.columnGrouping.lastColumnGroupingModel) {
134
+ return;
135
+ }
131
136
  updateColumnGroupingState(props.columnGroupingModel);
132
- }, [updateColumnGroupingState, props.columnGroupingModel]);
137
+ }, [apiRef, updateColumnGroupingState, props.columnGroupingModel]);
133
138
  };
@@ -1,4 +1,4 @@
1
- import { GridColDef, GridStateColDef } from "../../../models/colDef/gridColDef.js";
1
+ import type { GridColDef, GridStateColDef } from "../../../models/colDef/gridColDef.js";
2
2
  import type { GridColumnDimensionProperties } from "./gridColumnsUtils.js";
3
3
  export declare enum GridPinnedColumnPosition {
4
4
  LEFT = "left",
@@ -21,8 +21,8 @@ export interface GridPinnedColumnFields {
21
21
  right?: string[];
22
22
  }
23
23
  export declare const EMPTY_PINNED_COLUMN_FIELDS: {
24
- left: string[];
25
- right: string[];
24
+ left: never[];
25
+ right: never[];
26
26
  };
27
27
  export interface GridPinnedColumns {
28
28
  left: GridStateColDef[];
@@ -1,6 +1,7 @@
1
1
  import { createSelector, createSelectorMemoized, createRootSelector } from "../../../utils/createSelector.js";
2
2
  import { EMPTY_PINNED_COLUMN_FIELDS } from "./gridColumnsInterfaces.js";
3
3
  import { gridIsRtlSelector } from "../../core/gridCoreSelector.js";
4
+ import { gridListColumnSelector, gridListViewSelector } from "../listView/index.js";
4
5
 
5
6
  /**
6
7
  * Get the columns state
@@ -45,7 +46,7 @@ export const gridInitialColumnVisibilityModelSelector = createSelector(gridColum
45
46
  * Get the visible columns as a lookup (an object containing the field for keys and the definition for values).
46
47
  * @category Visible Columns
47
48
  */
48
- export const gridVisibleColumnDefinitionsSelector = createSelectorMemoized(gridColumnDefinitionsSelector, gridColumnVisibilityModelSelector, (columns, columnVisibilityModel) => columns.filter(column => columnVisibilityModel[column.field] !== false));
49
+ export const gridVisibleColumnDefinitionsSelector = createSelectorMemoized(gridColumnDefinitionsSelector, gridColumnVisibilityModelSelector, gridListViewSelector, gridListColumnSelector, (columns, columnVisibilityModel, listView, listColumn) => listView && listColumn ? [listColumn] : columns.filter(column => columnVisibilityModel[column.field] !== false));
49
50
 
50
51
  /**
51
52
  * Get the field of each visible column.
@@ -70,7 +71,10 @@ export const gridExistingPinnedColumnSelector = createSelectorMemoized(gridPinne
70
71
  * Get the visible pinned columns.
71
72
  * @category Visible Columns
72
73
  */
73
- export const gridVisiblePinnedColumnDefinitionsSelector = createSelectorMemoized(gridColumnsStateSelector, gridPinnedColumnsSelector, gridVisibleColumnFieldsSelector, gridIsRtlSelector, (columnsState, model, visibleColumnFields, isRtl) => {
74
+ export const gridVisiblePinnedColumnDefinitionsSelector = createSelectorMemoized(gridColumnsStateSelector, gridPinnedColumnsSelector, gridVisibleColumnFieldsSelector, gridIsRtlSelector, gridListViewSelector, (columnsState, model, visibleColumnFields, isRtl, listView) => {
75
+ if (listView) {
76
+ return EMPTY_PINNED_COLUMN_FIELDS;
77
+ }
74
78
  const visiblePinnedFields = filterMissingColumns(model, visibleColumnFields, isRtl);
75
79
  const visiblePinnedColumns = {
76
80
  left: visiblePinnedFields.left.map(field => columnsState.lookup[field]),
@@ -242,11 +242,13 @@ export const createColumnsState = ({
242
242
  initialColumnVisibilityModel: updateInitialVisibilityModel ? columnVisibilityModel : currentState.initialColumnVisibilityModel
243
243
  };
244
244
  }
245
- let columnsToKeep = {};
245
+ const columnsToKeep = {};
246
246
  if (keepOnlyColumnsToUpsert && !isInsideStateInitializer) {
247
- columnsToKeep = Object.keys(columnsState.lookup).reduce((acc, key) => _extends({}, acc, {
248
- [key]: false
249
- }), {});
247
+ for (const key in columnsState.lookup) {
248
+ if (Object.prototype.hasOwnProperty.call(columnsState.lookup, key)) {
249
+ columnsToKeep[key] = false;
250
+ }
251
+ }
250
252
  }
251
253
  const columnsToUpsertLookup = {};
252
254
  columnsToUpsert.forEach(newColumn => {
@@ -14,6 +14,9 @@ import { GridPreferencePanelsValue } from "../preferencesPanel/index.js";
14
14
  import { gridPivotActiveSelector } from "../pivoting/index.js";
15
15
  import { jsx as _jsx } from "react/jsx-runtime";
16
16
  export const columnsStateInitializer = (state, props, apiRef) => {
17
+ apiRef.current.caches.columns = {
18
+ lastColumnsProp: props.columns
19
+ };
17
20
  const columnsState = createColumnsState({
18
21
  apiRef,
19
22
  columnsToUpsert: props.columns,
@@ -36,7 +39,6 @@ export const columnsStateInitializer = (state, props, apiRef) => {
36
39
  */
37
40
  export function useGridColumns(apiRef, props) {
38
41
  const logger = useGridLogger(apiRef, 'useGridColumns');
39
- const previousColumnsProp = React.useRef(props.columns);
40
42
  apiRef.current.registerControlState({
41
43
  stateId: 'visibleColumns',
42
44
  propModel: props.columnVisibilityModel,
@@ -299,27 +301,23 @@ export function useGridColumns(apiRef, props) {
299
301
  */
300
302
  // The effect do not track any value defined synchronously during the 1st render by hooks called after `useGridColumns`
301
303
  // As a consequence, the state generated by the 1st run of this useEffect will always be equal to the initialization one
302
- const isFirstRender = React.useRef(true);
303
304
  React.useEffect(() => {
304
- if (isFirstRender.current) {
305
- isFirstRender.current = false;
305
+ if (apiRef.current.caches.columns.lastColumnsProp === props.columns) {
306
306
  return;
307
307
  }
308
+ apiRef.current.caches.columns.lastColumnsProp = props.columns;
308
309
  logger.info(`GridColumns have changed, new length ${props.columns.length}`);
309
- if (previousColumnsProp.current === props.columns) {
310
- return;
311
- }
312
310
  const columnsState = createColumnsState({
313
311
  apiRef,
314
312
  initialState: undefined,
315
313
  // If the user provides a model, we don't want to set it in the state here because it has it's dedicated `useEffect` which calls `setColumnVisibilityModel`
316
314
  columnsToUpsert: props.columns,
317
315
  keepOnlyColumnsToUpsert: true,
318
- updateInitialVisibilityModel: true
316
+ updateInitialVisibilityModel: true,
317
+ columnVisibilityModel: props.columnVisibilityModel
319
318
  });
320
- previousColumnsProp.current = props.columns;
321
319
  setGridColumnsState(columnsState);
322
- }, [logger, apiRef, setGridColumnsState, props.columns]);
320
+ }, [logger, apiRef, setGridColumnsState, props.columns, props.columnVisibilityModel]);
323
321
  React.useEffect(() => {
324
322
  if (props.columnVisibilityModel !== undefined) {
325
323
  apiRef.current.setColumnVisibilityModel(props.columnVisibilityModel);
@@ -17,7 +17,7 @@ export const getColumnsToExport = ({
17
17
  }, []);
18
18
  }
19
19
  const validColumns = options.allColumns ? columns : gridVisibleColumnDefinitionsSelector(apiRef);
20
- return validColumns.filter(column => !column.disableExport);
20
+ return validColumns.filter(column => column.disableExport !== true);
21
21
  };
22
22
  export const defaultGetRowsToExport = ({
23
23
  apiRef
@@ -342,9 +342,9 @@ export const useGridFilter = (apiRef, props) => {
342
342
  useGridEvent(apiRef, 'rowExpansionChange', updateVisibleRowsLookupState);
343
343
  useGridEvent(apiRef, 'columnVisibilityModelChange', () => {
344
344
  const filterModel = gridFilterModelSelector(apiRef);
345
- if (filterModel.quickFilterValues && shouldQuickFilterExcludeHiddenColumns(filterModel)) {
345
+ if (filterModel.quickFilterValues?.length && shouldQuickFilterExcludeHiddenColumns(filterModel)) {
346
346
  // re-apply filters because the quick filter results may have changed
347
- apiRef.current.unstable_applyFilters();
347
+ updateFilteredRows();
348
348
  }
349
349
  });
350
350
 
@@ -352,7 +352,7 @@ export const useGridFilter = (apiRef, props) => {
352
352
  * 1ST RENDER
353
353
  */
354
354
  useFirstRender(() => {
355
- apiRef.current.unstable_applyFilters();
355
+ updateFilteredRows();
356
356
  });
357
357
 
358
358
  /**
@@ -10,4 +10,4 @@ import { DataGridProcessedProps } from "../../../models/props/DataGridProps.js";
10
10
  * @requires useGridScroll (method) - can be after
11
11
  * @requires useGridColumnSpanning (method) - can be after
12
12
  */
13
- export declare const useGridKeyboardNavigation: (apiRef: RefObject<GridPrivateApiCommunity>, props: Pick<DataGridProcessedProps, "pagination" | "paginationMode" | "getRowId" | "signature" | "headerFilters" | "listView">) => void;
13
+ export declare const useGridKeyboardNavigation: (apiRef: RefObject<GridPrivateApiCommunity>, props: Pick<DataGridProcessedProps, "pagination" | "paginationMode" | "getRowId" | "signature" | "headerFilters">) => void;
@@ -16,7 +16,6 @@ import { gridHeaderFilteringEditFieldSelector, gridHeaderFilteringMenuSelector }
16
16
  import { useGridRegisterPipeProcessor } from "../../core/pipeProcessing/index.js";
17
17
  import { isEventTargetInPortal } from "../../../utils/domUtils.js";
18
18
  import { getLeftColumnIndex, getRightColumnIndex, findNonRowSpannedCell } from "./utils.js";
19
- import { gridListColumnSelector } from "../listView/gridListViewSelectors.js";
20
19
  import { createSelectorMemoized } from "../../../utils/createSelector.js";
21
20
  import { gridVisibleRowsSelector } from "../pagination/index.js";
22
21
  import { gridPinnedRowsSelector } from "../rows/gridRowsSelector.js";
@@ -36,7 +35,6 @@ const gridVisibleRowsWithPinnedRowsSelector = createSelectorMemoized(gridVisible
36
35
  export const useGridKeyboardNavigation = (apiRef, props) => {
37
36
  const logger = useGridLogger(apiRef, 'useGridKeyboardNavigation');
38
37
  const isRtl = useRtl();
39
- const listView = props.listView;
40
38
  const getCurrentPageRows = React.useCallback(() => {
41
39
  return gridVisibleRowsWithPinnedRowsSelector(apiRef);
42
40
  }, [apiRef]);
@@ -59,7 +57,7 @@ export const useGridKeyboardNavigation = (apiRef, props) => {
59
57
  colIndex = nextCellColSpanInfo.rightVisibleCellIndex;
60
58
  }
61
59
  }
62
- const field = listView ? gridListColumnSelector(apiRef).field : gridVisibleColumnFieldsSelector(apiRef)[colIndex];
60
+ const field = gridVisibleColumnFieldsSelector(apiRef)[colIndex];
63
61
  const nonRowSpannedRowId = findNonRowSpannedCell(apiRef, rowId, field, rowSpanScanDirection);
64
62
  // `scrollToIndexes` requires a rowIndex relative to all visible rows.
65
63
  // Those rows do not include pinned rows, but pinned rows do not need scroll anyway.
@@ -70,7 +68,7 @@ export const useGridKeyboardNavigation = (apiRef, props) => {
70
68
  rowIndex: rowIndexRelativeToAllRows
71
69
  });
72
70
  apiRef.current.setCellFocus(nonRowSpannedRowId, field);
73
- }, [apiRef, logger, listView]);
71
+ }, [apiRef, logger]);
74
72
  const goToHeader = React.useCallback((colIndex, event) => {
75
73
  logger.debug(`Navigating to header col ${colIndex}`);
76
74
  apiRef.current.scrollToIndexes({
@@ -395,14 +393,12 @@ export const useGridKeyboardNavigation = (apiRef, props) => {
395
393
  return;
396
394
  }
397
395
  const viewportPageSize = apiRef.current.getViewportPageSize();
398
- const getColumnIndexFn = listView ? () => 0 : apiRef.current.getColumnIndex;
399
- const colIndexBefore = params.field ? getColumnIndexFn(params.field) : 0;
396
+ const colIndexBefore = params.field ? apiRef.current.getColumnIndex(params.field) : 0;
400
397
  const rowIndexBefore = currentPageRows.findIndex(row => row.id === params.id);
401
398
  const firstRowIndexInPage = 0;
402
399
  const lastRowIndexInPage = currentPageRows.length - 1;
403
400
  const firstColIndex = 0;
404
- const visibleColumns = listView ? [gridListColumnSelector(apiRef)] : gridVisibleColumnDefinitionsSelector(apiRef);
405
- const lastColIndex = visibleColumns.length - 1;
401
+ const lastColIndex = gridVisibleColumnDefinitionsSelector(apiRef).length - 1;
406
402
  let shouldPreventDefault = true;
407
403
  switch (event.key) {
408
404
  case 'ArrowDown':
@@ -519,7 +515,7 @@ export const useGridKeyboardNavigation = (apiRef, props) => {
519
515
  if (shouldPreventDefault) {
520
516
  event.preventDefault();
521
517
  }
522
- }, [apiRef, getCurrentPageRows, isRtl, goToCell, getRowIdFromIndex, headerFilteringEnabled, goToHeaderFilter, goToHeader, listView]);
518
+ }, [apiRef, getCurrentPageRows, isRtl, goToCell, getRowIdFromIndex, headerFilteringEnabled, goToHeaderFilter, goToHeader]);
523
519
  const checkIfCanStartEditing = React.useCallback((initialValue, {
524
520
  event
525
521
  }) => {
@@ -1,7 +1,14 @@
1
1
  import { GridStateCommunity } from "../../../models/gridStateCommunity.js";
2
+ import type { GridStateColDef } from "../../../models/colDef/gridColDef.js";
3
+ /**
4
+ * Get the list view state
5
+ * @category List View
6
+ * @ignore - Do not document
7
+ */
8
+ export declare const gridListViewSelector: import("@mui/x-data-grid").OutputSelector<GridStateCommunity, unknown, boolean>;
2
9
  /**
3
10
  * Get the list column definition
4
11
  * @category List View
5
12
  * @ignore - Do not document
6
13
  */
7
- export declare const gridListColumnSelector: import("@mui/x-data-grid").OutputSelector<GridStateCommunity, unknown, import("./useGridListView.js").GridListViewState>;
14
+ export declare const gridListColumnSelector: import("@mui/x-data-grid").OutputSelector<GridStateCommunity, unknown, GridStateColDef>;
@@ -1,4 +1,11 @@
1
1
  import { createRootSelector } from "../../../utils/createSelector.js";
2
+ /**
3
+ * Get the list view state
4
+ * @category List View
5
+ * @ignore - Do not document
6
+ */
7
+ export const gridListViewSelector = createRootSelector(state => state.props.listView ?? false);
8
+
2
9
  /**
3
10
  * Get the list column definition
4
11
  * @category List View
@@ -299,12 +299,13 @@ export const useGridRowSelection = (apiRef, props) => {
299
299
  }
300
300
  const currentSelection = gridRowSelectionStateSelector(apiRef);
301
301
  const rowsLookup = gridRowsLookupSelector(apiRef);
302
+ const rowTree = gridRowTreeSelector(apiRef);
302
303
  const filteredRowsLookup = gridFilteredRowsLookupSelector(apiRef);
303
304
  const isNonExistent = id => {
304
305
  if (props.filterMode === 'server') {
305
306
  return !rowsLookup[id];
306
307
  }
307
- return !rowsLookup[id] || filteredRowsLookup[id] === false;
308
+ return !rowTree[id] || filteredRowsLookup[id] === false;
308
309
  };
309
310
  const newSelectionModel = {
310
311
  type: currentSelection.type,
@@ -430,15 +431,15 @@ export const useGridRowSelection = (apiRef, props) => {
430
431
  const filterModel = gridFilterModelSelector(apiRef);
431
432
  const quickFilterModel = gridQuickFilterValuesSelector(apiRef);
432
433
  const hasFilters = filterModel.items.length > 0 || quickFilterModel?.some(val => val.length);
433
- if (!props.isRowSelectable && !props.checkboxSelectionVisibleOnly && applyAutoSelection && !hasFilters) {
434
+ if (!props.isRowSelectable && !props.checkboxSelectionVisibleOnly && (!isNestedData || props.rowSelectionPropagation?.descendants) && !hasFilters) {
434
435
  apiRef.current.setRowSelectionModel({
435
436
  type: value ? 'exclude' : 'include',
436
437
  ids: new Set()
437
- });
438
+ }, 'multipleRowsSelection');
438
439
  } else {
439
440
  apiRef.current.selectRows(getRowsToBeSelected(), value);
440
441
  }
441
- }, [apiRef, applyAutoSelection, getRowsToBeSelected, props.checkboxSelectionVisibleOnly, props.isRowSelectable]);
442
+ }, [apiRef, getRowsToBeSelected, props.checkboxSelectionVisibleOnly, props.isRowSelectable, props.rowSelectionPropagation?.descendants, isNestedData]);
442
443
  const handleHeaderSelectionCheckboxChange = React.useCallback(params => {
443
444
  toggleAllRows(params.value);
444
445
  }, [toggleAllRows]);
@@ -42,7 +42,7 @@ export declare const getRowsStateFromCache: ({
42
42
  }) => GridRowsState;
43
43
  export declare const isAutogeneratedRow: (row: GridRowModel) => boolean;
44
44
  export declare const isAutogeneratedRowNode: (rowNode: GridTreeNode) => rowNode is GridFooterNode | GridSkeletonRowNode | GridAutoGeneratedGroupNode | GridAutoGeneratedPinnedRowNode;
45
- export declare const getTreeNodeDescendants: (tree: GridRowTreeConfig, parentId: GridRowId, skipAutoGeneratedRows: boolean) => GridRowId[];
45
+ export declare const getTreeNodeDescendants: (tree: GridRowTreeConfig, parentId: GridRowId, skipAutoGeneratedRows: boolean, directChildrenOnly?: boolean) => GridRowId[];
46
46
  export declare const updateCacheWithNewRows: ({
47
47
  previousCache,
48
48
  getRowId,
@@ -127,7 +127,7 @@ export const getRowsStateFromCache = ({
127
127
  };
128
128
  export const isAutogeneratedRow = row => GRID_ID_AUTOGENERATED in row;
129
129
  export const isAutogeneratedRowNode = rowNode => rowNode.type === 'skeletonRow' || rowNode.type === 'footer' || rowNode.type === 'group' && rowNode.isAutoGenerated || rowNode.type === 'pinnedRow' && rowNode.isAutoGenerated;
130
- export const getTreeNodeDescendants = (tree, parentId, skipAutoGeneratedRows) => {
130
+ export const getTreeNodeDescendants = (tree, parentId, skipAutoGeneratedRows, directChildrenOnly) => {
131
131
  const node = tree[parentId];
132
132
  if (node.type !== 'group') {
133
133
  return [];
@@ -138,7 +138,10 @@ export const getTreeNodeDescendants = (tree, parentId, skipAutoGeneratedRows) =>
138
138
  if (!skipAutoGeneratedRows || !isAutogeneratedRowNode(tree[child])) {
139
139
  validDescendants.push(child);
140
140
  }
141
- const childDescendants = getTreeNodeDescendants(tree, child, skipAutoGeneratedRows);
141
+ if (directChildrenOnly) {
142
+ continue;
143
+ }
144
+ const childDescendants = getTreeNodeDescendants(tree, child, skipAutoGeneratedRows, directChildrenOnly);
142
145
  for (let j = 0; j < childDescendants.length; j += 1) {
143
146
  validDescendants.push(childDescendants[j]);
144
147
  }
@@ -198,7 +198,8 @@ export const useGridRows = (apiRef, props) => {
198
198
  skipAutoGeneratedRows = true,
199
199
  groupId,
200
200
  applySorting,
201
- applyFiltering
201
+ applyFiltering,
202
+ directChildrenOnly = false
202
203
  }) => {
203
204
  const tree = gridRowTreeSelector(apiRef);
204
205
  let children;
@@ -210,14 +211,14 @@ export const useGridRows = (apiRef, props) => {
210
211
  const sortedRowIds = gridSortedRowIdsSelector(apiRef);
211
212
  children = [];
212
213
  const startIndex = sortedRowIds.findIndex(id => id === groupId) + 1;
213
- for (let index = startIndex; index < sortedRowIds.length && tree[sortedRowIds[index]].depth > groupNode.depth; index += 1) {
214
+ for (let index = startIndex; index < sortedRowIds.length && (directChildrenOnly ? tree[sortedRowIds[index]].depth === groupNode.depth + 1 : tree[sortedRowIds[index]].depth > groupNode.depth); index += 1) {
214
215
  const id = sortedRowIds[index];
215
216
  if (!skipAutoGeneratedRows || !isAutogeneratedRowNode(tree[id])) {
216
217
  children.push(id);
217
218
  }
218
219
  }
219
220
  } else {
220
- children = getTreeNodeDescendants(tree, groupId, skipAutoGeneratedRows);
221
+ children = getTreeNodeDescendants(tree, groupId, skipAutoGeneratedRows, directChildrenOnly);
221
222
  }
222
223
  if (applyFiltering) {
223
224
  const filteredRowsLookup = gridFilteredRowsLookupSelector(apiRef);
@@ -9,4 +9,4 @@ import { DataGridProcessedProps } from "../../../models/props/DataGridProps.js";
9
9
  * @requires useGridFilter (state)
10
10
  * @requires useGridColumnSpanning (method)
11
11
  */
12
- export declare const useGridScroll: (apiRef: RefObject<GridPrivateApiCommunity>, props: Pick<DataGridProcessedProps, "pagination" | "listView">) => void;
12
+ export declare const useGridScroll: (apiRef: RefObject<GridPrivateApiCommunity>, props: Pick<DataGridProcessedProps, "pagination">) => void;
@@ -9,7 +9,6 @@ import { gridRowsMetaSelector } from "../rows/gridRowsMetaSelector.js";
9
9
  import { useGridApiMethod } from "../../utils/useGridApiMethod.js";
10
10
  import { gridExpandedSortedRowEntriesSelector } from "../filter/gridFilterSelector.js";
11
11
  import { gridDimensionsSelector } from "../dimensions/index.js";
12
- import { gridListColumnSelector } from "../listView/gridListViewSelectors.js";
13
12
 
14
13
  // Logic copied from https://www.w3.org/TR/wai-aria-practices/examples/listbox/js/listbox.js
15
14
  // Similar to https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
@@ -52,7 +51,7 @@ export const useGridScroll = (apiRef, props) => {
52
51
  const scrollToIndexes = React.useCallback(params => {
53
52
  const dimensions = gridDimensionsSelector(apiRef);
54
53
  const totalRowCount = gridRowCountSelector(apiRef);
55
- const visibleColumns = props.listView ? [gridListColumnSelector(apiRef)] : gridVisibleColumnDefinitionsSelector(apiRef);
54
+ const visibleColumns = gridVisibleColumnDefinitionsSelector(apiRef);
56
55
  const scrollToHeader = params.rowIndex == null;
57
56
  if (!scrollToHeader && totalRowCount === 0 || visibleColumns.length === 0) {
58
57
  return false;
@@ -99,7 +98,7 @@ export const useGridScroll = (apiRef, props) => {
99
98
  return true;
100
99
  }
101
100
  return false;
102
- }, [logger, apiRef, virtualScrollerRef, props.pagination, visibleSortedRows, props.listView]);
101
+ }, [logger, apiRef, virtualScrollerRef, props.pagination, visibleSortedRows]);
103
102
  const scroll = React.useCallback(params => {
104
103
  if (virtualScrollerRef.current && params.left !== undefined && colRef.current) {
105
104
  const direction = isRtl ? -1 : 1;