@mui/x-date-pickers 9.0.0-alpha.3 → 9.0.0-alpha.4

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/DateField/DateField.js +4 -4
  3. package/DateField/DateField.mjs +4 -4
  4. package/DateTimeField/DateTimeField.js +4 -4
  5. package/DateTimeField/DateTimeField.mjs +4 -4
  6. package/DigitalClock/DigitalClock.js +3 -1
  7. package/DigitalClock/DigitalClock.mjs +3 -1
  8. package/MultiSectionDigitalClock/MultiSectionDigitalClockSection.js +44 -7
  9. package/MultiSectionDigitalClock/MultiSectionDigitalClockSection.mjs +44 -7
  10. package/TimeField/TimeField.js +4 -4
  11. package/TimeField/TimeField.mjs +4 -4
  12. package/hooks/useSplitFieldProps.d.mts +1 -1
  13. package/hooks/useSplitFieldProps.d.ts +1 -1
  14. package/hooks/useSplitFieldProps.js +1 -1
  15. package/hooks/useSplitFieldProps.mjs +1 -1
  16. package/index.js +1 -1
  17. package/index.mjs +1 -1
  18. package/internals/components/PickerPopper/PickerPopper.js +17 -2
  19. package/internals/components/PickerPopper/PickerPopper.mjs +17 -2
  20. package/internals/hooks/useField/useField.types.d.mts +1 -1
  21. package/internals/hooks/useField/useField.types.d.ts +1 -1
  22. package/internals/hooks/useField/useFieldInternalPropsWithDefaults.js +2 -2
  23. package/internals/hooks/useField/useFieldInternalPropsWithDefaults.mjs +2 -2
  24. package/internals/hooks/useField/useFieldV6TextField.js +4 -3
  25. package/internals/hooks/useField/useFieldV6TextField.mjs +4 -3
  26. package/internals/hooks/useField/useFieldV7TextField.js +4 -3
  27. package/internals/hooks/useField/useFieldV7TextField.mjs +4 -3
  28. package/internals/hooks/useNullableFieldPrivateContext.d.mts +2 -1
  29. package/internals/hooks/useNullableFieldPrivateContext.d.ts +2 -1
  30. package/internals/hooks/usePicker/usePicker.js +13 -8
  31. package/internals/hooks/usePicker/usePicker.mjs +13 -8
  32. package/internals/hooks/usePicker/usePicker.types.d.mts +2 -1
  33. package/internals/hooks/usePicker/usePicker.types.d.ts +2 -1
  34. package/internals/index.d.mts +1 -0
  35. package/internals/index.d.ts +1 -0
  36. package/internals/index.js +7 -0
  37. package/internals/index.mjs +1 -0
  38. package/internals/utils/isElementInteractive.d.mts +1 -0
  39. package/internals/utils/isElementInteractive.d.ts +1 -0
  40. package/internals/utils/isElementInteractive.js +34 -0
  41. package/internals/utils/isElementInteractive.mjs +28 -0
  42. package/models/fields.d.mts +5 -1
  43. package/models/fields.d.ts +5 -1
  44. package/package.json +50 -50
package/CHANGELOG.md CHANGED
@@ -1,5 +1,128 @@
1
1
  # Changelog
2
2
 
