UNPKG

material-ui

Version:

Material Design UI components built with React

196 lines (167 loc) 4.61 kB
let React = require('react'); let StylePropable = require('../mixins/style-propable'); let Dom = require('../utils/dom'); let RippleCircle = require('./circle-ripple'); let TouchRipple = React.createClass({ mixins: [StylePropable], propTypes: { centerRipple: React.PropTypes.bool, color: React.PropTypes.string, opacity: React.PropTypes.number, }, getInitialState() { return { ripples: [{ key: 0, started: false, ending: false, }], }; }, render() { let styles = this.mergeAndPrefix({ height: '100%', width: '100%', position: 'absolute', top: 0, left: 0, overflow: 'hidden', }, this.props.style); return ( <div onMouseUp={this._handleMouseUp} onMouseDown={this._handleMouseDown} onMouseOut={this._handleMouseOut} onTouchStart={this._handleTouchStart} onTouchEnd={this._handleTouchEnd}> <div style={styles}> {this._getRippleElements()} </div> {this.props.children} </div> ); }, start(e, isRippleTouchGenerated) { let ripples = this.state.ripples; let nextKey = ripples[ripples.length-1].key + 1; let style = !this.props.centerRipple ? this._getRippleStyle(e) : {}; let ripple; //Do nothing if we're starting a click-event-generated ripple //while having touch-generated ripples if (!isRippleTouchGenerated) { for (let i = 0; i < ripples.length; i++) { if (ripples[i].touchGenerated) return; } } //Start the next unstarted ripple for (let i = 0; i < ripples.length; i++) { ripple = ripples[i]; if (!ripple.started) { ripple.started = true; ripple.touchGenerated = isRippleTouchGenerated; ripple.style = style; break; } } //Add an unstarted ripple at the end ripples.push({ key: nextKey, started: false, ending: false, }); //Re-render this.setState({ ripples: ripples, }); }, end() { let ripples = this.state.ripples; let ripple; let endingRipple; //End the the next un-ended ripple for (let i = 0; i < ripples.length; i++) { ripple = ripples[i]; if (ripple.started && !ripple.ending) { ripple.ending = true; endingRipple = ripple; break; } } //Only update if a ripple was found if (endingRipple) { //Re-render this.setState({ ripples: ripples, }); //Wait 2 seconds and remove the ripple from DOM setTimeout(() => { ripples.shift(); if (this.isMounted()) { this.setState({ ripples: ripples, }); } }, 2000); } }, _handleMouseDown(e) { //only listen to left clicks if (e.button === 0) this.start(e, false); }, _handleMouseUp() { this.end(); }, _handleMouseOut() { this.end(); }, _handleTouchStart(e) { this.start(e, true); }, _handleTouchEnd() { this.end(); }, _getRippleStyle(e) { let style = {}; let el = React.findDOMNode(this); let elHeight = el.offsetHeight; let elWidth = el.offsetWidth; let offset = Dom.offset(el); let isTouchEvent = e.touches && e.touches.length; let pageX = isTouchEvent ? e.touches[0].pageX : e.pageX; let pageY = isTouchEvent ? e.touches[0].pageY : e.pageY; let pointerX = pageX - offset.left; let pointerY = pageY - offset.top; let topLeftDiag = this._calcDiag(pointerX, pointerY); let topRightDiag = this._calcDiag(elWidth - pointerX, pointerY); let botRightDiag = this._calcDiag(elWidth - pointerX, elHeight - pointerY); let botLeftDiag = this._calcDiag(pointerX, elHeight - pointerY); let rippleRadius = Math.max( topLeftDiag, topRightDiag, botRightDiag, botLeftDiag ); let rippleSize = rippleRadius * 2; let left = pointerX - rippleRadius; let top = pointerY - rippleRadius; style.height = rippleSize + 'px'; style.width = rippleSize + 'px'; style.top = top + 'px'; style.left = left + 'px'; return style; }, _calcDiag(a, b) { return Math.sqrt((a * a) + (b * b)); }, _getRippleElements() { return this.state.ripples.map((ripple) => { return ( <RippleCircle key={ripple.key} started={ripple.started} ending={ripple.ending} style={ripple.style} color={this.props.color} opacity={this.props.opacity} /> ); }); }, }); module.exports = TouchRipple;