UNPKG

@mui/x-data-grid-pro

Version:

The Pro plan edition of the MUI X Data Grid components.

541 lines (515 loc) 18.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.treeDataReorderExecutor = exports.SameParentSwapOperation = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _xDataGrid = require("@mui/x-data-grid"); var _reorderExecutor = require("../rowReorder/reorderExecutor"); var _utils = require("../rowReorder/utils"); var _utils2 = require("./utils"); /** * Handles reordering of items within the same parent group. */ class SameParentSwapOperation extends _reorderExecutor.BaseReorderOperation { operationType = 'same-parent-swap'; detectOperation(ctx) { if (ctx.dropPosition === 'inside') { return null; } const { sourceRowId, placeholderIndex, sortedFilteredRowIds, sortedFilteredRowIndexLookup, rowTree, apiRef } = ctx; const sourceNode = (0, _xDataGrid.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, _xDataGrid.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[targetIndex]); if (placeholderIndex > sourceIndex && sourceNode.parent === targetNode.parent) { targetIndex = placeholderIndex - 1; targetNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[targetIndex]); if (targetNode && targetNode.depth !== sourceNode.depth) { while (targetNode.depth > sourceNode.depth && targetIndex >= 0) { targetIndex -= 1; targetNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[targetIndex]); } } if (targetIndex === -1) { return null; } } let isLastChild = false; if (!targetNode) { if (placeholderIndex >= sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) { targetNode = (0, _xDataGrid.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, _xDataGrid.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; } } // Check if below last node in the same group as source node const isBelowPosition = ctx.dropPosition === 'below'; if (isBelowPosition && sourceNode.parent !== adjustedTargetNode.parent) { const unAdjustedTargetIndex = placeholderIndex - 1; const unAdjustedTargetNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[unAdjustedTargetIndex]); if (unAdjustedTargetNode && unAdjustedTargetNode.parent === sourceNode.parent) { adjustedTargetNode = unAdjustedTargetNode; isLastChild = true; } } if (sourceNode.parent !== adjustedTargetNode.parent) { return null; } const actualTargetIndex = (0, _utils.calculateTargetIndex)(sourceNode, adjustedTargetNode, isLastChild, rowTree); targetNode = adjustedTargetNode; return { sourceNode, targetNode, actualTargetIndex, isLastChild, operationType: this.operationType }; } executeOperation(operation, ctx) { const { sourceNode, actualTargetIndex } = operation; const { apiRef, sourceRowId } = ctx; apiRef.current.setState(state => { const group = (0, _xDataGrid.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 parents. */ exports.SameParentSwapOperation = SameParentSwapOperation; class CrossParentLeafOperation extends _reorderExecutor.BaseReorderOperation { operationType = 'cross-parent-leaf'; detectOperation(ctx) { // Fail for "inside" position - let DropOnLeafOperation handle it if (ctx.dropPosition === 'inside') { return null; } const { sourceRowId, placeholderIndex, sortedFilteredRowIds, rowTree, apiRef, setTreeDataPath } = ctx; const sourceNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, sourceRowId); if (!sourceNode || sourceNode.type !== 'leaf') { return null; } if (!setTreeDataPath) { (0, _utils2.displaySetTreeDataPathWarning)('Cross-parent reordering'); } let targetIndex = placeholderIndex; if (targetIndex === sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) { targetIndex = sortedFilteredRowIds.length - 1; } if (targetIndex < 0) { return null; } const targetNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[targetIndex]); if (!targetNode) { return null; } if (sourceNode.parent === targetNode.parent) { return null; } const actualTargetIndex = (0, _utils.calculateTargetIndex)(sourceNode, targetNode, placeholderIndex >= sortedFilteredRowIds.length, rowTree); return { sourceNode, targetNode, actualTargetIndex, isLastChild: placeholderIndex >= sortedFilteredRowIds.length, operationType: this.operationType }; } async executeOperation(operation, ctx) { const { sourceNode, targetNode, actualTargetIndex } = operation; const { apiRef } = ctx; const rowTree = (0, _xDataGrid.gridRowTreeSelector)(apiRef); const targetParentNode = rowTree[targetNode.parent]; const targetPath = (0, _utils2.buildTreeDataPath)(targetParentNode, rowTree); const updatedRow = await (0, _utils2.updateLeafPath)(sourceNode, targetPath, ctx); if (!updatedRow) { return; } // Update tree structure apiRef.current.setState(state => { const updatedTree = (0, _extends2.default)({}, state.rows.tree); (0, _utils2.removeNodeFromSourceParent)(updatedTree, sourceNode); const targetParent = updatedTree[targetNode.parent]; const targetChildren = [...targetParent.children]; targetChildren.splice(actualTargetIndex, 0, sourceNode.id); updatedTree[targetNode.parent] = (0, _extends2.default)({}, targetParent, { children: targetChildren }); const parentNode = updatedTree[targetNode.parent]; (0, _utils2.updateNodeParentAndDepth)(updatedTree, sourceNode, targetNode.parent, parentNode.depth + 1); return (0, _extends2.default)({}, state, { rows: (0, _extends2.default)({}, state.rows, { tree: updatedTree }) }); }); apiRef.current.updateRows([updatedRow]); apiRef.current.publishEvent('rowsSet'); } } /** * Handles dropping any node (leaf or group) "inside" a leaf node. * This converts the target leaf into a parent group and makes the dragged node its child. */ class DropOnLeafOperation extends _reorderExecutor.BaseReorderOperation { operationType = 'drop-on-leaf'; detectOperation(ctx) { const { sourceRowId, dropPosition, placeholderIndex, sortedFilteredRowIds, apiRef, setTreeDataPath } = ctx; if (dropPosition !== 'inside') { return null; } const sourceNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, sourceRowId); if (!sourceNode || sourceNode.type === 'footer') { return null; } if (!setTreeDataPath) { (0, _utils2.displaySetTreeDataPathWarning)('Drop on leaf reordering'); } // Find target node let targetIndex = placeholderIndex; if (targetIndex === sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) { targetIndex = sortedFilteredRowIds.length - 1; } if (targetIndex < 0) { return null; } const targetNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[targetIndex]); if (!targetNode || targetNode.type !== 'leaf') { return null; } // Target leaf will become a parent, so the actual target index is 0 (first child) const actualTargetIndex = 0; return { sourceNode, targetNode, actualTargetIndex, isLastChild: false, operationType: this.operationType }; } async executeOperation(operation, ctx) { const { sourceNode, targetNode } = operation; const { apiRef } = ctx; const rowTree = (0, _xDataGrid.gridRowTreeSelector)(apiRef); // Build target path for the new structure const targetPath = (0, _utils2.buildTreeDataPath)(targetNode, rowTree); let rowsToUpdate = []; // Handle source node path updates if (sourceNode.type === 'leaf') { // Simple leaf move const updatedRow = await (0, _utils2.updateLeafPath)(sourceNode, targetPath, ctx); if (!updatedRow) { return; } rowsToUpdate.push(updatedRow); } else { // Group move - update entire hierarchy const sourceParentNode = rowTree[sourceNode.parent]; const sourceBasePath = (0, _utils2.buildTreeDataPath)(sourceParentNode, rowTree); rowsToUpdate = await (0, _utils2.updateGroupHierarchyPaths)(sourceNode, sourceBasePath, targetPath, ctx); if (rowsToUpdate.length === 0) { return; } } apiRef.current.setState(state => { const updatedTree = (0, _extends2.default)({}, state.rows.tree); (0, _utils2.removeNodeFromSourceParent)(updatedTree, sourceNode); updatedTree[targetNode.id] = (0, _extends2.default)({}, targetNode, { type: 'group', children: [sourceNode.id], childrenFromPath: {}, groupingField: null, isAutoGenerated: false, childrenExpanded: true }); (0, _utils2.updateNodeParentAndDepth)(updatedTree, sourceNode, targetNode.id, targetNode.depth + 1); return (0, _extends2.default)({}, state, { rows: (0, _extends2.default)({}, state.rows, { tree: updatedTree }) }); }); // Update rows in the grid apiRef.current.updateRows(rowsToUpdate); apiRef.current.publishEvent('rowsSet'); } } /** * Handles dropping any node (leaf or group) "inside" a group node. * This makes the dragged node the first child of the target group. */ class DropOnGroupOperation extends _reorderExecutor.BaseReorderOperation { operationType = 'drop-on-group'; detectOperation(ctx) { const { sourceRowId, dropPosition, placeholderIndex, sortedFilteredRowIds, apiRef, setTreeDataPath, rowTree } = ctx; // Only applies to "inside" drop position if (dropPosition !== 'inside') { return null; } const sourceNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, sourceRowId); if (!sourceNode || sourceNode.type === 'footer') { return null; } if (!setTreeDataPath) { (0, _utils2.displaySetTreeDataPathWarning)('Drop on group reordering'); } let targetIndex = placeholderIndex; if (targetIndex === sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) { targetIndex = sortedFilteredRowIds.length - 1; } if (targetIndex < 0) { return null; } const targetNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[targetIndex]); if (!targetNode || targetNode.type !== 'group') { return null; } if ((0, _utils.isDescendantOf)(targetNode, sourceNode, rowTree)) { return null; } const actualTargetIndex = 0; return { sourceNode, targetNode, actualTargetIndex, isLastChild: false, operationType: this.operationType }; } async executeOperation(operation, ctx) { const { sourceNode, targetNode } = operation; const { apiRef } = ctx; const rowTree = (0, _xDataGrid.gridRowTreeSelector)(apiRef); // Build target path for the new structure const targetPath = (0, _utils2.buildTreeDataPath)(targetNode, rowTree); let rowsToUpdate = []; // Handle source node path updates if (sourceNode.type === 'leaf') { // Simple leaf move const updatedRow = await (0, _utils2.updateLeafPath)(sourceNode, targetPath, ctx); if (!updatedRow) { return; } rowsToUpdate.push(updatedRow); } else { // Group move - update entire hierarchy const sourceParentNode = rowTree[sourceNode.parent]; const sourceBasePath = (0, _utils2.buildTreeDataPath)(sourceParentNode, rowTree); rowsToUpdate = await (0, _utils2.updateGroupHierarchyPaths)(sourceNode, sourceBasePath, targetPath, ctx); if (rowsToUpdate.length === 0) { return; } } // Update tree structure apiRef.current.setState(state => { const updatedTree = (0, _extends2.default)({}, state.rows.tree); // Remove source from its current parent (0, _utils2.removeNodeFromSourceParent)(updatedTree, sourceNode); // Add source as first child of target group const targetGroup = updatedTree[targetNode.id]; const targetChildren = [sourceNode.id, ...targetGroup.children]; updatedTree[targetNode.id] = (0, _extends2.default)({}, targetGroup, { children: targetChildren }); (0, _utils2.updateNodeParentAndDepth)(updatedTree, sourceNode, targetNode.id, targetNode.depth + 1); return (0, _extends2.default)({}, state, { rows: (0, _extends2.default)({}, state.rows, { tree: updatedTree }) }); }); // Update rows in the grid apiRef.current.updateRows(rowsToUpdate); apiRef.current.publishEvent('rowsSet'); } } /** * Handles moving group nodes (and all their descendants) between different parents. */ class CrossParentGroupOperation extends _reorderExecutor.BaseReorderOperation { operationType = 'cross-parent-group'; detectOperation(ctx) { if (ctx.dropPosition === 'inside') { return null; } const { sourceRowId, placeholderIndex, sortedFilteredRowIds, rowTree, apiRef, setTreeDataPath } = ctx; const sourceNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, sourceRowId); if (!sourceNode || sourceNode.type !== 'group') { return null; } if (!setTreeDataPath) { (0, _utils2.displaySetTreeDataPathWarning)('Cross-parent reordering'); } // Find target node let targetIndex = placeholderIndex; if (targetIndex === sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) { targetIndex = sortedFilteredRowIds.length - 1; } if (targetIndex < 0) { return null; } const targetNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, sortedFilteredRowIds[targetIndex]); if (!targetNode) { return null; } if (sourceNode.parent === targetNode.parent) { return null; } if ((0, _utils.isDescendantOf)(targetNode, sourceNode, rowTree)) { return null; } const actualTargetIndex = (0, _utils.calculateTargetIndex)(sourceNode, targetNode, placeholderIndex >= sortedFilteredRowIds.length, rowTree); return { sourceNode, targetNode, actualTargetIndex, isLastChild: placeholderIndex >= sortedFilteredRowIds.length, operationType: this.operationType }; } async executeOperation(operation, ctx) { const { sourceNode, targetNode, actualTargetIndex } = operation; const { apiRef } = ctx; const rowTree = (0, _xDataGrid.gridRowTreeSelector)(apiRef); // Calculate new base path for the moved group const targetParentNode = rowTree[targetNode.parent]; const newBasePath = (0, _utils2.buildTreeDataPath)(targetParentNode, rowTree); // Calculate the original base path depth const sourceParentNode = rowTree[sourceNode.parent]; const sourceBasePath = (0, _utils2.buildTreeDataPath)(sourceParentNode, rowTree); // Update group hierarchy paths const updates = await (0, _utils2.updateGroupHierarchyPaths)(sourceNode, sourceBasePath, newBasePath, ctx); if (updates.length > 0) { // Update tree structure (partial moves are allowed) apiRef.current.setState(state => { const updatedTree = (0, _extends2.default)({}, state.rows.tree); // Remove from source parent (0, _utils2.removeNodeFromSourceParent)(updatedTree, sourceNode); // Add to target parent const targetParent = updatedTree[targetNode.parent]; const targetChildren = [...targetParent.children]; targetChildren.splice(actualTargetIndex, 0, sourceNode.id); updatedTree[targetNode.parent] = (0, _extends2.default)({}, targetParent, { children: targetChildren }); const newParentNode = updatedTree[targetNode.parent]; const newGroupDepth = newParentNode.depth + 1; (0, _utils2.updateNodeParentAndDepth)(updatedTree, sourceNode, targetNode.parent, newGroupDepth); return (0, _extends2.default)({}, state, { rows: (0, _extends2.default)({}, state.rows, { tree: updatedTree }) }); }); apiRef.current.updateRows(updates); apiRef.current.publishEvent('rowsSet'); } } } const treeDataReorderExecutor = exports.treeDataReorderExecutor = new _reorderExecutor.RowReorderExecutor([new SameParentSwapOperation(), new CrossParentLeafOperation(), new DropOnLeafOperation(), new DropOnGroupOperation(), new CrossParentGroupOperation()]);