@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 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` [![pro](https://mui.com/r/x-pro-svg)](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` [![premium](https://mui.com/r/x-premium-svg)](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` [![pro](https://mui.com/r/x-pro-svg)](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` [![pro](https://mui.com/r/x-pro-svg)](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` [![premium](https://mui.com/r/x-premium-svg)](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` [![pro](https://mui.com/r/x-pro-svg)](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` [![premium](https://mui.com/r/x-premium-svg)](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.ToolbarContext.Provider, {
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(ToolbarContext.Provider, {
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
- focusableItemId,
49
- registerItem,
50
- unregisterItem,
51
- onItemKeyDown,
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: focusableItemId === id ? 0 : -1
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
- focusableItemId,
42
- registerItem,
43
- unregisterItem,
44
- onItemKeyDown,
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: focusableItemId === id ? 0 : -1
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
- dataRowIdToModelLookup[id] = (0, _extends2.default)({}, oldRow, partialRow);
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
- dataRowIdToModelLookup[id] = _extends({}, oldRow, partialRow);
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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v9.10.1
2
+ * @mui/x-data-grid v9.11.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v9.10.1
2
+ * @mui/x-data-grid v9.11.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/x-data-grid",
3
- "version": "9.10.1",
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.2.0",
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.10.1",
47
- "@mui/x-virtualizer": "0.6.2"
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,5 +0,0 @@
1
- export declare function sortByDocumentPosition(a: {
2
- ref: React.RefObject<HTMLButtonElement | null>;
3
- }, b: {
4
- ref: React.RefObject<HTMLButtonElement | null>;
5
- }): -1 | 0 | 1;
@@ -1,5 +0,0 @@
1
- export declare function sortByDocumentPosition(a: {
2
- ref: React.RefObject<HTMLButtonElement | null>;
3
- }, b: {
4
- ref: React.RefObject<HTMLButtonElement | null>;
5
- }): -1 | 0 | 1;
@@ -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
- }