@mui/x-date-pickers 8.29.2 → 8.29.3

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 (28) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/PickersTextField/PickersFilledInput/PickersFilledInput.js +1 -0
  3. package/PickersTextField/PickersFilledInput/PickersFilledInput.mjs +1 -0
  4. package/PickersTextField/PickersInput/PickersInput.js +1 -0
  5. package/PickersTextField/PickersInput/PickersInput.mjs +1 -0
  6. package/PickersTextField/PickersInputBase/PickersInputBase.js +20 -20
  7. package/PickersTextField/PickersInputBase/PickersInputBase.mjs +20 -20
  8. package/PickersTextField/PickersInputBase/PickersInputBase.types.d.mts +1 -0
  9. package/PickersTextField/PickersInputBase/PickersInputBase.types.d.ts +1 -0
  10. package/PickersTextField/PickersInputBase/pickersInputBaseClasses.js +1 -1
  11. package/PickersTextField/PickersInputBase/pickersInputBaseClasses.mjs +1 -1
  12. package/PickersTextField/PickersOutlinedInput/PickersOutlinedInput.js +1 -0
  13. package/PickersTextField/PickersOutlinedInput/PickersOutlinedInput.mjs +1 -0
  14. package/PickersTextField/PickersTextField.js +4 -1
  15. package/PickersTextField/PickersTextField.mjs +4 -1
  16. package/index.js +1 -1
  17. package/index.mjs +1 -1
  18. package/internals/hooks/useField/useField.types.d.mts +1 -0
  19. package/internals/hooks/useField/useField.types.d.ts +1 -0
  20. package/internals/hooks/useField/useFieldRootProps.d.mts +1 -0
  21. package/internals/hooks/useField/useFieldRootProps.d.ts +1 -0
  22. package/internals/hooks/useField/useFieldRootProps.js +135 -8
  23. package/internals/hooks/useField/useFieldRootProps.mjs +135 -8
  24. package/internals/hooks/useField/useFieldSectionContainerProps.js +23 -4
  25. package/internals/hooks/useField/useFieldSectionContainerProps.mjs +22 -4
  26. package/internals/hooks/useField/useFieldV7TextField.js +13 -0
  27. package/internals/hooks/useField/useFieldV7TextField.mjs +13 -0
  28. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -5,6 +5,78 @@
5
5
  All notable changes to this project will be documented in this file.
6
6
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
7
7
 
