@mui/x-data-grid-pro 8.19.0 → 8.21.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 +174 -0
- package/DataGridPro/DataGridPro.js +29 -2
- package/components/GridRowReorderCell.js +15 -3
- package/components/headerFiltering/GridHeaderFilterCell.js +2 -3
- package/esm/DataGridPro/DataGridPro.js +29 -2
- package/esm/components/GridRowReorderCell.js +15 -3
- package/esm/components/headerFiltering/GridHeaderFilterCell.js +2 -3
- package/esm/hooks/features/dataSource/useGridDataSourceBasePro.js +1 -1
- package/esm/hooks/features/rowReorder/commonReorderConditions.d.ts +30 -0
- package/esm/hooks/features/rowReorder/commonReorderConditions.js +78 -0
- package/esm/hooks/features/rowReorder/index.d.ts +2 -1
- package/esm/hooks/features/rowReorder/index.js +2 -1
- package/esm/hooks/features/rowReorder/models.d.ts +17 -0
- package/esm/hooks/features/rowReorder/models.js +1 -0
- package/esm/hooks/features/rowReorder/reorderExecutor.d.ts +27 -0
- package/esm/hooks/features/rowReorder/reorderExecutor.js +29 -0
- package/esm/hooks/features/rowReorder/reorderValidator.d.ts +12 -0
- package/esm/hooks/features/rowReorder/reorderValidator.js +14 -0
- package/esm/hooks/features/rowReorder/types.d.ts +25 -0
- package/esm/hooks/features/rowReorder/types.js +1 -0
- package/esm/hooks/features/rowReorder/useGridRowReorder.d.ts +1 -1
- package/esm/hooks/features/rowReorder/useGridRowReorder.js +167 -80
- package/esm/hooks/features/rowReorder/utils.d.ts +82 -0
- package/esm/hooks/features/rowReorder/utils.js +259 -0
- package/esm/hooks/features/rows/useGridRowsOverridableMethods.d.ts +7 -0
- package/esm/hooks/features/rows/useGridRowsOverridableMethods.js +59 -0
- package/esm/hooks/features/treeData/treeDataReorderExecutor.d.ts +11 -0
- package/esm/hooks/features/treeData/treeDataReorderExecutor.js +534 -0
- package/esm/hooks/features/treeData/treeDataReorderValidator.d.ts +2 -0
- package/esm/hooks/features/treeData/treeDataReorderValidator.js +35 -0
- package/esm/hooks/features/treeData/useGridTreeData.d.ts +3 -3
- package/esm/hooks/features/treeData/useGridTreeData.js +49 -4
- package/esm/hooks/features/treeData/utils.d.ts +8 -0
- package/esm/hooks/features/treeData/utils.js +96 -0
- package/esm/index.js +1 -1
- package/esm/internals/index.d.ts +8 -0
- package/esm/internals/index.js +6 -0
- package/esm/models/dataGridProProps.d.ts +32 -4
- package/esm/models/gridRowOrderChangeParams.d.ts +29 -5
- package/hooks/features/dataSource/useGridDataSourceBasePro.js +1 -1
- package/hooks/features/rowReorder/commonReorderConditions.d.ts +30 -0
- package/hooks/features/rowReorder/commonReorderConditions.js +84 -0
- package/hooks/features/rowReorder/index.d.ts +2 -1
- package/hooks/features/rowReorder/models.d.ts +17 -0
- package/hooks/features/rowReorder/models.js +5 -0
- package/hooks/features/rowReorder/reorderExecutor.d.ts +27 -0
- package/hooks/features/rowReorder/reorderExecutor.js +37 -0
- package/hooks/features/rowReorder/reorderValidator.d.ts +12 -0
- package/hooks/features/rowReorder/reorderValidator.js +21 -0
- package/hooks/features/rowReorder/types.d.ts +25 -0
- package/hooks/features/rowReorder/types.js +5 -0
- package/hooks/features/rowReorder/useGridRowReorder.d.ts +1 -1
- package/hooks/features/rowReorder/useGridRowReorder.js +168 -81
- package/hooks/features/rowReorder/utils.d.ts +82 -0
- package/hooks/features/rowReorder/utils.js +286 -0
- package/hooks/features/rows/useGridRowsOverridableMethods.d.ts +7 -0
- package/hooks/features/rows/useGridRowsOverridableMethods.js +67 -0
- package/hooks/features/treeData/treeDataReorderExecutor.d.ts +11 -0
- package/hooks/features/treeData/treeDataReorderExecutor.js +541 -0
- package/hooks/features/treeData/treeDataReorderValidator.d.ts +2 -0
- package/hooks/features/treeData/treeDataReorderValidator.js +41 -0
- package/hooks/features/treeData/useGridTreeData.d.ts +3 -3
- package/hooks/features/treeData/useGridTreeData.js +48 -3
- package/hooks/features/treeData/utils.d.ts +8 -0
- package/hooks/features/treeData/utils.js +109 -0
- package/index.js +1 -1
- package/internals/index.d.ts +8 -0
- package/internals/index.js +53 -1
- package/models/dataGridProProps.d.ts +32 -4
- package/models/gridRowOrderChangeParams.d.ts +29 -5
- package/package.json +4 -4
|
@@ -14,19 +14,22 @@ var _composeClasses = _interopRequireDefault(require("@mui/utils/composeClasses"
|
|
|
14
14
|
var _xDataGrid = require("@mui/x-data-grid");
|
|
15
15
|
var _internals = require("@mui/x-data-grid/internals");
|
|
16
16
|
var _gridRowReorderColDef = require("./gridRowReorderColDef");
|
|
17
|
+
var _utils = require("./utils");
|
|
17
18
|
const EMPTY_REORDER_STATE = {
|
|
18
19
|
previousTargetId: null,
|
|
19
20
|
dragDirection: null,
|
|
20
21
|
previousDropPosition: null
|
|
21
22
|
};
|
|
23
|
+
const TIMEOUT_CLEAR_BUFFER_PX = 5;
|
|
24
|
+
const EMPTY_TIMEOUT_INFO = {
|
|
25
|
+
rowId: null
|
|
26
|
+
};
|
|
22
27
|
const useUtilityClasses = ownerState => {
|
|
23
28
|
const {
|
|
24
29
|
classes
|
|
25
30
|
} = ownerState;
|
|
26
31
|
const slots = {
|
|
27
32
|
rowDragging: ['row--dragging'],
|
|
28
|
-
rowDropAbove: ['row--dropAbove'],
|
|
29
|
-
rowDropBelow: ['row--dropBelow'],
|
|
30
33
|
rowBeingDragged: ['row--beingDragged']
|
|
31
34
|
};
|
|
32
35
|
return (0, _composeClasses.default)(slots, _xDataGrid.getDataGridUtilityClass, classes);
|
|
@@ -48,13 +51,12 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
48
51
|
const dragRowNode = React.useRef(null);
|
|
49
52
|
const originRowIndex = React.useRef(null);
|
|
50
53
|
const removeDnDStylesTimeout = React.useRef(undefined);
|
|
51
|
-
const previousDropIndicatorRef = React.useRef(null);
|
|
52
54
|
const ownerState = {
|
|
53
55
|
classes: props.classes
|
|
54
56
|
};
|
|
55
57
|
const classes = useUtilityClasses(ownerState);
|
|
56
58
|
const [dragRowId, setDragRowId] = React.useState('');
|
|
57
|
-
const
|
|
59
|
+
const timeoutInfoRef = React.useRef(EMPTY_TIMEOUT_INFO);
|
|
58
60
|
const timeout = (0, _useTimeout.default)();
|
|
59
61
|
const previousReorderState = React.useRef(EMPTY_REORDER_STATE);
|
|
60
62
|
const dropTarget = React.useRef({
|
|
@@ -69,26 +71,30 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
69
71
|
}, []);
|
|
70
72
|
|
|
71
73
|
// TODO: remove sortModel check once row reorder is sorting compatible
|
|
72
|
-
// remove treeData check once row reorder is treeData compatible
|
|
73
74
|
const isRowReorderDisabled = React.useMemo(() => {
|
|
74
|
-
return !props.rowReordering || !!sortModel.length
|
|
75
|
-
}, [props.rowReordering, sortModel
|
|
76
|
-
const
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
targetRow.classList.add(position === 'above' ? classes.rowDropAbove : classes.rowDropBelow);
|
|
88
|
-
previousDropIndicatorRef.current = targetRow;
|
|
75
|
+
return !props.rowReordering || !!sortModel.length;
|
|
76
|
+
}, [props.rowReordering, sortModel]);
|
|
77
|
+
const calculateDropPosition = React.useCallback(event => {
|
|
78
|
+
// For tree data, we need to find the cell element to avoid flickerings on top 20% selection
|
|
79
|
+
const targetElement = props.treeData ? (0, _utils.findCellElement)(event.target) : event.target;
|
|
80
|
+
const targetRect = targetElement.getBoundingClientRect();
|
|
81
|
+
const relativeY = Math.floor(event.clientY - targetRect.top);
|
|
82
|
+
if (props.treeData) {
|
|
83
|
+
// For tree data: top 20% = above, middle 60% = over, bottom 20% = below
|
|
84
|
+
const topThreshold = targetRect.height * 0.2;
|
|
85
|
+
const bottomThreshold = targetRect.height * 0.8;
|
|
86
|
+
if (relativeY < topThreshold) {
|
|
87
|
+
return 'above';
|
|
89
88
|
}
|
|
89
|
+
if (relativeY > bottomThreshold) {
|
|
90
|
+
return 'below';
|
|
91
|
+
}
|
|
92
|
+
return 'inside';
|
|
90
93
|
}
|
|
91
|
-
|
|
94
|
+
// For flat data and row grouping: split at midpoint
|
|
95
|
+
const midPoint = targetRect.height / 2;
|
|
96
|
+
return relativeY < midPoint ? 'above' : 'below';
|
|
97
|
+
}, [props.treeData]);
|
|
92
98
|
const applyDraggedState = React.useCallback((rowId, isDragged) => {
|
|
93
99
|
if (rowId) {
|
|
94
100
|
const draggedRow = apiRef.current.rootElementRef?.current?.querySelector(`[data-id="${rowId}"]`);
|
|
@@ -101,7 +107,7 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
101
107
|
}
|
|
102
108
|
}
|
|
103
109
|
}, [apiRef, classes.rowBeingDragged]);
|
|
104
|
-
const applyRowAnimation = React.useCallback(callback => {
|
|
110
|
+
const applyRowAnimation = React.useCallback(async callback => {
|
|
105
111
|
const rootElement = apiRef.current.rootElementRef?.current;
|
|
106
112
|
if (!rootElement) {
|
|
107
113
|
return;
|
|
@@ -118,7 +124,7 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
118
124
|
initialPositions.set(rowId, row.getBoundingClientRect());
|
|
119
125
|
}
|
|
120
126
|
});
|
|
121
|
-
callback();
|
|
127
|
+
await callback();
|
|
122
128
|
|
|
123
129
|
// Use `requestAnimationFrame` to ensure DOM has updated
|
|
124
130
|
requestAnimationFrame(() => {
|
|
@@ -160,15 +166,20 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
160
166
|
if (isRowReorderDisabled || Object.keys(editRowsState).length !== 0) {
|
|
161
167
|
return;
|
|
162
168
|
}
|
|
163
|
-
if (
|
|
169
|
+
if (timeoutInfoRef.current) {
|
|
164
170
|
timeout.clear();
|
|
165
|
-
|
|
171
|
+
timeoutInfoRef.current = EMPTY_TIMEOUT_INFO;
|
|
166
172
|
}
|
|
167
173
|
logger.debug(`Start dragging row ${params.id}`);
|
|
168
174
|
// Prevent drag events propagation.
|
|
169
175
|
// For more information check here https://github.com/mui/mui-x/issues/2680.
|
|
170
176
|
event.stopPropagation();
|
|
171
|
-
apiRef.current.
|
|
177
|
+
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
|
|
178
|
+
rowReorder: (0, _extends2.default)({}, state.rowReorder, {
|
|
179
|
+
isActive: true,
|
|
180
|
+
draggedRowId: params.id
|
|
181
|
+
})
|
|
182
|
+
}));
|
|
172
183
|
dragRowNode.current = event.currentTarget;
|
|
173
184
|
// Apply cell-level dragging class to the drag handle
|
|
174
185
|
dragRowNode.current.classList.add(classes.rowDragging);
|
|
@@ -179,7 +190,7 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
179
190
|
removeDnDStylesTimeout.current = setTimeout(() => {
|
|
180
191
|
dragRowNode.current.classList.remove(classes.rowDragging);
|
|
181
192
|
});
|
|
182
|
-
const sortedRowIndexLookup = (0,
|
|
193
|
+
const sortedRowIndexLookup = (0, _xDataGrid.gridExpandedSortedRowIndexLookupSelector)(apiRef);
|
|
183
194
|
originRowIndex.current = sortedRowIndexLookup[params.id];
|
|
184
195
|
apiRef.current.setCellFocus(params.id, _gridRowReorderColDef.GRID_REORDER_COL_DEF.field);
|
|
185
196
|
}, [apiRef, isRowReorderDisabled, logger, classes.rowDragging, applyDraggedState, timeout]);
|
|
@@ -192,35 +203,36 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
192
203
|
if (!sourceNode || !targetNode || targetNode.type === 'footer' || targetNode.type === 'pinnedRow' || !event.target) {
|
|
193
204
|
return;
|
|
194
205
|
}
|
|
195
|
-
|
|
196
|
-
// Find the relative 'y' mouse position based on the event.target
|
|
197
|
-
const targetRect = event.target.getBoundingClientRect();
|
|
198
|
-
const relativeY = Math.floor(event.clientY - targetRect.top);
|
|
199
|
-
const midPoint = Math.floor(targetRect.height / 2);
|
|
200
206
|
logger.debug(`Dragging over row ${params.id}`);
|
|
201
207
|
event.preventDefault();
|
|
202
208
|
// Prevent drag events propagation.
|
|
203
209
|
// For more information check here https://github.com/mui/mui-x/issues/2680.
|
|
204
210
|
event.stopPropagation();
|
|
205
|
-
if (
|
|
211
|
+
if (timeoutInfoRef.current && (timeoutInfoRef.current.rowId !== params.id ||
|
|
212
|
+
// Avoid accidental opening of node when the user is moving over a row
|
|
213
|
+
event.clientY > timeoutInfoRef.current.clientY + TIMEOUT_CLEAR_BUFFER_PX || event.clientY < timeoutInfoRef.current.clientY - TIMEOUT_CLEAR_BUFFER_PX || event.clientX > timeoutInfoRef.current.clientX + TIMEOUT_CLEAR_BUFFER_PX || event.clientX < timeoutInfoRef.current.clientX - TIMEOUT_CLEAR_BUFFER_PX)) {
|
|
206
214
|
timeout.clear();
|
|
207
|
-
|
|
215
|
+
timeoutInfoRef.current = EMPTY_TIMEOUT_INFO;
|
|
208
216
|
}
|
|
209
|
-
|
|
217
|
+
|
|
218
|
+
// Calculate drop position using new logic
|
|
219
|
+
const dropPosition = calculateDropPosition(event);
|
|
220
|
+
if (targetNode.type === 'group' && !targetNode.childrenExpanded && !timeoutInfoRef.current.rowId && targetNode.id !== sourceNode.id && (dropPosition === 'inside' || targetNode.depth < sourceNode.depth)) {
|
|
210
221
|
timeout.start(500, () => {
|
|
211
222
|
const rowNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, params.id);
|
|
212
223
|
// TODO: Handle `dataSource` case with https://github.com/mui/mui-x/issues/18947
|
|
213
224
|
apiRef.current.setRowChildrenExpansion(params.id, !rowNode.childrenExpanded);
|
|
214
225
|
});
|
|
215
|
-
|
|
226
|
+
timeoutInfoRef.current = {
|
|
227
|
+
rowId: params.id,
|
|
228
|
+
clientY: event.clientY,
|
|
229
|
+
clientX: event.clientX
|
|
230
|
+
};
|
|
216
231
|
return;
|
|
217
232
|
}
|
|
218
|
-
const sortedRowIndexLookup = (0,
|
|
233
|
+
const sortedRowIndexLookup = (0, _xDataGrid.gridExpandedSortedRowIndexLookupSelector)(apiRef);
|
|
219
234
|
const targetRowIndex = sortedRowIndexLookup[params.id];
|
|
220
235
|
const sourceRowIndex = sortedRowIndexLookup[dragRowId];
|
|
221
|
-
|
|
222
|
-
// Determine drop position based on relativeY position within the row
|
|
223
|
-
const dropPosition = relativeY < midPoint ? 'above' : 'below';
|
|
224
236
|
const currentReorderState = {
|
|
225
237
|
dragDirection: targetRowIndex < sourceRowIndex ? 'up' : 'down',
|
|
226
238
|
previousTargetId: params.id,
|
|
@@ -233,7 +245,7 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
233
245
|
|
|
234
246
|
// Check if this is an adjacent position
|
|
235
247
|
const isAdjacentPosition = dropPosition === 'above' && targetRowIndex === sourceRowIndex + 1 || dropPosition === 'below' && targetRowIndex === sourceRowIndex - 1;
|
|
236
|
-
const
|
|
248
|
+
const isRowReorderValid = apiRef.current.unstable_applyPipeProcessors('isRowReorderValid', false, {
|
|
237
249
|
sourceRowId: dragRowId,
|
|
238
250
|
targetRowId: params.id,
|
|
239
251
|
dropPosition,
|
|
@@ -241,13 +253,21 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
241
253
|
});
|
|
242
254
|
|
|
243
255
|
// Show drop indicator for valid drops OR adjacent positions OR same node
|
|
244
|
-
if (
|
|
256
|
+
if (isRowReorderValid || isAdjacentPosition || isSameNode) {
|
|
245
257
|
dropTarget.current = {
|
|
246
258
|
targetRowId: params.id,
|
|
247
259
|
targetRowIndex,
|
|
248
260
|
dropPosition
|
|
249
261
|
};
|
|
250
|
-
|
|
262
|
+
// Update state with drop target
|
|
263
|
+
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
|
|
264
|
+
rowReorder: (0, _extends2.default)({}, state.rowReorder, {
|
|
265
|
+
dropTarget: {
|
|
266
|
+
rowId: params.id,
|
|
267
|
+
position: dropPosition
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
}));
|
|
251
271
|
} else {
|
|
252
272
|
// Clear indicators for invalid drops
|
|
253
273
|
dropTarget.current = {
|
|
@@ -255,7 +275,12 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
255
275
|
targetRowIndex: null,
|
|
256
276
|
dropPosition: null
|
|
257
277
|
};
|
|
258
|
-
|
|
278
|
+
// Clear state drop target
|
|
279
|
+
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
|
|
280
|
+
rowReorder: (0, _extends2.default)({}, state.rowReorder, {
|
|
281
|
+
dropTarget: undefined
|
|
282
|
+
})
|
|
283
|
+
}));
|
|
259
284
|
}
|
|
260
285
|
previousReorderState.current = currentReorderState;
|
|
261
286
|
}
|
|
@@ -266,16 +291,16 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
266
291
|
} else {
|
|
267
292
|
event.dataTransfer.dropEffect = 'copy';
|
|
268
293
|
}
|
|
269
|
-
}, [dragRowId, apiRef, logger, timeout,
|
|
270
|
-
const handleDragEnd = React.useCallback((_, event) => {
|
|
294
|
+
}, [dragRowId, apiRef, logger, timeout, calculateDropPosition]);
|
|
295
|
+
const handleDragEnd = React.useCallback(async (_, event) => {
|
|
271
296
|
// Call the gridEditRowsStateSelector directly to avoid infnite loop
|
|
272
297
|
const editRowsState = (0, _internals.gridEditRowsStateSelector)(apiRef);
|
|
273
298
|
if (dragRowId === '' || isRowReorderDisabled || Object.keys(editRowsState).length !== 0) {
|
|
274
299
|
return;
|
|
275
300
|
}
|
|
276
|
-
if (
|
|
301
|
+
if (timeoutInfoRef.current) {
|
|
277
302
|
timeout.clear();
|
|
278
|
-
|
|
303
|
+
timeoutInfoRef.current = EMPTY_TIMEOUT_INFO;
|
|
279
304
|
}
|
|
280
305
|
logger.debug('End dragging row');
|
|
281
306
|
event.preventDefault();
|
|
@@ -287,11 +312,6 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
287
312
|
const dragDirection = previousReorderState.current.dragDirection;
|
|
288
313
|
previousReorderState.current = EMPTY_REORDER_STATE;
|
|
289
314
|
|
|
290
|
-
// Clear visual indicators and dragged state
|
|
291
|
-
applyDropIndicator(null, null);
|
|
292
|
-
applyDraggedState(dragRowId, false);
|
|
293
|
-
apiRef.current.setRowDragActive(false);
|
|
294
|
-
|
|
295
315
|
// Check if the row was dropped outside the grid.
|
|
296
316
|
if (!event.dataTransfer || event.dataTransfer.dropEffect === 'none') {
|
|
297
317
|
// Reset drop target state
|
|
@@ -302,29 +322,80 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
302
322
|
};
|
|
303
323
|
originRowIndex.current = null;
|
|
304
324
|
setDragRowId('');
|
|
325
|
+
// Clear visual indicators and dragged state
|
|
326
|
+
applyDraggedState(dragRowId, false);
|
|
327
|
+
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
|
|
328
|
+
rowReorder: {
|
|
329
|
+
isActive: false,
|
|
330
|
+
draggedRowId: null
|
|
331
|
+
}
|
|
332
|
+
}));
|
|
305
333
|
return;
|
|
306
334
|
}
|
|
307
335
|
if (dropTarget.current.targetRowIndex !== null && dropTarget.current.targetRowId !== null) {
|
|
308
|
-
const
|
|
309
|
-
const targetRowIndex = dropTarget.current.targetRowIndex;
|
|
310
|
-
const validatedIndex = apiRef.current.unstable_applyPipeProcessors('getRowReorderTargetIndex', targetRowIndex, {
|
|
336
|
+
const isRowReorderValid = apiRef.current.unstable_applyPipeProcessors('isRowReorderValid', false, {
|
|
311
337
|
sourceRowId: dragRowId,
|
|
312
338
|
targetRowId: dropTarget.current.targetRowId,
|
|
313
339
|
dropPosition: dropTarget.current.dropPosition,
|
|
314
340
|
dragDirection: dragDirection
|
|
315
341
|
});
|
|
316
|
-
if (
|
|
317
|
-
|
|
318
|
-
|
|
342
|
+
if (isRowReorderValid) {
|
|
343
|
+
try {
|
|
344
|
+
const rowTree = (0, _xDataGrid.gridRowTreeSelector)(apiRef);
|
|
345
|
+
const sourceNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, dragRowId);
|
|
346
|
+
if (!sourceNode) {
|
|
347
|
+
throw new Error(`MUI X: No row node found for id #${dragRowId}`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Calculate oldParent and oldIndex
|
|
351
|
+
const oldParent = sourceNode.parent;
|
|
352
|
+
const oldParentNode = rowTree[oldParent];
|
|
353
|
+
const oldIndexInParent = oldParentNode.children.indexOf(dragRowId) ?? originRowIndex.current;
|
|
354
|
+
await applyRowAnimation(async () => {
|
|
355
|
+
await apiRef.current.setRowPosition(dragRowId, dropTarget.current.targetRowId, dropTarget.current.dropPosition);
|
|
356
|
+
const updatedTree = (0, _xDataGrid.gridRowTreeSelector)(apiRef);
|
|
357
|
+
const updatedNode = updatedTree[dragRowId];
|
|
358
|
+
if (!updatedNode) {
|
|
359
|
+
throw new Error(`MUI X: Row node for id #${dragRowId} not found after move`);
|
|
360
|
+
}
|
|
361
|
+
const newParent = updatedNode.parent;
|
|
362
|
+
const newParentNode = updatedTree[newParent];
|
|
363
|
+
const newIndexInParent = newParentNode.children.indexOf(dragRowId);
|
|
319
364
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
365
|
+
// Only emit event and clear state after successful reorder
|
|
366
|
+
const rowOrderChangeParams = {
|
|
367
|
+
row: apiRef.current.getRow(dragRowId),
|
|
368
|
+
oldIndex: oldIndexInParent,
|
|
369
|
+
targetIndex: newIndexInParent,
|
|
370
|
+
oldParent: oldParent === _xDataGrid.GRID_ROOT_GROUP_ID ? null : oldParent,
|
|
371
|
+
newParent: newParent === _xDataGrid.GRID_ROOT_GROUP_ID ? null : newParent
|
|
372
|
+
};
|
|
373
|
+
applyDraggedState(dragRowId, false);
|
|
374
|
+
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
|
|
375
|
+
rowReorder: {
|
|
376
|
+
isActive: false,
|
|
377
|
+
draggedRowId: null
|
|
378
|
+
}
|
|
379
|
+
}));
|
|
380
|
+
apiRef.current.publishEvent('rowOrderChange', rowOrderChangeParams);
|
|
381
|
+
});
|
|
382
|
+
} catch (error) {
|
|
383
|
+
// Handle error: reset visual state but don't publish success event
|
|
384
|
+
applyDraggedState(dragRowId, false);
|
|
385
|
+
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
|
|
386
|
+
rowReorder: {
|
|
387
|
+
isActive: false,
|
|
388
|
+
draggedRowId: null
|
|
389
|
+
}
|
|
390
|
+
}));
|
|
391
|
+
}
|
|
392
|
+
} else {
|
|
393
|
+
applyDraggedState(dragRowId, false);
|
|
394
|
+
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
|
|
395
|
+
rowReorder: (0, _extends2.default)({}, state.rowReorder, {
|
|
396
|
+
dropTarget: undefined
|
|
397
|
+
})
|
|
398
|
+
}));
|
|
328
399
|
}
|
|
329
400
|
}
|
|
330
401
|
|
|
@@ -335,8 +406,9 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
335
406
|
dropPosition: null
|
|
336
407
|
};
|
|
337
408
|
setDragRowId('');
|
|
338
|
-
}, [apiRef, dragRowId, isRowReorderDisabled, logger,
|
|
339
|
-
const
|
|
409
|
+
}, [apiRef, dragRowId, isRowReorderDisabled, logger, applyDraggedState, timeout, applyRowAnimation]);
|
|
410
|
+
const isValidRowReorderProp = props.isValidRowReorder;
|
|
411
|
+
const isRowReorderValid = React.useCallback((initialValue, {
|
|
340
412
|
sourceRowId,
|
|
341
413
|
targetRowId,
|
|
342
414
|
dropPosition,
|
|
@@ -345,28 +417,43 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
345
417
|
if ((0, _xDataGrid.gridRowMaximumTreeDepthSelector)(apiRef) > 1) {
|
|
346
418
|
return initialValue;
|
|
347
419
|
}
|
|
348
|
-
const sortedRowIndexLookup = (0,
|
|
420
|
+
const sortedRowIndexLookup = (0, _xDataGrid.gridExpandedSortedRowIndexLookupSelector)(apiRef);
|
|
349
421
|
const targetRowIndex = sortedRowIndexLookup[targetRowId];
|
|
350
422
|
const sourceRowIndex = sortedRowIndexLookup[sourceRowId];
|
|
351
423
|
|
|
352
|
-
//
|
|
424
|
+
// Apply internal validation: check if this drop would result in no actual movement
|
|
353
425
|
const isAdjacentNode = dropPosition === 'above' && targetRowIndex === sourceRowIndex + 1 ||
|
|
354
426
|
// dragging to immediately below (above next row)
|
|
355
427
|
dropPosition === 'below' && targetRowIndex === sourceRowIndex - 1; // dragging to immediately above (below previous row)
|
|
356
428
|
|
|
357
429
|
if (isAdjacentNode || sourceRowIndex === targetRowIndex) {
|
|
358
|
-
|
|
359
|
-
return -1;
|
|
430
|
+
return false;
|
|
360
431
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
432
|
+
|
|
433
|
+
// Internal validation passed, now apply additional user validation if provided
|
|
434
|
+
if (isValidRowReorderProp) {
|
|
435
|
+
const expandedSortedRowIds = (0, _xDataGrid.gridExpandedSortedRowIdsSelector)(apiRef);
|
|
436
|
+
const rowTree = (0, _xDataGrid.gridRowTreeSelector)(apiRef);
|
|
437
|
+
const sourceNode = rowTree[sourceRowId];
|
|
438
|
+
const targetNode = rowTree[targetRowId];
|
|
439
|
+
const prevNode = targetRowIndex > 0 ? rowTree[expandedSortedRowIds[targetRowIndex - 1]] : null;
|
|
440
|
+
const nextNode = targetRowIndex < expandedSortedRowIds.length - 1 ? rowTree[expandedSortedRowIds[targetRowIndex + 1]] : null;
|
|
441
|
+
const context = {
|
|
442
|
+
apiRef,
|
|
443
|
+
sourceNode,
|
|
444
|
+
targetNode,
|
|
445
|
+
prevNode,
|
|
446
|
+
nextNode,
|
|
447
|
+
dropPosition,
|
|
448
|
+
dragDirection
|
|
449
|
+
};
|
|
450
|
+
if (!isValidRowReorderProp(context)) {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
366
453
|
}
|
|
367
|
-
return
|
|
368
|
-
}, [apiRef]);
|
|
369
|
-
(0, _internals.useGridRegisterPipeProcessor)(apiRef, '
|
|
454
|
+
return true;
|
|
455
|
+
}, [apiRef, isValidRowReorderProp]);
|
|
456
|
+
(0, _internals.useGridRegisterPipeProcessor)(apiRef, 'isRowReorderValid', isRowReorderValid);
|
|
370
457
|
(0, _xDataGrid.useGridEvent)(apiRef, 'rowDragStart', handleDragStart);
|
|
371
458
|
(0, _xDataGrid.useGridEvent)(apiRef, 'rowDragOver', handleDragOver);
|
|
372
459
|
(0, _xDataGrid.useGridEvent)(apiRef, 'rowDragEnd', handleDragEnd);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { RefObject } from '@mui/x-internals/types';
|
|
2
|
+
import { type GridRowId, type GridTreeNode, type GridGroupNode, type GridRowTreeConfig, type GridKeyValue, type GridValidRowModel } from '@mui/x-data-grid';
|
|
3
|
+
import { type ReorderOperationType } from "./types.js";
|
|
4
|
+
import type { GridPrivateApiPro } from "../../../models/gridApiPro.js";
|
|
5
|
+
import { DataGridProProcessedProps } from "../../../models/dataGridProProps.js";
|
|
6
|
+
export { getNodePathInTree } from "../../../utils/tree/utils.js";
|
|
7
|
+
/**
|
|
8
|
+
* Finds the closest cell element from the given event target.
|
|
9
|
+
* If the target itself is a cell, returns it.
|
|
10
|
+
* Otherwise, searches for the closest parent with 'cell' in its className.
|
|
11
|
+
* @param target - The event target to start searching from
|
|
12
|
+
* @returns The cell element or the original target if no cell is found
|
|
13
|
+
*/
|
|
14
|
+
export declare function findCellElement(target: EventTarget | null): Element;
|
|
15
|
+
export declare function determineOperationType(sourceNode: GridTreeNode, targetNode: GridTreeNode): ReorderOperationType;
|
|
16
|
+
export declare function calculateTargetIndex(sourceNode: GridTreeNode, targetNode: GridTreeNode, isLastChild: boolean, rowTree: Record<GridRowId, GridTreeNode>): number;
|
|
17
|
+
export declare const collectAllLeafDescendants: (groupNode: GridGroupNode, tree: GridRowTreeConfig) => GridRowId[];
|
|
18
|
+
export declare const collectAllDescendants: (groupNode: GridGroupNode, tree: GridRowTreeConfig) => GridTreeNode[];
|
|
19
|
+
export declare const isDescendantOf: (possibleDescendant: GridTreeNode, ancestor: GridTreeNode, tree: GridRowTreeConfig) => boolean;
|
|
20
|
+
export declare const updateDescendantDepths: (group: GridGroupNode, tree: GridRowTreeConfig, depthDiff: number) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Finds an existing group node with the same groupingKey and groupingField under a parent.
|
|
23
|
+
*
|
|
24
|
+
* @param parentNode - The parent group node to search in
|
|
25
|
+
* @param groupingKey - The grouping key to match
|
|
26
|
+
* @param groupingField - The grouping field to match
|
|
27
|
+
* @param tree - The row tree configuration
|
|
28
|
+
* @returns The existing group node if found, null otherwise
|
|
29
|
+
*/
|
|
30
|
+
export declare function findExistingGroupWithSameKey(parentNode: GridGroupNode, groupingKey: GridKeyValue, groupingField: string, tree: GridRowTreeConfig): GridGroupNode | null;
|
|
31
|
+
/**
|
|
32
|
+
* Removes empty ancestor groups from the tree after a row move operation.
|
|
33
|
+
* Walks up the tree from the given group, removing any empty groups encountered.
|
|
34
|
+
*
|
|
35
|
+
* @param groupId - The ID of the group to start checking from
|
|
36
|
+
* @param tree - The row tree configuration
|
|
37
|
+
* @param removedGroups - Set to track which groups have been removed
|
|
38
|
+
* @returns The number of root-level groups that were removed
|
|
39
|
+
*/
|
|
40
|
+
export declare function removeEmptyAncestors(groupId: GridRowId, tree: GridRowTreeConfig, removedGroups: Set<GridRowId>): number;
|
|
41
|
+
export declare function handleProcessRowUpdateError(error: any, onProcessRowUpdateError?: DataGridProProcessedProps['onProcessRowUpdateError']): void;
|
|
42
|
+
/**
|
|
43
|
+
* Handles batch row updates with partial failure tracking.
|
|
44
|
+
*
|
|
45
|
+
* This class is designed for operations that need to update multiple rows
|
|
46
|
+
* atomically (like moving entire groups), while gracefully handling cases
|
|
47
|
+
* where some updates succeed and others fail.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* const updater = new BatchRowUpdater(apiRef, processRowUpdate, onError);
|
|
52
|
+
*
|
|
53
|
+
* // Queue multiple updates
|
|
54
|
+
* updater.queueUpdate('row1', originalRow1, newRow1);
|
|
55
|
+
* updater.queueUpdate('row2', originalRow2, newRow2);
|
|
56
|
+
*
|
|
57
|
+
* // Execute all updates
|
|
58
|
+
* const { successful, failed, updates } = await updater.executeAll();
|
|
59
|
+
*
|
|
60
|
+
* // Handle results
|
|
61
|
+
* if (successful.length > 0) {
|
|
62
|
+
* apiRef.current.updateRows(updates);
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare class BatchRowUpdater {
|
|
67
|
+
private apiRef;
|
|
68
|
+
private processRowUpdate;
|
|
69
|
+
private onProcessRowUpdateError;
|
|
70
|
+
private rowsToUpdate;
|
|
71
|
+
private originalRows;
|
|
72
|
+
private successfulRowIds;
|
|
73
|
+
private failedRowIds;
|
|
74
|
+
private pendingRowUpdates;
|
|
75
|
+
constructor(apiRef: RefObject<GridPrivateApiPro>, processRowUpdate: DataGridProProcessedProps['processRowUpdate'] | undefined, onProcessRowUpdateError: DataGridProProcessedProps['onProcessRowUpdateError'] | undefined);
|
|
76
|
+
queueUpdate(rowId: GridRowId, originalRow: GridValidRowModel, updatedRow: GridValidRowModel): void;
|
|
77
|
+
executeAll(): Promise<{
|
|
78
|
+
successful: GridRowId[];
|
|
79
|
+
failed: GridRowId[];
|
|
80
|
+
updates: GridValidRowModel[];
|
|
81
|
+
}>;
|
|
82
|
+
}
|