@mui/material 9.0.0 → 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 (278) 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 +73 -17
  8. package/Autocomplete/Autocomplete.mjs +73 -17
  9. package/Avatar/Avatar.js +4 -0
  10. package/Avatar/Avatar.mjs +4 -0
  11. package/Backdrop/Backdrop.d.mts +2 -2
  12. package/Backdrop/Backdrop.d.ts +2 -2
  13. package/Badge/Badge.js +31 -24
  14. package/Badge/Badge.mjs +31 -24
  15. package/BottomNavigationAction/BottomNavigationAction.js +6 -2
  16. package/BottomNavigationAction/BottomNavigationAction.mjs +6 -2
  17. package/Button/Button.js +19 -6
  18. package/Button/Button.mjs +19 -6
  19. package/ButtonBase/ButtonBase.d.mts +7 -0
  20. package/ButtonBase/ButtonBase.d.ts +7 -0
  21. package/ButtonBase/ButtonBase.js +5 -2
  22. package/ButtonBase/ButtonBase.mjs +5 -2
  23. package/ButtonBase/Ripple.js +21 -11
  24. package/ButtonBase/Ripple.mjs +21 -11
  25. package/ButtonBase/TouchRipple.js +252 -116
  26. package/ButtonBase/TouchRipple.mjs +253 -117
  27. package/CHANGELOG.md +216 -1245
  28. package/CardActionArea/CardActionArea.js +2 -1
  29. package/CardActionArea/CardActionArea.mjs +2 -1
  30. package/Checkbox/Checkbox.js +2 -1
  31. package/Checkbox/Checkbox.mjs +2 -1
  32. package/Chip/Chip.js +2 -1
  33. package/Chip/Chip.mjs +2 -1
  34. package/CircularProgress/CircularProgress.d.mts +12 -2
  35. package/CircularProgress/CircularProgress.d.ts +12 -2
  36. package/CircularProgress/CircularProgress.js +115 -58
  37. package/CircularProgress/CircularProgress.mjs +114 -58
  38. package/ClickAwayListener/ClickAwayListener.js +3 -6
  39. package/ClickAwayListener/ClickAwayListener.mjs +3 -6
  40. package/Collapse/Collapse.d.mts +15 -3
  41. package/Collapse/Collapse.d.ts +15 -3
  42. package/Collapse/Collapse.js +44 -31
  43. package/Collapse/Collapse.mjs +43 -30
  44. package/Dialog/Dialog.d.mts +2 -2
  45. package/Dialog/Dialog.d.ts +2 -2
  46. package/Dialog/Dialog.js +13 -6
  47. package/Dialog/Dialog.mjs +13 -6
  48. package/Drawer/Drawer.d.mts +2 -2
  49. package/Drawer/Drawer.d.ts +2 -2
  50. package/Drawer/Drawer.js +18 -4
  51. package/Drawer/Drawer.mjs +18 -4
  52. package/Fab/Fab.js +9 -2
  53. package/Fab/Fab.mjs +9 -2
  54. package/Fade/Fade.d.mts +15 -2
  55. package/Fade/Fade.d.ts +15 -2
  56. package/Fade/Fade.js +46 -19
  57. package/Fade/Fade.mjs +45 -18
  58. package/FilledInput/FilledInput.d.mts +4 -0
  59. package/FilledInput/FilledInput.d.ts +4 -0
  60. package/FilledInput/FilledInput.js +22 -23
  61. package/FilledInput/FilledInput.mjs +22 -23
  62. package/FormControl/useFormControl.d.mts +12 -2
  63. package/FormControl/useFormControl.d.ts +12 -2
  64. package/FormControl/useFormControl.js +13 -0
  65. package/FormControl/useFormControl.mjs +12 -0
  66. package/FormControlLabel/FormControlLabel.js +5 -8
  67. package/FormControlLabel/FormControlLabel.mjs +5 -8
  68. package/FormGroup/FormGroup.js +2 -5
  69. package/FormGroup/FormGroup.mjs +2 -5
  70. package/FormHelperText/FormHelperText.js +2 -5
  71. package/FormHelperText/FormHelperText.mjs +2 -5
  72. package/FormLabel/FormLabel.js +2 -5
  73. package/FormLabel/FormLabel.mjs +2 -5
  74. package/Grow/Grow.d.mts +15 -2
  75. package/Grow/Grow.d.ts +15 -2
  76. package/Grow/Grow.js +45 -28
  77. package/Grow/Grow.mjs +44 -27
  78. package/IconButton/IconButton.js +3 -9
  79. package/IconButton/IconButton.mjs +3 -9
  80. package/Input/Input.d.mts +4 -0
  81. package/Input/Input.d.ts +4 -0
  82. package/Input/Input.js +9 -2
  83. package/Input/Input.mjs +9 -2
  84. package/InputBase/InputBase.d.mts +2 -1
  85. package/InputBase/InputBase.d.ts +2 -1
  86. package/InputBase/InputBase.js +52 -16
  87. package/InputBase/InputBase.mjs +52 -16
  88. package/InputLabel/InputLabel.js +7 -9
  89. package/InputLabel/InputLabel.mjs +7 -9
  90. package/LICENSE +1 -1
  91. package/LinearProgress/LinearProgress.d.mts +12 -2
  92. package/LinearProgress/LinearProgress.d.ts +12 -2
  93. package/LinearProgress/LinearProgress.js +225 -126
  94. package/LinearProgress/LinearProgress.mjs +224 -126
  95. package/List/List.js +2 -1
  96. package/List/List.mjs +2 -1
  97. package/ListItem/ListItem.js +2 -1
  98. package/ListItem/ListItem.mjs +2 -1
  99. package/ListItemButton/ListItemButton.js +9 -2
  100. package/ListItemButton/ListItemButton.mjs +9 -2
  101. package/Menu/Menu.d.mts +1 -1
  102. package/Menu/Menu.d.ts +1 -1
  103. package/MenuItem/MenuItem.js +7 -1
  104. package/MenuItem/MenuItem.mjs +7 -1
  105. package/MenuList/MenuList.js +2 -1
  106. package/MenuList/MenuList.mjs +2 -1
  107. package/MobileStepper/MobileStepper.js +2 -1
  108. package/MobileStepper/MobileStepper.mjs +2 -1
  109. package/NativeSelect/NativeSelect.js +2 -5
  110. package/NativeSelect/NativeSelect.mjs +2 -5
  111. package/OutlinedInput/NotchedOutline.js +4 -3
  112. package/OutlinedInput/NotchedOutline.mjs +4 -3
  113. package/OutlinedInput/OutlinedInput.js +13 -23
  114. package/OutlinedInput/OutlinedInput.mjs +13 -23
  115. package/PaginationItem/PaginationItem.js +2 -1
  116. package/PaginationItem/PaginationItem.mjs +2 -1
  117. package/Paper/Paper.js +2 -1
  118. package/Paper/Paper.mjs +2 -1
  119. package/PigmentContainer/PigmentContainer.js +0 -1
  120. package/PigmentContainer/PigmentContainer.mjs +0 -1
  121. package/Popover/Popover.d.mts +1 -1
  122. package/Popover/Popover.d.ts +1 -1
  123. package/Popper/BasePopper.js +23 -1
  124. package/Popper/BasePopper.mjs +23 -1
  125. package/README.md +3 -2
  126. package/Radio/RadioButtonIcon.js +3 -2
  127. package/Radio/RadioButtonIcon.mjs +3 -2
  128. package/Rating/Rating.js +2 -1
  129. package/Rating/Rating.mjs +2 -1
  130. package/Select/Select.js +2 -5
  131. package/Select/Select.mjs +2 -5
  132. package/Select/SelectInput.js +276 -24
  133. package/Select/SelectInput.mjs +276 -24
  134. package/Select/utils/closedTypeahead.js +73 -0
  135. package/Select/utils/closedTypeahead.mjs +63 -0
  136. package/Skeleton/Skeleton.js +22 -2
  137. package/Skeleton/Skeleton.mjs +22 -2
  138. package/Slide/Slide.d.mts +15 -2
  139. package/Slide/Slide.d.ts +15 -2
  140. package/Slide/Slide.js +97 -47
  141. package/Slide/Slide.mjs +97 -47
  142. package/Slider/Slider.js +14 -4
  143. package/Slider/Slider.mjs +14 -4
  144. package/Slider/useSlider.js +4 -3
  145. package/Slider/useSlider.mjs +4 -3
  146. package/Snackbar/Snackbar.d.mts +2 -2
  147. package/Snackbar/Snackbar.d.ts +2 -2
  148. package/SpeedDial/SpeedDial.d.mts +1 -1
  149. package/SpeedDial/SpeedDial.d.ts +1 -1
  150. package/SpeedDial/SpeedDial.js +6 -2
  151. package/SpeedDial/SpeedDial.mjs +6 -2
  152. package/SpeedDialAction/SpeedDialAction.js +11 -2
  153. package/SpeedDialAction/SpeedDialAction.mjs +12 -3
  154. package/SpeedDialIcon/SpeedDialIcon.js +40 -37
  155. package/SpeedDialIcon/SpeedDialIcon.mjs +40 -37
  156. package/Step/Step.js +47 -15
  157. package/Step/Step.mjs +47 -15
  158. package/StepButton/StepButton.js +9 -3
  159. package/StepButton/StepButton.mjs +9 -3
  160. package/StepConnector/StepConnector.js +10 -0
  161. package/StepConnector/StepConnector.mjs +10 -0
  162. package/StepContent/StepContent.d.mts +2 -2
  163. package/StepContent/StepContent.d.ts +2 -2
  164. package/StepContent/StepContent.js +26 -2
  165. package/StepContent/StepContent.mjs +26 -2
  166. package/StepIcon/StepIcon.js +2 -1
  167. package/StepIcon/StepIcon.mjs +2 -1
  168. package/StepLabel/StepLabel.js +52 -7
  169. package/StepLabel/StepLabel.mjs +52 -7
  170. package/Stepper/Stepper.d.mts +2 -0
  171. package/Stepper/Stepper.d.ts +2 -0
  172. package/Stepper/Stepper.js +18 -0
  173. package/Stepper/Stepper.mjs +18 -0
  174. package/SvgIcon/SvgIcon.js +2 -1
  175. package/SvgIcon/SvgIcon.mjs +2 -1
  176. package/SwipeableDrawer/SwipeableDrawer.js +21 -6
  177. package/SwipeableDrawer/SwipeableDrawer.mjs +21 -6
  178. package/Switch/Switch.js +10 -8
  179. package/Switch/Switch.mjs +10 -8
  180. package/TableSortLabel/TableSortLabel.js +2 -1
  181. package/TableSortLabel/TableSortLabel.mjs +2 -1
  182. package/Tabs/ScrollbarSize.js +2 -1
  183. package/Tabs/ScrollbarSize.mjs +2 -1
  184. package/Tabs/Tabs.js +16 -4
  185. package/Tabs/Tabs.mjs +16 -4
  186. package/Tooltip/Tooltip.d.mts +2 -2
  187. package/Tooltip/Tooltip.d.ts +2 -2
  188. package/Tooltip/Tooltip.js +29 -108
  189. package/Tooltip/Tooltip.mjs +29 -108
  190. package/Unstable_TrapFocus/FocusTrap.js +60 -22
  191. package/Unstable_TrapFocus/FocusTrap.mjs +60 -22
  192. package/Zoom/Zoom.d.mts +15 -2
  193. package/Zoom/Zoom.d.ts +15 -2
  194. package/Zoom/Zoom.js +43 -16
  195. package/Zoom/Zoom.mjs +42 -15
  196. package/index.js +1 -1
  197. package/index.mjs +1 -1
  198. package/internal/Transition.d.mts +34 -0
  199. package/internal/Transition.d.ts +34 -0
  200. package/internal/Transition.js +444 -0
  201. package/internal/Transition.mjs +436 -0
  202. package/internal/react-transition-group.d.mts +8 -0
  203. package/internal/react-transition-group.d.ts +8 -0
  204. package/package.json +50 -50
  205. package/styles/createMotion.d.mts +8 -0
  206. package/styles/createMotion.d.ts +8 -0
  207. package/styles/createMotion.js +13 -0
  208. package/styles/createMotion.mjs +7 -0
  209. package/styles/createThemeFoundation.d.mts +2 -0
  210. package/styles/createThemeFoundation.d.ts +2 -0
  211. package/styles/createThemeNoVars.d.mts +3 -0
  212. package/styles/createThemeNoVars.d.ts +3 -0
  213. package/styles/createThemeNoVars.js +5 -0
  214. package/styles/createThemeNoVars.mjs +5 -0
  215. package/styles/createTransitions.d.mts +6 -2
  216. package/styles/createTransitions.d.ts +6 -2
  217. package/styles/createTransitions.js +12 -4
  218. package/styles/createTransitions.mjs +12 -4
  219. package/styles/enhanceHighContrast.d.mts +70 -0
  220. package/styles/enhanceHighContrast.d.ts +70 -0
  221. package/styles/enhanceHighContrast.js +502 -0
  222. package/styles/enhanceHighContrast.mjs +495 -0
  223. package/styles/index.d.mts +2 -0
  224. package/styles/index.d.ts +2 -0
  225. package/styles/index.js +8 -0
  226. package/styles/index.mjs +1 -0
  227. package/styles/reducedMotion.d.mts +7 -0
  228. package/styles/reducedMotion.d.ts +7 -0
  229. package/styles/reducedMotion.js +21 -0
  230. package/styles/reducedMotion.mjs +14 -0
  231. package/styles/responsiveFontSizes.js +19 -8
  232. package/styles/responsiveFontSizes.mjs +19 -8
  233. package/styles/shouldSkipGeneratingVar.js +1 -1
  234. package/styles/shouldSkipGeneratingVar.mjs +1 -1
  235. package/styles/stringifyTheme.js +1 -0
  236. package/styles/stringifyTheme.mjs +1 -0
  237. package/styles/useThemeProps.d.mts +3 -3
  238. package/styles/useThemeProps.d.ts +3 -3
  239. package/transitions/index.d.mts +1 -1
  240. package/transitions/index.d.ts +1 -1
  241. package/transitions/index.js +0 -11
  242. package/transitions/index.mjs +1 -1
  243. package/transitions/transition.d.mts +1 -12
  244. package/transitions/transition.d.ts +1 -12
  245. package/transitions/types.d.mts +73 -0
  246. package/transitions/types.d.ts +73 -0
  247. package/transitions/useReducedMotion.d.mts +14 -0
  248. package/transitions/useReducedMotion.d.ts +14 -0
  249. package/transitions/useReducedMotion.js +117 -0
  250. package/transitions/useReducedMotion.mjs +110 -0
  251. package/transitions/utils.d.mts +51 -2
  252. package/transitions/utils.d.ts +51 -2
  253. package/transitions/utils.js +97 -4
  254. package/transitions/utils.mjs +94 -4
  255. package/useAutocomplete/useAutocomplete.d.mts +12 -6
  256. package/useAutocomplete/useAutocomplete.d.ts +12 -6
  257. package/useAutocomplete/useAutocomplete.js +230 -55
  258. package/useAutocomplete/useAutocomplete.mjs +230 -55
  259. package/utils/contains.d.mts +2 -0
  260. package/utils/contains.d.ts +2 -0
  261. package/utils/contains.js +9 -0
  262. package/utils/contains.mjs +2 -0
  263. package/utils/focusable.d.mts +7 -0
  264. package/utils/focusable.d.ts +7 -0
  265. package/utils/focusable.js +20 -0
  266. package/utils/focusable.mjs +13 -0
  267. package/utils/getEventTarget.d.mts +2 -0
  268. package/utils/getEventTarget.d.ts +2 -0
  269. package/utils/getEventTarget.js +9 -0
  270. package/utils/getEventTarget.mjs +2 -0
  271. package/utils/mergeSlotProps.js +2 -8
  272. package/utils/mergeSlotProps.mjs +1 -8
  273. package/version/index.js +2 -2
  274. package/version/index.mjs +2 -2
  275. package/FormControl/formControlState.js +0 -21
  276. package/FormControl/formControlState.mjs +0 -15
  277. /package/transitions/{transition.js → types.js} +0 -0
  278. /package/transitions/{transition.mjs → types.mjs} +0 -0
