Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F102503867
TouchRipple.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Fri, Feb 21, 09:50
Size
9 KB
Mime Type
text/x-java
Expires
Sun, Feb 23, 09:50 (2 d)
Engine
blob
Format
Raw Data
Handle
24337069
Attached To
rOACCT Open Access Compliance Check Tool (OACCT)
TouchRipple.js
View Options
import _extends from "@babel/runtime/helpers/esm/extends";
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
const _excluded = ["center", "classes", "className"];
import * as React from 'react';
import PropTypes from 'prop-types';
import { TransitionGroup } from 'react-transition-group';
import clsx from 'clsx';
import { keyframes } from '@mui/system';
import styled from '../styles/styled';
import useThemeProps from '../styles/useThemeProps';
import Ripple from './Ripple';
import touchRippleClasses from './touchRippleClasses';
import { jsx as _jsx } from "react/jsx-runtime";
const DURATION = 550;
export const DELAY_RIPPLE = 80;
const enterKeyframe = keyframes`
0% {
transform: scale(0);
opacity: 0.1;
}
100% {
transform: scale(1);
opacity: 0.3;
}
`;
const exitKeyframe = keyframes`
0% {
opacity: 1;
}
100% {
opacity: 0;
}
`;
const pulsateKeyframe = keyframes`
0% {
transform: scale(1);
}
50% {
transform: scale(0.92);
}
100% {
transform: scale(1);
}
`;
export const TouchRippleRoot = styled('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.
export const TouchRippleRipple = styled(Ripple, {
name: 'MuiTouchRipple',
slot: 'Ripple'
})`
opacity: 0;
position: absolute;
&.${touchRippleClasses.rippleVisible} {
opacity: 0.3;
transform: scale(1);
animation-name: ${enterKeyframe};
animation-duration: ${DURATION}ms;
animation-timing-function: ${({
theme
}) => theme.transitions.easing.easeInOut};
}
&.${touchRippleClasses.ripplePulsate} {
animation-duration: ${({
theme
}) => theme.transitions.duration.shorter}ms;
}
& .${touchRippleClasses.child} {
opacity: 1;
display: block;
width: 100%;
height: 100%;
border-radius: 50%;
background-color: currentColor;
}
& .${touchRippleClasses.childLeaving} {
opacity: 0;
animation-name: ${exitKeyframe};
animation-duration: ${DURATION}ms;
animation-timing-function: ${({
theme
}) => theme.transitions.easing.easeInOut};
}
& .${touchRippleClasses.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 = useThemeProps({
props: inProps,
name: 'MuiTouchRipple'
});
const {
center: centerProp = false,
classes = {},
className
} = props,
other = _objectWithoutPropertiesLoose(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 = React.useRef(null);
// This is the hook called once the previous timeout is ready.
const startTimerCommit = React.useRef(null);
const container = React.useRef(null);
React.useEffect(() => {
return () => {
clearTimeout(startTimer.current);
};
}, []);
const startCommit = React.useCallback(params => {
const {
pulsate,
rippleX,
rippleY,
rippleSize,
cb
} = params;
setRipples(oldRipples => [...oldRipples, /*#__PURE__*/_jsx(TouchRippleRipple, {
classes: {
ripple: clsx(classes.ripple, touchRippleClasses.ripple),
rippleVisible: clsx(classes.rippleVisible, touchRippleClasses.rippleVisible),
ripplePulsate: clsx(classes.ripplePulsate, touchRippleClasses.ripplePulsate),
child: clsx(classes.child, touchRippleClasses.child),
childLeaving: clsx(classes.childLeaving, touchRippleClasses.childLeaving),
childPulsate: clsx(classes.childPulsate, touchRippleClasses.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?.type === 'mousedown' && ignoringMouseDown.current) {
ignoringMouseDown.current = false;
return;
}
if (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?.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.
startTimer.current = setTimeout(() => {
if (startTimerCommit.current) {
startTimerCommit.current();
startTimerCommit.current = null;
}
}, DELAY_RIPPLE); // We have to make a tradeoff with this value.
}
} else {
startCommit({
pulsate,
rippleX,
rippleY,
rippleSize,
cb
});
}
}, [centerProp, startCommit]);
const pulsate = React.useCallback(() => {
start({}, {
pulsate: true
});
}, [start]);
const stop = React.useCallback((event, cb) => {
clearTimeout(startTimer.current);
// The touch interaction occurs too quickly.
// We still want to show ripple effect.
if (event?.type === 'touchend' && startTimerCommit.current) {
startTimerCommit.current();
startTimerCommit.current = null;
startTimer.current = setTimeout(() => {
stop(event, cb);
});
return;
}
startTimerCommit.current = null;
setRipples(oldRipples => {
if (oldRipples.length > 0) {
return oldRipples.slice(1);
}
return oldRipples;
});
rippleCallback.current = cb;
}, []);
React.useImperativeHandle(ref, () => ({
pulsate,
start,
stop
}), [pulsate, start, stop]);
return /*#__PURE__*/_jsx(TouchRippleRoot, _extends({
className: clsx(touchRippleClasses.root, classes.root, className),
ref: container
}, other, {
children: /*#__PURE__*/_jsx(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.bool,
/**
* Override or extend the styles applied to the component.
* See [CSS API](#css) below for more details.
*/
classes: PropTypes.object,
/**
* @ignore
*/
className: PropTypes.string
} : void 0;
export default TouchRipple;
Event Timeline
Log In to Comment