"use strict"; 'use client'; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.TouchRippleRoot = exports.TouchRippleRipple = exports.DELAY_RIPPLE = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose")); var React = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _reactTransitionGroup = require("react-transition-group"); var _clsx = _interopRequireDefault(require("clsx")); var _system = require("@mui/system"); var _useTimeout = _interopRequireDefault(require("@mui/utils/useTimeout")); var _styled = _interopRequireDefault(require("../styles/styled")); var _useThemeProps = _interopRequireDefault(require("../styles/useThemeProps")); var _Ripple = _interopRequireDefault(require("./Ripple")); var _touchRippleClasses = _interopRequireDefault(require("./touchRippleClasses")); var _jsxRuntime = require("react/jsx-runtime"); const _excluded = ["center", "classes", "className"]; function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } const DURATION = 550; const DELAY_RIPPLE = exports.DELAY_RIPPLE = 80; const enterKeyframe = (0, _system.keyframes)` 0% { transform: scale(0); opacity: 0.1; } 100% { transform: scale(1); opacity: 0.3; } `; const exitKeyframe = (0, _system.keyframes)` 0% { opacity: 1; } 100% { opacity: 0; } `; const pulsateKeyframe = (0, _system.keyframes)` 0% { transform: scale(1); } 50% { transform: scale(0.92); } 100% { transform: scale(1); } `; const TouchRippleRoot = exports.TouchRippleRoot = (0, _styled.default)('span', { name: 'MuiTouchRipple', slot: 'Root' })({ overflow: 'hidden', pointerEvents: 'none', position: 'absolute', zIndex: 0, top: 0, right: 0, bottom: 0, left: 0, borderRadius: 'inherit' }); // This `styled()` function invokes keyframes. `styled-components` only supports keyframes // in string templates. Do not convert these styles in JS object as it will break. const TouchRippleRipple = exports.TouchRippleRipple = (0, _styled.default)(_Ripple.default, { name: 'MuiTouchRipple', slot: 'Ripple' })` opacity: 0; position: absolute; &.${_touchRippleClasses.default.rippleVisible} { opacity: 0.3; transform: scale(1); animation-name: ${enterKeyframe}; animation-duration: ${DURATION}ms; animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut}; } &.${_touchRippleClasses.default.ripplePulsate} { animation-duration: ${({ theme }) => theme.transitions.duration.shorter}ms; } & .${_touchRippleClasses.default.child} { opacity: 1; display: block; width: 100%; height: 100%; border-radius: 50%; background-color: currentColor; } & .${_touchRippleClasses.default.childLeaving} { opacity: 0; animation-name: ${exitKeyframe}; animation-duration: ${DURATION}ms; animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut}; } & .${_touchRippleClasses.default.childPulsate} { position: absolute; /* @noflip */ left: 0px; top: 0; animation-name: ${pulsateKeyframe}; animation-duration: 2500ms; animation-timing-function: ${({ theme }) => theme.transitions.easing.easeInOut}; animation-iteration-count: infinite; animation-delay: 200ms; } `; /** * @ignore - internal component. * * TODO v5: Make private */ const TouchRipple = /*#__PURE__*/React.forwardRef(function TouchRipple(inProps, ref) { const props = (0, _useThemeProps.default)({ props: inProps, name: 'MuiTouchRipple' }); const { center: centerProp = false, classes = {}, className } = props, other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded); const [ripples, setRipples] = React.useState([]); const nextKey = React.useRef(0); const rippleCallback = React.useRef(null); React.useEffect(() => { if (rippleCallback.current) { rippleCallback.current(); rippleCallback.current = null; } }, [ripples]); // Used to filter out mouse emulated events on mobile. const ignoringMouseDown = React.useRef(false); // We use a timer in order to only show the ripples for touch "click" like events. // We don't want to display the ripple for touch scroll events. const startTimer = (0, _useTimeout.default)(); // This is the hook called once the previous timeout is ready. const startTimerCommit = React.useRef(null); const container = React.useRef(null); const startCommit = React.useCallback(params => { const { pulsate, rippleX, rippleY, rippleSize, cb } = params; setRipples(oldRipples => [...oldRipples, /*#__PURE__*/(0, _jsxRuntime.jsx)(TouchRippleRipple, { classes: { ripple: (0, _clsx.default)(classes.ripple, _touchRippleClasses.default.ripple), rippleVisible: (0, _clsx.default)(classes.rippleVisible, _touchRippleClasses.default.rippleVisible), ripplePulsate: (0, _clsx.default)(classes.ripplePulsate, _touchRippleClasses.default.ripplePulsate), child: (0, _clsx.default)(classes.child, _touchRippleClasses.default.child), childLeaving: (0, _clsx.default)(classes.childLeaving, _touchRippleClasses.default.childLeaving), childPulsate: (0, _clsx.default)(classes.childPulsate, _touchRippleClasses.default.childPulsate) }, timeout: DURATION, pulsate: pulsate, rippleX: rippleX, rippleY: rippleY, rippleSize: rippleSize }, nextKey.current)]); nextKey.current += 1; rippleCallback.current = cb; }, [classes]); const start = React.useCallback((event = {}, options = {}, cb = () => {}) => { const { pulsate = false, center = centerProp || options.pulsate, fakeElement = false // For test purposes } = options; if ((event == null ? void 0 : event.type) === 'mousedown' && ignoringMouseDown.current) { ignoringMouseDown.current = false; return; } if ((event == null ? void 0 : event.type) === 'touchstart') { ignoringMouseDown.current = true; } const element = fakeElement ? null : container.current; const rect = element ? element.getBoundingClientRect() : { width: 0, height: 0, left: 0, top: 0 }; // Get the size of the ripple let rippleX; let rippleY; let rippleSize; if (center || event === undefined || event.clientX === 0 && event.clientY === 0 || !event.clientX && !event.touches) { rippleX = Math.round(rect.width / 2); rippleY = Math.round(rect.height / 2); } else { const { clientX, clientY } = event.touches && event.touches.length > 0 ? event.touches[0] : event; rippleX = Math.round(clientX - rect.left); rippleY = Math.round(clientY - rect.top); } if (center) { rippleSize = Math.sqrt((2 * rect.width ** 2 + rect.height ** 2) / 3); // For some reason the animation is broken on Mobile Chrome if the size is even. if (rippleSize % 2 === 0) { rippleSize += 1; } } else { const sizeX = Math.max(Math.abs((element ? element.clientWidth : 0) - rippleX), rippleX) * 2 + 2; const sizeY = Math.max(Math.abs((element ? element.clientHeight : 0) - rippleY), rippleY) * 2 + 2; rippleSize = Math.sqrt(sizeX ** 2 + sizeY ** 2); } // Touche devices if (event != null && event.touches) { // check that this isn't another touchstart due to multitouch // otherwise we will only clear a single timer when unmounting while two // are running if (startTimerCommit.current === null) { // Prepare the ripple effect. startTimerCommit.current = () => { startCommit({ pulsate, rippleX, rippleY, rippleSize, cb }); }; // Delay the execution of the ripple effect. // We have to make a tradeoff with this delay value. startTimer.start(DELAY_RIPPLE, () => { if (startTimerCommit.current) { startTimerCommit.current(); startTimerCommit.current = null; } }); } } else { startCommit({ pulsate, rippleX, rippleY, rippleSize, cb }); } }, [centerProp, startCommit, startTimer]); const pulsate = React.useCallback(() => { start({}, { pulsate: true }); }, [start]); const stop = React.useCallback((event, cb) => { startTimer.clear(); // The touch interaction occurs too quickly. // We still want to show ripple effect. if ((event == null ? void 0 : event.type) === 'touchend' && startTimerCommit.current) { startTimerCommit.current(); startTimerCommit.current = null; startTimer.start(0, () => { stop(event, cb); }); return; } startTimerCommit.current = null; setRipples(oldRipples => { if (oldRipples.length > 0) { return oldRipples.slice(1); } return oldRipples; }); rippleCallback.current = cb; }, [startTimer]); React.useImperativeHandle(ref, () => ({ pulsate, start, stop }), [pulsate, start, stop]); return /*#__PURE__*/(0, _jsxRuntime.jsx)(TouchRippleRoot, (0, _extends2.default)({ className: (0, _clsx.default)(_touchRippleClasses.default.root, classes.root, className), ref: container }, other, { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactTransitionGroup.TransitionGroup, { component: null, exit: true, children: ripples }) })); }); process.env.NODE_ENV !== "production" ? TouchRipple.propTypes = { /** * If `true`, the ripple starts at the center of the component * rather than at the point of interaction. */ center: _propTypes.default.bool, /** * Override or extend the styles applied to the component. */ classes: _propTypes.default.object, /** * @ignore */ className: _propTypes.default.string } : void 0; var _default = exports.default = TouchRipple;