@mui/x-data-grid-premium 8.10.2 → 8.11.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 (62) hide show
  1. package/CHANGELOG.md +214 -13
  2. package/DataGridPremium/DataGridPremium.js +6 -4
  3. package/DataGridPremium/useDataGridPremiumComponent.d.ts +2 -1
  4. package/DataGridPremium/useDataGridPremiumComponent.js +2 -2
  5. package/esm/DataGridPremium/DataGridPremium.js +6 -4
  6. package/esm/DataGridPremium/useDataGridPremiumComponent.d.ts +2 -1
  7. package/esm/DataGridPremium/useDataGridPremiumComponent.js +2 -2
  8. package/esm/hooks/features/aggregation/wrapColumnWithAggregation.d.ts +1 -0
  9. package/esm/hooks/features/clipboard/useGridClipboardImport.js +1 -1
  10. package/esm/hooks/features/export/serializer/setupExcelExportWebWorker.js +1 -2
  11. package/esm/hooks/features/rowGrouping/createGroupingColDef.d.ts +2 -1
  12. package/esm/hooks/features/rowGrouping/createGroupingColDef.js +31 -7
  13. package/esm/hooks/features/rowGrouping/gridRowGroupingInterfaces.d.ts +1 -0
  14. package/esm/hooks/features/rowGrouping/gridRowGroupingUtils.js +5 -1
  15. package/esm/hooks/features/rowGrouping/useGridRowGrouping.d.ts +1 -1
  16. package/esm/hooks/features/rowGrouping/useGridRowGrouping.js +48 -2
  17. package/esm/hooks/features/rowReorder/operations.d.ts +40 -0
  18. package/esm/hooks/features/rowReorder/operations.js +535 -0
  19. package/esm/hooks/features/rowReorder/reorderExecutor.d.ts +15 -0
  20. package/esm/hooks/features/rowReorder/reorderExecutor.js +25 -0
  21. package/esm/hooks/features/rowReorder/reorderValidator.d.ts +16 -0
  22. package/esm/hooks/features/rowReorder/reorderValidator.js +116 -0
  23. package/esm/hooks/features/rowReorder/types.d.ts +42 -0
  24. package/esm/hooks/features/rowReorder/types.js +1 -0
  25. package/esm/hooks/features/rowReorder/utils.d.ts +127 -0
  26. package/esm/hooks/features/rowReorder/utils.js +343 -0
  27. package/esm/hooks/features/rows/useGridRowsOverridableMethods.d.ts +7 -0
  28. package/esm/hooks/features/rows/useGridRowsOverridableMethods.js +52 -0
  29. package/esm/index.js +1 -1
  30. package/esm/models/gridGroupingValueSetter.d.ts +14 -0
  31. package/esm/models/gridGroupingValueSetter.js +1 -0
  32. package/esm/models/index.d.ts +1 -0
  33. package/esm/models/index.js +1 -0
  34. package/esm/typeOverloads/modules.d.ts +7 -1
  35. package/hooks/features/aggregation/wrapColumnWithAggregation.d.ts +1 -0
  36. package/hooks/features/clipboard/useGridClipboardImport.js +1 -1
  37. package/hooks/features/export/serializer/setupExcelExportWebWorker.js +1 -2
  38. package/hooks/features/rowGrouping/createGroupingColDef.d.ts +2 -1
  39. package/hooks/features/rowGrouping/createGroupingColDef.js +31 -7
  40. package/hooks/features/rowGrouping/gridRowGroupingInterfaces.d.ts +1 -0
  41. package/hooks/features/rowGrouping/gridRowGroupingUtils.js +5 -1
  42. package/hooks/features/rowGrouping/useGridRowGrouping.d.ts +1 -1
  43. package/hooks/features/rowGrouping/useGridRowGrouping.js +46 -0
  44. package/hooks/features/rowReorder/operations.d.ts +40 -0
  45. package/hooks/features/rowReorder/operations.js +546 -0
  46. package/hooks/features/rowReorder/reorderExecutor.d.ts +15 -0
  47. package/hooks/features/rowReorder/reorderExecutor.js +31 -0
  48. package/hooks/features/rowReorder/reorderValidator.d.ts +16 -0
  49. package/hooks/features/rowReorder/reorderValidator.js +122 -0
  50. package/hooks/features/rowReorder/types.d.ts +42 -0
  51. package/hooks/features/rowReorder/types.js +5 -0
  52. package/hooks/features/rowReorder/utils.d.ts +127 -0
  53. package/hooks/features/rowReorder/utils.js +360 -0
  54. package/hooks/features/rows/useGridRowsOverridableMethods.d.ts +7 -0
  55. package/hooks/features/rows/useGridRowsOverridableMethods.js +60 -0
  56. package/index.js +1 -1
  57. package/models/gridGroupingValueSetter.d.ts +14 -0
  58. package/models/gridGroupingValueSetter.js +5 -0
  59. package/models/index.d.ts +1 -0
  60. package/models/index.js +11 -0
  61. package/package.json +14 -14
  62. package/typeOverloads/modules.d.ts +7 -1