@@ -9,18 +9,58 @@ import clsx from 'clsx';
9
9
  import composeClasses from '@mui/utils/composeClasses';
10
10
  import useId from '@mui/utils/useId';
11
11
  import refType from '@mui/utils/refType';
12
+ import useTimeout from '@mui/utils/useTimeout';
12
13
  import ownerDocument from "../utils/ownerDocument.mjs";
13
14
  import Menu from "../Menu/Menu.mjs";
14
15
  import { StyledSelectSelect, StyledSelectIcon } from "../NativeSelect/NativeSelectInput.mjs";
15
16
  import { isFilled } from "../InputBase/utils.mjs";
16
17
  import { styled } from "../zero-styled/index.mjs";
17
18
  import slotShouldForwardProp from "../styles/slotShouldForwardProp.mjs";
19
+ import useEnhancedEffect from "../utils/useEnhancedEffect.mjs";
20
+ import useEventCallback from "../utils/useEventCallback.mjs";
18
21
  import useForkRef from "../utils/useForkRef.mjs";
19
22
  import useControlled from "../utils/useControlled.mjs";
20
23
  import selectClasses, { getSelectUtilityClasses } from "./selectClasses.mjs";
21
24
  import { areEqualValues, isEmpty, getOpenInteractionType } from "./utils/index.mjs";
