1 | import _extends from "@babel/runtime/helpers/esm/extends";
|
2 | import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
3 | const _excluded = ["center", "classes", "className"];
|
4 | import * as React from 'react';
|
5 | import PropTypes from 'prop-types';
|
6 | import { TransitionGroup } from 'react-transition-group';
|
7 | import clsx from 'clsx';
|
8 | import { keyframes } from '@mui/system';
|
9 | import styled from '../styles/styled';
|
10 | import useThemeProps from '../styles/useThemeProps';
|
11 | import Ripple from './Ripple';
|
12 | import touchRippleClasses from './touchRippleClasses';
|
13 | import { jsx as _jsx } from "react/jsx-runtime";
|
14 | const DURATION = 550;
|
15 | export const DELAY_RIPPLE = 80;
|
16 | const enterKeyframe = keyframes`
|
17 | 0% {
|
18 | transform: scale(0);
|
19 | opacity: 0.1;
|
20 | }
|
21 |
|
22 | 100% {
|
23 | transform: scale(1);
|
24 | opacity: 0.3;
|
25 | }
|
26 | `;
|
27 | const exitKeyframe = keyframes`
|
28 | 0% {
|
29 | opacity: 1;
|
30 | }
|
31 |
|
32 | 100% {
|
33 | opacity: 0;
|
34 | }
|
35 | `;
|
36 | const pulsateKeyframe = keyframes`
|
37 | 0% {
|
38 | transform: scale(1);
|
39 | }
|
40 |
|
41 | 50% {
|
42 | transform: scale(0.92);
|
43 | }
|
44 |
|
45 | 100% {
|
46 | transform: scale(1);
|
47 | }
|
48 | `;
|
49 | export const TouchRippleRoot = styled('span', {
|
50 | name: 'MuiTouchRipple',
|
51 | slot: 'Root'
|
52 | })({
|
53 | overflow: 'hidden',
|
54 | pointerEvents: 'none',
|
55 | position: 'absolute',
|
56 | zIndex: 0,
|
57 | top: 0,
|
58 | right: 0,
|
59 | bottom: 0,
|
60 | left: 0,
|
61 | borderRadius: 'inherit'
|
62 | });
|
63 |
|
64 |
|
65 | export const TouchRippleRipple = styled(Ripple, {
|
66 | name: 'MuiTouchRipple',
|
67 | slot: 'Ripple'
|
68 | })`
|
69 | opacity: 0;
|
70 | position: absolute;
|
71 |
|
72 | &.${touchRippleClasses.rippleVisible} {
|
73 | opacity: 0.3;
|
74 | 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 | }
|
96 |
|
97 | & .${touchRippleClasses.childLeaving} {
|
98 | opacity: 0;
|
99 | animation-name: ${exitKeyframe};
|
100 | animation-duration: ${DURATION}ms;
|
101 | animation-timing-function: ${({
|
102 | theme
|
103 | }) => theme.transitions.easing.easeInOut};
|
104 | }
|
105 |
|
106 | & .${touchRippleClasses.childPulsate} {
|
107 | position: absolute;
|
108 | /* @noflip */
|
109 | left: 0px;
|
110 | top: 0;
|
111 | animation-name: ${pulsateKeyframe};
|
112 | animation-duration: 2500ms;
|
113 | animation-timing-function: ${({
|
114 | theme
|
115 | }) => theme.transitions.easing.easeInOut};
|
116 | animation-iteration-count: infinite;
|
117 | animation-delay: 200ms;
|
118 | }
|
119 | `;
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 | const TouchRipple = React.forwardRef(function TouchRipple(inProps, ref) {
|
127 | const props = useThemeProps({
|
128 | props: inProps,
|
129 | name: 'MuiTouchRipple'
|
130 | });
|
131 |
|
132 | const {
|
133 | center: centerProp = false,
|
134 | classes = {},
|
135 | className
|
136 | } = props,
|
137 | other = _objectWithoutPropertiesLoose(props, _excluded);
|
138 |
|
139 | const [ripples, setRipples] = React.useState([]);
|
140 | const nextKey = React.useRef(0);
|
141 | const rippleCallback = React.useRef(null);
|
142 | React.useEffect(() => {
|
143 | if (rippleCallback.current) {
|
144 | rippleCallback.current();
|
145 | rippleCallback.current = null;
|
146 | }
|
147 | }, [ripples]);
|
148 |
|
149 | const ignoringMouseDown = React.useRef(false);
|
150 |
|
151 |
|
152 | const startTimer = React.useRef(null);
|
153 |
|
154 | const startTimerCommit = React.useRef(null);
|
155 | const container = React.useRef(null);
|
156 | React.useEffect(() => {
|
157 | return () => {
|
158 | clearTimeout(startTimer.current);
|
159 | };
|
160 | }, []);
|
161 | const startCommit = React.useCallback(params => {
|
162 | const {
|
163 | pulsate,
|
164 | rippleX,
|
165 | rippleY,
|
166 | rippleSize,
|
167 | cb
|
168 | } = params;
|
169 | setRipples(oldRipples => [...oldRipples, _jsx(TouchRippleRipple, {
|
170 | classes: {
|
171 | ripple: clsx(classes.ripple, touchRippleClasses.ripple),
|
172 | rippleVisible: clsx(classes.rippleVisible, touchRippleClasses.rippleVisible),
|
173 | ripplePulsate: clsx(classes.ripplePulsate, touchRippleClasses.ripplePulsate),
|
174 | child: clsx(classes.child, touchRippleClasses.child),
|
175 | childLeaving: clsx(classes.childLeaving, touchRippleClasses.childLeaving),
|
176 | childPulsate: clsx(classes.childPulsate, touchRippleClasses.childPulsate)
|
177 | },
|
178 | timeout: DURATION,
|
179 | pulsate: pulsate,
|
180 | rippleX: rippleX,
|
181 | rippleY: rippleY,
|
182 | rippleSize: rippleSize
|
183 | }, nextKey.current)]);
|
184 | nextKey.current += 1;
|
185 | rippleCallback.current = cb;
|
186 | }, [classes]);
|
187 | const start = React.useCallback((event = {}, options = {}, cb) => {
|
188 | const {
|
189 | pulsate = false,
|
190 | center = centerProp || options.pulsate,
|
191 | fakeElement = false
|
192 |
|
193 | } = options;
|
194 |
|
195 | if (event?.type === 'mousedown' && ignoringMouseDown.current) {
|
196 | ignoringMouseDown.current = false;
|
197 | return;
|
198 | }
|
199 |
|
200 | if (event?.type === 'touchstart') {
|
201 | ignoringMouseDown.current = true;
|
202 | }
|
203 |
|
204 | const element = fakeElement ? null : container.current;
|
205 | const rect = element ? element.getBoundingClientRect() : {
|
206 | width: 0,
|
207 | height: 0,
|
208 | left: 0,
|
209 | top: 0
|
210 | };
|
211 |
|
212 | let rippleX;
|
213 | let rippleY;
|
214 | let rippleSize;
|
215 |
|
216 | if (center || event === undefined || event.clientX === 0 && event.clientY === 0 || !event.clientX && !event.touches) {
|
217 | rippleX = Math.round(rect.width / 2);
|
218 | rippleY = Math.round(rect.height / 2);
|
219 | } else {
|
220 | const {
|
221 | clientX,
|
222 | clientY
|
223 | } = event.touches && event.touches.length > 0 ? event.touches[0] : event;
|
224 | rippleX = Math.round(clientX - rect.left);
|
225 | rippleY = Math.round(clientY - rect.top);
|
226 | }
|
227 |
|
228 | if (center) {
|
229 | rippleSize = Math.sqrt((2 * rect.width ** 2 + rect.height ** 2) / 3);
|
230 |
|
231 | if (rippleSize % 2 === 0) {
|
232 | rippleSize += 1;
|
233 | }
|
234 | } else {
|
235 | const sizeX = Math.max(Math.abs((element ? element.clientWidth : 0) - rippleX), rippleX) * 2 + 2;
|
236 | const sizeY = Math.max(Math.abs((element ? element.clientHeight : 0) - rippleY), rippleY) * 2 + 2;
|
237 | rippleSize = Math.sqrt(sizeX ** 2 + sizeY ** 2);
|
238 | }
|
239 |
|
240 |
|
241 | if (event?.touches) {
|
242 |
|
243 |
|
244 |
|
245 | if (startTimerCommit.current === null) {
|
246 |
|
247 | startTimerCommit.current = () => {
|
248 | startCommit({
|
249 | pulsate,
|
250 | rippleX,
|
251 | rippleY,
|
252 | rippleSize,
|
253 | cb
|
254 | });
|
255 | };
|
256 |
|
257 |
|
258 | startTimer.current = setTimeout(() => {
|
259 | if (startTimerCommit.current) {
|
260 | startTimerCommit.current();
|
261 | startTimerCommit.current = null;
|
262 | }
|
263 | }, DELAY_RIPPLE);
|
264 | }
|
265 | } else {
|
266 | startCommit({
|
267 | pulsate,
|
268 | rippleX,
|
269 | rippleY,
|
270 | rippleSize,
|
271 | cb
|
272 | });
|
273 | }
|
274 | }, [centerProp, startCommit]);
|
275 | const pulsate = React.useCallback(() => {
|
276 | start({}, {
|
277 | pulsate: true
|
278 | });
|
279 | }, [start]);
|
280 | const stop = React.useCallback((event, cb) => {
|
281 | clearTimeout(startTimer.current);
|
282 |
|
283 |
|
284 | if (event?.type === 'touchend' && startTimerCommit.current) {
|
285 | startTimerCommit.current();
|
286 | startTimerCommit.current = null;
|
287 | startTimer.current = setTimeout(() => {
|
288 | stop(event, cb);
|
289 | });
|
290 | return;
|
291 | }
|
292 |
|
293 | startTimerCommit.current = null;
|
294 | setRipples(oldRipples => {
|
295 | if (oldRipples.length > 0) {
|
296 | return oldRipples.slice(1);
|
297 | }
|
298 |
|
299 | return oldRipples;
|
300 | });
|
301 | rippleCallback.current = cb;
|
302 | }, []);
|
303 | React.useImperativeHandle(ref, () => ({
|
304 | pulsate,
|
305 | start,
|
306 | stop
|
307 | }), [pulsate, start, stop]);
|
308 | return _jsx(TouchRippleRoot, _extends({
|
309 | className: clsx(touchRippleClasses.root, classes.root, className),
|
310 | ref: container
|
311 | }, other, {
|
312 | children: _jsx(TransitionGroup, {
|
313 | component: null,
|
314 | exit: true,
|
315 | children: ripples
|
316 | })
|
317 | }));
|
318 | });
|
319 | process.env.NODE_ENV !== "production" ? TouchRipple.propTypes = {
|
320 | |
321 |
|
322 |
|
323 |
|
324 | center: PropTypes.bool,
|
325 |
|
326 | |
327 |
|
328 |
|
329 |
|
330 | classes: PropTypes.object,
|
331 |
|
332 | |
333 |
|
334 |
|
335 | className: PropTypes.string
|
336 | } : void 0;
|
337 | export default TouchRipple; |
\ | No newline at end of file |