@mui/x-data-grid 7.29.11 → 7.29.13
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 +64 -0
- package/components/columnHeaders/GridColumnGroupHeader.js +13 -1
- package/components/columnHeaders/GridColumnHeaderItem.js +12 -3
- package/components/columnHeaders/GridGenericColumnHeaderItem.js +0 -14
- package/hooks/features/dimensions/useGridDimensions.js +46 -1
- package/hooks/features/export/useGridPrintExport.js +2 -7
- package/index.js +1 -1
- package/modern/components/columnHeaders/GridColumnGroupHeader.js +13 -1
- package/modern/components/columnHeaders/GridColumnHeaderItem.js +12 -3
- package/modern/components/columnHeaders/GridGenericColumnHeaderItem.js +0 -14
- package/modern/hooks/features/dimensions/useGridDimensions.js +46 -1
- package/modern/hooks/features/export/useGridPrintExport.js +2 -7
- package/modern/index.js +1 -1
- package/node/components/columnHeaders/GridColumnGroupHeader.js +13 -1
- package/node/components/columnHeaders/GridColumnHeaderItem.js +12 -3
- package/node/components/columnHeaders/GridGenericColumnHeaderItem.js +0 -14
- package/node/hooks/features/dimensions/useGridDimensions.js +46 -1
- package/node/hooks/features/export/useGridPrintExport.js +2 -7
- package/node/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,70 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## 7.29.13
|
|
7
|
+
|
|
8
|
+
_Apr 27, 2026_
|
|
9
|
+
|
|
10
|
+
We'd like to extend a big thank you to the 4 contributors who made this release possible. Here are some highlights ✨:
|
|
11
|
+
|
|
12
|
+
- 🐞 Bugfixes
|
|
13
|
+
|
|
14
|
+
The following are all team members who have contributed to this release:
|
|
15
|
+
@arminmeh, @dav-is, @LukasTy, @michelengelen
|
|
16
|
+
|
|
17
|
+
### Data Grid
|
|
18
|
+
|
|
19
|
+
#### `@mui/x-data-grid@7.29.13`
|
|
20
|
+
|
|
21
|
+
- [DataGrid] Prevent repeated `hasScrollbar` state updates (#21849) @arminmeh
|
|
22
|
+
|
|
23
|
+
#### `@mui/x-data-grid-pro@7.29.13` [](https://mui.com/r/x-pro-svg-link "Pro plan")
|
|
24
|
+
|
|
25
|
+
Same changes as in `@mui/x-data-grid@7.29.13`.
|
|
26
|
+
|
|
27
|
+
#### `@mui/x-data-grid-premium@7.29.13` [](https://mui.com/r/x-premium-svg-link "Premium plan")
|
|
28
|
+
|
|
29
|
+
Same changes as in `@mui/x-data-grid-pro@7.29.13`.
|
|
30
|
+
|
|
31
|
+
### Docs
|
|
32
|
+
|
|
33
|
+
- [docs] Add `v9` as root path link and move `v8` under subpath (#22037) @LukasTy
|
|
34
|
+
- [docs] Add check for auto-generated group rows in `renderCountry` (#22143) @michelengelen
|
|
35
|
+
- [docs] Remove obsolete v7 deprecation warning for `dayOfWeekFormatter` (@LukasTy) (#22121)
|
|
36
|
+
|
|
37
|
+
### Miscellaneous
|
|
38
|
+
|
|
39
|
+
- [docs-infra] Set `SEARCH_INDEX` Env for v7 (#21876) @dav-is
|
|
40
|
+
|
|
41
|
+
## 7.29.12
|
|
42
|
+
|
|
43
|
+
_Nov 26, 2025_
|
|
44
|
+
|
|
45
|
+
We'd like to extend a big thank you to the 2 contributors who made this release possible. Here are some highlights ✨:
|
|
46
|
+
|
|
47
|
+
- 🐞 Bugfixes
|
|
48
|
+
|
|
49
|
+
Special thanks go out to the community members for their valuable contributions:
|
|
50
|
+
@m2mathew
|
|
51
|
+
|
|
52
|
+
Following are all team members who have contributed to this release:
|
|
53
|
+
@arminmeh
|
|
54
|
+
|
|
55
|
+
### Data Grid
|
|
56
|
+
|
|
57
|
+
#### `@mui/x-data-grid@7.29.12`
|
|
58
|
+
|
|
59
|
+
- [DataGrid] Avoid automatic scroll back to the focused element after it leaves the viewport (#20417) @arminmeh
|
|
60
|
+
- [DataGrid] Fix missing rows in the print export window (#20157) @m2mathew
|
|
61
|
+
|
|
62
|
+
#### `@mui/x-data-grid-pro@7.29.12` [](https://mui.com/r/x-pro-svg-link "Pro plan")
|
|
63
|
+
|
|
64
|
+
Same changes as in `@mui/x-data-grid@7.29.12`.
|
|
65
|
+
|
|
66
|
+
#### `@mui/x-data-grid-premium@7.29.12` [](https://mui.com/r/x-premium-svg-link "Premium plan")
|
|
67
|
+
|
|
68
|
+
Same changes as in `@mui/x-data-grid-pro@7.29.12`.
|
|
69
|
+
|
|
6
70
|
## 7.29.11
|
|
7
71
|
|
|
8
72
|
_Nov 19, 2025_
|
|
@@ -2,6 +2,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { unstable_useId as useId, unstable_composeClasses as composeClasses } from '@mui/utils';
|
|
4
4
|
import { useRtl } from '@mui/system/RtlProvider';
|
|
5
|
+
import { doesSupportPreventScroll } from "../../utils/doesSupportPreventScroll.js";
|
|
5
6
|
import { getDataGridUtilityClass } from "../../constants/gridClasses.js";
|
|
6
7
|
import { useGridRootProps } from "../../hooks/utils/useGridRootProps.js";
|
|
7
8
|
import { gridColumnGroupsLookupSelector } from "../../hooks/features/columnGrouping/gridColumnGroupsSelector.js";
|
|
@@ -86,7 +87,18 @@ function GridColumnGroupHeader(props) {
|
|
|
86
87
|
if (hasFocus) {
|
|
87
88
|
const focusableElement = headerCellRef.current.querySelector('[tabindex="0"]');
|
|
88
89
|
const elementToFocus = focusableElement || headerCellRef.current;
|
|
89
|
-
elementToFocus
|
|
90
|
+
if (!elementToFocus) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (doesSupportPreventScroll()) {
|
|
94
|
+
elementToFocus.focus({
|
|
95
|
+
preventScroll: true
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
const scrollPosition = apiRef.current.getScrollPosition();
|
|
99
|
+
elementToFocus.focus();
|
|
100
|
+
apiRef.current.scroll(scrollPosition);
|
|
101
|
+
}
|
|
90
102
|
}
|
|
91
103
|
}, [apiRef, hasFocus]);
|
|
92
104
|
const publish = React.useCallback(eventName => event => {
|
|
@@ -5,6 +5,7 @@ import clsx from 'clsx';
|
|
|
5
5
|
import { unstable_composeClasses as composeClasses, unstable_useId as useId } from '@mui/utils';
|
|
6
6
|
import { fastMemo } from '@mui/x-internals/fastMemo';
|
|
7
7
|
import { useRtl } from '@mui/system/RtlProvider';
|
|
8
|
+
import { doesSupportPreventScroll } from "../../utils/doesSupportPreventScroll.js";
|
|
8
9
|
import { useGridPrivateApiContext } from "../../hooks/utils/useGridPrivateApiContext.js";
|
|
9
10
|
import { ColumnHeaderMenuIcon } from "./ColumnHeaderMenuIcon.js";
|
|
10
11
|
import { GridColumnHeaderMenu } from "../menu/columnMenu/GridColumnHeaderMenu.js";
|
|
@@ -160,9 +161,17 @@ function GridColumnHeaderItem(props) {
|
|
|
160
161
|
if (hasFocus && !columnMenuState.open) {
|
|
161
162
|
const focusableElement = headerCellRef.current.querySelector('[tabindex="0"]');
|
|
162
163
|
const elementToFocus = focusableElement || headerCellRef.current;
|
|
163
|
-
elementToFocus
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
if (!elementToFocus) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (doesSupportPreventScroll()) {
|
|
168
|
+
elementToFocus.focus({
|
|
169
|
+
preventScroll: true
|
|
170
|
+
});
|
|
171
|
+
} else {
|
|
172
|
+
const scrollPosition = apiRef.current.getScrollPosition();
|
|
173
|
+
elementToFocus.focus();
|
|
174
|
+
apiRef.current.scroll(scrollPosition);
|
|
166
175
|
}
|
|
167
176
|
}
|
|
168
177
|
}, [apiRef, hasFocus]);
|
|
@@ -5,7 +5,6 @@ import * as React from 'react';
|
|
|
5
5
|
import clsx from 'clsx';
|
|
6
6
|
import { unstable_useForkRef as useForkRef } from '@mui/utils';
|
|
7
7
|
import { forwardRef } from '@mui/x-internals/forwardRef';
|
|
8
|
-
import { useGridPrivateApiContext } from "../../hooks/utils/useGridPrivateApiContext.js";
|
|
9
8
|
import { GridColumnHeaderTitle } from "./GridColumnHeaderTitle.js";
|
|
10
9
|
import { GridColumnHeaderSeparator } from "./GridColumnHeaderSeparator.js";
|
|
11
10
|
import { useGridRootProps } from "../../hooks/utils/useGridRootProps.js";
|
|
@@ -17,7 +16,6 @@ const GridGenericColumnHeaderItem = forwardRef(function GridGenericColumnHeaderI
|
|
|
17
16
|
height,
|
|
18
17
|
isResizing,
|
|
19
18
|
sortDirection,
|
|
20
|
-
hasFocus,
|
|
21
19
|
tabIndex,
|
|
22
20
|
separatorSide,
|
|
23
21
|
isDraggable,
|
|
@@ -35,7 +33,6 @@ const GridGenericColumnHeaderItem = forwardRef(function GridGenericColumnHeaderI
|
|
|
35
33
|
style
|
|
36
34
|
} = props,
|
|
37
35
|
other = _objectWithoutPropertiesLoose(props, _excluded);
|
|
38
|
-
const apiRef = useGridPrivateApiContext();
|
|
39
36
|
const rootProps = useGridRootProps();
|
|
40
37
|
const headerCellRef = React.useRef(null);
|
|
41
38
|
const handleRef = useForkRef(headerCellRef, ref);
|
|
@@ -43,17 +40,6 @@ const GridGenericColumnHeaderItem = forwardRef(function GridGenericColumnHeaderI
|
|
|
43
40
|
if (sortDirection != null) {
|
|
44
41
|
ariaSort = sortDirection === 'asc' ? 'ascending' : 'descending';
|
|
45
42
|
}
|
|
46
|
-
React.useLayoutEffect(() => {
|
|
47
|
-
const columnMenuState = apiRef.current.state.columnMenu;
|
|
48
|
-
if (hasFocus && !columnMenuState.open) {
|
|
49
|
-
const focusableElement = headerCellRef.current.querySelector('[tabindex="0"]');
|
|
50
|
-
const elementToFocus = focusableElement || headerCellRef.current;
|
|
51
|
-
elementToFocus?.focus();
|
|
52
|
-
if (apiRef.current.columnHeadersContainerRef?.current) {
|
|
53
|
-
apiRef.current.columnHeadersContainerRef.current.scrollLeft = 0;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}, [apiRef, hasFocus]);
|
|
57
43
|
return /*#__PURE__*/_jsxs("div", _extends({
|
|
58
44
|
className: clsx(classes.root, headerClassName),
|
|
59
45
|
style: _extends({}, style, {
|
|
@@ -67,6 +67,21 @@ export function useGridDimensions(apiRef, props) {
|
|
|
67
67
|
const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
|
|
68
68
|
const columnsTotalWidth = useGridSelector(apiRef, columnsTotalWidthSelector);
|
|
69
69
|
const isFirstSizing = React.useRef(true);
|
|
70
|
+
|
|
71
|
+
// Vertical scrollbar oscillation detector.
|
|
72
|
+
// Counts consecutive hasScrollY flips that happen with no row-height change.
|
|
73
|
+
// After 2 flips it is certainly a layout feedback loop, so every further flip
|
|
74
|
+
// is forced to false (no scrollbar). The counter resets when row heights change.
|
|
75
|
+
// Only vertical scrollbar can oscillate because column widths are never 'auto'.
|
|
76
|
+
// https://github.com/mui/mui-x/issues/20539
|
|
77
|
+
const scrollYOscillation = React.useRef({
|
|
78
|
+
counter: 0,
|
|
79
|
+
heights: {
|
|
80
|
+
content: 0,
|
|
81
|
+
pinnedTop: 0,
|
|
82
|
+
pinnedBottom: 0
|
|
83
|
+
}
|
|
84
|
+
});
|
|
70
85
|
const {
|
|
71
86
|
rowHeight,
|
|
72
87
|
headerHeight,
|
|
@@ -134,6 +149,7 @@ export function useGridDimensions(apiRef, props) {
|
|
|
134
149
|
width: nonPinnedColumnsTotalWidth,
|
|
135
150
|
height: roundToDecimalPlaces(rowsMeta.currentPageTotalHeight, 1)
|
|
136
151
|
};
|
|
152
|
+
const prevDimensions = apiRef.current.state.dimensions;
|
|
137
153
|
let viewportOuterSize;
|
|
138
154
|
let viewportInnerSize;
|
|
139
155
|
let hasScrollX = false;
|
|
@@ -171,6 +187,36 @@ export function useGridDimensions(apiRef, props) {
|
|
|
171
187
|
hasScrollY = content.height + scrollbarSize > container.height;
|
|
172
188
|
}
|
|
173
189
|
}
|
|
190
|
+
|
|
191
|
+
// Detect vertical scrollbar oscillation.
|
|
192
|
+
// Track consecutive hasScrollY flips with no row-height change.
|
|
193
|
+
// Once confirmed (≥ 2 flips), force hasScrollY off — the scrollbar is
|
|
194
|
+
// not genuinely needed, it is a layout feedback loop caused by stale
|
|
195
|
+
// rootSize or the horizontal scrollbar's height cascading.
|
|
196
|
+
{
|
|
197
|
+
const osc = scrollYOscillation.current;
|
|
198
|
+
const heightsChanged = rowsMeta.currentPageTotalHeight !== osc.heights.content || rowsMeta.pinnedTopRowsTotalHeight !== osc.heights.pinnedTop || rowsMeta.pinnedBottomRowsTotalHeight !== osc.heights.pinnedBottom;
|
|
199
|
+
if (heightsChanged) {
|
|
200
|
+
osc.counter = 0;
|
|
201
|
+
osc.heights = {
|
|
202
|
+
content: rowsMeta.currentPageTotalHeight,
|
|
203
|
+
pinnedTop: rowsMeta.pinnedTopRowsTotalHeight,
|
|
204
|
+
pinnedBottom: rowsMeta.pinnedBottomRowsTotalHeight
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
if (prevDimensions.isReady && hasScrollY !== prevDimensions.hasScrollY) {
|
|
208
|
+
if (!heightsChanged) {
|
|
209
|
+
osc.counter += 1;
|
|
210
|
+
}
|
|
211
|
+
if (osc.counter >= 2) {
|
|
212
|
+
hasScrollY = false;
|
|
213
|
+
// Recompute hasScrollX without the vertical scrollbar's width impact,
|
|
214
|
+
// otherwise the cascade (hasScrollY → narrower viewport → hasScrollX)
|
|
215
|
+
// keeps the horizontal scrollbar/filler alive and the root keeps resizing.
|
|
216
|
+
hasScrollX = hasScrollXIfNoYScrollBar;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
174
220
|
if (hasScrollY) {
|
|
175
221
|
viewportInnerSize.width -= scrollbarSize;
|
|
176
222
|
}
|
|
@@ -205,7 +251,6 @@ export function useGridDimensions(apiRef, props) {
|
|
|
205
251
|
topContainerHeight,
|
|
206
252
|
bottomContainerHeight
|
|
207
253
|
};
|
|
208
|
-
const prevDimensions = apiRef.current.state.dimensions;
|
|
209
254
|
if (isDeepEqual(prevDimensions, newDimensions)) {
|
|
210
255
|
return;
|
|
211
256
|
}
|
|
@@ -201,7 +201,7 @@ export const useGridPrintExport = (apiRef, props) => {
|
|
|
201
201
|
// Revert grid to previous state
|
|
202
202
|
apiRef.current.restoreState(previousGridState.current || {});
|
|
203
203
|
if (!previousGridState.current?.columns?.columnVisibilityModel) {
|
|
204
|
-
// if the apiRef.current.exportState(); did not
|
|
204
|
+
// if the apiRef.current.exportState(); did not export the column visibility, we update it
|
|
205
205
|
apiRef.current.setColumnVisibilityModel(previousColumnVisibility.current);
|
|
206
206
|
}
|
|
207
207
|
apiRef.current.setState(state => _extends({}, state, {
|
|
@@ -238,12 +238,7 @@ export const useGridPrintExport = (apiRef, props) => {
|
|
|
238
238
|
}));
|
|
239
239
|
}
|
|
240
240
|
previousVirtualizationState.current = apiRef.current.state.virtualization;
|
|
241
|
-
apiRef.current.
|
|
242
|
-
virtualization: _extends({}, state.virtualization, {
|
|
243
|
-
enabled: false,
|
|
244
|
-
enabledForColumns: false
|
|
245
|
-
})
|
|
246
|
-
}));
|
|
241
|
+
apiRef.current.unstable_setVirtualization(false);
|
|
247
242
|
await updateGridColumnsForPrint(options?.fields, options?.allColumns, options?.includeCheckboxes);
|
|
248
243
|
updateGridRowsForPrint(options?.getRowsToExport ?? defaultGetRowsToExport);
|
|
249
244
|
await raf(); // wait for the state changes to take action
|
package/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { unstable_useId as useId, unstable_composeClasses as composeClasses } from '@mui/utils';
|
|
4
4
|
import { useRtl } from '@mui/system/RtlProvider';
|
|
5
|
+
import { doesSupportPreventScroll } from "../../utils/doesSupportPreventScroll.js";
|
|
5
6
|
import { getDataGridUtilityClass } from "../../constants/gridClasses.js";
|
|
6
7
|
import { useGridRootProps } from "../../hooks/utils/useGridRootProps.js";
|
|
7
8
|
import { gridColumnGroupsLookupSelector } from "../../hooks/features/columnGrouping/gridColumnGroupsSelector.js";
|
|
@@ -86,7 +87,18 @@ function GridColumnGroupHeader(props) {
|
|
|
86
87
|
if (hasFocus) {
|
|
87
88
|
const focusableElement = headerCellRef.current.querySelector('[tabindex="0"]');
|
|
88
89
|
const elementToFocus = focusableElement || headerCellRef.current;
|
|
89
|
-
elementToFocus
|
|
90
|
+
if (!elementToFocus) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (doesSupportPreventScroll()) {
|
|
94
|
+
elementToFocus.focus({
|
|
95
|
+
preventScroll: true
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
const scrollPosition = apiRef.current.getScrollPosition();
|
|
99
|
+
elementToFocus.focus();
|
|
100
|
+
apiRef.current.scroll(scrollPosition);
|
|
101
|
+
}
|
|
90
102
|
}
|
|
91
103
|
}, [apiRef, hasFocus]);
|
|
92
104
|
const publish = React.useCallback(eventName => event => {
|
|
@@ -5,6 +5,7 @@ import clsx from 'clsx';
|
|
|
5
5
|
import { unstable_composeClasses as composeClasses, unstable_useId as useId } from '@mui/utils';
|
|
6
6
|
import { fastMemo } from '@mui/x-internals/fastMemo';
|
|
7
7
|
import { useRtl } from '@mui/system/RtlProvider';
|
|
8
|
+
import { doesSupportPreventScroll } from "../../utils/doesSupportPreventScroll.js";
|
|
8
9
|
import { useGridPrivateApiContext } from "../../hooks/utils/useGridPrivateApiContext.js";
|
|
9
10
|
import { ColumnHeaderMenuIcon } from "./ColumnHeaderMenuIcon.js";
|
|
10
11
|
import { GridColumnHeaderMenu } from "../menu/columnMenu/GridColumnHeaderMenu.js";
|
|
@@ -160,9 +161,17 @@ function GridColumnHeaderItem(props) {
|
|
|
160
161
|
if (hasFocus && !columnMenuState.open) {
|
|
161
162
|
const focusableElement = headerCellRef.current.querySelector('[tabindex="0"]');
|
|
162
163
|
const elementToFocus = focusableElement || headerCellRef.current;
|
|
163
|
-
elementToFocus
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
if (!elementToFocus) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (doesSupportPreventScroll()) {
|
|
168
|
+
elementToFocus.focus({
|
|
169
|
+
preventScroll: true
|
|
170
|
+
});
|
|
171
|
+
} else {
|
|
172
|
+
const scrollPosition = apiRef.current.getScrollPosition();
|
|
173
|
+
elementToFocus.focus();
|
|
174
|
+
apiRef.current.scroll(scrollPosition);
|
|
166
175
|
}
|
|
167
176
|
}
|
|
168
177
|
}, [apiRef, hasFocus]);
|
|
@@ -5,7 +5,6 @@ import * as React from 'react';
|
|
|
5
5
|
import clsx from 'clsx';
|
|
6
6
|
import { unstable_useForkRef as useForkRef } from '@mui/utils';
|
|
7
7
|
import { forwardRef } from '@mui/x-internals/forwardRef';
|
|
8
|
-
import { useGridPrivateApiContext } from "../../hooks/utils/useGridPrivateApiContext.js";
|
|
9
8
|
import { GridColumnHeaderTitle } from "./GridColumnHeaderTitle.js";
|
|
10
9
|
import { GridColumnHeaderSeparator } from "./GridColumnHeaderSeparator.js";
|
|
11
10
|
import { useGridRootProps } from "../../hooks/utils/useGridRootProps.js";
|
|
@@ -17,7 +16,6 @@ const GridGenericColumnHeaderItem = forwardRef(function GridGenericColumnHeaderI
|
|
|
17
16
|
height,
|
|
18
17
|
isResizing,
|
|
19
18
|
sortDirection,
|
|
20
|
-
hasFocus,
|
|
21
19
|
tabIndex,
|
|
22
20
|
separatorSide,
|
|
23
21
|
isDraggable,
|
|
@@ -35,7 +33,6 @@ const GridGenericColumnHeaderItem = forwardRef(function GridGenericColumnHeaderI
|
|
|
35
33
|
style
|
|
36
34
|
} = props,
|
|
37
35
|
other = _objectWithoutPropertiesLoose(props, _excluded);
|
|
38
|
-
const apiRef = useGridPrivateApiContext();
|
|
39
36
|
const rootProps = useGridRootProps();
|
|
40
37
|
const headerCellRef = React.useRef(null);
|
|
41
38
|
const handleRef = useForkRef(headerCellRef, ref);
|
|
@@ -43,17 +40,6 @@ const GridGenericColumnHeaderItem = forwardRef(function GridGenericColumnHeaderI
|
|
|
43
40
|
if (sortDirection != null) {
|
|
44
41
|
ariaSort = sortDirection === 'asc' ? 'ascending' : 'descending';
|
|
45
42
|
}
|
|
46
|
-
React.useLayoutEffect(() => {
|
|
47
|
-
const columnMenuState = apiRef.current.state.columnMenu;
|
|
48
|
-
if (hasFocus && !columnMenuState.open) {
|
|
49
|
-
const focusableElement = headerCellRef.current.querySelector('[tabindex="0"]');
|
|
50
|
-
const elementToFocus = focusableElement || headerCellRef.current;
|
|
51
|
-
elementToFocus?.focus();
|
|
52
|
-
if (apiRef.current.columnHeadersContainerRef?.current) {
|
|
53
|
-
apiRef.current.columnHeadersContainerRef.current.scrollLeft = 0;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}, [apiRef, hasFocus]);
|
|
57
43
|
return /*#__PURE__*/_jsxs("div", _extends({
|
|
58
44
|
className: clsx(classes.root, headerClassName),
|
|
59
45
|
style: _extends({}, style, {
|
|
@@ -67,6 +67,21 @@ export function useGridDimensions(apiRef, props) {
|
|
|
67
67
|
const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
|
|
68
68
|
const columnsTotalWidth = useGridSelector(apiRef, columnsTotalWidthSelector);
|
|
69
69
|
const isFirstSizing = React.useRef(true);
|
|
70
|
+
|
|
71
|
+
// Vertical scrollbar oscillation detector.
|
|
72
|
+
// Counts consecutive hasScrollY flips that happen with no row-height change.
|
|
73
|
+
// After 2 flips it is certainly a layout feedback loop, so every further flip
|
|
74
|
+
// is forced to false (no scrollbar). The counter resets when row heights change.
|
|
75
|
+
// Only vertical scrollbar can oscillate because column widths are never 'auto'.
|
|
76
|
+
// https://github.com/mui/mui-x/issues/20539
|
|
77
|
+
const scrollYOscillation = React.useRef({
|
|
78
|
+
counter: 0,
|
|
79
|
+
heights: {
|
|
80
|
+
content: 0,
|
|
81
|
+
pinnedTop: 0,
|
|
82
|
+
pinnedBottom: 0
|
|
83
|
+
}
|
|
84
|
+
});
|
|
70
85
|
const {
|
|
71
86
|
rowHeight,
|
|
72
87
|
headerHeight,
|
|
@@ -134,6 +149,7 @@ export function useGridDimensions(apiRef, props) {
|
|
|
134
149
|
width: nonPinnedColumnsTotalWidth,
|
|
135
150
|
height: roundToDecimalPlaces(rowsMeta.currentPageTotalHeight, 1)
|
|
136
151
|
};
|
|
152
|
+
const prevDimensions = apiRef.current.state.dimensions;
|
|
137
153
|
let viewportOuterSize;
|
|
138
154
|
let viewportInnerSize;
|
|
139
155
|
let hasScrollX = false;
|
|
@@ -171,6 +187,36 @@ export function useGridDimensions(apiRef, props) {
|
|
|
171
187
|
hasScrollY = content.height + scrollbarSize > container.height;
|
|
172
188
|
}
|
|
173
189
|
}
|
|
190
|
+
|
|
191
|
+
// Detect vertical scrollbar oscillation.
|
|
192
|
+
// Track consecutive hasScrollY flips with no row-height change.
|
|
193
|
+
// Once confirmed (≥ 2 flips), force hasScrollY off — the scrollbar is
|
|
194
|
+
// not genuinely needed, it is a layout feedback loop caused by stale
|
|
195
|
+
// rootSize or the horizontal scrollbar's height cascading.
|
|
196
|
+
{
|
|
197
|
+
const osc = scrollYOscillation.current;
|
|
198
|
+
const heightsChanged = rowsMeta.currentPageTotalHeight !== osc.heights.content || rowsMeta.pinnedTopRowsTotalHeight !== osc.heights.pinnedTop || rowsMeta.pinnedBottomRowsTotalHeight !== osc.heights.pinnedBottom;
|
|
199
|
+
if (heightsChanged) {
|
|
200
|
+
osc.counter = 0;
|
|
201
|
+
osc.heights = {
|
|
202
|
+
content: rowsMeta.currentPageTotalHeight,
|
|
203
|
+
pinnedTop: rowsMeta.pinnedTopRowsTotalHeight,
|
|
204
|
+
pinnedBottom: rowsMeta.pinnedBottomRowsTotalHeight
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
if (prevDimensions.isReady && hasScrollY !== prevDimensions.hasScrollY) {
|
|
208
|
+
if (!heightsChanged) {
|
|
209
|
+
osc.counter += 1;
|
|
210
|
+
}
|
|
211
|
+
if (osc.counter >= 2) {
|
|
212
|
+
hasScrollY = false;
|
|
213
|
+
// Recompute hasScrollX without the vertical scrollbar's width impact,
|
|
214
|
+
// otherwise the cascade (hasScrollY → narrower viewport → hasScrollX)
|
|
215
|
+
// keeps the horizontal scrollbar/filler alive and the root keeps resizing.
|
|
216
|
+
hasScrollX = hasScrollXIfNoYScrollBar;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
174
220
|
if (hasScrollY) {
|
|
175
221
|
viewportInnerSize.width -= scrollbarSize;
|
|
176
222
|
}
|
|
@@ -205,7 +251,6 @@ export function useGridDimensions(apiRef, props) {
|
|
|
205
251
|
topContainerHeight,
|
|
206
252
|
bottomContainerHeight
|
|
207
253
|
};
|
|
208
|
-
const prevDimensions = apiRef.current.state.dimensions;
|
|
209
254
|
if (isDeepEqual(prevDimensions, newDimensions)) {
|
|
210
255
|
return;
|
|
211
256
|
}
|
|
@@ -201,7 +201,7 @@ export const useGridPrintExport = (apiRef, props) => {
|
|
|
201
201
|
// Revert grid to previous state
|
|
202
202
|
apiRef.current.restoreState(previousGridState.current || {});
|
|
203
203
|
if (!previousGridState.current?.columns?.columnVisibilityModel) {
|
|
204
|
-
// if the apiRef.current.exportState(); did not
|
|
204
|
+
// if the apiRef.current.exportState(); did not export the column visibility, we update it
|
|
205
205
|
apiRef.current.setColumnVisibilityModel(previousColumnVisibility.current);
|
|
206
206
|
}
|
|
207
207
|
apiRef.current.setState(state => _extends({}, state, {
|
|
@@ -238,12 +238,7 @@ export const useGridPrintExport = (apiRef, props) => {
|
|
|
238
238
|
}));
|
|
239
239
|
}
|
|
240
240
|
previousVirtualizationState.current = apiRef.current.state.virtualization;
|
|
241
|
-
apiRef.current.
|
|
242
|
-
virtualization: _extends({}, state.virtualization, {
|
|
243
|
-
enabled: false,
|
|
244
|
-
enabledForColumns: false
|
|
245
|
-
})
|
|
246
|
-
}));
|
|
241
|
+
apiRef.current.unstable_setVirtualization(false);
|
|
247
242
|
await updateGridColumnsForPrint(options?.fields, options?.allColumns, options?.includeCheckboxes);
|
|
248
243
|
updateGridRowsForPrint(options?.getRowsToExport ?? defaultGetRowsToExport);
|
|
249
244
|
await raf(); // wait for the state changes to take action
|
package/modern/index.js
CHANGED
|
@@ -10,6 +10,7 @@ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")
|
|
|
10
10
|
var React = _interopRequireWildcard(require("react"));
|
|
11
11
|
var _utils = require("@mui/utils");
|
|
12
12
|
var _RtlProvider = require("@mui/system/RtlProvider");
|
|
13
|
+
var _doesSupportPreventScroll = require("../../utils/doesSupportPreventScroll");
|
|
13
14
|
var _gridClasses = require("../../constants/gridClasses");
|
|
14
15
|
var _useGridRootProps = require("../../hooks/utils/useGridRootProps");
|
|
15
16
|
var _gridColumnGroupsSelector = require("../../hooks/features/columnGrouping/gridColumnGroupsSelector");
|
|
@@ -94,7 +95,18 @@ function GridColumnGroupHeader(props) {
|
|
|
94
95
|
if (hasFocus) {
|
|
95
96
|
const focusableElement = headerCellRef.current.querySelector('[tabindex="0"]');
|
|
96
97
|
const elementToFocus = focusableElement || headerCellRef.current;
|
|
97
|
-
elementToFocus
|
|
98
|
+
if (!elementToFocus) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if ((0, _doesSupportPreventScroll.doesSupportPreventScroll)()) {
|
|
102
|
+
elementToFocus.focus({
|
|
103
|
+
preventScroll: true
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
const scrollPosition = apiRef.current.getScrollPosition();
|
|
107
|
+
elementToFocus.focus();
|
|
108
|
+
apiRef.current.scroll(scrollPosition);
|
|
109
|
+
}
|
|
98
110
|
}
|
|
99
111
|
}, [apiRef, hasFocus]);
|
|
100
112
|
const publish = React.useCallback(eventName => event => {
|
|
@@ -13,6 +13,7 @@ var _clsx = _interopRequireDefault(require("clsx"));
|
|
|
13
13
|
var _utils = require("@mui/utils");
|
|
14
14
|
var _fastMemo = require("@mui/x-internals/fastMemo");
|
|
15
15
|
var _RtlProvider = require("@mui/system/RtlProvider");
|
|
16
|
+
var _doesSupportPreventScroll = require("../../utils/doesSupportPreventScroll");
|
|
16
17
|
var _useGridPrivateApiContext = require("../../hooks/utils/useGridPrivateApiContext");
|
|
17
18
|
var _ColumnHeaderMenuIcon = require("./ColumnHeaderMenuIcon");
|
|
18
19
|
var _GridColumnHeaderMenu = require("../menu/columnMenu/GridColumnHeaderMenu");
|
|
@@ -168,9 +169,17 @@ function GridColumnHeaderItem(props) {
|
|
|
168
169
|
if (hasFocus && !columnMenuState.open) {
|
|
169
170
|
const focusableElement = headerCellRef.current.querySelector('[tabindex="0"]');
|
|
170
171
|
const elementToFocus = focusableElement || headerCellRef.current;
|
|
171
|
-
elementToFocus
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
if (!elementToFocus) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if ((0, _doesSupportPreventScroll.doesSupportPreventScroll)()) {
|
|
176
|
+
elementToFocus.focus({
|
|
177
|
+
preventScroll: true
|
|
178
|
+
});
|
|
179
|
+
} else {
|
|
180
|
+
const scrollPosition = apiRef.current.getScrollPosition();
|
|
181
|
+
elementToFocus.focus();
|
|
182
|
+
apiRef.current.scroll(scrollPosition);
|
|
174
183
|
}
|
|
175
184
|
}
|
|
176
185
|
}, [apiRef, hasFocus]);
|
|
@@ -12,7 +12,6 @@ var React = _interopRequireWildcard(require("react"));
|
|
|
12
12
|
var _clsx = _interopRequireDefault(require("clsx"));
|
|
13
13
|
var _utils = require("@mui/utils");
|
|
14
14
|
var _forwardRef = require("@mui/x-internals/forwardRef");
|
|
15
|
-
var _useGridPrivateApiContext = require("../../hooks/utils/useGridPrivateApiContext");
|
|
16
15
|
var _GridColumnHeaderTitle = require("./GridColumnHeaderTitle");
|
|
17
16
|
var _GridColumnHeaderSeparator = require("./GridColumnHeaderSeparator");
|
|
18
17
|
var _useGridRootProps = require("../../hooks/utils/useGridRootProps");
|
|
@@ -25,7 +24,6 @@ const GridGenericColumnHeaderItem = exports.GridGenericColumnHeaderItem = (0, _f
|
|
|
25
24
|
height,
|
|
26
25
|
isResizing,
|
|
27
26
|
sortDirection,
|
|
28
|
-
hasFocus,
|
|
29
27
|
tabIndex,
|
|
30
28
|
separatorSide,
|
|
31
29
|
isDraggable,
|
|
@@ -43,7 +41,6 @@ const GridGenericColumnHeaderItem = exports.GridGenericColumnHeaderItem = (0, _f
|
|
|
43
41
|
style
|
|
44
42
|
} = props,
|
|
45
43
|
other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
|
|
46
|
-
const apiRef = (0, _useGridPrivateApiContext.useGridPrivateApiContext)();
|
|
47
44
|
const rootProps = (0, _useGridRootProps.useGridRootProps)();
|
|
48
45
|
const headerCellRef = React.useRef(null);
|
|
49
46
|
const handleRef = (0, _utils.unstable_useForkRef)(headerCellRef, ref);
|
|
@@ -51,17 +48,6 @@ const GridGenericColumnHeaderItem = exports.GridGenericColumnHeaderItem = (0, _f
|
|
|
51
48
|
if (sortDirection != null) {
|
|
52
49
|
ariaSort = sortDirection === 'asc' ? 'ascending' : 'descending';
|
|
53
50
|
}
|
|
54
|
-
React.useLayoutEffect(() => {
|
|
55
|
-
const columnMenuState = apiRef.current.state.columnMenu;
|
|
56
|
-
if (hasFocus && !columnMenuState.open) {
|
|
57
|
-
const focusableElement = headerCellRef.current.querySelector('[tabindex="0"]');
|
|
58
|
-
const elementToFocus = focusableElement || headerCellRef.current;
|
|
59
|
-
elementToFocus?.focus();
|
|
60
|
-
if (apiRef.current.columnHeadersContainerRef?.current) {
|
|
61
|
-
apiRef.current.columnHeadersContainerRef.current.scrollLeft = 0;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}, [apiRef, hasFocus]);
|
|
65
51
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", (0, _extends2.default)({
|
|
66
52
|
className: (0, _clsx.default)(classes.root, headerClassName),
|
|
67
53
|
style: (0, _extends2.default)({}, style, {
|
|
@@ -77,6 +77,21 @@ function useGridDimensions(apiRef, props) {
|
|
|
77
77
|
const densityFactor = (0, _utils2.useGridSelector)(apiRef, _density.gridDensityFactorSelector);
|
|
78
78
|
const columnsTotalWidth = (0, _utils2.useGridSelector)(apiRef, columnsTotalWidthSelector);
|
|
79
79
|
const isFirstSizing = React.useRef(true);
|
|
80
|
+
|
|
81
|
+
// Vertical scrollbar oscillation detector.
|
|
82
|
+
// Counts consecutive hasScrollY flips that happen with no row-height change.
|
|
83
|
+
// After 2 flips it is certainly a layout feedback loop, so every further flip
|
|
84
|
+
// is forced to false (no scrollbar). The counter resets when row heights change.
|
|
85
|
+
// Only vertical scrollbar can oscillate because column widths are never 'auto'.
|
|
86
|
+
// https://github.com/mui/mui-x/issues/20539
|
|
87
|
+
const scrollYOscillation = React.useRef({
|
|
88
|
+
counter: 0,
|
|
89
|
+
heights: {
|
|
90
|
+
content: 0,
|
|
91
|
+
pinnedTop: 0,
|
|
92
|
+
pinnedBottom: 0
|
|
93
|
+
}
|
|
94
|
+
});
|
|
80
95
|
const {
|
|
81
96
|
rowHeight,
|
|
82
97
|
headerHeight,
|
|
@@ -144,6 +159,7 @@ function useGridDimensions(apiRef, props) {
|
|
|
144
159
|
width: nonPinnedColumnsTotalWidth,
|
|
145
160
|
height: (0, _roundToDecimalPlaces.roundToDecimalPlaces)(rowsMeta.currentPageTotalHeight, 1)
|
|
146
161
|
};
|
|
162
|
+
const prevDimensions = apiRef.current.state.dimensions;
|
|
147
163
|
let viewportOuterSize;
|
|
148
164
|
let viewportInnerSize;
|
|
149
165
|
let hasScrollX = false;
|
|
@@ -181,6 +197,36 @@ function useGridDimensions(apiRef, props) {
|
|
|
181
197
|
hasScrollY = content.height + scrollbarSize > container.height;
|
|
182
198
|
}
|
|
183
199
|
}
|
|
200
|
+
|
|
201
|
+
// Detect vertical scrollbar oscillation.
|
|
202
|
+
// Track consecutive hasScrollY flips with no row-height change.
|
|
203
|
+
// Once confirmed (≥ 2 flips), force hasScrollY off — the scrollbar is
|
|
204
|
+
// not genuinely needed, it is a layout feedback loop caused by stale
|
|
205
|
+
// rootSize or the horizontal scrollbar's height cascading.
|
|
206
|
+
{
|
|
207
|
+
const osc = scrollYOscillation.current;
|
|
208
|
+
const heightsChanged = rowsMeta.currentPageTotalHeight !== osc.heights.content || rowsMeta.pinnedTopRowsTotalHeight !== osc.heights.pinnedTop || rowsMeta.pinnedBottomRowsTotalHeight !== osc.heights.pinnedBottom;
|
|
209
|
+
if (heightsChanged) {
|
|
210
|
+
osc.counter = 0;
|
|
211
|
+
osc.heights = {
|
|
212
|
+
content: rowsMeta.currentPageTotalHeight,
|
|
213
|
+
pinnedTop: rowsMeta.pinnedTopRowsTotalHeight,
|
|
214
|
+
pinnedBottom: rowsMeta.pinnedBottomRowsTotalHeight
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
if (prevDimensions.isReady && hasScrollY !== prevDimensions.hasScrollY) {
|
|
218
|
+
if (!heightsChanged) {
|
|
219
|
+
osc.counter += 1;
|
|
220
|
+
}
|
|
221
|
+
if (osc.counter >= 2) {
|
|
222
|
+
hasScrollY = false;
|
|
223
|
+
// Recompute hasScrollX without the vertical scrollbar's width impact,
|
|
224
|
+
// otherwise the cascade (hasScrollY → narrower viewport → hasScrollX)
|
|
225
|
+
// keeps the horizontal scrollbar/filler alive and the root keeps resizing.
|
|
226
|
+
hasScrollX = hasScrollXIfNoYScrollBar;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
184
230
|
if (hasScrollY) {
|
|
185
231
|
viewportInnerSize.width -= scrollbarSize;
|
|
186
232
|
}
|
|
@@ -215,7 +261,6 @@ function useGridDimensions(apiRef, props) {
|
|
|
215
261
|
topContainerHeight,
|
|
216
262
|
bottomContainerHeight
|
|
217
263
|
};
|
|
218
|
-
const prevDimensions = apiRef.current.state.dimensions;
|
|
219
264
|
if ((0, _utils3.isDeepEqual)(prevDimensions, newDimensions)) {
|
|
220
265
|
return;
|
|
221
266
|
}
|
|
@@ -209,7 +209,7 @@ const useGridPrintExport = (apiRef, props) => {
|
|
|
209
209
|
// Revert grid to previous state
|
|
210
210
|
apiRef.current.restoreState(previousGridState.current || {});
|
|
211
211
|
if (!previousGridState.current?.columns?.columnVisibilityModel) {
|
|
212
|
-
// if the apiRef.current.exportState(); did not
|
|
212
|
+
// if the apiRef.current.exportState(); did not export the column visibility, we update it
|
|
213
213
|
apiRef.current.setColumnVisibilityModel(previousColumnVisibility.current);
|
|
214
214
|
}
|
|
215
215
|
apiRef.current.setState(state => (0, _extends2.default)({}, state, {
|
|
@@ -246,12 +246,7 @@ const useGridPrintExport = (apiRef, props) => {
|
|
|
246
246
|
}));
|
|
247
247
|
}
|
|
248
248
|
previousVirtualizationState.current = apiRef.current.state.virtualization;
|
|
249
|
-
apiRef.current.
|
|
250
|
-
virtualization: (0, _extends2.default)({}, state.virtualization, {
|
|
251
|
-
enabled: false,
|
|
252
|
-
enabledForColumns: false
|
|
253
|
-
})
|
|
254
|
-
}));
|
|
249
|
+
apiRef.current.unstable_setVirtualization(false);
|
|
255
250
|
await updateGridColumnsForPrint(options?.fields, options?.allColumns, options?.includeCheckboxes);
|
|
256
251
|
updateGridRowsForPrint(options?.getRowsToExport ?? _utils2.defaultGetRowsToExport);
|
|
257
252
|
await raf(); // wait for the state changes to take action
|
package/node/index.js
CHANGED