25
+ import { canCycleRepeatedCharacter, getMatchingOptionIndex, getTypeaheadOptions } from "./utils/closedTypeahead.mjs";
22
26
  import { SelectFocusSourceProvider } from "./utils/SelectFocusSourceContext.mjs";
23
27
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
28
+ const OPENING_MOUSE_UP_BOUNDARY_OFFSET = 2;
29
+ // The initial mouseup may land on an item when the menu opens over the trigger.
30
+ const SELECTED_MOUSE_UP_DELAY = 400;
31
+ const UNSELECTED_MOUSE_UP_DELAY = 200;
32
+ const TYPEAHEAD_RESET_MS = 750;
33
+ const SPACE = ' ';
34
+ const ARROW_UP = 'ArrowUp';
35
+ const ARROW_DOWN = 'ArrowDown';
36
+ const ENTER = 'Enter';
37
+
38
+ /**
39
+ * Returns true when a native mouse event should be treated as happening inside
40
+ * the element, even if a portal or backdrop retargeted the event away from it.
41
+ *
42
+ * Select uses this for the opening mouseup: when the menu opens over the
43
+ * trigger, the release can target the backdrop or portaled menu even though the
44
+ * pointer is still inside the trigger or menu bounds.
45
+ */
46
+ function isMouseEventInsideElement(event, element) {
47
+ if (!element) {
48
+ return false;
49
+ }
50
+ const eventPath = event.composedPath();
51
+ if (eventPath.includes(element)) {
52
+ return true;
53
+ }
54
+ if (event.target?.nodeType && element.contains(event.target)) {
55
+ return true;
56
+ }
57
+ const rect = element.getBoundingClientRect();
58
+ if (rect.width === 0 && rect.height === 0) {
59
+ // Hidden or transition-mounted elements do not have useful bounds to hit-test.
60
+ return false;
61
+ }
62
+ return event.clientX >= rect.left - OPENING_MOUSE_UP_BOUNDARY_OFFSET && event.clientX <= rect.right + OPENING_MOUSE_UP_BOUNDARY_OFFSET && event.clientY >= rect.top - OPENING_MOUSE_UP_BOUNDARY_OFFSET && event.clientY <= rect.bottom + OPENING_MOUSE_UP_BOUNDARY_OFFSET;
63
+ }
24
64
  const SelectSelect = styled(StyledSelectSelect, {
25
65
  name: 'MuiSelect',
26
66
  slot: 'Select',
@@ -147,6 +187,23 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
147
187
  });
