@mui/material 9.0.1 → 9.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/Accordion/Accordion.d.mts +2 -2
  2. package/Accordion/Accordion.d.ts +2 -2
  3. package/Accordion/Accordion.js +3 -2
  4. package/Accordion/Accordion.mjs +3 -2
  5. package/AccordionSummary/AccordionSummary.js +27 -29
  6. package/AccordionSummary/AccordionSummary.mjs +27 -29
  7. package/Autocomplete/Autocomplete.js +8 -6
  8. package/Autocomplete/Autocomplete.mjs +8 -6
  9. package/Backdrop/Backdrop.d.mts +2 -2
  10. package/Backdrop/Backdrop.d.ts +2 -2
  11. package/Badge/Badge.js +28 -24
  12. package/Badge/Badge.mjs +28 -24
  13. package/BottomNavigationAction/BottomNavigationAction.js +6 -2
  14. package/BottomNavigationAction/BottomNavigationAction.mjs +6 -2
  15. package/Button/Button.js +11 -15
  16. package/Button/Button.mjs +11 -15
  17. package/ButtonBase/Ripple.js +21 -11
  18. package/ButtonBase/Ripple.mjs +21 -11
  19. package/ButtonBase/TouchRipple.js +252 -116
  20. package/ButtonBase/TouchRipple.mjs +253 -117
  21. package/CHANGELOG.md +84 -0
  22. package/CardActionArea/CardActionArea.js +2 -1
  23. package/CardActionArea/CardActionArea.mjs +2 -1
  24. package/Chip/Chip.js +2 -1
  25. package/Chip/Chip.mjs +2 -1
  26. package/CircularProgress/CircularProgress.js +85 -55
  27. package/CircularProgress/CircularProgress.mjs +84 -55
  28. package/Collapse/Collapse.d.mts +15 -3
  29. package/Collapse/Collapse.d.ts +15 -3
  30. package/Collapse/Collapse.js +44 -31
  31. package/Collapse/Collapse.mjs +43 -30
  32. package/Dialog/Dialog.d.mts +2 -2
  33. package/Dialog/Dialog.d.ts +2 -2
  34. package/Dialog/Dialog.js +2 -0
  35. package/Dialog/Dialog.mjs +2 -0
  36. package/Drawer/Drawer.d.mts +2 -2
  37. package/Drawer/Drawer.d.ts +2 -2
  38. package/Fab/Fab.js +2 -1
  39. package/Fab/Fab.mjs +2 -1
  40. package/Fade/Fade.d.mts +15 -2
  41. package/Fade/Fade.d.ts +15 -2
  42. package/Fade/Fade.js +46 -19
  43. package/Fade/Fade.mjs +45 -18
  44. package/FilledInput/FilledInput.js +4 -3
  45. package/FilledInput/FilledInput.mjs +4 -3
  46. package/Grow/Grow.d.mts +15 -2
  47. package/Grow/Grow.d.ts +15 -2
  48. package/Grow/Grow.js +45 -28
  49. package/Grow/Grow.mjs +44 -27
  50. package/IconButton/IconButton.js +2 -1
  51. package/IconButton/IconButton.mjs +2 -1
  52. package/Input/Input.js +3 -2
  53. package/Input/Input.mjs +3 -2
  54. package/InputBase/InputBase.js +2 -1
  55. package/InputBase/InputBase.mjs +2 -1
  56. package/InputLabel/InputLabel.js +2 -1
  57. package/InputLabel/InputLabel.mjs +2 -1
  58. package/LICENSE +1 -1
  59. package/LinearProgress/LinearProgress.js +187 -120
  60. package/LinearProgress/LinearProgress.mjs +186 -120
  61. package/ListItem/ListItem.js +2 -1
  62. package/ListItem/ListItem.mjs +2 -1
  63. package/ListItemButton/ListItemButton.js +2 -1
  64. package/ListItemButton/ListItemButton.mjs +2 -1
  65. package/Menu/Menu.d.mts +1 -1
  66. package/Menu/Menu.d.ts +1 -1
  67. package/MobileStepper/MobileStepper.js +2 -1
  68. package/MobileStepper/MobileStepper.mjs +2 -1
  69. package/OutlinedInput/NotchedOutline.js +4 -3
  70. package/OutlinedInput/NotchedOutline.mjs +4 -3
  71. package/PaginationItem/PaginationItem.js +2 -1
  72. package/PaginationItem/PaginationItem.mjs +2 -1
  73. package/Paper/Paper.js +2 -1
  74. package/Paper/Paper.mjs +2 -1
  75. package/Popover/Popover.d.mts +1 -1
  76. package/Popover/Popover.d.ts +1 -1
  77. package/README.md +3 -2
  78. package/Radio/RadioButtonIcon.js +3 -2
  79. package/Radio/RadioButtonIcon.mjs +3 -2
  80. package/Rating/Rating.js +2 -1
  81. package/Rating/Rating.mjs +2 -1
  82. package/Select/SelectInput.js +115 -25
  83. package/Select/SelectInput.mjs +115 -25
  84. package/Select/utils/closedTypeahead.js +73 -0
  85. package/Select/utils/closedTypeahead.mjs +63 -0
  86. package/Skeleton/Skeleton.js +22 -2
  87. package/Skeleton/Skeleton.mjs +22 -2
  88. package/Slide/Slide.d.mts +15 -2
  89. package/Slide/Slide.d.ts +15 -2
  90. package/Slide/Slide.js +53 -25
  91. package/Slide/Slide.mjs +52 -24
  92. package/Slider/Slider.js +4 -3
  93. package/Slider/Slider.mjs +4 -3
  94. package/Slider/useSlider.js +1 -1
  95. package/Slider/useSlider.mjs +1 -1
  96. package/Snackbar/Snackbar.d.mts +2 -2
  97. package/Snackbar/Snackbar.d.ts +2 -2
  98. package/SpeedDial/SpeedDial.d.mts +1 -1
  99. package/SpeedDial/SpeedDial.d.ts +1 -1
  100. package/SpeedDial/SpeedDial.js +6 -2
  101. package/SpeedDial/SpeedDial.mjs +6 -2
  102. package/SpeedDialAction/SpeedDialAction.js +11 -2
  103. package/SpeedDialAction/SpeedDialAction.mjs +12 -3
  104. package/SpeedDialIcon/SpeedDialIcon.js +40 -37
  105. package/SpeedDialIcon/SpeedDialIcon.mjs +40 -37
  106. package/Step/Step.js +47 -15
  107. package/Step/Step.mjs +47 -15
  108. package/StepButton/StepButton.js +9 -3
  109. package/StepButton/StepButton.mjs +9 -3
  110. package/StepConnector/StepConnector.js +10 -0
  111. package/StepConnector/StepConnector.mjs +10 -0
  112. package/StepContent/StepContent.d.mts +2 -2
  113. package/StepContent/StepContent.d.ts +2 -2
  114. package/StepContent/StepContent.js +26 -2
  115. package/StepContent/StepContent.mjs +26 -2
  116. package/StepIcon/StepIcon.js +2 -1
  117. package/StepIcon/StepIcon.mjs +2 -1
  118. package/StepLabel/StepLabel.js +52 -7
  119. package/StepLabel/StepLabel.mjs +52 -7
  120. package/Stepper/Stepper.d.mts +2 -0
  121. package/Stepper/Stepper.d.ts +2 -0
  122. package/Stepper/Stepper.js +18 -0
  123. package/Stepper/Stepper.mjs +18 -0
  124. package/SvgIcon/SvgIcon.js +2 -1
  125. package/SvgIcon/SvgIcon.mjs +2 -1
  126. package/SwipeableDrawer/SwipeableDrawer.js +14 -3
  127. package/SwipeableDrawer/SwipeableDrawer.mjs +14 -3
  128. package/Switch/Switch.js +3 -2
  129. package/Switch/Switch.mjs +3 -2
  130. package/TableSortLabel/TableSortLabel.js +2 -1
  131. package/TableSortLabel/TableSortLabel.mjs +2 -1
  132. package/Tabs/Tabs.js +14 -3
  133. package/Tabs/Tabs.mjs +14 -3
  134. package/Tooltip/Tooltip.d.mts +2 -2
  135. package/Tooltip/Tooltip.d.ts +2 -2
  136. package/Tooltip/Tooltip.js +3 -0
  137. package/Tooltip/Tooltip.mjs +3 -0
  138. package/Unstable_TrapFocus/FocusTrap.js +42 -8
  139. package/Unstable_TrapFocus/FocusTrap.mjs +42 -8
  140. package/Zoom/Zoom.d.mts +15 -2
  141. package/Zoom/Zoom.d.ts +15 -2
  142. package/Zoom/Zoom.js +43 -16
  143. package/Zoom/Zoom.mjs +42 -15
  144. package/index.js +1 -1
  145. package/index.mjs +1 -1
  146. package/internal/Transition.d.mts +34 -0
  147. package/internal/Transition.d.ts +34 -0
  148. package/internal/Transition.js +444 -0
  149. package/internal/Transition.mjs +436 -0
  150. package/internal/react-transition-group.d.mts +8 -0
  151. package/internal/react-transition-group.d.ts +8 -0
  152. package/package.json +6 -6
  153. package/styles/createMotion.d.mts +8 -0
  154. package/styles/createMotion.d.ts +8 -0
  155. package/styles/createMotion.js +13 -0
  156. package/styles/createMotion.mjs +7 -0
  157. package/styles/createThemeFoundation.d.mts +2 -0
  158. package/styles/createThemeFoundation.d.ts +2 -0
  159. package/styles/createThemeNoVars.d.mts +3 -0
  160. package/styles/createThemeNoVars.d.ts +3 -0
  161. package/styles/createThemeNoVars.js +5 -0
  162. package/styles/createThemeNoVars.mjs +5 -0
  163. package/styles/createTransitions.d.mts +6 -2
  164. package/styles/createTransitions.d.ts +6 -2
  165. package/styles/createTransitions.js +12 -4
  166. package/styles/createTransitions.mjs +12 -4
  167. package/styles/enhanceHighContrast.d.mts +70 -0
  168. package/styles/enhanceHighContrast.d.ts +70 -0
  169. package/styles/enhanceHighContrast.js +502 -0
  170. package/styles/enhanceHighContrast.mjs +495 -0
  171. package/styles/index.d.mts +2 -0
  172. package/styles/index.d.ts +2 -0
  173. package/styles/index.js +8 -0
  174. package/styles/index.mjs +1 -0
  175. package/styles/reducedMotion.d.mts +7 -0
  176. package/styles/reducedMotion.d.ts +7 -0
  177. package/styles/reducedMotion.js +21 -0
  178. package/styles/reducedMotion.mjs +14 -0
  179. package/styles/shouldSkipGeneratingVar.js +1 -1
  180. package/styles/shouldSkipGeneratingVar.mjs +1 -1
  181. package/styles/stringifyTheme.js +1 -0
  182. package/styles/stringifyTheme.mjs +1 -0
  183. package/transitions/index.d.mts +1 -1
  184. package/transitions/index.d.ts +1 -1
  185. package/transitions/index.js +0 -11
  186. package/transitions/index.mjs +1 -1
  187. package/transitions/transition.d.mts +1 -12
  188. package/transitions/transition.d.ts +1 -12
  189. package/transitions/types.d.mts +73 -0
  190. package/transitions/types.d.ts +73 -0
  191. package/transitions/useReducedMotion.d.mts +14 -0
  192. package/transitions/useReducedMotion.d.ts +14 -0
  193. package/transitions/useReducedMotion.js +117 -0
  194. package/transitions/useReducedMotion.mjs +110 -0
  195. package/transitions/utils.d.mts +34 -2
  196. package/transitions/utils.d.ts +34 -2
  197. package/transitions/utils.js +33 -4
  198. package/transitions/utils.mjs +31 -4
  199. package/useAutocomplete/useAutocomplete.d.mts +8 -1
  200. package/useAutocomplete/useAutocomplete.d.ts +8 -1
  201. package/useAutocomplete/useAutocomplete.js +66 -4
  202. package/useAutocomplete/useAutocomplete.mjs +66 -4
  203. package/version/index.js +3 -3
  204. package/version/index.mjs +3 -3
  205. /package/transitions/{transition.js → types.js} +0 -0
  206. /package/transitions/{transition.mjs → types.mjs} +0 -0
