@mui/x-data-grid-premium
Version:
The Premium plan edition of the MUI X Data Grid Components.
546 lines (538 loc) • 22.1 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SameParentSwapOperation = exports.CrossParentLeafOperation = exports.CrossParentGroupOperation = exports.BaseReorderOperation = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _xDataGridPro = require("@mui/x-data-grid-pro");
var _warning = require("@mui/x-internals/warning");
var _isDeepEqual = require("@mui/x-internals/isDeepEqual");
var _rowGrouping = require("../rowGrouping");
var _gridRowGroupingUtils = require("../rowGrouping/gridRowGroupingUtils");
var _utils = require("./utils");
/**
* Base class for all reorder operations.
* Provides abstract methods for operation detection and execution.
*/
class BaseReorderOperation {}
/**
* Handles reordering of items within the same parent group.
*/
exports.BaseReorderOperation = BaseReorderOperation;
class SameParentSwapOperation extends BaseReorderOperation {
operationType = 'same-parent-swap';
detectOperation(ctx) {
const {
sourceRowId,
placeholderIndex,
sortedFilteredRowIds,
sortedFilteredRowIndexLookup,
rowTree,
apiRef
} = ctx;
const sourceNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sourceRowId);
if (!sourceNode || sourceNode.type === 'footer') {
return null;
}
let targetIndex = placeholderIndex;
const sourceIndex = sortedFilteredRowIndexLookup[sourceRowId];
if (targetIndex === sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) {
targetIndex -= 1;
}
let targetNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[targetIndex]);
if (placeholderIndex > sourceIndex && sourceNode.parent === targetNode.parent) {
targetIndex = placeholderIndex - 1;
targetNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[targetIndex]);
if (targetNode && targetNode.depth !== sourceNode.depth) {
while (targetNode.depth > sourceNode.depth && targetIndex >= 0) {
targetIndex -= 1;
targetNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[targetIndex]);
}
}
if (targetIndex === -1) {
return null;
}
}
let isLastChild = false;
if (!targetNode) {
if (placeholderIndex >= sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) {
targetNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[sortedFilteredRowIds.length - 1]);
isLastChild = true;
} else {
return null;
}
}
let adjustedTargetNode = targetNode;
// Case A and B adjustment
if (targetNode.type === 'group' && sourceNode.parent !== targetNode.parent && sourceNode.depth > targetNode.depth) {
let i = targetIndex - 1;
while (i >= 0) {
const node = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[i]);
if (node && node.depth < sourceNode.depth) {
return null;
}
if (node && node.depth === sourceNode.depth) {
targetIndex = i;
adjustedTargetNode = node;
break;
}
i -= 1;
}
}
const operationType = (0, _utils.determineOperationType)(sourceNode, adjustedTargetNode);
if (operationType !== 'same-parent-swap') {
return null;
}
const actualTargetIndex = (0, _utils.calculateTargetIndex)(sourceNode, adjustedTargetNode, isLastChild, rowTree);
targetNode = adjustedTargetNode;
if (sourceNode.type !== targetNode.type) {
return null;
}
return {
sourceNode,
targetNode,
actualTargetIndex,
isLastChild,
operationType
};
}
executeOperation(operation, ctx) {
const {
sourceNode,
actualTargetIndex
} = operation;
const {
apiRef,
sourceRowId
} = ctx;
apiRef.current.setState(state => {
const group = (0, _xDataGridPro.gridRowTreeSelector)(apiRef)[sourceNode.parent];
const currentChildren = [...group.children];
const oldIndex = currentChildren.findIndex(row => row === sourceRowId);
if (oldIndex === -1 || actualTargetIndex === -1 || oldIndex === actualTargetIndex) {
return state;
}
currentChildren.splice(actualTargetIndex, 0, currentChildren.splice(oldIndex, 1)[0]);
return (0, _extends2.default)({}, state, {
rows: (0, _extends2.default)({}, state.rows, {
tree: (0, _extends2.default)({}, state.rows.tree, {
[sourceNode.parent]: (0, _extends2.default)({}, group, {
children: currentChildren
})
})
})
});
});
apiRef.current.publishEvent('rowsSet');
}
}
/**
* Handles moving leaf nodes between different parent groups.
*/
exports.SameParentSwapOperation = SameParentSwapOperation;
class CrossParentLeafOperation extends BaseReorderOperation {
operationType = 'cross-parent-leaf';
detectOperation(ctx) {
const {
sourceRowId,
placeholderIndex,
sortedFilteredRowIds,
rowTree,
apiRef
} = ctx;
const sourceNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sourceRowId);
if (!sourceNode || sourceNode.type === 'footer') {
return null;
}
let targetNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[placeholderIndex]);
let isLastChild = false;
if (!targetNode) {
if (placeholderIndex >= sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) {
targetNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[sortedFilteredRowIds.length - 1]);
isLastChild = true;
} else {
return null;
}
}
let adjustedTargetNode = targetNode;
// Case D adjustment
if (sourceNode.type === 'leaf' && targetNode.type === 'group' && targetNode.depth < sourceNode.depth) {
const prevIndex = placeholderIndex - 1;
if (prevIndex >= 0) {
const prevRowId = sortedFilteredRowIds[prevIndex];
const leafTargetNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, prevRowId);
if (leafTargetNode && leafTargetNode.type === 'leaf') {
adjustedTargetNode = leafTargetNode;
isLastChild = true;
}
}
}
const operationType = (0, _utils.determineOperationType)(sourceNode, adjustedTargetNode);
if (operationType !== 'cross-parent-leaf') {
return null;
}
const actualTargetIndex = (0, _utils.calculateTargetIndex)(sourceNode, adjustedTargetNode, isLastChild, rowTree);
targetNode = adjustedTargetNode;
// Validate depth constraints
if (sourceNode.type === 'leaf' && targetNode.type === 'leaf') {
if (sourceNode.depth !== targetNode.depth) {
return null;
}
} else if (sourceNode.type === 'leaf' && targetNode.type === 'group') {
if (targetNode.depth >= sourceNode.depth) {
return null;
}
}
return {
sourceNode,
targetNode: adjustedTargetNode,
actualTargetIndex,
isLastChild,
operationType
};
}
async executeOperation(operation, ctx) {
const {
sourceNode,
targetNode,
isLastChild
} = operation;
const {
apiRef,
sourceRowId,
processRowUpdate,
onProcessRowUpdateError
} = ctx;
let target = targetNode;
if (targetNode.type === 'group') {
const prevIndex = ctx.placeholderIndex - 1;
if (prevIndex >= 0) {
const prevRowId = ctx.sortedFilteredRowIds[prevIndex];
const prevNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, prevRowId);
if (prevNode && prevNode.type === 'leaf') {
target = prevNode;
}
}
}
const rowTree = (0, _xDataGridPro.gridRowTreeSelector)(apiRef);
const sourceGroup = rowTree[sourceNode.parent];
const targetGroup = rowTree[target.parent];
const sourceChildren = sourceGroup.children;
const targetChildren = targetGroup.children;
const sourceIndex = sourceChildren.findIndex(row => row === sourceRowId);
const targetIndex = targetChildren.findIndex(row => row === target.id);
if (sourceIndex === -1 || targetIndex === -1) {
return;
}
const dataRowIdToModelLookup = (0, _xDataGridPro.gridRowsLookupSelector)(apiRef);
const columnsLookup = (0, _xDataGridPro.gridColumnLookupSelector)(apiRef);
const sanitizedRowGroupingModel = (0, _rowGrouping.gridRowGroupingSanitizedModelSelector)(apiRef);
const originalSourceRow = dataRowIdToModelLookup[sourceRowId];
let updatedSourceRow = (0, _extends2.default)({}, originalSourceRow);
const targetRow = dataRowIdToModelLookup[target.id];
const groupingRules = (0, _gridRowGroupingUtils.getGroupingRules)({
sanitizedRowGroupingModel,
columnsLookup
});
for (const groupingRule of groupingRules) {
const colDef = columnsLookup[groupingRule.field];
if (groupingRule.groupingValueSetter && colDef) {
const targetGroupingValue = (0, _gridRowGroupingUtils.getCellGroupingCriteria)({
row: targetRow,
colDef,
groupingRule,
apiRef
}).key;
updatedSourceRow = groupingRule.groupingValueSetter(targetGroupingValue, updatedSourceRow, colDef, apiRef);
} else {
updatedSourceRow[groupingRule.field] = targetRow[groupingRule.field];
}
}
const commitStateUpdate = finalSourceRow => {
apiRef.current.setState(state => {
const updatedSourceChildren = sourceChildren.filter(rowId => rowId !== sourceRowId);
const updatedTree = (0, _extends2.default)({}, state.rows.tree);
const removedGroups = new Set();
let rootLevelRemovals = 0;
if (updatedSourceChildren.length === 0) {
removedGroups.add(sourceGroup.id);
rootLevelRemovals = (0, _utils.removeEmptyAncestors)(sourceGroup.parent, updatedTree, removedGroups);
}
removedGroups.forEach(groupId => {
const group = updatedTree[groupId];
if (group && group.parent && updatedTree[group.parent]) {
const parent = updatedTree[group.parent];
updatedTree[group.parent] = (0, _extends2.default)({}, parent, {
children: parent.children.filter(childId => childId !== groupId)
});
}
delete updatedTree[groupId];
});
if (!removedGroups.has(sourceGroup.id)) {
updatedTree[sourceNode.parent] = (0, _extends2.default)({}, sourceGroup, {
children: updatedSourceChildren
});
}
const updatedTargetChildren = isLastChild ? [...targetChildren, sourceRowId] : [...targetChildren.slice(0, targetIndex), sourceRowId, ...targetChildren.slice(targetIndex)];
updatedTree[target.parent] = (0, _extends2.default)({}, targetGroup, {
children: updatedTargetChildren
});
updatedTree[sourceNode.id] = (0, _extends2.default)({}, sourceNode, {
parent: target.parent
});
return (0, _extends2.default)({}, state, {
rows: (0, _extends2.default)({}, state.rows, {
totalTopLevelRowCount: state.rows.totalTopLevelRowCount - rootLevelRemovals,
tree: updatedTree
})
});
});
apiRef.current.updateRows([finalSourceRow]);
apiRef.current.publishEvent('rowsSet');
};
if (processRowUpdate && !(0, _isDeepEqual.isDeepEqual)(originalSourceRow, updatedSourceRow)) {
const params = {
rowId: sourceRowId,
previousRow: originalSourceRow,
updatedRow: updatedSourceRow
};
apiRef.current.setLoading(true);
try {
const processedRow = await processRowUpdate(updatedSourceRow, originalSourceRow, params);
const finalRow = processedRow || updatedSourceRow;
commitStateUpdate(finalRow);
} catch (error) {
apiRef.current.setLoading(false);
if (onProcessRowUpdateError) {
onProcessRowUpdateError(error);
} else if (process.env.NODE_ENV !== 'production') {
(0, _warning.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');
}
} finally {
apiRef.current.setLoading(false);
}
} else {
commitStateUpdate(updatedSourceRow);
}
}
}
/**
* Handles moving entire groups between different parents.
*/
exports.CrossParentLeafOperation = CrossParentLeafOperation;
class CrossParentGroupOperation extends BaseReorderOperation {
operationType = 'cross-parent-group';
detectOperation(ctx) {
const {
sourceRowId,
placeholderIndex,
sortedFilteredRowIds,
rowTree,
apiRef
} = ctx;
const sourceNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sourceRowId);
if (!sourceNode || sourceNode.type === 'footer') {
return null;
}
let targetIndex = placeholderIndex;
let targetNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[placeholderIndex]);
let isLastChild = false;
if (!targetNode) {
if (placeholderIndex >= sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) {
targetNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[sortedFilteredRowIds.length - 1]);
targetIndex = sortedFilteredRowIds.length - 1;
isLastChild = true;
} else {
return null;
}
}
let adjustedTargetNode = targetNode;
// Case G adjustment
if (sourceNode.type === 'group' && targetNode.type === 'group' && sourceNode.parent !== targetNode.parent && sourceNode.depth > targetNode.depth) {
let prevIndex = targetIndex - 1;
if (prevIndex < 0) {
return null;
}
let prevNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[prevIndex]);
if (prevNode && prevNode.depth !== sourceNode.depth) {
while (prevNode.depth > sourceNode.depth && prevIndex >= 0) {
prevIndex -= 1;
prevNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[prevIndex]);
}
}
if (!prevNode || prevNode.type !== 'group' || prevNode.depth !== sourceNode.depth) {
return null;
}
isLastChild = true;
adjustedTargetNode = prevNode;
}
const operationType = (0, _utils.determineOperationType)(sourceNode, adjustedTargetNode);
if (operationType !== 'cross-parent-group') {
return null;
}
const actualTargetIndex = (0, _utils.calculateTargetIndex)(sourceNode, adjustedTargetNode, isLastChild, rowTree);
const operation = {
sourceNode,
targetNode: adjustedTargetNode,
actualTargetIndex,
isLastChild,
operationType
};
targetNode = adjustedTargetNode;
if (sourceNode.depth !== targetNode.depth) {
return null;
}
return operation;
}
async executeOperation(operation, ctx) {
const {
sourceNode,
targetNode,
isLastChild
} = operation;
const {
apiRef,
processRowUpdate,
onProcessRowUpdateError
} = ctx;
const tree = (0, _xDataGridPro.gridRowTreeSelector)(apiRef);
const dataRowIdToModelLookup = (0, _xDataGridPro.gridRowsLookupSelector)(apiRef);
const columnsLookup = (0, _xDataGridPro.gridColumnLookupSelector)(apiRef);
const sanitizedRowGroupingModel = (0, _rowGrouping.gridRowGroupingSanitizedModelSelector)(apiRef);
const allLeafIds = (0, _utils.collectAllLeafDescendants)(sourceNode, tree);
if (allLeafIds.length === 0) {
return;
}
const updater = new _utils.BatchRowUpdater(processRowUpdate, onProcessRowUpdateError);
const groupingRules = (0, _gridRowGroupingUtils.getGroupingRules)({
sanitizedRowGroupingModel,
columnsLookup
});
const targetParentPath = (0, _utils.getNodePathInTree)({
id: targetNode.parent,
tree
});
for (const leafId of allLeafIds) {
const originalRow = dataRowIdToModelLookup[leafId];
let updatedRow = (0, _extends2.default)({}, originalRow);
for (let depth = 0; depth < targetParentPath.length; depth += 1) {
const pathItem = targetParentPath[depth];
if (pathItem.field) {
const groupingRule = groupingRules.find(rule => rule.field === pathItem.field);
if (groupingRule) {
const colDef = columnsLookup[groupingRule.field];
if (groupingRule.groupingValueSetter && colDef) {
updatedRow = groupingRule.groupingValueSetter(pathItem.key, updatedRow, colDef, apiRef);
} else {
updatedRow[groupingRule.field] = pathItem.key;
}
}
}
}
updater.queueUpdate(leafId, originalRow, updatedRow);
}
apiRef.current.setLoading(true);
try {
const {
successful,
failed,
updates
} = await updater.executeAll();
if (successful.length > 0) {
apiRef.current.setState(state => {
const updatedTree = (0, _extends2.default)({}, state.rows.tree);
const treeDepths = (0, _extends2.default)({}, state.rows.treeDepths);
let rootLevelRemovals = 0;
if (failed.length === 0) {
const sourceParentNode = updatedTree[sourceNode.parent];
if (!sourceParentNode) {
const targetParentNode = updatedTree[targetNode.parent];
const targetIndex = targetParentNode.children.indexOf(targetNode.id);
const newTargetChildren = [...targetParentNode.children];
if (isLastChild) {
newTargetChildren.push(sourceNode.id);
} else {
newTargetChildren.splice(targetIndex, 0, sourceNode.id);
}
updatedTree[targetNode.parent] = (0, _extends2.default)({}, targetParentNode, {
children: newTargetChildren
});
updatedTree[sourceNode.id] = (0, _extends2.default)({}, sourceNode, {
parent: targetNode.parent
});
} else {
const updatedSourceParentChildren = sourceParentNode.children.filter(id => id !== sourceNode.id);
if (updatedSourceParentChildren.length === 0) {
const removedGroups = new Set();
removedGroups.add(sourceNode.parent);
const parentOfSourceParent = updatedTree[sourceNode.parent].parent;
if (parentOfSourceParent) {
rootLevelRemovals = (0, _utils.removeEmptyAncestors)(parentOfSourceParent, updatedTree, removedGroups);
}
removedGroups.forEach(groupId => {
const group = updatedTree[groupId];
if (group && group.parent && updatedTree[group.parent]) {
const parent = updatedTree[group.parent];
updatedTree[group.parent] = (0, _extends2.default)({}, parent, {
children: parent.children.filter(childId => childId !== groupId)
});
}
delete updatedTree[groupId];
});
} else {
updatedTree[sourceNode.parent] = (0, _extends2.default)({}, sourceParentNode, {
children: updatedSourceParentChildren
});
}
const targetParentNode = updatedTree[targetNode.parent];
const sourceGroupNode = sourceNode;
const existingGroup = sourceGroupNode.groupingKey !== null && sourceGroupNode.groupingField !== null ? (0, _utils.findExistingGroupWithSameKey)(targetParentNode, sourceGroupNode.groupingKey, sourceGroupNode.groupingField, updatedTree) : null;
if (existingGroup) {
const updatedExistingGroup = (0, _extends2.default)({}, existingGroup, {
children: [...existingGroup.children, ...sourceGroupNode.children]
});
updatedTree[existingGroup.id] = updatedExistingGroup;
sourceGroupNode.children.forEach(childId => {
const childNode = updatedTree[childId];
if (childNode) {
updatedTree[childId] = (0, _extends2.default)({}, childNode, {
parent: existingGroup.id
});
}
});
delete updatedTree[sourceNode.id];
} else {
const targetIndex = targetParentNode.children.indexOf(targetNode.id);
const newTargetChildren = [...targetParentNode.children];
if (isLastChild) {
newTargetChildren.push(sourceNode.id);
} else {
newTargetChildren.splice(targetIndex, 0, sourceNode.id);
}
updatedTree[targetNode.parent] = (0, _extends2.default)({}, targetParentNode, {
children: newTargetChildren
});
updatedTree[sourceNode.id] = (0, _extends2.default)({}, sourceNode, {
parent: targetNode.parent
});
}
}
}
return (0, _extends2.default)({}, state, {
rows: (0, _extends2.default)({}, state.rows, {
totalTopLevelRowCount: state.rows.totalTopLevelRowCount - rootLevelRemovals,
tree: updatedTree,
treeDepths
})
});
});
apiRef.current.updateRows(updates);
apiRef.current.publishEvent('rowsSet');
}
} finally {
apiRef.current.setLoading(false);
}
}
}
exports.CrossParentGroupOperation = CrossParentGroupOperation;