@mui/x-data-grid 8.18.0 → 8.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/CHANGELOG.md +175 -0
  2. package/DataGrid/useDataGridComponent.js +4 -3
  3. package/components/GridRow.js +5 -2
  4. package/components/GridRowDragAndDropOverlay.d.ts +7 -0
  5. package/components/GridRowDragAndDropOverlay.js +73 -0
  6. package/components/cell/GridActionsCell.d.ts +9 -0
  7. package/components/cell/GridActionsCell.js +54 -34
  8. package/components/cell/GridBooleanCell.js +0 -10
  9. package/components/cell/GridCell.js +4 -10
  10. package/components/columnHeaders/GridColumnHeaderItem.js +2 -2
  11. package/components/columnSelection/GridCellCheckboxRenderer.js +37 -22
  12. package/components/containers/GridRootStyles.js +17 -40
  13. package/components/toolbarV8/Toolbar.js +1 -1
  14. package/components/virtualization/GridVirtualScrollbar.d.ts +1 -0
  15. package/components/virtualization/GridVirtualScrollbar.js +13 -8
  16. package/components/virtualization/GridVirtualScroller.js +2 -1
  17. package/components/virtualization/GridVirtualScrollerRenderZone.js +1 -1
  18. package/constants/dataGridPropsDefaultValues.js +2 -1
  19. package/constants/gridClasses.d.ts +0 -8
  20. package/constants/gridClasses.js +1 -1
  21. package/esm/DataGrid/useDataGridComponent.js +5 -4
  22. package/esm/components/GridRow.js +5 -2
  23. package/esm/components/GridRowDragAndDropOverlay.d.ts +7 -0
  24. package/esm/components/GridRowDragAndDropOverlay.js +66 -0
  25. package/esm/components/cell/GridActionsCell.d.ts +9 -0
  26. package/esm/components/cell/GridActionsCell.js +55 -34
  27. package/esm/components/cell/GridBooleanCell.js +0 -10
  28. package/esm/components/cell/GridCell.js +4 -10
  29. package/esm/components/columnHeaders/GridColumnHeaderItem.js +2 -2
  30. package/esm/components/columnSelection/GridCellCheckboxRenderer.js +37 -22
  31. package/esm/components/containers/GridRootStyles.js +17 -40
  32. package/esm/components/toolbarV8/Toolbar.js +1 -1
  33. package/esm/components/virtualization/GridVirtualScrollbar.d.ts +1 -0
  34. package/esm/components/virtualization/GridVirtualScrollbar.js +12 -7
  35. package/esm/components/virtualization/GridVirtualScroller.js +2 -1
  36. package/esm/components/virtualization/GridVirtualScrollerRenderZone.js +1 -1
  37. package/esm/constants/dataGridPropsDefaultValues.js +2 -1
  38. package/esm/constants/gridClasses.d.ts +0 -8
  39. package/esm/constants/gridClasses.js +1 -1
  40. package/esm/hooks/core/gridPropsSelectors.d.ts +2 -1
  41. package/esm/hooks/core/gridPropsSelectors.js +3 -0
  42. package/esm/hooks/core/pipeProcessing/gridPipeProcessingApi.d.ts +4 -3
  43. package/esm/hooks/core/useGridProps.js +8 -2
  44. package/esm/hooks/core/useGridVirtualizer.d.ts +80 -6
  45. package/esm/hooks/core/useGridVirtualizer.js +27 -12
  46. package/esm/hooks/features/columnGrouping/useGridColumnGrouping.js +6 -1
  47. package/esm/hooks/features/columnMenu/useGridColumnMenu.js +14 -4
  48. package/esm/hooks/features/columns/useGridColumnSpanning.js +9 -4
  49. package/esm/hooks/features/dataSource/useGridDataSourceBase.js +2 -2
  50. package/esm/hooks/features/dimensions/useGridDimensions.js +12 -6
  51. package/esm/hooks/features/editing/useGridCellEditing.js +1 -1
  52. package/esm/hooks/features/editing/useGridRowEditing.js +1 -1
  53. package/esm/hooks/features/export/serializers/csvSerializer.js +2 -4
  54. package/esm/hooks/features/export/useGridPrintExport.js +18 -18
  55. package/esm/hooks/features/filter/gridFilterUtils.js +5 -11
  56. package/esm/hooks/features/filter/index.d.ts +1 -1
  57. package/esm/hooks/features/filter/index.js +1 -1
  58. package/esm/hooks/features/filter/useGridFilter.d.ts +1 -1
  59. package/esm/hooks/features/filter/useGridFilter.js +3 -1
  60. package/esm/hooks/features/focus/useGridFocus.js +0 -1
  61. package/esm/hooks/features/keyboardNavigation/useGridKeyboardNavigation.d.ts +1 -1
  62. package/esm/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +189 -25
  63. package/esm/hooks/features/pagination/useGridPaginationMeta.js +3 -3
  64. package/esm/hooks/features/pagination/useGridPaginationModel.js +7 -4
  65. package/esm/hooks/features/pagination/useGridRowCount.js +31 -15
  66. package/esm/hooks/features/rowReorder/gridRowReorderInterfaces.d.ts +19 -0
  67. package/esm/hooks/features/rowReorder/gridRowReorderSelector.d.ts +20 -1
  68. package/esm/hooks/features/rowReorder/gridRowReorderSelector.js +19 -1
  69. package/esm/hooks/features/rowSelection/useGridRowSelection.js +17 -8
  70. package/esm/hooks/features/rowSelection/utils.d.ts +1 -0
  71. package/esm/hooks/features/rowSelection/utils.js +17 -4
  72. package/esm/hooks/features/rows/useGridRowSpanning.js +23 -60
  73. package/esm/hooks/features/rows/useGridRows.js +3 -1
  74. package/esm/hooks/features/rows/useGridRowsOverridableMethods.d.ts +1 -0
  75. package/esm/hooks/features/rows/useGridRowsOverridableMethods.js +57 -7
  76. package/esm/hooks/features/scroll/useGridScroll.js +2 -3
  77. package/esm/hooks/features/sorting/gridSortingUtils.js +1 -3
  78. package/esm/hooks/features/sorting/useGridSorting.d.ts +1 -1
  79. package/esm/hooks/features/sorting/useGridSorting.js +3 -1
  80. package/esm/hooks/features/virtualization/useGridVirtualization.js +24 -5
  81. package/esm/hooks/utils/useGridEvent.js +6 -2
  82. package/esm/hooks/utils/useGridSelector.js +2 -4
  83. package/esm/hooks/utils/useRunOncePerLoop.d.ts +4 -1
  84. package/esm/hooks/utils/useRunOncePerLoop.js +28 -18
  85. package/esm/index.js +1 -1
  86. package/esm/internals/index.d.ts +5 -4
  87. package/esm/internals/index.js +3 -3
  88. package/esm/material/index.js +1 -4
  89. package/esm/models/api/gridRowApi.d.ts +14 -1
  90. package/esm/models/api/index.d.ts +1 -1
  91. package/esm/models/api/index.js +0 -1
  92. package/esm/models/colDef/gridColDef.d.ts +14 -0
  93. package/esm/models/configuration/gridConfiguration.d.ts +2 -2
  94. package/esm/models/configuration/gridRowConfiguration.d.ts +6 -5
  95. package/esm/models/events/gridEventLookup.d.ts +5 -0
  96. package/esm/models/gridStateCommunity.d.ts +1 -1
  97. package/esm/models/params/gridCellParams.d.ts +0 -10
  98. package/esm/models/props/DataGridProps.d.ts +13 -6
  99. package/esm/utils/keyboardUtils.d.ts +1 -8
  100. package/esm/utils/keyboardUtils.js +0 -7
  101. package/hooks/core/gridPropsSelectors.d.ts +2 -1
  102. package/hooks/core/gridPropsSelectors.js +4 -1
  103. package/hooks/core/pipeProcessing/gridPipeProcessingApi.d.ts +4 -3
  104. package/hooks/core/useGridProps.js +8 -2
  105. package/hooks/core/useGridVirtualizer.d.ts +80 -6
  106. package/hooks/core/useGridVirtualizer.js +26 -11
  107. package/hooks/features/columnGrouping/useGridColumnGrouping.js +6 -1
  108. package/hooks/features/columnMenu/useGridColumnMenu.js +14 -4
  109. package/hooks/features/columns/useGridColumnSpanning.js +9 -4
  110. package/hooks/features/dataSource/useGridDataSourceBase.js +2 -2
  111. package/hooks/features/dimensions/useGridDimensions.js +12 -6
  112. package/hooks/features/editing/useGridCellEditing.js +1 -1
  113. package/hooks/features/editing/useGridRowEditing.js +1 -1
  114. package/hooks/features/export/serializers/csvSerializer.js +2 -4
  115. package/hooks/features/export/useGridPrintExport.js +18 -18
  116. package/hooks/features/filter/gridFilterUtils.js +5 -11
  117. package/hooks/features/filter/index.d.ts +1 -1
  118. package/hooks/features/filter/index.js +6 -0
  119. package/hooks/features/filter/useGridFilter.d.ts +1 -1
  120. package/hooks/features/filter/useGridFilter.js +3 -1
  121. package/hooks/features/focus/useGridFocus.js +0 -1
  122. package/hooks/features/keyboardNavigation/useGridKeyboardNavigation.d.ts +1 -1
  123. package/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +189 -25
  124. package/hooks/features/pagination/useGridPaginationMeta.js +2 -2
  125. package/hooks/features/pagination/useGridPaginationModel.js +7 -4
  126. package/hooks/features/pagination/useGridRowCount.js +30 -14
  127. package/hooks/features/rowReorder/gridRowReorderInterfaces.d.ts +19 -0
  128. package/hooks/features/rowReorder/gridRowReorderSelector.d.ts +20 -1
  129. package/hooks/features/rowReorder/gridRowReorderSelector.js +20 -2
  130. package/hooks/features/rowSelection/useGridRowSelection.js +17 -8
  131. package/hooks/features/rowSelection/utils.d.ts +1 -0
  132. package/hooks/features/rowSelection/utils.js +16 -3
  133. package/hooks/features/rows/useGridRowSpanning.js +23 -60
  134. package/hooks/features/rows/useGridRows.js +3 -1
  135. package/hooks/features/rows/useGridRowsOverridableMethods.d.ts +1 -0
  136. package/hooks/features/rows/useGridRowsOverridableMethods.js +57 -7
  137. package/hooks/features/scroll/useGridScroll.js +2 -3
  138. package/hooks/features/sorting/gridSortingUtils.js +1 -3
  139. package/hooks/features/sorting/useGridSorting.d.ts +1 -1
  140. package/hooks/features/sorting/useGridSorting.js +3 -1
  141. package/hooks/features/virtualization/useGridVirtualization.js +24 -5
  142. package/hooks/utils/useGridEvent.js +6 -2
  143. package/hooks/utils/useGridSelector.js +2 -4
  144. package/hooks/utils/useRunOncePerLoop.d.ts +4 -1
  145. package/hooks/utils/useRunOncePerLoop.js +27 -18
  146. package/index.js +1 -1
  147. package/internals/index.d.ts +5 -4
  148. package/internals/index.js +16 -9
  149. package/material/index.js +1 -4
  150. package/models/api/gridRowApi.d.ts +14 -1
  151. package/models/api/index.d.ts +1 -1
  152. package/models/api/index.js +0 -11
  153. package/models/colDef/gridColDef.d.ts +14 -0
  154. package/models/configuration/gridConfiguration.d.ts +2 -2
  155. package/models/configuration/gridRowConfiguration.d.ts +6 -5
  156. package/models/events/gridEventLookup.d.ts +5 -0
  157. package/models/gridStateCommunity.d.ts +1 -1
  158. package/models/params/gridCellParams.d.ts +0 -10
  159. package/models/props/DataGridProps.d.ts +13 -6
  160. package/package.json +3 -3
  161. package/utils/keyboardUtils.d.ts +1 -8
  162. package/utils/keyboardUtils.js +1 -13
