@mui/x-data-grid-premium 8.10.2 → 8.11.1

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +214 -13
  2. package/DataGridPremium/DataGridPremium.js +6 -4
  3. package/DataGridPremium/useDataGridPremiumComponent.d.ts +2 -1
  4. package/DataGridPremium/useDataGridPremiumComponent.js +2 -2
  5. package/esm/DataGridPremium/DataGridPremium.js +6 -4
  6. package/esm/DataGridPremium/useDataGridPremiumComponent.d.ts +2 -1
  7. package/esm/DataGridPremium/useDataGridPremiumComponent.js +2 -2
  8. package/esm/hooks/features/aggregation/wrapColumnWithAggregation.d.ts +1 -0
  9. package/esm/hooks/features/clipboard/useGridClipboardImport.js +1 -1
  10. package/esm/hooks/features/export/serializer/setupExcelExportWebWorker.js +1 -2
  11. package/esm/hooks/features/rowGrouping/createGroupingColDef.d.ts +2 -1
  12. package/esm/hooks/features/rowGrouping/createGroupingColDef.js +31 -7
  13. package/esm/hooks/features/rowGrouping/gridRowGroupingInterfaces.d.ts +1 -0
  14. package/esm/hooks/features/rowGrouping/gridRowGroupingUtils.js +5 -1
  15. package/esm/hooks/features/rowGrouping/useGridRowGrouping.d.ts +1 -1
  16. package/esm/hooks/features/rowGrouping/useGridRowGrouping.js +48 -2
  17. package/esm/hooks/features/rowReorder/operations.d.ts +40 -0
  18. package/esm/hooks/features/rowReorder/operations.js +535 -0
  19. package/esm/hooks/features/rowReorder/reorderExecutor.d.ts +15 -0
  20. package/esm/hooks/features/rowReorder/reorderExecutor.js +25 -0
  21. package/esm/hooks/features/rowReorder/reorderValidator.d.ts +16 -0
  22. package/esm/hooks/features/rowReorder/reorderValidator.js +116 -0
  23. package/esm/hooks/features/rowReorder/types.d.ts +42 -0
  24. package/esm/hooks/features/rowReorder/types.js +1 -0
  25. package/esm/hooks/features/rowReorder/utils.d.ts +127 -0
  26. package/esm/hooks/features/rowReorder/utils.js +343 -0
  27. package/esm/hooks/features/rows/useGridRowsOverridableMethods.d.ts +7 -0
  28. package/esm/hooks/features/rows/useGridRowsOverridableMethods.js +52 -0
  29. package/esm/index.js +1 -1
  30. package/esm/models/gridGroupingValueSetter.d.ts +14 -0
  31. package/esm/models/gridGroupingValueSetter.js +1 -0
  32. package/esm/models/index.d.ts +1 -0
  33. package/esm/models/index.js +1 -0
  34. package/esm/typeOverloads/modules.d.ts +7 -1
  35. package/hooks/features/aggregation/wrapColumnWithAggregation.d.ts +1 -0
  36. package/hooks/features/clipboard/useGridClipboardImport.js +1 -1
  37. package/hooks/features/export/serializer/setupExcelExportWebWorker.js +1 -2
  38. package/hooks/features/rowGrouping/createGroupingColDef.d.ts +2 -1
  39. package/hooks/features/rowGrouping/createGroupingColDef.js +31 -7
  40. package/hooks/features/rowGrouping/gridRowGroupingInterfaces.d.ts +1 -0
  41. package/hooks/features/rowGrouping/gridRowGroupingUtils.js +5 -1
  42. package/hooks/features/rowGrouping/useGridRowGrouping.d.ts +1 -1
  43. package/hooks/features/rowGrouping/useGridRowGrouping.js +46 -0
  44. package/hooks/features/rowReorder/operations.d.ts +40 -0
  45. package/hooks/features/rowReorder/operations.js +546 -0
  46. package/hooks/features/rowReorder/reorderExecutor.d.ts +15 -0
  47. package/hooks/features/rowReorder/reorderExecutor.js +31 -0
  48. package/hooks/features/rowReorder/reorderValidator.d.ts +16 -0
  49. package/hooks/features/rowReorder/reorderValidator.js +122 -0
  50. package/hooks/features/rowReorder/types.d.ts +42 -0
  51. package/hooks/features/rowReorder/types.js +5 -0
  52. package/hooks/features/rowReorder/utils.d.ts +127 -0
  53. package/hooks/features/rowReorder/utils.js +360 -0
  54. package/hooks/features/rows/useGridRowsOverridableMethods.d.ts +7 -0
  55. package/hooks/features/rows/useGridRowsOverridableMethods.js +60 -0
  56. package/index.js +1 -1
  57. package/models/gridGroupingValueSetter.d.ts +14 -0
  58. package/models/gridGroupingValueSetter.js +5 -0
  59. package/models/index.d.ts +1 -0
  60. package/models/index.js +11 -0
  61. package/package.json +14 -14
  62. package/typeOverloads/modules.d.ts +7 -1
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.rowGroupingReorderValidator = void 0;
7
+ var _utils = require("./utils");
8
+ const validationRules = [
9
+ // ===== Basic invalid cases =====
10
+ {
11
+ name: 'same-position',
12
+ applies: ctx => ctx.sourceRowIndex === ctx.targetRowIndex,
13
+ isInvalid: () => true,
14
+ message: 'Source and target are the same'
15
+ }, {
16
+ name: 'adjacent-position',
17
+ applies: ctx => _utils.conditions.isAdjacentPosition(ctx),
18
+ isInvalid: () => true,
19
+ message: 'Source and target are adjacent'
20
+ }, {
21
+ name: 'group-to-leaf',
22
+ applies: _utils.conditions.isGroupToLeaf,
23
+ isInvalid: () => true,
24
+ message: 'Cannot drop group on leaf'
25
+ },
26
+ // ===== Group to Group Rules =====
27
+ {
28
+ name: 'group-to-group-above-leaf-belongs-to-source',
29
+ applies: ctx => _utils.conditions.isGroupToGroup(ctx) && _utils.conditions.isDropAbove(ctx) && _utils.conditions.prevIsLeaf(ctx),
30
+ isInvalid: _utils.conditions.prevBelongsToSource,
31
+ message: 'Previous leaf belongs to source group or its descendants'
32
+ }, {
33
+ name: 'group-to-group-above-invalid-depth',
34
+ applies: ctx => _utils.conditions.isGroupToGroup(ctx) && _utils.conditions.isDropAbove(ctx) && !_utils.conditions.sameDepth(ctx) && !(ctx.targetNode.depth < ctx.sourceNode.depth && (_utils.conditions.prevIsLeaf(ctx) || _utils.conditions.prevIsGroup(ctx) && _utils.conditions.prevDepthEqualsSource(ctx))),
35
+ isInvalid: () => true,
36
+ message: 'Invalid depth configuration for group above group'
37
+ }, {
38
+ name: 'group-to-group-above-different-parent-depth',
39
+ applies: ctx => _utils.conditions.isGroupToGroup(ctx) && _utils.conditions.isDropAbove(ctx) && _utils.conditions.prevIsGroup(ctx) && _utils.conditions.prevDepthEqualsSource(ctx) && _utils.conditions.targetGroupExpanded(ctx),
40
+ isInvalid: ctx => ctx.prevNode.depth !== ctx.sourceNode.depth,
41
+ message: 'Cannot reorder groups with different depths'
42
+ }, {
43
+ name: 'group-to-group-below-invalid-config',
44
+ applies: ctx => _utils.conditions.isGroupToGroup(ctx) && _utils.conditions.isDropBelow(ctx),
45
+ isInvalid: ctx => {
46
+ // Valid case 1: Same depth and target not expanded
47
+ if (_utils.conditions.sameDepth(ctx) && _utils.conditions.targetGroupCollapsed(ctx)) {
48
+ return false;
49
+ }
50
+ // Valid case 2: Target is parent level, expanded, with compatible first child
51
+ if (_utils.conditions.targetDepthIsSourceMinusOne(ctx) && _utils.conditions.targetGroupExpanded(ctx) && _utils.conditions.targetFirstChildIsGroupWithSourceDepth(ctx)) {
52
+ return false;
53
+ }
54
+ return true;
55
+ },
56
+ message: 'Invalid group below group configuration'
57
+ },
58
+ // ===== Leaf to Leaf Rules =====
59
+ {
60
+ name: 'leaf-to-leaf-different-depth',
61
+ applies: ctx => _utils.conditions.isLeafToLeaf(ctx) && !_utils.conditions.sameDepth(ctx),
62
+ isInvalid: () => true,
63
+ message: 'Leaves at different depths cannot be reordered'
64
+ }, {
65
+ name: 'leaf-to-leaf-invalid-below',
66
+ applies: ctx => _utils.conditions.isLeafToLeaf(ctx) && _utils.conditions.sameDepth(ctx) && !_utils.conditions.sameParent(ctx) && _utils.conditions.isDropBelow(ctx),
67
+ isInvalid: ctx => !(_utils.conditions.nextIsGroup(ctx) && ctx.sourceNode.depth > ctx.nextNode.depth) && !_utils.conditions.nextIsLeaf(ctx),
68
+ message: 'Invalid leaf below leaf configuration'
69
+ },
70
+ // ===== Leaf to Group Rules =====
71
+ {
72
+ name: 'leaf-to-group-above-no-prev-leaf',
73
+ applies: ctx => _utils.conditions.isLeafToGroup(ctx) && _utils.conditions.isDropAbove(ctx),
74
+ isInvalid: ctx => !_utils.conditions.hasPrevNode(ctx) || !_utils.conditions.prevIsLeaf(ctx),
75
+ message: 'No valid previous leaf for leaf above group'
76
+ }, {
77
+ name: 'leaf-to-group-above-depth-mismatch',
78
+ applies: ctx => _utils.conditions.isLeafToGroup(ctx) && _utils.conditions.isDropAbove(ctx) && _utils.conditions.prevIsLeaf(ctx) && !(ctx.sourceNode.depth > ctx.targetNode.depth && ctx.targetNode.depth === 0),
79
+ isInvalid: ctx => ctx.prevNode.depth !== ctx.sourceNode.depth,
80
+ message: 'Previous node depth mismatch for leaf above group'
81
+ }, {
82
+ name: 'leaf-to-group-below-collapsed',
83
+ applies: ctx => _utils.conditions.isLeafToGroup(ctx) && _utils.conditions.isDropBelow(ctx),
84
+ isInvalid: _utils.conditions.targetGroupCollapsed,
85
+ message: 'Cannot drop below collapsed group'
86
+ }, {
87
+ name: 'leaf-to-group-below-invalid-depth',
88
+ applies: ctx => _utils.conditions.isLeafToGroup(ctx) && _utils.conditions.isDropBelow(ctx) && _utils.conditions.targetGroupExpanded(ctx),
89
+ isInvalid: ctx => {
90
+ // Valid case 1: Target is parent level
91
+ if (ctx.sourceNode.depth > ctx.targetNode.depth && ctx.targetNode.depth === ctx.sourceNode.depth - 1) {
92
+ return false;
93
+ }
94
+ // Valid case 2: First child has same depth as source
95
+ if (_utils.conditions.targetFirstChildDepthEqualsSource(ctx)) {
96
+ return false;
97
+ }
98
+ return true;
99
+ },
100
+ message: 'Invalid depth configuration for leaf below group'
101
+ }];
102
+ class RowReorderValidator {
103
+ constructor(rules = validationRules) {
104
+ this.rules = rules;
105
+ }
106
+ addRule(rule) {
107
+ this.rules.push(rule);
108
+ }
109
+ removeRule(ruleName) {
110
+ this.rules = this.rules.filter(r => r.name !== ruleName);
111
+ }
112
+ validate(context) {
113
+ // Check all validation rules
114
+ for (const rule of this.rules) {
115
+ if (rule.applies(context) && rule.isInvalid(context)) {
116
+ return false;
117
+ }
118
+ }
119
+ return true;
120
+ }
121
+ }
122
+ const rowGroupingReorderValidator = exports.rowGroupingReorderValidator = new RowReorderValidator(validationRules);
@@ -0,0 +1,42 @@
1
+ import { GridRowId, GridTreeNode } from '@mui/x-data-grid-pro';
2
+ import type { GridRowTreeConfig } from '@mui/x-data-grid-pro';
3
+ import { RefObject } from '@mui/x-internals/types';
4
+ import { GridPrivateApiPremium } from "../../../models/gridApiPremium.js";
5
+ import { DataGridPremiumProcessedProps } from "../../../models/dataGridPremiumProps.js";
6
+ export type DropPosition = 'above' | 'below';
7
+ export type DragDirection = 'up' | 'down';
8
+ export interface ReorderValidationContext {
9
+ sourceNode: GridTreeNode;
10
+ targetNode: GridTreeNode;
11
+ prevNode: GridTreeNode | null;
12
+ nextNode: GridTreeNode | null;
13
+ rowTree: Record<GridRowId, GridTreeNode>;
14
+ dropPosition: DropPosition;
15
+ dragDirection: DragDirection;
16
+ targetRowIndex: number;
17
+ sourceRowIndex: number;
18
+ expandedSortedRowIndexLookup: Record<GridRowId, number>;
19
+ }
20
+ export interface ReorderExecutionContext {
21
+ sourceRowId: GridRowId;
22
+ placeholderIndex: number;
23
+ sortedFilteredRowIds: GridRowId[];
24
+ sortedFilteredRowIndexLookup: Record<GridRowId, number>;
25
+ rowTree: GridRowTreeConfig;
26
+ apiRef: RefObject<GridPrivateApiPremium>;
27
+ processRowUpdate?: DataGridPremiumProcessedProps['processRowUpdate'];
28
+ onProcessRowUpdateError?: DataGridPremiumProcessedProps['onProcessRowUpdateError'];
29
+ }
30
+ export interface ReorderOperation {
31
+ sourceNode: GridTreeNode;
32
+ targetNode: GridTreeNode;
33
+ actualTargetIndex: number;
34
+ isLastChild: boolean;
35
+ operationType: ReorderOperationType;
36
+ }
37
+ export interface ReorderScenario {
38
+ name: string;
39
+ detectOperation: (ctx: ReorderExecutionContext) => ReorderOperation | null;
40
+ execute: (operation: ReorderOperation, ctx: ReorderExecutionContext) => Promise<void> | void;
41
+ }
42
+ export type ReorderOperationType = 'same-parent-swap' | 'cross-parent-leaf' | 'cross-parent-group';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
@@ -0,0 +1,127 @@
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-pro';
3
+ import type { RowTreeBuilderGroupingCriterion } from '@mui/x-data-grid-pro/internals';
4
+ import type { ReorderValidationContext as Ctx, ReorderOperationType } from "./types.js";
5
+ import type { GridPrivateApiPremium } from "../../../models/gridApiPremium.js";
6
+ import { DataGridPremiumProcessedProps } from "../../../models/dataGridPremiumProps.js";
7
+ /**
8
+ * Reusable validation conditions for row reordering validation
9
+ */
10
+ export declare const conditions: {
11
+ isGroupToGroup: (ctx: Ctx) => boolean;
12
+ isLeafToLeaf: (ctx: Ctx) => boolean;
13
+ isLeafToGroup: (ctx: Ctx) => boolean;
14
+ isGroupToLeaf: (ctx: Ctx) => boolean;
15
+ isDropAbove: (ctx: Ctx) => boolean;
16
+ isDropBelow: (ctx: Ctx) => boolean;
17
+ sameDepth: (ctx: Ctx) => boolean;
18
+ sourceDepthGreater: (ctx: Ctx) => boolean;
19
+ targetDepthIsSourceMinusOne: (ctx: Ctx) => boolean;
20
+ sameParent: (ctx: Ctx) => boolean;
21
+ targetGroupExpanded: (ctx: Ctx) => boolean;
22
+ targetGroupCollapsed: (ctx: Ctx) => boolean;
23
+ hasPrevNode: (ctx: Ctx) => boolean;
24
+ hasNextNode: (ctx: Ctx) => boolean;
25
+ prevIsLeaf: (ctx: Ctx) => boolean;
26
+ prevIsGroup: (ctx: Ctx) => boolean;
27
+ nextIsLeaf: (ctx: Ctx) => boolean;
28
+ nextIsGroup: (ctx: Ctx) => boolean;
29
+ prevDepthEquals: (ctx: Ctx, depth: number) => boolean;
30
+ prevDepthEqualsSource: (ctx: Ctx) => boolean;
31
+ prevBelongsToSource: (ctx: Ctx) => boolean;
32
+ isAdjacentPosition: (ctx: Ctx) => boolean;
33
+ targetFirstChildIsGroupWithSourceDepth: (ctx: Ctx) => boolean;
34
+ targetFirstChildDepthEqualsSource: (ctx: Ctx) => boolean;
35
+ };
36
+ export declare function determineOperationType(sourceNode: GridTreeNode, targetNode: GridTreeNode): ReorderOperationType;
37
+ export declare function calculateTargetIndex(sourceNode: GridTreeNode, targetNode: GridTreeNode, isLastChild: boolean, rowTree: Record<GridRowId, GridTreeNode>): number;
38
+ export declare const getNodePathInTree: ({
39
+ id,
40
+ tree
41
+ }: {
42
+ id: GridRowId;
43
+ tree: GridRowTreeConfig;
44
+ }) => RowTreeBuilderGroupingCriterion[];
45
+ export declare const collectAllLeafDescendants: (groupNode: GridGroupNode, tree: GridRowTreeConfig) => GridRowId[];
46
+ /**
47
+ * Adjusts the target node based on specific reorder scenarios and constraints.
48
+ *
49
+ * This function applies scenario-specific logic to find the actual target node
50
+ * for operations, handling cases like:
51
+ * - Moving to collapsed groups
52
+ * - Depth-based adjustments
53
+ * - End-of-list positioning
54
+ *
55
+ * @param sourceNode The node being moved
56
+ * @param targetNode The initial target node
57
+ * @param targetIndex The index of the target node in the visible rows
58
+ * @param placeholderIndex The index where the placeholder appears
59
+ * @param sortedFilteredRowIds Array of visible row IDs in display order
60
+ * @param apiRef Reference to the grid API
61
+ * @returns Object containing the adjusted target node and last child flag
62
+ */
63
+ export declare function adjustTargetNode(sourceNode: GridTreeNode, targetNode: GridTreeNode, targetIndex: number, placeholderIndex: number, sortedFilteredRowIds: GridRowId[], apiRef: RefObject<GridPrivateApiPremium>): {
64
+ adjustedTargetNode: GridTreeNode;
65
+ isLastChild: boolean;
66
+ };
67
+ /**
68
+ * Finds an existing group node with the same groupingKey and groupingField under a parent.
69
+ *
70
+ * @param parentNode - The parent group node to search in
71
+ * @param groupingKey - The grouping key to match
72
+ * @param groupingField - The grouping field to match
73
+ * @param tree - The row tree configuration
74
+ * @returns The existing group node if found, null otherwise
75
+ */
76
+ export declare function findExistingGroupWithSameKey(parentNode: GridGroupNode, groupingKey: GridKeyValue, groupingField: string, tree: GridRowTreeConfig): GridGroupNode | null;
77
+ /**
78
+ * Removes empty ancestor groups from the tree after a row move operation.
79
+ * Walks up the tree from the given group, removing any empty groups encountered.
80
+ *
81
+ * @param groupId - The ID of the group to start checking from
82
+ * @param tree - The row tree configuration
83
+ * @param removedGroups - Set to track which groups have been removed
84
+ * @returns The number of root-level groups that were removed
85
+ */
86
+ export declare function removeEmptyAncestors(groupId: GridRowId, tree: GridRowTreeConfig, removedGroups: Set<GridRowId>): number;
87
+ export declare function handleProcessRowUpdateError(error: any, onProcessRowUpdateError?: DataGridPremiumProcessedProps['onProcessRowUpdateError']): void;
88
+ /**
89
+ * Handles batch row updates with partial failure tracking.
90
+ *
91
+ * This class is designed for operations that need to update multiple rows
92
+ * atomically (like moving entire groups), while gracefully handling cases
93
+ * where some updates succeed and others fail.
94
+ *
95
+ * @example
96
+ * ```tsx
97
+ * const updater = new BatchRowUpdater(processRowUpdate, onError);
98
+ *
99
+ * // Queue multiple updates
100
+ * updater.queueUpdate('row1', originalRow1, newRow1);
101
+ * updater.queueUpdate('row2', originalRow2, newRow2);
102
+ *
103
+ * // Execute all updates
104
+ * const { successful, failed, updates } = await updater.executeAll();
105
+ *
106
+ * // Handle results
107
+ * if (successful.length > 0) {
108
+ * apiRef.current.updateRows(updates);
109
+ * }
110
+ * ```
111
+ */
112
+ export declare class BatchRowUpdater {
113
+ private processRowUpdate;
114
+ private onProcessRowUpdateError;
115
+ private rowsToUpdate;
116
+ private originalRows;
117
+ private successfulRowIds;
118
+ private failedRowIds;
119
+ private pendingRowUpdates;
120
+ constructor(processRowUpdate: DataGridPremiumProcessedProps['processRowUpdate'] | undefined, onProcessRowUpdateError: DataGridPremiumProcessedProps['onProcessRowUpdateError'] | undefined);
121
+ queueUpdate(rowId: GridRowId, originalRow: GridValidRowModel, updatedRow: GridValidRowModel): void;
122
+ executeAll(): Promise<{
123
+ successful: GridRowId[];
124
+ failed: GridRowId[];
125
+ updates: GridValidRowModel[];
126
+ }>;
127
+ }
@@ -0,0 +1,360 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.BatchRowUpdater = void 0;
7
+ exports.adjustTargetNode = adjustTargetNode;
8
+ exports.calculateTargetIndex = calculateTargetIndex;
9
+ exports.conditions = exports.collectAllLeafDescendants = void 0;
10
+ exports.determineOperationType = determineOperationType;
11
+ exports.findExistingGroupWithSameKey = findExistingGroupWithSameKey;
12
+ exports.getNodePathInTree = void 0;
13
+ exports.handleProcessRowUpdateError = handleProcessRowUpdateError;
14
+ exports.removeEmptyAncestors = removeEmptyAncestors;
15
+ var _xDataGridPro = require("@mui/x-data-grid-pro");
16
+ var _warning = require("@mui/x-internals/warning");
17
+ // TODO: Share these conditions with the executor by making the contexts similar
18
+ /**
19
+ * Reusable validation conditions for row reordering validation
20
+ */
21
+ const conditions = exports.conditions = {
22
+ // Node type checks
23
+ isGroupToGroup: ctx => ctx.sourceNode.type === 'group' && ctx.targetNode.type === 'group',
24
+ isLeafToLeaf: ctx => ctx.sourceNode.type === 'leaf' && ctx.targetNode.type === 'leaf',
25
+ isLeafToGroup: ctx => ctx.sourceNode.type === 'leaf' && ctx.targetNode.type === 'group',
26
+ isGroupToLeaf: ctx => ctx.sourceNode.type === 'group' && ctx.targetNode.type === 'leaf',
27
+ // Drop position checks
28
+ isDropAbove: ctx => ctx.dropPosition === 'above',
29
+ isDropBelow: ctx => ctx.dropPosition === 'below',
30
+ // Depth checks
31
+ sameDepth: ctx => ctx.sourceNode.depth === ctx.targetNode.depth,
32
+ sourceDepthGreater: ctx => ctx.sourceNode.depth > ctx.targetNode.depth,
33
+ targetDepthIsSourceMinusOne: ctx => ctx.targetNode.depth === ctx.sourceNode.depth - 1,
34
+ // Parent checks
35
+ sameParent: ctx => ctx.sourceNode.parent === ctx.targetNode.parent,
36
+ // Node state checks
37
+ targetGroupExpanded: ctx => (ctx.targetNode.type === 'group' && ctx.targetNode.childrenExpanded) ?? false,
38
+ targetGroupCollapsed: ctx => ctx.targetNode.type === 'group' && !ctx.targetNode.childrenExpanded,
39
+ // Previous/Next node checks
40
+ hasPrevNode: ctx => ctx.prevNode !== null,
41
+ hasNextNode: ctx => ctx.nextNode !== null,
42
+ prevIsLeaf: ctx => ctx.prevNode?.type === 'leaf',
43
+ prevIsGroup: ctx => ctx.prevNode?.type === 'group',
44
+ nextIsLeaf: ctx => ctx.nextNode?.type === 'leaf',
45
+ nextIsGroup: ctx => ctx.nextNode?.type === 'group',
46
+ prevDepthEquals: (ctx, depth) => ctx.prevNode?.depth === depth,
47
+ prevDepthEqualsSource: ctx => ctx.prevNode?.depth === ctx.sourceNode.depth,
48
+ // Complex checks
49
+ prevBelongsToSource: ctx => {
50
+ if (!ctx.prevNode) {
51
+ return false;
52
+ }
53
+ // Check if prevNode.parent OR any of its ancestors === sourceNode.id
54
+ let currentId = ctx.prevNode.parent;
55
+ while (currentId) {
56
+ if (currentId === ctx.sourceNode.id) {
57
+ return true;
58
+ }
59
+ const node = ctx.rowTree[currentId];
60
+ if (!node) {
61
+ break;
62
+ }
63
+ currentId = node.parent;
64
+ }
65
+ return false;
66
+ },
67
+ // Position checks
68
+ isAdjacentPosition: ctx => {
69
+ const {
70
+ sourceRowIndex,
71
+ targetRowIndex,
72
+ dropPosition
73
+ } = ctx;
74
+ return dropPosition === 'above' && targetRowIndex === sourceRowIndex + 1 || dropPosition === 'below' && targetRowIndex === sourceRowIndex - 1;
75
+ },
76
+ // First child check
77
+ targetFirstChildIsGroupWithSourceDepth: ctx => {
78
+ if (ctx.targetNode.type !== 'group') {
79
+ return false;
80
+ }
81
+ const targetGroup = ctx.targetNode;
82
+ const firstChild = targetGroup.children?.[0] ? ctx.rowTree[targetGroup.children[0]] : null;
83
+ return firstChild?.type === 'group' && firstChild.depth === ctx.sourceNode.depth;
84
+ },
85
+ targetFirstChildDepthEqualsSource: ctx => {
86
+ if (ctx.targetNode.type !== 'group') {
87
+ return false;
88
+ }
89
+ const targetGroup = ctx.targetNode;
90
+ const firstChild = targetGroup.children?.[0] ? ctx.rowTree[targetGroup.children[0]] : null;
91
+ return firstChild ? firstChild.depth === ctx.sourceNode.depth : false;
92
+ }
93
+ };
94
+ function determineOperationType(sourceNode, targetNode) {
95
+ if (sourceNode.parent === targetNode.parent) {
96
+ return 'same-parent-swap';
97
+ }
98
+ if (sourceNode.type === 'leaf') {
99
+ return 'cross-parent-leaf';
100
+ }
101
+ return 'cross-parent-group';
102
+ }
103
+ function calculateTargetIndex(sourceNode, targetNode, isLastChild, rowTree) {
104
+ if (sourceNode.parent === targetNode.parent && !isLastChild) {
105
+ // Same parent: find target's position in parent's children
106
+ const parent = rowTree[sourceNode.parent];
107
+ return parent.children.findIndex(id => id === targetNode.id);
108
+ }
109
+ if (isLastChild) {
110
+ // Append at the end
111
+ const targetParent = rowTree[targetNode.parent];
112
+ return targetParent.children.length;
113
+ }
114
+
115
+ // Find position in target parent
116
+ const targetParent = rowTree[targetNode.parent];
117
+ const targetIndex = targetParent.children.findIndex(id => id === targetNode.id);
118
+ return targetIndex >= 0 ? targetIndex : 0;
119
+ }
120
+
121
+ // Get the path from a node to the root in the tree
122
+ const getNodePathInTree = ({
123
+ id,
124
+ tree
125
+ }) => {
126
+ const path = [];
127
+ let node = tree[id];
128
+ while (node.id !== _xDataGridPro.GRID_ROOT_GROUP_ID) {
129
+ path.push({
130
+ field: node.type === 'leaf' ? null : node.groupingField,
131
+ key: node.groupingKey
132
+ });
133
+ node = tree[node.parent];
134
+ }
135
+ path.reverse();
136
+ return path;
137
+ };
138
+
139
+ // Recursively collect all leaf node IDs from a group
140
+ exports.getNodePathInTree = getNodePathInTree;
141
+ const collectAllLeafDescendants = (groupNode, tree) => {
142
+ const leafIds = [];
143
+ const collectFromNode = nodeId => {
144
+ const node = tree[nodeId];
145
+ if (node.type === 'leaf') {
146
+ leafIds.push(nodeId);
147
+ } else if (node.type === 'group') {
148
+ node.children.forEach(collectFromNode);
149
+ }
150
+ };
151
+ groupNode.children.forEach(collectFromNode);
152
+ return leafIds;
153
+ };
154
+
155
+ /**
156
+ * Adjusts the target node based on specific reorder scenarios and constraints.
157
+ *
158
+ * This function applies scenario-specific logic to find the actual target node
159
+ * for operations, handling cases like:
160
+ * - Moving to collapsed groups
161
+ * - Depth-based adjustments
162
+ * - End-of-list positioning
163
+ *
164
+ * @param sourceNode The node being moved
165
+ * @param targetNode The initial target node
166
+ * @param targetIndex The index of the target node in the visible rows
167
+ * @param placeholderIndex The index where the placeholder appears
168
+ * @param sortedFilteredRowIds Array of visible row IDs in display order
169
+ * @param apiRef Reference to the grid API
170
+ * @returns Object containing the adjusted target node and last child flag
171
+ */
172
+ exports.collectAllLeafDescendants = collectAllLeafDescendants;
173
+ function adjustTargetNode(sourceNode, targetNode, targetIndex, placeholderIndex, sortedFilteredRowIds, apiRef) {
174
+ let adjustedTargetNode = targetNode;
175
+ let isLastChild = false;
176
+
177
+ // Handle end-of-list case
178
+ if (placeholderIndex >= sortedFilteredRowIds.length && sortedFilteredRowIds.length > 0) {
179
+ isLastChild = true;
180
+ }
181
+
182
+ // Case A and B adjustment: Move to last child of parent where target should be the node above
183
+ if (targetNode.type === 'group' && sourceNode.parent !== targetNode.parent && sourceNode.depth > targetNode.depth) {
184
+ // Find the first node with the same depth as source before target and quit early if a
185
+ // node with depth < source.depth is found
186
+ let i = targetIndex - 1;
187
+ while (i >= 0) {
188
+ const node = apiRef.current.getRowNode(sortedFilteredRowIds[i]);
189
+ if (node && node.depth < sourceNode.depth) {
190
+ break;
191
+ }
192
+ if (node && node.depth === sourceNode.depth) {
193
+ adjustedTargetNode = node;
194
+ break;
195
+ }
196
+ i -= 1;
197
+ }
198
+ }
199
+
200
+ // Case D adjustment: Leaf to group where we need previous leaf
201
+ if (sourceNode.type === 'leaf' && targetNode.type === 'group' && targetNode.depth < sourceNode.depth) {
202
+ isLastChild = true;
203
+ const prevIndex = placeholderIndex - 1;
204
+ if (prevIndex >= 0) {
205
+ const prevRowId = sortedFilteredRowIds[prevIndex];
206
+ const leafTargetNode = (0, _xDataGridPro.gridRowNodeSelector)(apiRef, prevRowId);
207
+ if (leafTargetNode && leafTargetNode.type === 'leaf') {
208
+ adjustedTargetNode = leafTargetNode;
209
+ }
210
+ }
211
+ }
212
+ return {
213
+ adjustedTargetNode,
214
+ isLastChild
215
+ };
216
+ }
217
+
218
+ /**
219
+ * Finds an existing group node with the same groupingKey and groupingField under a parent.
220
+ *
221
+ * @param parentNode - The parent group node to search in
222
+ * @param groupingKey - The grouping key to match
223
+ * @param groupingField - The grouping field to match
224
+ * @param tree - The row tree configuration
225
+ * @returns The existing group node if found, null otherwise
226
+ */
227
+ function findExistingGroupWithSameKey(parentNode, groupingKey, groupingField, tree) {
228
+ for (const childId of parentNode.children) {
229
+ const childNode = tree[childId];
230
+ if (childNode && childNode.type === 'group' && childNode.groupingKey === groupingKey && childNode.groupingField === groupingField) {
231
+ return childNode;
232
+ }
233
+ }
234
+ return null;
235
+ }
236
+
237
+ /**
238
+ * Removes empty ancestor groups from the tree after a row move operation.
239
+ * Walks up the tree from the given group, removing any empty groups encountered.
240
+ *
241
+ * @param groupId - The ID of the group to start checking from
242
+ * @param tree - The row tree configuration
243
+ * @param removedGroups - Set to track which groups have been removed
244
+ * @returns The number of root-level groups that were removed
245
+ */
246
+ function removeEmptyAncestors(groupId, tree, removedGroups) {
247
+ let rootLevelRemovals = 0;
248
+ let currentGroupId = groupId;
249
+ while (currentGroupId && currentGroupId !== _xDataGridPro.GRID_ROOT_GROUP_ID) {
250
+ const group = tree[currentGroupId];
251
+ if (!group) {
252
+ break;
253
+ }
254
+ const remainingChildren = group.children.filter(childId => !removedGroups.has(childId));
255
+ if (remainingChildren.length > 0) {
256
+ break;
257
+ }
258
+ if (group.depth === 0) {
259
+ rootLevelRemovals += 1;
260
+ }
261
+ removedGroups.add(currentGroupId);
262
+ currentGroupId = group.parent;
263
+ }
264
+ return rootLevelRemovals;
265
+ }
266
+ function handleProcessRowUpdateError(error, onProcessRowUpdateError) {
267
+ if (onProcessRowUpdateError) {
268
+ onProcessRowUpdateError(error);
269
+ } else if (process.env.NODE_ENV !== 'production') {
270
+ (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');
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Handles batch row updates with partial failure tracking.
276
+ *
277
+ * This class is designed for operations that need to update multiple rows
278
+ * atomically (like moving entire groups), while gracefully handling cases
279
+ * where some updates succeed and others fail.
280
+ *
281
+ * @example
282
+ * ```tsx
283
+ * const updater = new BatchRowUpdater(processRowUpdate, onError);
284
+ *
285
+ * // Queue multiple updates
286
+ * updater.queueUpdate('row1', originalRow1, newRow1);
287
+ * updater.queueUpdate('row2', originalRow2, newRow2);
288
+ *
289
+ * // Execute all updates
290
+ * const { successful, failed, updates } = await updater.executeAll();
291
+ *
292
+ * // Handle results
293
+ * if (successful.length > 0) {
294
+ * apiRef.current.updateRows(updates);
295
+ * }
296
+ * ```
297
+ */
298
+ class BatchRowUpdater {
299
+ rowsToUpdate = new Map();
300
+ originalRows = new Map();
301
+ successfulRowIds = new Set();
302
+ failedRowIds = new Set();
303
+ pendingRowUpdates = [];
304
+ constructor(processRowUpdate, onProcessRowUpdateError) {
305
+ this.processRowUpdate = processRowUpdate;
306
+ this.onProcessRowUpdateError = onProcessRowUpdateError;
307
+ }
308
+ queueUpdate(rowId, originalRow, updatedRow) {
309
+ this.originalRows.set(rowId, originalRow);
310
+ this.rowsToUpdate.set(rowId, updatedRow);
311
+ }
312
+ async executeAll() {
313
+ const rowIds = Array.from(this.rowsToUpdate.keys());
314
+ if (rowIds.length === 0) {
315
+ return {
316
+ successful: [],
317
+ failed: [],
318
+ updates: []
319
+ };
320
+ }
321
+
322
+ // Handle each row update, tracking success/failure
323
+ const handleRowUpdate = async rowId => {
324
+ const newRow = this.rowsToUpdate.get(rowId);
325
+ const oldRow = this.originalRows.get(rowId);
326
+ try {
327
+ if (typeof this.processRowUpdate === 'function') {
328
+ const params = {
329
+ rowId,
330
+ previousRow: oldRow,
331
+ updatedRow: newRow
332
+ };
333
+ const finalRow = await this.processRowUpdate(newRow, oldRow, params);
334
+ this.pendingRowUpdates.push(finalRow || newRow);
335
+ this.successfulRowIds.add(rowId);
336
+ } else {
337
+ this.pendingRowUpdates.push(newRow);
338
+ this.successfulRowIds.add(rowId);
339
+ }
340
+ } catch (error) {
341
+ this.failedRowIds.add(rowId);
342
+ handleProcessRowUpdateError(error, this.onProcessRowUpdateError);
343
+ }
344
+ };
345
+
346
+ // Use Promise.all with wrapped promises to avoid Promise.allSettled (browser support)
347
+ const promises = rowIds.map(rowId => {
348
+ return new Promise(resolve => {
349
+ handleRowUpdate(rowId).then(resolve).catch(resolve);
350
+ });
351
+ });
352
+ await Promise.all(promises);
353
+ return {
354
+ successful: Array.from(this.successfulRowIds),
355
+ failed: Array.from(this.failedRowIds),
356
+ updates: this.pendingRowUpdates
357
+ };
358
+ }
359
+ }
360
+ exports.BatchRowUpdater = BatchRowUpdater;
@@ -0,0 +1,7 @@
1
+ import { GridRowId } from '@mui/x-data-grid-pro';
2
+ import type { RefObject } from '@mui/x-internals/types';
3
+ import type { GridPrivateApiPremium } from "../../../models/gridApiPremium.js";
4
+ import type { DataGridPremiumProcessedProps } from "../../../models/dataGridPremiumProps.js";
5
+ export declare const useGridRowsOverridableMethods: (apiRef: RefObject<GridPrivateApiPremium>, props: Pick<DataGridPremiumProcessedProps, "processRowUpdate" | "onProcessRowUpdateError">) => {
6
+ setRowIndex: (sourceRowId: GridRowId, targetOriginalIndex: number) => Promise<void>;
7
+ };