@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
@@ -2,16 +2,109 @@
2
2
 
3
3
  import * as React from 'react';
4
4
  import PropTypes from 'prop-types';
5
- import { TransitionGroup } from 'react-transition-group';
6
5
  import clsx from 'clsx';
6
+ import useOnMount from '@mui/utils/useOnMount';
7
7
  import useTimeout from '@mui/utils/useTimeout';
8
- import { keyframes, styled } from "../zero-styled/index.mjs";
8
+ import { keyframes, css, styled, useTheme } from "../zero-styled/index.mjs";
9
9
  import { useDefaultProps } from "../DefaultPropsProvider/index.mjs";
10
10
  import Ripple from "./Ripple.mjs";
11
11
  import touchRippleClasses from "./touchRippleClasses.mjs";
12
+ import useEventCallback from "../utils/useEventCallback.mjs";
13
+ import useReducedMotion from "../transitions/useReducedMotion.mjs";
12
14
  import { jsx as _jsx } from "react/jsx-runtime";
13
15
  const DURATION = 550;
14
16
  export const DELAY_RIPPLE = 80;
17
+ const EMPTY_OBJ = {};
18
+ const EMPTY_ARRAY = [];
19
+ const NOOP = () => {};
20
+
21
+ /**
22
+ * Keep the same DOM order TouchRipple had when it used react-transition-group:
23
+ * exiting ripples stay in place, and new ripples are inserted before the final
24
+ * group of ripples that are waiting for their exit animation to finish.
25
+ *
26
+ * @param {number[]} prevOrder The previous DOM order, including ripples that may be exiting.
27
+ * @param {number[]} nextActiveKeys The ripples that should still be treated as active.
28
+ * @returns {number[]} The next DOM order, preserving the position of exiting ripples where possible.
29
+ */
30
+ function mergeRippleOrder(prevOrder, nextActiveKeys) {
31
+ const nextKeySet = new Set(nextActiveKeys);
32
+ const nextKeysPending = new Map();
33
+ let pendingKeys = [];
34
+ for (const prevKey of prevOrder) {
35
+ if (nextKeySet.has(prevKey)) {
36
+ if (pendingKeys.length > 0) {
37
+ nextKeysPending.set(prevKey, pendingKeys);
38
+ pendingKeys = [];
39
+ }
40
+ } else {
41
+ pendingKeys.push(prevKey);
42
+ }
43
+ }
44
+ const nextOrder = [];
45
+ for (const nextKey of nextActiveKeys) {
46
+ const pendingBefore = nextKeysPending.get(nextKey);
47
+ if (pendingBefore) {
48
+ nextOrder.push(...pendingBefore);
49
+ }
50
+ nextOrder.push(nextKey);
51
+ }
52
+ nextOrder.push(...pendingKeys);
53
+ return nextOrder;
54
+ }
55
+
56
+ /**
57
+ * Calculate where the ripple should start and how large it must be to cover the host element.
58
+ *
59
+ * @param {object} params
60
+ * @param {object} params.event The mouse or touch event that started the ripple.
61
+ * @param {HTMLElement | null} params.element The host element used for measurements. Tests pass `null`.
62
+ * @param {boolean} params.center If `true`, start the ripple from the center of the host element.
63
+ * @returns {{ rippleX: number, rippleY: number, rippleSize: number }} The ripple position and size.
64
+ */
65
+ function computeRippleState({
66
+ event,
67
+ element,
68
+ center
69
+ }) {
70
+ const rect = element ? element.getBoundingClientRect() : {
71
+ width: 0,
72
+ height: 0,
73
+ left: 0,
74
+ top: 0
75
+ };
76
+ let rippleX;
77
+ let rippleY;
78
+ if (center || event === undefined || event.clientX === 0 && event.clientY === 0 || !event.clientX && !event.touches) {
79
+ rippleX = Math.round(rect.width / 2);
80
+ rippleY = Math.round(rect.height / 2);
81
+ } else {
82
+ const {
83
+ clientX,
84
+ clientY
85
+ } = event.touches && event.touches.length > 0 ? event.touches[0] : event;
86
+ rippleX = Math.round(clientX - rect.left);
87
+ rippleY = Math.round(clientY - rect.top);
88
+ }
89
+ let rippleSize;
90
+ if (center) {
91
+ rippleSize = Math.sqrt((2 * rect.width ** 2 + rect.height ** 2) / 3);
92
+
93
+ // Mobile Chrome can skip this animation for even pixel sizes.
94
+ if (rippleSize % 2 === 0) {
95
+ rippleSize += 1;
96
+ }
97
+ } else {
98
+ const sizeX = Math.max(Math.abs((element ? element.clientWidth : 0) - rippleX), rippleX) * 2 + 2;
99
+ const sizeY = Math.max(Math.abs((element ? element.clientHeight : 0) - rippleY), rippleY) * 2 + 2;
100
+ rippleSize = Math.sqrt(sizeX ** 2 + sizeY ** 2);
101
+ }
102
+ return {
103
+ rippleX,
104
+ rippleY,
105
+ rippleSize
106
+ };
107
+ }
15
108
  const enterKeyframe = keyframes`
16
109
  0% {
17
110
  transform: scale(0);
@@ -45,6 +138,44 @@ const pulsateKeyframe = keyframes`
45
138
  transform: scale(1);