@@ -2,10 +2,11 @@
2
2
 
3
3
  import _extends from "@babel/runtime/helpers/esm/extends";
4
4
  import * as React from 'react';
5
- import { useGridEvent, useGridApiMethod, gridColumnLookupSelector } from '@mui/x-data-grid-pro';
6
- import { useGridRegisterPipeProcessor, GridStrategyGroup, RowGroupingStrategy } from '@mui/x-data-grid-pro/internals';
5
+ import { useGridEvent, useGridApiMethod, gridColumnLookupSelector, gridRowMaximumTreeDepthSelector, gridRowTreeSelector, gridExpandedSortedRowIdsSelector } from '@mui/x-data-grid-pro';
6
+ import { useGridRegisterPipeProcessor, GridStrategyGroup, gridExpandedSortedRowIndexLookupSelector, RowGroupingStrategy } from '@mui/x-data-grid-pro/internals';
7
7
  import { gridRowGroupingModelSelector, gridRowGroupingSanitizedModelSelector } from "./gridRowGroupingSelector.js";
8
8
  import { getRowGroupingFieldFromGroupingCriteria, isGroupingColumn, mergeStateWithRowGroupingModel, setStrategyAvailability, getGroupingRules, areGroupingRulesEqual } from "./gridRowGroupingUtils.js";
9
+ import { rowGroupingReorderValidator } from "../rowReorder/reorderValidator.js";
9
10
  export const rowGroupingStateInitializer = (state, props, apiRef) => {
10
11
  apiRef.current.caches.rowGrouping = {
11
12
  rulesOnLastRowTreeCreation: []
@@ -168,6 +169,51 @@ export const useGridRowGrouping = (apiRef, props) => {
168
169
  }
169
170
  }
170
171
  }, [apiRef, props.disableRowGrouping]);
172
+ const getRowReorderTargetIndex = React.useCallback((initialValue, {
173
+ sourceRowId,
174
+ targetRowId,
175
+ dropPosition,
176
+ dragDirection
177
+ }) => {
178
+ if (gridRowMaximumTreeDepthSelector(apiRef) === 1 || props.treeData) {
179
+ return initialValue;
180
+ }
181
+ const expandedSortedRowIndexLookup = gridExpandedSortedRowIndexLookupSelector(apiRef);
182
+ const expandedSortedRowIds = gridExpandedSortedRowIdsSelector(apiRef);
183
+ const rowTree = gridRowTreeSelector(apiRef);
184
+ const sourceRowIndex = expandedSortedRowIndexLookup[sourceRowId];
185
+ const targetRowIndex = expandedSortedRowIndexLookup[targetRowId];
186
+ const sourceNode = rowTree[sourceRowId];
187
+ const targetNode = rowTree[targetRowId];
188
+ const prevNode = targetRowIndex > 0 ? rowTree[expandedSortedRowIds[targetRowIndex - 1]] : null;
189
+ const nextNode = targetRowIndex < expandedSortedRowIds.length - 1 ? rowTree[expandedSortedRowIds[targetRowIndex + 1]] : null;
190
+
191
+ // Basic validity checks
192
+ if (!sourceNode || !targetNode) {
193
+ return -1;
194
+ }
195
+
196
+ // Create context object
197
+ const context = {
198
+ sourceNode,
199
+ targetNode,
200
+ prevNode,
201
+ nextNode,
202
+ rowTree,
203
+ dropPosition,
204
+ dragDirection,
205
+ targetRowIndex,
206
+ sourceRowIndex,
207
+ expandedSortedRowIndexLookup
208
+ };
209
+
210
+ // Check if the reorder is valid
211
+ if (rowGroupingReorderValidator.validate(context)) {
212
+ return dropPosition === 'below' ? targetRowIndex + 1 : targetRowIndex;
213
+ }
214
+ return -1;
215
+ }, [apiRef, props.treeData]);
216
+ useGridRegisterPipeProcessor(apiRef, 'getRowReorderTargetIndex', getRowReorderTargetIndex);
171
217
  useGridEvent(apiRef, 'cellKeyDown', handleCellKeyDown);
172
218
  useGridEvent(apiRef, 'columnsChange', checkGroupingColumnsModelDiff);
173
219
  useGridEvent(apiRef, 'rowGroupingModelChange', checkGroupingColumnsModelDiff);
