@mui/x-data-grid-pro 8.18.0 → 8.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +175 -0
- package/DataGridPro/DataGridPro.js +42 -8
- package/DataGridPro/useDataGridProComponent.js +3 -2
- package/components/GridDetailPanelToggleCell.js +0 -10
- package/components/GridRowReorderCell.js +15 -13
- package/components/GridTreeDataGroupingCell.js +0 -10
- package/components/headerFiltering/GridHeaderFilterCell.js +2 -3
- package/esm/DataGridPro/DataGridPro.js +42 -8
- package/esm/DataGridPro/useDataGridProComponent.js +4 -3
- package/esm/components/GridDetailPanelToggleCell.js +0 -10
- package/esm/components/GridRowReorderCell.js +15 -13
- package/esm/components/GridTreeDataGroupingCell.js +0 -10
- package/esm/components/headerFiltering/GridHeaderFilterCell.js +2 -3
- package/esm/hooks/features/columnPinning/useGridColumnPinningPreProcessors.js +4 -0
- package/esm/hooks/features/dataSource/useGridDataSourceBasePro.js +1 -1
- package/esm/hooks/features/detailPanel/useGridDetailPanel.js +6 -5
- package/esm/hooks/features/infiniteLoader/useGridInfiniteLoader.js +4 -4
- 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 +171 -82
- 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/serverSideLazyLoader/useGridInfiniteLoadingIntersection.js +3 -3
- 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/columnPinning/useGridColumnPinningPreProcessors.js +4 -0
- package/hooks/features/dataSource/useGridDataSourceBasePro.js +1 -1
- package/hooks/features/detailPanel/useGridDetailPanel.js +5 -4
- package/hooks/features/infiniteLoader/useGridInfiniteLoader.js +2 -2
- 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 +169 -80
- 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/serverSideLazyLoader/useGridInfiniteLoadingIntersection.js +3 -3
- 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,14 +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
|
|
58
|
-
const timeoutRowId = React.useRef('');
|
|
59
|
+
const timeoutInfoRef = React.useRef(EMPTY_TIMEOUT_INFO);
|
|
59
60
|
const timeout = (0, _useTimeout.default)();
|
|
60
61
|
const previousReorderState = React.useRef(EMPTY_REORDER_STATE);
|
|
61
62
|
const dropTarget = React.useRef({
|
|
@@ -70,26 +71,30 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
70
71
|
}, []);
|
|
71
72
|
|
|
72
73
|
// TODO: remove sortModel check once row reorder is sorting compatible
|
|
73
|
-
// remove treeData check once row reorder is treeData compatible
|
|
74
74
|
const isRowReorderDisabled = React.useMemo(() => {
|
|
75
|
-
return !props.rowReordering || !!sortModel.length
|
|
76
|
-
}, [props.rowReordering, sortModel
|
|
77
|
-
const
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
targetRow.classList.add(position === 'above' ? classes.rowDropAbove : classes.rowDropBelow);
|
|
89
|
-
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';
|
|
90
88
|
}
|
|
89
|
+
if (relativeY > bottomThreshold) {
|
|
90
|
+
return 'below';
|
|
91
|
+
}
|
|
92
|
+
return 'inside';
|
|
91
93
|
}
|
|
92
|
-
|
|
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]);
|
|
93
98
|
const applyDraggedState = React.useCallback((rowId, isDragged) => {
|
|
94
99
|
if (rowId) {
|
|
95
100
|
const draggedRow = apiRef.current.rootElementRef?.current?.querySelector(`[data-id="${rowId}"]`);
|
|
@@ -102,7 +107,7 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
102
107
|
}
|
|
103
108
|
}
|
|
104
109
|
}, [apiRef, classes.rowBeingDragged]);
|
|
105
|
-
const applyRowAnimation = React.useCallback(callback => {
|
|
110
|
+
const applyRowAnimation = React.useCallback(async callback => {
|
|
106
111
|
const rootElement = apiRef.current.rootElementRef?.current;
|
|
107
112
|
if (!rootElement) {
|
|
108
113
|
return;
|
|
@@ -119,7 +124,7 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
119
124
|
initialPositions.set(rowId, row.getBoundingClientRect());
|
|
120
125
|
}
|
|
121
126
|
});
|
|
122
|
-
callback();
|
|
127
|
+
await callback();
|
|
123
128
|
|
|
124
129
|
// Use `requestAnimationFrame` to ensure DOM has updated
|
|
125
130
|
requestAnimationFrame(() => {
|
|
@@ -161,15 +166,20 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
161
166
|
if (isRowReorderDisabled || Object.keys(editRowsState).length !== 0) {
|
|
162
167
|
return;
|
|
163
168
|
}
|
|
164
|
-
if (
|
|
169
|
+
if (timeoutInfoRef.current) {
|
|
165
170
|
timeout.clear();
|
|
166
|
-
|
|
171
|
+
timeoutInfoRef.current = EMPTY_TIMEOUT_INFO;
|
|
167
172
|
}
|
|
168
173
|
logger.debug(`Start dragging row ${params.id}`);
|
|
169
174
|
// Prevent drag events propagation.
|
|
170
175
|
// For more information check here https://github.com/mui/mui-x/issues/2680.
|
|
171
176
|
event.stopPropagation();
|
|
172
|
-
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
|
+
}));
|
|
173
183
|
dragRowNode.current = event.currentTarget;
|
|
174
184
|
// Apply cell-level dragging class to the drag handle
|
|
175
185
|
dragRowNode.current.classList.add(classes.rowDragging);
|
|
@@ -180,9 +190,10 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
180
190
|
removeDnDStylesTimeout.current = setTimeout(() => {
|
|
181
191
|
dragRowNode.current.classList.remove(classes.rowDragging);
|
|
182
192
|
});
|
|
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
|
-
}, [apiRef, isRowReorderDisabled, logger, classes.rowDragging, applyDraggedState,
|
|
196
|
+
}, [apiRef, isRowReorderDisabled, logger, classes.rowDragging, applyDraggedState, timeout]);
|
|
186
197
|
const handleDragOver = React.useCallback((params, event) => {
|
|
187
198
|
if (dragRowId === '') {
|
|
188
199
|
return;
|
|
@@ -192,34 +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
|
}
|
|
233
|
+
const sortedRowIndexLookup = (0, _xDataGrid.gridExpandedSortedRowIndexLookupSelector)(apiRef);
|
|
218
234
|
const targetRowIndex = sortedRowIndexLookup[params.id];
|
|
219
235
|
const sourceRowIndex = sortedRowIndexLookup[dragRowId];
|
|
220
|
-
|
|
221
|
-
// Determine drop position based on relativeY position within the row
|
|
222
|
-
const dropPosition = relativeY < midPoint ? 'above' : 'below';
|
|
223
236
|
const currentReorderState = {
|
|
224
237
|
dragDirection: targetRowIndex < sourceRowIndex ? 'up' : 'down',
|
|
225
238
|
previousTargetId: params.id,
|
|
@@ -232,7 +245,7 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
232
245
|
|
|
233
246
|
// Check if this is an adjacent position
|
|
234
247
|
const isAdjacentPosition = dropPosition === 'above' && targetRowIndex === sourceRowIndex + 1 || dropPosition === 'below' && targetRowIndex === sourceRowIndex - 1;
|
|
235
|
-
const
|
|
248
|
+
const isRowReorderValid = apiRef.current.unstable_applyPipeProcessors('isRowReorderValid', false, {
|
|
236
249
|
sourceRowId: dragRowId,
|
|
237
250
|
targetRowId: params.id,
|
|
238
251
|
dropPosition,
|
|
@@ -240,13 +253,21 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
240
253
|
});
|
|
241
254
|
|
|
242
255
|
// Show drop indicator for valid drops OR adjacent positions OR same node
|
|
243
|
-
if (
|
|
256
|
+
if (isRowReorderValid || isAdjacentPosition || isSameNode) {
|
|
244
257
|
dropTarget.current = {
|
|
245
258
|
targetRowId: params.id,
|
|
246
259
|
targetRowIndex,
|
|
247
260
|
dropPosition
|
|
248
261
|
};
|
|
249
|
-
|
|
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
|
+
}));
|
|
250
271
|
} else {
|
|
251
272
|
// Clear indicators for invalid drops
|
|
252
273
|
dropTarget.current = {
|
|
@@ -254,7 +275,12 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
254
275
|
targetRowIndex: null,
|
|
255
276
|
dropPosition: null
|
|
256
277
|
};
|
|
257
|
-
|
|
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
|
+
}));
|
|
258
284
|
}
|
|
259
285
|
previousReorderState.current = currentReorderState;
|
|
260
286
|
}
|
|
@@ -265,16 +291,16 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
265
291
|
} else {
|
|
266
292
|
event.dataTransfer.dropEffect = 'copy';
|
|
267
293
|
}
|
|
268
|
-
}, [dragRowId, apiRef, logger, timeout,
|
|
269
|
-
const handleDragEnd = React.useCallback((_, event) => {
|
|
294
|
+
}, [dragRowId, apiRef, logger, timeout, calculateDropPosition]);
|
|
295
|
+
const handleDragEnd = React.useCallback(async (_, event) => {
|
|
270
296
|
// Call the gridEditRowsStateSelector directly to avoid infnite loop
|
|
271
297
|
const editRowsState = (0, _internals.gridEditRowsStateSelector)(apiRef);
|
|
272
298
|
if (dragRowId === '' || isRowReorderDisabled || Object.keys(editRowsState).length !== 0) {
|
|
273
299
|
return;
|
|
274
300
|
}
|
|
275
|
-
if (
|
|
301
|
+
if (timeoutInfoRef.current) {
|
|
276
302
|
timeout.clear();
|
|
277
|
-
|
|
303
|
+
timeoutInfoRef.current = EMPTY_TIMEOUT_INFO;
|
|
278
304
|
}
|
|
279
305
|
logger.debug('End dragging row');
|
|
280
306
|
event.preventDefault();
|
|
@@ -286,11 +312,6 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
286
312
|
const dragDirection = previousReorderState.current.dragDirection;
|
|
287
313
|
previousReorderState.current = EMPTY_REORDER_STATE;
|
|
288
314
|
|
|
289
|
-
// Clear visual indicators and dragged state
|
|
290
|
-
applyDropIndicator(null, null);
|
|
291
|
-
applyDraggedState(dragRowId, false);
|
|
292
|
-
apiRef.current.setRowDragActive(false);
|
|
293
|
-
|
|
294
315
|
// Check if the row was dropped outside the grid.
|
|
295
316
|
if (!event.dataTransfer || event.dataTransfer.dropEffect === 'none') {
|
|
296
317
|
// Reset drop target state
|
|
@@ -301,29 +322,80 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
301
322
|
};
|
|
302
323
|
originRowIndex.current = null;
|
|
303
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
|
+
}));
|
|
304
333
|
return;
|
|
305
334
|
}
|
|
306
335
|
if (dropTarget.current.targetRowIndex !== null && dropTarget.current.targetRowId !== null) {
|
|
307
|
-
const
|
|
308
|
-
const targetRowIndex = dropTarget.current.targetRowIndex;
|
|
309
|
-
const validatedIndex = apiRef.current.unstable_applyPipeProcessors('getRowReorderTargetIndex', targetRowIndex, {
|
|
336
|
+
const isRowReorderValid = apiRef.current.unstable_applyPipeProcessors('isRowReorderValid', false, {
|
|
310
337
|
sourceRowId: dragRowId,
|
|
311
338
|
targetRowId: dropTarget.current.targetRowId,
|
|
312
339
|
dropPosition: dropTarget.current.dropPosition,
|
|
313
340
|
dragDirection: dragDirection
|
|
314
341
|
});
|
|
315
|
-
if (
|
|
316
|
-
|
|
317
|
-
|
|
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
|
+
}
|
|
318
349
|
|
|
319
|
-
//
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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);
|
|
364
|
+
|
|
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
|
+
}));
|
|
327
399
|
}
|
|
328
400
|
}
|
|
329
401
|
|
|
@@ -334,8 +406,9 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
334
406
|
dropPosition: null
|
|
335
407
|
};
|
|
336
408
|
setDragRowId('');
|
|
337
|
-
}, [apiRef, dragRowId, isRowReorderDisabled, logger,
|
|
338
|
-
const
|
|
409
|
+
}, [apiRef, dragRowId, isRowReorderDisabled, logger, applyDraggedState, timeout, applyRowAnimation]);
|
|
410
|
+
const isValidRowReorderProp = props.isValidRowReorder;
|
|
411
|
+
const isRowReorderValid = React.useCallback((initialValue, {
|
|
339
412
|
sourceRowId,
|
|
340
413
|
targetRowId,
|
|
341
414
|
dropPosition,
|
|
@@ -344,27 +417,43 @@ const useGridRowReorder = (apiRef, props) => {
|
|
|
344
417
|
if ((0, _xDataGrid.gridRowMaximumTreeDepthSelector)(apiRef) > 1) {
|
|
345
418
|
return initialValue;
|
|
346
419
|
}
|
|
420
|
+
const sortedRowIndexLookup = (0, _xDataGrid.gridExpandedSortedRowIndexLookupSelector)(apiRef);
|
|
347
421
|
const targetRowIndex = sortedRowIndexLookup[targetRowId];
|
|
348
422
|
const sourceRowIndex = sortedRowIndexLookup[sourceRowId];
|
|
349
423
|
|
|
350
|
-
//
|
|
424
|
+
// Apply internal validation: check if this drop would result in no actual movement
|
|
351
425
|
const isAdjacentNode = dropPosition === 'above' && targetRowIndex === sourceRowIndex + 1 ||
|
|
352
426
|
// dragging to immediately below (above next row)
|
|
353
427
|
dropPosition === 'below' && targetRowIndex === sourceRowIndex - 1; // dragging to immediately above (below previous row)
|
|
354
428
|
|
|
355
429
|
if (isAdjacentNode || sourceRowIndex === targetRowIndex) {
|
|
356
|
-
|
|
357
|
-
return -1;
|
|
430
|
+
return false;
|
|
358
431
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
+
}
|
|
364
453
|
}
|
|
365
|
-
return
|
|
366
|
-
}, [apiRef,
|
|
367
|
-
(0, _internals.useGridRegisterPipeProcessor)(apiRef, '
|
|
454
|
+
return true;
|
|
455
|
+
}, [apiRef, isValidRowReorderProp]);
|
|
456
|
+
(0, _internals.useGridRegisterPipeProcessor)(apiRef, 'isRowReorderValid', isRowReorderValid);
|
|
368
457
|
(0, _xDataGrid.useGridEvent)(apiRef, 'rowDragStart', handleDragStart);
|
|
369
458
|
(0, _xDataGrid.useGridEvent)(apiRef, 'rowDragOver', handleDragOver);
|
|
370
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
|
+
}
|