@mui/x-data-grid 9.10.1 → 9.11.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 +118 -0
- package/README.md +1 -11
- package/components/toolbarV8/Toolbar.js +2 -125
- package/components/toolbarV8/Toolbar.mjs +2 -125
- package/components/toolbarV8/ToolbarButton.js +11 -50
- package/components/toolbarV8/ToolbarButton.mjs +11 -50
- package/hooks/features/dataSource/useGridDataSourceBase.js +33 -4
- package/hooks/features/dataSource/useGridDataSourceBase.mjs +33 -4
- package/hooks/features/rows/gridRowsUtils.js +17 -1
- package/hooks/features/rows/gridRowsUtils.mjs +17 -1
- package/index.js +1 -1
- package/index.mjs +1 -1
- package/package.json +4 -4
- package/components/toolbarV8/ToolbarContext.d.mts +0 -11
- package/components/toolbarV8/ToolbarContext.d.ts +0 -11
- package/components/toolbarV8/ToolbarContext.js +0 -21
- package/components/toolbarV8/ToolbarContext.mjs +0 -13
- package/components/toolbarV8/utils.d.mts +0 -5
- package/components/toolbarV8/utils.d.ts +0 -5
- package/components/toolbarV8/utils.js +0 -23
- package/components/toolbarV8/utils.mjs +0 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,123 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 9.11.0
|
|
4
|
+
|
|
5
|
+
_Aug 6, 2026_
|
|
6
|
+
|
|
7
|
+
We'd like to extend a big thank you to the 14 contributors who made this release possible. Here are some highlights ✨:
|
|
8
|
+
|
|
9
|
+
- ✨ Add `addItems()` and `getItemSelection()` API methods to Tree View
|
|
10
|
+
|
|
11
|
+
Special thanks go out to these community members for their valuable contributions:
|
|
12
|
+
@12joan, @Anexus5919, @kevincorizi-sbt, @mixelburg, @mustafajw07, @strazto
|
|
13
|
+
|
|
14
|
+
The following team members contributed to this release:
|
|
15
|
+
@flaviendelangle, @hasdfa, @JCQuintas, @LukasTy, @MBilalShafi, @michelengelen, @noraleonte, @rita-codes
|
|
16
|
+
|
|
17
|
+
### Data Grid
|
|
18
|
+
|
|
19
|
+
#### `@mui/x-data-grid@9.11.0`
|
|
20
|
+
|
|
21
|
+
- [DataGrid] Fix `updateRows` stripping class prototypes from rows in datasource mode (#22288) @mixelburg
|
|
22
|
+
- [DataGrid] Do not re-fetch data when an `Activity` becomes visible (#22603) @12joan
|
|
23
|
+
- [DataGrid] Fix toolbar button stealing focus when a sibling's disabled state changes (#23204) @MBilalShafi
|
|
24
|
+
|
|
25
|
+
#### `@mui/x-data-grid-pro@9.11.0` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
26
|
+
|
|
27
|
+
Same changes as in `@mui/x-data-grid@9.11.0`.
|
|
28
|
+
|
|
29
|
+
#### `@mui/x-data-grid-premium@9.11.0` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
30
|
+
|
|
31
|
+
Same changes as in `@mui/x-data-grid-pro@9.11.0`.
|
|
32
|
+
|
|
33
|
+
### Date and Time Pickers
|
|
34
|
+
|
|
35
|
+
#### `@mui/x-date-pickers@9.11.0`
|
|
36
|
+
|
|
37
|
+
- [pickers] Fix day shift when editing dates predating timezone standardization (#23296) @JCQuintas
|
|
38
|
+
|
|
39
|
+
#### `@mui/x-date-pickers-pro@9.11.0` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
40
|
+
|
|
41
|
+
Same changes as in `@mui/x-date-pickers@9.11.0`, plus:
|
|
42
|
+
|
|
43
|
+
- [DateRangePicker] Fix disabled filler cells showing the range highlight (#23293) @JCQuintas
|
|
44
|
+
|
|
45
|
+
### Charts
|
|
46
|
+
|
|
47
|
+
#### `@mui/x-charts@9.11.0`
|
|
48
|
+
|
|
49
|
+
- [charts] Activate the focused item with `Enter`/`Space` (#23218) @JCQuintas
|
|
50
|
+
- [charts] Extract the axis click payload builders (#23215) @JCQuintas
|
|
51
|
+
- [charts] Fix `GestureManager` event listener leak on chart unmount (#23283) @kevincorizi-sbt
|
|
52
|
+
- [charts] Fix `slotProps.legend.position` and `direction` in `RadarChart` (#23254) @JCQuintas
|
|
53
|
+
- [charts] Fix axis clicks being discarded on slight pointer movement (#23244) @noraleonte
|
|
54
|
+
- [charts] Fix image export of charts sized by their parent element (#23255) @JCQuintas
|
|
55
|
+
- [charts] Forward `experimentalFeatures` on `RadarChart` and `Heatmap` (#23216) @JCQuintas
|
|
56
|
+
- [charts] Hide focus indicator when the chart loses focus (#23213) @JCQuintas
|
|
57
|
+
|
|
58
|
+
#### `@mui/x-charts-pro@9.11.0` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
59
|
+
|
|
60
|
+
Same changes as in `@mui/x-charts@9.11.0`.
|
|
61
|
+
|
|
62
|
+
#### `@mui/x-charts-premium@9.11.0` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
63
|
+
|
|
64
|
+
Same changes as in `@mui/x-charts-pro@9.11.0`, plus:
|
|
65
|
+
|
|
66
|
+
- [charts-premium] Fix `RangeBar` type override (#23217) @JCQuintas
|
|
67
|
+
|
|
68
|
+
### Tree View
|
|
69
|
+
|
|
70
|
+
#### `@mui/x-tree-view@9.11.0`
|
|
71
|
+
|
|
72
|
+
- [tree view] Add `addItems()` API method (#23159) @JCQuintas
|
|
73
|
+
- [tree view] Add `getItemSelection` API method (#23257) @noraleonte
|
|
74
|
+
- [tree view] Ignore `keepExistingSelection` when `multiSelect` is `false` (#23242) @JCQuintas
|
|
75
|
+
- [tree view] Prevent duplicate ids in multi-select arrow navigation (#23006) @Anexus5919
|
|
76
|
+
|
|
77
|
+
#### `@mui/x-tree-view-pro@9.11.0` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
78
|
+
|
|
79
|
+
Same changes as in `@mui/x-tree-view@9.11.0`, plus:
|
|
80
|
+
|
|
81
|
+
- [tree view] Discard superseded lazy-loading responses (#23005) @Anexus5919
|
|
82
|
+
|
|
83
|
+
### Scheduler
|
|
84
|
+
|
|
85
|
+
#### `@mui/x-scheduler@9.0.0-beta.8`
|
|
86
|
+
|
|
87
|
+
- [scheduler] Add `localeText` prop to standalone views (#23210) @rita-codes
|
|
88
|
+
- [scheduler] Finetune touch experience for time grid events and introduce editing drawer (#22624) @noraleonte
|
|
89
|
+
- [scheduler] Introduce the edit dialog form context & lifecycle contract (#23284) @rita-codes
|
|
90
|
+
- [scheduler] Refactor edit dialog General tab into section components (#23206) @rita-codes
|
|
91
|
+
- [scheduler] Replace copied Base UI internals with `@base-ui/react/internals` imports (#21972) @flaviendelangle
|
|
92
|
+
- [scheduler] Hide resource picker when no resources are provided (#23290) @mustafajw07
|
|
93
|
+
- [scheduler] Support multi-resource occurrences in `EventTimeline` (#23240) @mustafajw07
|
|
94
|
+
|
|
95
|
+
#### `@mui/x-scheduler-premium@9.0.0-beta.8` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
96
|
+
|
|
97
|
+
Same changes as in `@mui/x-scheduler@9.0.0-beta.8`.
|
|
98
|
+
|
|
99
|
+
### Codemod
|
|
100
|
+
|
|
101
|
+
#### `@mui/x-codemod@9.11.0`
|
|
102
|
+
|
|
103
|
+
- [codemod] Remove unused `@babel/core` and `@babel/traverse` dependencies (#23245) @LukasTy
|
|
104
|
+
|
|
105
|
+
### Docs
|
|
106
|
+
|
|
107
|
+
- [docs] Add recipe for adding new rows from clipboard copy (#22913) @michelengelen
|
|
108
|
+
- [docs] Clarify the package versions in v8 upgrade guides (#22376) @strazto
|
|
109
|
+
- [docs] Replace README peer dependency lists with an npm install command (#23256) @LukasTy
|
|
110
|
+
|
|
111
|
+
### Core
|
|
112
|
+
|
|
113
|
+
- [code-infra] Align action pin version comments (#23238) @LukasTy
|
|
114
|
+
|
|
115
|
+
### Miscellaneous
|
|
116
|
+
|
|
117
|
+
- [chat] Drop the undeclared `@mui/icons-material` dependency (#23252) @LukasTy
|
|
118
|
+
- [chat] Sanitize image part URLs and share the URL allow-list with markdown (#23058) @hasdfa
|
|
119
|
+
- [test] Type the `PickersTextField` test stub instead of `as any` (#23194) @LukasTy
|
|
120
|
+
|
|
3
121
|
## 9.10.1
|
|
4
122
|
|
|
5
123
|
_Jul 23, 2026_
|
package/README.md
CHANGED
|
@@ -8,17 +8,7 @@ It's part of [MUI X](https://mui.com/x/), an open-core extension of our Core li
|
|
|
8
8
|
Install the package in your project directory with:
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
npm install @mui/x-data-grid
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
This component has the following peer dependencies that you need to install as well.
|
|
15
|
-
|
|
16
|
-
```json
|
|
17
|
-
"peerDependencies": {
|
|
18
|
-
"@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0",
|
|
19
|
-
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
20
|
-
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
|
21
|
-
},
|
|
11
|
+
npm install @mui/x-data-grid @mui/material @emotion/react @emotion/styled
|
|
22
12
|
```
|
|
23
13
|
|
|
24
14
|
## Documentation
|
|
@@ -16,11 +16,10 @@ var _composeClasses = _interopRequireDefault(require("@mui/utils/composeClasses"
|
|
|
16
16
|
var _clsx = _interopRequireDefault(require("clsx"));
|
|
17
17
|
var _forwardRef = require("@mui/x-internals/forwardRef");
|
|
18
18
|
var _useComponentRenderer = require("@mui/x-internals/useComponentRenderer");
|
|
19
|
+
var _ToolbarContext = require("@mui/x-internals/ToolbarContext");
|
|
19
20
|
var _cssVariables = require("../../constants/cssVariables");
|
|
20
21
|
var _gridClasses = require("../../constants/gridClasses");
|
|
21
|
-
var _ToolbarContext = require("./ToolbarContext");
|
|
22
22
|
var _useGridRootProps = require("../../hooks/utils/useGridRootProps");
|
|
23
|
-
var _utils = require("./utils");
|
|
24
23
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
25
24
|
const _excluded = ["render", "className"];
|
|
26
25
|
const useUtilityClasses = ownerState => {
|
|
@@ -67,127 +66,6 @@ const Toolbar = exports.Toolbar = (0, _forwardRef.forwardRef)(function Toolbar(p
|
|
|
67
66
|
other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
|
|
68
67
|
const rootProps = (0, _useGridRootProps.useGridRootProps)();
|
|
69
68
|
const classes = useUtilityClasses(rootProps);
|
|
70
|
-
const [focusableItemId, setFocusableItemId] = React.useState(null);
|
|
71
|
-
const [items, setItems] = React.useState([]);
|
|
72
|
-
const getSortedItems = React.useCallback(() => items.sort(_utils.sortByDocumentPosition), [items]);
|
|
73
|
-
const findEnabledItem = React.useCallback((startIndex, step, wrap = true) => {
|
|
74
|
-
let index = startIndex;
|
|
75
|
-
const sortedItems = getSortedItems();
|
|
76
|
-
const itemCount = sortedItems.length;
|
|
77
|
-
|
|
78
|
-
// Look for enabled items in the specified direction
|
|
79
|
-
for (let i = 0; i < itemCount; i += 1) {
|
|
80
|
-
index += step;
|
|
81
|
-
|
|
82
|
-
// Handle wrapping around the ends
|
|
83
|
-
if (index >= itemCount) {
|
|
84
|
-
if (!wrap) {
|
|
85
|
-
return -1;
|
|
86
|
-
}
|
|
87
|
-
index = 0;
|
|
88
|
-
} else if (index < 0) {
|
|
89
|
-
if (!wrap) {
|
|
90
|
-
return -1;
|
|
91
|
-
}
|
|
92
|
-
index = itemCount - 1;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Return if we found an enabled item
|
|
96
|
-
if (!sortedItems[index].ref.current?.disabled && sortedItems[index].ref.current?.ariaDisabled !== 'true') {
|
|
97
|
-
return index;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// If we've checked all items and found none enabled
|
|
102
|
-
return -1;
|
|
103
|
-
}, [getSortedItems]);
|
|
104
|
-
const registerItem = React.useCallback((id, itemRef) => {
|
|
105
|
-
setItems(prevItems => [...prevItems, {
|
|
106
|
-
id,
|
|
107
|
-
ref: itemRef
|
|
108
|
-
}]);
|
|
109
|
-
}, []);
|
|
110
|
-
const unregisterItem = React.useCallback(id => {
|
|
111
|
-
setItems(prevItems => prevItems.filter(i => i.id !== id));
|
|
112
|
-
}, []);
|
|
113
|
-
const onItemKeyDown = React.useCallback(event => {
|
|
114
|
-
if (!focusableItemId) {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
const sortedItems = getSortedItems();
|
|
118
|
-
const focusableItemIndex = sortedItems.findIndex(item => item.id === focusableItemId);
|
|
119
|
-
let newIndex = -1;
|
|
120
|
-
if (event.key === 'ArrowRight') {
|
|
121
|
-
event.preventDefault();
|
|
122
|
-
newIndex = findEnabledItem(focusableItemIndex, 1);
|
|
123
|
-
} else if (event.key === 'ArrowLeft') {
|
|
124
|
-
event.preventDefault();
|
|
125
|
-
newIndex = findEnabledItem(focusableItemIndex, -1);
|
|
126
|
-
} else if (event.key === 'Home') {
|
|
127
|
-
event.preventDefault();
|
|
128
|
-
newIndex = findEnabledItem(-1, 1, false);
|
|
129
|
-
} else if (event.key === 'End') {
|
|
130
|
-
event.preventDefault();
|
|
131
|
-
newIndex = findEnabledItem(sortedItems.length, -1, false);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// TODO: Check why this is necessary
|
|
135
|
-
if (newIndex >= 0 && newIndex < sortedItems.length) {
|
|
136
|
-
const item = sortedItems[newIndex];
|
|
137
|
-
setFocusableItemId(item.id);
|
|
138
|
-
item.ref.current?.focus();
|
|
139
|
-
}
|
|
140
|
-
}, [getSortedItems, focusableItemId, findEnabledItem]);
|
|
141
|
-
const onItemFocus = React.useCallback(id => {
|
|
142
|
-
if (focusableItemId !== id) {
|
|
143
|
-
setFocusableItemId(id);
|
|
144
|
-
}
|
|
145
|
-
}, [focusableItemId, setFocusableItemId]);
|
|
146
|
-
const onItemDisabled = React.useCallback(id => {
|
|
147
|
-
const sortedItems = getSortedItems();
|
|
148
|
-
const currentIndex = sortedItems.findIndex(item => item.id === id);
|
|
149
|
-
const newIndex = findEnabledItem(currentIndex, 1);
|
|
150
|
-
if (newIndex >= 0 && newIndex < sortedItems.length) {
|
|
151
|
-
const item = sortedItems[newIndex];
|
|
152
|
-
setFocusableItemId(item.id);
|
|
153
|
-
item.ref.current?.focus();
|
|
154
|
-
}
|
|
155
|
-
}, [getSortedItems, findEnabledItem]);
|
|
156
|
-
React.useEffect(() => {
|
|
157
|
-
const sortedItems = getSortedItems();
|
|
158
|
-
if (sortedItems.length > 0) {
|
|
159
|
-
// Set initial focusable item
|
|
160
|
-
if (!focusableItemId) {
|
|
161
|
-
setFocusableItemId(sortedItems[0].id);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
const focusableItemIndex = sortedItems.findIndex(item => item.id === focusableItemId);
|
|
165
|
-
if (!sortedItems[focusableItemIndex]) {
|
|
166
|
-
// Last item has been removed from the items array
|
|
167
|
-
const item = sortedItems[sortedItems.length - 1];
|
|
168
|
-
if (item) {
|
|
169
|
-
setFocusableItemId(item.id);
|
|
170
|
-
item.ref.current?.focus();
|
|
171
|
-
}
|
|
172
|
-
} else if (focusableItemIndex === -1) {
|
|
173
|
-
// Focused item has been removed from the items array
|
|
174
|
-
const item = sortedItems[focusableItemIndex];
|
|
175
|
-
if (item) {
|
|
176
|
-
setFocusableItemId(item.id);
|
|
177
|
-
item.ref.current?.focus();
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
182
|
-
}, [getSortedItems, findEnabledItem]);
|
|
183
|
-
const contextValue = React.useMemo(() => ({
|
|
184
|
-
focusableItemId,
|
|
185
|
-
registerItem,
|
|
186
|
-
unregisterItem,
|
|
187
|
-
onItemKeyDown,
|
|
188
|
-
onItemFocus,
|
|
189
|
-
onItemDisabled
|
|
190
|
-
}), [focusableItemId, registerItem, unregisterItem, onItemKeyDown, onItemFocus, onItemDisabled]);
|
|
191
69
|
const element = (0, _useComponentRenderer.useComponentRenderer)(ToolbarRoot, render, (0, _extends2.default)({
|
|
192
70
|
role: 'toolbar',
|
|
193
71
|
'aria-orientation': 'horizontal',
|
|
@@ -196,8 +74,7 @@ const Toolbar = exports.Toolbar = (0, _forwardRef.forwardRef)(function Toolbar(p
|
|
|
196
74
|
}, other, {
|
|
197
75
|
ref
|
|
198
76
|
}));
|
|
199
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ToolbarContext.
|
|
200
|
-
value: contextValue,
|
|
77
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ToolbarContext.ToolbarContextProvider, {
|
|
201
78
|
children: element
|
|
202
79
|
});
|
|
203
80
|
});
|
|
@@ -10,11 +10,10 @@ import composeClasses from '@mui/utils/composeClasses';
|
|
|
10
10
|
import clsx from 'clsx';
|
|
11
11
|
import { forwardRef } from '@mui/x-internals/forwardRef';
|
|
12
12
|
import { useComponentRenderer } from '@mui/x-internals/useComponentRenderer';
|
|
13
|
+
import { ToolbarContextProvider } from '@mui/x-internals/ToolbarContext';
|
|
13
14
|
import { vars } from "../../constants/cssVariables.mjs";
|
|
14
15
|
import { getDataGridUtilityClass } from "../../constants/gridClasses.mjs";
|
|
15
|
-
import { ToolbarContext } from "./ToolbarContext.mjs";
|
|
16
16
|
import { useGridRootProps } from "../../hooks/utils/useGridRootProps.mjs";
|
|
17
|
-
import { sortByDocumentPosition } from "./utils.mjs";
|
|
18
17
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
19
18
|
const useUtilityClasses = ownerState => {
|
|
20
19
|
const {
|
|
@@ -60,127 +59,6 @@ const Toolbar = forwardRef(function Toolbar(props, ref) {
|
|
|
60
59
|
other = _objectWithoutPropertiesLoose(props, _excluded);
|
|
61
60
|
const rootProps = useGridRootProps();
|
|
62
61
|
const classes = useUtilityClasses(rootProps);
|
|
63
|
-
const [focusableItemId, setFocusableItemId] = React.useState(null);
|
|
64
|
-
const [items, setItems] = React.useState([]);
|
|
65
|
-
const getSortedItems = React.useCallback(() => items.sort(sortByDocumentPosition), [items]);
|
|
66
|
-
const findEnabledItem = React.useCallback((startIndex, step, wrap = true) => {
|
|
67
|
-
let index = startIndex;
|
|
68
|
-
const sortedItems = getSortedItems();
|
|
69
|
-
const itemCount = sortedItems.length;
|
|
70
|
-
|
|
71
|
-
// Look for enabled items in the specified direction
|
|
72
|
-
for (let i = 0; i < itemCount; i += 1) {
|
|
73
|
-
index += step;
|
|
74
|
-
|
|
75
|
-
// Handle wrapping around the ends
|
|
76
|
-
if (index >= itemCount) {
|
|
77
|
-
if (!wrap) {
|
|
78
|
-
return -1;
|
|
79
|
-
}
|
|
80
|
-
index = 0;
|
|
81
|
-
} else if (index < 0) {
|
|
82
|
-
if (!wrap) {
|
|
83
|
-
return -1;
|
|
84
|
-
}
|
|
85
|
-
index = itemCount - 1;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Return if we found an enabled item
|
|
89
|
-
if (!sortedItems[index].ref.current?.disabled && sortedItems[index].ref.current?.ariaDisabled !== 'true') {
|
|
90
|
-
return index;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// If we've checked all items and found none enabled
|
|
95
|
-
return -1;
|
|
96
|
-
}, [getSortedItems]);
|
|
97
|
-
const registerItem = React.useCallback((id, itemRef) => {
|
|
98
|
-
setItems(prevItems => [...prevItems, {
|
|
99
|
-
id,
|
|
100
|
-
ref: itemRef
|
|
101
|
-
}]);
|
|
102
|
-
}, []);
|
|
103
|
-
const unregisterItem = React.useCallback(id => {
|
|
104
|
-
setItems(prevItems => prevItems.filter(i => i.id !== id));
|
|
105
|
-
}, []);
|
|
106
|
-
const onItemKeyDown = React.useCallback(event => {
|
|
107
|
-
if (!focusableItemId) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const sortedItems = getSortedItems();
|
|
111
|
-
const focusableItemIndex = sortedItems.findIndex(item => item.id === focusableItemId);
|
|
112
|
-
let newIndex = -1;
|
|
113
|
-
if (event.key === 'ArrowRight') {
|
|
114
|
-
event.preventDefault();
|
|
115
|
-
newIndex = findEnabledItem(focusableItemIndex, 1);
|
|
116
|
-
} else if (event.key === 'ArrowLeft') {
|
|
117
|
-
event.preventDefault();
|
|
118
|
-
newIndex = findEnabledItem(focusableItemIndex, -1);
|
|
119
|
-
} else if (event.key === 'Home') {
|
|
120
|
-
event.preventDefault();
|
|
121
|
-
newIndex = findEnabledItem(-1, 1, false);
|
|
122
|
-
} else if (event.key === 'End') {
|
|
123
|
-
event.preventDefault();
|
|
124
|
-
newIndex = findEnabledItem(sortedItems.length, -1, false);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// TODO: Check why this is necessary
|
|
128
|
-
if (newIndex >= 0 && newIndex < sortedItems.length) {
|
|
129
|
-
const item = sortedItems[newIndex];
|
|
130
|
-
setFocusableItemId(item.id);
|
|
131
|
-
item.ref.current?.focus();
|
|
132
|
-
}
|
|
133
|
-
}, [getSortedItems, focusableItemId, findEnabledItem]);
|
|
134
|
-
const onItemFocus = React.useCallback(id => {
|
|
135
|
-
if (focusableItemId !== id) {
|
|
136
|
-
setFocusableItemId(id);
|
|
137
|
-
}
|
|
138
|
-
}, [focusableItemId, setFocusableItemId]);
|
|
139
|
-
const onItemDisabled = React.useCallback(id => {
|
|
140
|
-
const sortedItems = getSortedItems();
|
|
141
|
-
const currentIndex = sortedItems.findIndex(item => item.id === id);
|
|
142
|
-
const newIndex = findEnabledItem(currentIndex, 1);
|
|
143
|
-
if (newIndex >= 0 && newIndex < sortedItems.length) {
|
|
144
|
-
const item = sortedItems[newIndex];
|
|
145
|
-
setFocusableItemId(item.id);
|
|
146
|
-
item.ref.current?.focus();
|
|
147
|
-
}
|
|
148
|
-
}, [getSortedItems, findEnabledItem]);
|
|
149
|
-
React.useEffect(() => {
|
|
150
|
-
const sortedItems = getSortedItems();
|
|
151
|
-
if (sortedItems.length > 0) {
|
|
152
|
-
// Set initial focusable item
|
|
153
|
-
if (!focusableItemId) {
|
|
154
|
-
setFocusableItemId(sortedItems[0].id);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
const focusableItemIndex = sortedItems.findIndex(item => item.id === focusableItemId);
|
|
158
|
-
if (!sortedItems[focusableItemIndex]) {
|
|
159
|
-
// Last item has been removed from the items array
|
|
160
|
-
const item = sortedItems[sortedItems.length - 1];
|
|
161
|
-
if (item) {
|
|
162
|
-
setFocusableItemId(item.id);
|
|
163
|
-
item.ref.current?.focus();
|
|
164
|
-
}
|
|
165
|
-
} else if (focusableItemIndex === -1) {
|
|
166
|
-
// Focused item has been removed from the items array
|
|
167
|
-
const item = sortedItems[focusableItemIndex];
|
|
168
|
-
if (item) {
|
|
169
|
-
setFocusableItemId(item.id);
|
|
170
|
-
item.ref.current?.focus();
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
175
|
-
}, [getSortedItems, findEnabledItem]);
|
|
176
|
-
const contextValue = React.useMemo(() => ({
|
|
177
|
-
focusableItemId,
|
|
178
|
-
registerItem,
|
|
179
|
-
unregisterItem,
|
|
180
|
-
onItemKeyDown,
|
|
181
|
-
onItemFocus,
|
|
182
|
-
onItemDisabled
|
|
183
|
-
}), [focusableItemId, registerItem, unregisterItem, onItemKeyDown, onItemFocus, onItemDisabled]);
|
|
184
62
|
const element = useComponentRenderer(ToolbarRoot, render, _extends({
|
|
185
63
|
role: 'toolbar',
|
|
186
64
|
'aria-orientation': 'horizontal',
|
|
@@ -189,8 +67,7 @@ const Toolbar = forwardRef(function Toolbar(props, ref) {
|
|
|
189
67
|
}, other, {
|
|
190
68
|
ref
|
|
191
69
|
}));
|
|
192
|
-
return /*#__PURE__*/_jsx(
|
|
193
|
-
value: contextValue,
|
|
70
|
+
return /*#__PURE__*/_jsx(ToolbarContextProvider, {
|
|
194
71
|
children: element
|
|
195
72
|
});
|
|
196
73
|
});
|
|
@@ -12,13 +12,13 @@ var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runt
|
|
|
12
12
|
var React = _interopRequireWildcard(require("react"));
|
|
13
13
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
14
14
|
var _useForkRef = _interopRequireDefault(require("@mui/utils/useForkRef"));
|
|
15
|
-
var _useId = _interopRequireDefault(require("@mui/utils/useId"));
|
|
16
15
|
var _forwardRef = require("@mui/x-internals/forwardRef");
|
|
17
16
|
var _useComponentRenderer = require("@mui/x-internals/useComponentRenderer");
|
|
17
|
+
var _ToolbarContext = require("@mui/x-internals/ToolbarContext");
|
|
18
18
|
var _useGridRootProps = require("../../hooks/utils/useGridRootProps");
|
|
19
|
-
var _ToolbarContext = require("./ToolbarContext");
|
|
20
19
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
21
|
-
const _excluded = ["render", "onKeyDown", "onFocus", "disabled", "aria-disabled"]
|
|
20
|
+
const _excluded = ["render", "onKeyDown", "onFocus", "onBlur", "disabled", "aria-disabled"],
|
|
21
|
+
_excluded2 = ["tabIndex"];
|
|
22
22
|
/**
|
|
23
23
|
* A button for performing actions from the toolbar.
|
|
24
24
|
* It renders the `baseIconButton` slot.
|
|
@@ -33,59 +33,20 @@ const _excluded = ["render", "onKeyDown", "onFocus", "disabled", "aria-disabled"
|
|
|
33
33
|
*/
|
|
34
34
|
const ToolbarButton = exports.ToolbarButton = (0, _forwardRef.forwardRef)(function ToolbarButton(props, ref) {
|
|
35
35
|
const {
|
|
36
|
-
render
|
|
37
|
-
onKeyDown,
|
|
38
|
-
onFocus,
|
|
39
|
-
disabled,
|
|
40
|
-
'aria-disabled': ariaDisabled
|
|
36
|
+
render
|
|
41
37
|
} = props,
|
|
42
38
|
other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
|
|
43
|
-
const id = (0, _useId.default)();
|
|
44
39
|
const rootProps = (0, _useGridRootProps.useGridRootProps)();
|
|
45
40
|
const buttonRef = React.useRef(null);
|
|
46
41
|
const handleRef = (0, _useForkRef.default)(buttonRef, ref);
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
onItemFocus,
|
|
53
|
-
onItemDisabled
|
|
54
|
-
} = (0, _ToolbarContext.useToolbarContext)();
|
|
55
|
-
const handleKeyDown = event => {
|
|
56
|
-
onItemKeyDown(event);
|
|
57
|
-
onKeyDown?.(event);
|
|
58
|
-
};
|
|
59
|
-
const handleFocus = event => {
|
|
60
|
-
onItemFocus(id);
|
|
61
|
-
onFocus?.(event);
|
|
62
|
-
};
|
|
63
|
-
React.useEffect(() => {
|
|
64
|
-
registerItem(id, buttonRef);
|
|
65
|
-
return () => unregisterItem(id);
|
|
66
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
67
|
-
}, []);
|
|
68
|
-
const previousDisabled = React.useRef(disabled);
|
|
69
|
-
React.useEffect(() => {
|
|
70
|
-
if (previousDisabled.current !== disabled && disabled === true) {
|
|
71
|
-
onItemDisabled(id, disabled);
|
|
72
|
-
}
|
|
73
|
-
previousDisabled.current = disabled;
|
|
74
|
-
}, [disabled, id, onItemDisabled]);
|
|
75
|
-
const previousAriaDisabled = React.useRef(ariaDisabled);
|
|
76
|
-
React.useEffect(() => {
|
|
77
|
-
if (previousAriaDisabled.current !== ariaDisabled && ariaDisabled === true) {
|
|
78
|
-
onItemDisabled(id, true);
|
|
79
|
-
}
|
|
80
|
-
previousAriaDisabled.current = ariaDisabled;
|
|
81
|
-
}, [ariaDisabled, id, onItemDisabled]);
|
|
42
|
+
const _useRegisterToolbarBu = (0, _ToolbarContext.useRegisterToolbarButton)(props, buttonRef),
|
|
43
|
+
{
|
|
44
|
+
tabIndex
|
|
45
|
+
} = _useRegisterToolbarBu,
|
|
46
|
+
toolbarButtonProps = (0, _objectWithoutPropertiesLoose2.default)(_useRegisterToolbarBu, _excluded2);
|
|
82
47
|
const element = (0, _useComponentRenderer.useComponentRenderer)(rootProps.slots.baseIconButton, render, (0, _extends2.default)({}, rootProps.slotProps?.baseIconButton, {
|
|
83
|
-
tabIndex
|
|
84
|
-
}, other, {
|
|
85
|
-
disabled,
|
|
86
|
-
'aria-disabled': ariaDisabled,
|
|
87
|
-
onKeyDown: handleKeyDown,
|
|
88
|
-
onFocus: handleFocus,
|
|
48
|
+
tabIndex
|
|
49
|
+
}, other, toolbarButtonProps, {
|
|
89
50
|
ref: handleRef
|
|
90
51
|
}));
|
|
91
52
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(React.Fragment, {
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
4
4
|
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
5
|
-
const _excluded = ["render", "onKeyDown", "onFocus", "disabled", "aria-disabled"]
|
|
5
|
+
const _excluded = ["render", "onKeyDown", "onFocus", "onBlur", "disabled", "aria-disabled"],
|
|
6
|
+
_excluded2 = ["tabIndex"];
|
|
6
7
|
import * as React from 'react';
|
|
7
8
|
import PropTypes from 'prop-types';
|
|
8
9
|
import useForkRef from '@mui/utils/useForkRef';
|
|
9
|
-
import useId from '@mui/utils/useId';
|
|
10
10
|
import { forwardRef } from '@mui/x-internals/forwardRef';
|
|
11
11
|
import { useComponentRenderer } from '@mui/x-internals/useComponentRenderer';
|
|
12
|
+
import { useRegisterToolbarButton } from '@mui/x-internals/ToolbarContext';
|
|
12
13
|
import { useGridRootProps } from "../../hooks/utils/useGridRootProps.mjs";
|
|
13
|
-
import { useToolbarContext } from "./ToolbarContext.mjs";
|
|
14
14
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
15
15
|
/**
|
|
16
16
|
* A button for performing actions from the toolbar.
|
|
@@ -26,59 +26,20 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
26
26
|
*/
|
|
27
27
|
const ToolbarButton = forwardRef(function ToolbarButton(props, ref) {
|
|
28
28
|
const {
|
|
29
|
-
render
|
|
30
|
-
onKeyDown,
|
|
31
|
-
onFocus,
|
|
32
|
-
disabled,
|
|
33
|
-
'aria-disabled': ariaDisabled
|
|
29
|
+
render
|
|
34
30
|
} = props,
|
|
35
31
|
other = _objectWithoutPropertiesLoose(props, _excluded);
|
|
36
|
-
const id = useId();
|
|
37
32
|
const rootProps = useGridRootProps();
|
|
38
33
|
const buttonRef = React.useRef(null);
|
|
39
34
|
const handleRef = useForkRef(buttonRef, ref);
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
onItemFocus,
|
|
46
|
-
onItemDisabled
|
|
47
|
-
} = useToolbarContext();
|
|
48
|
-
const handleKeyDown = event => {
|
|
49
|
-
onItemKeyDown(event);
|
|
50
|
-
onKeyDown?.(event);
|
|
51
|
-
};
|
|
52
|
-
const handleFocus = event => {
|
|
53
|
-
onItemFocus(id);
|
|
54
|
-
onFocus?.(event);
|
|
55
|
-
};
|
|
56
|
-
React.useEffect(() => {
|
|
57
|
-
registerItem(id, buttonRef);
|
|
58
|
-
return () => unregisterItem(id);
|
|
59
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
60
|
-
}, []);
|
|
61
|
-
const previousDisabled = React.useRef(disabled);
|
|
62
|
-
React.useEffect(() => {
|
|
63
|
-
if (previousDisabled.current !== disabled && disabled === true) {
|
|
64
|
-
onItemDisabled(id, disabled);
|
|
65
|
-
}
|
|
66
|
-
previousDisabled.current = disabled;
|
|
67
|
-
}, [disabled, id, onItemDisabled]);
|
|
68
|
-
const previousAriaDisabled = React.useRef(ariaDisabled);
|
|
69
|
-
React.useEffect(() => {
|
|
70
|
-
if (previousAriaDisabled.current !== ariaDisabled && ariaDisabled === true) {
|
|
71
|
-
onItemDisabled(id, true);
|
|
72
|
-
}
|
|
73
|
-
previousAriaDisabled.current = ariaDisabled;
|
|
74
|
-
}, [ariaDisabled, id, onItemDisabled]);
|
|
35
|
+
const _useRegisterToolbarBu = useRegisterToolbarButton(props, buttonRef),
|
|
36
|
+
{
|
|
37
|
+
tabIndex
|
|
38
|
+
} = _useRegisterToolbarBu,
|
|
39
|
+
toolbarButtonProps = _objectWithoutPropertiesLoose(_useRegisterToolbarBu, _excluded2);
|
|
75
40
|
const element = useComponentRenderer(rootProps.slots.baseIconButton, render, _extends({}, rootProps.slotProps?.baseIconButton, {
|
|
76
|
-
tabIndex
|
|
77
|
-
}, other, {
|
|
78
|
-
disabled,
|
|
79
|
-
'aria-disabled': ariaDisabled,
|
|
80
|
-
onKeyDown: handleKeyDown,
|
|
81
|
-
onFocus: handleFocus,
|
|
41
|
+
tabIndex
|
|
42
|
+
}, other, toolbarButtonProps, {
|
|
82
43
|
ref: handleRef
|
|
83
44
|
}));
|
|
84
45
|
return /*#__PURE__*/_jsx(React.Fragment, {
|
|
@@ -47,6 +47,11 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
47
47
|
}, [currentStrategy]);
|
|
48
48
|
const paginationModel = (0, _useGridSelector.useGridSelector)(apiRef, _gridPaginationSelector.gridPaginationModelSelector);
|
|
49
49
|
const lastRequestId = React.useRef(0);
|
|
50
|
+
// `false` while a request is in flight, and for any request that errors or is discarded as
|
|
51
|
+
// stale. Lets the mount effect tell "rows are already displayed" from "the fetch never landed".
|
|
52
|
+
const rowsAreUpToDate = React.useRef(false);
|
|
53
|
+
// Requests that are still running and will apply their response when they settle.
|
|
54
|
+
const pendingRequestCount = React.useRef(0);
|
|
50
55
|
const pollingIntervalRef = React.useRef(null);
|
|
51
56
|
const onDataSourceErrorProp = props.onDataSourceError;
|
|
52
57
|
const revalidateMs = props.dataSourceRevalidateMs;
|
|
@@ -91,6 +96,7 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
91
96
|
keepChildrenExpanded
|
|
92
97
|
}
|
|
93
98
|
});
|
|
99
|
+
rowsAreUpToDate.current = true;
|
|
94
100
|
if (standardRowsUpdateStrategyActive) {
|
|
95
101
|
apiRef.current.setLoading(false);
|
|
96
102
|
}
|
|
@@ -103,6 +109,8 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
103
109
|
}
|
|
104
110
|
const requestId = lastRequestId.current + 1;
|
|
105
111
|
lastRequestId.current = requestId;
|
|
112
|
+
rowsAreUpToDate.current = false;
|
|
113
|
+
pendingRequestCount.current += 1;
|
|
106
114
|
try {
|
|
107
115
|
const getRowsResponse = await getRows(fetchParams);
|
|
108
116
|
const cacheResponses = cacheChunkManager.splitResponse(fetchParams, getRowsResponse);
|
|
@@ -116,6 +124,7 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
116
124
|
keepChildrenExpanded
|
|
117
125
|
}
|
|
118
126
|
});
|
|
127
|
+
rowsAreUpToDate.current = true;
|
|
119
128
|
}
|
|
120
129
|
} catch (originalError) {
|
|
121
130
|
if (lastRequestId.current === requestId) {
|
|
@@ -138,6 +147,7 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
138
147
|
}
|
|
139
148
|
}
|
|
140
149
|
} finally {
|
|
150
|
+
pendingRequestCount.current -= 1;
|
|
141
151
|
if (standardRowsUpdateStrategyActive && lastRequestId.current === requestId) {
|
|
142
152
|
apiRef.current.setLoading(false);
|
|
143
153
|
}
|
|
@@ -313,12 +323,34 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
313
323
|
}
|
|
314
324
|
}, [revalidateMs, stopPolling]);
|
|
315
325
|
React.useEffect(() => stopPolling, [stopPolling]);
|
|
326
|
+
const lastApiRef = React.useRef(apiRef);
|
|
327
|
+
const lastStrategy = React.useRef(currentStrategy);
|
|
328
|
+
const lastDataSource = React.useRef(props.dataSource);
|
|
316
329
|
React.useEffect(() => {
|
|
330
|
+
// `<Activity mode="hidden">` leaves the root element in the document, a real unmount does not.
|
|
331
|
+
// Only discard the in-flight response in the latter case, so hiding lets the request land.
|
|
332
|
+
const rootElement = apiRef.current?.rootElementRef?.current ?? null;
|
|
333
|
+
const ignoreInFlightRequest = () => {
|
|
334
|
+
if (!rootElement?.isConnected) {
|
|
335
|
+
lastRequestId.current += 1;
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
|
|
317
339
|
// Return early if the proper strategy isn't set yet
|
|
318
340
|
// Context: https://github.com/mui/mui-x/issues/19650
|
|
319
341
|
if (currentStrategy !== _utils2.DataSourceRowsUpdateStrategy.Default && currentStrategy !== _utils2.DataSourceRowsUpdateStrategy.LazyLoading && currentStrategy !== _utils2.DataSourceRowsUpdateStrategy.GroupedData && currentStrategy !== _utils2.DataSourceRowsUpdateStrategy.LazyLoadedGroupedData) {
|
|
320
342
|
return undefined;
|
|
321
343
|
}
|
|
344
|
+
const dependenciesChanged = lastApiRef.current !== apiRef || lastStrategy.current !== currentStrategy || lastDataSource.current !== props.dataSource;
|
|
345
|
+
lastApiRef.current = apiRef;
|
|
346
|
+
lastStrategy.current = currentStrategy;
|
|
347
|
+
lastDataSource.current = props.dataSource;
|
|
348
|
+
|
|
349
|
+
// Re-mounting the effect (`<Activity />` becoming visible again, for instance) must not
|
|
350
|
+
// re-fetch data that is already displayed.
|
|
351
|
+
if (!dependenciesChanged && (rowsAreUpToDate.current || pendingRequestCount.current > 0)) {
|
|
352
|
+
return ignoreInFlightRequest;
|
|
353
|
+
}
|
|
322
354
|
if (props.dataSource) {
|
|
323
355
|
stopPolling();
|
|
324
356
|
// `dataSourceKeepPreviousData` only applies to the flat `Default` strategy (mirroring
|
|
@@ -332,10 +364,7 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
332
364
|
apiRef.current.dataSource.cache.clear();
|
|
333
365
|
apiRef.current.dataSource.fetchRows();
|
|
334
366
|
}
|
|
335
|
-
return
|
|
336
|
-
// ignore the current request on unmount
|
|
337
|
-
lastRequestId.current += 1;
|
|
338
|
-
};
|
|
367
|
+
return ignoreInFlightRequest;
|
|
339
368
|
}, [apiRef, props.dataSource, props.dataSourceKeepPreviousData, currentStrategy, stopPolling]);
|
|
340
369
|
React.useEffect(() => {
|
|
341
370
|
// `dataSourceKeepPreviousData` is a no-op for tree data and row grouping: those
|
|
@@ -40,6 +40,11 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
40
40
|
}, [currentStrategy]);
|
|
41
41
|
const paginationModel = useGridSelector(apiRef, gridPaginationModelSelector);
|
|
42
42
|
const lastRequestId = React.useRef(0);
|
|
43
|
+
// `false` while a request is in flight, and for any request that errors or is discarded as
|
|
44
|
+
// stale. Lets the mount effect tell "rows are already displayed" from "the fetch never landed".
|
|
45
|
+
const rowsAreUpToDate = React.useRef(false);
|
|
46
|
+
// Requests that are still running and will apply their response when they settle.
|
|
47
|
+
const pendingRequestCount = React.useRef(0);
|
|
43
48
|
const pollingIntervalRef = React.useRef(null);
|
|
44
49
|
const onDataSourceErrorProp = props.onDataSourceError;
|
|
45
50
|
const revalidateMs = props.dataSourceRevalidateMs;
|
|
@@ -84,6 +89,7 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
84
89
|
keepChildrenExpanded
|
|
85
90
|
}
|
|
86
91
|
});
|
|
92
|
+
rowsAreUpToDate.current = true;
|
|
87
93
|
if (standardRowsUpdateStrategyActive) {
|
|
88
94
|
apiRef.current.setLoading(false);
|
|
89
95
|
}
|
|
@@ -96,6 +102,8 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
96
102
|
}
|
|
97
103
|
const requestId = lastRequestId.current + 1;
|
|
98
104
|
lastRequestId.current = requestId;
|
|
105
|
+
rowsAreUpToDate.current = false;
|
|
106
|
+
pendingRequestCount.current += 1;
|
|
99
107
|
try {
|
|
100
108
|
const getRowsResponse = await getRows(fetchParams);
|
|
101
109
|
const cacheResponses = cacheChunkManager.splitResponse(fetchParams, getRowsResponse);
|
|
@@ -109,6 +117,7 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
109
117
|
keepChildrenExpanded
|
|
110
118
|
}
|
|
111
119
|
});
|
|
120
|
+
rowsAreUpToDate.current = true;
|
|
112
121
|
}
|
|
113
122
|
} catch (originalError) {
|
|
114
123
|
if (lastRequestId.current === requestId) {
|
|
@@ -131,6 +140,7 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
131
140
|
}
|
|
132
141
|
}
|
|
133
142
|
} finally {
|
|
143
|
+
pendingRequestCount.current -= 1;
|
|
134
144
|
if (standardRowsUpdateStrategyActive && lastRequestId.current === requestId) {
|
|
135
145
|
apiRef.current.setLoading(false);
|
|
136
146
|
}
|
|
@@ -306,12 +316,34 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
306
316
|
}
|
|
307
317
|
}, [revalidateMs, stopPolling]);
|
|
308
318
|
React.useEffect(() => stopPolling, [stopPolling]);
|
|
319
|
+
const lastApiRef = React.useRef(apiRef);
|
|
320
|
+
const lastStrategy = React.useRef(currentStrategy);
|
|
321
|
+
const lastDataSource = React.useRef(props.dataSource);
|
|
309
322
|
React.useEffect(() => {
|
|
323
|
+
// `<Activity mode="hidden">` leaves the root element in the document, a real unmount does not.
|
|
324
|
+
// Only discard the in-flight response in the latter case, so hiding lets the request land.
|
|
325
|
+
const rootElement = apiRef.current?.rootElementRef?.current ?? null;
|
|
326
|
+
const ignoreInFlightRequest = () => {
|
|
327
|
+
if (!rootElement?.isConnected) {
|
|
328
|
+
lastRequestId.current += 1;
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
310
332
|
// Return early if the proper strategy isn't set yet
|
|
311
333
|
// Context: https://github.com/mui/mui-x/issues/19650
|
|
312
334
|
if (currentStrategy !== DataSourceRowsUpdateStrategy.Default && currentStrategy !== DataSourceRowsUpdateStrategy.LazyLoading && currentStrategy !== DataSourceRowsUpdateStrategy.GroupedData && currentStrategy !== DataSourceRowsUpdateStrategy.LazyLoadedGroupedData) {
|
|
313
335
|
return undefined;
|
|
314
336
|
}
|
|
337
|
+
const dependenciesChanged = lastApiRef.current !== apiRef || lastStrategy.current !== currentStrategy || lastDataSource.current !== props.dataSource;
|
|
338
|
+
lastApiRef.current = apiRef;
|
|
339
|
+
lastStrategy.current = currentStrategy;
|
|
340
|
+
lastDataSource.current = props.dataSource;
|
|
341
|
+
|
|
342
|
+
// Re-mounting the effect (`<Activity />` becoming visible again, for instance) must not
|
|
343
|
+
// re-fetch data that is already displayed.
|
|
344
|
+
if (!dependenciesChanged && (rowsAreUpToDate.current || pendingRequestCount.current > 0)) {
|
|
345
|
+
return ignoreInFlightRequest;
|
|
346
|
+
}
|
|
315
347
|
if (props.dataSource) {
|
|
316
348
|
stopPolling();
|
|
317
349
|
// `dataSourceKeepPreviousData` only applies to the flat `Default` strategy (mirroring
|
|
@@ -325,10 +357,7 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
|
|
|
325
357
|
apiRef.current.dataSource.cache.clear();
|
|
326
358
|
apiRef.current.dataSource.fetchRows();
|
|
327
359
|
}
|
|
328
|
-
return
|
|
329
|
-
// ignore the current request on unmount
|
|
330
|
-
lastRequestId.current += 1;
|
|
331
|
-
};
|
|
360
|
+
return ignoreInFlightRequest;
|
|
332
361
|
}, [apiRef, props.dataSource, props.dataSourceKeepPreviousData, currentStrategy, stopPolling]);
|
|
333
362
|
React.useEffect(() => {
|
|
334
363
|
// `dataSourceKeepPreviousData` is a no-op for tree data and row grouping: those
|
|
@@ -261,7 +261,23 @@ const updateCacheWithNewRows = ({
|
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
// Update the data row lookups.
|
|
264
|
-
|
|
264
|
+
// Prototype-preservation rules:
|
|
265
|
+
// 1. If partialRow is a class instance (non-plain object), use its prototype — the
|
|
266
|
+
// caller is passing in a fully-constructed instance and owns the prototype chain.
|
|
267
|
+
// 2. If partialRow is a plain object (Object.prototype), prefer oldRow's prototype so
|
|
268
|
+
// that a partial-update object does not silently discard the existing row's class.
|
|
269
|
+
// 3. If both are plain objects the result is also a plain object (no-op).
|
|
270
|
+
//
|
|
271
|
+
// Note: private class fields (#field syntax) cannot be preserved through a spread-style
|
|
272
|
+
// merge because the brand check is tied to the original instance. Rows that rely on
|
|
273
|
+
// private fields must supply a fully-constructed instance as partialRow (rule 1 above)
|
|
274
|
+
// rather than a plain-object partial update.
|
|
275
|
+
const partialRowProto = Object.getPrototypeOf(partialRow);
|
|
276
|
+
const isPartialRowPlain = partialRowProto === Object.prototype || partialRowProto === null;
|
|
277
|
+
const proto = isPartialRowPlain ? Object.getPrototypeOf(oldRow) : partialRowProto;
|
|
278
|
+
const merged = Object.create(proto);
|
|
279
|
+
Object.assign(merged, oldRow, partialRow);
|
|
280
|
+
dataRowIdToModelLookup[id] = merged;
|
|
265
281
|
return;
|
|
266
282
|
}
|
|
267
283
|
|
|
@@ -242,7 +242,23 @@ export const updateCacheWithNewRows = ({
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
// Update the data row lookups.
|
|
245
|
-
|
|
245
|
+
// Prototype-preservation rules:
|
|
246
|
+
// 1. If partialRow is a class instance (non-plain object), use its prototype — the
|
|
247
|
+
// caller is passing in a fully-constructed instance and owns the prototype chain.
|
|
248
|
+
// 2. If partialRow is a plain object (Object.prototype), prefer oldRow's prototype so
|
|
249
|
+
// that a partial-update object does not silently discard the existing row's class.
|
|
250
|
+
// 3. If both are plain objects the result is also a plain object (no-op).
|
|
251
|
+
//
|
|
252
|
+
// Note: private class fields (#field syntax) cannot be preserved through a spread-style
|
|
253
|
+
// merge because the brand check is tied to the original instance. Rows that rely on
|
|
254
|
+
// private fields must supply a fully-constructed instance as partialRow (rule 1 above)
|
|
255
|
+
// rather than a plain-object partial update.
|
|
256
|
+
const partialRowProto = Object.getPrototypeOf(partialRow);
|
|
257
|
+
const isPartialRowPlain = partialRowProto === Object.prototype || partialRowProto === null;
|
|
258
|
+
const proto = isPartialRowPlain ? Object.getPrototypeOf(oldRow) : partialRowProto;
|
|
259
|
+
const merged = Object.create(proto);
|
|
260
|
+
Object.assign(merged, oldRow, partialRow);
|
|
261
|
+
dataRowIdToModelLookup[id] = merged;
|
|
246
262
|
return;
|
|
247
263
|
}
|
|
248
264
|
|
package/index.js
CHANGED
package/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mui/x-data-grid",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.11.0",
|
|
4
4
|
"author": "MUI Team",
|
|
5
5
|
"description": "The Community plan edition of the MUI X Data Grid components.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,12 +39,12 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@babel/runtime": "^7.29.7",
|
|
41
41
|
"@base-ui/utils": "^0.3.1",
|
|
42
|
-
"@mui/utils": "^9.
|
|
42
|
+
"@mui/utils": "^9.3.0",
|
|
43
43
|
"clsx": "^2.1.1",
|
|
44
44
|
"prop-types": "^15.8.1",
|
|
45
45
|
"use-sync-external-store": "^1.6.0",
|
|
46
|
-
"@mui/x-internals": "^9.
|
|
47
|
-
"@mui/x-virtualizer": "0.6.
|
|
46
|
+
"@mui/x-internals": "^9.11.0",
|
|
47
|
+
"@mui/x-virtualizer": "0.6.3"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@emotion/react": "^11.9.0",
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
export interface ToolbarContextValue {
|
|
3
|
-
focusableItemId: string | null;
|
|
4
|
-
registerItem: (id: string, ref: React.RefObject<HTMLButtonElement | null>) => void;
|
|
5
|
-
unregisterItem: (id: string) => void;
|
|
6
|
-
onItemKeyDown: (event: React.KeyboardEvent<HTMLButtonElement>) => void;
|
|
7
|
-
onItemFocus: (id: string) => void;
|
|
8
|
-
onItemDisabled: (id: string, disabled: boolean) => void;
|
|
9
|
-
}
|
|
10
|
-
export declare const ToolbarContext: React.Context<ToolbarContextValue | undefined>;
|
|
11
|
-
export declare function useToolbarContext(): ToolbarContextValue;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
export interface ToolbarContextValue {
|
|
3
|
-
focusableItemId: string | null;
|
|
4
|
-
registerItem: (id: string, ref: React.RefObject<HTMLButtonElement | null>) => void;
|
|
5
|
-
unregisterItem: (id: string) => void;
|
|
6
|
-
onItemKeyDown: (event: React.KeyboardEvent<HTMLButtonElement>) => void;
|
|
7
|
-
onItemFocus: (id: string) => void;
|
|
8
|
-
onItemDisabled: (id: string, disabled: boolean) => void;
|
|
9
|
-
}
|
|
10
|
-
export declare const ToolbarContext: React.Context<ToolbarContextValue | undefined>;
|
|
11
|
-
export declare function useToolbarContext(): ToolbarContextValue;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
'use client';
|
|
3
|
-
|
|
4
|
-
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
|
|
5
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
6
|
-
Object.defineProperty(exports, "__esModule", {
|
|
7
|
-
value: true
|
|
8
|
-
});
|
|
9
|
-
exports.ToolbarContext = void 0;
|
|
10
|
-
exports.useToolbarContext = useToolbarContext;
|
|
11
|
-
var _formatErrorMessage2 = _interopRequireDefault(require("@mui/x-internals/formatErrorMessage"));
|
|
12
|
-
var React = _interopRequireWildcard(require("react"));
|
|
13
|
-
const ToolbarContext = exports.ToolbarContext = /*#__PURE__*/React.createContext(undefined);
|
|
14
|
-
if (process.env.NODE_ENV !== "production") ToolbarContext.displayName = "ToolbarContext";
|
|
15
|
-
function useToolbarContext() {
|
|
16
|
-
const context = React.useContext(ToolbarContext);
|
|
17
|
-
if (context === undefined) {
|
|
18
|
-
throw new Error(process.env.NODE_ENV !== "production" ? 'MUI X: Missing context. Toolbar subcomponents must be placed within a <Toolbar /> component.' : (0, _formatErrorMessage2.default)(63));
|
|
19
|
-
}
|
|
20
|
-
return context;
|
|
21
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import _formatErrorMessage from "@mui/x-internals/formatErrorMessage";
|
|
4
|
-
import * as React from 'react';
|
|
5
|
-
export const ToolbarContext = /*#__PURE__*/React.createContext(undefined);
|
|
6
|
-
if (process.env.NODE_ENV !== "production") ToolbarContext.displayName = "ToolbarContext";
|
|
7
|
-
export function useToolbarContext() {
|
|
8
|
-
const context = React.useContext(ToolbarContext);
|
|
9
|
-
if (context === undefined) {
|
|
10
|
-
throw new Error(process.env.NODE_ENV !== "production" ? 'MUI X: Missing context. Toolbar subcomponents must be placed within a <Toolbar /> component.' : _formatErrorMessage(63));
|
|
11
|
-
}
|
|
12
|
-
return context;
|
|
13
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.sortByDocumentPosition = sortByDocumentPosition;
|
|
7
|
-
/* eslint-disable no-bitwise */
|
|
8
|
-
function sortByDocumentPosition(a, b) {
|
|
9
|
-
if (!a.ref.current || !b.ref.current) {
|
|
10
|
-
return 0;
|
|
11
|
-
}
|
|
12
|
-
const position = a.ref.current.compareDocumentPosition(b.ref.current);
|
|
13
|
-
if (!position) {
|
|
14
|
-
return 0;
|
|
15
|
-
}
|
|
16
|
-
if (position & Node.DOCUMENT_POSITION_FOLLOWING || position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
|
|
17
|
-
return -1;
|
|
18
|
-
}
|
|
19
|
-
if (position & Node.DOCUMENT_POSITION_PRECEDING || position & Node.DOCUMENT_POSITION_CONTAINS) {
|
|
20
|
-
return 1;
|
|
21
|
-
}
|
|
22
|
-
return 0;
|
|
23
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-bitwise */
|
|
2
|
-
export function sortByDocumentPosition(a, b) {
|
|
3
|
-
if (!a.ref.current || !b.ref.current) {
|
|
4
|
-
return 0;
|
|
5
|
-
}
|
|
6
|
-
const position = a.ref.current.compareDocumentPosition(b.ref.current);
|
|
7
|
-
if (!position) {
|
|
8
|
-
return 0;
|
|
9
|
-
}
|
|
10
|
-
if (position & Node.DOCUMENT_POSITION_FOLLOWING || position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
|
|
11
|
-
return -1;
|
|
12
|
-
}
|
|
13
|
-
if (position & Node.DOCUMENT_POSITION_PRECEDING || position & Node.DOCUMENT_POSITION_CONTAINS) {
|
|
14
|
-
return 1;
|
|
15
|
-
}
|
|
16
|
-
return 0;
|
|
17
|
-
}
|