148
188
  const inputRef = React.useRef(null);
149
189
  const displayRef = React.useRef(null);
190
+ const paperRef = React.useRef(null);
191
+ const openRef = React.useRef(false);
192
+ const hasSelectedItemInListRef = React.useRef(false);
193
+ const openingMouseUpListenerCleanupRef = React.useRef(null);
194
+ const didPointerDownOnItemRef = React.useRef(false);
195
+ const selectionRef = React.useRef({
196
+ allowSelectedMouseUp: false,
197
+ allowUnselectedMouseUp: false
198
+ });
199
+ const closedTypeaheadRef = React.useRef({
200
+ buffer: '',
201
+ previousSearchIndex: null,
202
+ matchedIndex: null
203
+ });
204
+ const selectedMouseUpTimer = useTimeout();
205
+ const unselectedMouseUpTimer = useTimeout();
206
+ const typeaheadResetTimer = useTimeout();
150
207
  const [displayNode, setDisplayNode] = React.useState(null);
151
208
  const {
152
209
  current: isOpenControlled
@@ -169,6 +226,52 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
169
226
  value
170
227
  }), [value]);
171
228
  const open = displayNode !== null && openState;
229
+ const resetClosedTypeahead = React.useCallback(() => {
230
+ typeaheadResetTimer.clear();
231
+ closedTypeaheadRef.current.buffer = '';
232
+ closedTypeaheadRef.current.previousSearchIndex = null;
233
+ closedTypeaheadRef.current.matchedIndex = null;
234
+ }, [typeaheadResetTimer]);
235
+ useEnhancedEffect(() => {
236
+ openRef.current = open;
237
+ if (open) {
238
+ resetClosedTypeahead();
239
+ }
240
+ }, [open, resetClosedTypeahead]);
241
+ const clearSelectionTimers = React.useCallback(() => {
242
+ selectedMouseUpTimer.clear();
243
+ unselectedMouseUpTimer.clear();
244
+ }, [selectedMouseUpTimer, unselectedMouseUpTimer]);
245
+ const resetMouseUpSelection = React.useCallback(() => {
246
+ clearSelectionTimers();
247
+ didPointerDownOnItemRef.current = false;
248
+ selectionRef.current = {
249
+ allowSelectedMouseUp: false,
250
+ allowUnselectedMouseUp: false
251
+ };
252
+ }, [clearSelectionTimers]);
253
+ const clearOpeningMouseUpListener = React.useCallback(() => {
254
+ if (openingMouseUpListenerCleanupRef.current) {
255
+ openingMouseUpListenerCleanupRef.current();
256
+ openingMouseUpListenerCleanupRef.current = null;
257
+ }
258
+ }, []);
259
+ React.useEffect(() => {
260
+ if (!open) {
261
+ resetMouseUpSelection();
262
+ clearOpeningMouseUpListener();
263
+ }
264
+ }, [open, resetMouseUpSelection, clearOpeningMouseUpListener]);
265
+
266
+ // Keep unmount cleanup separate from the `open` effect. Effect cleanups also run
267
+ // before the next effect, which would clear the opening mouseup listener while opening.
268
+ React.useEffect(() => {
269
+ return () => {
270
+ resetMouseUpSelection();
271
+ clearOpeningMouseUpListener();
272
+ resetClosedTypeahead();
273
+ };
274
+ }, [resetMouseUpSelection, clearOpeningMouseUpListener, resetClosedTypeahead]);
172
275
  React.useEffect(() => {
173
276
  if (!open || !anchorElement || autoWidth) {
174
277
  return undefined;
@@ -219,8 +322,13 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
219
322
  }
220
323
  return undefined;
221
324
  }, [labelId]);