8
+ ## 8.29.3
9
+
10
+ _Aug 21, 2026_
11
+
12
+ We'd like to extend a big thank you to the 2 contributors who made this release possible. Here are some highlights ✨:
13
+
14
+ - 🐞 Bugfixes
15
+
16
+ The following team members contributed to this release:
17
+ @LukasTy, @MBilalShafi
18
+
19
+ ### Data Grid
20
+
21
+ #### `@mui/x-data-grid@8.29.3`
22
+
23
+ - [DataGrid] Fix toolbar button stealing focus when a sibling's disabled state changes (#23265) @MBilalShafi
24
+
25
+ #### `@mui/x-data-grid-pro@8.29.3` [![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@8.29.3`.
28
+
29
+ #### `@mui/x-data-grid-premium@8.29.3` [![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@8.29.3`.
32
+
33
+ ### Date and Time Pickers
34
+
35
+ #### `@mui/x-date-pickers@8.29.3`
36
+
37
+ - [fields] Keep the field editable on blank space clicks (#23372) @LukasTy
38
+ - [pickers] Fix format placeholder brightness with a start adornment (#23205) @LukasTy
39
+
40
+ #### `@mui/x-date-pickers-pro@8.29.3` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
41
+
42
+ Same changes as in `@mui/x-date-pickers@8.29.3`, plus:
43
+
44
+ - [DateRangePicker] Fix broken active range position underline in single input range fields (#23201) @LukasTy
45
+
46
+ ### Charts
47
+
48
+ #### `@mui/x-charts@8.29.3`
49
+
50
+ Internal changes.
51
+
52
+ #### `@mui/x-charts-pro@8.29.3` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
53
+
54
+ Same changes as in `@mui/x-charts@8.29.3`.
55
+
56
+ #### `@mui/x-charts-premium@8.29.3` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
57
+
58
+ Same changes as in `@mui/x-charts-pro@8.29.3`.
59
+
60
+ ### Tree View
61
+
62
+ #### `@mui/x-tree-view@8.29.3`
63
+
64
+ Internal changes.
65
+
66
+ #### `@mui/x-tree-view-pro@8.29.3` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
67
+
68
+ Same changes as in `@mui/x-tree-view@8.29.3`.
69
+
70
+ ### Codemod
71
+
72
+ #### `@mui/x-codemod@8.29.3`
73
+
74
+ Internal changes.
75
+
76
+ ### Miscellaneous
77
+
78
+ - [core] Fix `test-react-18` install failure on v8.x (#23202) @LukasTy
79
+
8
80
  ## 8.29.2
9
81
 
10
82
  _Jul 2, 2026_
@@ -279,6 +279,7 @@ process.env.NODE_ENV !== "production" ? PickersFilledInput.propTypes = {
279
279
  onClick: _propTypes.default.func.isRequired,
280
280
  onInput: _propTypes.default.func.isRequired,
281
281
  onKeyDown: _propTypes.default.func.isRequired,
282
+ onMouseDown: _propTypes.default.func.isRequired,
282
283
  onPaste: _propTypes.default.func.isRequired,
283
284
  ownerState: _propTypes.default /* @typescript-to-proptypes-ignore */.any,
284
285
  readOnly: _propTypes.default.bool,
@@ -272,6 +272,7 @@ process.env.NODE_ENV !== "production" ? PickersFilledInput.propTypes = {
272
272
  onClick: PropTypes.func.isRequired,
273
273
  onInput: PropTypes.func.isRequired,
274
274
  onKeyDown: PropTypes.func.isRequired,
275
+ onMouseDown: PropTypes.func.isRequired,
275
276
  onPaste: PropTypes.func.isRequired,
276
277
  ownerState: PropTypes /* @typescript-to-proptypes-ignore */.any,
277
278
  readOnly: PropTypes.bool,
@@ -197,6 +197,7 @@ process.env.NODE_ENV !== "production" ? PickersInput.propTypes = {
197
197
  onClick: _propTypes.default.func.isRequired,
198
198
  onInput: _propTypes.default.func.isRequired,
199
199
  onKeyDown: _propTypes.default.func.isRequired,
200
+ onMouseDown: _propTypes.default.func.isRequired,
200
201
  onPaste: _propTypes.default.func.isRequired,
201
202
  ownerState: _propTypes.default /* @typescript-to-proptypes-ignore */.any,
202
203
  readOnly: _propTypes.default.bool,
@@ -190,6 +190,7 @@ process.env.NODE_ENV !== "production" ? PickersInput.propTypes = {
190
190
  onClick: PropTypes.func.isRequired,
191
191
  onInput: PropTypes.func.isRequired,
192
192
  onKeyDown: PropTypes.func.isRequired,
193
+ onMouseDown: PropTypes.func.isRequired,
193
194
  onPaste: PropTypes.func.isRequired,
194
195
  ownerState: PropTypes /* @typescript-to-proptypes-ignore */.any,
195
196
  readOnly: PropTypes.bool,
@@ -88,39 +88,28 @@ const PickersInputBaseSectionsContainer = exports.PickersInputBaseSectionsContai
88
88
  paddingTop: 1
89
89
  }
90
90
  }, {
91
+ // While the field is empty and blurred, the format is shown as a placeholder.
91
92
  props: {
92
- hasStartAdornment: false,
93
93
  isFieldFocused: false,
94
94
  isFieldValueEmpty: true
95
95
  },
96
- style: {
97
- color: 'currentColor',
98
- opacity: 0
99
- }
100
- }, {
101
- props: {
102
- hasStartAdornment: false,
103
- isFieldFocused: false,
104
- isFieldValueEmpty: true,
105
- inputHasLabel: false
106
- },
107
96
  style: theme.vars ? {
108
97
  opacity: theme.vars.opacity.inputPlaceholder
109
98
  } : {
110
99
  opacity: theme.palette.mode === 'light' ? 0.42 : 0.5
111
100
  }
112
101
  }, {
102
+ // ...except when a non-shrunk label sits in the notch, where the format would overlap it.
103
+ // A start adornment always shrinks the label, so it never triggers this case.
113
104
  props: {
114
- hasStartAdornment: false,
115
105
  isFieldFocused: false,
116
106
  isFieldValueEmpty: true,
107
+ hasStartAdornment: false,
117
108
  inputHasLabel: true,
118
- isLabelShrunk: true
109
+ isLabelShrunk: false
119
110
  },
120
- style: theme.vars ? {
121
- opacity: theme.vars.opacity.inputPlaceholder
122
- } : {
123
- opacity: theme.palette.mode === 'light' ? 0.42 : 0.5
111
+ style: {
112
+ opacity: 0
124
113
  }
125
114
  }]
126
115
  }));
@@ -150,7 +139,16 @@ const PickersInputBaseSectionContent = (0, _styles.styled)(_PickersSectionList.U
150
139
  // 23px
151
140
  letterSpacing: 'inherit',
152
141
  width: 'fit-content',
153
- outline: 'none'
142
+ outline: 'none',
143
+ // Disables Chromium's focus-delegation onto contenteditable descendants
144
+ // while the field is not focused. Gated to Chromium via a Blink-only
145
+ // property, because the rule breaks Playwright's `fill()` on WebKit.
146
+ '@supports (-webkit-app-region: drag)': {
147
+ [`.${_pickersInputBaseClasses.pickersInputBaseClasses.root}:not(:focus-within) &`]: {
148
+ WebkitUserModify: 'read-only',
149
+ userSelect: 'none'
150
+ }
151
+ }
154
152
  }));
155
153
  const PickersInputBaseSectionSeparator = (0, _styles.styled)(_PickersSectionList.Unstable_PickersSectionListSectionSeparator, {
156
154
  name: 'MuiPickersInputBase',
@@ -216,7 +214,8 @@ const useUtilityClasses = (classes, ownerState) => {
216
214
  return (0, _composeClasses.default)(slots, _pickersInputBaseClasses.getPickersInputBaseUtilityClass, classes);
217
215
  };
218
216
  function resolveSectionElementWidth(sectionElement, rootRef, index, dateRangePosition) {
219
- if (sectionElement.content.id) {
217
+ // Only measure sections that belong to a range date.
218
+ if (sectionElement.content['data-range-position'] !== undefined) {
220
219
  const activeSectionElements = rootRef.current?.querySelectorAll(`[data-sectionindex="${index}"] [data-range-position="${dateRangePosition}"]`);
221
220
  if (activeSectionElements) {
222
221
  return Array.from(activeSectionElements).reduce((currentActiveBarWidth, element) => {
@@ -461,6 +460,7 @@ process.env.NODE_ENV !== "production" ? PickersInputBase.propTypes = {
461
460
  onClick: _propTypes.default.func.isRequired,
462
461
  onInput: _propTypes.default.func.isRequired,
463
462
  onKeyDown: _propTypes.default.func.isRequired,
463
+ onMouseDown: _propTypes.default.func.isRequired,
464
464
  onPaste: _propTypes.default.func.isRequired,
465
465
  ownerState: _propTypes.default /* @typescript-to-proptypes-ignore */.any,
466
466
  readOnly: _propTypes.default.bool,
@@ -81,39 +81,28 @@ export const PickersInputBaseSectionsContainer = styled(PickersSectionListRoot,
81
81
  paddingTop: 1
82
82
  }
83
83
  }, {
84
+ // While the field is empty and blurred, the format is shown as a placeholder.
84
85
  props: {
85
- hasStartAdornment: false,
86
86
  isFieldFocused: false,
87
87
  isFieldValueEmpty: true
88
88
  },
89
- style: {
90
- color: 'currentColor',
91
- opacity: 0
92
- }
93
- }, {
94
- props: {
95
- hasStartAdornment: false,
96
- isFieldFocused: false,
97
- isFieldValueEmpty: true,
98
- inputHasLabel: false
99
- },
100
89
  style: theme.vars ? {
101
90
  opacity: theme.vars.opacity.inputPlaceholder
102
91
  } : {
103
92
  opacity: theme.palette.mode === 'light' ? 0.42 : 0.5
104
93
  }
105
94
  }, {
95
+ // ...except when a non-shrunk label sits in the notch, where the format would overlap it.
96
+ // A start adornment always shrinks the label, so it never triggers this case.
106
97
  props: {
107
- hasStartAdornment: false,
108
98
  isFieldFocused: false,
109
99
  isFieldValueEmpty: true,
100
+ hasStartAdornment: false,
110
101
  inputHasLabel: true,
111
- isLabelShrunk: true
102
+ isLabelShrunk: false
112
103
  },
113
- style: theme.vars ? {
114
- opacity: theme.vars.opacity.inputPlaceholder
115
- } : {
116
- opacity: theme.palette.mode === 'light' ? 0.42 : 0.5
104
+ style: {
105
+ opacity: 0
117
106
  }
118
107
  }]
119
108
  }));
@@ -143,7 +132,16 @@ const PickersInputBaseSectionContent = styled(PickersSectionListSectionContent,
143
132
  // 23px
144
133
  letterSpacing: 'inherit',
145
134
  width: 'fit-content',
146
- outline: 'none'
135
+ outline: 'none',
136
+ // Disables Chromium's focus-delegation onto contenteditable descendants
137
+ // while the field is not focused. Gated to Chromium via a Blink-only
138
+ // property, because the rule breaks Playwright's `fill()` on WebKit.
139
+ '@supports (-webkit-app-region: drag)': {
140
+ [`.${pickersInputBaseClasses.root}:not(:focus-within) &`]: {
141
+ WebkitUserModify: 'read-only',
142
+ userSelect: 'none'
143
+ }
144
+ }
147
145
  }));
148
146
  const PickersInputBaseSectionSeparator = styled(PickersSectionListSectionSeparator, {
149
147
  name: 'MuiPickersInputBase',
@@ -209,7 +207,8 @@ const useUtilityClasses = (classes, ownerState) => {
209
207
  return composeClasses(slots, getPickersInputBaseUtilityClass, classes);
210
208
  };
211
209
  function resolveSectionElementWidth(sectionElement, rootRef, index, dateRangePosition) {
212
- if (sectionElement.content.id) {
210
+ // Only measure sections that belong to a range date.
211
+ if (sectionElement.content['data-range-position'] !== undefined) {
213
212
  const activeSectionElements = rootRef.current?.querySelectorAll(`[data-sectionindex="${index}"] [data-range-position="${dateRangePosition}"]`);
214
213
  if (activeSectionElements) {
215
214
  return Array.from(activeSectionElements).reduce((currentActiveBarWidth, element) => {
@@ -454,6 +453,7 @@ process.env.NODE_ENV !== "production" ? PickersInputBase.propTypes = {
454
453
  onClick: PropTypes.func.isRequired,
455
454
  onInput: PropTypes.func.isRequired,
456
455
  onKeyDown: PropTypes.func.isRequired,
456
+ onMouseDown: PropTypes.func.isRequired,
457
457
  onPaste: PropTypes.func.isRequired,
458
458
  ownerState: PropTypes /* @typescript-to-proptypes-ignore */.any,
459
459
  readOnly: PropTypes.bool,
@@ -11,6 +11,7 @@ export interface PickersInputPropsUsedByField extends Pick<PickersSectionListPro
11
11
  */
12
12
  areAllSectionsEmpty: boolean;
13
13
  onClick: React.MouseEventHandler<HTMLDivElement>;
14
+ onMouseDown: React.MouseEventHandler<HTMLDivElement>;
14
15
  onKeyDown: React.EventHandler<MuiEvent<React.KeyboardEvent<HTMLDivElement>>>;
15
16
  onInput: React.FormEventHandler<HTMLDivElement>;
16
17
  onPaste: React.ClipboardEventHandler<HTMLDivElement>;
@@ -11,6 +11,7 @@ export interface PickersInputPropsUsedByField extends Pick<PickersSectionListPro
11
11
  */
12
12
  areAllSectionsEmpty: boolean;
13
13
  onClick: React.MouseEventHandler<HTMLDivElement>;
14
+ onMouseDown: React.MouseEventHandler<HTMLDivElement>;
14
15
  onKeyDown: React.EventHandler<MuiEvent<React.KeyboardEvent<HTMLDivElement>>>;
15
16
  onInput: React.FormEventHandler<HTMLDivElement>;
16
17
  onPaste: React.ClipboardEventHandler<HTMLDivElement>;
@@ -11,4 +11,4 @@ var _generateUtilityClasses = _interopRequireDefault(require("@mui/utils/generat
11
11
  function getPickersInputBaseUtilityClass(slot) {
12
12
  return (0, _generateUtilityClass.default)('MuiPickersInputBase', slot);
13
13
  }
14
- const pickersInputBaseClasses = exports.pickersInputBaseClasses = (0, _generateUtilityClasses.default)('MuiPickersInputBase', ['root', 'focused', 'disabled', 'error', 'notchedOutline', 'sectionContent', 'sectionBefore', 'sectionAfter', 'adornedStart', 'adornedEnd', 'input', 'inputSizeSmall', 'activeBar']);
14
+ const pickersInputBaseClasses = exports.pickersInputBaseClasses = (0, _generateUtilityClasses.default)('MuiPickersInputBase', ['root', 'focused', 'disabled', 'readOnly', 'error', 'notchedOutline', 'sectionsContainer', 'sectionContent', 'sectionBefore', 'sectionAfter', 'adornedStart', 'adornedEnd', 'input', 'inputSizeSmall', 'activeBar']);
@@ -3,4 +3,4 @@ import generateUtilityClasses from '@mui/utils/generateUtilityClasses';
3
3
  export function getPickersInputBaseUtilityClass(slot) {
4
4
  return generateUtilityClass('MuiPickersInputBase', slot);
5
5
  }
6
- export const pickersInputBaseClasses = generateUtilityClasses('MuiPickersInputBase', ['root', 'focused', 'disabled', 'error', 'notchedOutline', 'sectionContent', 'sectionBefore', 'sectionAfter', 'adornedStart', 'adornedEnd', 'input', 'inputSizeSmall', 'activeBar']);
6
+ export const pickersInputBaseClasses = generateUtilityClasses('MuiPickersInputBase', ['root', 'focused', 'disabled', 'readOnly', 'error', 'notchedOutline', 'sectionsContainer', 'sectionContent', 'sectionBefore', 'sectionAfter', 'adornedStart', 'adornedEnd', 'input', 'inputSizeSmall', 'activeBar']);
@@ -172,6 +172,7 @@ process.env.NODE_ENV !== "production" ? PickersOutlinedInput.propTypes = {
172
172
  onClick: _propTypes.default.func.isRequired,
173
173
  onInput: _propTypes.default.func.isRequired,
174
174
  onKeyDown: _propTypes.default.func.isRequired,
175
+ onMouseDown: _propTypes.default.func.isRequired,
175
176
  onPaste: _propTypes.default.func.isRequired,
176
177
  ownerState: _propTypes.default /* @typescript-to-proptypes-ignore */.any,
177
178
  readOnly: _propTypes.default.bool,
@@ -165,6 +165,7 @@ process.env.NODE_ENV !== "production" ? PickersOutlinedInput.propTypes = {
165
165
  onClick: PropTypes.func.isRequired,
166
166
  onInput: PropTypes.func.isRequired,
167
167
  onKeyDown: PropTypes.func.isRequired,
168
+ onMouseDown: PropTypes.func.isRequired,
168
169
  onPaste: PropTypes.func.isRequired,
169
170
  ownerState: PropTypes /* @typescript-to-proptypes-ignore */.any,
170
171
  readOnly: PropTypes.bool,
@@ -27,7 +27,7 @@ var _PickersInput = require("./PickersInput");
27
27
  var _useFieldOwnerState = require("../internals/hooks/useFieldOwnerState");
28
28
  var _usePickerTextFieldOwnerState = require("./usePickerTextFieldOwnerState");
29
29
  var _jsxRuntime = require("react/jsx-runtime");
30
- const _excluded = ["onFocus", "onBlur", "className", "classes", "color", "disabled", "error", "variant", "required", "hiddenLabel", "InputProps", "inputProps", "inputRef", "sectionListRef", "elements", "areAllSectionsEmpty", "onClick", "onKeyDown", "onKeyUp", "onPaste", "onInput", "endAdornment", "startAdornment", "tabIndex", "contentEditable", "focused", "value", "onChange", "fullWidth", "id", "name", "helperText", "FormHelperTextProps", "label", "InputLabelProps", "data-active-range-position"];
30
+ const _excluded = ["onFocus", "onBlur", "className", "classes", "color", "disabled", "error", "variant", "required", "hiddenLabel", "InputProps", "inputProps", "inputRef", "sectionListRef", "elements", "areAllSectionsEmpty", "onClick", "onMouseDown", "onKeyDown", "onKeyUp", "onPaste", "onInput", "endAdornment", "startAdornment", "tabIndex", "contentEditable", "focused", "value", "onChange", "fullWidth", "id", "name", "helperText", "FormHelperTextProps", "label", "InputLabelProps", "data-active-range-position"];
31
31
  const VARIANT_COMPONENT = {
32
32
  standard: _PickersInput.PickersInput,
33
33
  filled: _PickersFilledInput.PickersFilledInput,
@@ -75,6 +75,7 @@ const PickersTextField = exports.PickersTextField = /*#__PURE__*/React.forwardRe
75
75
  elements,
76
76
  areAllSectionsEmpty,
77
77
  onClick,
78
+ onMouseDown,
78
79
  onKeyDown,
79
80
  onKeyUp,
80
81
  onPaste,
@@ -155,6 +156,7 @@ const PickersTextField = exports.PickersTextField = /*#__PURE__*/React.forwardRe
155
156
  elements: elements,
156
157
  areAllSectionsEmpty: areAllSectionsEmpty,
157
158
  onClick: onClick,
159
+ onMouseDown: onMouseDown,
158
160
  onKeyDown: onKeyDown,
159
161
  onKeyUp: onKeyUp,
160
162
  onInput: onInput,
@@ -267,6 +269,7 @@ process.env.NODE_ENV !== "production" ? PickersTextField.propTypes = {
267
269
  onFocus: _propTypes.default.func.isRequired,
268
270
  onInput: _propTypes.default.func.isRequired,
269
271
  onKeyDown: _propTypes.default.func.isRequired,
272
+ onMouseDown: _propTypes.default.func.isRequired,
270
273
  onPaste: _propTypes.default.func.isRequired,
271
274
  readOnly: _propTypes.default.bool,
272
275
  /**
@@ -2,7 +2,7 @@
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 = ["onFocus", "onBlur", "className", "classes", "color", "disabled", "error", "variant", "required", "hiddenLabel", "InputProps", "inputProps", "inputRef", "sectionListRef", "elements", "areAllSectionsEmpty", "onClick", "onKeyDown", "onKeyUp", "onPaste", "onInput", "endAdornment", "startAdornment", "tabIndex", "contentEditable", "focused", "value", "onChange", "fullWidth", "id", "name", "helperText", "FormHelperTextProps", "label", "InputLabelProps", "data-active-range-position"];
5
+ const _excluded = ["onFocus", "onBlur", "className", "classes", "color", "disabled", "error", "variant", "required", "hiddenLabel", "InputProps", "inputProps", "inputRef", "sectionListRef", "elements", "areAllSectionsEmpty", "onClick", "onMouseDown", "onKeyDown", "onKeyUp", "onPaste", "onInput", "endAdornment", "startAdornment", "tabIndex", "contentEditable", "focused", "value", "onChange", "fullWidth", "id", "name", "helperText", "FormHelperTextProps", "label", "InputLabelProps", "data-active-range-position"];
6
6
  import * as React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import clsx from 'clsx';
@@ -68,6 +68,7 @@ const PickersTextField = /*#__PURE__*/React.forwardRef(function PickersTextField
68
68
  elements,
69
69
  areAllSectionsEmpty,
70
70
  onClick,
71
+ onMouseDown,
71
72
  onKeyDown,
72
73
  onKeyUp,
73
74
  onPaste,
@@ -148,6 +149,7 @@ const PickersTextField = /*#__PURE__*/React.forwardRef(function PickersTextField
148
149
  elements: elements,
149
150
  areAllSectionsEmpty: areAllSectionsEmpty,
150
151
  onClick: onClick,
152
+ onMouseDown: onMouseDown,
151
153
  onKeyDown: onKeyDown,
152
154
  onKeyUp: onKeyUp,
153
155
  onInput: onInput,
@@ -260,6 +262,7 @@ process.env.NODE_ENV !== "production" ? PickersTextField.propTypes = {
260
262
  onFocus: PropTypes.func.isRequired,
261
263
  onInput: PropTypes.func.isRequired,
262
264
  onKeyDown: PropTypes.func.isRequired,
265
+ onMouseDown: PropTypes.func.isRequired,
263
266
  onPaste: PropTypes.func.isRequired,
264
267
  readOnly: PropTypes.bool,
265
268
  /**
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-date-pickers v8.29.2
2
+ * @mui/x-date-pickers v8.29.3
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 v8.29.2
2
+ * @mui/x-date-pickers v8.29.3
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -107,6 +107,7 @@ export type UseFieldForwardedProps<TEnableAccessibleFieldDOMStructure extends bo
107
107
  focused?: boolean;
108
108
  sectionListRef?: React.Ref<PickersSectionListRef>;
109
109
  onClick?: React.MouseEventHandler;
110
+ onMouseDown?: React.MouseEventHandler;
110
111
  onKeyDown?: React.KeyboardEventHandler;
111
112
  onFocus?: React.FocusEventHandler;
112
113
  onBlur?: React.FocusEventHandler;
@@ -107,6 +107,7 @@ export type UseFieldForwardedProps<TEnableAccessibleFieldDOMStructure extends bo
107
107
  focused?: boolean;
108
108
  sectionListRef?: React.Ref<PickersSectionListRef>;
109
109
  onClick?: React.MouseEventHandler;
110
+ onMouseDown?: React.MouseEventHandler;
110
111
  onKeyDown?: React.KeyboardEventHandler;
111
112
  onFocus?: React.FocusEventHandler;
112
113
  onBlur?: React.FocusEventHandler;
@@ -24,6 +24,7 @@ interface UseFieldRootPropsReturnValue {
24
24
  onBlur: React.FocusEventHandler<HTMLDivElement>;
25
25
  onFocus: React.FocusEventHandler<HTMLDivElement>;
26
26
  onClick: React.MouseEventHandler<HTMLDivElement>;
27
+ onMouseDown: React.MouseEventHandler<HTMLDivElement>;
27
28
  onPaste: React.ClipboardEventHandler<HTMLDivElement>;
28
29
  onInput: React.FormEventHandler<HTMLDivElement>;
29
30
  contentEditable: boolean;
@@ -24,6 +24,7 @@ interface UseFieldRootPropsReturnValue {
24
24
  onBlur: React.FocusEventHandler<HTMLDivElement>;
25
25
  onFocus: React.FocusEventHandler<HTMLDivElement>;
26
26
  onClick: React.MouseEventHandler<HTMLDivElement>;
27
+ onMouseDown: React.MouseEventHandler<HTMLDivElement>;
27
28
  onPaste: React.ClipboardEventHandler<HTMLDivElement>;
28
29
  onInput: React.FormEventHandler<HTMLDivElement>;
29
30
  contentEditable: boolean;
@@ -50,14 +50,22 @@ function useFieldRootProps(parameters) {
50
50
  stateResponse
51
51
  });
52
52
  const containerClickTimeout = (0, _useTimeout.default)();
53
- const handleClick = (0, _useEventCallback.default)(event => {
53
+ const handleClick = (0, _useEventCallback.default)(() => {
54
54
  if (disabled || !domGetters.isReady()) {
55
55
  return;
56
56
  }
57
- setFocused(true);
58
57
  if (parsedSelectedSections === 'all') {
58
+ setFocused(true);
59
59
  containerClickTimeout.start(0, () => {
60
- const cursorPosition = document.getSelection().getRangeAt(0).startOffset;
60
+ // `getSelection`/`getRangeAt` can be unset transiently (e.g. focus
61
+ // moved before this 0-tick callback ran). Fall back to the first
62
+ // section in that case.
63
+ const selection = document.getSelection();
64
+ if (!selection || selection.rangeCount === 0) {
65
+ setSelectedSections(sectionOrder.startIndex);
66
+ return;
67
+ }
68
+ const cursorPosition = selection.getRangeAt(0).startOffset;
61
69
  if (cursorPosition === 0) {
62
70
  setSelectedSections(sectionOrder.startIndex);
63
71
  return;
@@ -71,15 +79,83 @@ function useFieldRootProps(parameters) {
71
79
  }
72
80
  setSelectedSections(sectionIndex - 1);
73
81
  });
74
- } else if (!focused) {
82
+ return;
83
+ }
84
+
85
+ // For trusted pointer input, `handleMouseDown` already ran and set the
86
+ // section. This branch only matters for `click` events that arrive
87
+ // without a preceding `mousedown` (programmatic `element.click()`, some
88
+ // assistive-technology activations, synthetic event sequences in tests).
89
+ // Fall back to the first section so the field doesn't enter a "focused
90
+ // but no active section" state.
91
+ if (!focused) {
75
92
  setFocused(true);
76
- setSelectedSections(sectionOrder.startIndex);
77
- } else {
78
- const hasClickedOnASection = domGetters.getRoot().contains(event.target);
79
- if (!hasClickedOnASection) {
93
+ if (parsedSelectedSections == null) {
94
+ setSelectedSections(sectionOrder.startIndex);
95
+ }
96
+ }
97
+ });
98
+
99
+ // Replaces Chromium's focus delegation with an explicit section focus on
100
+ // every primary mousedown inside the sections container. The CSS gate
101
+ // (`WebkitUserModify: read-only` while not `:focus-within`) can make the
102
+ // section's contenteditable span temporarily non-focusable, in which case
103
+ // Chromium falls back to the sections-container `tabindex=0` and our
104
+ // `handleFocus` then runs the "no active section" fallback, briefly
105
+ // selecting the first section before the click bubble corrects it.
106
+ // Setting the target section here (and letting `syncSelectionToDOM` move
107
+ // focus via `.focus()`, which works even with the user-modify rule
108
+ // active) skips that race entirely.
109
+ const handleMouseDown = (0, _useEventCallback.default)(event => {
110
+ if (disabled || !domGetters.isReady() || parsedSelectedSections === 'all') {
111
+ return;
112
+ }
113
+ if (event.button !== 0) {
114
+ return;
115
+ }
116
+ const target = event.target;
117
+ const sectionListRoot = domGetters.getRoot();
118
+ const isInsideSectionList = sectionListRoot.contains(target);
119
+ // Only the field root is ours outside the sections container: it is the
120
+ // target of a padding click. The adornments keep their own behavior.
121
+ if (!isInsideSectionList && target !== event.currentTarget) {
122
+ return;
123
+ }
124
+ const sectionElement = isInsideSectionList ? target.closest('[data-sectionindex]') : null;
125
+
126
+ // Blank space is the field padding and the area past the sections. Mimic
127
+ // the native date input: first section on entry, keep it afterwards.
128
+ // `preventDefault` is what keeps it, by stopping both the browser blur and
129
+ // Chromium's focus delegation.
130
+ const isBlankSpaceClick = sectionElement == null && (!isInsideSectionList || isPointOutsideSections(sectionListRoot, event.clientX));
131
+ if (isBlankSpaceClick) {
132
+ event.preventDefault();
133
+ if (!focused) {
134
+ setFocused(true);
80
135
  setSelectedSections(sectionOrder.startIndex);
81
136
  }
137
+ return;
82
138
  }
139
+
140
+ // Prefer the visually-containing section (matches Chromium's
141
+ // delegation + section container `onClick`), fall back to the
142
+ // closest-by-distance section for clicks on the container padding.
143
+ const parsedIndex = sectionElement ? Number(sectionElement.dataset.sectionindex) : findClosestSectionIndexToPoint(sectionListRoot, event.clientX);
144
+ // `Number(undefined) === NaN` and `NaN == null === false`, so guard
145
+ // explicitly here even though `data-sectionindex` is set by
146
+ // `useFieldSectionContainerProps` for every section in practice.
147
+ if (parsedIndex == null || !Number.isInteger(parsedIndex)) {
148
+ return;
149
+ }
150
+ event.preventDefault();
151
+ setFocused(true);
152
+ // `mousedown` is now authoritative for pointer section selection. The
153
+ // section container's own `onClick` deduplicates against the resulting
154
+ // `parsedSelectedSections` on the click bubble (see
155
+ // `useFieldSectionContainerProps`), so we always select here and let that
156
+ // guard absorb the redundant call -- matching the pre-PR
157
+ // `onSelectedSectionsChange` invocation count.
158
+ setSelectedSections(parsedIndex);
83
159
  });
84
160
  const handleInput = (0, _useEventCallback.default)(event => {
85
161
  if (!domGetters.isReady() || parsedSelectedSections !== 'all') {
@@ -148,10 +224,61 @@ function useFieldRootProps(parameters) {
148
224
  onBlur: handleBlur,
149
225
  onFocus: handleFocus,
150
226
  onClick: handleClick,
227
+ onMouseDown: handleMouseDown,
151
228
  onPaste: handlePaste,
152
229
  onInput: handleInput,
153
230
  // Other
154
231
  contentEditable: parsedSelectedSections === 'all',
155
232
  tabIndex: internalPropsWithDefaults.disabled || parsedSelectedSections === 0 ? -1 : 0 // TODO: Try to set to undefined when there is a section selected.
156
233
  };
234
+ }
235
+
236
+ /**
237
+ * Slop on each side of the sections, so a click that just misses the outermost
238
+ * one still selects it. Under half a digit at the default font size.
239
+ */
240
+ const BLANK_SPACE_TOLERANCE = 4;
241
+
242
+ /**
243
+ * Returns `true` when `clientX` sits past the sections on either side.
244
+ * Horizontal only: a click in the container's vertical padding still belongs to
245
+ * the section above or below it.
246
+ */
247
+ function isPointOutsideSections(root, clientX) {
248
+ const sections = root.querySelectorAll('[data-sectionindex]');
249
+ if (sections.length === 0) {
250
+ return false;
251
+ }
252
+ let left = Infinity;
253
+ let right = -Infinity;
254
+ for (let i = 0; i < sections.length; i += 1) {
255
+ const rect = sections[i].getBoundingClientRect();
256
+ left = Math.min(left, rect.left);
257
+ right = Math.max(right, rect.right);
258
+ }
259
+ return clientX < left - BLANK_SPACE_TOLERANCE || clientX > right + BLANK_SPACE_TOLERANCE;
260
+ }
261
+
262
+ /**
263
+ * Returns the index of the section whose horizontal center is closest to `clientX`.
264
+ * Returns `null` if the field renders no `[role="spinbutton"]` descendants
265
+ * (defensive -- every section content span sets `role="spinbutton"` in practice).
266
+ */
267
+ function findClosestSectionIndexToPoint(root, clientX) {
268
+ const sections = root.querySelectorAll('[role="spinbutton"]');
269
+ if (sections.length === 0) {
270
+ return null;
271
+ }
272
+ let closestIndex = 0;
273
+ let closestDistance = Infinity;
274
+ for (let i = 0; i < sections.length; i += 1) {
275
+ const rect = sections[i].getBoundingClientRect();
276
+ const center = (rect.left + rect.right) / 2;
277
+ const distance = Math.abs(clientX - center);
278
+ if (distance < closestDistance) {
279
+ closestDistance = distance;
280
+ closestIndex = i;
281
+ }
282
+ }
283
+ return closestIndex;
157
284
  }
@@ -44,14 +44,22 @@ export function useFieldRootProps(parameters) {
44
44
  stateResponse
45
45
  });
46
46
  const containerClickTimeout = useTimeout();
47
- const handleClick = useEventCallback(event => {
47
+ const handleClick = useEventCallback(() => {
48
48
  if (disabled || !domGetters.isReady()) {
49
49
  return;
50
50
  }
51
- setFocused(true);
52
51
  if (parsedSelectedSections === 'all') {
52
+ setFocused(true);
53
53
  containerClickTimeout.start(0, () => {
54
- const cursorPosition = document.getSelection().getRangeAt(0).startOffset;
54
+ // `getSelection`/`getRangeAt` can be unset transiently (e.g. focus
55
+ // moved before this 0-tick callback ran). Fall back to the first
56
+ // section in that case.
57
+ const selection = document.getSelection();
58
+ if (!selection || selection.rangeCount === 0) {
59
+ setSelectedSections(sectionOrder.startIndex);
60
+ return;
61
+ }
62
+ const cursorPosition = selection.getRangeAt(0).startOffset;
55
63
  if (cursorPosition === 0) {
56
64
  setSelectedSections(sectionOrder.startIndex);
57
65
  return;
@@ -65,15 +73,83 @@ export function useFieldRootProps(parameters) {
65
73
  }
66
74
  setSelectedSections(sectionIndex - 1);
67
75
  });
68
- } else if (!focused) {
76
+ return;
77
+ }
78
+
79
+ // For trusted pointer input, `handleMouseDown` already ran and set the
80
+ // section. This branch only matters for `click` events that arrive
81
+ // without a preceding `mousedown` (programmatic `element.click()`, some
82
+ // assistive-technology activations, synthetic event sequences in tests).
83
+ // Fall back to the first section so the field doesn't enter a "focused
84
+ // but no active section" state.
85
+ if (!focused) {
69
86
  setFocused(true);
70
- setSelectedSections(sectionOrder.startIndex);
71
- } else {
72
- const hasClickedOnASection = domGetters.getRoot().contains(event.target);
73
- if (!hasClickedOnASection) {
87
+ if (parsedSelectedSections == null) {
88
+ setSelectedSections(sectionOrder.startIndex);
89
+ }
90
+ }
91
+ });
92
+
93
+ // Replaces Chromium's focus delegation with an explicit section focus on
94
+ // every primary mousedown inside the sections container. The CSS gate
95
+ // (`WebkitUserModify: read-only` while not `:focus-within`) can make the
96
+ // section's contenteditable span temporarily non-focusable, in which case
97
+ // Chromium falls back to the sections-container `tabindex=0` and our
98
+ // `handleFocus` then runs the "no active section" fallback, briefly
99
+ // selecting the first section before the click bubble corrects it.
100
+ // Setting the target section here (and letting `syncSelectionToDOM` move
101
+ // focus via `.focus()`, which works even with the user-modify rule
102
+ // active) skips that race entirely.
103
+ const handleMouseDown = useEventCallback(event => {
104
+ if (disabled || !domGetters.isReady() || parsedSelectedSections === 'all') {
105
+ return;
106
+ }
107
+ if (event.button !== 0) {
108
+ return;
109
+ }
110
+ const target = event.target;
111
+ const sectionListRoot = domGetters.getRoot();
112
+ const isInsideSectionList = sectionListRoot.contains(target);
113
+ // Only the field root is ours outside the sections container: it is the
114
+ // target of a padding click. The adornments keep their own behavior.
115
+ if (!isInsideSectionList && target !== event.currentTarget) {
116
+ return;
117
+ }
118
+ const sectionElement = isInsideSectionList ? target.closest('[data-sectionindex]') : null;
119
+
120
+ // Blank space is the field padding and the area past the sections. Mimic
121
+ // the native date input: first section on entry, keep it afterwards.
122
+ // `preventDefault` is what keeps it, by stopping both the browser blur and
123
+ // Chromium's focus delegation.
124
+ const isBlankSpaceClick = sectionElement == null && (!isInsideSectionList || isPointOutsideSections(sectionListRoot, event.clientX));
125
+ if (isBlankSpaceClick) {
126
+ event.preventDefault();
127
+ if (!focused) {
128
+ setFocused(true);
74
129
  setSelectedSections(sectionOrder.startIndex);
75
130
  }
131
+ return;
76
132
  }
133
+
134
+ // Prefer the visually-containing section (matches Chromium's
135
+ // delegation + section container `onClick`), fall back to the
136
+ // closest-by-distance section for clicks on the container padding.
137
+ const parsedIndex = sectionElement ? Number(sectionElement.dataset.sectionindex) : findClosestSectionIndexToPoint(sectionListRoot, event.clientX);
138
+ // `Number(undefined) === NaN` and `NaN == null === false`, so guard
139
+ // explicitly here even though `data-sectionindex` is set by
140
+ // `useFieldSectionContainerProps` for every section in practice.
141
+ if (parsedIndex == null || !Number.isInteger(parsedIndex)) {
142
+ return;
143
+ }
144
+ event.preventDefault();
145
+ setFocused(true);
146
+ // `mousedown` is now authoritative for pointer section selection. The
147
+ // section container's own `onClick` deduplicates against the resulting
148
+ // `parsedSelectedSections` on the click bubble (see
149
+ // `useFieldSectionContainerProps`), so we always select here and let that
150
+ // guard absorb the redundant call -- matching the pre-PR
151
+ // `onSelectedSectionsChange` invocation count.
152
+ setSelectedSections(parsedIndex);
77
153
  });
78
154
  const handleInput = useEventCallback(event => {
79
155
  if (!domGetters.isReady() || parsedSelectedSections !== 'all') {
@@ -142,10 +218,61 @@ export function useFieldRootProps(parameters) {
142
218
  onBlur: handleBlur,
143
219
  onFocus: handleFocus,
144
220
  onClick: handleClick,
221
+ onMouseDown: handleMouseDown,
145
222
  onPaste: handlePaste,
146
223
  onInput: handleInput,
147
224
  // Other
148
225
  contentEditable: parsedSelectedSections === 'all',
149
226
  tabIndex: internalPropsWithDefaults.disabled || parsedSelectedSections === 0 ? -1 : 0 // TODO: Try to set to undefined when there is a section selected.
150
227
  };
228
+ }
229
+
230
+ /**
231
+ * Slop on each side of the sections, so a click that just misses the outermost
232
+ * one still selects it. Under half a digit at the default font size.
233
+ */
234
+ const BLANK_SPACE_TOLERANCE = 4;
235
+
236
+ /**
237
+ * Returns `true` when `clientX` sits past the sections on either side.
238
+ * Horizontal only: a click in the container's vertical padding still belongs to
239
+ * the section above or below it.
240
+ */
241
+ function isPointOutsideSections(root, clientX) {
242
+ const sections = root.querySelectorAll('[data-sectionindex]');
243
+ if (sections.length === 0) {
244
+ return false;
245
+ }
246
+ let left = Infinity;
247
+ let right = -Infinity;
248
+ for (let i = 0; i < sections.length; i += 1) {
249
+ const rect = sections[i].getBoundingClientRect();
250
+ left = Math.min(left, rect.left);
251
+ right = Math.max(right, rect.right);
252
+ }
253
+ return clientX < left - BLANK_SPACE_TOLERANCE || clientX > right + BLANK_SPACE_TOLERANCE;
254
+ }
255
+
256
+ /**
257
+ * Returns the index of the section whose horizontal center is closest to `clientX`.
258
+ * Returns `null` if the field renders no `[role="spinbutton"]` descendants
259
+ * (defensive -- every section content span sets `role="spinbutton"` in practice).
260
+ */
261
+ function findClosestSectionIndexToPoint(root, clientX) {
262
+ const sections = root.querySelectorAll('[role="spinbutton"]');
263
+ if (sections.length === 0) {
264
+ return null;
265
+ }
266
+ let closestIndex = 0;
267
+ let closestDistance = Infinity;
268
+ for (let i = 0; i < sections.length; i += 1) {
269
+ const rect = sections[i].getBoundingClientRect();
270
+ const center = (rect.left + rect.right) / 2;
271
+ const distance = Math.abs(clientX - center);
272
+ if (distance < closestDistance) {
273
+ closestDistance = distance;
274
+ closestIndex = i;
275
+ }
276
+ }
277
+ return closestIndex;
151
278
  }
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
3
4
  var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
4
5
  Object.defineProperty(exports, "__esModule", {
5
6
  value: true
6
7
  });
7
8
  exports.useFieldSectionContainerProps = useFieldSectionContainerProps;
8
9
  var React = _interopRequireWildcard(require("react"));
10
+ var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback"));
9
11
  /**
10
12
  * Generate the props to pass to the container element of each section of the field.
11
13
  * It is not used by the non-accessible DOM structure (with an <input /> element for editing).
@@ -16,6 +18,8 @@ var React = _interopRequireWildcard(require("react"));
16
18
  function useFieldSectionContainerProps(parameters) {
17
19
  const {
18
20
  stateResponse: {
21
+ // States and derived states
22
+ parsedSelectedSections,
19
23
  // Methods to update the states
20
24
  setSelectedSections
21
25
  },
@@ -23,16 +27,31 @@ function useFieldSectionContainerProps(parameters) {
23
27
  disabled = false
24
28
  }
25
29
  } = parameters;
26
- const createHandleClick = React.useCallback(sectionIndex => event => {
30
+
31
+ // A single stable handler shared by every section, rather than a per-index
32
+ // factory. The section index is read from the container's `data-sectionindex`
33
+ // at click time.
34
+ const handleClick = (0, _useEventCallback.default)(event => {
27
35
  // The click event on the clear button would propagate to the input, trigger this handler and result in a wrong section selection.
28
36
  // We avoid this by checking if the call to this function is actually intended, or a side effect.
29
37
  if (disabled || event.isDefaultPrevented()) {
30
38
  return;
31
39
  }
40
+ const sectionIndex = Number(event.currentTarget.dataset.sectionindex);
41
+ if (!Number.isInteger(sectionIndex)) {
42
+ return;
43
+ }
44
+
45
+ // For pointer input the `mousedown` handler already selected the section
46
+ // before this click bubbles, so bail to avoid a redundant
47
+ // `onSelectedSectionsChange` invocation.
48
+ if (parsedSelectedSections === sectionIndex) {
49
+ return;
50
+ }
32
51
  setSelectedSections(sectionIndex);
33
- }, [disabled, setSelectedSections]);
52
+ });
34
53
  return React.useCallback(sectionIndex => ({
35
54
  'data-sectionindex': sectionIndex,
36
- onClick: createHandleClick(sectionIndex)
37
- }), [createHandleClick]);
55
+ onClick: handleClick
56
+ }), [handleClick]);
38
57
  }
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import useEventCallback from '@mui/utils/useEventCallback';
2
3
  /**
3
4
  * Generate the props to pass to the container element of each section of the field.
4
5
  * It is not used by the non-accessible DOM structure (with an <input /> element for editing).
@@ -9,6 +10,8 @@ import * as React from 'react';
9
10
  export function useFieldSectionContainerProps(parameters) {
10
11
  const {
11
12
  stateResponse: {
13
+ // States and derived states
14
+ parsedSelectedSections,
12
15
  // Methods to update the states
13
16
  setSelectedSections
14
17
  },
@@ -16,16 +19,31 @@ export function useFieldSectionContainerProps(parameters) {
16
19
  disabled = false
17
20
  }
18
21
  } = parameters;
19
- const createHandleClick = React.useCallback(sectionIndex => event => {
22
+
23
+ // A single stable handler shared by every section, rather than a per-index
24
+ // factory. The section index is read from the container's `data-sectionindex`
25
+ // at click time.
26
+ const handleClick = useEventCallback(event => {
20
27
  // The click event on the clear button would propagate to the input, trigger this handler and result in a wrong section selection.
21
28
  // We avoid this by checking if the call to this function is actually intended, or a side effect.
22
29
  if (disabled || event.isDefaultPrevented()) {
23
30
  return;
24
31
  }
32
+ const sectionIndex = Number(event.currentTarget.dataset.sectionindex);
33
+ if (!Number.isInteger(sectionIndex)) {
34
+ return;
35
+ }
36
+
37
+ // For pointer input the `mousedown` handler already selected the section
38
+ // before this click bubbles, so bail to avoid a redundant
39
+ // `onSelectedSectionsChange` invocation.
40
+ if (parsedSelectedSections === sectionIndex) {
41
+ return;
42
+ }
25
43
  setSelectedSections(sectionIndex);
26
- }, [disabled, setSelectedSections]);
44
+ });
27
45
  return React.useCallback(sectionIndex => ({
28
46
  'data-sectionindex': sectionIndex,
29
- onClick: createHandleClick(sectionIndex)
30
- }), [createHandleClick]);
47
+ onClick: handleClick
48
+ }), [handleClick]);
31
49
  }
@@ -46,6 +46,7 @@ const useFieldV7TextField = parameters => {
46
46
  sectionListRef: sectionListRefProp,
47
47
  onBlur,
48
48
  onClick,
49
+ onMouseDown,
49
50
  onFocus,
50
51
  onInput,
51
52
  onPaste,
@@ -147,6 +148,17 @@ const useFieldV7TextField = parameters => {
147
148
  onClick?.(event);
148
149
  rootProps.onClick(event);
149
150
  });
151
+ const handleRootMouseDown = (0, _useEventCallback.default)(event => {
152
+ // The `isDefaultPrevented` check skips mousedowns that have already been
153
+ // suppressed before this handler runs -- in particular, propagated events
154
+ // from the clear / open buttons whose own handlers `preventDefault`, and
155
+ // capture-phase parents that intentionally block field interactions.
156
+ if (event.isDefaultPrevented()) {
157
+ return;
158
+ }
159
+ onMouseDown?.(event);
160
+ rootProps.onMouseDown(event);
161
+ });
150
162
  const handleRootPaste = (0, _useEventCallback.default)(event => {
151
163
  onPaste?.(event);
152
164
  rootProps.onPaste(event);
@@ -229,6 +241,7 @@ const useFieldV7TextField = parameters => {
229
241
  return (0, _extends2.default)({}, forwardedProps, rootProps, {
230
242
  onBlur: handleRootBlur,
231
243
  onClick: handleRootClick,
244
+ onMouseDown: handleRootMouseDown,
232
245
  onFocus: handleRootFocus,
233
246
  onInput: handleRootInput,
234
247
  onPaste: handleRootPaste,
@@ -39,6 +39,7 @@ export const useFieldV7TextField = parameters => {
39
39
  sectionListRef: sectionListRefProp,
40
40
  onBlur,
41
41
  onClick,
42
+ onMouseDown,
42
43
  onFocus,
43
44
  onInput,
44
45
  onPaste,
@@ -140,6 +141,17 @@ export const useFieldV7TextField = parameters => {
140
141
  onClick?.(event);
141
142
  rootProps.onClick(event);
142
143
  });
144
+ const handleRootMouseDown = useEventCallback(event => {
145
+ // The `isDefaultPrevented` check skips mousedowns that have already been
146
+ // suppressed before this handler runs -- in particular, propagated events
147
+ // from the clear / open buttons whose own handlers `preventDefault`, and
148
+ // capture-phase parents that intentionally block field interactions.
149
+ if (event.isDefaultPrevented()) {
150
+ return;
151
+ }
152
+ onMouseDown?.(event);
153
+ rootProps.onMouseDown(event);
154
+ });
143
155
  const handleRootPaste = useEventCallback(event => {
144
156
  onPaste?.(event);
145
157
  rootProps.onPaste(event);
@@ -222,6 +234,7 @@ export const useFieldV7TextField = parameters => {
222
234
  return _extends({}, forwardedProps, rootProps, {
223
235
  onBlur: handleRootBlur,
224
236
  onClick: handleRootClick,
237
+ onMouseDown: handleRootMouseDown,
225
238
  onFocus: handleRootFocus,
226
239
  onInput: handleRootInput,
227
240
  onPaste: handleRootPaste,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/x-date-pickers",
3
- "version": "8.29.2",
3
+ "version": "8.29.3",
4
4
  "author": "MUI Team",
5
5
  "description": "The community edition of the MUI X Date and Time Picker components.",
6
6
  "license": "MIT",
@@ -39,7 +39,7 @@
39
39
  "clsx": "^2.1.1",
40
40
  "prop-types": "^15.8.1",
41
41
  "react-transition-group": "^4.4.5",
42
- "@mui/x-internals": "8.29.2"
42
+ "@mui/x-internals": "8.29.3"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@emotion/react": "^11.9.0",