UNPKG

10.4 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
4
5var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
6
7Object.defineProperty(exports, "__esModule", {
8 value: true
9});
10exports.default = exports.styles = exports.DELAY_RIPPLE = void 0;
11
12var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
13
14var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
15
16var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
17
18var React = _interopRequireWildcard(require("react"));
19
20var _propTypes = _interopRequireDefault(require("prop-types"));
21
22var _reactTransitionGroup = require("react-transition-group");
23
24var _clsx = _interopRequireDefault(require("clsx"));
25
26var _withStyles = _interopRequireDefault(require("../styles/withStyles"));
27
28var _Ripple = _interopRequireDefault(require("./Ripple"));
29
30var DURATION = 550;
31var DELAY_RIPPLE = 80;
32exports.DELAY_RIPPLE = DELAY_RIPPLE;
33
34var styles = function styles(theme) {
35 return {
36 /* Styles applied to the root element. */
37 root: {
38 overflow: 'hidden',
39 pointerEvents: 'none',
40 position: 'absolute',
41 zIndex: 0,
42 top: 0,
43 right: 0,
44 bottom: 0,
45 left: 0,
46 borderRadius: 'inherit'
47 },
48
49 /* Styles applied to the internal `Ripple` components `ripple` class. */
50 ripple: {
51 opacity: 0,
52 position: 'absolute'
53 },
54
55 /* Styles applied to the internal `Ripple` components `rippleVisible` class. */
56 rippleVisible: {
57 opacity: 0.3,
58 transform: 'scale(1)',
59 animation: "$enter ".concat(DURATION, "ms ").concat(theme.transitions.easing.easeInOut)
60 },
61
62 /* Styles applied to the internal `Ripple` components `ripplePulsate` class. */
63 ripplePulsate: {
64 animationDuration: "".concat(theme.transitions.duration.shorter, "ms")
65 },
66
67 /* Styles applied to the internal `Ripple` components `child` class. */
68 child: {
69 opacity: 1,
70 display: 'block',
71 width: '100%',
72 height: '100%',
73 borderRadius: '50%',
74 backgroundColor: 'currentColor'
75 },
76
77 /* Styles applied to the internal `Ripple` components `childLeaving` class. */
78 childLeaving: {
79 opacity: 0,
80 animation: "$exit ".concat(DURATION, "ms ").concat(theme.transitions.easing.easeInOut)
81 },
82
83 /* Styles applied to the internal `Ripple` components `childPulsate` class. */
84 childPulsate: {
85 position: 'absolute',
86 left: 0,
87 top: 0,
88 animation: "$pulsate 2500ms ".concat(theme.transitions.easing.easeInOut, " 200ms infinite")
89 },
90 '@keyframes enter': {
91 '0%': {
92 transform: 'scale(0)',
93 opacity: 0.1
94 },
95 '100%': {
96 transform: 'scale(1)',
97 opacity: 0.3
98 }
99 },
100 '@keyframes exit': {
101 '0%': {
102 opacity: 1
103 },
104 '100%': {
105 opacity: 0
106 }
107 },
108 '@keyframes pulsate': {
109 '0%': {
110 transform: 'scale(1)'
111 },
112 '50%': {
113 transform: 'scale(0.92)'
114 },
115 '100%': {
116 transform: 'scale(1)'
117 }
118 }
119 };
120};
121/**
122 * @ignore - internal component.
123 *
124 * TODO v5: Make private
125 */
126
127
128exports.styles = styles;
129var TouchRipple = /*#__PURE__*/React.forwardRef(function TouchRipple(props, ref) {
130 var _props$center = props.center,
131 centerProp = _props$center === void 0 ? false : _props$center,
132 classes = props.classes,
133 className = props.className,
134 other = (0, _objectWithoutProperties2.default)(props, ["center", "classes", "className"]);
135
136 var _React$useState = React.useState([]),
137 ripples = _React$useState[0],
138 setRipples = _React$useState[1];
139
140 var nextKey = React.useRef(0);
141 var rippleCallback = React.useRef(null);
142 React.useEffect(function () {
143 if (rippleCallback.current) {
144 rippleCallback.current();
145 rippleCallback.current = null;
146 }
147 }, [ripples]); // Used to filter out mouse emulated events on mobile.
148
149 var ignoringMouseDown = React.useRef(false); // We use a timer in order to only show the ripples for touch "click" like events.
150 // We don't want to display the ripple for touch scroll events.
151
152 var startTimer = React.useRef(null); // This is the hook called once the previous timeout is ready.
153
154 var startTimerCommit = React.useRef(null);
155 var container = React.useRef(null);
156 React.useEffect(function () {
157 return function () {
158 clearTimeout(startTimer.current);
159 };
160 }, []);
161 var startCommit = React.useCallback(function (params) {
162 var pulsate = params.pulsate,
163 rippleX = params.rippleX,
164 rippleY = params.rippleY,
165 rippleSize = params.rippleSize,
166 cb = params.cb;
167 setRipples(function (oldRipples) {
168 return [].concat((0, _toConsumableArray2.default)(oldRipples), [/*#__PURE__*/React.createElement(_Ripple.default, {
169 key: nextKey.current,
170 classes: classes,
171 timeout: DURATION,
172 pulsate: pulsate,
173 rippleX: rippleX,
174 rippleY: rippleY,
175 rippleSize: rippleSize
176 })]);
177 });
178 nextKey.current += 1;
179 rippleCallback.current = cb;
180 }, [classes]);
181 var start = React.useCallback(function () {
182 var event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
183 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
184 var cb = arguments.length > 2 ? arguments[2] : undefined;
185 var _options$pulsate = options.pulsate,
186 pulsate = _options$pulsate === void 0 ? false : _options$pulsate,
187 _options$center = options.center,
188 center = _options$center === void 0 ? centerProp || options.pulsate : _options$center,
189 _options$fakeElement = options.fakeElement,
190 fakeElement = _options$fakeElement === void 0 ? false : _options$fakeElement;
191
192 if (event.type === 'mousedown' && ignoringMouseDown.current) {
193 ignoringMouseDown.current = false;
194 return;
195 }
196
197 if (event.type === 'touchstart') {
198 ignoringMouseDown.current = true;
199 }
200
201 var element = fakeElement ? null : container.current;
202 var rect = element ? element.getBoundingClientRect() : {
203 width: 0,
204 height: 0,
205 left: 0,
206 top: 0
207 }; // Get the size of the ripple
208
209 var rippleX;
210 var rippleY;
211 var rippleSize;
212
213 if (center || event.clientX === 0 && event.clientY === 0 || !event.clientX && !event.touches) {
214 rippleX = Math.round(rect.width / 2);
215 rippleY = Math.round(rect.height / 2);
216 } else {
217 var _ref = event.touches ? event.touches[0] : event,
218 clientX = _ref.clientX,
219 clientY = _ref.clientY;
220
221 rippleX = Math.round(clientX - rect.left);
222 rippleY = Math.round(clientY - rect.top);
223 }
224
225 if (center) {
226 rippleSize = Math.sqrt((2 * Math.pow(rect.width, 2) + Math.pow(rect.height, 2)) / 3); // For some reason the animation is broken on Mobile Chrome if the size if even.
227
228 if (rippleSize % 2 === 0) {
229 rippleSize += 1;
230 }
231 } else {
232 var sizeX = Math.max(Math.abs((element ? element.clientWidth : 0) - rippleX), rippleX) * 2 + 2;
233 var sizeY = Math.max(Math.abs((element ? element.clientHeight : 0) - rippleY), rippleY) * 2 + 2;
234 rippleSize = Math.sqrt(Math.pow(sizeX, 2) + Math.pow(sizeY, 2));
235 } // Touche devices
236
237
238 if (event.touches) {
239 // check that this isn't another touchstart due to multitouch
240 // otherwise we will only clear a single timer when unmounting while two
241 // are running
242 if (startTimerCommit.current === null) {
243 // Prepare the ripple effect.
244 startTimerCommit.current = function () {
245 startCommit({
246 pulsate: pulsate,
247 rippleX: rippleX,
248 rippleY: rippleY,
249 rippleSize: rippleSize,
250 cb: cb
251 });
252 }; // Delay the execution of the ripple effect.
253
254
255 startTimer.current = setTimeout(function () {
256 if (startTimerCommit.current) {
257 startTimerCommit.current();
258 startTimerCommit.current = null;
259 }
260 }, DELAY_RIPPLE); // We have to make a tradeoff with this value.
261 }
262 } else {
263 startCommit({
264 pulsate: pulsate,
265 rippleX: rippleX,
266 rippleY: rippleY,
267 rippleSize: rippleSize,
268 cb: cb
269 });
270 }
271 }, [centerProp, startCommit]);
272 var pulsate = React.useCallback(function () {
273 start({}, {
274 pulsate: true
275 });
276 }, [start]);
277 var stop = React.useCallback(function (event, cb) {
278 clearTimeout(startTimer.current); // The touch interaction occurs too quickly.
279 // We still want to show ripple effect.
280
281 if (event.type === 'touchend' && startTimerCommit.current) {
282 event.persist();
283 startTimerCommit.current();
284 startTimerCommit.current = null;
285 startTimer.current = setTimeout(function () {
286 stop(event, cb);
287 });
288 return;
289 }
290
291 startTimerCommit.current = null;
292 setRipples(function (oldRipples) {
293 if (oldRipples.length > 0) {
294 return oldRipples.slice(1);
295 }
296
297 return oldRipples;
298 });
299 rippleCallback.current = cb;
300 }, []);
301 React.useImperativeHandle(ref, function () {
302 return {
303 pulsate: pulsate,
304 start: start,
305 stop: stop
306 };
307 }, [pulsate, start, stop]);
308 return /*#__PURE__*/React.createElement("span", (0, _extends2.default)({
309 className: (0, _clsx.default)(classes.root, className),
310 ref: container
311 }, other), /*#__PURE__*/React.createElement(_reactTransitionGroup.TransitionGroup, {
312 component: null,
313 exit: true
314 }, ripples));
315});
316process.env.NODE_ENV !== "production" ? TouchRipple.propTypes = {
317 /**
318 * If `true`, the ripple starts at the center of the component
319 * rather than at the point of interaction.
320 */
321 center: _propTypes.default.bool,
322
323 /**
324 * Override or extend the styles applied to the component.
325 * See [CSS API](#css) below for more details.
326 */
327 classes: _propTypes.default.object.isRequired,
328
329 /**
330 * @ignore
331 */
332 className: _propTypes.default.string
333} : void 0;
334
335var _default = (0, _withStyles.default)(styles, {
336 flip: false,
337 name: 'MuiTouchRipple'
338})( /*#__PURE__*/React.memo(TouchRipple));
339
340exports.default = _default;
\No newline at end of file