@mui/x-data-grid-premium 8.10.1 → 8.11.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.
- package/CHANGELOG.md +221 -8
- package/DataGridPremium/DataGridPremium.js +6 -4
- package/DataGridPremium/useDataGridPremiumComponent.d.ts +2 -1
- package/DataGridPremium/useDataGridPremiumComponent.js +2 -2
- package/esm/DataGridPremium/DataGridPremium.js +6 -4
- package/esm/DataGridPremium/useDataGridPremiumComponent.d.ts +2 -1
- package/esm/DataGridPremium/useDataGridPremiumComponent.js +2 -2
- package/esm/hooks/features/aggregation/wrapColumnWithAggregation.d.ts +1 -0
- package/esm/hooks/features/clipboard/useGridClipboardImport.js +1 -1
- package/esm/hooks/features/export/serializer/setupExcelExportWebWorker.js +1 -2
- package/esm/hooks/features/rowGrouping/createGroupingColDef.d.ts +2 -1
- package/esm/hooks/features/rowGrouping/createGroupingColDef.js +31 -7
- package/esm/hooks/features/rowGrouping/gridRowGroupingInterfaces.d.ts +1 -0
- package/esm/hooks/features/rowGrouping/gridRowGroupingUtils.js +5 -1
- package/esm/hooks/features/rowGrouping/useGridRowGrouping.d.ts +1 -1
- package/esm/hooks/features/rowGrouping/useGridRowGrouping.js +48 -2
- package/esm/hooks/features/rowReorder/operations.d.ts +40 -0
- package/esm/hooks/features/rowReorder/operations.js +535 -0
- package/esm/hooks/features/rowReorder/reorderExecutor.d.ts +15 -0
- package/esm/hooks/features/rowReorder/reorderExecutor.js +25 -0
- package/esm/hooks/features/rowReorder/reorderValidator.d.ts +16 -0
- package/esm/hooks/features/rowReorder/reorderValidator.js +116 -0
- package/esm/hooks/features/rowReorder/types.d.ts +42 -0
- package/esm/hooks/features/rowReorder/types.js +1 -0
- package/esm/hooks/features/rowReorder/utils.d.ts +127 -0
- package/esm/hooks/features/rowReorder/utils.js +343 -0
- package/esm/hooks/features/rows/useGridRowsOverridableMethods.d.ts +7 -0
- package/esm/hooks/features/rows/useGridRowsOverridableMethods.js +52 -0
- package/esm/index.js +1 -1
- package/esm/models/gridGroupingValueSetter.d.ts +14 -0
- package/esm/models/gridGroupingValueSetter.js +1 -0
- package/esm/models/index.d.ts +1 -0
- package/esm/models/index.js +1 -0
- package/esm/typeOverloads/modules.d.ts +7 -1
- package/hooks/features/aggregation/wrapColumnWithAggregation.d.ts +1 -0
- package/hooks/features/clipboard/useGridClipboardImport.js +1 -1
- package/hooks/features/export/serializer/setupExcelExportWebWorker.js +1 -2
- package/hooks/features/rowGrouping/createGroupingColDef.d.ts +2 -1
- package/hooks/features/rowGrouping/createGroupingColDef.js +31 -7
- package/hooks/features/rowGrouping/gridRowGroupingInterfaces.d.ts +1 -0
- package/hooks/features/rowGrouping/gridRowGroupingUtils.js +5 -1
- package/hooks/features/rowGrouping/useGridRowGrouping.d.ts +1 -1
- package/hooks/features/rowGrouping/useGridRowGrouping.js +46 -0
- package/hooks/features/rowReorder/operations.d.ts +40 -0
- package/hooks/features/rowReorder/operations.js +546 -0
- package/hooks/features/rowReorder/reorderExecutor.d.ts +15 -0
- package/hooks/features/rowReorder/reorderExecutor.js +31 -0
- package/hooks/features/rowReorder/reorderValidator.d.ts +16 -0
- package/hooks/features/rowReorder/reorderValidator.js +122 -0
- package/hooks/features/rowReorder/types.d.ts +42 -0
- package/hooks/features/rowReorder/types.js +5 -0
- package/hooks/features/rowReorder/utils.d.ts +127 -0
- package/hooks/features/rowReorder/utils.js +360 -0
- package/hooks/features/rows/useGridRowsOverridableMethods.d.ts +7 -0
- package/hooks/features/rows/useGridRowsOverridableMethods.js +60 -0
- package/index.js +1 -1
- package/models/gridGroupingValueSetter.d.ts +14 -0
- package/models/gridGroupingValueSetter.js +5 -0
- package/models/index.d.ts +1 -0
- package/models/index.js +11 -0
- package/package.json +5 -5
- package/typeOverloads/modules.d.ts +7 -1
|
@@ -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()]);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ReorderValidationContext } from "./types.js";
|
|
2
|
+
interface ValidationRule {
|
|
3
|
+
name: string;
|
|
4
|
+
applies: (ctx: ReorderValidationContext) => boolean;
|
|
5
|
+
isInvalid: (ctx: ReorderValidationContext) => boolean;
|
|
6
|
+
message?: string;
|
|
7
|
+
}
|
|
8
|
+
declare class RowReorderValidator {
|
|
9
|
+
private rules;
|
|
10
|
+
constructor(rules?: ValidationRule[]);
|
|
11
|
+
addRule(rule: ValidationRule): void;
|
|
12
|
+
removeRule(ruleName: string): void;
|
|
13
|
+
validate(context: ReorderValidationContext): boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare const rowGroupingReorderValidator: RowReorderValidator;
|
|
16
|
+
export {};
|