@mui/x-data-grid-premium 8.7.0 → 8.9.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 +199 -24
- package/DataGridPremium/DataGridPremium.js +1 -1
- package/DataGridPremium/useDataGridPremiumComponent.js +19 -7
- package/DataGridPremium/useDataGridPremiumProps.js +2 -1
- package/esm/DataGridPremium/DataGridPremium.js +1 -1
- package/esm/DataGridPremium/useDataGridPremiumComponent.js +21 -8
- package/esm/DataGridPremium/useDataGridPremiumProps.js +2 -1
- package/esm/hooks/features/aggregation/createAggregationLookup.d.ts +9 -4
- package/esm/hooks/features/aggregation/createAggregationLookup.js +78 -41
- package/esm/hooks/features/aggregation/gridAggregationFunctions.js +2 -2
- package/esm/hooks/features/aggregation/gridAggregationInterfaces.d.ts +13 -1
- package/esm/hooks/features/aggregation/gridAggregationUtils.d.ts +2 -1
- package/esm/hooks/features/aggregation/gridAggregationUtils.js +2 -1
- package/esm/hooks/features/aggregation/useGridAggregation.js +94 -18
- package/esm/hooks/features/pivoting/gridPivotingInterfaces.d.ts +12 -2
- package/esm/hooks/features/pivoting/useGridPivoting.d.ts +2 -1
- package/esm/hooks/features/pivoting/useGridPivoting.js +57 -35
- package/esm/hooks/features/pivoting/utils.d.ts +3 -1
- package/esm/hooks/features/pivoting/utils.js +22 -14
- package/esm/hooks/features/rowGrouping/gridRowGroupingUtils.js +5 -1
- package/esm/hooks/features/rowGrouping/useGridDataSourceRowGroupingPreProcessors.js +2 -2
- package/esm/index.js +3 -3
- package/esm/typeOverloads/modules.d.ts +2 -1
- package/hooks/features/aggregation/createAggregationLookup.d.ts +9 -4
- package/hooks/features/aggregation/createAggregationLookup.js +79 -41
- package/hooks/features/aggregation/gridAggregationFunctions.js +2 -2
- package/hooks/features/aggregation/gridAggregationInterfaces.d.ts +13 -1
- package/hooks/features/aggregation/gridAggregationUtils.d.ts +2 -1
- package/hooks/features/aggregation/gridAggregationUtils.js +4 -2
- package/hooks/features/aggregation/useGridAggregation.js +92 -16
- package/hooks/features/pivoting/gridPivotingInterfaces.d.ts +12 -2
- package/hooks/features/pivoting/useGridPivoting.d.ts +2 -1
- package/hooks/features/pivoting/useGridPivoting.js +60 -37
- package/hooks/features/pivoting/utils.d.ts +3 -1
- package/hooks/features/pivoting/utils.js +22 -14
- package/hooks/features/rowGrouping/gridRowGroupingUtils.js +5 -1
- package/hooks/features/rowGrouping/useGridDataSourceRowGroupingPreProcessors.js +1 -1
- package/index.js +3 -3
- package/package.json +6 -6
- package/typeOverloads/modules.d.ts +2 -1
|
@@ -1,51 +1,67 @@
|
|
|
1
|
-
import { gridColumnLookupSelector,
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { gridColumnLookupSelector, gridRowTreeSelector, GRID_ROOT_GROUP_ID, gridRowsLookupSelector } from '@mui/x-data-grid-pro';
|
|
2
|
+
import { getVisibleRows } from '@mui/x-data-grid/internals';
|
|
3
|
+
export const shouldApplySorting = (aggregationRules, aggregatedFields) => {
|
|
4
|
+
return aggregatedFields.some(field => aggregationRules[field].aggregationFunction.applySorting);
|
|
5
|
+
};
|
|
6
|
+
const getGroupAggregatedValue = (groupId, apiRef, aggregationRowsScope, aggregatedFields, aggregationRules, position, applySorting, valueGetters, publicApi, groupAggregatedValuesLookup) => {
|
|
5
7
|
const groupAggregationLookup = {};
|
|
6
8
|
const aggregatedValues = [];
|
|
9
|
+
for (let i = 0; i < aggregatedFields.length; i += 1) {
|
|
10
|
+
aggregatedValues[i] = {
|
|
11
|
+
aggregatedField: aggregatedFields[i],
|
|
12
|
+
values: []
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const rowTree = gridRowTreeSelector(apiRef);
|
|
16
|
+
const rowLookup = gridRowsLookupSelector(apiRef);
|
|
17
|
+
const isPivotActive = apiRef.current.state.pivoting.active;
|
|
7
18
|
const rowIds = apiRef.current.getRowGroupChildren({
|
|
8
|
-
groupId
|
|
19
|
+
groupId,
|
|
20
|
+
applySorting,
|
|
21
|
+
directChildrenOnly: true,
|
|
22
|
+
skipAutoGeneratedRows: false,
|
|
23
|
+
applyFiltering: aggregationRowsScope === 'filtered'
|
|
9
24
|
});
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// If the row is a group, we want to aggregate based on its children
|
|
17
|
-
// For instance in the following tree, we want the aggregated values of A to be based on A.A, A.B.A and A.B.B but not A.B
|
|
18
|
-
// A
|
|
19
|
-
// A.A
|
|
20
|
-
// A.B
|
|
21
|
-
// A.B.A
|
|
22
|
-
// A.B.B
|
|
23
|
-
const rowNode = gridRowNodeSelector(apiRef, rowId);
|
|
25
|
+
for (let i = 0; i < rowIds.length; i += 1) {
|
|
26
|
+
const rowId = rowIds[i];
|
|
27
|
+
const rowNode = rowTree[rowId];
|
|
24
28
|
if (rowNode.type === 'group') {
|
|
25
|
-
|
|
29
|
+
// MERGE EXISTING VALUES FROM THE LOOKUP TABLE
|
|
30
|
+
const childGroupValues = groupAggregatedValuesLookup.get(rowId);
|
|
31
|
+
if (childGroupValues) {
|
|
32
|
+
for (let j = 0; j < aggregatedFields.length; j += 1) {
|
|
33
|
+
aggregatedValues[j].values = aggregatedValues[j].values.concat(childGroupValues[j].values);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
continue;
|
|
26
37
|
}
|
|
27
|
-
const row =
|
|
38
|
+
const row = rowLookup[rowId];
|
|
28
39
|
for (let j = 0; j < aggregatedFields.length; j += 1) {
|
|
29
40
|
const aggregatedField = aggregatedFields[j];
|
|
30
41
|
const columnAggregationRules = aggregationRules[aggregatedField];
|
|
31
42
|
const aggregationFunction = columnAggregationRules.aggregationFunction;
|
|
32
43
|
const field = aggregatedField;
|
|
33
|
-
|
|
34
|
-
aggregatedValues[j] = {
|
|
35
|
-
aggregatedField,
|
|
36
|
-
values: []
|
|
37
|
-
};
|
|
38
|
-
}
|
|
44
|
+
let value;
|
|
39
45
|
if (typeof aggregationFunction.getCellValue === 'function') {
|
|
40
|
-
|
|
46
|
+
value = aggregationFunction.getCellValue({
|
|
47
|
+
field,
|
|
41
48
|
row
|
|
42
|
-
})
|
|
49
|
+
});
|
|
50
|
+
} else if (isPivotActive) {
|
|
51
|
+
// Since we know that pivoted fields are flat, we can use the row directly, and save lots of processing time
|
|
52
|
+
value = row[field];
|
|
43
53
|
} else {
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
if (!row) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const valueGetter = valueGetters[aggregatedField];
|
|
58
|
+
value = valueGetter(row);
|
|
59
|
+
}
|
|
60
|
+
if (value !== undefined) {
|
|
61
|
+
aggregatedValues[j].values.push(value);
|
|
46
62
|
}
|
|
47
63
|
}
|
|
48
|
-
}
|
|
64
|
+
}
|
|
49
65
|
for (let i = 0; i < aggregatedValues.length; i += 1) {
|
|
50
66
|
const {
|
|
51
67
|
aggregatedField,
|
|
@@ -56,13 +72,16 @@ const getGroupAggregatedValue = (groupId, apiRef, aggregationRowsScope, aggregat
|
|
|
56
72
|
values,
|
|
57
73
|
groupId,
|
|
58
74
|
field: aggregatedField // Added per user request in https://github.com/mui/mui-x/issues/6995#issuecomment-1327423455
|
|
59
|
-
});
|
|
75
|
+
}, publicApi);
|
|
60
76
|
groupAggregationLookup[aggregatedField] = {
|
|
61
77
|
position,
|
|
62
78
|
value
|
|
63
79
|
};
|
|
64
80
|
}
|
|
65
|
-
return
|
|
81
|
+
return {
|
|
82
|
+
groupAggregationLookup,
|
|
83
|
+
aggregatedValues
|
|
84
|
+
};
|
|
66
85
|
};
|
|
67
86
|
const getGroupAggregatedValueDataSource = (groupId, apiRef, aggregatedFields, position) => {
|
|
68
87
|
const groupAggregationLookup = {};
|
|
@@ -77,21 +96,37 @@ const getGroupAggregatedValueDataSource = (groupId, apiRef, aggregatedFields, po
|
|
|
77
96
|
};
|
|
78
97
|
export const createAggregationLookup = ({
|
|
79
98
|
apiRef,
|
|
80
|
-
|
|
99
|
+
aggregationRules,
|
|
100
|
+
aggregatedFields,
|
|
81
101
|
aggregationRowsScope,
|
|
82
102
|
getAggregationPosition,
|
|
83
|
-
isDataSource
|
|
103
|
+
isDataSource,
|
|
104
|
+
applySorting = false
|
|
84
105
|
}) => {
|
|
85
|
-
const aggregationRules = getAggregationRules(gridColumnLookupSelector(apiRef), gridAggregationModelSelector(apiRef), aggregationFunctions, isDataSource);
|
|
86
|
-
const aggregatedFields = Object.keys(aggregationRules);
|
|
87
106
|
if (aggregatedFields.length === 0) {
|
|
88
107
|
return {};
|
|
89
108
|
}
|
|
109
|
+
const columnsLookup = gridColumnLookupSelector(apiRef);
|
|
110
|
+
const valueGetters = {};
|
|
111
|
+
for (let i = 0; i < aggregatedFields.length; i += 1) {
|
|
112
|
+
const field = aggregatedFields[i];
|
|
113
|
+
const column = columnsLookup[field];
|
|
114
|
+
const valueGetter = row => apiRef.current.getRowValue(row, column);
|
|
115
|
+
valueGetters[field] = valueGetter;
|
|
116
|
+
}
|
|
90
117
|
const aggregationLookup = {};
|
|
91
118
|
const rowTree = gridRowTreeSelector(apiRef);
|
|
119
|
+
const groupAggregatedValuesLookup = new Map();
|
|
120
|
+
const {
|
|
121
|
+
rowIdToIndexMap
|
|
122
|
+
} = getVisibleRows(apiRef);
|
|
92
123
|
const createGroupAggregationLookup = groupNode => {
|
|
93
|
-
|
|
94
|
-
|
|
124
|
+
let children = groupNode.children;
|
|
125
|
+
if (applySorting) {
|
|
126
|
+
children = children.toSorted((a, b) => rowIdToIndexMap.get(a) - rowIdToIndexMap.get(b));
|
|
127
|
+
}
|
|
128
|
+
for (let i = 0; i < children.length; i += 1) {
|
|
129
|
+
const childId = children[i];
|
|
95
130
|
const childNode = rowTree[childId];
|
|
96
131
|
if (childNode.type === 'group') {
|
|
97
132
|
createGroupAggregationLookup(childNode);
|
|
@@ -102,7 +137,9 @@ export const createAggregationLookup = ({
|
|
|
102
137
|
if (isDataSource) {
|
|
103
138
|
aggregationLookup[groupNode.id] = getGroupAggregatedValueDataSource(groupNode.id, apiRef, aggregatedFields, position);
|
|
104
139
|
} else if (groupNode.children.length) {
|
|
105
|
-
|
|
140
|
+
const result = getGroupAggregatedValue(groupNode.id, apiRef, aggregationRowsScope, aggregatedFields, aggregationRules, position, applySorting, valueGetters, apiRef.current, groupAggregatedValuesLookup);
|
|
141
|
+
aggregationLookup[groupNode.id] = result.groupAggregationLookup;
|
|
142
|
+
groupAggregatedValuesLookup.set(groupNode.id, result.aggregatedValues);
|
|
106
143
|
}
|
|
107
144
|
}
|
|
108
145
|
};
|
|
@@ -6,7 +6,7 @@ const sumAgg = {
|
|
|
6
6
|
let sum = 0;
|
|
7
7
|
for (let i = 0; i < values.length; i += 1) {
|
|
8
8
|
const value = values[i];
|
|
9
|
-
if (
|
|
9
|
+
if (typeof value === 'number' && !Number.isNaN(value)) {
|
|
10
10
|
sum += value;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
@@ -25,7 +25,7 @@ const avgAgg = {
|
|
|
25
25
|
let valuesCount = 0;
|
|
26
26
|
for (let i = 0; i < values.length; i += 1) {
|
|
27
27
|
const value = values[i];
|
|
28
|
-
if (
|
|
28
|
+
if (typeof value === 'number' && !Number.isNaN(value)) {
|
|
29
29
|
valuesCount += 1;
|
|
30
30
|
sum += value;
|
|
31
31
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GridRowId, GridRowModel, GridColDef, GridValueFormatter } from '@mui/x-data-grid-pro';
|
|
2
|
+
import { GridApiPremium } from "../../../models/gridApiPremium.js";
|
|
2
3
|
export interface GridAggregationState {
|
|
3
4
|
model: GridAggregationModel;
|
|
4
5
|
lookup: GridAggregationLookup;
|
|
@@ -28,6 +29,10 @@ export interface GridAggregationGetCellValueParams {
|
|
|
28
29
|
* The row model of the row that the current cell belongs to.
|
|
29
30
|
*/
|
|
30
31
|
row: GridRowModel;
|
|
32
|
+
/**
|
|
33
|
+
* The field of the cell that the aggregation function is applied to.
|
|
34
|
+
*/
|
|
35
|
+
field: GridColDef['field'];
|
|
31
36
|
}
|
|
32
37
|
/**
|
|
33
38
|
* Grid aggregation function definition interface.
|
|
@@ -39,9 +44,10 @@ export interface GridAggregationFunction<V = any, AV = V> {
|
|
|
39
44
|
* Function that takes the current cell values and generates the aggregated value.
|
|
40
45
|
* @template V, AV
|
|
41
46
|
* @param {GridAggregationParams<V>} params The params of the current aggregated cell.
|
|
47
|
+
* @param {GridApiPremium} api The grid API.
|
|
42
48
|
* @returns {AV} The aggregated value.
|
|
43
49
|
*/
|
|
44
|
-
apply: (params: GridAggregationParams<V
|
|
50
|
+
apply: (params: GridAggregationParams<V>, api: GridApiPremium) => AV | null | undefined;
|
|
45
51
|
/**
|
|
46
52
|
* Label of the aggregation function.
|
|
47
53
|
* Used for adding a label to the footer of the grouping column when this aggregation function is the only one being used.
|
|
@@ -72,6 +78,12 @@ export interface GridAggregationFunction<V = any, AV = V> {
|
|
|
72
78
|
* @returns {V} The value of the cell that will be passed to the aggregation `apply` function
|
|
73
79
|
*/
|
|
74
80
|
getCellValue?: (params: GridAggregationGetCellValueParams) => V;
|
|
81
|
+
/**
|
|
82
|
+
* Indicates if the aggregation function depends on rows being in a sorted order.
|
|
83
|
+
* If `true`, the values provided to `apply` will be sorted.
|
|
84
|
+
* @default false
|
|
85
|
+
*/
|
|
86
|
+
applySorting?: boolean;
|
|
75
87
|
}
|
|
76
88
|
/**
|
|
77
89
|
* Grid aggregation function data source definition interface.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RefObject } from '@mui/x-internals/types';
|
|
2
|
-
import { GridColDef, GridRowId } from '@mui/x-data-grid-pro';
|
|
2
|
+
import { GridColDef, GridRowId, GridGroupNode } from '@mui/x-data-grid-pro';
|
|
3
3
|
import { GridColumnRawLookup, GridHydrateRowsValue } from '@mui/x-data-grid-pro/internals';
|
|
4
4
|
import { GridAggregationFunction, GridAggregationFunctionDataSource, GridAggregationModel, GridAggregationRule, GridAggregationRules } from "./gridAggregationInterfaces.js";
|
|
5
5
|
import { GridStatePremium } from "../../../models/gridStatePremium.js";
|
|
@@ -59,4 +59,5 @@ export declare const getAggregationFunctionLabel: ({
|
|
|
59
59
|
apiRef: RefObject<GridApiPremium>;
|
|
60
60
|
aggregationRule: GridAggregationRule;
|
|
61
61
|
}) => string;
|
|
62
|
+
export declare const defaultGetAggregationPosition: (groupNode: GridGroupNode) => "inline" | "footer";
|
|
62
63
|
export {};
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
4
4
|
import * as React from 'react';
|
|
5
|
-
import { gridColumnLookupSelector, useGridEvent, useGridApiMethod } from '@mui/x-data-grid-pro';
|
|
5
|
+
import { gridColumnLookupSelector, useGridEvent, useGridApiMethod, useRunOncePerLoop, gridRenderContextSelector, gridVisibleColumnFieldsSelector, gridSortModelSelector } from '@mui/x-data-grid-pro';
|
|
6
6
|
import { useGridRegisterPipeProcessor } from '@mui/x-data-grid-pro/internals';
|
|
7
7
|
import { gridAggregationModelSelector } from "./gridAggregationSelectors.js";
|
|
8
8
|
import { getAggregationRules, mergeStateWithAggregationModel, areAggregationRulesEqual } from "./gridAggregationUtils.js";
|
|
9
|
-
import { createAggregationLookup } from "./createAggregationLookup.js";
|
|
9
|
+
import { createAggregationLookup, shouldApplySorting } from "./createAggregationLookup.js";
|
|
10
10
|
export const aggregationStateInitializer = (state, props, apiRef) => {
|
|
11
11
|
apiRef.current.caches.aggregation = {
|
|
12
12
|
rulesOnLastColumnHydration: {},
|
|
@@ -36,20 +36,95 @@ export const useGridAggregation = (apiRef, props) => {
|
|
|
36
36
|
apiRef.current.setState(mergeStateWithAggregationModel(model));
|
|
37
37
|
}
|
|
38
38
|
}, [apiRef]);
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
39
|
+
const abortControllerRef = React.useRef(null);
|
|
40
|
+
const applyAggregation = React.useCallback(reason => {
|
|
41
|
+
// Abort previous if any
|
|
42
|
+
if (abortControllerRef.current) {
|
|
43
|
+
abortControllerRef.current.abort();
|
|
44
|
+
}
|
|
45
|
+
const abortController = new AbortController();
|
|
46
|
+
abortControllerRef.current = abortController;
|
|
47
|
+
const aggregationRules = getAggregationRules(gridColumnLookupSelector(apiRef), gridAggregationModelSelector(apiRef), props.aggregationFunctions, !!props.dataSource);
|
|
48
|
+
const aggregatedFields = Object.keys(aggregationRules);
|
|
49
|
+
const needsSorting = shouldApplySorting(aggregationRules, aggregatedFields);
|
|
50
|
+
if (reason === 'sort' && !needsSorting) {
|
|
51
|
+
// no need to re-apply aggregation on `sortedRowsSet` if sorting is not needed
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const renderContext = gridRenderContextSelector(apiRef);
|
|
55
|
+
const visibleColumns = gridVisibleColumnFieldsSelector(apiRef);
|
|
56
|
+
const chunks = [];
|
|
57
|
+
const visibleAggregatedFields = visibleColumns.slice(renderContext.firstColumnIndex, renderContext.lastColumnIndex + 1).filter(field => aggregatedFields.includes(field));
|
|
58
|
+
if (visibleAggregatedFields.length > 0) {
|
|
59
|
+
chunks.push(visibleAggregatedFields);
|
|
60
|
+
}
|
|
61
|
+
const otherAggregatedFields = aggregatedFields.filter(field => !visibleAggregatedFields.includes(field));
|
|
62
|
+
const chunkSize = 20; // columns per chunk
|
|
63
|
+
for (let i = 0; i < otherAggregatedFields.length; i += chunkSize) {
|
|
64
|
+
chunks.push(otherAggregatedFields.slice(i, i + chunkSize));
|
|
65
|
+
}
|
|
66
|
+
let chunkIndex = 0;
|
|
67
|
+
const aggregationLookup = {};
|
|
68
|
+
let chunkStartTime = performance.now();
|
|
69
|
+
const timeLimit = 1000 / 120;
|
|
70
|
+
const processChunk = () => {
|
|
71
|
+
if (abortController.signal.aborted) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const currentChunk = chunks[chunkIndex];
|
|
75
|
+
if (!currentChunk) {
|
|
76
|
+
const sortModel = gridSortModelSelector(apiRef).map(s => s.field);
|
|
77
|
+
const hasAggregatedSorting = sortModel.some(field => aggregationRules[field]);
|
|
78
|
+
if (hasAggregatedSorting) {
|
|
79
|
+
apiRef.current.applySorting();
|
|
80
|
+
}
|
|
81
|
+
abortControllerRef.current = null;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const applySorting = shouldApplySorting(aggregationRules, currentChunk);
|
|
85
|
+
|
|
86
|
+
// createAggregationLookup now RETURNS new partial lookup
|
|
87
|
+
const partialLookup = createAggregationLookup({
|
|
88
|
+
apiRef,
|
|
89
|
+
getAggregationPosition: props.getAggregationPosition,
|
|
90
|
+
aggregatedFields: currentChunk,
|
|
91
|
+
aggregationRules,
|
|
92
|
+
aggregationRowsScope: props.aggregationRowsScope,
|
|
93
|
+
isDataSource: !!props.dataSource,
|
|
94
|
+
applySorting
|
|
95
|
+
});
|
|
96
|
+
for (const key of Object.keys(partialLookup)) {
|
|
97
|
+
for (const field of Object.keys(partialLookup[key])) {
|
|
98
|
+
aggregationLookup[key] ??= {};
|
|
99
|
+
aggregationLookup[key][field] = partialLookup[key][field];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
apiRef.current.setState(state => _extends({}, state, {
|
|
103
|
+
aggregation: _extends({}, state.aggregation, {
|
|
104
|
+
lookup: _extends({}, aggregationLookup)
|
|
105
|
+
})
|
|
106
|
+
}));
|
|
107
|
+
chunkIndex += 1;
|
|
108
|
+
if (performance.now() - chunkStartTime < timeLimit) {
|
|
109
|
+
processChunk();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
chunkStartTime = performance.now();
|
|
114
|
+
processChunk();
|
|
115
|
+
}, 0);
|
|
116
|
+
};
|
|
117
|
+
processChunk();
|
|
52
118
|
}, [apiRef, props.getAggregationPosition, props.aggregationFunctions, props.aggregationRowsScope, props.dataSource]);
|
|
119
|
+
React.useEffect(() => {
|
|
120
|
+
return () => {
|
|
121
|
+
if (abortControllerRef.current) {
|
|
122
|
+
abortControllerRef.current.abort();
|
|
123
|
+
abortControllerRef.current = null;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}, []);
|
|
127
|
+
const deferredApplyAggregation = useRunOncePerLoop(applyAggregation);
|
|
53
128
|
const aggregationApi = {
|
|
54
129
|
setAggregationModel
|
|
55
130
|
};
|
|
@@ -78,17 +153,18 @@ export const useGridAggregation = (apiRef, props) => {
|
|
|
78
153
|
// Re-apply the row hydration to add / remove the aggregation footers
|
|
79
154
|
if (!props.dataSource && !areAggregationRulesEqual(rulesOnLastRowHydration, aggregationRules)) {
|
|
80
155
|
apiRef.current.requestPipeProcessorsApplication('hydrateRows');
|
|
81
|
-
|
|
156
|
+
deferredApplyAggregation();
|
|
82
157
|
}
|
|
83
158
|
|
|
84
159
|
// Re-apply the column hydration to wrap / unwrap the aggregated columns
|
|
85
160
|
if (!areAggregationRulesEqual(rulesOnLastColumnHydration, aggregationRules)) {
|
|
86
161
|
apiRef.current.requestPipeProcessorsApplication('hydrateColumns');
|
|
87
162
|
}
|
|
88
|
-
}, [apiRef,
|
|
163
|
+
}, [apiRef, deferredApplyAggregation, props.aggregationFunctions, props.disableAggregation, props.dataSource]);
|
|
89
164
|
useGridEvent(apiRef, 'aggregationModelChange', checkAggregationRulesDiff);
|
|
90
165
|
useGridEvent(apiRef, 'columnsChange', checkAggregationRulesDiff);
|
|
91
|
-
useGridEvent(apiRef, 'filteredRowsSet',
|
|
166
|
+
useGridEvent(apiRef, 'filteredRowsSet', deferredApplyAggregation);
|
|
167
|
+
useGridEvent(apiRef, 'sortedRowsSet', () => deferredApplyAggregation('sort'));
|
|
92
168
|
|
|
93
169
|
/**
|
|
94
170
|
* EFFECTS
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { GridColDef } from '@mui/x-data-grid-pro';
|
|
1
|
+
import type { GridColDef, GridRowModel } from '@mui/x-data-grid-pro';
|
|
2
2
|
import type { GridPivotingPrivateApiCommunity, GridPivotingStatePartial } from '@mui/x-data-grid/internals';
|
|
3
|
+
import type { RefObject } from '@mui/x-internals/types';
|
|
3
4
|
import type { DataGridPremiumProcessedProps } from "../../../models/dataGridPremiumProps.js";
|
|
5
|
+
import type { GridInitialStatePremium } from "../../../models/gridStatePremium.js";
|
|
4
6
|
export type GridPivotingPropsOverrides = {
|
|
5
7
|
rows: DataGridPremiumProcessedProps['rows'];
|
|
6
8
|
columns: DataGridPremiumProcessedProps['columns'];
|
|
@@ -66,4 +68,12 @@ export interface GridPivotingPrivateApi extends GridPivotingPrivateApiCommunity
|
|
|
66
68
|
targetFieldPosition?: DropPosition;
|
|
67
69
|
}) => void;
|
|
68
70
|
}
|
|
69
|
-
export type GridPivotingColDefOverrides = Pick<GridColDef, 'width' | 'flex' | 'headerName' | 'description' | 'align' | 'headerAlign' | 'cellClassName' | 'headerClassName' | 'display' | 'maxWidth' | 'minWidth' | 'resizable' | 'sortingOrder'>;
|
|
71
|
+
export type GridPivotingColDefOverrides = Pick<GridColDef, 'width' | 'flex' | 'headerName' | 'description' | 'align' | 'headerAlign' | 'cellClassName' | 'headerClassName' | 'display' | 'maxWidth' | 'minWidth' | 'resizable' | 'sortingOrder'>;
|
|
72
|
+
export interface GridPivotingInternalCache {
|
|
73
|
+
nonPivotDataRef: RefObject<{
|
|
74
|
+
rows: GridRowModel[];
|
|
75
|
+
columns: Map<string, GridColDef>;
|
|
76
|
+
originalRowsProp: readonly GridRowModel[];
|
|
77
|
+
} | undefined>;
|
|
78
|
+
exportedStateRef: RefObject<GridInitialStatePremium | null>;
|
|
79
|
+
}
|
|
@@ -4,4 +4,5 @@ import { GridStateInitializer } from '@mui/x-data-grid-pro/internals';
|
|
|
4
4
|
import type { DataGridPremiumProcessedProps } from "../../../models/dataGridPremiumProps.js";
|
|
5
5
|
import { GridPrivateApiPremium } from "../../../models/gridApiPremium.js";
|
|
6
6
|
export declare const pivotingStateInitializer: GridStateInitializer<Pick<DataGridPremiumProcessedProps, 'pivotActive' | 'pivotModel' | 'pivotPanelOpen' | 'initialState' | 'disablePivoting' | 'getPivotDerivedColumns' | 'columns'>>;
|
|
7
|
-
export declare const useGridPivoting: (apiRef: RefObject<GridPrivateApiPremium>, props: Pick<DataGridPremiumProcessedProps, "pivotActive" | "onPivotActiveChange" | "pivotModel" | "onPivotModelChange" | "pivotPanelOpen" | "onPivotPanelOpenChange" | "disablePivoting" | "getPivotDerivedColumns" | "pivotingColDef" | "aggregationFunctions">, originalColumnsProp: readonly GridColDef[], originalRowsProp: readonly GridRowModel[]) => void;
|
|
7
|
+
export declare const useGridPivoting: (apiRef: RefObject<GridPrivateApiPremium>, props: Pick<DataGridPremiumProcessedProps, "pivotActive" | "onPivotActiveChange" | "pivotModel" | "onPivotModelChange" | "pivotPanelOpen" | "onPivotPanelOpenChange" | "disablePivoting" | "getPivotDerivedColumns" | "pivotingColDef" | "groupingColDef" | "aggregationFunctions">, originalColumnsProp: readonly GridColDef[], originalRowsProp: readonly GridRowModel[]) => void;
|
|
8
|
+
export declare const useGridPivotingExportState: (apiRef: RefObject<GridPrivateApiPremium>) => void;
|
|
@@ -13,6 +13,14 @@ const emptyPivotModel = {
|
|
|
13
13
|
values: []
|
|
14
14
|
};
|
|
15
15
|
export const pivotingStateInitializer = (state, props, apiRef) => {
|
|
16
|
+
apiRef.current.caches.pivoting = {
|
|
17
|
+
exportedStateRef: {
|
|
18
|
+
current: null
|
|
19
|
+
},
|
|
20
|
+
nonPivotDataRef: {
|
|
21
|
+
current: undefined
|
|
22
|
+
}
|
|
23
|
+
};
|
|
16
24
|
if (!isPivotingAvailableFn(props)) {
|
|
17
25
|
return _extends({}, state, {
|
|
18
26
|
pivoting: {
|
|
@@ -34,8 +42,10 @@ export const pivotingStateInitializer = (state, props, apiRef) => {
|
|
|
34
42
|
};
|
|
35
43
|
export const useGridPivoting = (apiRef, props, originalColumnsProp, originalRowsProp) => {
|
|
36
44
|
const isPivotActive = useGridSelector(apiRef, gridPivotActiveSelector);
|
|
37
|
-
const
|
|
38
|
-
|
|
45
|
+
const {
|
|
46
|
+
exportedStateRef,
|
|
47
|
+
nonPivotDataRef
|
|
48
|
+
} = apiRef.current.caches.pivoting;
|
|
39
49
|
const isPivotingAvailable = isPivotingAvailableFn(props);
|
|
40
50
|
apiRef.current.registerControlState({
|
|
41
51
|
stateId: 'pivotModel',
|
|
@@ -68,9 +78,10 @@ export const useGridPivoting = (apiRef, props, originalColumnsProp, originalRows
|
|
|
68
78
|
const initialColumns = getInitialColumns(originalColumnsProp, props.getPivotDerivedColumns, apiRef.current.getLocaleText);
|
|
69
79
|
return {
|
|
70
80
|
rows,
|
|
71
|
-
columns: initialColumns
|
|
81
|
+
columns: initialColumns,
|
|
82
|
+
originalRowsProp
|
|
72
83
|
};
|
|
73
|
-
}, [apiRef, props.getPivotDerivedColumns, originalColumnsProp]);
|
|
84
|
+
}, [apiRef, props.getPivotDerivedColumns, originalColumnsProp, originalRowsProp, exportedStateRef]);
|
|
74
85
|
const computePivotingState = React.useCallback(({
|
|
75
86
|
active,
|
|
76
87
|
model: pivotModel
|
|
@@ -91,51 +102,44 @@ export const useGridPivoting = (apiRef, props, originalColumnsProp, originalRows
|
|
|
91
102
|
columns,
|
|
92
103
|
pivotModel,
|
|
93
104
|
apiRef: apiRef,
|
|
94
|
-
pivotingColDef: props.pivotingColDef
|
|
105
|
+
pivotingColDef: props.pivotingColDef,
|
|
106
|
+
groupingColDef: props.groupingColDef
|
|
95
107
|
})
|
|
96
108
|
};
|
|
97
109
|
}
|
|
98
110
|
return undefined;
|
|
99
|
-
}, [apiRef, props.pivotingColDef]);
|
|
111
|
+
}, [apiRef, props.pivotingColDef, props.groupingColDef, nonPivotDataRef]);
|
|
100
112
|
useOnMount(() => {
|
|
101
113
|
if (!isPivotingAvailable || !isPivotActive) {
|
|
102
114
|
return undefined;
|
|
103
115
|
}
|
|
116
|
+
nonPivotDataRef.current = getInitialData();
|
|
104
117
|
const isLoading = gridRowsLoadingSelector(apiRef) ?? false;
|
|
105
|
-
|
|
106
|
-
nonPivotDataRef.current = getInitialData();
|
|
107
|
-
apiRef.current.setState(state => {
|
|
108
|
-
const pivotingState = _extends({}, state.pivoting, computePivotingState(state.pivoting));
|
|
109
|
-
return _extends({}, state, {
|
|
110
|
-
pivoting: pivotingState
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
};
|
|
114
|
-
if (!isLoading) {
|
|
115
|
-
runPivoting();
|
|
118
|
+
if (isLoading) {
|
|
116
119
|
return undefined;
|
|
117
120
|
}
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
121
|
+
apiRef.current.setState(state => {
|
|
122
|
+
const pivotingState = _extends({}, state.pivoting, computePivotingState(state.pivoting));
|
|
123
|
+
return _extends({}, state, {
|
|
124
|
+
pivoting: pivotingState
|
|
125
|
+
});
|
|
124
126
|
});
|
|
125
|
-
return
|
|
127
|
+
return undefined;
|
|
126
128
|
});
|
|
127
129
|
useEnhancedEffect(() => {
|
|
128
130
|
if (!isPivotingAvailable || !isPivotActive) {
|
|
129
|
-
if (exportedStateRef.current) {
|
|
130
|
-
apiRef.current.restoreState(exportedStateRef.current);
|
|
131
|
-
exportedStateRef.current = null;
|
|
132
|
-
}
|
|
133
131
|
if (nonPivotDataRef.current) {
|
|
132
|
+
// Prevent rows from being resynced from the original rows prop
|
|
133
|
+
apiRef.current.caches.rows.rowsBeforePartialUpdates = nonPivotDataRef.current.originalRowsProp;
|
|
134
134
|
apiRef.current.setRows(nonPivotDataRef.current.rows);
|
|
135
135
|
nonPivotDataRef.current = undefined;
|
|
136
136
|
}
|
|
137
|
+
if (exportedStateRef.current) {
|
|
138
|
+
apiRef.current.restoreState(exportedStateRef.current);
|
|
139
|
+
exportedStateRef.current = null;
|
|
140
|
+
}
|
|
137
141
|
}
|
|
138
|
-
}, [isPivotActive, apiRef, isPivotingAvailable]);
|
|
142
|
+
}, [isPivotActive, apiRef, isPivotingAvailable, nonPivotDataRef, exportedStateRef]);
|
|
139
143
|
const setPivotModel = React.useCallback(callback => {
|
|
140
144
|
if (!isPivotingAvailable) {
|
|
141
145
|
return;
|
|
@@ -221,7 +225,6 @@ export const useGridPivoting = (apiRef, props, originalColumnsProp, originalRows
|
|
|
221
225
|
if (!isPivotingAvailable) {
|
|
222
226
|
return;
|
|
223
227
|
}
|
|
224
|
-
apiRef.current.selectRows([], false, true);
|
|
225
228
|
apiRef.current.setState(state => {
|
|
226
229
|
const newPivotMode = typeof callback === 'function' ? callback(state.pivoting?.active) : callback;
|
|
227
230
|
if (state.pivoting?.active === newPivotMode) {
|
|
@@ -240,7 +243,8 @@ export const useGridPivoting = (apiRef, props, originalColumnsProp, originalRows
|
|
|
240
243
|
});
|
|
241
244
|
return newState;
|
|
242
245
|
});
|
|
243
|
-
|
|
246
|
+
apiRef.current.selectRows([], false, true);
|
|
247
|
+
}, [apiRef, computePivotingState, getInitialData, isPivotingAvailable, nonPivotDataRef]);
|
|
244
248
|
const setPivotPanelOpen = React.useCallback(callback => {
|
|
245
249
|
if (!isPivotingAvailable) {
|
|
246
250
|
return;
|
|
@@ -276,9 +280,9 @@ export const useGridPivoting = (apiRef, props, originalColumnsProp, originalRows
|
|
|
276
280
|
})
|
|
277
281
|
});
|
|
278
282
|
});
|
|
279
|
-
}, [isPivotingAvailable, apiRef, props.getPivotDerivedColumns, computePivotingState]);
|
|
283
|
+
}, [isPivotingAvailable, apiRef, props.getPivotDerivedColumns, computePivotingState, nonPivotDataRef]);
|
|
280
284
|
const updateNonPivotRows = React.useCallback((rows, keepPreviousRows = true) => {
|
|
281
|
-
if (!nonPivotDataRef.current || !rows || rows.length === 0) {
|
|
285
|
+
if (!nonPivotDataRef.current || !isPivotingAvailable || !rows || rows.length === 0) {
|
|
282
286
|
return;
|
|
283
287
|
}
|
|
284
288
|
if (keepPreviousRows) {
|
|
@@ -304,7 +308,7 @@ export const useGridPivoting = (apiRef, props, originalColumnsProp, originalRows
|
|
|
304
308
|
pivoting: _extends({}, state.pivoting, computePivotingState(state.pivoting))
|
|
305
309
|
});
|
|
306
310
|
});
|
|
307
|
-
}, [apiRef, computePivotingState]);
|
|
311
|
+
}, [apiRef, computePivotingState, isPivotingAvailable, nonPivotDataRef]);
|
|
308
312
|
useGridApiMethod(apiRef, {
|
|
309
313
|
setPivotModel,
|
|
310
314
|
setPivotActive,
|
|
@@ -320,7 +324,10 @@ export const useGridPivoting = (apiRef, props, originalColumnsProp, originalRows
|
|
|
320
324
|
}, [originalColumnsProp, apiRef]);
|
|
321
325
|
useEnhancedEffect(() => {
|
|
322
326
|
apiRef.current.updateNonPivotRows(originalRowsProp, false);
|
|
323
|
-
|
|
327
|
+
if (nonPivotDataRef.current) {
|
|
328
|
+
nonPivotDataRef.current.originalRowsProp = originalRowsProp;
|
|
329
|
+
}
|
|
330
|
+
}, [originalRowsProp, apiRef, nonPivotDataRef]);
|
|
324
331
|
useEnhancedEffect(() => {
|
|
325
332
|
if (props.pivotModel !== undefined) {
|
|
326
333
|
apiRef.current.setPivotModel(props.pivotModel);
|
|
@@ -336,4 +343,19 @@ export const useGridPivoting = (apiRef, props, originalColumnsProp, originalRows
|
|
|
336
343
|
apiRef.current.setPivotPanelOpen(props.pivotPanelOpen);
|
|
337
344
|
}
|
|
338
345
|
}, [apiRef, props.pivotPanelOpen]);
|
|
346
|
+
};
|
|
347
|
+
export const useGridPivotingExportState = apiRef => {
|
|
348
|
+
const stateExportPreProcessing = React.useCallback(state => {
|
|
349
|
+
const isPivotActive = gridPivotActiveSelector(apiRef);
|
|
350
|
+
if (!isPivotActive) {
|
|
351
|
+
return state;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// To-do: implement context.exportOnlyDirtyModels
|
|
355
|
+
const newState = _extends({}, state, apiRef.current.caches.pivoting.exportedStateRef.current, {
|
|
356
|
+
sorting: state.sorting
|
|
357
|
+
});
|
|
358
|
+
return newState;
|
|
359
|
+
}, [apiRef]);
|
|
360
|
+
useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing);
|
|
339
361
|
};
|
|
@@ -11,11 +11,13 @@ export declare const getPivotedData: ({
|
|
|
11
11
|
columns,
|
|
12
12
|
pivotModel,
|
|
13
13
|
apiRef,
|
|
14
|
-
pivotingColDef
|
|
14
|
+
pivotingColDef,
|
|
15
|
+
groupingColDef
|
|
15
16
|
}: {
|
|
16
17
|
rows: GridRowModel[];
|
|
17
18
|
columns: Map<string, GridColDef>;
|
|
18
19
|
pivotModel: GridPivotModel;
|
|
19
20
|
apiRef: RefObject<GridApiPremium>;
|
|
20
21
|
pivotingColDef: DataGridPremiumProcessedProps["pivotingColDef"];
|
|
22
|
+
groupingColDef: DataGridPremiumProcessedProps["groupingColDef"];
|
|
21
23
|
}) => GridPivotingPropsOverrides;
|