UNPKG

@mui/x-data-grid-premium

Version:

The Premium plan edition of the MUI X Data Grid Components.

546 lines (538 loc) 22.1 kB
"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;