222
- const update = (openParam, event) => {
325
+ const update = useEventCallback((openParam, event) => {
326
+ if (!openParam) {
327
+ resetMouseUpSelection();
328
+ clearOpeningMouseUpListener();
329
+ }
223
330
  if (openParam) {
331
+ resetClosedTypeahead();
224
332
  setOpenInteractionType(getOpenInteractionType(event));
225
333
  if (onOpen) {
226
334
  onOpen(event);
@@ -232,9 +340,33 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
232
340
  }
233
341
  }
234
342
  if (!isOpenControlled) {
343
+ openRef.current = openParam;
235
344
  setMenuMinWidthState(autoWidth ? null : anchorElement.clientWidth);
236
345
  setOpenState(openParam);
237
346
  }
347
+ });
348
+ const scheduleMouseUpSelection = () => {
349
+ resetMouseUpSelection();
350
+
351
+ // When there is no selected item in the list, a mousedown
352
+ // on the trigger followed by a quick mouseup over the first option can accidentally select
353
+ // within 200ms. Delay unselected mouseup to match the safer 400ms window.
354
+ if (!hasSelectedItemInListRef.current) {
355
+ selectedMouseUpTimer.start(SELECTED_MOUSE_UP_DELAY, () => {
356
+ selectionRef.current.allowSelectedMouseUp = true;
357
+ selectionRef.current.allowUnselectedMouseUp = true;
358
+ });
359
+ } else {
360
+ // mousedown -> move to unselected item -> mouseup should not select within 200ms.
361
+ unselectedMouseUpTimer.start(UNSELECTED_MOUSE_UP_DELAY, () => {
362
+ selectionRef.current.allowUnselectedMouseUp = true;
363
+
364
+ // mousedown -> mouseup on selected item should not select within 400ms.
365
+ selectedMouseUpTimer.start(UNSELECTED_MOUSE_UP_DELAY, () => {
366
+ selectionRef.current.allowSelectedMouseUp = true;
367
+ });
368
+ });
369
+ }
238
370
  };
239
371
  const handleMouseDown = event => {
240
372
  onMouseDown?.(event);
@@ -245,6 +377,29 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
245
377
  // Hijack the default focus behavior.
246
378
  event.preventDefault();
247
379
  displayRef.current.focus();
380
+ const doc = ownerDocument(event.currentTarget);
381
+ scheduleMouseUpSelection();
382
+ clearOpeningMouseUpListener();
383
+ const handleMouseUp = mouseEvent => {
384
+ openingMouseUpListenerCleanupRef.current = null;
385
+ if (!displayRef.current) {
386
+ return;
387
+ }
388
+ if (isMouseEventInsideElement(mouseEvent, displayRef.current) || isMouseEventInsideElement(mouseEvent, paperRef.current)) {
389
+ return;
390
+ }
391
+ if (!openRef.current && isOpenControlled) {
392
+ return;
393
+ }
394
+ update(false, mouseEvent);
395
+ };
396
+ doc.addEventListener('mouseup', handleMouseUp, {
397
+ capture: true,
398
+ once: true
399
+ });
400
+ openingMouseUpListenerCleanupRef.current = () => {
401
+ doc.removeEventListener('mouseup', handleMouseUp, true);
402
+ };
248
403
  update(true, event);
249
404
  };
250
405
  const handleClose = event => {
@@ -263,7 +418,27 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
263
418
  onChange(event, child);
264
419
  }
265
420
  };
421
+ const handleValueChange = (event, child, newValue) => {
422
+ setValueState(newValue);
423
+ if (onChange) {
424
+ // Redefine target to allow name and value to be read.
425
+ // This allows seamless integration with the most popular form libraries.
426
+ // https://github.com/mui/material-ui/issues/13485#issuecomment-676048492
427
+ // Clone the event to not override `target` of the original event.
428
+ const nativeEvent = event.nativeEvent || event;
429
+ const clonedEvent = new nativeEvent.constructor(nativeEvent.type, nativeEvent);
430
+ Object.defineProperty(clonedEvent, 'target', {
431
+ writable: true,
432
+ value: {
433
+ value: newValue,
434
+ name
435
+ }
436
+ });
437
+ onChange(clonedEvent, child);
438
+ }
439
+ };
266
440
  const handleItemClick = child => event => {
441
+ didPointerDownOnItemRef.current = false;
267
442
  let newValue;
268
443
 
269
444
  // We use the tabindex attribute to signal the available options.
@@ -285,35 +460,76 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
285
460
  child.props.onClick(event);
286
461
  }
287
462
  if (value !== newValue) {
288
- setValueState(newValue);
289
- if (onChange) {
290
- // Redefine target to allow name and value to be read.
291
- // This allows seamless integration with the most popular form libraries.
292
- // https://github.com/mui/material-ui/issues/13485#issuecomment-676048492
293
- // Clone the event to not override `target` of the original event.
294
- const nativeEvent = event.nativeEvent || event;
295
- const clonedEvent = new nativeEvent.constructor(nativeEvent.type, nativeEvent);
296
- Object.defineProperty(clonedEvent, 'target', {
297
- writable: true,
298
- value: {
299
- value: newValue,
300
- name
301
- }
302
- });
303
- onChange(clonedEvent, child);
304
- }
463
+ handleValueChange(event, child, newValue);
305
464
  }
306
465
  if (!multiple) {
307
466
  update(false, event);
308
467
  }
309
468
  };