@@ -0,0 +1,40 @@
1
+ import type { ReorderOperation, ReorderExecutionContext } from "./types.js";
2
+ /**
3
+ * Base class for all reorder operations.
4
+ * Provides abstract methods for operation detection and execution.
5
+ */
6
+ export declare abstract class BaseReorderOperation {
7
+ abstract readonly operationType: string;
8
+ /**
9
+ * Detects if this operation can handle the given context.
10
+ */
11
+ abstract detectOperation(ctx: ReorderExecutionContext): ReorderOperation | null;
12
+ /**
13
+ * Executes the detected operation.
14
+ */
15
+ abstract executeOperation(operation: ReorderOperation, ctx: ReorderExecutionContext): Promise<void> | void;
16
+ }
17
+ /**
18
+ * Handles reordering of items within the same parent group.
19
+ */
20
+ export declare class SameParentSwapOperation extends BaseReorderOperation {
21
+ readonly operationType = "same-parent-swap";
22
+ detectOperation(ctx: ReorderExecutionContext): ReorderOperation | null;
23
+ executeOperation(operation: ReorderOperation, ctx: ReorderExecutionContext): void;
24
+ }
25
+ /**
26
+ * Handles moving leaf nodes between different parent groups.
27
+ */
28
+ export declare class CrossParentLeafOperation extends BaseReorderOperation {
29
+ readonly operationType = "cross-parent-leaf";
30
+ detectOperation(ctx: ReorderExecutionContext): ReorderOperation | null;
31
+ executeOperation(operation: ReorderOperation, ctx: ReorderExecutionContext): Promise<void>;
32
+ }
33
+ /**
34
+ * Handles moving entire groups between different parents.
35
+ */
36
+ export declare class CrossParentGroupOperation extends BaseReorderOperation {
37
+ readonly operationType = "cross-parent-group";
38
+ detectOperation(ctx: ReorderExecutionContext): ReorderOperation | null;
39
+ executeOperation(operation: ReorderOperation, ctx: ReorderExecutionContext): Promise<void>;
40
+ }
@@ -0,0 +1,535 @@
1
+ import _extends from "@babel/runtime/helpers/esm/extends";
2
+ import { gridRowNodeSelector, gridRowTreeSelector, gridRowsLookupSelector, gridColumnLookupSelector } from '@mui/x-data-grid-pro';
3
+ import { warnOnce } from '@mui/x-internals/warning';
4
+ import { isDeepEqual } from '@mui/x-internals/isDeepEqual';
5
+ import { gridRowGroupingSanitizedModelSelector } from "../rowGrouping/index.js";
6
+ import { getGroupingRules, getCellGroupingCriteria } from "../rowGrouping/gridRowGroupingUtils.js";
7
+ import { determineOperationType, calculateTargetIndex, collectAllLeafDescendants, getNodePathInTree, removeEmptyAncestors, findExistingGroupWithSameKey, BatchRowUpdater } from "./utils.js";
8
+ /**
9
+ * Base class for all reorder operations.
10
+ * Provides abstract methods for operation detection and execution.
11
+ */
12
+ export class BaseReorderOperation {}
13
+
14
+ /**
15
+ * Handles reordering of items within the same parent group.
16
+ */
17
+ export class SameParentSwapOperation extends BaseReorderOperation {
18
+ operationType = 'same-parent-swap';
19
+ detectOperation(ctx) {
20
+ const {
21
+ sourceRowId,
22
+ placeholderIndex,
23
+ sortedFilteredRowIds,
24
+ sortedFilteredRowIndexLookup,
25
+ rowTree,
26
+ apiRef
27
+ } = ctx;
28
+ const sourceNode = gridRowNodeSelector(apiRef, sourceRowId);
29
+ if (!sourceNode || sourceNode.type === 'footer') {
30
+ return null;
31
+ }
32
+ let targetIndex = placeholderIndex;
33
+ const sourceIndex = sortedFilteredRowIndexLookup[sourceRowId];
34
+ if (targetIndex === sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) {
35
+ targetIndex -= 1;
36
+ }
37
+ let targetNode = gridRowNodeSelector(apiRef, sortedFilteredRowIds[targetIndex]);
38
+ if (placeholderIndex > sourceIndex && sourceNode.parent === targetNode.parent) {
39
+ targetIndex = placeholderIndex - 1;
40
+ targetNode = gridRowNodeSelector(apiRef, sortedFilteredRowIds[targetIndex]);
41
+ if (targetNode && targetNode.depth !== sourceNode.depth) {
42
+ while (targetNode.depth > sourceNode.depth && targetIndex >= 0) {
43
+ targetIndex -= 1;
44
+ targetNode = gridRowNodeSelector(apiRef, sortedFilteredRowIds[targetIndex]);
45
+ }
46
+ }
47
+ if (targetIndex === -1) {
48
+ return null;
49
+ }
50
+ }
51
+ let isLastChild = false;
52
+ if (!targetNode) {
53
+ if (placeholderIndex >= sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) {
54
+ targetNode = gridRowNodeSelector(apiRef, sortedFilteredRowIds[sortedFilteredRowIds.length - 1]);
55
+ isLastChild = true;
56
+ } else {
57
+ return null;
58
+ }
59
+ }
60
+ let adjustedTargetNode = targetNode;
61
+
62
+ // Case A and B adjustment
63
+ if (targetNode.type === 'group' && sourceNode.parent !== targetNode.parent && sourceNode.depth > targetNode.depth) {
64
+ let i = targetIndex - 1;
65
+ while (i >= 0) {
66
+ const node = gridRowNodeSelector(apiRef, sortedFilteredRowIds[i]);
67
+ if (node && node.depth < sourceNode.depth) {
68
+ return null;
69
+ }
70
+ if (node && node.depth === sourceNode.depth) {
71
+ targetIndex = i;
72
+ adjustedTargetNode = node;
73
+ break;
74
+ }
75
+ i -= 1;
76
+ }
77
+ }
78
+ const operationType = determineOperationType(sourceNode, adjustedTargetNode);
79
+ if (operationType !== 'same-parent-swap') {
80
+ return null;
81
+ }
82
+ const actualTargetIndex = calculateTargetIndex(sourceNode, adjustedTargetNode, isLastChild, rowTree);
83
+ targetNode = adjustedTargetNode;
84
+ if (sourceNode.type !== targetNode.type) {
85
+ return null;
86
+ }
87
+ return {
88
+ sourceNode,
89
+ targetNode,
90
+ actualTargetIndex,
91
+ isLastChild,
92
+ operationType
93
+ };
94
+ }
95
+ executeOperation(operation, ctx) {
96
+ const {
97
+ sourceNode,
98
+ actualTargetIndex
99
+ } = operation;
100
+ const {
101
+ apiRef,
102
+ sourceRowId
103
+ } = ctx;
104
+ apiRef.current.setState(state => {
105
+ const group = gridRowTreeSelector(apiRef)[sourceNode.parent];
106
+ const currentChildren = [...group.children];
107
+ const oldIndex = currentChildren.findIndex(row => row === sourceRowId);
108
+ if (oldIndex === -1 || actualTargetIndex === -1 || oldIndex === actualTargetIndex) {
109
+ return state;
110
+ }
111
+ currentChildren.splice(actualTargetIndex, 0, currentChildren.splice(oldIndex, 1)[0]);
112
+ return _extends({}, state, {
113
+ rows: _extends({}, state.rows, {
114
+ tree: _extends({}, state.rows.tree, {
115
+ [sourceNode.parent]: _extends({}, group, {
116
+ children: currentChildren
117
+ })
118
+ })
119
+ })
120
+ });
121
+ });
122
+ apiRef.current.publishEvent('rowsSet');
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Handles moving leaf nodes between different parent groups.
128
+ */
129
+ export class CrossParentLeafOperation extends BaseReorderOperation {
130
+ operationType = 'cross-parent-leaf';
131
+ detectOperation(ctx) {
132
+ const {
133
+ sourceRowId,
134
+ placeholderIndex,
135
+ sortedFilteredRowIds,
136
+ rowTree,
137
+ apiRef
138
+ } = ctx;
139
+ const sourceNode = gridRowNodeSelector(apiRef, sourceRowId);
140
+ if (!sourceNode || sourceNode.type === 'footer') {
141
+ return null;
142
+ }
143
+ let targetNode = gridRowNodeSelector(apiRef, sortedFilteredRowIds[placeholderIndex]);
144
+ let isLastChild = false;
145
+ if (!targetNode) {
146
+ if (placeholderIndex >= sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) {
147
+ targetNode = gridRowNodeSelector(apiRef, sortedFilteredRowIds[sortedFilteredRowIds.length - 1]);
148
+ isLastChild = true;
149
+ } else {
150
+ return null;
151
+ }
152
+ }
153
+ let adjustedTargetNode = targetNode;
154
+
155
+ // Case D adjustment
156
+ if (sourceNode.type === 'leaf' && targetNode.type === 'group' && targetNode.depth < sourceNode.depth) {
157
+ const prevIndex = placeholderIndex - 1;
158
+ if (prevIndex >= 0) {
159
+ const prevRowId = sortedFilteredRowIds[prevIndex];
160
+ const leafTargetNode = gridRowNodeSelector(apiRef, prevRowId);
161
+ if (leafTargetNode && leafTargetNode.type === 'leaf') {
162
+ adjustedTargetNode = leafTargetNode;
163
+ isLastChild = true;
164
+ }
165
+ }
166
+ }
167
+ const operationType = determineOperationType(sourceNode, adjustedTargetNode);
168
+ if (operationType !== 'cross-parent-leaf') {
169
+ return null;
170
+ }
171
+ const actualTargetIndex = calculateTargetIndex(sourceNode, adjustedTargetNode, isLastChild, rowTree);
172
+ targetNode = adjustedTargetNode;
173
+
174
+ // Validate depth constraints
175
+ if (sourceNode.type === 'leaf' && targetNode.type === 'leaf') {
176
+ if (sourceNode.depth !== targetNode.depth) {
177
+ return null;
178
+ }
179
+ } else if (sourceNode.type === 'leaf' && targetNode.type === 'group') {
180
+ if (targetNode.depth >= sourceNode.depth) {
181
+ return null;
182
+ }
183
+ }
184
+ return {
185
+ sourceNode,
186
+ targetNode: adjustedTargetNode,
187
+ actualTargetIndex,
188
+ isLastChild,
189
+ operationType
190
+ };
191
+ }
192
+ async executeOperation(operation, ctx) {
193
+ const {
194
+ sourceNode,
195
+ targetNode,
196
+ isLastChild
197
+ } = operation;
198
+ const {
199
+ apiRef,
200
+ sourceRowId,
201
+ processRowUpdate,
202
+ onProcessRowUpdateError
203
+ } = ctx;
204
+ let target = targetNode;
205
+ if (targetNode.type === 'group') {
206
+ const prevIndex = ctx.placeholderIndex - 1;
207
+ if (prevIndex >= 0) {
208
+ const prevRowId = ctx.sortedFilteredRowIds[prevIndex];
209
+ const prevNode = gridRowNodeSelector(apiRef, prevRowId);
210
+ if (prevNode && prevNode.type === 'leaf') {
211
+ target = prevNode;
212
+ }
213
+ }
214
+ }
215
+ const rowTree = gridRowTreeSelector(apiRef);
216
+ const sourceGroup = rowTree[sourceNode.parent];
217
+ const targetGroup = rowTree[target.parent];
218
+ const sourceChildren = sourceGroup.children;
219
+ const targetChildren = targetGroup.children;
220
+ const sourceIndex = sourceChildren.findIndex(row => row === sourceRowId);
221
+ const targetIndex = targetChildren.findIndex(row => row === target.id);
222
+ if (sourceIndex === -1 || targetIndex === -1) {
223
+ return;
224
+ }
225
+ const dataRowIdToModelLookup = gridRowsLookupSelector(apiRef);
226
+ const columnsLookup = gridColumnLookupSelector(apiRef);
227
+ const sanitizedRowGroupingModel = gridRowGroupingSanitizedModelSelector(apiRef);
228
+ const originalSourceRow = dataRowIdToModelLookup[sourceRowId];
229
+ let updatedSourceRow = _extends({}, originalSourceRow);
230
+ const targetRow = dataRowIdToModelLookup[target.id];
231
+ const groupingRules = getGroupingRules({
232
+ sanitizedRowGroupingModel,
233
+ columnsLookup
234
+ });
235
+ for (const groupingRule of groupingRules) {
236
+ const colDef = columnsLookup[groupingRule.field];
237
+ if (groupingRule.groupingValueSetter && colDef) {
238
+ const targetGroupingValue = getCellGroupingCriteria({
239
+ row: targetRow,
240
+ colDef,
241
+ groupingRule,
242
+ apiRef
243
+ }).key;
244
+ updatedSourceRow = groupingRule.groupingValueSetter(targetGroupingValue, updatedSourceRow, colDef, apiRef);
245
+ } else {
246
+ updatedSourceRow[groupingRule.field] = targetRow[groupingRule.field];
247
+ }
248
+ }
249
+ const commitStateUpdate = finalSourceRow => {
250
+ apiRef.current.setState(state => {
251
+ const updatedSourceChildren = sourceChildren.filter(rowId => rowId !== sourceRowId);
252
+ const updatedTree = _extends({}, state.rows.tree);
253
+ const removedGroups = new Set();
254
+ let rootLevelRemovals = 0;
255
+ if (updatedSourceChildren.length === 0) {
256
+ removedGroups.add(sourceGroup.id);
257
+ rootLevelRemovals = removeEmptyAncestors(sourceGroup.parent, updatedTree, removedGroups);
258
+ }
259
+ removedGroups.forEach(groupId => {
260
+ const group = updatedTree[groupId];
261
+ if (group && group.parent && updatedTree[group.parent]) {
262
+ const parent = updatedTree[group.parent];
263
+ updatedTree[group.parent] = _extends({}, parent, {
264
+ children: parent.children.filter(childId => childId !== groupId)
265
+ });
266
+ }
267
+ delete updatedTree[groupId];
268
+ });
269
+ if (!removedGroups.has(sourceGroup.id)) {
270
+ updatedTree[sourceNode.parent] = _extends({}, sourceGroup, {
271
+ children: updatedSourceChildren
272
+ });
273
+ }
274
+ const updatedTargetChildren = isLastChild ? [...targetChildren, sourceRowId] : [...targetChildren.slice(0, targetIndex), sourceRowId, ...targetChildren.slice(targetIndex)];
275
+ updatedTree[target.parent] = _extends({}, targetGroup, {
276
+ children: updatedTargetChildren
277
+ });
278
+ updatedTree[sourceNode.id] = _extends({}, sourceNode, {
279
+ parent: target.parent
280
+ });
281
+ return _extends({}, state, {
282
+ rows: _extends({}, state.rows, {
283
+ totalTopLevelRowCount: state.rows.totalTopLevelRowCount - rootLevelRemovals,
284
+ tree: updatedTree
285
+ })
286
+ });
287
+ });
288
+ apiRef.current.updateRows([finalSourceRow]);
289
+ apiRef.current.publishEvent('rowsSet');
290
+ };
291
+ if (processRowUpdate && !isDeepEqual(originalSourceRow, updatedSourceRow)) {
292
+ const params = {
293
+ rowId: sourceRowId,
294
+ previousRow: originalSourceRow,
295
+ updatedRow: updatedSourceRow
296
+ };
297
+ apiRef.current.setLoading(true);
298
+ try {
299
+ const processedRow = await processRowUpdate(updatedSourceRow, originalSourceRow, params);
300
+ const finalRow = processedRow || updatedSourceRow;
301
+ commitStateUpdate(finalRow);
302
+ } catch (error) {
303
+ apiRef.current.setLoading(false);
304
+ if (onProcessRowUpdateError) {
305
+ onProcessRowUpdateError(error);
306
+ } else if (process.env.NODE_ENV !== 'production') {
307
+ warnOnce(['MUI X: A call to `processRowUpdate()` threw an error which was not handled because `onProcessRowUpdateError()` is missing.', 'To handle the error pass a callback to the `onProcessRowUpdateError()` prop, for example `<DataGrid onProcessRowUpdateError={(error) => ...} />`.', 'For more detail, see https://mui.com/x/react-data-grid/editing/persistence/.'], 'error');
308
+ }
309
+ } finally {
310
+ apiRef.current.setLoading(false);
311
+ }
312
+ } else {
313
+ commitStateUpdate(updatedSourceRow);
314
+ }
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Handles moving entire groups between different parents.
320
+ */
321
+ export class CrossParentGroupOperation extends BaseReorderOperation {
322
+ operationType = 'cross-parent-group';
323
+ detectOperation(ctx) {
324
+ const {
325
+ sourceRowId,
326
+ placeholderIndex,
327
+ sortedFilteredRowIds,
328
+ rowTree,
329
+ apiRef
330
+ } = ctx;
331
+ const sourceNode = gridRowNodeSelector(apiRef, sourceRowId);
332
+ if (!sourceNode || sourceNode.type === 'footer') {
333
+ return null;
334
+ }
335
+ let targetIndex = placeholderIndex;
336
+ let targetNode = gridRowNodeSelector(apiRef, sortedFilteredRowIds[placeholderIndex]);
337
+ let isLastChild = false;
338
+ if (!targetNode) {
339
+ if (placeholderIndex >= sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) {
340
+ targetNode = gridRowNodeSelector(apiRef, sortedFilteredRowIds[sortedFilteredRowIds.length - 1]);
341
+ targetIndex = sortedFilteredRowIds.length - 1;
342
+ isLastChild = true;
343
+ } else {
344
+ return null;
345
+ }
346
+ }
347
+ let adjustedTargetNode = targetNode;
348
+
349
+ // Case G adjustment
350
+ if (sourceNode.type === 'group' && targetNode.type === 'group' && sourceNode.parent !== targetNode.parent && sourceNode.depth > targetNode.depth) {
351
+ let prevIndex = targetIndex - 1;
352
+ if (prevIndex < 0) {
353
+ return null;
354
+ }
355
+ let prevNode = gridRowNodeSelector(apiRef, sortedFilteredRowIds[prevIndex]);
356
+ if (prevNode && prevNode.depth !== sourceNode.depth) {
357
+ while (prevNode.depth > sourceNode.depth && prevIndex >= 0) {
358
+ prevIndex -= 1;
359
+ prevNode = gridRowNodeSelector(apiRef, sortedFilteredRowIds[prevIndex]);
360
+ }
361
+ }
362
+ if (!prevNode || prevNode.type !== 'group' || prevNode.depth !== sourceNode.depth) {
363
+ return null;
364
+ }
365
+ isLastChild = true;
366
+ adjustedTargetNode = prevNode;
367
+ }
368
+ const operationType = determineOperationType(sourceNode, adjustedTargetNode);
369
+ if (operationType !== 'cross-parent-group') {
370
+ return null;
371
+ }
372
+ const actualTargetIndex = calculateTargetIndex(sourceNode, adjustedTargetNode, isLastChild, rowTree);
373
+ const operation = {
374
+ sourceNode,
375
+ targetNode: adjustedTargetNode,
376
+ actualTargetIndex,
377
+ isLastChild,
378
+ operationType
379
+ };
380
+ targetNode = adjustedTargetNode;
381
+ if (sourceNode.depth !== targetNode.depth) {
382
+ return null;
383
+ }
384
+ return operation;
385
+ }
386
+ async executeOperation(operation, ctx) {
387
+ const {
388
+ sourceNode,
389
+ targetNode,
390
+ isLastChild
391
+ } = operation;
392
+ const {
393
+ apiRef,
394
+ processRowUpdate,
395
+ onProcessRowUpdateError
396
+ } = ctx;
397
+ const tree = gridRowTreeSelector(apiRef);
398
+ const dataRowIdToModelLookup = gridRowsLookupSelector(apiRef);
399
+ const columnsLookup = gridColumnLookupSelector(apiRef);
400
+ const sanitizedRowGroupingModel = gridRowGroupingSanitizedModelSelector(apiRef);
401
+ const allLeafIds = collectAllLeafDescendants(sourceNode, tree);
402
+ if (allLeafIds.length === 0) {
403
+ return;
404
+ }
405
+ const updater = new BatchRowUpdater(processRowUpdate, onProcessRowUpdateError);
406
+ const groupingRules = getGroupingRules({
407
+ sanitizedRowGroupingModel,
408
+ columnsLookup
409
+ });
410
+ const targetParentPath = getNodePathInTree({
411
+ id: targetNode.parent,
412
+ tree
413
+ });
414
+ for (const leafId of allLeafIds) {
415
+ const originalRow = dataRowIdToModelLookup[leafId];
416
+ let updatedRow = _extends({}, originalRow);
417
+ for (let depth = 0; depth < targetParentPath.length; depth += 1) {
418
+ const pathItem = targetParentPath[depth];
419
+ if (pathItem.field) {
420
+ const groupingRule = groupingRules.find(rule => rule.field === pathItem.field);
421
+ if (groupingRule) {
422
+ const colDef = columnsLookup[groupingRule.field];
423
+ if (groupingRule.groupingValueSetter && colDef) {
424
+ updatedRow = groupingRule.groupingValueSetter(pathItem.key, updatedRow, colDef, apiRef);
425
+ } else {
426
+ updatedRow[groupingRule.field] = pathItem.key;
427
+ }
428
+ }
429
+ }
430
+ }
431
+ updater.queueUpdate(leafId, originalRow, updatedRow);
432
+ }
433
+ apiRef.current.setLoading(true);
434
+ try {
435
+ const {
436
+ successful,
437
+ failed,
438
+ updates
439
+ } = await updater.executeAll();
440
+ if (successful.length > 0) {
441
+ apiRef.current.setState(state => {
442
+ const updatedTree = _extends({}, state.rows.tree);
443
+ const treeDepths = _extends({}, state.rows.treeDepths);
444
+ let rootLevelRemovals = 0;
445
+ if (failed.length === 0) {
446
+ const sourceParentNode = updatedTree[sourceNode.parent];
447
+ if (!sourceParentNode) {
448
+ const targetParentNode = updatedTree[targetNode.parent];
449
+ const targetIndex = targetParentNode.children.indexOf(targetNode.id);
450
+ const newTargetChildren = [...targetParentNode.children];
451
+ if (isLastChild) {
452
+ newTargetChildren.push(sourceNode.id);
453
+ } else {
454
+ newTargetChildren.splice(targetIndex, 0, sourceNode.id);
455
+ }
456
+ updatedTree[targetNode.parent] = _extends({}, targetParentNode, {
457
+ children: newTargetChildren
458
+ });
459
+ updatedTree[sourceNode.id] = _extends({}, sourceNode, {
460
+ parent: targetNode.parent
461
+ });
462
+ } else {
463
+ const updatedSourceParentChildren = sourceParentNode.children.filter(id => id !== sourceNode.id);
464
+ if (updatedSourceParentChildren.length === 0) {
465
+ const removedGroups = new Set();
466
+ removedGroups.add(sourceNode.parent);
467
+ const parentOfSourceParent = updatedTree[sourceNode.parent].parent;
468
+ if (parentOfSourceParent) {
469
+ rootLevelRemovals = removeEmptyAncestors(parentOfSourceParent, updatedTree, removedGroups);
470
+ }
471
+ removedGroups.forEach(groupId => {
472
+ const group = updatedTree[groupId];
473
+ if (group && group.parent && updatedTree[group.parent]) {
474
+ const parent = updatedTree[group.parent];
475
+ updatedTree[group.parent] = _extends({}, parent, {
476
+ children: parent.children.filter(childId => childId !== groupId)
477
+ });
478
+ }
479
+ delete updatedTree[groupId];
480
+ });
481
+ } else {
482
+ updatedTree[sourceNode.parent] = _extends({}, sourceParentNode, {
483
+ children: updatedSourceParentChildren
484
+ });
485
+ }
486
+ const targetParentNode = updatedTree[targetNode.parent];
487
+ const sourceGroupNode = sourceNode;
488
+ const existingGroup = sourceGroupNode.groupingKey !== null && sourceGroupNode.groupingField !== null ? findExistingGroupWithSameKey(targetParentNode, sourceGroupNode.groupingKey, sourceGroupNode.groupingField, updatedTree) : null;
489
+ if (existingGroup) {
490
+ const updatedExistingGroup = _extends({}, existingGroup, {
491
+ children: [...existingGroup.children, ...sourceGroupNode.children]
492
+ });
493
+ updatedTree[existingGroup.id] = updatedExistingGroup;
494
+ sourceGroupNode.children.forEach(childId => {
495
+ const childNode = updatedTree[childId];
496
+ if (childNode) {
497
+ updatedTree[childId] = _extends({}, childNode, {
498
+ parent: existingGroup.id
499
+ });
500
+ }
501
+ });
502
+ delete updatedTree[sourceNode.id];
503
+ } else {
504
+ const targetIndex = targetParentNode.children.indexOf(targetNode.id);
505
+ const newTargetChildren = [...targetParentNode.children];
506
+ if (isLastChild) {
507
+ newTargetChildren.push(sourceNode.id);
508
+ } else {
509
+ newTargetChildren.splice(targetIndex, 0, sourceNode.id);
510
+ }
511
+ updatedTree[targetNode.parent] = _extends({}, targetParentNode, {
512
+ children: newTargetChildren
513
+ });
514
+ updatedTree[sourceNode.id] = _extends({}, sourceNode, {
515
+ parent: targetNode.parent
516
+ });
517
+ }
518
+ }
519
+ }
520
+ return _extends({}, state, {
521
+ rows: _extends({}, state.rows, {
522
+ totalTopLevelRowCount: state.rows.totalTopLevelRowCount - rootLevelRemovals,
523
+ tree: updatedTree,
524
+ treeDepths
525
+ })
526
+ });
527
+ });
528
+ apiRef.current.updateRows(updates);
529
+ apiRef.current.publishEvent('rowsSet');
530
+ }
531
+ } finally {
532
+ apiRef.current.setLoading(false);
533
+ }
534
+ }
535
+ }
@@ -0,0 +1,15 @@
1
+ import { BaseReorderOperation } from "./operations.js";
2
+ import type { ReorderExecutionContext } from "./types.js";
3
+ /**
4
+ * Executor class for handling row reorder operations in grouped data grids.
5
+ *
6
+ * This class coordinates the execution of different reorder operation types,
7
+ * trying each operation in order until one succeeds or all fail.
8
+ */
9
+ declare class RowReorderExecutor {
10
+ private operations;
11
+ constructor(operations: BaseReorderOperation[]);
12
+ execute(ctx: ReorderExecutionContext): Promise<void>;
13
+ }
14
+ export declare const rowGroupingReorderExecutor: RowReorderExecutor;
15
+ export {};
@@ -0,0 +1,25 @@
1
+ import { warnOnce } from '@mui/x-internals/warning';
2
+ import { SameParentSwapOperation, CrossParentLeafOperation, CrossParentGroupOperation } from "./operations.js";
3
+ /**
4
+ * Executor class for handling row reorder operations in grouped data grids.
5
+ *
6
+ * This class coordinates the execution of different reorder operation types,
7
+ * trying each operation in order until one succeeds or all fail.
8
+ */
9
+ class RowReorderExecutor {
10
+ constructor(operations) {
11
+ this.operations = operations;
12
+ }
13
+ async execute(ctx) {
14
+ for (const operation of this.operations) {
15
+ const detectedOperation = operation.detectOperation(ctx);
16
+ if (detectedOperation) {
17
+ // eslint-disable-next-line no-await-in-loop
18
+ await operation.executeOperation(detectedOperation, ctx);
19
+ return;
20
+ }
21
+ }
22
+ warnOnce(['MUI X: The parameters provided to the `setRowIndex()` resulted in a no-op.', 'Consider looking at the documentation at https://mui.com/x/react-data-grid/row-grouping/'], 'warning');
23
+ }
24
+ }
25
+ export const rowGroupingReorderExecutor = new RowReorderExecutor([new SameParentSwapOperation(), new CrossParentLeafOperation(), new CrossParentGroupOperation()]);