46
139
  }
47
140
  `;
141
+ function getAnimationStyles(theme) {
142
+ if (theme.motion.reducedMotion === 'always') {
143
+ return null;
144
+ }
145
+ const styles = css`
146
+ &.${touchRippleClasses.rippleVisible} {
147
+ animation-name: ${enterKeyframe};
148
+ animation-duration: ${DURATION}ms;
149
+ animation-timing-function: ${theme.transitions.easing.easeInOut};
150
+ }
151
+
152
+ &.${touchRippleClasses.ripplePulsate} {
153
+ animation-duration: ${theme.transitions.duration.shorter}ms;
154
+ }
155
+
156
+ & .${touchRippleClasses.childLeaving} {
157
+ animation-name: ${exitKeyframe};
158
+ animation-duration: ${DURATION}ms;
159
+ animation-timing-function: ${theme.transitions.easing.easeInOut};
160
+ }
161
+
162
+ & .${touchRippleClasses.childPulsate} {
163
+ animation-name: ${pulsateKeyframe};
164
+ animation-duration: 2500ms;
165
+ animation-timing-function: ${theme.transitions.easing.easeInOut};
166
+ animation-iteration-count: infinite;
167
+ animation-delay: 200ms;
168
+ }
169
+ `;
170
+ if (theme.motion.reducedMotion === 'system') {
171
+ return css`
172
+ @media (prefers-reduced-motion: no-preference) {
173
+ ${styles}
174
+ }
175
+ `;
176
+ }
177
+ return styles;
178
+ }
48
179
  export const TouchRippleRoot = styled('span', {
49
180
  name: 'MuiTouchRipple',
50
181
  slot: 'Root'
@@ -60,8 +191,8 @@ export const TouchRippleRoot = styled('span', {
60
191
  borderRadius: 'inherit'
61
192
  });
62
193
 
63
- // This `styled()` function invokes keyframes. `styled-components` only supports keyframes
64
- // in string templates. Do not convert these styles in JS object as it will break.
194
+ // This `styled()` call uses keyframes. styled-components only supports keyframes
195
+ // in template strings, so do not convert these styles to a JS object.
65
196
  export const TouchRippleRipple = styled(Ripple, {
66
197
  name: 'MuiTouchRipple',
67
198
  slot: 'Ripple'
@@ -72,35 +203,10 @@ export const TouchRippleRipple = styled(Ripple, {
72
203
  &.${touchRippleClasses.rippleVisible} {
73
204
  opacity: 0.3;
74
205
  transform: scale(1);
75
- animation-name: ${enterKeyframe};
76
- animation-duration: ${DURATION}ms;
77
- animation-timing-function: ${({
78
- theme
79
- }) => theme.transitions.easing.easeInOut};
80
- }
81
-
82
- &.${touchRippleClasses.ripplePulsate} {
83
- animation-duration: ${({
84
- theme
85
- }) => theme.transitions.duration.shorter}ms;
86
- }
87
-
88
- & .${touchRippleClasses.child} {
89
- opacity: 1;
90
- display: block;
91
- width: 100%;
92
- height: 100%;
93
- border-radius: 50%;
94
- background-color: currentColor;
95
206
  }
96
207
 
97
208
  & .${touchRippleClasses.childLeaving} {
98
209
  opacity: 0;
99
- animation-name: ${exitKeyframe};
100
- animation-duration: ${DURATION}ms;
101
- animation-timing-function: ${({
102
- theme
103
- }) => theme.transitions.easing.easeInOut};
104
210
  }
105
211
 
106
212
  & .${touchRippleClasses.childPulsate} {
@@ -108,35 +214,54 @@ export const TouchRippleRipple = styled(Ripple, {
108
214
  /* @noflip */
109
215
  left: 0px;
110
216
  top: 0;
111
- animation-name: ${pulsateKeyframe};
112
- animation-duration: 2500ms;
113
- animation-timing-function: ${({
217
+ }
218
+
219
+ ${({
114
220
  theme
115
- }) => theme.transitions.easing.easeInOut};
116
- animation-iteration-count: infinite;
117
- animation-delay: 200ms;
221
+ }) => getAnimationStyles(theme)}
222
+
223
+ & .${touchRippleClasses.child} {
224
+ opacity: 1;
225
+ display: block;
226
+ width: 100%;
227
+ height: 100%;
228
+ border-radius: 50%;
229
+ background-color: currentColor;
118
230
  }
119
231
  `;