469
+ const handleItemMouseUp = (child, selected) => event => {
470
+ child.props.onMouseUp?.(event);
471
+ if (didPointerDownOnItemRef.current) {
472
+ didPointerDownOnItemRef.current = false;
473
+ return;
474
+ }
475
+ const disallowSelectedMouseUp = !selectionRef.current.allowSelectedMouseUp && selected;
476
+ const disallowUnselectedMouseUp = !selectionRef.current.allowUnselectedMouseUp && !selected;
477
+ if (disallowSelectedMouseUp || disallowUnselectedMouseUp) {
478
+ return;
479
+ }
480
+ event.currentTarget.click();
481
+ };
482
+ const handleClosedTypeahead = event => {
483
+ const state = closedTypeaheadRef.current;
484
+ const hasActiveBuffer = state.buffer !== '';
485
+ if (open || multiple || disabled || event.defaultPrevented || event.nativeEvent?.isComposing || event.key.length !== 1 || event.ctrlKey || event.metaKey || event.altKey || event.key === SPACE && !hasActiveBuffer) {
486
+ return false;
487
+ }
488
+ if (event.key === SPACE) {
489
+ event.preventDefault();
490
+ }
491
+ const isNewSession = state.buffer === '';
492
+ const {
493
+ options: searchableOptions,
494
+ selectedIndex
495
+ } = getTypeaheadOptions(childrenArray, value);
496
+ if (searchableOptions.length === 0) {
497
+ if (event.key !== SPACE) {
498
+ resetClosedTypeahead();
499
+ }
500
+ return true;
501
+ }
502
+ if (isNewSession) {
503
+ state.previousSearchIndex = selectedIndex;
504
+ }
505
+ const key = event.key.toLowerCase();
506
+ if (state.buffer === key && canCycleRepeatedCharacter(searchableOptions, key)) {
507
+ state.buffer = '';
508
+ state.previousSearchIndex = state.matchedIndex;
509
+ }
510
+ state.buffer += key;
511
+ typeaheadResetTimer.start(TYPEAHEAD_RESET_MS, resetClosedTypeahead);
512
+ const matchingIndex = getMatchingOptionIndex(searchableOptions, state.buffer, (state.previousSearchIndex ?? -1) + 1);
513
+ if (matchingIndex !== -1) {
514
+ const matchedOption = searchableOptions[matchingIndex];
515
+ state.matchedIndex = matchingIndex;
516
+ if (!areEqualValues(value, matchedOption.value)) {
517
+ handleValueChange(event, matchedOption.child, matchedOption.value);
518
+ }
519
+ return true;
520
+ }
521
+ if (event.key !== SPACE) {
522
+ resetClosedTypeahead();
523
+ }
524
+ return true;
525
+ };
310
526
  const handleKeyDown = event => {
311
527
  if (!readOnly) {
312
- const validKeys = [' ', 'ArrowUp', 'ArrowDown',
313
- // The native select doesn't respond to enter on macOS, but it's recommended by
528
+ const isClosedTypeaheadHandled = handleClosedTypeahead(event);
529
+ // The native select doesn't respond to Enter on macOS, but it's recommended by
314
530
  // https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/
315
- 'Enter'];
316
- if (validKeys.includes(event.key)) {
531
+ const isOpenKey = event.key === SPACE || event.key === ARROW_UP || event.key === ARROW_DOWN || event.key === ENTER;
532
+ if (!isClosedTypeaheadHandled && isOpenKey) {
317
533
  event.preventDefault();
318
534
  update(true, event);
319
535
  }
@@ -321,6 +537,7 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
321
537
  }
322
538
  };
323
539
  const handleBlur = event => {
540
+ resetClosedTypeahead();
324
541
  // if open event.stopImmediatePropagation
325
542
  if (!open && onBlur) {
326
543
  // Preact support, target is read only property on a native event.
@@ -334,6 +551,19 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
334
551
  onBlur(event);
335
552
  }
336
553
  };
554
+ const handleItemKeyDown = child => event => {
555
+ child?.props?.onKeyDown?.(event);
556
+ if (event.key === SPACE && event.target === event.currentTarget && !event.defaultPrevented) {
557
+ // Prevent the browser from scrolling the page
558
+ event.preventDefault();
559
+ // Ignore auto-repeated keydowns to avoid toggling multiple times
560
+ if (!event.repeat) {
561
+ // Trigger via click so that onClick receives a click event,
562
+ // consistent with Enter and pointer interactions.
563
+ event.currentTarget.click();
564
+ }
565
+ }
566
+ };
337
567
  delete other['aria-invalid'];
338
568
  let display;
339
569
  let displaySingle;
@@ -380,9 +610,18 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
380
610
  }
381
611
  return /*#__PURE__*/React.cloneElement(child, {
382
612
  'aria-selected': selected ? 'true' : 'false',
613
+ onMouseDown: event => {
614
+ didPointerDownOnItemRef.current = true;
615
+ child.props.onMouseDown?.(event);
616
+ },
617
+ onPointerDown: event => {
618
+ didPointerDownOnItemRef.current = true;
619
+ child.props.onPointerDown?.(event);
620
+ },
383
621
  onClick: handleItemClick(child),
622
+ onMouseUp: handleItemMouseUp(child, selected),
384
623
  onKeyUp: event => {
385
- if (event.key === ' ') {
624
+ if (event.key === SPACE) {
386
625
  // otherwise our MenuItems dispatches a click event
387
626
  // it's not behavior of the native <option> and causes
388
627
  // the select to close immediately since we open on space keydown
@@ -392,6 +631,7 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
392
631
  child.props.onKeyUp(event);
393
632
  }
394
633
  },
634
+ onKeyDown: handleItemKeyDown(child),
395
635
  role: 'option',
396
636
  selected,
397
637
  value: undefined,
@@ -399,6 +639,14 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
399
639
  'data-value': child.props.value // Instead, we provide it as a data attribute.
400
640
  });
401
641
  });
642
+
643
+ // Keep the opening mouseup guard current without mutating refs during render.
644
+ useEnhancedEffect(() => {
645
+ hasSelectedItemInListRef.current = foundMatch;
646
+ if (!open && !multiple && !foundMatch) {
647
+ resetClosedTypeahead();
648
+ }
649
+ }, [foundMatch, multiple, open, resetClosedTypeahead]);
402
650
  if (process.env.NODE_ENV !== 'production') {
403
651
  // TODO: uncomment once we enable eslint-plugin-react-compiler // eslint-disable-next-line react-compiler/react-compiler
404
652
  // eslint-disable-next-line react-hooks/rules-of-hooks
@@ -448,6 +696,7 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
448
696
  };
449
697
  const classes = useUtilityClasses(ownerState);
450
698
  const menuPaperSlotProps = typeof MenuProps.slotProps?.paper === 'function' ? MenuProps.slotProps.paper(ownerState) : MenuProps.slotProps?.paper;
