react-toolbox/components/ripple/Ripple.js

156 lines
5.1 KiB
JavaScript
Raw Normal View History

import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
2016-05-16 15:17:26 +03:00
import classnames from 'classnames';
import { themr } from 'react-css-themr';
2016-05-28 16:37:10 +03:00
import { RIPPLE } from '../identifiers.js';
2016-04-12 22:41:28 +03:00
import events from '../utils/events';
2015-12-07 04:34:12 +03:00
import prefixer from '../utils/prefixer';
2015-12-07 04:34:12 +03:00
const defaults = {
centered: false,
className: '',
2016-05-28 16:37:10 +03:00
spread: 2,
theme: {}
2015-12-07 04:34:12 +03:00
};
2016-05-28 16:37:10 +03:00
const rippleFactory = (options = {}) => {
2015-12-07 04:34:12 +03:00
const {
centered: defaultCentered,
className: defaultClassName,
2016-01-26 16:58:28 +03:00
spread: defaultSpread,
2016-05-28 16:37:10 +03:00
theme: defaultTheme,
2016-01-26 16:58:28 +03:00
...props
2015-12-07 04:34:12 +03:00
} = {...defaults, ...options};
2015-12-07 04:34:12 +03:00
return ComposedComponent => {
class RippledComponent extends Component {
2015-12-07 04:34:12 +03:00
static propTypes = {
children: PropTypes.any,
disabled: PropTypes.bool,
onRippleEnded: PropTypes.func,
ripple: PropTypes.bool,
rippleCentered: PropTypes.bool,
rippleClassName: PropTypes.string,
rippleSpread: PropTypes.number,
theme: PropTypes.shape({
ripple: PropTypes.string,
rippleActive: PropTypes.string,
rippleRestarting: PropTypes.string,
rippleWrapper: PropTypes.string
2016-05-16 15:17:26 +03:00
})
2015-12-07 04:34:12 +03:00
};
2015-12-07 04:34:12 +03:00
static defaultProps = {
disabled: false,
ripple: true,
rippleCentered: defaultCentered,
rippleClassName: defaultClassName,
rippleSpread: defaultSpread
};
2015-12-07 04:34:12 +03:00
state = {
active: false,
left: null,
restarting: false,
top: null,
visible: false,
2015-12-07 04:34:12 +03:00
width: null
};
componentDidUpdate (prevProps, prevState) {
if (!prevState.visible && this.state.visible) {
2016-04-12 22:41:28 +03:00
events.addEventListenerOnTransitionEnded(this.refs.ripple, (evt) => {
if (evt.propertyName === 'opacity') this.handleOpacityEnded(evt);
2016-04-12 22:41:28 +03:00
});
}
}
handleOpacityEnded = (evt) => {
events.removeEventListenerOnTransitionEnded(this.refs.ripple);
this.setState({visible: false});
if (this.props.onRippleEnded) this.props.onRippleEnded(evt);
2016-04-12 22:41:28 +03:00
}
2015-12-07 04:34:12 +03:00
handleEnd = () => {
document.removeEventListener(this.touch ? 'touchend' : 'mouseup', this.handleEnd);
this.setState({active: false});
};
2015-12-07 04:34:12 +03:00
start = ({pageX, pageY}, touch = false) => {
if (!this._isTouchRippleReceivingMouseEvent(touch)) {
this.touch = touch;
document.addEventListener(this.touch ? 'touchend' : 'mouseup', this.handleEnd);
const {top, left, width} = this._getDescriptor(pageX, pageY);
this.setState({active: false, restarting: true, visible: true, top, left, width}, () => {
2015-12-07 04:34:12 +03:00
this.refs.ripple.offsetWidth; //eslint-disable-line no-unused-expressions
this.setState({active: true, restarting: false, visible: true});
2015-12-07 04:34:12 +03:00
});
}
};
2015-11-29 14:40:09 +03:00
2015-12-07 04:34:12 +03:00
_isTouchRippleReceivingMouseEvent (touch) {
return this.touch && !touch;
}
_getDescriptor (pageX, pageY) {
const {left, top, height, width} = ReactDOM.findDOMNode(this).getBoundingClientRect();
const {rippleCentered: centered, rippleSpread: spread} = this.props;
return {
left: centered ? 0 : pageX - left - width / 2 - window.pageXOffset,
top: centered ? 0 : pageY - top - height / 2 - window.pageYOffset,
2015-12-07 04:34:12 +03:00
width: width * spread
};
}
handleMouseDown = (event) => {
if (!this.props.disabled) this.start(event);
if (this.props.onMouseDown) this.props.onMouseDown(event);
};
render () {
if (!this.props.ripple) {
return <ComposedComponent {...this.props} />;
} else {
const {
children,
2016-04-09 21:34:34 +03:00
ripple, //eslint-disable-line no-unused-vars
2016-07-04 23:03:57 +03:00
onRippleEnded, //eslint-disable-line no-unused-vars
2015-12-07 04:34:12 +03:00
rippleClassName: className,
2016-04-09 21:34:34 +03:00
rippleCentered: centered, //eslint-disable-line no-unused-vars
rippleSpread: spread, //eslint-disable-line no-unused-vars
2015-12-07 04:34:12 +03:00
...other
} = this.props;
2016-05-16 15:17:26 +03:00
const rippleClassName = classnames(this.props.theme.ripple, {
[this.props.theme.rippleActive]: this.state.active,
[this.props.theme.rippleRestarting]: this.state.restarting
2015-12-07 04:34:12 +03:00
}, className);
const { left, top, width } = this.state;
const scale = this.state.restarting ? 0 : 1;
const rippleStyle = prefixer({
transform: `translate3d(${-width / 2 + left}px, ${-width / 2 + top}px, 0) scale(${scale})`
}, {width, height: width});
const rippleElement = (
<span data-react-toolbox='ripple' className={this.props.theme.rippleWrapper} {...props}>
<span ref='ripple' role='ripple' className={rippleClassName} style={rippleStyle} />
</span>
);
2015-12-07 04:34:12 +03:00
return (
<ComposedComponent {...other} onMouseDown={this.handleMouseDown}>
{children ? children : null}
{this.state.visible || this.state.active ? rippleElement : null}
2015-12-07 04:34:12 +03:00
</ComposedComponent>
);
}
}
2016-05-16 15:17:26 +03:00
}
2016-05-28 16:37:10 +03:00
return themr(RIPPLE, defaultTheme)(RippledComponent);
2015-12-07 04:34:12 +03:00
};
};
2016-05-28 16:37:10 +03:00
export default rippleFactory;