260 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			260 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 | 
						|
 | 
						|
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
 | 
						|
 | 
						|
//  weak
 | 
						|
 | 
						|
import React from 'react';
 | 
						|
import ReactDOM from 'react-dom';
 | 
						|
import TransitionGroup from 'react-transition-group/TransitionGroup';
 | 
						|
import classNames from 'classnames';
 | 
						|
import withStyles from '../styles/withStyles';
 | 
						|
import Ripple from './Ripple';
 | 
						|
 | 
						|
const DURATION = 550;
 | 
						|
export const DELAY_RIPPLE = 80;
 | 
						|
 | 
						|
export const styles = theme => ({
 | 
						|
  root: {
 | 
						|
    display: 'block',
 | 
						|
    position: 'absolute',
 | 
						|
    overflow: 'hidden',
 | 
						|
    borderRadius: 'inherit',
 | 
						|
    width: '100%',
 | 
						|
    height: '100%',
 | 
						|
    left: 0,
 | 
						|
    top: 0,
 | 
						|
    pointerEvents: 'none',
 | 
						|
    zIndex: 0
 | 
						|
  },
 | 
						|
  wrapper: {
 | 
						|
    opacity: 1
 | 
						|
  },
 | 
						|
  wrapperLeaving: {
 | 
						|
    opacity: 0,
 | 
						|
    animation: `mui-ripple-exit ${DURATION}ms ${theme.transitions.easing.easeInOut}`
 | 
						|
  },
 | 
						|
  wrapperPulsating: {
 | 
						|
    position: 'absolute',
 | 
						|
    left: 0,
 | 
						|
    top: 0,
 | 
						|
    display: 'block',
 | 
						|
    width: '100%',
 | 
						|
    height: '100%',
 | 
						|
    animation: `mui-ripple-pulsate 1500ms ${theme.transitions.easing.easeInOut} 200ms infinite`,
 | 
						|
    rippleVisible: {
 | 
						|
      opacity: 0.2
 | 
						|
    }
 | 
						|
  },
 | 
						|
  '@keyframes mui-ripple-enter': {
 | 
						|
    '0%': {
 | 
						|
      transform: 'scale(0)'
 | 
						|
    },
 | 
						|
    '100%': {
 | 
						|
      transform: 'scale(1)'
 | 
						|
    }
 | 
						|
  },
 | 
						|
  '@keyframes mui-ripple-exit': {
 | 
						|
    '0%': {
 | 
						|
      opacity: 1
 | 
						|
    },
 | 
						|
    '100%': {
 | 
						|
      opacity: 0
 | 
						|
    }
 | 
						|
  },
 | 
						|
  '@keyframes mui-ripple-pulsate': {
 | 
						|
    '0%': {
 | 
						|
      transform: 'scale(1)'
 | 
						|
    },
 | 
						|
    '50%': {
 | 
						|
      transform: 'scale(0.9)'
 | 
						|
    },
 | 
						|
    '100%': {
 | 
						|
      transform: 'scale(1)'
 | 
						|
    }
 | 
						|
  },
 | 
						|
  ripple: {
 | 
						|
    width: 50,
 | 
						|
    height: 50,
 | 
						|
    left: 0,
 | 
						|
    top: 0,
 | 
						|
    opacity: 0,
 | 
						|
    position: 'absolute',
 | 
						|
    borderRadius: '50%',
 | 
						|
    background: 'currentColor'
 | 
						|
  },
 | 
						|
  rippleVisible: {
 | 
						|
    opacity: 0.3,
 | 
						|
    transform: 'scale(1)',
 | 
						|
    animation: `mui-ripple-enter ${DURATION}ms ${theme.transitions.easing.easeInOut}`
 | 
						|
  },
 | 
						|
  rippleFast: {
 | 
						|
    animationDuration: '200ms'
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
/**
 | 
						|
 * @ignore - internal component.
 | 
						|
 */
 | 
						|
class TouchRipple extends React.Component {
 | 
						|
  constructor(...args) {
 | 
						|
    var _temp;
 | 
						|
 | 
						|
    return _temp = super(...args), this.state = {
 | 
						|
      nextKey: 0,
 | 
						|
      ripples: []
 | 
						|
    }, this.ignoringMouseDown = false, this.startTimer = null, this.startTimerCommit = null, this.pulsate = () => {
 | 
						|
      this.start({}, { pulsate: true });
 | 
						|
    }, this.start = (event = {}, options = {}, cb) => {
 | 
						|
      const {
 | 
						|
        pulsate = false,
 | 
						|
        center = this.props.center || options.pulsate,
 | 
						|
        fakeElement = false // For test purposes
 | 
						|
      } = options;
 | 
						|
 | 
						|
      if (event.type === 'mousedown' && this.ignoringMouseDown) {
 | 
						|
        this.ignoringMouseDown = false;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      if (event.type === 'touchstart') {
 | 
						|
        this.ignoringMouseDown = true;
 | 
						|
      }
 | 
						|
 | 
						|
      const element = fakeElement ? null : ReactDOM.findDOMNode(this);
 | 
						|
      const rect = element ? // $FlowFixMe
 | 
						|
      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.clientX === 0 && event.clientY === 0 || !event.clientX && !event.touches) {
 | 
						|
        rippleX = Math.round(rect.width / 2);
 | 
						|
        rippleY = Math.round(rect.height / 2);
 | 
						|
      } else {
 | 
						|
        const clientX = event.clientX ? event.clientX : event.touches[0].clientX;
 | 
						|
        const clientY = event.clientY ? event.clientY : event.touches[0].clientY;
 | 
						|
        rippleX = Math.round(clientX - rect.left);
 | 
						|
        rippleY = Math.round(clientY - rect.top);
 | 
						|
      }
 | 
						|
 | 
						|
      if (center) {
 | 
						|
        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.
 | 
						|
        if (rippleSize % 2 === 0) {
 | 
						|
          rippleSize += 1;
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        const sizeX = Math.max(
 | 
						|
        // $FlowFixMe
 | 
						|
        Math.abs((element ? element.clientWidth : 0) - rippleX), rippleX) * 2 + 2;
 | 
						|
        const sizeY = Math.max(
 | 
						|
        // $FlowFixMe
 | 
						|
        Math.abs((element ? element.clientHeight : 0) - rippleY), rippleY) * 2 + 2;
 | 
						|
        rippleSize = Math.sqrt(Math.pow(sizeX, 2) + Math.pow(sizeY, 2));
 | 
						|
      }
 | 
						|
 | 
						|
      // Touche devices
 | 
						|
      if (event.touches) {
 | 
						|
        // Prepare the ripple effect.
 | 
						|
        this.startTimerCommit = () => {
 | 
						|
          this.startCommit({ pulsate, rippleX, rippleY, rippleSize, cb });
 | 
						|
        };
 | 
						|
        // Deplay the execution of the ripple effect.
 | 
						|
        this.startTimer = setTimeout(() => {
 | 
						|
          this.startTimerCommit();
 | 
						|
          this.startTimerCommit = null;
 | 
						|
        }, DELAY_RIPPLE); // We have to make a tradeoff with this value.
 | 
						|
      } else {
 | 
						|
        this.startCommit({ pulsate, rippleX, rippleY, rippleSize, cb });
 | 
						|
      }
 | 
						|
    }, this.startCommit = params => {
 | 
						|
      const { pulsate, rippleX, rippleY, rippleSize, cb } = params;
 | 
						|
      let ripples = this.state.ripples;
 | 
						|
 | 
						|
      // Add a ripple to the ripples array
 | 
						|
      ripples = [...ripples, React.createElement(Ripple, {
 | 
						|
        key: this.state.nextKey,
 | 
						|
        classes: this.props.classes,
 | 
						|
        timeout: {
 | 
						|
          exit: DURATION,
 | 
						|
          enter: DURATION
 | 
						|
        },
 | 
						|
        pulsate: pulsate,
 | 
						|
        rippleX: rippleX,
 | 
						|
        rippleY: rippleY,
 | 
						|
        rippleSize: rippleSize
 | 
						|
      })];
 | 
						|
 | 
						|
      this.setState({
 | 
						|
        nextKey: this.state.nextKey + 1,
 | 
						|
        ripples
 | 
						|
      }, cb);
 | 
						|
    }, this.stop = (event, cb) => {
 | 
						|
      clearTimeout(this.startTimer);
 | 
						|
      const { ripples } = this.state;
 | 
						|
 | 
						|
      // The touch interaction occures to quickly.
 | 
						|
      // We still want to show ripple effect.
 | 
						|
      if (event.type === 'touchend' && this.startTimerCommit) {
 | 
						|
        event.persist();
 | 
						|
        this.startTimerCommit();
 | 
						|
        this.startTimerCommit = null;
 | 
						|
        this.startTimer = setTimeout(() => {
 | 
						|
          this.stop(event, cb);
 | 
						|
        }, 0);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      this.startTimerCommit = null;
 | 
						|
 | 
						|
      if (ripples && ripples.length) {
 | 
						|
        this.setState({
 | 
						|
          ripples: ripples.slice(1)
 | 
						|
        }, cb);
 | 
						|
      }
 | 
						|
    }, _temp;
 | 
						|
  }
 | 
						|
 | 
						|
  componentWillUnmount() {
 | 
						|
    clearTimeout(this.startTimer);
 | 
						|
  }
 | 
						|
 | 
						|
  // Used to filter out mouse emulated events on mobile.
 | 
						|
 | 
						|
  // 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.
 | 
						|
 | 
						|
  // This is the hook called once the previous timeout is ready.
 | 
						|
 | 
						|
 | 
						|
  render() {
 | 
						|
    const _props = this.props,
 | 
						|
          { center, classes, className } = _props,
 | 
						|
          other = _objectWithoutProperties(_props, ['center', 'classes', 'className']);
 | 
						|
 | 
						|
    return React.createElement(
 | 
						|
      TransitionGroup,
 | 
						|
      _extends({
 | 
						|
        component: 'span',
 | 
						|
        enter: true,
 | 
						|
        exit: true,
 | 
						|
        className: classNames(classes.root, className)
 | 
						|
      }, other),
 | 
						|
      this.state.ripples
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
TouchRipple.defaultProps = {
 | 
						|
  center: false
 | 
						|
};
 | 
						|
export default withStyles(styles, { flip: false, name: 'MuiTouchRipple' })(TouchRipple); |