@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.
- package/CHANGELOG.md +123 -0
- package/DateField/DateField.js +4 -4
- package/DateField/DateField.mjs +4 -4
- package/DateTimeField/DateTimeField.js +4 -4
- package/DateTimeField/DateTimeField.mjs +4 -4
- package/DigitalClock/DigitalClock.js +3 -1
- package/DigitalClock/DigitalClock.mjs +3 -1
- package/MultiSectionDigitalClock/MultiSectionDigitalClockSection.js +44 -7
- package/MultiSectionDigitalClock/MultiSectionDigitalClockSection.mjs +44 -7
- package/TimeField/TimeField.js +4 -4
- package/TimeField/TimeField.mjs +4 -4
- package/hooks/useSplitFieldProps.d.mts +1 -1
- package/hooks/useSplitFieldProps.d.ts +1 -1
- package/hooks/useSplitFieldProps.js +1 -1
- package/hooks/useSplitFieldProps.mjs +1 -1
- package/index.js +1 -1
- package/index.mjs +1 -1
- package/internals/components/PickerPopper/PickerPopper.js +17 -2
- package/internals/components/PickerPopper/PickerPopper.mjs +17 -2
- package/internals/hooks/useField/useField.types.d.mts +1 -1
- package/internals/hooks/useField/useField.types.d.ts +1 -1
- package/internals/hooks/useField/useFieldInternalPropsWithDefaults.js +2 -2
- package/internals/hooks/useField/useFieldInternalPropsWithDefaults.mjs +2 -2
- package/internals/hooks/useField/useFieldV6TextField.js +4 -3
- package/internals/hooks/useField/useFieldV6TextField.mjs +4 -3
- package/internals/hooks/useField/useFieldV7TextField.js +4 -3
- package/internals/hooks/useField/useFieldV7TextField.mjs +4 -3
- package/internals/hooks/useNullableFieldPrivateContext.d.mts +2 -1
- package/internals/hooks/useNullableFieldPrivateContext.d.ts +2 -1
- package/internals/hooks/usePicker/usePicker.js +13 -8
- package/internals/hooks/usePicker/usePicker.mjs +13 -8
- package/internals/hooks/usePicker/usePicker.types.d.mts +2 -1
- package/internals/hooks/usePicker/usePicker.types.d.ts +2 -1
- package/internals/index.d.mts +1 -0
- package/internals/index.d.ts +1 -0
- package/internals/index.js +7 -0
- package/internals/index.mjs +1 -0
- package/internals/utils/isElementInteractive.d.mts +1 -0
- package/internals/utils/isElementInteractive.d.ts +1 -0
- package/internals/utils/isElementInteractive.js +34 -0
- package/internals/utils/isElementInteractive.mjs +28 -0
- package/models/fields.d.mts +5 -1
- package/models/fields.d.ts +5 -1
- 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` [](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` [](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` [](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` [](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` [](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` [](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_
|
package/DateField/DateField.js
CHANGED
|
@@ -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.
|
package/DateField/DateField.mjs
CHANGED
|
@@ -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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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);
|
package/TimeField/TimeField.js
CHANGED
|
@@ -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.
|
package/TimeField/TimeField.mjs
CHANGED
|
@@ -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", "
|
|
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", "
|
|
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', '
|
|
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', '
|
|
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
package/index.mjs
CHANGED
|
@@ -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 => {
|