120
232
 
121
233
  /**
122
234
  * @ignore - internal component.
123
- *
124
- * TODO v5: Make private
125
235
  */
126
236
  const TouchRipple = /*#__PURE__*/React.forwardRef(function TouchRipple(inProps, ref) {
127
237
  const props = useDefaultProps({
128
238
  props: inProps,
129
239
  name: 'MuiTouchRipple'
130
240
  });
241
+ const theme = useTheme();
242
+ const reducedMotion = useReducedMotion(theme.motion.reducedMotion, false);
131
243
  const {
132
244
  center: centerProp = false,
133
- classes = {},
245
+ classes = EMPTY_OBJ,
134
246
  className,
135
247
  ...other
136
248
  } = props;
137
- const [ripples, setRipples] = React.useState([]);
249
+ // Store ripples as data so we can keep exiting ripples mounted until their
250
+ // exit animation ends. Ripple calls onExited when it is safe to remove one.
251
+ const [rippleState, setRippleState] = React.useState({
252
+ items: EMPTY_ARRAY,
253
+ order: EMPTY_ARRAY
254
+ });
255
+ const ripples = rippleState.items;
138
256
  const nextKey = React.useRef(0);
139
257
  const rippleCallback = React.useRef(null);
258
+ const mountedRef = React.useRef(false);
259
+ useOnMount(() => {
260
+ mountedRef.current = true;
261
+ return () => {
262
+ mountedRef.current = false;
263
+ };
264
+ });
140
265
  React.useEffect(() => {
141
266
  if (rippleCallback.current) {
142
267
  rippleCallback.current();
@@ -150,10 +275,23 @@ const TouchRipple = /*#__PURE__*/React.forwardRef(function TouchRipple(inProps,
150
275
  // We don't want to display the ripple for touch scroll events.
151
276
  const startTimer = useTimeout();
152
277
 
153
- // This is the hook called once the previous timeout is ready.
278
+ // Holds delayed touch-start work until the delay expires or touchend forces it to run.
154
279
  const startTimerCommit = React.useRef(null);
155
280
  const container = React.useRef(null);
156
- const startCommit = React.useCallback(params => {
281
+ const handleExited = useEventCallback(key => {
282
+ if (!mountedRef.current) {
283
+ return;
284
+ }
285
+ setRippleState(prevState => {
286
+ const nextItems = prevState.items.filter(ripple => ripple.key !== key);
287
+ const nextOrder = mergeRippleOrder(prevState.order.filter(rippleKey => rippleKey !== key), nextItems.filter(ripple => !ripple.exiting).map(ripple => ripple.key));
288
+ return {
289
+ items: nextItems,
290
+ order: nextOrder
291
+ };
292
+ });
293
+ });
294
+ const startCommit = useEventCallback(params => {
157
295
  const {
158
296
  pulsate,
159
297
  rippleX,
@@ -161,29 +299,29 @@ const TouchRipple = /*#__PURE__*/React.forwardRef(function TouchRipple(inProps,
161
299
  rippleSize,
162
300
  cb
163
301
  } = params;
164
- setRipples(oldRipples => [...oldRipples, /*#__PURE__*/_jsx(TouchRippleRipple, {
165
- classes: {
166
- ripple: clsx(classes.ripple, touchRippleClasses.ripple),
167
- rippleVisible: clsx(classes.rippleVisible, touchRippleClasses.rippleVisible),
168
- ripplePulsate: clsx(classes.ripplePulsate, touchRippleClasses.ripplePulsate),
169
- child: clsx(classes.child, touchRippleClasses.child),
170
- childLeaving: clsx(classes.childLeaving, touchRippleClasses.childLeaving),
171
- childPulsate: clsx(classes.childPulsate, touchRippleClasses.childPulsate)
172
- },
173
- timeout: DURATION,
174
- pulsate: pulsate,
175
- rippleX: rippleX,
176
- rippleY: rippleY,
177
- rippleSize: rippleSize
178
- }, nextKey.current)]);
302
+ const key = nextKey.current;
179
303
  nextKey.current += 1;
304
+ setRippleState(prevState => {
305
+ const nextItems = [...prevState.items, {
306
+ key,
307
+ pulsate,
308
+ rippleX,
309
+ rippleY,
310
+ rippleSize,
311
+ exiting: false
312
+ }];
313
+ return {
314
+ items: nextItems,
315
+ order: mergeRippleOrder(prevState.order, nextItems.filter(ripple => !ripple.exiting).map(ripple => ripple.key))
316
+ };
317
+ });
180
318
  rippleCallback.current = cb;
181
- }, [classes]);
182
- const start = React.useCallback((event = {}, options = {}, cb = () => {}) => {
319
+ });
320
+ const start = useEventCallback((event = EMPTY_OBJ, options = EMPTY_OBJ, cb = NOOP) => {
183
321
  const {
184
322
  pulsate = false,
185
323
  center = centerProp || options.pulsate,
186
- fakeElement = false // For test purposes
324
+ fakeElement = false // Used only by tests.
187
325
  } = options;
188
326
  if (event?.type === 'mousedown' && ignoringMouseDown.current) {
189
327
  ignoringMouseDown.current = false;
@@ -193,48 +331,21 @@ const TouchRipple = /*#__PURE__*/React.forwardRef(function TouchRipple(inProps,
193
331
  ignoringMouseDown.current = true;
194
332
  }
195
333
  const element = fakeElement ? null : container.current;
196
- const rect = element ? element.getBoundingClientRect() : {
197
- width: 0,
198
- height: 0,
199
- left: 0,
200
- top: 0
201
- };
202
-
203
- // Get the size of the ripple
204
- let rippleX;
205
- let rippleY;
206
- let rippleSize;
207
- if (center || event === undefined || event.clientX === 0 && event.clientY === 0 || !event.clientX && !event.touches) {
208
- rippleX = Math.round(rect.width / 2);
209
- rippleY = Math.round(rect.height / 2);
210
- } else {
211
- const {
212
- clientX,
213
- clientY
214
- } = event.touches && event.touches.length > 0 ? event.touches[0] : event;
215
- rippleX = Math.round(clientX - rect.left);
216
- rippleY = Math.round(clientY - rect.top);
217
- }
218
- if (center) {
219
- rippleSize = Math.sqrt((2 * rect.width ** 2 + rect.height ** 2) / 3);
220
-
221
- // For some reason the animation is broken on Mobile Chrome if the size is even.
222
- if (rippleSize % 2 === 0) {
223
- rippleSize += 1;
224
- }
225
- } else {
226
- const sizeX = Math.max(Math.abs((element ? element.clientWidth : 0) - rippleX), rippleX) * 2 + 2;
227
- const sizeY = Math.max(Math.abs((element ? element.clientHeight : 0) - rippleY), rippleY) * 2 + 2;
228
- rippleSize = Math.sqrt(sizeX ** 2 + sizeY ** 2);
229
- }
334
+ const {
335
+ rippleX,
336
+ rippleY,
337
+ rippleSize
338
+ } = computeRippleState({
339
+ event,
340
+ element,
341
+ center
342
+ });
230
343
 
231
- // Touch devices
344
+ // Delay touch ripples so scroll gestures do not flash a ripple.
232
345
  if (event?.touches) {
233
- // check that this isn't another touchstart due to multitouch
234
- // otherwise we will only clear a single timer when unmounting while two
235
- // are running
346
+ // Ignore extra touchstart events from multi-touch. There is only one
347
+ // delayed start callback to clear on unmount.
236
348
  if (startTimerCommit.current === null) {
237
- // Prepare the ripple effect.
238
349
  startTimerCommit.current = () => {
239
350
  startCommit({
240
351
  pulsate,
@@ -244,8 +355,6 @@ const TouchRipple = /*#__PURE__*/React.forwardRef(function TouchRipple(inProps,
244
355
  cb
245
356
  });
246
357
  };
247
- // Delay the execution of the ripple effect.
248
- // We have to make a tradeoff with this delay value.
249
358
  startTimer.start(DELAY_RIPPLE, () => {
250
359
  if (startTimerCommit.current) {
251
360
  startTimerCommit.current();
@@ -262,17 +371,17 @@ const TouchRipple = /*#__PURE__*/React.forwardRef(function TouchRipple(inProps,
262
371
  cb
263
372
  });
264
373
  }
265
- }, [centerProp, startCommit, startTimer]);
266
- const pulsate = React.useCallback(() => {
267
- start({}, {
374
+ });
375
+ const pulsate = useEventCallback(() => {
376
+ start(EMPTY_OBJ, {
268
377
  pulsate: true
269
378
  });
270
- }, [start]);
271
- const stop = React.useCallback((event, cb) => {
379
+ });
380
+ const stop = useEventCallback((event, cb) => {
272
381
  startTimer.clear();
273
382
 
274
- // The touch interaction occurs too quickly.
275
- // We still want to show ripple effect.
383
+ // If touch ends before the delay finishes, show the ripple now and stop it
384
+ // on the next tick so the user still gets feedback.
276
385
  if (event?.type === 'touchend' && startTimerCommit.current) {
277
386
  startTimerCommit.current();
278
387
  startTimerCommit.current = null;
@@ -282,28 +391,55 @@ const TouchRipple = /*#__PURE__*/React.forwardRef(function TouchRipple(inProps,
282
391
  return;
283
392
  }
284
393
  startTimerCommit.current = null;
285
- setRipples(oldRipples => {
286
- if (oldRipples.length > 0) {
287
- return oldRipples.slice(1);
394
+ setRippleState(prevState => {
395
+ const firstActiveIndex = prevState.items.findIndex(ripple => !ripple.exiting);
396
+ if (firstActiveIndex === -1) {
397
+ return prevState;
288
398
  }
289
- return oldRipples;
399
+ const nextItems = prevState.items.slice();
400
+ nextItems[firstActiveIndex] = {
401
+ ...nextItems[firstActiveIndex],
402
+ exiting: true
403
+ };
404
+ return {
405
+ items: nextItems,
406
+ order: mergeRippleOrder(prevState.order, nextItems.filter(ripple => !ripple.exiting).map(ripple => ripple.key))
407
+ };
290
408
  });
291
409
  rippleCallback.current = cb;
292
- }, [startTimer]);
410
+ });
293
411
  React.useImperativeHandle(ref, () => ({
294
412
  pulsate,
295
413
  start,
296
414
  stop
297
415
  }), [pulsate, start, stop]);
416
+ const rippleByKey = new Map(ripples.map(ripple => [ripple.key, ripple]));
417
+ const orderedRipples = rippleState.order.map(rippleKey => rippleByKey.get(rippleKey)).filter(Boolean);
418
+
419
+ // Keep the old react-transition-group DOM order:
420
+ // exiting ripples stay in place, and new ripples are inserted before the
421
+ // final group waiting for its exit animation to finish.
298
422
  return /*#__PURE__*/_jsx(TouchRippleRoot, {
299
423
  className: clsx(touchRippleClasses.root, classes.root, className),
300
424
  ref: container,
301
425
  ...other,
302
- children: /*#__PURE__*/_jsx(TransitionGroup, {
303
- component: null,
304
- exit: true,
305
- children: ripples
306
- })
426
+ children: orderedRipples.map(ripple => /*#__PURE__*/_jsx(TouchRippleRipple, {
427
+ classes: {
428
+ ripple: clsx(classes.ripple, touchRippleClasses.ripple),
429
+ rippleVisible: clsx(classes.rippleVisible, touchRippleClasses.rippleVisible),
430
+ ripplePulsate: clsx(classes.ripplePulsate, touchRippleClasses.ripplePulsate),
431
+ child: clsx(classes.child, touchRippleClasses.child),
432
+ childLeaving: clsx(classes.childLeaving, touchRippleClasses.childLeaving),
433
+ childPulsate: clsx(classes.childPulsate, touchRippleClasses.childPulsate)
434
+ },
435
+ timeout: reducedMotion.shouldReduceMotion ? 0 : DURATION,
436
+ pulsate: ripple.pulsate,
437
+ rippleX: ripple.rippleX,
438
+ rippleY: ripple.rippleY,
439
+ rippleSize: ripple.rippleSize,
440
+ in: !ripple.exiting,
441
+ onExited: () => handleExited(ripple.key)
442
+ }, ripple.key))
307
443
  });
308
444
  });
309
445
  process.env.NODE_ENV !== "production" ? TouchRipple.propTypes /* remove-proptypes */ = {