@@ -62,7 +62,6 @@ export const useGridRowSelection = (apiRef, props) => {
62
62
  isRowSelectable: propIsRowSelectable
63
63
  } = props;
64
64
  const canHaveMultipleSelection = isMultipleRowSelectionEnabled(props);
65
- const tree = useGridSelector(apiRef, gridRowTreeSelector);
66
65
  const expandMouseRowRangeSelection = React.useCallback(id => {
67
66
  let endId = id;
68
67
  const startId = lastRowToggled.current ?? id;
@@ -101,6 +100,14 @@ export const useGridRowSelection = (apiRef, props) => {
101
100
  const currentModel = gridRowSelectionStateSelector(apiRef);
102
101
  if (currentModel !== model) {
103
102
  logger.debug(`Setting selection model`);
103
+
104
+ // clear the reference to the last selected row if that row is not in the model anymore
105
+ if (lastRowToggled.current !== null) {
106
+ const isInModel = model.ids.has(lastRowToggled.current);
107
+ if (model.type === 'include' && !isInModel || model.type === 'exclude' && isInModel) {
108
+ lastRowToggled.current = null;
109
+ }
110
+ }
104
111
  apiRef.current.setState(state => _extends({}, state, {
105
112
  rowSelection: props.rowSelection ? model : emptyModel
106
113
  }), reason);
@@ -128,7 +135,8 @@ export const useGridRowSelection = (apiRef, props) => {
128
135
  if (!apiRef.current.isRowSelectable(id)) {
129
136
  return;
130
137
  }
131
- lastRowToggled.current = id;
138
+ const tree = gridRowTreeSelector(apiRef);
139
+ lastRowToggled.current = isSelected ? id : null;
132
140
  if (resetSelection) {
133
141
  logger.debug(`Setting selection for row ${id}`);
134
142
  const newSelectionModel = {
@@ -173,12 +181,13 @@ export const useGridRowSelection = (apiRef, props) => {
173
181
  apiRef.current.setRowSelectionModel(newSelectionModel, 'singleRowSelection');
174
182
  }
175
183
  }
176
- }, [apiRef, logger, applyAutoSelection, tree, props.rowSelectionPropagation?.descendants, props.rowSelectionPropagation?.parents, canHaveMultipleSelection]);
184
+ }, [apiRef, logger, applyAutoSelection, props.rowSelectionPropagation?.descendants, props.rowSelectionPropagation?.parents, canHaveMultipleSelection]);
177
185
  const selectRows = React.useCallback((ids, isSelected = true, resetSelection = false) => {
178
186
  logger.debug(`Setting selection for several rows`);
179
187
  if (props.rowSelection === false) {
180
188
  return;
181
189
  }
190
+ const tree = gridRowTreeSelector(apiRef);
182
191
  const selectableIds = new Set();
183
192
  for (let i = 0; i < ids.length; i += 1) {
184
193
  const id = ids[i];
@@ -239,7 +248,7 @@ export const useGridRowSelection = (apiRef, props) => {
239
248
  if (isSelectionValid) {
240
249
  apiRef.current.setRowSelectionModel(newSelectionModel, 'multipleRowsSelection');
241
250
  }
242
- }, [logger, applyAutoSelection, canHaveMultipleSelection, apiRef, tree, props.rowSelectionPropagation?.descendants, props.rowSelectionPropagation?.parents, props.rowSelection]);
251
+ }, [logger, applyAutoSelection, canHaveMultipleSelection, apiRef, props.rowSelectionPropagation?.descendants, props.rowSelectionPropagation?.parents, props.rowSelection]);
243
252
  const getPropagatedRowSelectionModel = React.useCallback(inputSelectionModel => {
244
253
  if (!isNestedData || !applyAutoSelection || inputSelectionModel.type === 'exclude' || inputSelectionModel.ids.size === 0 && inputSelectionModel.type === 'include') {
245
254
  return inputSelectionModel;
@@ -248,6 +257,7 @@ export const useGridRowSelection = (apiRef, props) => {
248
257
  type: inputSelectionModel.type,
249
258
  ids: new Set(inputSelectionModel.ids)
250
259
  };
260
+ const tree = gridRowTreeSelector(apiRef);
251
261
  const selectionManager = createRowSelectionManager(propagatedSelectionModel);
252
262
  const addRow = rowId => {
253
263
  selectionManager.select(rowId);
@@ -256,7 +266,7 @@ export const useGridRowSelection = (apiRef, props) => {
256
266
  findRowsToSelect(apiRef, tree, id, props.rowSelectionPropagation?.descendants ?? false, props.rowSelectionPropagation?.parents ?? false, addRow, selectionManager);
257
267
  }
258
268
  return propagatedSelectionModel;
259
- }, [apiRef, tree, props.rowSelectionPropagation?.descendants, props.rowSelectionPropagation?.parents, isNestedData, applyAutoSelection]);
269
+ }, [apiRef, props.rowSelectionPropagation?.descendants, props.rowSelectionPropagation?.parents, isNestedData, applyAutoSelection]);
260
270
  const selectRowRange = React.useCallback(({
261
271
  startId,
262
272
  endId
@@ -325,7 +335,7 @@ export const useGridRowSelection = (apiRef, props) => {
325
335
  if (!props.rowSelectionPropagation?.parents) {
326
336
  continue;
327
337
  }
328
- const node = tree[id];
338
+ const node = rowTree[id];
329
339
  if (node?.type === 'group') {
330
340
  const isAutoGenerated = node.isAutoGenerated;
331
341
  if (isAutoGenerated) {
@@ -366,7 +376,7 @@ export const useGridRowSelection = (apiRef, props) => {
366
376
  apiRef.current.setRowSelectionModel(newSelectionModel, 'multipleRowsSelection');
367
377
  }
368
378
  }
369
- }, [apiRef, isNestedData, props.rowSelectionPropagation?.parents, props.keepNonExistentRowsSelected, props.filterMode, tree, getRowsToBeSelected]);
379
+ }, [apiRef, isNestedData, props.rowSelectionPropagation?.parents, props.keepNonExistentRowsSelected, props.filterMode, getRowsToBeSelected]);
370
380
  const handleSingleRowSelection = React.useCallback((id, event) => {
371
381
  const hasCtrlKey = event.metaKey || event.ctrlKey;
372
382
 
@@ -530,7 +540,6 @@ export const useGridRowSelection = (apiRef, props) => {
530
540
  }
531
541
  apiRef.current.setRowSelectionModel(propRowSelectionModel);
532
542
  });
533
- useGridEvent(apiRef, 'sortedRowsSet', runIfRowSelectionIsEnabled(() => removeOutdatedSelection(true)));
534
543
  useGridEvent(apiRef, 'filteredRowsSet', runIfRowSelectionIsEnabled(() => removeOutdatedSelection()));
535
544
  useGridEvent(apiRef, 'rowClick', runIfRowSelectionIsEnabled(handleRowClick));
536
545
  useGridEvent(apiRef, 'rowSelectionCheckboxChange', runIfRowSelectionIsEnabled(handleRowSelectionCheckboxChange));
@@ -13,6 +13,7 @@ export declare const checkboxPropsSelector: (args_0: import("react").RefObject<{
13
13
  }) => {
14
14
  isIndeterminate: boolean;
15
15
  isChecked: boolean;
16
+ isSelectable: boolean;
16
17
  };
17
18
  export declare function isMultipleRowSelectionEnabled(props: Pick<DataGridProcessedProps, 'signature' | 'disableMultipleRowSelection' | 'checkboxSelection'>): boolean;
18
19
  export declare const findRowsToSelect: (apiRef: RefObject<GridPrivateApiCommunity>, tree: GridRowTreeConfig, selectedRow: GridRowId, autoSelectDescendants: boolean, autoSelectParents: boolean, addRow: (rowId: GridRowId) => void, rowSelectionManager?: RowSelectionManager) => void;
@@ -3,8 +3,10 @@ import { GRID_ROOT_GROUP_ID } from "../rows/gridRowsUtils.js";
3
3
  import { gridFilteredRowsLookupSelector } from "../filter/gridFilterSelector.js";
4
4
  import { gridSortedRowIdsSelector } from "../sorting/gridSortingSelector.js";
5
5
  import { gridRowSelectionManagerSelector } from "./gridRowSelectionSelector.js";
6
- import { gridRowTreeSelector } from "../rows/gridRowsSelector.js";
6
+ import { gridRowsLookupSelector, gridRowTreeSelector } from "../rows/gridRowsSelector.js";
7
7
  import { createSelector } from "../../../utils/createSelector.js";
8
+ import { gridColumnDefinitionsSelector } from "../columns/index.js";
9
+ import { gridRowSelectableSelector } from "../../core/gridPropsSelectors.js";
8
10
  export const ROW_SELECTION_PROPAGATION_DEFAULT = {
9
11
  parents: true,
10
12
  descendants: true
@@ -27,15 +29,25 @@ function getGridRowGroupSelectableDescendants(apiRef, groupId) {
27
29
  }
28
30
  return descendants;
29
31
  }
30
- export const checkboxPropsSelector = createSelector(gridRowTreeSelector, gridFilteredRowsLookupSelector, gridRowSelectionManagerSelector, (rowTree, filteredRowsLookup, rowSelectionManager, {
32
+ export const checkboxPropsSelector = createSelector(gridColumnDefinitionsSelector, gridRowTreeSelector, gridFilteredRowsLookupSelector, gridRowSelectionManagerSelector, gridRowsLookupSelector, gridRowSelectableSelector, (columns, rowTree, filteredRowsLookup, rowSelectionManager, rowsLookup, isRowSelectable, {
31
33
  groupId,
32
34
  autoSelectParents
33
35
  }) => {
34
36
  const groupNode = rowTree[groupId];
37
+ const rowParams = {
38
+ id: groupId,
39
+ row: rowsLookup[groupId],
40
+ columns
41
+ };
42
+ let isSelectable = true;
43
+ if (typeof isRowSelectable === 'function') {
44
+ isSelectable = isRowSelectable(rowParams);
45
+ }
35
46
  if (!groupNode || groupNode.type !== 'group' || rowSelectionManager.has(groupId)) {
36
47
  return {
37
48
  isIndeterminate: false,
38
- isChecked: rowSelectionManager.has(groupId)
49
+ isChecked: rowSelectionManager.has(groupId),
50
+ isSelectable
39
51
  };
40
52
  }
41
53
  let hasSelectedDescendant = false;
@@ -60,7 +72,8 @@ export const checkboxPropsSelector = createSelector(gridRowTreeSelector, gridFil
60
72
  traverseDescendants(groupId);
61
73
  return {
62
74
  isIndeterminate: hasSelectedDescendant && hasUnSelectedDescendant,
63
- isChecked: autoSelectParents ? hasSelectedDescendant && !hasUnSelectedDescendant : false
75
+ isChecked: autoSelectParents ? hasSelectedDescendant && !hasUnSelectedDescendant : false,
76
+ isSelectable
64
77
  };
65
78
  });
66
79
  export function isMultipleRowSelectionEnabled(props) {
@@ -9,8 +9,7 @@ import { gridRenderContextSelector } from "../virtualization/gridVirtualizationS
9
9
  import { getUnprocessedRange, isRowContextInitialized, getCellValue } from "./gridRowSpanningUtils.js";
10
10
  import { useGridEvent } from "../../utils/useGridEvent.js";
11
11
  import { runIf } from "../../../utils/utils.js";
12
- import { gridPageSizeSelector } from "../pagination/index.js";
13
- import { gridDataRowIdsSelector } from "./gridRowsSelector.js";
12
+ import { useRunOncePerLoop } from "../../utils/useRunOncePerLoop.js";
14
13
  const EMPTY_CACHES = {
15
14
  spannedCells: {},
16
15
  hiddenCells: {},
@@ -24,13 +23,6 @@ const EMPTY_STATE = {
24
23
  caches: EMPTY_CACHES,
25
24
  processedRange: EMPTY_RANGE
26
25
  };
27
-
28
- /**
29
- * Default number of rows to process during state initialization to avoid flickering.
30
- * Number `20` is arbitrarily chosen to be large enough to cover most of the cases without
31
- * compromising performance.
32
- */
33
- const DEFAULT_ROWS_TO_PROCESS = 20;
34
26
  const computeRowSpanningState = (apiRef, colDefs, visibleRows, range, rangeToProcess, resetState) => {
35
27
  const virtualizer = apiRef.current.virtualizer;
36
28
  const previousState = resetState ? EMPTY_STATE : Rowspan.selectors.state(virtualizer.store.state);
@@ -128,65 +120,25 @@ const computeRowSpanningState = (apiRef, colDefs, visibleRows, range, rangeToPro
128
120
  processedRange
129
121
  };
130
122
  };
131
- const getInitialRangeToProcess = (props, apiRef) => {
132
- const rowCount = gridDataRowIdsSelector(apiRef).length;
133
- if (props.pagination) {
134
- const pageSize = gridPageSizeSelector(apiRef);
135
- let paginationLastRowIndex = DEFAULT_ROWS_TO_PROCESS;
136
- if (pageSize > 0) {
137
- paginationLastRowIndex = pageSize - 1;
138
- }
139
- return {
140
- firstRowIndex: 0,
141
- lastRowIndex: Math.min(paginationLastRowIndex, rowCount)
142
- };
143
- }
144
- return {
145
- firstRowIndex: 0,
146
- lastRowIndex: Math.min(DEFAULT_ROWS_TO_PROCESS, rowCount)
147
- };
148
- };
149
123
 
150
124
  /**
151
125
  * @requires columnsStateInitializer (method) - should be initialized before
152
126
  * @requires rowsStateInitializer (method) - should be initialized before
153
127
  * @requires filterStateInitializer (method) - should be initialized before
154
128
  */
155
- export const rowSpanningStateInitializer = (state, props, apiRef) => {
156
- if (!props.rowSpanning) {
157
- return _extends({}, state, {
158
- rowSpanning: EMPTY_STATE
159
- });
160
- }
161
- const rowIds = state.rows.dataRowIds || [];
162
- const orderedFields = state.columns.orderedFields || [];
163
- const dataRowIdToModelLookup = state.rows.dataRowIdToModelLookup;
164
- const columnsLookup = state.columns.lookup;
165
- const isFilteringPending = Boolean(state.filter.filterModel.items.length) || Boolean(state.filter.filterModel.quickFilterValues?.length);
166
- if (!rowIds.length || !orderedFields.length || !dataRowIdToModelLookup || !columnsLookup || isFilteringPending) {
167
- return _extends({}, state, {
168
- rowSpanning: EMPTY_STATE
169
- });
170
- }
171
- const rangeToProcess = getInitialRangeToProcess(props, apiRef);
172
- const rows = rowIds.map(id => ({
173
- id,
174
- model: dataRowIdToModelLookup[id]
175
- }));
176
- const colDefs = orderedFields.map(field => columnsLookup[field]);
177
- const rowSpanning = computeRowSpanningState(apiRef, colDefs, rows, rangeToProcess, rangeToProcess, true);
129
+ export const rowSpanningStateInitializer = state => {
178
130
  return _extends({}, state, {
179
- rowSpanning
131
+ rowSpanning: EMPTY_STATE
180
132
  });
181
133
  };
182
134
  export const useGridRowSpanning = (apiRef, props) => {
183
- const store = apiRef.current.virtualizer.store;
184
135
  const updateRowSpanningState = React.useCallback((renderContext, resetState = false) => {
136
+ const store = apiRef.current.virtualizer.store;
185
137
  const {
186
138
  range,
187
139
  rows: visibleRows
188
140
  } = getVisibleRows(apiRef);
189
- if (resetState && store.getSnapshot().rowSpanning !== EMPTY_STATE) {
141
+ if (resetState) {
190
142
  store.set('rowSpanning', EMPTY_STATE);
191
143
  }
192
144
  if (range === null || !isRowContextInitialized(renderContext)) {
@@ -212,7 +164,7 @@ export const useGridRowSpanning = (apiRef, props) => {
212
164
  return;
213
165
  }
214
166
  store.set('rowSpanning', newState);
215
- }, [apiRef, store]);
167
+ }, [apiRef]);
216
168
 
217
169
  // Reset events trigger a full re-computation of the row spanning state:
218
170
  // - The `rowSpanning` prop is updated (feature flag)
@@ -220,25 +172,36 @@ export const useGridRowSpanning = (apiRef, props) => {
220
172
  // - The sorting is applied
221
173
  // - The `paginationModel` is updated
222
174
  // - The rows are updated
175
+ const {
176
+ schedule: deferredUpdateRowSpanningState,
177
+ cancel
178
+ } = useRunOncePerLoop(updateRowSpanningState);
223
179
  const resetRowSpanningState = React.useCallback(() => {
224
180
  const renderContext = gridRenderContextSelector(apiRef);
225
181
  if (!isRowContextInitialized(renderContext)) {
226
182
  return;
227
183
  }
228
- updateRowSpanningState(renderContext, true);
229
- }, [apiRef, updateRowSpanningState]);
230
- useGridEvent(apiRef, 'renderedRowsIntervalChange', runIf(props.rowSpanning, updateRowSpanningState));
184
+ deferredUpdateRowSpanningState(renderContext, true);
185
+ }, [apiRef, deferredUpdateRowSpanningState]);
186
+ useGridEvent(apiRef, 'renderedRowsIntervalChange', runIf(props.rowSpanning, renderContext => {
187
+ const didHavePendingReset = cancel();
188
+ updateRowSpanningState(renderContext, didHavePendingReset);
189
+ }));
231
190
  useGridEvent(apiRef, 'sortedRowsSet', runIf(props.rowSpanning, resetRowSpanningState));
232
191
  useGridEvent(apiRef, 'paginationModelChange', runIf(props.rowSpanning, resetRowSpanningState));
233
192
  useGridEvent(apiRef, 'filteredRowsSet', runIf(props.rowSpanning, resetRowSpanningState));
234
193
  useGridEvent(apiRef, 'columnsChange', runIf(props.rowSpanning, resetRowSpanningState));
235
194
  React.useEffect(() => {
195
+ const store = apiRef.current.virtualizer?.store;
196
+ if (!store) {
197
+ return;
198
+ }
236
199
  if (!props.rowSpanning) {
237
200
  if (store.state.rowSpanning !== EMPTY_STATE) {
238
201
  store.set('rowSpanning', EMPTY_STATE);
239
202
  }
240
- } else if (store.state.rowSpanning.caches === EMPTY_CACHES) {
241
- resetRowSpanningState();
203
+ } else if (store.state.rowSpanning === EMPTY_STATE) {
204
+ updateRowSpanningState(gridRenderContextSelector(apiRef));
242
205
  }
243
- }, [apiRef, store, resetRowSpanningState, props.rowSpanning]);
206
+ }, [apiRef, props.rowSpanning, updateRowSpanningState]);
244
207
  };
@@ -52,7 +52,8 @@ export const useGridRows = (apiRef, props, configuration) => {
52
52
 
53
53
  // Get overridable methods from configuration
54
54
  const {
55
- setRowIndex
55
+ setRowIndex,
56
+ setRowPosition
56
57
  } = configuration.hooks.useGridRowsOverridableMethods(apiRef, props);
57
58
  const getRow = React.useCallback(id => {
58
59
  const model = gridRowsLookupSelector(apiRef)[id];
@@ -337,6 +338,7 @@ export const useGridRows = (apiRef, props, configuration) => {
337
338
  };
338
339
  const rowProApi = {
339
340
  setRowIndex,
341
+ setRowPosition,
340
342
  setRowChildrenExpansion,
341
343
  getRowGroupChildren,
342
344
  expandAllRows,
@@ -3,4 +3,5 @@ import { GridRowId } from "../../../models/gridRows.js";
3
3
  import { GridPrivateApiCommunity } from "../../../models/api/gridApiCommunity.js";
4
4
  export declare const useGridRowsOverridableMethods: (apiRef: RefObject<GridPrivateApiCommunity>) => {
5
5
  setRowIndex: (rowId: GridRowId, targetIndex: number) => void;
6
+ setRowPosition: (sourceRowId: GridRowId, targetRowId: GridRowId, position: import("../../../internals/index.js").RowReorderDropPosition) => void | Promise<void>;
6
7
  };
@@ -1,20 +1,69 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
3
  import { gridRowTreeSelector, gridRowNodeSelector } from "./gridRowsSelector.js";
4
+ import { gridExpandedSortedRowIndexLookupSelector } from "../filter/gridFilterSelector.js";
4
5
  import { GRID_ROOT_GROUP_ID } from "./gridRowsUtils.js";
5
6
  export const useGridRowsOverridableMethods = apiRef => {
7
+ const setRowPosition = React.useCallback((sourceRowId, targetRowId, position) => {
8
+ const sourceNode = gridRowNodeSelector(apiRef, sourceRowId);
9
+ const targetNode = gridRowNodeSelector(apiRef, targetRowId);
10
+ if (!sourceNode) {
11
+ throw new Error(`MUI X: No row with id #${sourceRowId} found.`);
12
+ }
13
+ if (!targetNode) {
14
+ throw new Error(`MUI X: No row with id #${targetRowId} found.`);
15
+ }
16
+ if (sourceNode.type !== 'leaf') {
17
+ throw new Error(`MUI X: The row reordering does not support reordering of footer or grouping rows.`);
18
+ }
19
+ if (position === 'inside') {
20
+ throw new Error(`MUI X: The 'inside' position is only supported for tree data. Use 'above' or 'below' for flat data.`);
21
+ }
22
+
23
+ // Get the target index from the targetRowId using the lookup selector
24
+ const sortedFilteredRowIndexLookup = gridExpandedSortedRowIndexLookupSelector(apiRef);
25
+ const targetRowIndexUnadjusted = sortedFilteredRowIndexLookup[targetRowId];
26
+ if (targetRowIndexUnadjusted === undefined) {
27
+ throw new Error(`MUI X: Target row with id #${targetRowId} not found in current view.`);
28
+ }
29
+ const sourceRowIndex = sortedFilteredRowIndexLookup[sourceRowId];
30
+ if (sourceRowIndex === undefined) {
31
+ throw new Error(`MUI X: Source row with id #${sourceRowId} not found in current view.`);
32
+ }
33
+ const dragDirection = targetRowIndexUnadjusted < sourceRowIndex ? 'up' : 'down';
34
+ let targetRowIndex;
35
+ if (dragDirection === 'up') {
36
+ targetRowIndex = position === 'above' ? targetRowIndexUnadjusted : targetRowIndexUnadjusted + 1;
37
+ } else {
38
+ targetRowIndex = position === 'above' ? targetRowIndexUnadjusted - 1 : targetRowIndexUnadjusted;
39
+ }
40
+ if (targetRowIndex === sourceRowIndex) {
41
+ return;
42
+ }
43
+ apiRef.current.setState(state => {
44
+ const group = gridRowTreeSelector(apiRef)[GRID_ROOT_GROUP_ID];
45
+ const allRows = group.children;
46
+ const updatedRows = [...allRows];
47
+ updatedRows.splice(targetRowIndex, 0, updatedRows.splice(sourceRowIndex, 1)[0]);
48
+ return _extends({}, state, {
49
+ rows: _extends({}, state.rows, {
50
+ tree: _extends({}, state.rows.tree, {
51
+ [GRID_ROOT_GROUP_ID]: _extends({}, group, {
52
+ children: updatedRows
53
+ })
54
+ })
55
+ })
56
+ });
57
+ });
58
+ apiRef.current.publishEvent('rowsSet');
59
+ }, [apiRef]);
6
60
  const setRowIndex = React.useCallback((rowId, targetIndex) => {
7
61
  const node = gridRowNodeSelector(apiRef, rowId);
8
62
  if (!node) {
9
63
  throw new Error(`MUI X: No row with id #${rowId} found.`);
10
64
  }
11
-
12
- // TODO: Remove irrelevant checks
13
- if (node.parent !== GRID_ROOT_GROUP_ID) {
14
- throw new Error(`MUI X: The row reordering do not support reordering of grouped rows yet.`);
15
- }
16
65
  if (node.type !== 'leaf') {
17
- throw new Error(`MUI X: The row reordering do not support reordering of footer or grouping rows.`);
66
+ throw new Error(`MUI X: The row reordering does not support reordering of footer or grouping rows.`);
18
67
  }
19
68
  apiRef.current.setState(state => {
20
69
  const group = gridRowTreeSelector(apiRef)[GRID_ROOT_GROUP_ID];
@@ -38,6 +87,7 @@ export const useGridRowsOverridableMethods = apiRef => {
38
87
  apiRef.current.publishEvent('rowsSet');
39
88
  }, [apiRef]);
40
89
  return {
41
- setRowIndex
90
+ setRowIndex,
91
+ setRowPosition
42
92
  };
43
93
  };
@@ -2,7 +2,6 @@ import * as React from 'react';
2
2
  import { useRtl } from '@mui/system/RtlProvider';
3
3
  import { useGridLogger } from "../../utils/useGridLogger.js";
4
4
  import { gridColumnPositionsSelector, gridVisibleColumnDefinitionsSelector } from "../columns/gridColumnsSelector.js";
5
- import { useGridSelector } from "../../utils/useGridSelector.js";
6
5
  import { gridPageSelector, gridPageSizeSelector } from "../pagination/gridPaginationSelector.js";
7
6
  import { gridRowCountSelector } from "../rows/gridRowsSelector.js";
8
7
  import { gridRowsMetaSelector } from "../rows/gridRowsMetaSelector.js";
@@ -47,7 +46,6 @@ export const useGridScroll = (apiRef, props) => {
47
46
  const logger = useGridLogger(apiRef, 'useGridScroll');
48
47
  const colRef = apiRef.current.columnHeadersContainerRef;
49
48
  const virtualScrollerRef = apiRef.current.virtualScrollerRef;
50
- const visibleSortedRows = useGridSelector(apiRef, gridExpandedSortedRowEntriesSelector);
51
49
  const scrollToIndexes = React.useCallback(params => {
52
50
  const dimensions = gridDimensionsSelector(apiRef);
53
51
  const totalRowCount = gridRowCountSelector(apiRef);
@@ -62,6 +60,7 @@ export const useGridScroll = (apiRef, props) => {
62
60
  const columnPositions = gridColumnPositionsSelector(apiRef);
63
61
  let cellWidth;
64
62
  if (typeof params.rowIndex !== 'undefined') {
63
+ const visibleSortedRows = gridExpandedSortedRowEntriesSelector(apiRef);
65
64
  const rowId = visibleSortedRows[params.rowIndex]?.id;
66
65
  const cellColSpanInfo = apiRef.current.unstable_getCellColSpanInfo(rowId, params.colIndex);
67
66
  if (cellColSpanInfo && !cellColSpanInfo.spannedByColSpan) {
@@ -98,7 +97,7 @@ export const useGridScroll = (apiRef, props) => {
98
97
  return true;
99
98
  }
100
99
  return false;
101
- }, [logger, apiRef, virtualScrollerRef, props.pagination, visibleSortedRows]);
100
+ }, [logger, apiRef, virtualScrollerRef, props.pagination]);
102
101
  const scroll = React.useCallback(params => {
103
102
  if (virtualScrollerRef.current && params.left !== undefined && colRef.current) {
104
103
  const direction = isRtl ? -1 : 1;
@@ -3,9 +3,7 @@ import { warnOnce } from '@mui/x-internals/warning';
3
3
  import { gridRowNodeSelector } from "../rows/gridRowsSelector.js";
4
4
  export const sanitizeSortModel = (model, disableMultipleColumnsSorting) => {
5
5
  if (disableMultipleColumnsSorting && model.length > 1) {
6
- if (process.env.NODE_ENV !== 'production') {
7
- warnOnce(['MUI X: The `sortModel` can only contain a single item when the `disableMultipleColumnsSorting` prop is set to `true`.', 'If you are using the community version of the Data Grid, this prop is always `true`.'], 'error');
8
- }
6
+ warnOnce(['MUI X: The `sortModel` can only contain a single item when the `disableMultipleColumnsSorting` prop is set to `true`.', 'If you are using the community version of the Data Grid, this prop is always `true`.'], 'error');
9
7
  return [model[0]];
10
8
  }
11
9
  return model;
@@ -7,4 +7,4 @@ export declare const sortingStateInitializer: GridStateInitializer<Pick<DataGrid
7
7
  * @requires useGridRows (event)
8
8
  * @requires useGridColumns (event)
9
9
  */
10
- export declare const useGridSorting: (apiRef: RefObject<GridPrivateApiCommunity>, props: Pick<DataGridProcessedProps, "initialState" | "sortModel" | "onSortModelChange" | "sortingOrder" | "sortingMode" | "disableColumnSorting" | "disableMultipleColumnsSorting" | "multipleColumnsSortingMode">) => void;
10
+ export declare const useGridSorting: (apiRef: RefObject<GridPrivateApiCommunity>, props: Pick<DataGridProcessedProps, "initialState" | "sortModel" | "onSortModelChange" | "sortingOrder" | "sortingMode" | "disableColumnSorting" | "disableMultipleColumnsSorting" | "multipleColumnsSortingMode" | "signature">) => void;
@@ -236,7 +236,9 @@ export const useGridSorting = (apiRef, props) => {
236
236
  * 1ST RENDER
237
237
  */
238
238
  useFirstRender(() => {
239
- apiRef.current.applySorting();
239
+ if (props.signature === 'DataGrid') {
240
+ apiRef.current.applySorting();
241
+ }
240
242
  });
241
243
 
242
244
  /**
@@ -30,9 +30,6 @@ export const virtualizationStateInitializer = (state, props) => {
30
30
  });
31
31
  };
32
32
  export function useGridVirtualization(apiRef, rootProps) {
33
- const {
34
- virtualizer
35
- } = apiRef.current;
36
33
  const {
37
34
  autoHeight,
38
35
  disableVirtualization
@@ -43,7 +40,14 @@ export function useGridVirtualization(apiRef, rootProps) {
43
40
  */
44
41
 
45
42
  const setVirtualization = enabled => {
43
+ const {
44
+ virtualizer
45
+ } = apiRef.current;
46
46
  enabled &&= HAS_LAYOUT;
47
+ const snapshot = virtualizer.store.getSnapshot();
48
+ if (snapshot.virtualization.enabled === enabled && snapshot.virtualization.enabledForRows === enabled && snapshot.virtualization.enabledForColumns === enabled) {
49
+ return;
50
+ }
47
51
  virtualizer.store.set('virtualization', _extends({}, virtualizer.store.state.virtualization, {
48
52
  enabled,
49
53
  enabledForColumns: enabled,
@@ -51,7 +55,14 @@ export function useGridVirtualization(apiRef, rootProps) {
51
55
  }));
52
56
  };
53
57
  const setColumnVirtualization = enabled => {
58
+ const {
59
+ virtualizer
60
+ } = apiRef.current;
54
61
  enabled &&= HAS_LAYOUT;
62
+ const snapshot = virtualizer.store.getSnapshot();
63
+ if (snapshot.virtualization.enabledForColumns === enabled) {
64
+ return;
65
+ }
55
66
  virtualizer.store.set('virtualization', _extends({}, virtualizer.store.state.virtualization, {
56
67
  enabledForColumns: enabled
57
68
  }));
@@ -61,7 +72,12 @@ export function useGridVirtualization(apiRef, rootProps) {
61
72
  unstable_setColumnVirtualization: setColumnVirtualization
62
73
  };
63
74
  useGridApiMethod(apiRef, api, 'public');
64
- const forceUpdateRenderContext = virtualizer.api.forceUpdateRenderContext;
75
+ const forceUpdateRenderContext = () => {
76
+ const {
77
+ virtualizer
78
+ } = apiRef.current;
79
+ virtualizer?.api.scheduleUpdateRenderContext();
80
+ };
65
81
  apiRef.current.register('private', {
66
82
  updateRenderContext: forceUpdateRenderContext
67
83
  });
@@ -76,7 +92,10 @@ export function useGridVirtualization(apiRef, rootProps) {
76
92
 
77
93
  /* eslint-disable react-hooks/exhaustive-deps */
78
94
  React.useEffect(() => {
95
+ if (!apiRef.current.virtualizer) {
96
+ return;
97
+ }
79
98
  setVirtualization(!rootProps.disableVirtualization);
80
- }, [disableVirtualization, autoHeight]);
99
+ }, [apiRef, disableVirtualization, autoHeight]);
81
100
  /* eslint-enable react-hooks/exhaustive-deps */
82
101
  }
@@ -24,7 +24,9 @@ export function useGridEvent(apiRef, eventName, handler, options) {
24
24
  const cleanupTokenRef = React.useRef(null);
25
25
  if (!subscription.current && handlerRef.current) {
26
26
  const enhancedHandler = (params, event, details) => {
27
- if (!event.defaultMuiPrevented) {
27
+ // Check for the existence of the event once more to avoid Safari 26 issue
28
+ // https://github.com/mui/mui-x/issues/20159
29
+ if (event && !event.defaultMuiPrevented) {
28
30
  handlerRef.current?.(params, event, details);
29
31
  }
30
32
  };
@@ -51,7 +53,9 @@ export function useGridEvent(apiRef, eventName, handler, options) {
51
53
  React.useEffect(() => {
52
54
  if (!subscription.current && handlerRef.current) {
53
55
  const enhancedHandler = (params, event, details) => {
54
- if (!event.defaultMuiPrevented) {
56
+ // Check for the existence of the event once more to avoid Safari 26 issue
57
+ // https://github.com/mui/mui-x/issues/20159
58
+ if (event && !event.defaultMuiPrevented) {
55
59
  handlerRef.current?.(params, event, details);
56
60
  }
57
61
  };
@@ -31,10 +31,8 @@ const createRefs = () => ({
31
31
  const EMPTY = [];
32
32
  const emptyGetSnapshot = () => null;
33
33
  export function useGridSelector(apiRef, selector, args = undefined, equals = defaultCompare) {
34
- if (process.env.NODE_ENV !== 'production') {
35
- if (!apiRef.current.state) {
36
- warnOnce(['MUI X: `useGridSelector` has been called before the initialization of the state.', 'This hook can only be used inside the context of the grid.']);
37
- }
34
+ if (!apiRef.current.state) {
35
+ warnOnce(['MUI X: `useGridSelector` has been called before the initialization of the state.', 'This hook can only be used inside the context of the grid.']);
38
36
  }
39
37
  const refs = useLazyRef(createRefs);
40
38
  const didInit = refs.current.selector !== null;
@@ -1 +1,4 @@
1
- export declare function useRunOncePerLoop<T extends (...args: any[]) => void>(callback: T, nextFrame?: boolean): (...args: Parameters<T>) => void;
1
+ export declare function useRunOncePerLoop<T extends (...args: any[]) => void>(callback: T): {
2
+ schedule: (...args: Parameters<T>) => void;
3
+ cancel: () => boolean;
4
+ };
@@ -1,26 +1,36 @@
1
+ 'use client';
2
+
1
3
  import * as React from 'react';
2
- export function useRunOncePerLoop(callback, nextFrame = false) {
3
- const scheduledRef = React.useRef(false);
4
+ export function useRunOncePerLoop(callback) {
5
+ const scheduledCallbackRef = React.useRef(null);
4
6
  const schedule = React.useCallback((...args) => {
5
- if (scheduledRef.current) {
6
- return;
7
+ // for robustness, a fallback in case we don't react to state updates and layoutEffect is not run
8
+ // if we react to state updates, layoutEffect will run before microtasks
9
+ if (!scheduledCallbackRef.current) {
10
+ queueMicrotask(() => {
11
+ if (scheduledCallbackRef.current) {
12
+ scheduledCallbackRef.current();
13
+ }
14
+ });
7
15
  }
8
- scheduledRef.current = true;
9
- const runner = () => {
10
- scheduledRef.current = false;
16
+ scheduledCallbackRef.current = () => {
17
+ scheduledCallbackRef.current = null;
11
18
  callback(...args);
12
19
  };
13
- if (nextFrame) {
14
- if (typeof requestAnimationFrame === 'function') {
15
- requestAnimationFrame(runner);
16
- }
17
- return;
20
+ }, [callback]);
21
+ React.useLayoutEffect(() => {
22
+ if (scheduledCallbackRef.current) {
23
+ scheduledCallbackRef.current();
18
24
  }
19
- if (typeof queueMicrotask === 'function') {
20
- queueMicrotask(runner);
21
- } else {
22
- Promise.resolve().then(runner);
25
+ });
26
+ return {
27
+ schedule,
28
+ cancel: () => {
29
+ if (scheduledCallbackRef.current) {
30
+ scheduledCallbackRef.current = null;
31
+ return true;
32
+ }
33
+ return false;
23
34
  }
24
- }, [callback, nextFrame]);
25
- return schedule;
35
+ };
26
36
  }
package/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v8.18.0
2
+ * @mui/x-data-grid v8.20.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the