699
+ const handlePaperRef = useForkRef(menuPaperSlotProps?.ref, paperRef);
451
700
  const menuListSlotProps = typeof MenuProps.slotProps?.list === 'function' ? MenuProps.slotProps.list(ownerState) : MenuProps.slotProps?.list;
452
701
  const listboxId = useId();
453
702
  const nativeInputId = useId();
@@ -461,8 +710,9 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
461
710
  "aria-disabled": disabled ? 'true' : undefined,
462
711
  "aria-expanded": open ? 'true' : 'false',
463
712
  "aria-haspopup": "listbox",
713
+ "aria-readonly": readOnly ? 'true' : undefined,
464
714
  "aria-label": ariaLabel,
465
- "aria-labelledby": [labelId, buttonId].filter(Boolean).join(' ') || undefined,
715
+ "aria-labelledby": labelId,
466
716
  "aria-describedby": ariaDescribedby,
467
717
  "aria-required": required ? 'true' : undefined,
468
718
  "aria-invalid": error ? 'true' : undefined,
@@ -491,6 +741,7 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
491
741
  onChange: handleChange,
492
742
  tabIndex: -1,
493
743
  disabled: disabled,
744
+ readOnly: readOnly,
494
745
  className: classes.nativeInput,
495
746
  autoFocus: autoFocus,
496
747
  required: required,
@@ -529,6 +780,7 @@ const SelectInput = /*#__PURE__*/React.forwardRef(function SelectInput(props, re
529
780
  },
530
781
  paper: {
531
782
  ...menuPaperSlotProps,
783
+ ref: handlePaperRef,
532
784
  style: {
533
785
  minWidth: menuMinWidth,
534
786
  ...menuPaperSlotProps?.style
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.canCycleRepeatedCharacter = canCycleRepeatedCharacter;
9
+ exports.getMatchingOptionIndex = getMatchingOptionIndex;
10
+ exports.getTypeaheadOptions = getTypeaheadOptions;
11
+ var React = _interopRequireWildcard(require("react"));
12
+ var _areEqualValues = _interopRequireDefault(require("./areEqualValues"));
13
+ function hasOwnValueProp(child) {
14
+ return Object.prototype.hasOwnProperty.call(child.props, 'value');
15
+ }
16
+ function getTextFromReactNode(node) {
17
+ if (typeof node === 'string' || typeof node === 'number') {
18
+ return String(node);
19
+ }
20
+ let text = '';
21
+ React.Children.forEach(node, child => {
22
+ if (typeof child === 'string' || typeof child === 'number') {
23
+ text += String(child);
24
+ } else if (/*#__PURE__*/React.isValidElement(child)) {
25
+ text += getTextFromReactNode(child.props.children);
26
+ }
27
+ });
28
+ return text;
29
+ }
30
+ function getMatchingOptionIndex(options, search, startIndex = 0) {
31
+ if (options.length === 0) {
32
+ return -1;
33
+ }
34
+ const normalizedStartIndex = (startIndex % options.length + options.length) % options.length;
35
+ for (let offset = 0; offset < options.length; offset += 1) {
36
+ const index = (normalizedStartIndex + offset) % options.length;
37
+ if (options[index].label.startsWith(search)) {
38
+ return index;
39
+ }
40
+ }
41
+ return -1;
42
+ }
43
+ function canCycleRepeatedCharacter(options, key) {
44
+ return !options.some(option => option.label[0] === key && option.label[1] === key);
45
+ }
46
+ function getTypeaheadOptions(childrenArray, value) {
47
+ const options = [];
48
+ let selectedIndex = -1;
49
+ for (let index = 0; index < childrenArray.length; index += 1) {
50
+ const child = childrenArray[index];
51
+ if (! /*#__PURE__*/React.isValidElement(child) || !hasOwnValueProp(child) || child.props.disabled) {
52
+ continue;
53
+ }
54
+
55
+ // Closed typeahead cannot exclude CSS-hidden text because no option DOM is mounted.
56
+ const label = getTextFromReactNode(child.props.children).trim().toLowerCase();
57
+ if (label === '') {
58
+ continue;
59
+ }
60
+ if (selectedIndex === -1 && (0, _areEqualValues.default)(value, child.props.value)) {
61
+ selectedIndex = options.length;
62
+ }
63
+ options.push({
64
+ child,
65
+ label,
66
+ value: child.props.value
67
+ });
68
+ }
69
+ return {
70
+ options,
71
+ selectedIndex
72
+ };
73
+ }
@@ -0,0 +1,63 @@
1
+ import * as React from 'react';
2
+ import areEqualValues from "./areEqualValues.mjs";
3
+ function hasOwnValueProp(child) {
4
+ return Object.prototype.hasOwnProperty.call(child.props, 'value');
5
+ }
6
+ function getTextFromReactNode(node) {
7
+ if (typeof node === 'string' || typeof node === 'number') {
8
+ return String(node);
9
+ }
10
+ let text = '';
11
+ React.Children.forEach(node, child => {
12
+ if (typeof child === 'string' || typeof child === 'number') {
13
+ text += String(child);
14
+ } else if (/*#__PURE__*/React.isValidElement(child)) {
15
+ text += getTextFromReactNode(child.props.children);
16
+ }
17
+ });
18
+ return text;
19
+ }
20
+ export function getMatchingOptionIndex(options, search, startIndex = 0) {
21
+ if (options.length === 0) {
22
+ return -1;
23
+ }
24
+ const normalizedStartIndex = (startIndex % options.length + options.length) % options.length;
25
+ for (let offset = 0; offset < options.length; offset += 1) {
26
+ const index = (normalizedStartIndex + offset) % options.length;
27
+ if (options[index].label.startsWith(search)) {
28
+ return index;
29
+ }
30
+ }
31
+ return -1;
32
+ }
33
+ export function canCycleRepeatedCharacter(options, key) {
34
+ return !options.some(option => option.label[0] === key && option.label[1] === key);
35
+ }
36
+ export function getTypeaheadOptions(childrenArray, value) {
37
+ const options = [];
38
+ let selectedIndex = -1;
39
+ for (let index = 0; index < childrenArray.length; index += 1) {
40
+ const child = childrenArray[index];
41
+ if (! /*#__PURE__*/React.isValidElement(child) || !hasOwnValueProp(child) || child.props.disabled) {
42
+ continue;
43
+ }
44
+
45
+ // Closed typeahead cannot exclude CSS-hidden text because no option DOM is mounted.
46
+ const label = getTextFromReactNode(child.props.children).trim().toLowerCase();
47
+ if (label === '') {
48
+ continue;
49
+ }
50
+ if (selectedIndex === -1 && areEqualValues(value, child.props.value)) {
51
+ selectedIndex = options.length;
52
+ }
53
+ options.push({
54
+ child,
55
+ label,
56
+ value: child.props.value
57
+ });
58
+ }
59
+ return {
60
+ options,
61
+ selectedIndex
62
+ };
63
+ }
@@ -15,6 +15,7 @@ var _styles = require("../styles");
15
15
  var _zeroStyled = require("../zero-styled");
16
16
  var _memoTheme = _interopRequireDefault(require("../utils/memoTheme"));
17
17
  var _DefaultPropsProvider = require("../DefaultPropsProvider");
18
+ var _utils = require("../transitions/utils");
18
19
  var _skeletonClasses = require("./skeletonClasses");
19
20
  var _jsxRuntime = require("react/jsx-runtime");
20
21
  const useUtilityClasses = ownerState => {
@@ -84,6 +85,15 @@ const SkeletonRoot = (0, _zeroStyled.styled)('span', {
84
85
  }) => {
85
86
  const radiusUnit = (0, _styles.unstable_getUnit)(theme.shape.borderRadius) || 'px';
86
87
  const radiusValue = (0, _styles.unstable_toUnitless)(theme.shape.borderRadius);
88
+ const reducedMotionPulseStyles = (0, _utils.getReducedMotionStyles)(theme, {
89
+ animation: 'none'
90
+ });
91
+ const reducedMotionWaveStyles = (0, _utils.getReducedMotionStyles)(theme, {
92
+ '&::after': {
93
+ animation: 'none',
94
+ display: 'none'
95
+ }
96
+ });
87
97
  return {
88
98
  display: 'block',
89
99
  // Create a "on paper" color with sufficient contrast retaining the color
@@ -148,7 +158,12 @@ const SkeletonRoot = (0, _zeroStyled.styled)('span', {
148
158
  style: pulseAnimation || {
149
159
  animation: `${pulseKeyframe} 2s ease-in-out 0.5s infinite`
150
160
  }
151
- }, {
161
+ }, ...(reducedMotionPulseStyles ? [{
162
+ props: {
163
+ animation: 'pulse'
164
+ },
165
+ style: reducedMotionPulseStyles
166
+ }] : []), {
152
167
  props: {
153
168
  animation: 'wave'
154
169
  },
@@ -182,7 +197,12 @@ const SkeletonRoot = (0, _zeroStyled.styled)('span', {
182
197
  animation: `${waveKeyframe} 2s linear 0.5s infinite`
183
198
  }
184
199
  }
185
- }]
200
+ }, ...(reducedMotionWaveStyles ? [{
201
+ props: {
202
+ animation: 'wave'
203
+ },
204
+ style: reducedMotionWaveStyles
205
+ }] : [])]
186
206
  };
187
207
  }));