@@ -1,8 +1,12 @@
1
+ import { defaultStyles, resolveReducedMotionStyles } from "../styles/reducedMotion.mjs";
1
2
  export const reflow = node => node.scrollTop;
2
3
  const DEFAULT_TRANSLATE_OFFSET = {
3
4
  offsetX: 0,
4
5
  offsetY: 0
5
6
  };
7
+ const EMPTY_STYLE = {};
8
+ const DEFAULT_TRANSITION_PROPS = ['all'];
9
+ const EMPTY_OPTIONS = {};
6
10
  const transformOffsetIndexes = {
7
11
  matrix: [4, 5],
8
12
  matrix3d: [12, 13],
@@ -66,7 +70,7 @@ export function normalizedTransitionCallback(nodeRef, callback) {
66
70
  return maybeIsAppearing => {
67
71
  if (callback) {
68
72
  const node = nodeRef.current;
69
- // onEnterXxx and onExitXxx callbacks have a different arguments.length value.
73
+ // Enter callbacks receive isAppearing; exit callbacks do not.
70
74
  if (maybeIsAppearing === undefined) {
71
75
  callback(node);
72
76
  } else {
@@ -76,8 +80,8 @@ export function normalizedTransitionCallback(nodeRef, callback) {
76
80
  };
77
81
  }
78
82
  /**
79
- * Computes the child style for a transition component, reusing existing
80
- * references when possible to preserve referential equality for React.memo.
83
+ * Return the child style for a transition. Reuse predefined style objects when
84
+ * no custom styles are present so memoized children see the same object.
81
85
  */
82
86
  export function getTransitionChildStyle(state, inProp, baseStyles, hiddenStyles, styleProp, childStyle) {
83
87
  const base = state === 'exited' && !inProp ? hiddenStyles : baseStyles[state] || baseStyles.exited;
@@ -91,11 +95,34 @@ export function getTransitionProps(props, options) {
91
95
  const {
92
96
  timeout,
93
97
  easing,
94
- style = {}
98
+ style = EMPTY_STYLE
95
99
  } = props;
96
100
  return {
97
101
  duration: style.transitionDuration ?? (typeof timeout === 'number' ? timeout : timeout[options.mode] || 0),
98
102
  easing: style.transitionTimingFunction ?? (typeof easing === 'object' ? easing[options.mode] : easing),
99
103
  delay: style.transitionDelay
100
104
  };
105
+ }
106
+
107
+ /**
108
+ * Returns CSS that disables component-owned transitions when reduced motion is active.
109
+ * Pass custom styles only when the default `transition: none` reset is not enough.
110
+ */
111
+ export function getReducedMotionStyles(theme, styles) {
112
+ const resolvedStyles = styles ?? defaultStyles;
113
+ return resolveReducedMotionStyles(theme.motion?.reducedMotion, resolvedStyles);
114
+ }
115
+ export function getTransitionStyles(theme, props = DEFAULT_TRANSITION_PROPS, options = EMPTY_OPTIONS) {
116
+ const transition = theme.transitions?.create?.(props, options);
117
+ const reducedMotionStyles = getReducedMotionStyles(theme);
118
+ if (transition === undefined) {
119
+ return reducedMotionStyles ?? EMPTY_STYLE;
120
+ }
121
+ const transitionStyles = {
122
+ transition
123
+ };
124
+ return reducedMotionStyles ? {
125
+ ...transitionStyles,
126
+ ...reducedMotionStyles
127
+ } : transitionStyles;
101
128
  }
@@ -260,6 +260,12 @@ export interface UseAutocompleteProps<Value, Multiple extends boolean | undefine
260
260
  * @default false
261
261
  */
262
262
  readOnly?: boolean | undefined;
263
+ /**
264
+ * If `true`, clears an option highlighted by mouse movement when the mouse leaves the listbox.
265
+ * This behavior will be enabled by default in the next major version.
266
+ * @default false
267
+ */
268
+ resetHighlightOnMouseLeave?: boolean | undefined;
263
269
  /**
264
270
  * If `true`, the input's text is selected on focus.
265
271
  * It helps the user clear the selected value.
@@ -347,9 +353,10 @@ export interface UseAutocompleteReturnValue<Value, Multiple extends boolean | un
347
353
  getPopupIndicatorProps: () => React.HTMLAttributes<HTMLButtonElement>;
348
354
  /**
349
355
  * Resolver for the listbox component's props.
356
+ * @param externalProps props for the listbox component
350
357
  * @returns props that should be spread on the listbox component
351
358
  */
352
- getListboxProps: () => React.HTMLAttributes<HTMLUListElement>;
359
+ getListboxProps: (externalProps?: any) => React.HTMLAttributes<HTMLUListElement>;
353
360
  /**
354
361
  * Resolver for the rendered option element's props.
355
362
  * @param renderedOption option rendered on the Autocomplete
@@ -260,6 +260,12 @@ export interface UseAutocompleteProps<Value, Multiple extends boolean | undefine
260
260
  * @default false
261
261
  */
262
262
  readOnly?: boolean | undefined;
263
+ /**
264
+ * If `true`, clears an option highlighted by mouse movement when the mouse leaves the listbox.
265
+ * This behavior will be enabled by default in the next major version.
266
+ * @default false
267
+ */
268
+ resetHighlightOnMouseLeave?: boolean | undefined;
263
269
  /**
264
270
  * If `true`, the input's text is selected on focus.
265
271
  * It helps the user clear the selected value.
@@ -347,9 +353,10 @@ export interface UseAutocompleteReturnValue<Value, Multiple extends boolean | un
347
353
  getPopupIndicatorProps: () => React.HTMLAttributes<HTMLButtonElement>;
348
354
  /**
349
355
  * Resolver for the listbox component's props.
356
+ * @param externalProps props for the listbox component
350
357
  * @returns props that should be spread on the listbox component
351
358
  */
352
- getListboxProps: () => React.HTMLAttributes<HTMLUListElement>;
359
+ getListboxProps: (externalProps?: any) => React.HTMLAttributes<HTMLUListElement>;
353
360
  /**
354
361
  * Resolver for the rendered option element's props.
355
362
  * @param renderedOption option rendered on the Autocomplete
@@ -116,6 +116,7 @@ function useAutocomplete(props) {
116
116
  options,
117
117
  readOnly = false,
118
118
  renderValue,
119
+ resetHighlightOnMouseLeave = false,
119
120
  selectOnFocus = !props.freeSolo,
120
121
  value: valueProp
121
122
  } = props;
@@ -136,6 +137,9 @@ function useAutocomplete(props) {
136
137
  const firstFocus = React.useRef(true);
137
138
  const inputRef = React.useRef(null);
138
139
  const listboxRef = React.useRef(null);
140
+ // VoiceOver synthesises a spurious Backspace on the input after a chip
141
+ // deletion moves DOM focus back to it. This flag suppresses that one event.
142
+ const ignoreNextBackspaceRef = React.useRef(false);
139
143
  const windowLostFocus = React.useRef(false);
140
144
  const [anchorEl, setAnchorEl] = React.useState(null);
141
145
  const [focusedItem, setFocusedItem] = React.useState(-1);
@@ -243,8 +247,10 @@ function useAutocomplete(props) {
243
247
  return;
244
248
  }
245
249
 
246
- // Only reset the input's value when freeSolo if the component's value changes.
247
- if (freeSolo && !valueChange) {
250
+ // In freeSolo mode, only reset the input after a real value change.
251
+ // Also prevent the initial default value of `null` from clearing controlled values.
252
+ const shouldSkipFreeSoloReset = freeSolo && (!valueChange || value == null && previousProps.value === undefined);
253
+ if (shouldSkipFreeSoloReset) {
248
254
  return;
249
255
  }
250
256
  resetInputValue(null, value, 'reset');
@@ -300,6 +306,12 @@ function useAutocomplete(props) {
300
306
  reason,
301
307
  preserveScroll = false
302
308
  }) => {
309
+ // React can clear refs before pending passive effects run during unmount.
310
+ // If both refs are gone, there is no DOM left to sync.
311
+ if (inputRef.current == null && listboxRef.current == null) {
312
+ return;
313
+ }
314
+
303
315
  // does the index exist?
304
316
  if (index === -1) {
305
317
  inputRef.current.removeAttribute('aria-activedescendant');
@@ -898,6 +910,10 @@ function useAutocomplete(props) {
898
910
  break;
899
911
  case 'Backspace':
900
912
  // Remove the value on the left of the "cursor"
913
+ if (ignoreNextBackspaceRef.current) {
914
+ ignoreNextBackspaceRef.current = false;
915
+ break;
916
+ }
901
917
  if (multiple && !readOnly && inputValue === '' && value.length > 0) {
902
918
  const index = focusedItem === -1 ? value.length - 1 : focusedItem;
903
919
  const newValue = value.slice();
@@ -905,6 +921,18 @@ function useAutocomplete(props) {
905
921
  handleValue(event, newValue, 'removeOption', {
906
922
  option: value[index]
907
923
  });
924
+ if (focusedItem !== -1) {
925
+ // Suppress the spurious Backspace VoiceOver synthesises on the
926
+ // input after focus returns to it. Clear it shortly after
927
+ // deletion so a later real Backspace is not ignored if the
928
+ // synthetic follow-up event never arrives.
929
+ ignoreNextBackspaceRef.current = true;
930
+ setTimeout(() => {
931
+ if (ignoreNextBackspaceRef.current) {
932
+ ignoreNextBackspaceRef.current = false;
933
+ }
934
+ }, 0);
935
+ }
908
936
  }
909
937
  if (!multiple && renderValue && !readOnly && inputValue === '') {
910
938
  handleValue(event, null, 'removeOption', {
@@ -1030,6 +1058,17 @@ function useAutocomplete(props) {
1030
1058
  touchScrolledRef.current = false;
1031
1059
  }
1032
1060
  };
1061
+ const handleListboxMouseLeave = event => {
1062
+ if (!resetHighlightOnMouseLeave || highlightedIndexRef.current === -1 || highlightReasonRef.current !== 'mouse' || isTouchRef.current) {
1063
+ return;
1064
+ }
1065
+ setHighlightedIndex({
1066
+ event,
1067
+ index: -1,
1068
+ reason: 'mouse',
1069
+ preserveScroll: true
1070
+ });
1071
+ };
1033
1072
  const handleOptionTouchStart = event => {
1034
1073
  touchScrolledRef.current = false;
1035
1074
  setHighlightedIndex({
@@ -1183,6 +1222,12 @@ function useAutocomplete(props) {
1183
1222
  }),
1184
1223
  'data-item-index': index,
1185
1224
  tabIndex: -1,
1225
+ onFocus: () => {
1226
+ // If focus on the item doesn't come from keyboard events, we update the state via onFocus.
1227
+ if (focusedItem !== index) {
1228
+ setFocusedItem(index);
1229
+ }
1230
+ },
1186
1231
  ...(!readOnly && {
1187
1232
  onDelete: multiple ? handleItemDelete(index) : handleSingleItemDelete
1188
1233
  })
@@ -1192,20 +1237,37 @@ function useAutocomplete(props) {
1192
1237
  type: 'button',
1193
1238
  onClick: handlePopupIndicator
1194
1239
  }),
1195
- getListboxProps: () => ({
1240
+ getListboxProps: (other = {}) => ({
1241
+ ...other,
1196
1242
  role: 'listbox',
1197
1243
  id: `${id}-listbox`,
1198
1244
  'aria-labelledby': `${id}-label`,
1199
1245
  'aria-multiselectable': multiple || undefined,
1200
1246
  ref: handleListboxRef,
1201
1247
  onMouseDown: event => {
1248
+ other.onMouseDown?.(event);
1249
+ if (event.defaultMuiPrevented) {
1250
+ return;
1251
+ }
1252
+
1202
1253
  // Prevent blur
1203
1254
  event.preventDefault();
1204
1255
  },
1205
- onScroll: () => {
1256
+ onScroll: event => {
1257
+ other.onScroll?.(event);
1258
+ if (event.defaultMuiPrevented) {
1259
+ return;
1260
+ }
1206
1261
  if (isTouchRef.current) {
1207
1262
  touchScrolledRef.current = true;
1208
1263
  }
1264
+ },
1265
+ onMouseLeave: event => {
1266
+ other.onMouseLeave?.(event);
1267
+ if (event.defaultMuiPrevented) {
1268
+ return;
1269
+ }
1270
+ handleListboxMouseLeave(event);
1209
1271
  }
1210
1272
  }),
1211
1273
  getOptionProps: ({
@@ -108,6 +108,7 @@ function useAutocomplete(props) {
108
108
  options,
109
109
  readOnly = false,
110
110
  renderValue,
111
+ resetHighlightOnMouseLeave = false,
111
112
  selectOnFocus = !props.freeSolo,
112
113
  value: valueProp
113
114
  } = props;
@@ -128,6 +129,9 @@ function useAutocomplete(props) {
128
129
  const firstFocus = React.useRef(true);
129
130
  const inputRef = React.useRef(null);
130
131
  const listboxRef = React.useRef(null);
132
+ // VoiceOver synthesises a spurious Backspace on the input after a chip
133
+ // deletion moves DOM focus back to it. This flag suppresses that one event.
134
+ const ignoreNextBackspaceRef = React.useRef(false);
131
135
  const windowLostFocus = React.useRef(false);
132
136
  const [anchorEl, setAnchorEl] = React.useState(null);
133
137
  const [focusedItem, setFocusedItem] = React.useState(-1);
@@ -235,8 +239,10 @@ function useAutocomplete(props) {
235
239
  return;
236
240
  }
237
241
 
238
- // Only reset the input's value when freeSolo if the component's value changes.
239
- if (freeSolo && !valueChange) {
242
+ // In freeSolo mode, only reset the input after a real value change.
243
+ // Also prevent the initial default value of `null` from clearing controlled values.
244
+ const shouldSkipFreeSoloReset = freeSolo && (!valueChange || value == null && previousProps.value === undefined);
245
+ if (shouldSkipFreeSoloReset) {
240
246
  return;
241
247
  }
242
248
  resetInputValue(null, value, 'reset');
@@ -292,6 +298,12 @@ function useAutocomplete(props) {
292
298
  reason,
293
299
  preserveScroll = false
294
300
  }) => {
301
+ // React can clear refs before pending passive effects run during unmount.
302
+ // If both refs are gone, there is no DOM left to sync.
303
+ if (inputRef.current == null && listboxRef.current == null) {
304
+ return;
305
+ }
306
+
295
307
  // does the index exist?
296
308
  if (index === -1) {
297
309
  inputRef.current.removeAttribute('aria-activedescendant');
@@ -890,6 +902,10 @@ function useAutocomplete(props) {
890
902
  break;
891
903
  case 'Backspace':
892
904
  // Remove the value on the left of the "cursor"
905
+ if (ignoreNextBackspaceRef.current) {
906
+ ignoreNextBackspaceRef.current = false;
907
+ break;
908
+ }
893
909
  if (multiple && !readOnly && inputValue === '' && value.length > 0) {
894
910
  const index = focusedItem === -1 ? value.length - 1 : focusedItem;
895
911
  const newValue = value.slice();
@@ -897,6 +913,18 @@ function useAutocomplete(props) {
897
913
  handleValue(event, newValue, 'removeOption', {
898
914
  option: value[index]
899
915
  });
916
+ if (focusedItem !== -1) {
917
+ // Suppress the spurious Backspace VoiceOver synthesises on the
918
+ // input after focus returns to it. Clear it shortly after
919
+ // deletion so a later real Backspace is not ignored if the
920
+ // synthetic follow-up event never arrives.
921
+ ignoreNextBackspaceRef.current = true;
922
+ setTimeout(() => {
923
+ if (ignoreNextBackspaceRef.current) {
924
+ ignoreNextBackspaceRef.current = false;
925
+ }
926
+ }, 0);
927
+ }
900
928
  }
901
929
  if (!multiple && renderValue && !readOnly && inputValue === '') {
902
930
  handleValue(event, null, 'removeOption', {
@@ -1022,6 +1050,17 @@ function useAutocomplete(props) {
1022
1050
  touchScrolledRef.current = false;
1023
1051
  }
1024
1052
  };
1053
+ const handleListboxMouseLeave = event => {
1054
+ if (!resetHighlightOnMouseLeave || highlightedIndexRef.current === -1 || highlightReasonRef.current !== 'mouse' || isTouchRef.current) {
1055
+ return;
1056
+ }
1057
+ setHighlightedIndex({
1058
+ event,
1059
+ index: -1,
1060
+ reason: 'mouse',
1061
+ preserveScroll: true
1062
+ });
1063
+ };
1025
1064
  const handleOptionTouchStart = event => {
1026
1065
  touchScrolledRef.current = false;
1027
1066
  setHighlightedIndex({
@@ -1175,6 +1214,12 @@ function useAutocomplete(props) {
1175
1214
  }),
1176
1215
  'data-item-index': index,
1177
1216
  tabIndex: -1,
1217
+ onFocus: () => {
1218
+ // If focus on the item doesn't come from keyboard events, we update the state via onFocus.
1219
+ if (focusedItem !== index) {
1220
+ setFocusedItem(index);
1221
+ }
1222
+ },
1178
1223
  ...(!readOnly && {
1179
1224
  onDelete: multiple ? handleItemDelete(index) : handleSingleItemDelete
1180
1225
  })
@@ -1184,20 +1229,37 @@ function useAutocomplete(props) {
1184
1229
  type: 'button',
1185
1230
  onClick: handlePopupIndicator
1186
1231
  }),
1187
- getListboxProps: () => ({
1232
+ getListboxProps: (other = {}) => ({
1233
+ ...other,
1188
1234
  role: 'listbox',
1189
1235
  id: `${id}-listbox`,
1190
1236
  'aria-labelledby': `${id}-label`,
1191
1237
  'aria-multiselectable': multiple || undefined,
1192
1238
  ref: handleListboxRef,
1193
1239
  onMouseDown: event => {
1240
+ other.onMouseDown?.(event);
1241
+ if (event.defaultMuiPrevented) {
1242
+ return;
1243
+ }
1244
+
1194
1245
  // Prevent blur
1195
1246
  event.preventDefault();
1196
1247
  },
1197
- onScroll: () => {
1248
+ onScroll: event => {
1249
+ other.onScroll?.(event);
1250
+ if (event.defaultMuiPrevented) {
1251
+ return;
1252
+ }
1198
1253
  if (isTouchRef.current) {
1199
1254
  touchScrolledRef.current = true;
1200
1255
  }
1256
+ },
1257
+ onMouseLeave: event => {
1258
+ other.onMouseLeave?.(event);
1259
+ if (event.defaultMuiPrevented) {
1260
+ return;
1261
+ }
1262
+ handleListboxMouseLeave(event);
1201
1263
  }
1202
1264
  }),
1203
1265
  getOptionProps: ({
package/version/index.js CHANGED
@@ -4,9 +4,9 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.version = exports.prerelease = exports.patch = exports.minor = exports.major = exports.default = void 0;
7
- const version = exports.version = "9.0.1";
7
+ const version = exports.version = "9.1.0";
8
8
  const major = exports.major = Number("9");
9
- const minor = exports.minor = Number("0");
10
- const patch = exports.patch = Number("1");
9
+ const minor = exports.minor = Number("1");
10
+ const patch = exports.patch = Number("0");
11
11
  const prerelease = exports.prerelease = undefined;
12
12
  var _default = exports.default = version;
package/version/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- export const version = "9.0.1";
1
+ export const version = "9.1.0";
2
2
  export const major = Number("9");
3
- export const minor = Number("0");
4
- export const patch = Number("1");
3
+ export const minor = Number("1");
4
+ export const patch = Number("0");
5
5
  export const prerelease = undefined;
6
6
  export default version;
File without changes
File without changes