3
+ ## 9.0.0-alpha.4
4
+
5
+ _Mar 19, 2026_
6
+
7
+ We'd like to extend a big thank you to the 12 contributors who made this release possible. Here are some highlights ✨:
8
+
9
+ - 🐞 Bugfixes and internal improvements
10
+
11
+ The following team members contributed to this release:
12
+ @aemartos, @alexfauquette, @bernardobelchior, @Janpot, @JCQuintas, @LukasTy, @mapache-salvaje, @michelengelen, @noraleonte, @rita-codes, @sai6855, @siriwatknp
13
+
14
+ ### Data Grid
15
+
16
+ #### `@mui/x-data-grid@9.0.0-alpha.4`
17
+
18
+ - [DataGrid] Mark charts integration as stable (#21764) @JCQuintas
19
+ - [DataGrid] Move `elementOverrides` to constants and remove duplicates (#21618) @sai6855
20
+ - [DataGrid] Migrate from deprecated Material UI APIs (#21682) @siriwatknp
21
+
22
+ #### `@mui/x-data-grid-pro@9.0.0-alpha.4` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
23
+
24
+ Same changes as in `@mui/x-data-grid@9.0.0-alpha.4`.
25
+
26
+ #### `@mui/x-data-grid-premium@9.0.0-alpha.4` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
27
+
28
+ Same changes as in `@mui/x-data-grid-pro@9.0.0-alpha.4`.
29
+
30
+ ### Date and Time Pickers
31
+
32
+ #### `@mui/x-date-pickers@9.0.0-alpha.4`
33
+
34
+ - [pickers] Avoid stealing focus on click away (#13434) @LukasTy
35
+ - [pickers] Promote `fieldRef` to stable and add `clearValue` method (#21655) @michelengelen
36
+
37
+ #### `@mui/x-date-pickers-pro@9.0.0-alpha.4` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
38
+
39
+ Same changes as in `@mui/x-date-pickers@9.0.0-alpha.4`.
40
+
41
+ ### Charts
42
+
43
+ #### `@mui/x-charts@9.0.0-alpha.4`
44
+
45
+ - [charts] Add v9 chart series types and helper functions migration (#21009) @bernardobelchior
46
+ - [charts] Extract event listener to the layer container (#21751) @alexfauquette
47
+ - [charts] Fix WebGL print export canvas stretching (#21738) @JCQuintas
48
+ - [charts] Improve deprecation warnings (#21760) @alexfauquette
49
+ - [charts] Improve type safety in `identifierCleaner` (#21719) @bernardobelchior
50
+ - [charts] Make `preferStrictDomainInLineCharts` the default (#21744) @JCQuintas
51
+ - [charts] Move title and description to the layer container (#21757) @alexfauquette
52
+ - [charts] Refactor `FunnelChart` classes structure (#21652) @JCQuintas
53
+ - [charts] Refactor `Heatmap` classes structure (#21653) @JCQuintas
54
+ - [charts] Refactor `RadarChart` classes structure (#21650) @JCQuintas
55
+ - [charts] Refactor `SankeyChart` classes structure (#21654) @JCQuintas
56
+ - [charts] Refactor legend getters to use utility functions (#21628) @sai6855
57
+ - [charts] Remove deprecated `ChartContainer` and `ChartDataProvider` (#21777) @alexfauquette
58
+ - [charts] Remove deprecated `itemId` from `SeriesLegendItemContext` (#21788) @alexfauquette
59
+ - [charts] Remove deprecated `useMouseTracker()` (#21787) @alexfauquette
60
+ - [charts] Remove deprecated classes (#21775) @alexfauquette
61
+ - [charts] Remove deprecated props from PieArcLabel animation (#21789) @alexfauquette
62
+ - [charts] Remove get*UtilityClass from public exports (#21769) @JCQuintas
63
+ - [charts] Remove the deprecated `disableHover` property (#21785) @alexfauquette
64
+ - [charts] Remove the deprecated `message` prop (#21784) @alexfauquette
65
+ - [charts] Remove deprecated props about voronoi (#21796) @alexfauquette
66
+ - [charts] Remove deprecated pieArcClasses (#21795) @alexfauquette
67
+ - [charts] Rename `data-series-id` by `data-series` (#21761) @alexfauquette
68
+ - [charts] Rename `voronoiMaxRadius`/`disableVoronoi` to `hitAreaRadius`/`disableHitArea` (#21750) @bernardobelchior
69
+ - [charts] Update pt-PT locale (#21296) @bernardobelchior
70
+ - [charts] Use different shape per series by default (#21713) @alexfauquette
71
+ - [charts] Add className prop to Radar components (#21794) @JCQuintas
72
+ - [charts] Add className prop to shared chart components (#21792) @JCQuintas
73
+ - [charts] Add className prop to BarPlot (#21791) @JCQuintas
74
+ - [charts] Portal tooltip into ChartsLayerContainer (#21801) @JCQuintas
75
+
76
+ #### `@mui/x-charts-pro@9.0.0-alpha.4` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
77
+
78
+ Same changes as in `@mui/x-charts@9.0.0-alpha.4`, plus:
79
+
80
+ - [charts-pro] Allow `brush` interaction to accept `requiredKeys/pointerMode` (#21716) @JCQuintas
81
+ - [charts-pro] Remove deprecated `onAxisClick` for Heatmap (#21786) @alexfauquette
82
+
83
+ #### `@mui/x-charts-premium@9.0.0-alpha.4` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
84
+
85
+ Same changes as in `@mui/x-charts-pro@9.0.0-alpha.4`, plus:
86
+
87
+ - [charts-premium] Add candlestick chart (#21129) @bernardobelchior
88
+
89
+ ### Tree View
90
+
91
+ #### `@mui/x-tree-view@9.0.0-alpha.4`
92
+
93
+ Internal changes.
94
+
95
+ #### `@mui/x-tree-view-pro@9.0.0-alpha.4` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
96
+
97
+ Same changes as in `@mui/x-tree-view@9.0.0-alpha.4`.
98
+
99
+ ### Codemod
100
+
101
+ #### `@mui/x-codemod@9.0.0-alpha.4`
102
+
103
+ Internal changes.
104
+
105
+ ### Docs
106
+
107
+ - [docs-infra] Exclude `ServerSideLazyLoadingRevalidation` from argos (#21734) @sai6855
108
+ - [docs] Update charts v9 migration guide to include premium package (#21743) @bernardobelchior
109
+ - [docs] Update v9 migration guides to install next tag (#21741) @bernardobelchior
110
+ - [docs] Revise the Pie chart docs (#21565) @mapache-salvaje
111
+ - [docs] Revise the Bar Chart docs (#21482) @mapache-salvaje
112
+ - [docs] Removed a `console.log` from an aggregation demo (#21698) @michelengelen
113
+
114
+ ### Core
115
+
116
+ - [code-infra] Add pkg-pr-new as dev dependency (#21754) @Janpot
117
+ - [code-infra] Prevent `combiner` to have default parameters (#21707) @JCQuintas
118
+ - [code-infra] Remove CI coverage collection and upload to Codecov (#21671) @Janpot
119
+ - [internal] Remove @bernardobelchior from Charts CODEOWNERS (#21776) @Copilot
120
+
121
+ ### Miscellaneous
122
+
123
+ - [x-license] Fix process.env.MUI_VERSION not being replaced during build (#21727) @aemartos
124
+ - [x-license] Add new watermark license status message (#21720) @aemartos
125
+
3
126
  ## 9.0.0-alpha.3
4
127
 
5
128
  _Mar 12, 2026_
@@ -108,6 +108,10 @@ process.env.NODE_ENV !== "production" ? DateField.propTypes = {
108
108
  * @default true
109
109
  */
110
110
  enableAccessibleFieldDOMStructure: _propTypes.default.bool,
111
+ /**
112
+ * The ref object used to imperatively interact with the field.
113
+ */
114
+ fieldRef: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.object]),
111
115
  /**
112
116
  * If `true`, the component is displayed in focused state.
113
117
  */
@@ -320,10 +324,6 @@ process.env.NODE_ENV !== "production" ? DateField.propTypes = {
320
324
  * @default The timezone of the `value` or `defaultValue` prop is defined, 'default' otherwise.
321
325
  */
322
326
  timezone: _propTypes.default.string,
323
- /**
324
- * The ref object used to imperatively interact with the field.
325
- */
326
- unstableFieldRef: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.object]),
327
327
  /**
328
328
  * The selected value.
329
329
  * Used when the component is controlled.
@@ -101,6 +101,10 @@ process.env.NODE_ENV !== "production" ? DateField.propTypes = {
101
101
  * @default true
102
102
  */
103
103
  enableAccessibleFieldDOMStructure: PropTypes.bool,
104
+ /**
105
+ * The ref object used to imperatively interact with the field.
106
+ */
107
+ fieldRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
104
108
  /**
105
109
  * If `true`, the component is displayed in focused state.
106
110
  */
@@ -313,10 +317,6 @@ process.env.NODE_ENV !== "production" ? DateField.propTypes = {
313
317
  * @default The timezone of the `value` or `defaultValue` prop is defined, 'default' otherwise.
314
318
  */
315
319
  timezone: PropTypes.string,
316
- /**
317
- * The ref object used to imperatively interact with the field.
318
- */
319
- unstableFieldRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
320
320
  /**
321
321
  * The selected value.
322
322
  * Used when the component is controlled.
@@ -118,6 +118,10 @@ process.env.NODE_ENV !== "production" ? DateTimeField.propTypes = {
118
118
  * @default true
119
119
  */
120
120
  enableAccessibleFieldDOMStructure: _propTypes.default.bool,
121
+ /**
122
+ * The ref object used to imperatively interact with the field.
123
+ */
124
+ fieldRef: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.object]),
121
125
  /**
122
126
  * If `true`, the component is displayed in focused state.
123
127
  */
@@ -360,10 +364,6 @@ process.env.NODE_ENV !== "production" ? DateTimeField.propTypes = {
360
364
  * @default The timezone of the `value` or `defaultValue` prop is defined, 'default' otherwise.
361
365
  */
362
366
  timezone: _propTypes.default.string,
363
- /**
364
- * The ref object used to imperatively interact with the field.
365
- */
366
- unstableFieldRef: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.object]),
367
367
  /**
368
368
  * The selected value.
369
369
  * Used when the component is controlled.
@@ -111,6 +111,10 @@ process.env.NODE_ENV !== "production" ? DateTimeField.propTypes = {
111
111
  * @default true
112
112
  */
113
113
  enableAccessibleFieldDOMStructure: PropTypes.bool,
114
+ /**
115
+ * The ref object used to imperatively interact with the field.
116
+ */
117
+ fieldRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
114
118
  /**
115
119
  * If `true`, the component is displayed in focused state.
116
120
  */
@@ -353,10 +357,6 @@ process.env.NODE_ENV !== "production" ? DateTimeField.propTypes = {
353
357
  * @default The timezone of the `value` or `defaultValue` prop is defined, 'default' otherwise.
354
358
  */
355
359
  timezone: PropTypes.string,
356
- /**
357
- * The ref object used to imperatively interact with the field.
358
- */
359
- unstableFieldRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
360
360
  /**
361
361
  * The selected value.
362
362
  * Used when the component is controlled.
@@ -111,6 +111,7 @@ const DigitalClock = exports.DigitalClock = /*#__PURE__*/React.forwardRef(functi
111
111
  const containerRef = React.useRef(null);
112
112
  const handleRef = (0, _useForkRef.default)(ref, containerRef);
113
113
  const listRef = React.useRef(null);
114
+ const lastActiveRef = React.useRef(null);
114
115
  const props = (0, _styles.useThemeProps)({
115
116
  props: inProps,
116
117
  name: 'MuiDigitalClock'
@@ -206,7 +207,8 @@ const DigitalClock = exports.DigitalClock = /*#__PURE__*/React.forwardRef(functi
206
207
  return;
207
208
  }
208
209
  const offsetTop = activeItem.offsetTop;
209
- if (autoFocus || !!focusedView) {
210
+ if ((autoFocus || !!focusedView) && activeItem !== lastActiveRef.current) {
211
+ lastActiveRef.current = activeItem;
210
212
  activeItem.focus();
211
213
  }
212
214
 
@@ -104,6 +104,7 @@ export const DigitalClock = /*#__PURE__*/React.forwardRef(function DigitalClock(
104
104
  const containerRef = React.useRef(null);
105
105
  const handleRef = useForkRef(ref, containerRef);
106
106
  const listRef = React.useRef(null);
107
+ const lastActiveRef = React.useRef(null);
107
108
  const props = useThemeProps({
108
109
  props: inProps,
109
110
  name: 'MuiDigitalClock'
@@ -199,7 +200,8 @@ export const DigitalClock = /*#__PURE__*/React.forwardRef(function DigitalClock(
199
200
  return;
200
201
  }
201
202
  const offsetTop = activeItem.offsetTop;
202
- if (autoFocus || !!focusedView) {
203
+ if ((autoFocus || !!focusedView) && activeItem !== lastActiveRef.current) {
204
+ lastActiveRef.current = activeItem;
203
205
  activeItem.focus();
204
206
  }
205
207
 
@@ -17,6 +17,7 @@ var _MenuList = _interopRequireDefault(require("@mui/material/MenuList"));
17
17
  var _MenuItem = _interopRequireDefault(require("@mui/material/MenuItem"));
18
18
  var _useForkRef = _interopRequireDefault(require("@mui/utils/useForkRef"));
19
19
  var _useEnhancedEffect = _interopRequireDefault(require("@mui/utils/useEnhancedEffect"));
20
+ var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback"));
20
21
  var _multiSectionDigitalClockSectionClasses = require("./multiSectionDigitalClockSectionClasses");
21
22
  var _dimensions = require("../internals/constants/dimensions");
22
23
  var _utils = require("../internals/utils/utils");
@@ -100,6 +101,7 @@ const MultiSectionDigitalClockSection = exports.MultiSectionDigitalClockSection
100
101
  const containerRef = React.useRef(null);
101
102
  const handleRef = (0, _useForkRef.default)(ref, containerRef);
102
103
  const previousActive = React.useRef(null);
104
+ const shouldRefocusOnNextRender = React.useRef(false);
103
105
  const props = (0, _styles.useThemeProps)({
104
106
  props: inProps,
105
107
  name: 'MuiMultiSectionDigitalClockSection'
@@ -131,13 +133,21 @@ const MultiSectionDigitalClockSection = exports.MultiSectionDigitalClockSection
131
133
  return;
132
134
  }
133
135
  const activeItem = containerRef.current.querySelector('[role="option"][tabindex="0"], [role="option"][aria-selected="true"]');
134
- if (active && autoFocus && activeItem) {
136
+ if (!activeItem) {
137
+ return;
138
+ }
139
+ const activeElement = document.activeElement;
140
+ const isSameItemAsPrevious = previousActive.current === activeItem;
141
+ const isFocusInsideSection = !!activeElement && containerRef.current.contains(activeElement);
142
+ const shouldRefocusSameItem = isSameItemAsPrevious && shouldRefocusOnNextRender.current;
143
+ if (active && autoFocus && (!isSameItemAsPrevious || shouldRefocusSameItem) && (previousActive.current == null || shouldRefocusOnNextRender.current || isFocusInsideSection)) {
144
+ previousActive.current = activeItem;
145
+ shouldRefocusOnNextRender.current = false;
135
146
  activeItem.focus();
136
147
  }
137
- if (!activeItem || previousActive.current === activeItem) {
148
+ if (isSameItemAsPrevious) {
138
149
  return;
139
150
  }
140
- previousActive.current = activeItem;
141
151
  const offsetTop = activeItem.offsetTop;
142
152
  const itemHeight = activeItem.offsetHeight;
143
153
  const containerHeight = containerRef.current.clientHeight;
@@ -155,9 +165,35 @@ const MultiSectionDigitalClockSection = exports.MultiSectionDigitalClockSection
155
165
  // Ensure we don't scroll past the top
156
166
  containerRef.current.scrollTop = Math.max(0, scrollPosition);
157
167
  });
168
+ const handleBlur = (0, _useEventCallback.default)(event => {
169
+ // Keep focus restoration only for in-picker keyboard navigation.
170
+ // Do not restore focus after leaving the picker, which would steal focus from external inputs.
171
+ const relatedTarget = event.relatedTarget;
172
+ const blurParent = relatedTarget?.parentElement;
173
+ const relatedTargetRole = relatedTarget?.getAttribute('role');
174
+ const shouldRefocus = blurParent?.nodeName === 'UL' && blurParent !== containerRef.current || relatedTargetRole === 'gridcell';
175
+ shouldRefocusOnNextRender.current = shouldRefocus;
176
+ if (previousActive.current && blurParent?.nodeName === 'UL' && blurParent !== containerRef.current) {
177
+ previousActive.current = null;
178
+ }
179
+ });
180
+
181
+ // Reset tracking when section becomes inactive
182
+ // so focus can be reapplied when user returns via keyboard
183
+ React.useEffect(() => {
184
+ if (!active) {
185
+ previousActive.current = null;
186
+ }
187
+ }, [active]);
158
188
  const focusedOptionIndex = items.findIndex(item => item.isFocused(item.value));
159
- const handleKeyDown = event => {
189
+ const handleKeyDown = (0, _useEventCallback.default)(event => {
160
190
  switch (event.key) {
191
+ case 'Tab':
192
+ {
193
+ // Preserve focus restoration when leaving the section with keyboard navigation.
194
+ shouldRefocusOnNextRender.current = true;
195
+ break;
196
+ }
161
197
  case 'PageUp':
162
198
  {
163
199
  const newIndex = (0, _utils.getFocusedListItemIndex)(containerRef.current) - 5;
@@ -183,15 +219,16 @@ const MultiSectionDigitalClockSection = exports.MultiSectionDigitalClockSection
183
219
  break;
184
220
  }
185
221
  default:
222
+ break;
186
223
  }
187
- };
224
+ });
188
225
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(MultiSectionDigitalClockSectionRoot, (0, _extends2.default)({
189
226
  ref: handleRef,
190
227
  className: (0, _clsx.default)(classes.root, className),
191
228
  ownerState: ownerState,
192
- autoFocusItem: autoFocus && active,
193
229
  role: "listbox",
194
- onKeyDown: handleKeyDown
230
+ onKeyDown: handleKeyDown,
231
+ onBlur: handleBlur
195
232
  }, other, {
196
233
  children: items.map((option, index) => {
197
234
  const isItemDisabled = option.isDisabled?.(option.value);
@@ -11,6 +11,7 @@ import MenuList from '@mui/material/MenuList';
11
11
  import MenuItem from '@mui/material/MenuItem';
12
12
  import useForkRef from '@mui/utils/useForkRef';
13
13
  import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
14
+ import useEventCallback from '@mui/utils/useEventCallback';
14
15
  import { getMultiSectionDigitalClockSectionUtilityClass } from "./multiSectionDigitalClockSectionClasses.mjs";
15
16
  import { DIGITAL_CLOCK_VIEW_HEIGHT, MULTI_SECTION_CLOCK_SECTION_WIDTH } from "../internals/constants/dimensions.mjs";
16
17
  import { getFocusedListItemIndex } from "../internals/utils/utils.mjs";
@@ -93,6 +94,7 @@ export const MultiSectionDigitalClockSection = /*#__PURE__*/React.forwardRef(fun
93
94
  const containerRef = React.useRef(null);
94
95
  const handleRef = useForkRef(ref, containerRef);
95
96
  const previousActive = React.useRef(null);
97
+ const shouldRefocusOnNextRender = React.useRef(false);
96
98
  const props = useThemeProps({
97
99
  props: inProps,
98
100
  name: 'MuiMultiSectionDigitalClockSection'
@@ -124,13 +126,21 @@ export const MultiSectionDigitalClockSection = /*#__PURE__*/React.forwardRef(fun
124
126
  return;
125
127
  }
126
128
  const activeItem = containerRef.current.querySelector('[role="option"][tabindex="0"], [role="option"][aria-selected="true"]');
127
- if (active && autoFocus && activeItem) {
129
+ if (!activeItem) {
130
+ return;
131
+ }
132
+ const activeElement = document.activeElement;
133
+ const isSameItemAsPrevious = previousActive.current === activeItem;
134
+ const isFocusInsideSection = !!activeElement && containerRef.current.contains(activeElement);
135
+ const shouldRefocusSameItem = isSameItemAsPrevious && shouldRefocusOnNextRender.current;
136
+ if (active && autoFocus && (!isSameItemAsPrevious || shouldRefocusSameItem) && (previousActive.current == null || shouldRefocusOnNextRender.current || isFocusInsideSection)) {
137
+ previousActive.current = activeItem;
138
+ shouldRefocusOnNextRender.current = false;
128
139
  activeItem.focus();
129
140
  }
130
- if (!activeItem || previousActive.current === activeItem) {
141
+ if (isSameItemAsPrevious) {
131
142
  return;
132
143
  }
133
- previousActive.current = activeItem;
134
144
  const offsetTop = activeItem.offsetTop;
135
145
  const itemHeight = activeItem.offsetHeight;
136
146
  const containerHeight = containerRef.current.clientHeight;
@@ -148,9 +158,35 @@ export const MultiSectionDigitalClockSection = /*#__PURE__*/React.forwardRef(fun
148
158
  // Ensure we don't scroll past the top
149
159
  containerRef.current.scrollTop = Math.max(0, scrollPosition);
150
160
  });
161
+ const handleBlur = useEventCallback(event => {
162
+ // Keep focus restoration only for in-picker keyboard navigation.
163
+ // Do not restore focus after leaving the picker, which would steal focus from external inputs.
164
+ const relatedTarget = event.relatedTarget;
165
+ const blurParent = relatedTarget?.parentElement;
166
+ const relatedTargetRole = relatedTarget?.getAttribute('role');
167
+ const shouldRefocus = blurParent?.nodeName === 'UL' && blurParent !== containerRef.current || relatedTargetRole === 'gridcell';
168
+ shouldRefocusOnNextRender.current = shouldRefocus;
169
+ if (previousActive.current && blurParent?.nodeName === 'UL' && blurParent !== containerRef.current) {
170
+ previousActive.current = null;
171
+ }
172
+ });
173
+
174
+ // Reset tracking when section becomes inactive
175
+ // so focus can be reapplied when user returns via keyboard
176
+ React.useEffect(() => {
177
+ if (!active) {
178
+ previousActive.current = null;
179
+ }
180
+ }, [active]);
151
181
  const focusedOptionIndex = items.findIndex(item => item.isFocused(item.value));
152
- const handleKeyDown = event => {
182
+ const handleKeyDown = useEventCallback(event => {
153
183
  switch (event.key) {
184
+ case 'Tab':
185
+ {
186
+ // Preserve focus restoration when leaving the section with keyboard navigation.
187
+ shouldRefocusOnNextRender.current = true;
188
+ break;
189
+ }
154
190
  case 'PageUp':
155
191
  {
156
192
  const newIndex = getFocusedListItemIndex(containerRef.current) - 5;
@@ -176,15 +212,16 @@ export const MultiSectionDigitalClockSection = /*#__PURE__*/React.forwardRef(fun
176
212
  break;
177
213
  }
178
214
  default:
215
+ break;
179
216
  }
180
- };
217
+ });
181
218
  return /*#__PURE__*/_jsx(MultiSectionDigitalClockSectionRoot, _extends({
182
219
  ref: handleRef,
183
220
  className: clsx(classes.root, className),
184
221
  ownerState: ownerState,
185
- autoFocusItem: autoFocus && active,
186
222
  role: "listbox",
187
- onKeyDown: handleKeyDown
223
+ onKeyDown: handleKeyDown,
224
+ onBlur: handleBlur
188
225
  }, other, {
189
226
  children: items.map((option, index) => {
190
227
  const isItemDisabled = option.isDisabled?.(option.value);
@@ -118,6 +118,10 @@ process.env.NODE_ENV !== "production" ? TimeField.propTypes = {
118
118
  * @default true
119
119
  */
120
120
  enableAccessibleFieldDOMStructure: _propTypes.default.bool,
121
+ /**
122
+ * The ref object used to imperatively interact with the field.
123
+ */
124
+ fieldRef: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.object]),
121
125
  /**
122
126
  * If `true`, the component is displayed in focused state.
123
127
  */
@@ -321,10 +325,6 @@ process.env.NODE_ENV !== "production" ? TimeField.propTypes = {
321
325
  * @default The timezone of the `value` or `defaultValue` prop is defined, 'default' otherwise.
322
326
  */
323
327
  timezone: _propTypes.default.string,
324
- /**
325
- * The ref object used to imperatively interact with the field.
326
- */
327
- unstableFieldRef: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.object]),
328
328
  /**
329
329
  * The selected value.
330
330
  * Used when the component is controlled.
@@ -111,6 +111,10 @@ process.env.NODE_ENV !== "production" ? TimeField.propTypes = {
111
111
  * @default true
112
112
  */
113
113
  enableAccessibleFieldDOMStructure: PropTypes.bool,
114
+ /**
115
+ * The ref object used to imperatively interact with the field.
116
+ */
117
+ fieldRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
114
118
  /**
115
119
  * If `true`, the component is displayed in focused state.
116
120
  */
@@ -314,10 +318,6 @@ process.env.NODE_ENV !== "production" ? TimeField.propTypes = {
314
318
  * @default The timezone of the `value` or `defaultValue` prop is defined, 'default' otherwise.
315
319
  */
316
320
  timezone: PropTypes.string,
317
- /**
318
- * The ref object used to imperatively interact with the field.
319
- */
320
- unstableFieldRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
321
321
  /**
322
322
  * The selected value.
323
323
  * Used when the component is controlled.
@@ -1,6 +1,6 @@
1
1
  import { DATE_TIME_VALIDATION_PROP_NAMES, DATE_VALIDATION_PROP_NAMES, TIME_VALIDATION_PROP_NAMES } from "../validation/extractValidationProps.mjs";
2
2
  import { PickerValueType } from "../models/common.mjs";
3
- declare const SHARED_FIELD_INTERNAL_PROP_NAMES: readonly ["value", "defaultValue", "referenceDate", "format", "formatDensity", "onChange", "timezone", "onError", "shouldRespectLeadingZeros", "selectedSections", "onSelectedSectionsChange", "unstableFieldRef", "unstableStartFieldRef", "unstableEndFieldRef", "enableAccessibleFieldDOMStructure", "disabled", "readOnly", "dateSeparator", "autoFocus", "focused"];
3
+ declare const SHARED_FIELD_INTERNAL_PROP_NAMES: readonly ["value", "defaultValue", "referenceDate", "format", "formatDensity", "onChange", "timezone", "onError", "shouldRespectLeadingZeros", "selectedSections", "onSelectedSectionsChange", "fieldRef", "startFieldRef", "endFieldRef", "enableAccessibleFieldDOMStructure", "disabled", "readOnly", "dateSeparator", "autoFocus", "focused"];
4
4
  export type InternalPropNames<TValueType extends PickerValueType> = (typeof SHARED_FIELD_INTERNAL_PROP_NAMES)[number] | (TValueType extends 'date' | 'date-time' ? (typeof DATE_VALIDATION_PROP_NAMES)[number] : never) | (TValueType extends 'time' | 'date-time' ? (typeof TIME_VALIDATION_PROP_NAMES)[number] : never) | (TValueType extends 'date-time' ? (typeof DATE_TIME_VALIDATION_PROP_NAMES)[number] : never);
5
5
  /**
6
6
  * Split the props received by the field component into:
@@ -1,6 +1,6 @@
1
1
  import { DATE_TIME_VALIDATION_PROP_NAMES, DATE_VALIDATION_PROP_NAMES, TIME_VALIDATION_PROP_NAMES } from "../validation/extractValidationProps.js";
2
2
  import { PickerValueType } from "../models/common.js";
3
- declare const SHARED_FIELD_INTERNAL_PROP_NAMES: readonly ["value", "defaultValue", "referenceDate", "format", "formatDensity", "onChange", "timezone", "onError", "shouldRespectLeadingZeros", "selectedSections", "onSelectedSectionsChange", "unstableFieldRef", "unstableStartFieldRef", "unstableEndFieldRef", "enableAccessibleFieldDOMStructure", "disabled", "readOnly", "dateSeparator", "autoFocus", "focused"];
3
+ declare const SHARED_FIELD_INTERNAL_PROP_NAMES: readonly ["value", "defaultValue", "referenceDate", "format", "formatDensity", "onChange", "timezone", "onError", "shouldRespectLeadingZeros", "selectedSections", "onSelectedSectionsChange", "fieldRef", "startFieldRef", "endFieldRef", "enableAccessibleFieldDOMStructure", "disabled", "readOnly", "dateSeparator", "autoFocus", "focused"];
4
4
  export type InternalPropNames<TValueType extends PickerValueType> = (typeof SHARED_FIELD_INTERNAL_PROP_NAMES)[number] | (TValueType extends 'date' | 'date-time' ? (typeof DATE_VALIDATION_PROP_NAMES)[number] : never) | (TValueType extends 'time' | 'date-time' ? (typeof TIME_VALIDATION_PROP_NAMES)[number] : never) | (TValueType extends 'date-time' ? (typeof DATE_TIME_VALIDATION_PROP_NAMES)[number] : never);
5
5
  /**
6
6
  * Split the props received by the field component into:
@@ -10,7 +10,7 @@ exports.useSplitFieldProps = void 0;
10
10
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
11
11
  var React = _interopRequireWildcard(require("react"));
12
12
  var _extractValidationProps = require("../validation/extractValidationProps");
13
- const SHARED_FIELD_INTERNAL_PROP_NAMES = ['value', 'defaultValue', 'referenceDate', 'format', 'formatDensity', 'onChange', 'timezone', 'onError', 'shouldRespectLeadingZeros', 'selectedSections', 'onSelectedSectionsChange', 'unstableFieldRef', 'unstableStartFieldRef', 'unstableEndFieldRef', 'enableAccessibleFieldDOMStructure', 'disabled', 'readOnly', 'dateSeparator', 'autoFocus', 'focused'];
13
+ const SHARED_FIELD_INTERNAL_PROP_NAMES = ['value', 'defaultValue', 'referenceDate', 'format', 'formatDensity', 'onChange', 'timezone', 'onError', 'shouldRespectLeadingZeros', 'selectedSections', 'onSelectedSectionsChange', 'fieldRef', 'startFieldRef', 'endFieldRef', 'enableAccessibleFieldDOMStructure', 'disabled', 'readOnly', 'dateSeparator', 'autoFocus', 'focused'];
14
14
  /**
15
15
  * Split the props received by the field component into:
16
16
  * - `internalProps` which are used by the various hooks called by the field component.
@@ -3,7 +3,7 @@
3
3
  import _extends from "@babel/runtime/helpers/esm/extends";
4
4
  import * as React from 'react';
5
5
  import { DATE_TIME_VALIDATION_PROP_NAMES, DATE_VALIDATION_PROP_NAMES, TIME_VALIDATION_PROP_NAMES } from "../validation/extractValidationProps.mjs";
6
- const SHARED_FIELD_INTERNAL_PROP_NAMES = ['value', 'defaultValue', 'referenceDate', 'format', 'formatDensity', 'onChange', 'timezone', 'onError', 'shouldRespectLeadingZeros', 'selectedSections', 'onSelectedSectionsChange', 'unstableFieldRef', 'unstableStartFieldRef', 'unstableEndFieldRef', 'enableAccessibleFieldDOMStructure', 'disabled', 'readOnly', 'dateSeparator', 'autoFocus', 'focused'];
6
+ const SHARED_FIELD_INTERNAL_PROP_NAMES = ['value', 'defaultValue', 'referenceDate', 'format', 'formatDensity', 'onChange', 'timezone', 'onError', 'shouldRespectLeadingZeros', 'selectedSections', 'onSelectedSectionsChange', 'fieldRef', 'startFieldRef', 'endFieldRef', 'enableAccessibleFieldDOMStructure', 'disabled', 'readOnly', 'dateSeparator', 'autoFocus', 'focused'];
7
7
  /**
8
8
  * Split the props received by the field component into:
9
9
  * - `internalProps` which are used by the various hooks called by the field component.
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-date-pickers v9.0.0-alpha.3
2
+ * @mui/x-date-pickers v9.0.0-alpha.4
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-date-pickers v9.0.0-alpha.3
2
+ * @mui/x-date-pickers v9.0.0-alpha.4
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -21,6 +21,7 @@ var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallb
21
21
  var _ownerDocument = _interopRequireDefault(require("@mui/utils/ownerDocument"));
22
22
  var _composeClasses = _interopRequireDefault(require("@mui/utils/composeClasses"));
23
23
  var _styles = require("@mui/material/styles");
24
+ var _isElementInteractive = require("../../utils/isElementInteractive");
24
25
  var _pickerPopperClasses = require("./pickerPopperClasses");
25
26
  var _utils = require("../../utils/utils");
26
27
  var _usePickerPrivateContext = require("../../hooks/usePickerPrivateContext");
@@ -213,6 +214,13 @@ const PickerPopperPaperWrapper = /*#__PURE__*/React.forwardRef((props, ref) => {
213
214
  }));
214
215
  });
215
216
  if (process.env.NODE_ENV !== "production") PickerPopperPaperWrapper.displayName = "PickerPopperPaperWrapper";
217
+ const isEventTargetInteractive = eventTarget => {
218
+ const element = eventTarget instanceof HTMLElement ? eventTarget : null;
219
+ if (!element) {
220
+ return false;
221
+ }
222
+ return (0, _isElementInteractive.isElementInteractive)(element);
223
+ };
216
224
  function PickerPopper(inProps) {
217
225
  const props = (0, _styles.useThemeProps)({
218
226
  props: inProps,
@@ -284,6 +292,14 @@ function PickerPopper(inProps) {
284
292
  dismissViews();
285
293
  });
286
294
  } else {
295
+ // Get all the targets of this event.
296
+ const eventTargets = event.composedPath();
297
+ // https://github.com/mui/mui-x/pull/13434
298
+ // Check if the click is on an interactive element.
299
+ // If it is, we don't want to refocus the last focused element.
300
+ if (eventTargets.some(isEventTargetInteractive)) {
301
+ lastFocusedElementRef.current = null;
302
+ }
287
303
  dismissViews();
288
304
  }
289
305
  });
@@ -330,8 +346,7 @@ function PickerPopper(inProps) {
330
346
  // which would force screen readers to read too old label
331
347
  ,
332
348
  disableRestoreFocus: true,
333
- disableEnforceFocus: viewContainerRole === 'tooltip',
334
- isEnabled: () => true
349
+ disableEnforceFocus: viewContainerRole === 'tooltip'
335
350
  }, slotProps?.desktopTrapFocus, {
336
351
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(Transition, (0, _extends2.default)({}, TransitionProps, slotProps?.desktopTransition, {
337
352
  onExited: event => {
@@ -15,6 +15,7 @@ import useEventCallback from '@mui/utils/useEventCallback';
15
15
  import ownerDocument from '@mui/utils/ownerDocument';
16
16
  import composeClasses from '@mui/utils/composeClasses';
17
17
  import { styled, useThemeProps } from '@mui/material/styles';
18
+ import { isElementInteractive } from "../../utils/isElementInteractive.mjs";
18
19
  import { getPickerPopperUtilityClass } from "./pickerPopperClasses.mjs";
19
20
  import { executeInTheNextEventLoopTick, getActiveElement } from "../../utils/utils.mjs";
20
21
  import { usePickerPrivateContext } from "../../hooks/usePickerPrivateContext.mjs";
@@ -206,6 +207,13 @@ const PickerPopperPaperWrapper = /*#__PURE__*/React.forwardRef((props, ref) => {
206
207
  }));
207
208
  });
208
209
  if (process.env.NODE_ENV !== "production") PickerPopperPaperWrapper.displayName = "PickerPopperPaperWrapper";
210
+ const isEventTargetInteractive = eventTarget => {
211
+ const element = eventTarget instanceof HTMLElement ? eventTarget : null;
212
+ if (!element) {
213
+ return false;
214
+ }
215
+ return isElementInteractive(element);
216
+ };
209
217
  export function PickerPopper(inProps) {
210
218
  const props = useThemeProps({
211
219
  props: inProps,
@@ -277,6 +285,14 @@ export function PickerPopper(inProps) {
277
285
  dismissViews();
278
286
  });
279
287
  } else {
288
+ // Get all the targets of this event.
289
+ const eventTargets = event.composedPath();
290
+ // https://github.com/mui/mui-x/pull/13434
291
+ // Check if the click is on an interactive element.
292
+ // If it is, we don't want to refocus the last focused element.
293
+ if (eventTargets.some(isEventTargetInteractive)) {
294
+ lastFocusedElementRef.current = null;
295
+ }
280
296
  dismissViews();
281
297
  }
282
298
  });
@@ -323,8 +339,7 @@ export function PickerPopper(inProps) {
323
339
  // which would force screen readers to read too old label
324
340
  ,
325
341
  disableRestoreFocus: true,
326
- disableEnforceFocus: viewContainerRole === 'tooltip',
327
- isEnabled: () => true
342
+ disableEnforceFocus: viewContainerRole === 'tooltip'
328
343
  }, slotProps?.desktopTrapFocus, {
329
344
  children: /*#__PURE__*/_jsx(Transition, _extends({}, TransitionProps, slotProps?.desktopTransition, {
330
345
  onExited: event => {