UNPKG

zarm-web

Version:
264 lines (234 loc) 7.09 kB
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } import { Component, isValidElement, Children, cloneElement } from 'react'; import classnames from 'classnames'; import { noop } from '../utils'; const TRANSITION_TYPE = { ENTERING: 'show', EXITING: 'hide' }; export default class Transition extends Component { constructor(_props) { super(_props); this.nextStatus = void 0; this.timeoutId = void 0; this.computeInitialStatus = () => { const { visible, mountOnShow, transitionOnMount, unmountOnHide } = this.props; if (visible) { if (transitionOnMount) { return { initial: Transition.EXITED, next: Transition.ENTERING }; } return { initial: Transition.ENTERED }; } if (mountOnShow || unmountOnHide) return { initial: Transition.UNMOUNTED }; return { initial: Transition.EXITED }; }; this.computeNextStatus = () => { const { animating, status } = this.state; if (animating) return status === Transition.ENTERING ? Transition.EXITING : Transition.ENTERING; return status === Transition.ENTERED ? Transition.EXITING : Transition.ENTERING; }; this.computeStatus = props => { const { status } = this.state; const { visible } = props; if (visible) { return { status: status === Transition.UNMOUNTED && Transition.EXITED, next: status !== Transition.ENTERING && status !== Transition.ENTERED && Transition.ENTERING }; } return { next: (status === Transition.ENTERING || status === Transition.ENTERED) && Transition.EXITING }; }; this.updateStatus = () => { const { animating } = this.state; if (this.nextStatus) { this.nextStatus = this.computeNextStatus(); if (!animating) this.handleStart(); } }; this.handleStart = () => { const { duration } = this.props; const status = this.nextStatus; this.nextStatus = null; this.setState({ status, animating: true }, () => { this.props.onStart(_objectSpread({}, this.props, { status })); this.timeoutId = setTimeout(() => { this.handleComplete(); }, duration); }); }; this.handleComplete = () => { const { status } = this.state; this.props.onComplete(_objectSpread({}, this.props, { status })); if (this.nextStatus) { return this.handleStart(); } const completeStatus = this.computeCompletedStatus(); const beforeHook = status === Transition.ENTERING ? 'onBeforeShow' : 'onBeforeHide'; const callback = status === Transition.ENTERING ? 'onShow' : 'onHide'; this.props[beforeHook](_objectSpread({}, this.props, { status: completeStatus })); this.setState({ status: completeStatus, animating: false }, () => { this.props[callback](_objectSpread({}, this.props, { status: completeStatus })); }); }; this.computeCompletedStatus = () => { const { unmountOnHide } = this.props; const { status } = this.state; if (status === Transition.ENTERING) return Transition.ENTERED; return unmountOnHide ? Transition.UNMOUNTED : Transition.EXITED; }; this.computeClasses = () => { const { name, children, directional } = this.props; const { animating, status } = this.state; const childrenClass = children.props.className; if (directional) { return classnames(childrenClass, { animating, [`${name}`]: name, [`${name}-in`]: status === Transition.ENTERING, [`${name}-out`]: status === Transition.EXITING, hidden: status === Transition.EXITED, visible: status !== Transition.EXITED, transition: true }); } return classnames(childrenClass, { animating, transition: true, [`${name}`]: name }); }; this.computeStyle = () => { const { children, duration } = this.props; const { status } = this.state; const childrenStyle = children.props.style; const type = TRANSITION_TYPE[status]; const animationDuration = type && `${duration}ms`; return _objectSpread({}, childrenStyle, { animationDuration }); }; const { initial: _status, next } = this.computeInitialStatus(); this.nextStatus = next; this.state = { status: _status }; } componentDidMount() { this.updateStatus(); } componentWillReceiveProps(nextProps) { const { status, next } = this.computeStatus(nextProps); this.nextStatus = next; if (status) this.setState({ status }); } componentDidUpdate() { this.updateStatus(); } componentWillUnmount() { clearTimeout(this.timeoutId); } render() { const { children } = this.props; const { status } = this.state; if (status === Transition.UNMOUNTED) return null; if (isValidElement(children) && Children.only(children)) { return cloneElement(children, { className: this.computeClasses(), style: this.computeStyle() }); } } } Transition.defaultProps = { name: 'fade', duration: 500, directional: true, visible: true, mountOnShow: true, transitionOnMount: false, unmountOnHide: false, onStart: noop, onShow: noop, onHide: noop, onComplete: noop, onBeforeShow: noop, onBeforeHide: noop }; Transition.ENTERED = 'ENTERED'; Transition.ENTERING = 'ENTERING'; Transition.EXITED = 'EXITED'; Transition.EXITING = 'EXITING'; Transition.UNMOUNTED = 'UNMOUNTED';