188
208
  const Skeleton = /*#__PURE__*/React.forwardRef(function Skeleton(inProps, ref) {
@@ -8,6 +8,7 @@ import { unstable_getUnit as getUnit, unstable_toUnitless as toUnitless } from "
8
8
  import { keyframes, css, styled } from "../zero-styled/index.mjs";
9
9
  import memoTheme from "../utils/memoTheme.mjs";
10
10
  import { useDefaultProps } from "../DefaultPropsProvider/index.mjs";
11
+ import { getReducedMotionStyles } from "../transitions/utils.mjs";
11
12
  import { getSkeletonUtilityClass } from "./skeletonClasses.mjs";
12
13
  import { jsx as _jsx } from "react/jsx-runtime";
13
14
  const useUtilityClasses = ownerState => {
@@ -77,6 +78,15 @@ const SkeletonRoot = styled('span', {
77
78
  }) => {
78
79
  const radiusUnit = getUnit(theme.shape.borderRadius) || 'px';
79
80
  const radiusValue = toUnitless(theme.shape.borderRadius);
81
+ const reducedMotionPulseStyles = getReducedMotionStyles(theme, {
82
+ animation: 'none'
83
+ });
84
+ const reducedMotionWaveStyles = getReducedMotionStyles(theme, {
85
+ '&::after': {
86
+ animation: 'none',
87
+ display: 'none'
88
+ }
89
+ });
80
90
  return {
81
91
  display: 'block',
82
92
  // Create a "on paper" color with sufficient contrast retaining the color
@@ -141,7 +151,12 @@ const SkeletonRoot = styled('span', {
141
151
  style: pulseAnimation || {
142
152
  animation: `${pulseKeyframe} 2s ease-in-out 0.5s infinite`
143
153
  }
144
- }, {
154
+ }, ...(reducedMotionPulseStyles ? [{
155
+ props: {
156
+ animation: 'pulse'
157
+ },
158
+ style: reducedMotionPulseStyles
159
+ }] : []), {
145
160
  props: {
146
161
  animation: 'wave'
147
162
  },
@@ -175,7 +190,12 @@ const SkeletonRoot = styled('span', {
175
190
  animation: `${waveKeyframe} 2s linear 0.5s infinite`
176
191
  }
177
192
  }
178
- }]
193
+ }, ...(reducedMotionWaveStyles ? [{
194
+ props: {
195
+ animation: 'wave'
196
+ },
197
+ style: reducedMotionWaveStyles
198
+ }] : [])]
179
199
  };
180
200
  }));
181
201
  const Skeleton = /*#__PURE__*/React.forwardRef(function Skeleton(inProps, ref) {