@mui/x-data-grid 7.17.0 → 7.18.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 +98 -5
- package/DataGrid/DataGrid.js +11 -1
- package/DataGrid/useDataGridComponent.js +3 -0
- package/DataGrid/useDataGridProps.js +2 -1
- package/components/GridRow.js +1 -0
- package/components/cell/GridCell.js +30 -5
- package/hooks/features/columnHeaders/useGridColumnHeaders.d.ts +0 -1
- package/hooks/features/columnHeaders/useGridColumnHeaders.js +11 -11
- package/hooks/features/columnResize/useGridColumnResize.js +6 -6
- package/hooks/features/dimensions/gridDimensionsApi.d.ts +4 -0
- package/hooks/features/dimensions/useGridDimensions.d.ts +1 -1
- package/hooks/features/dimensions/useGridDimensions.js +4 -1
- package/hooks/features/editing/useGridCellEditing.js +2 -18
- package/hooks/features/editing/useGridRowEditing.js +6 -1
- package/hooks/features/editing/utils.d.ts +2 -0
- package/hooks/features/editing/utils.js +15 -0
- package/hooks/features/export/useGridPrintExport.js +2 -1
- package/hooks/features/focus/useGridFocus.js +2 -1
- package/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +10 -46
- package/hooks/features/keyboardNavigation/utils.d.ts +17 -0
- package/hooks/features/keyboardNavigation/utils.js +58 -0
- package/hooks/features/rows/gridRowSpanningSelectors.d.ts +4 -0
- package/hooks/features/rows/gridRowSpanningSelectors.js +5 -0
- package/hooks/features/rows/gridRowSpanningUtils.d.ts +10 -0
- package/hooks/features/rows/gridRowSpanningUtils.js +42 -0
- package/hooks/features/rows/useGridRowSpanning.d.ts +27 -0
- package/hooks/features/rows/useGridRowSpanning.js +257 -0
- package/hooks/features/virtualization/useGridVirtualScroller.d.ts +1 -1
- package/hooks/features/virtualization/useGridVirtualScroller.js +17 -7
- package/hooks/utils/useGridApiEventHandler.js +0 -1
- package/index.js +1 -1
- package/internals/index.d.ts +1 -0
- package/internals/index.js +1 -0
- package/models/colDef/gridColDef.d.ts +4 -0
- package/models/gridStateCommunity.d.ts +2 -0
- package/models/props/DataGridProps.d.ts +10 -0
- package/modern/DataGrid/DataGrid.js +11 -1
- package/modern/DataGrid/useDataGridComponent.js +3 -0
- package/modern/DataGrid/useDataGridProps.js +2 -1
- package/modern/components/GridRow.js +1 -0
- package/modern/components/cell/GridCell.js +30 -5
- package/modern/hooks/features/columnHeaders/useGridColumnHeaders.js +11 -11
- package/modern/hooks/features/columnResize/useGridColumnResize.js +6 -6
- package/modern/hooks/features/dimensions/useGridDimensions.js +4 -1
- package/modern/hooks/features/editing/useGridCellEditing.js +2 -18
- package/modern/hooks/features/editing/useGridRowEditing.js +6 -1
- package/modern/hooks/features/editing/utils.js +15 -0
- package/modern/hooks/features/export/useGridPrintExport.js +2 -1
- package/modern/hooks/features/focus/useGridFocus.js +2 -1
- package/modern/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +10 -46
- package/modern/hooks/features/keyboardNavigation/utils.js +58 -0
- package/modern/hooks/features/rows/gridRowSpanningSelectors.js +5 -0
- package/modern/hooks/features/rows/gridRowSpanningUtils.js +42 -0
- package/modern/hooks/features/rows/useGridRowSpanning.js +257 -0
- package/modern/hooks/features/virtualization/useGridVirtualScroller.js +17 -7
- package/modern/hooks/utils/useGridApiEventHandler.js +0 -1
- package/modern/index.js +1 -1
- package/modern/internals/index.js +1 -0
- package/modern/utils/domUtils.js +12 -12
- package/node/DataGrid/DataGrid.js +11 -1
- package/node/DataGrid/useDataGridComponent.js +3 -0
- package/node/DataGrid/useDataGridProps.js +2 -1
- package/node/components/GridRow.js +1 -0
- package/node/components/cell/GridCell.js +30 -5
- package/node/hooks/features/columnHeaders/useGridColumnHeaders.js +11 -11
- package/node/hooks/features/columnResize/useGridColumnResize.js +6 -6
- package/node/hooks/features/dimensions/useGridDimensions.js +4 -1
- package/node/hooks/features/editing/useGridCellEditing.js +2 -18
- package/node/hooks/features/editing/useGridRowEditing.js +6 -1
- package/node/hooks/features/editing/utils.js +22 -0
- package/node/hooks/features/export/useGridPrintExport.js +2 -1
- package/node/hooks/features/focus/useGridFocus.js +2 -1
- package/node/hooks/features/keyboardNavigation/useGridKeyboardNavigation.js +16 -53
- package/node/hooks/features/keyboardNavigation/utils.js +68 -0
- package/node/hooks/features/rows/gridRowSpanningSelectors.js +11 -0
- package/node/hooks/features/rows/gridRowSpanningUtils.js +52 -0
- package/node/hooks/features/rows/useGridRowSpanning.js +267 -0
- package/node/hooks/features/virtualization/useGridVirtualScroller.js +17 -7
- package/node/hooks/utils/useGridApiEventHandler.js +0 -1
- package/node/index.js +1 -1
- package/node/internals/index.js +15 -0
- package/node/utils/domUtils.js +12 -12
- package/package.json +3 -3
- package/utils/domUtils.d.ts +4 -4
- package/utils/domUtils.js +12 -12
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function getUnprocessedRange(testRange, processedRange) {
|
|
2
|
+
if (testRange.firstRowIndex >= processedRange.firstRowIndex && testRange.lastRowIndex <= processedRange.lastRowIndex) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
// Overflowing at the end
|
|
6
|
+
// Example: testRange={ firstRowIndex: 10, lastRowIndex: 20 }, processedRange={ firstRowIndex: 0, lastRowIndex: 15 }
|
|
7
|
+
// Unprocessed Range={ firstRowIndex: 16, lastRowIndex: 20 }
|
|
8
|
+
if (testRange.firstRowIndex >= processedRange.firstRowIndex && testRange.lastRowIndex > processedRange.lastRowIndex) {
|
|
9
|
+
return {
|
|
10
|
+
firstRowIndex: processedRange.lastRowIndex,
|
|
11
|
+
lastRowIndex: testRange.lastRowIndex
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
// Overflowing at the beginning
|
|
15
|
+
// Example: testRange={ firstRowIndex: 0, lastRowIndex: 20 }, processedRange={ firstRowIndex: 16, lastRowIndex: 30 }
|
|
16
|
+
// Unprocessed Range={ firstRowIndex: 0, lastRowIndex: 15 }
|
|
17
|
+
if (testRange.firstRowIndex < processedRange.firstRowIndex && testRange.lastRowIndex <= processedRange.lastRowIndex) {
|
|
18
|
+
return {
|
|
19
|
+
firstRowIndex: testRange.firstRowIndex,
|
|
20
|
+
lastRowIndex: processedRange.firstRowIndex - 1
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// TODO: Should return two ranges handle overflowing at both ends ?
|
|
24
|
+
return testRange;
|
|
25
|
+
}
|
|
26
|
+
export function isRowContextInitialized(renderContext) {
|
|
27
|
+
return renderContext.firstRowIndex !== 0 || renderContext.lastRowIndex !== 0;
|
|
28
|
+
}
|
|
29
|
+
export function isRowRangeUpdated(range1, range2) {
|
|
30
|
+
return range1.firstRowIndex !== range2.firstRowIndex || range1.lastRowIndex !== range2.lastRowIndex;
|
|
31
|
+
}
|
|
32
|
+
export const getCellValue = (row, colDef, apiRef) => {
|
|
33
|
+
if (!row) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
let cellValue = row[colDef.field];
|
|
37
|
+
const valueGetter = colDef.rowSpanValueGetter ?? colDef.valueGetter;
|
|
38
|
+
if (valueGetter) {
|
|
39
|
+
cellValue = valueGetter(cellValue, row, colDef, apiRef);
|
|
40
|
+
}
|
|
41
|
+
return cellValue;
|
|
42
|
+
};
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import useLazyRef from '@mui/utils/useLazyRef';
|
|
4
|
+
import { gridVisibleColumnDefinitionsSelector } from "../columns/gridColumnsSelector.js";
|
|
5
|
+
import { useGridVisibleRows } from "../../utils/useGridVisibleRows.js";
|
|
6
|
+
import { gridRenderContextSelector } from "../virtualization/gridVirtualizationSelectors.js";
|
|
7
|
+
import { useGridSelector } from "../../utils/useGridSelector.js";
|
|
8
|
+
import { getUnprocessedRange, isRowRangeUpdated, isRowContextInitialized, getCellValue } from "./gridRowSpanningUtils.js";
|
|
9
|
+
const EMPTY_STATE = {
|
|
10
|
+
spannedCells: {},
|
|
11
|
+
hiddenCells: {},
|
|
12
|
+
hiddenCellOriginMap: {}
|
|
13
|
+
};
|
|
14
|
+
const EMPTY_RANGE = {
|
|
15
|
+
firstRowIndex: 0,
|
|
16
|
+
lastRowIndex: 0
|
|
17
|
+
};
|
|
18
|
+
const skippedFields = new Set(['__check__', '__reorder__', '__detail_panel_toggle__']);
|
|
19
|
+
/**
|
|
20
|
+
* Default number of rows to process during state initialization to avoid flickering.
|
|
21
|
+
* Number `20` is arbitrarily chosen to be large enough to cover most of the cases without
|
|
22
|
+
* compromising performance.
|
|
23
|
+
*/
|
|
24
|
+
const DEFAULT_ROWS_TO_PROCESS = 20;
|
|
25
|
+
const computeRowSpanningState = (apiRef, colDefs, visibleRows, range, rangeToProcess, resetState, processedRange) => {
|
|
26
|
+
const spannedCells = resetState ? {} : _extends({}, apiRef.current.state.rowSpanning.spannedCells);
|
|
27
|
+
const hiddenCells = resetState ? {} : _extends({}, apiRef.current.state.rowSpanning.hiddenCells);
|
|
28
|
+
const hiddenCellOriginMap = resetState ? {} : _extends({}, apiRef.current.state.rowSpanning.hiddenCellOriginMap);
|
|
29
|
+
if (resetState) {
|
|
30
|
+
processedRange = EMPTY_RANGE;
|
|
31
|
+
}
|
|
32
|
+
colDefs.forEach(colDef => {
|
|
33
|
+
if (skippedFields.has(colDef.field)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
for (let index = rangeToProcess.firstRowIndex; index <= rangeToProcess.lastRowIndex; index += 1) {
|
|
37
|
+
const row = visibleRows[index];
|
|
38
|
+
if (hiddenCells[row.id]?.[colDef.field]) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const cellValue = getCellValue(row.model, colDef, apiRef);
|
|
42
|
+
if (cellValue == null) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
let spannedRowId = row.id;
|
|
46
|
+
let spannedRowIndex = index;
|
|
47
|
+
let rowSpan = 0;
|
|
48
|
+
|
|
49
|
+
// For first index, also scan in the previous rows to handle the reset state case e.g by sorting
|
|
50
|
+
const backwardsHiddenCells = [];
|
|
51
|
+
if (index === rangeToProcess.firstRowIndex) {
|
|
52
|
+
let prevIndex = index - 1;
|
|
53
|
+
const prevRowEntry = visibleRows[prevIndex];
|
|
54
|
+
while (prevIndex >= range.firstRowIndex && getCellValue(prevRowEntry.model, colDef, apiRef) === cellValue) {
|
|
55
|
+
const currentRow = visibleRows[prevIndex + 1];
|
|
56
|
+
if (hiddenCells[currentRow.id]) {
|
|
57
|
+
hiddenCells[currentRow.id][colDef.field] = true;
|
|
58
|
+
} else {
|
|
59
|
+
hiddenCells[currentRow.id] = {
|
|
60
|
+
[colDef.field]: true
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
backwardsHiddenCells.push(index);
|
|
64
|
+
rowSpan += 1;
|
|
65
|
+
spannedRowId = prevRowEntry.id;
|
|
66
|
+
spannedRowIndex = prevIndex;
|
|
67
|
+
prevIndex -= 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
backwardsHiddenCells.forEach(hiddenCellIndex => {
|
|
71
|
+
if (hiddenCellOriginMap[hiddenCellIndex]) {
|
|
72
|
+
hiddenCellOriginMap[hiddenCellIndex][colDef.field] = spannedRowIndex;
|
|
73
|
+
} else {
|
|
74
|
+
hiddenCellOriginMap[hiddenCellIndex] = {
|
|
75
|
+
[colDef.field]: spannedRowIndex
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Scan the next rows
|
|
81
|
+
let relativeIndex = index + 1;
|
|
82
|
+
while (relativeIndex <= range.lastRowIndex && visibleRows[relativeIndex] && getCellValue(visibleRows[relativeIndex].model, colDef, apiRef) === cellValue) {
|
|
83
|
+
const currentRow = visibleRows[relativeIndex];
|
|
84
|
+
if (hiddenCells[currentRow.id]) {
|
|
85
|
+
hiddenCells[currentRow.id][colDef.field] = true;
|
|
86
|
+
} else {
|
|
87
|
+
hiddenCells[currentRow.id] = {
|
|
88
|
+
[colDef.field]: true
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (hiddenCellOriginMap[relativeIndex]) {
|
|
92
|
+
hiddenCellOriginMap[relativeIndex][colDef.field] = spannedRowIndex;
|
|
93
|
+
} else {
|
|
94
|
+
hiddenCellOriginMap[relativeIndex] = {
|
|
95
|
+
[colDef.field]: spannedRowIndex
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
relativeIndex += 1;
|
|
99
|
+
rowSpan += 1;
|
|
100
|
+
}
|
|
101
|
+
if (rowSpan > 0) {
|
|
102
|
+
if (spannedCells[spannedRowId]) {
|
|
103
|
+
spannedCells[spannedRowId][colDef.field] = rowSpan + 1;
|
|
104
|
+
} else {
|
|
105
|
+
spannedCells[spannedRowId] = {
|
|
106
|
+
[colDef.field]: rowSpan + 1
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
processedRange = {
|
|
112
|
+
firstRowIndex: Math.min(processedRange.firstRowIndex, rangeToProcess.firstRowIndex),
|
|
113
|
+
lastRowIndex: Math.max(processedRange.lastRowIndex, rangeToProcess.lastRowIndex)
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
spannedCells,
|
|
118
|
+
hiddenCells,
|
|
119
|
+
hiddenCellOriginMap,
|
|
120
|
+
processedRange
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @requires columnsStateInitializer (method) - should be initialized before
|
|
126
|
+
* @requires rowsStateInitializer (method) - should be initialized before
|
|
127
|
+
* @requires filterStateInitializer (method) - should be initialized before
|
|
128
|
+
*/
|
|
129
|
+
export const rowSpanningStateInitializer = (state, props, apiRef) => {
|
|
130
|
+
if (props.unstable_rowSpanning) {
|
|
131
|
+
const rowIds = state.rows.dataRowIds || [];
|
|
132
|
+
const orderedFields = state.columns.orderedFields || [];
|
|
133
|
+
const dataRowIdToModelLookup = state.rows.dataRowIdToModelLookup;
|
|
134
|
+
const columnsLookup = state.columns.lookup;
|
|
135
|
+
const isFilteringPending = Boolean(state.filter.filterModel.items.length) || Boolean(state.filter.filterModel.quickFilterValues?.length);
|
|
136
|
+
if (!rowIds.length || !orderedFields.length || !dataRowIdToModelLookup || !columnsLookup || isFilteringPending) {
|
|
137
|
+
return _extends({}, state, {
|
|
138
|
+
rowSpanning: EMPTY_STATE
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const rangeToProcess = {
|
|
142
|
+
firstRowIndex: 0,
|
|
143
|
+
lastRowIndex: Math.min(DEFAULT_ROWS_TO_PROCESS - 1, Math.max(rowIds.length - 1, 0))
|
|
144
|
+
};
|
|
145
|
+
const rows = rowIds.map(id => ({
|
|
146
|
+
id,
|
|
147
|
+
model: dataRowIdToModelLookup[id]
|
|
148
|
+
}));
|
|
149
|
+
const colDefs = orderedFields.map(field => columnsLookup[field]);
|
|
150
|
+
const {
|
|
151
|
+
spannedCells,
|
|
152
|
+
hiddenCells,
|
|
153
|
+
hiddenCellOriginMap
|
|
154
|
+
} = computeRowSpanningState(apiRef, colDefs, rows, rangeToProcess, rangeToProcess, true, EMPTY_RANGE);
|
|
155
|
+
return _extends({}, state, {
|
|
156
|
+
rowSpanning: {
|
|
157
|
+
spannedCells,
|
|
158
|
+
hiddenCells,
|
|
159
|
+
hiddenCellOriginMap
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return _extends({}, state, {
|
|
164
|
+
rowSpanning: EMPTY_STATE
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
export const useGridRowSpanning = (apiRef, props) => {
|
|
168
|
+
const {
|
|
169
|
+
range,
|
|
170
|
+
rows: visibleRows
|
|
171
|
+
} = useGridVisibleRows(apiRef, props);
|
|
172
|
+
const renderContext = useGridSelector(apiRef, gridRenderContextSelector);
|
|
173
|
+
const colDefs = useGridSelector(apiRef, gridVisibleColumnDefinitionsSelector);
|
|
174
|
+
const processedRange = useLazyRef(() => {
|
|
175
|
+
return Object.keys(apiRef.current.state.rowSpanning.spannedCells).length > 0 ? {
|
|
176
|
+
firstRowIndex: 0,
|
|
177
|
+
lastRowIndex: Math.min(DEFAULT_ROWS_TO_PROCESS - 1, Math.max(apiRef.current.state.rows.dataRowIds.length - 1, 0))
|
|
178
|
+
} : EMPTY_RANGE;
|
|
179
|
+
});
|
|
180
|
+
const lastRange = React.useRef(EMPTY_RANGE);
|
|
181
|
+
const updateRowSpanningState = React.useCallback(
|
|
182
|
+
// A reset needs to occur when:
|
|
183
|
+
// - The `unstable_rowSpanning` prop is updated (feature flag)
|
|
184
|
+
// - The filtering is applied
|
|
185
|
+
// - The sorting is applied
|
|
186
|
+
// - The `paginationModel` is updated
|
|
187
|
+
// - The rows are updated
|
|
188
|
+
(resetState = true) => {
|
|
189
|
+
if (!props.unstable_rowSpanning) {
|
|
190
|
+
if (apiRef.current.state.rowSpanning !== EMPTY_STATE) {
|
|
191
|
+
apiRef.current.setState(state => _extends({}, state, {
|
|
192
|
+
rowSpanning: EMPTY_STATE
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (range === null || !isRowContextInitialized(renderContext)) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (resetState) {
|
|
201
|
+
processedRange.current = EMPTY_RANGE;
|
|
202
|
+
}
|
|
203
|
+
const rangeToProcess = getUnprocessedRange({
|
|
204
|
+
firstRowIndex: renderContext.firstRowIndex,
|
|
205
|
+
lastRowIndex: renderContext.lastRowIndex - 1
|
|
206
|
+
}, processedRange.current);
|
|
207
|
+
if (rangeToProcess === null) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const {
|
|
211
|
+
spannedCells,
|
|
212
|
+
hiddenCells,
|
|
213
|
+
hiddenCellOriginMap,
|
|
214
|
+
processedRange: newProcessedRange
|
|
215
|
+
} = computeRowSpanningState(apiRef, colDefs, visibleRows, range, rangeToProcess, resetState, processedRange.current);
|
|
216
|
+
processedRange.current = newProcessedRange;
|
|
217
|
+
const newSpannedCellsCount = Object.keys(spannedCells).length;
|
|
218
|
+
const newHiddenCellsCount = Object.keys(hiddenCells).length;
|
|
219
|
+
const currentSpannedCellsCount = Object.keys(apiRef.current.state.rowSpanning.spannedCells).length;
|
|
220
|
+
const currentHiddenCellsCount = Object.keys(apiRef.current.state.rowSpanning.hiddenCells).length;
|
|
221
|
+
const shouldUpdateState = resetState || newSpannedCellsCount !== currentSpannedCellsCount || newHiddenCellsCount !== currentHiddenCellsCount;
|
|
222
|
+
if (!shouldUpdateState) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
apiRef.current.setState(state => {
|
|
226
|
+
return _extends({}, state, {
|
|
227
|
+
rowSpanning: {
|
|
228
|
+
spannedCells,
|
|
229
|
+
hiddenCells,
|
|
230
|
+
hiddenCellOriginMap
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
}, [apiRef, props.unstable_rowSpanning, range, renderContext, visibleRows, colDefs, processedRange]);
|
|
235
|
+
const prevRenderContext = React.useRef(renderContext);
|
|
236
|
+
const isFirstRender = React.useRef(true);
|
|
237
|
+
const shouldResetState = React.useRef(false);
|
|
238
|
+
React.useEffect(() => {
|
|
239
|
+
const firstRender = isFirstRender.current;
|
|
240
|
+
if (isFirstRender.current) {
|
|
241
|
+
isFirstRender.current = false;
|
|
242
|
+
}
|
|
243
|
+
if (range && lastRange.current && isRowRangeUpdated(range, lastRange.current)) {
|
|
244
|
+
lastRange.current = range;
|
|
245
|
+
shouldResetState.current = true;
|
|
246
|
+
}
|
|
247
|
+
if (!firstRender && prevRenderContext.current !== renderContext) {
|
|
248
|
+
if (isRowRangeUpdated(prevRenderContext.current, renderContext)) {
|
|
249
|
+
updateRowSpanningState(shouldResetState.current);
|
|
250
|
+
shouldResetState.current = false;
|
|
251
|
+
}
|
|
252
|
+
prevRenderContext.current = renderContext;
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
updateRowSpanningState();
|
|
256
|
+
}, [updateRowSpanningState, renderContext, range, lastRange]);
|
|
257
|
+
};
|
|
@@ -24,6 +24,7 @@ import { getFirstNonSpannedColumnToRender } from "../columns/gridColumnsUtils.js
|
|
|
24
24
|
import { getMinimalContentHeight } from "../rows/gridRowsUtils.js";
|
|
25
25
|
import { gridRenderContextSelector, gridVirtualizationRowEnabledSelector, gridVirtualizationColumnEnabledSelector } from "./gridVirtualizationSelectors.js";
|
|
26
26
|
import { EMPTY_RENDER_CONTEXT } from "./useGridVirtualization.js";
|
|
27
|
+
import { gridRowSpanningHiddenCellsOriginMapSelector } from "../rows/gridRowSpanningSelectors.js";
|
|
27
28
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
28
29
|
const MINIMUM_COLUMN_WIDTH = 50;
|
|
29
30
|
var ScrollDirection = /*#__PURE__*/function (ScrollDirection) {
|
|
@@ -325,7 +326,7 @@ export const useGridVirtualScroller = () => {
|
|
|
325
326
|
if (!isPinnedSection && frozenContext.current && rowIndexInPage >= frozenContext.current.firstRowIndex && rowIndexInPage < frozenContext.current.lastRowIndex) {
|
|
326
327
|
currentRenderContext = frozenContext.current;
|
|
327
328
|
}
|
|
328
|
-
const offsetLeft = computeOffsetLeft(columnPositions, currentRenderContext,
|
|
329
|
+
const offsetLeft = computeOffsetLeft(columnPositions, currentRenderContext, pinnedColumns.left.length);
|
|
329
330
|
const showBottomBorder = isLastVisibleInSection && params.position === 'top';
|
|
330
331
|
rows.push(/*#__PURE__*/_jsx(rootProps.slots.row, _extends({
|
|
331
332
|
row: model,
|
|
@@ -353,7 +354,7 @@ export const useGridVirtualScroller = () => {
|
|
|
353
354
|
if (panel) {
|
|
354
355
|
rows.push(panel);
|
|
355
356
|
}
|
|
356
|
-
if (
|
|
357
|
+
if (params.position === undefined && isLastVisibleInSection) {
|
|
357
358
|
rows.push(apiRef.current.getInfiniteLoadingTriggerElement?.({
|
|
358
359
|
lastRowId: id
|
|
359
360
|
}));
|
|
@@ -447,6 +448,7 @@ function inputsSelector(apiRef, rootProps, enabledForRows, enabledForColumns) {
|
|
|
447
448
|
const dimensions = gridDimensionsSelector(apiRef.current.state);
|
|
448
449
|
const currentPage = getVisibleRows(apiRef, rootProps);
|
|
449
450
|
const visibleColumns = gridVisibleColumnDefinitionsSelector(apiRef);
|
|
451
|
+
const hiddenCellsOriginMap = gridRowSpanningHiddenCellsOriginMapSelector(apiRef);
|
|
450
452
|
const lastRowId = apiRef.current.state.rows.dataRowIds.at(-1);
|
|
451
453
|
const lastColumn = visibleColumns.at(-1);
|
|
452
454
|
return {
|
|
@@ -467,7 +469,8 @@ function inputsSelector(apiRef, rootProps, enabledForRows, enabledForColumns) {
|
|
|
467
469
|
rows: currentPage.rows,
|
|
468
470
|
range: currentPage.range,
|
|
469
471
|
pinnedColumns: gridVisiblePinnedColumnDefinitionsSelector(apiRef),
|
|
470
|
-
visibleColumns
|
|
472
|
+
visibleColumns,
|
|
473
|
+
hiddenCellsOriginMap
|
|
471
474
|
};
|
|
472
475
|
}
|
|
473
476
|
function computeRenderContext(inputs, scrollPosition, scrollCache) {
|
|
@@ -485,10 +488,18 @@ function computeRenderContext(inputs, scrollPosition, scrollCache) {
|
|
|
485
488
|
if (inputs.enabledForRows) {
|
|
486
489
|
// Clamp the value because the search may return an index out of bounds.
|
|
487
490
|
// In the last index, this is not needed because Array.slice doesn't include it.
|
|
488
|
-
|
|
491
|
+
let firstRowIndex = Math.min(getNearestIndexToRender(inputs, top, {
|
|
489
492
|
atStart: true,
|
|
490
493
|
lastPosition: inputs.rowsMeta.positions[inputs.rowsMeta.positions.length - 1] + inputs.lastRowHeight
|
|
491
494
|
}), inputs.rowsMeta.positions.length - 1);
|
|
495
|
+
|
|
496
|
+
// If any of the cells in the `firstRowIndex` is hidden due to an extended row span,
|
|
497
|
+
// Make sure the row from where the rowSpan is originated is visible.
|
|
498
|
+
const rowSpanHiddenCellOrigin = inputs.hiddenCellsOriginMap[firstRowIndex];
|
|
499
|
+
if (rowSpanHiddenCellOrigin) {
|
|
500
|
+
const minSpannedRowIndex = Math.min(...Object.values(rowSpanHiddenCellOrigin));
|
|
501
|
+
firstRowIndex = Math.min(firstRowIndex, minSpannedRowIndex);
|
|
502
|
+
}
|
|
492
503
|
const lastRowIndex = inputs.autoHeight ? firstRowIndex + inputs.rows.length : getNearestIndexToRender(inputs, top + inputs.viewportInnerHeight);
|
|
493
504
|
renderContext.firstRowIndex = firstRowIndex;
|
|
494
505
|
renderContext.lastRowIndex = lastRowIndex;
|
|
@@ -642,9 +653,8 @@ export function areRenderContextsEqual(context1, context2) {
|
|
|
642
653
|
}
|
|
643
654
|
return context1.firstRowIndex === context2.firstRowIndex && context1.lastRowIndex === context2.lastRowIndex && context1.firstColumnIndex === context2.firstColumnIndex && context1.lastColumnIndex === context2.lastColumnIndex;
|
|
644
655
|
}
|
|
645
|
-
export function computeOffsetLeft(columnPositions, renderContext,
|
|
646
|
-
const
|
|
647
|
-
const left = factor * (columnPositions[renderContext.firstColumnIndex] ?? 0) - (columnPositions[pinnedLeftLength] ?? 0);
|
|
656
|
+
export function computeOffsetLeft(columnPositions, renderContext, pinnedLeftLength) {
|
|
657
|
+
const left = (columnPositions[renderContext.firstColumnIndex] ?? 0) - (columnPositions[pinnedLeftLength] ?? 0);
|
|
648
658
|
return Math.abs(left);
|
|
649
659
|
}
|
|
650
660
|
function directionForDelta(dx, dy) {
|
|
@@ -90,7 +90,6 @@ const optionsSubscriberOptions = {
|
|
|
90
90
|
isFirst: true
|
|
91
91
|
};
|
|
92
92
|
export function useGridApiOptionHandler(apiRef, eventName, handler) {
|
|
93
|
-
// Validate that only one per event name?
|
|
94
93
|
useGridApiEventHandler(apiRef, eventName, handler, optionsSubscriberOptions);
|
|
95
94
|
}
|
|
96
95
|
export { GridSignature };
|
package/modern/index.js
CHANGED
|
@@ -32,6 +32,7 @@ export { useGridPreferencesPanel, preferencePanelStateInitializer } from "../hoo
|
|
|
32
32
|
export { useGridEditing, editingStateInitializer } from "../hooks/features/editing/useGridEditing.js";
|
|
33
33
|
export { gridEditRowsStateSelector } from "../hooks/features/editing/gridEditingSelectors.js";
|
|
34
34
|
export { useGridRows, rowsStateInitializer } from "../hooks/features/rows/useGridRows.js";
|
|
35
|
+
export { useGridRowSpanning, rowSpanningStateInitializer } from "../hooks/features/rows/useGridRowSpanning.js";
|
|
35
36
|
export { useGridAriaAttributes } from "../hooks/utils/useGridAriaAttributes.js";
|
|
36
37
|
export { useGridRowAriaAttributes } from "../hooks/features/rows/useGridRowAriaAttributes.js";
|
|
37
38
|
export { useGridRowsPreProcessors } from "../hooks/features/rows/useGridRowsPreProcessors.js";
|
package/modern/utils/domUtils.js
CHANGED
|
@@ -120,22 +120,22 @@ const findPinnedCells = ({
|
|
|
120
120
|
});
|
|
121
121
|
return cells;
|
|
122
122
|
};
|
|
123
|
-
export function findLeftPinnedCellsAfterCol(api, col) {
|
|
123
|
+
export function findLeftPinnedCellsAfterCol(api, col, isRtl) {
|
|
124
124
|
const colIndex = parseCellColIndex(col);
|
|
125
125
|
return findPinnedCells({
|
|
126
126
|
api,
|
|
127
127
|
colIndex,
|
|
128
|
-
position: 'left',
|
|
129
|
-
filterFn: index => index > colIndex
|
|
128
|
+
position: isRtl ? 'right' : 'left',
|
|
129
|
+
filterFn: index => isRtl ? index < colIndex : index > colIndex
|
|
130
130
|
});
|
|
131
131
|
}
|
|
132
|
-
export function findRightPinnedCellsBeforeCol(api, col) {
|
|
132
|
+
export function findRightPinnedCellsBeforeCol(api, col, isRtl) {
|
|
133
133
|
const colIndex = parseCellColIndex(col);
|
|
134
134
|
return findPinnedCells({
|
|
135
135
|
api,
|
|
136
136
|
colIndex,
|
|
137
|
-
position: 'right',
|
|
138
|
-
filterFn: index => index < colIndex
|
|
137
|
+
position: isRtl ? 'left' : 'right',
|
|
138
|
+
filterFn: index => isRtl ? index > colIndex : index < colIndex
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
141
|
const findPinnedHeaders = ({
|
|
@@ -159,22 +159,22 @@ const findPinnedHeaders = ({
|
|
|
159
159
|
});
|
|
160
160
|
return elements;
|
|
161
161
|
};
|
|
162
|
-
export function findLeftPinnedHeadersAfterCol(api, col) {
|
|
162
|
+
export function findLeftPinnedHeadersAfterCol(api, col, isRtl) {
|
|
163
163
|
const colIndex = parseCellColIndex(col);
|
|
164
164
|
return findPinnedHeaders({
|
|
165
165
|
api,
|
|
166
|
-
position: 'left',
|
|
166
|
+
position: isRtl ? 'right' : 'left',
|
|
167
167
|
colIndex,
|
|
168
|
-
filterFn: index => index > colIndex
|
|
168
|
+
filterFn: index => isRtl ? index < colIndex : index > colIndex
|
|
169
169
|
});
|
|
170
170
|
}
|
|
171
|
-
export function findRightPinnedHeadersBeforeCol(api, col) {
|
|
171
|
+
export function findRightPinnedHeadersBeforeCol(api, col, isRtl) {
|
|
172
172
|
const colIndex = parseCellColIndex(col);
|
|
173
173
|
return findPinnedHeaders({
|
|
174
174
|
api,
|
|
175
|
-
position: 'right',
|
|
175
|
+
position: isRtl ? 'left' : 'right',
|
|
176
176
|
colIndex,
|
|
177
|
-
filterFn: index => index < colIndex
|
|
177
|
+
filterFn: index => isRtl ? index > colIndex : index < colIndex
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
180
|
export function findGridHeader(api, field) {
|
|
@@ -125,6 +125,11 @@ DataGridRaw.propTypes = {
|
|
|
125
125
|
* @default 150
|
|
126
126
|
*/
|
|
127
127
|
columnBufferPx: _propTypes.default.number,
|
|
128
|
+
/**
|
|
129
|
+
* Sets the height in pixels of the column group headers in the Data Grid.
|
|
130
|
+
* Inherits the `columnHeaderHeight` value if not set.
|
|
131
|
+
*/
|
|
132
|
+
columnGroupHeaderHeight: _propTypes.default.number,
|
|
128
133
|
columnGroupingModel: _propTypes.default.arrayOf(_propTypes.default.object),
|
|
129
134
|
/**
|
|
130
135
|
* Sets the height in pixel of the column headers in the Data Grid.
|
|
@@ -750,5 +755,10 @@ DataGridRaw.propTypes = {
|
|
|
750
755
|
/**
|
|
751
756
|
* The system prop that allows defining system overrides as well as additional CSS styles.
|
|
752
757
|
*/
|
|
753
|
-
sx: _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.object, _propTypes.default.bool])), _propTypes.default.func, _propTypes.default.object])
|
|
758
|
+
sx: _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.object, _propTypes.default.bool])), _propTypes.default.func, _propTypes.default.object]),
|
|
759
|
+
/**
|
|
760
|
+
* If `true`, the Data Grid will auto span the cells over the rows having the same value.
|
|
761
|
+
* @default false
|
|
762
|
+
*/
|
|
763
|
+
unstable_rowSpanning: _propTypes.default.bool
|
|
754
764
|
};
|
|
@@ -33,6 +33,7 @@ var _useGridColumnSpanning = require("../hooks/features/columns/useGridColumnSpa
|
|
|
33
33
|
var _useGridColumnGrouping = require("../hooks/features/columnGrouping/useGridColumnGrouping");
|
|
34
34
|
var _virtualization = require("../hooks/features/virtualization");
|
|
35
35
|
var _useGridColumnResize = require("../hooks/features/columnResize/useGridColumnResize");
|
|
36
|
+
var _useGridRowSpanning = require("../hooks/features/rows/useGridRowSpanning");
|
|
36
37
|
const useDataGridComponent = (inputApiRef, props) => {
|
|
37
38
|
const apiRef = (0, _useGridInitialization.useGridInitialization)(inputApiRef, props);
|
|
38
39
|
|
|
@@ -54,6 +55,7 @@ const useDataGridComponent = (inputApiRef, props) => {
|
|
|
54
55
|
(0, _useGridInitializeState.useGridInitializeState)(_useGridSorting.sortingStateInitializer, apiRef, props);
|
|
55
56
|
(0, _useGridInitializeState.useGridInitializeState)(_useGridPreferencesPanel.preferencePanelStateInitializer, apiRef, props);
|
|
56
57
|
(0, _useGridInitializeState.useGridInitializeState)(_useGridFilter.filterStateInitializer, apiRef, props);
|
|
58
|
+
(0, _useGridInitializeState.useGridInitializeState)(_useGridRowSpanning.rowSpanningStateInitializer, apiRef, props);
|
|
57
59
|
(0, _useGridInitializeState.useGridInitializeState)(_useGridDensity.densityStateInitializer, apiRef, props);
|
|
58
60
|
(0, _useGridInitializeState.useGridInitializeState)(_useGridColumnResize.columnResizeStateInitializer, apiRef, props);
|
|
59
61
|
(0, _useGridInitializeState.useGridInitializeState)(_useGridPagination.paginationStateInitializer, apiRef, props);
|
|
@@ -65,6 +67,7 @@ const useDataGridComponent = (inputApiRef, props) => {
|
|
|
65
67
|
(0, _useGridRowSelection.useGridRowSelection)(apiRef, props);
|
|
66
68
|
(0, _useGridColumns.useGridColumns)(apiRef, props);
|
|
67
69
|
(0, _useGridRows.useGridRows)(apiRef, props);
|
|
70
|
+
(0, _useGridRowSpanning.useGridRowSpanning)(apiRef, props);
|
|
68
71
|
(0, _useGridParamsApi.useGridParamsApi)(apiRef);
|
|
69
72
|
(0, _useGridColumnSpanning.useGridColumnSpanning)(apiRef);
|
|
70
73
|
(0, _useGridColumnGrouping.useGridColumnGrouping)(apiRef, props);
|
|
@@ -81,7 +81,8 @@ const DATA_GRID_PROPS_DEFAULT_VALUES = exports.DATA_GRID_PROPS_DEFAULT_VALUES =
|
|
|
81
81
|
showColumnVerticalBorder: false,
|
|
82
82
|
sortingMode: 'client',
|
|
83
83
|
sortingOrder: ['asc', 'desc', null],
|
|
84
|
-
throttleRowsMs: 0
|
|
84
|
+
throttleRowsMs: 0,
|
|
85
|
+
unstable_rowSpanning: false
|
|
85
86
|
};
|
|
86
87
|
const defaultSlots = _defaultGridSlotsComponents.DATA_GRID_DEFAULT_SLOTS_COMPONENTS;
|
|
87
88
|
const useDataGridProps = inProps => {
|
|
@@ -346,6 +346,7 @@ process.env.NODE_ENV !== "production" ? GridRow.propTypes = {
|
|
|
346
346
|
height: _propTypes.default.number.isRequired,
|
|
347
347
|
width: _propTypes.default.number.isRequired
|
|
348
348
|
}).isRequired,
|
|
349
|
+
groupHeaderHeight: _propTypes.default.number.isRequired,
|
|
349
350
|
hasScrollX: _propTypes.default.bool.isRequired,
|
|
350
351
|
hasScrollY: _propTypes.default.bool.isRequired,
|
|
351
352
|
headerFilterHeight: _propTypes.default.number.isRequired,
|
|
@@ -13,6 +13,7 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
|
13
13
|
var _clsx = _interopRequireDefault(require("clsx"));
|
|
14
14
|
var _utils = require("@mui/utils");
|
|
15
15
|
var _fastMemo = require("@mui/x-internals/fastMemo");
|
|
16
|
+
var _RtlProvider = require("@mui/system/RtlProvider");
|
|
16
17
|
var _doesSupportPreventScroll = require("../../utils/doesSupportPreventScroll");
|
|
17
18
|
var _gridClasses = require("../../constants/gridClasses");
|
|
18
19
|
var _models = require("../../models");
|
|
@@ -23,6 +24,7 @@ var _gridFocusStateSelector = require("../../hooks/features/focus/gridFocusState
|
|
|
23
24
|
var _useGridParamsApi = require("../../hooks/features/rows/useGridParamsApi");
|
|
24
25
|
var _cellBorderUtils = require("../../utils/cellBorderUtils");
|
|
25
26
|
var _gridColumnsInterfaces = require("../../hooks/features/columns/gridColumnsInterfaces");
|
|
27
|
+
var _gridRowSpanningSelectors = require("../../hooks/features/rows/gridRowSpanningSelectors");
|
|
26
28
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
27
29
|
const _excluded = ["column", "rowId", "editCellState", "align", "children", "colIndex", "width", "className", "style", "gridHasScrollX", "colSpan", "disableDragEvents", "isNotVisible", "pinnedOffset", "pinnedPosition", "sectionIndex", "sectionLength", "gridHasFiller", "onClick", "onDoubleClick", "onMouseDown", "onMouseUp", "onMouseOver", "onKeyDown", "onKeyUp", "onDragEnter", "onDragOver"],
|
|
28
30
|
_excluded2 = ["changeReason", "unstable_updateValueOnRender"];
|
|
@@ -114,6 +116,7 @@ const GridCell = /*#__PURE__*/React.forwardRef(function GridCell(props, ref) {
|
|
|
114
116
|
other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
|
|
115
117
|
const apiRef = (0, _useGridApiContext.useGridApiContext)();
|
|
116
118
|
const rootProps = (0, _useGridRootProps.useGridRootProps)();
|
|
119
|
+
const isRtl = (0, _RtlProvider.useRtl)();
|
|
117
120
|
const field = column.field;
|
|
118
121
|
const cellParams = (0, _useGridSelector.useGridSelector)(apiRef, () => {
|
|
119
122
|
// This is required because `.getCellParams` tries to get the `state.rows.tree` entry
|
|
@@ -134,6 +137,8 @@ const GridCell = /*#__PURE__*/React.forwardRef(function GridCell(props, ref) {
|
|
|
134
137
|
id: rowId,
|
|
135
138
|
field
|
|
136
139
|
}));
|
|
140
|
+
const hiddenCells = (0, _useGridSelector.useGridSelector)(apiRef, _gridRowSpanningSelectors.gridRowSpanningHiddenCellsSelector);
|
|
141
|
+
const spannedCells = (0, _useGridSelector.useGridSelector)(apiRef, _gridRowSpanningSelectors.gridRowSpanningSpannedCellsSelector);
|
|
137
142
|
const {
|
|
138
143
|
cellMode,
|
|
139
144
|
hasFocus,
|
|
@@ -206,6 +211,8 @@ const GridCell = /*#__PURE__*/React.forwardRef(function GridCell(props, ref) {
|
|
|
206
211
|
propHandler(event);
|
|
207
212
|
}
|
|
208
213
|
}, [apiRef, field, rowId]);
|
|
214
|
+
const isCellRowSpanned = hiddenCells[rowId]?.[field] ?? false;
|
|
215
|
+
const rowSpan = spannedCells[rowId]?.[field] ?? 1;
|
|
209
216
|
const style = React.useMemo(() => {
|
|
210
217
|
if (isNotVisible) {
|
|
211
218
|
return {
|
|
@@ -218,14 +225,21 @@ const GridCell = /*#__PURE__*/React.forwardRef(function GridCell(props, ref) {
|
|
|
218
225
|
const cellStyle = (0, _extends2.default)({
|
|
219
226
|
'--width': `${width}px`
|
|
220
227
|
}, styleProp);
|
|
221
|
-
|
|
222
|
-
|
|
228
|
+
const isLeftPinned = pinnedPosition === PinnedPosition.LEFT;
|
|
229
|
+
const isRightPinned = pinnedPosition === PinnedPosition.RIGHT;
|
|
230
|
+
if (isLeftPinned || isRightPinned) {
|
|
231
|
+
let side = isLeftPinned ? 'left' : 'right';
|
|
232
|
+
if (isRtl) {
|
|
233
|
+
side = isLeftPinned ? 'right' : 'left';
|
|
234
|
+
}
|
|
235
|
+
cellStyle[side] = pinnedOffset;
|
|
223
236
|
}
|
|
224
|
-
if (
|
|
225
|
-
cellStyle.
|
|
237
|
+
if (rowSpan > 1) {
|
|
238
|
+
cellStyle.height = `calc(var(--height) * ${rowSpan})`;
|
|
239
|
+
cellStyle.zIndex = 5;
|
|
226
240
|
}
|
|
227
241
|
return cellStyle;
|
|
228
|
-
}, [width, isNotVisible, styleProp, pinnedOffset, pinnedPosition]);
|
|
242
|
+
}, [width, isNotVisible, styleProp, pinnedOffset, pinnedPosition, isRtl, rowSpan]);
|
|
229
243
|
React.useEffect(() => {
|
|
230
244
|
if (!hasFocus || cellMode === _models.GridCellModes.Edit) {
|
|
231
245
|
return;
|
|
@@ -245,6 +259,16 @@ const GridCell = /*#__PURE__*/React.forwardRef(function GridCell(props, ref) {
|
|
|
245
259
|
}
|
|
246
260
|
}
|
|
247
261
|
}, [hasFocus, cellMode, apiRef]);
|
|
262
|
+
if (isCellRowSpanned) {
|
|
263
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
264
|
+
"data-colindex": colIndex,
|
|
265
|
+
role: "presentation",
|
|
266
|
+
style: (0, _extends2.default)({}, style, {
|
|
267
|
+
minWidth: 'var(--width)',
|
|
268
|
+
maxWidth: 'var(--width)'
|
|
269
|
+
})
|
|
270
|
+
});
|
|
271
|
+
}
|
|
248
272
|
if (cellParams === EMPTY_CELL_PARAMS) {
|
|
249
273
|
return null;
|
|
250
274
|
}
|
|
@@ -305,6 +329,7 @@ const GridCell = /*#__PURE__*/React.forwardRef(function GridCell(props, ref) {
|
|
|
305
329
|
"data-colindex": colIndex,
|
|
306
330
|
"aria-colindex": colIndex + 1,
|
|
307
331
|
"aria-colspan": colSpan,
|
|
332
|
+
"aria-rowspan": rowSpan,
|
|
308
333
|
style: style,
|
|
309
334
|
title: title,
|
|
310
335
|
tabIndex: tabIndex,
|