'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.MotionPropTypes = undefined; var _defineProperty2 = require('babel-runtime/helpers/defineProperty'); var _defineProperty3 = _interopRequireDefault(_defineProperty2); var _extends2 = require('babel-runtime/helpers/extends'); var _extends3 = _interopRequireDefault(_extends2); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); var _inherits2 = require('babel-runtime/helpers/inherits'); var _inherits3 = _interopRequireDefault(_inherits2); exports.genCSSMotion = genCSSMotion; var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _reactLifecyclesCompat = require('react-lifecycles-compat'); var _findDOMNode = require('rc-util/lib/Dom/findDOMNode'); var _findDOMNode2 = _interopRequireDefault(_findDOMNode); var _classnames = require('classnames'); var _classnames2 = _interopRequireDefault(_classnames); var _raf = require('raf'); var _raf2 = _interopRequireDefault(_raf); var _motion = require('./util/motion'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var STATUS_NONE = 'none'; /* eslint-disable react/default-props-match-prop-types, react/no-multi-comp */ var STATUS_APPEAR = 'appear'; var STATUS_ENTER = 'enter'; var STATUS_LEAVE = 'leave'; var MotionPropTypes = exports.MotionPropTypes = { eventProps: _propTypes2['default'].object, // Internal usage. Only pass by CSSMotionList visible: _propTypes2['default'].bool, children: _propTypes2['default'].func, motionName: _propTypes2['default'].oneOfType([_propTypes2['default'].string, _propTypes2['default'].object]), motionAppear: _propTypes2['default'].bool, motionEnter: _propTypes2['default'].bool, motionLeave: _propTypes2['default'].bool, motionLeaveImmediately: _propTypes2['default'].bool, // Trigger leave motion immediately motionDeadline: _propTypes2['default'].number, removeOnLeave: _propTypes2['default'].bool, leavedClassName: _propTypes2['default'].string, onAppearStart: _propTypes2['default'].func, onAppearActive: _propTypes2['default'].func, onAppearEnd: _propTypes2['default'].func, onEnterStart: _propTypes2['default'].func, onEnterActive: _propTypes2['default'].func, onEnterEnd: _propTypes2['default'].func, onLeaveStart: _propTypes2['default'].func, onLeaveActive: _propTypes2['default'].func, onLeaveEnd: _propTypes2['default'].func }; /** * `transitionSupport` is used for none transition test case. * Default we use browser transition event support check. */ function genCSSMotion(config) { var transitionSupport = config; var forwardRef = !!_react2['default'].forwardRef; if (typeof config === 'object') { transitionSupport = config.transitionSupport; forwardRef = 'forwardRef' in config ? config.forwardRef : forwardRef; } function isSupportTransition(props) { return !!(props.motionName && transitionSupport); } var CSSMotion = function (_React$Component) { (0, _inherits3['default'])(CSSMotion, _React$Component); function CSSMotion() { (0, _classCallCheck3['default'])(this, CSSMotion); var _this = (0, _possibleConstructorReturn3['default'])(this, (CSSMotion.__proto__ || Object.getPrototypeOf(CSSMotion)).call(this)); _this.onDomUpdate = function () { var _this$state = _this.state, status = _this$state.status, newStatus = _this$state.newStatus; var _this$props = _this.props, onAppearStart = _this$props.onAppearStart, onEnterStart = _this$props.onEnterStart, onLeaveStart = _this$props.onLeaveStart, onAppearActive = _this$props.onAppearActive, onEnterActive = _this$props.onEnterActive, onLeaveActive = _this$props.onLeaveActive, motionAppear = _this$props.motionAppear, motionEnter = _this$props.motionEnter, motionLeave = _this$props.motionLeave; if (!isSupportTransition(_this.props)) { return; } // Event injection var $ele = _this.getElement(); if (_this.$cacheEle !== $ele) { _this.removeEventListener(_this.$cacheEle); _this.addEventListener($ele); _this.$cacheEle = $ele; } // Init status if (newStatus && status === STATUS_APPEAR && motionAppear) { _this.updateStatus(onAppearStart, null, null, function () { _this.updateActiveStatus(onAppearActive, STATUS_APPEAR); }); } else if (newStatus && status === STATUS_ENTER && motionEnter) { _this.updateStatus(onEnterStart, null, null, function () { _this.updateActiveStatus(onEnterActive, STATUS_ENTER); }); } else if (newStatus && status === STATUS_LEAVE && motionLeave) { _this.updateStatus(onLeaveStart, null, null, function () { _this.updateActiveStatus(onLeaveActive, STATUS_LEAVE); }); } }; _this.onMotionEnd = function (event) { var _this$state2 = _this.state, status = _this$state2.status, statusActive = _this$state2.statusActive; var _this$props2 = _this.props, onAppearEnd = _this$props2.onAppearEnd, onEnterEnd = _this$props2.onEnterEnd, onLeaveEnd = _this$props2.onLeaveEnd; if (status === STATUS_APPEAR && statusActive) { _this.updateStatus(onAppearEnd, { status: STATUS_NONE }, event); } else if (status === STATUS_ENTER && statusActive) { _this.updateStatus(onEnterEnd, { status: STATUS_NONE }, event); } else if (status === STATUS_LEAVE && statusActive) { _this.updateStatus(onLeaveEnd, { status: STATUS_NONE }, event); } }; _this.setNodeRef = function (node) { var internalRef = _this.props.internalRef; _this.node = node; if (typeof internalRef === 'function') { internalRef(node); } else if (internalRef && 'current' in internalRef) { internalRef.current = node; } }; _this.getElement = function () { try { return (0, _findDOMNode2['default'])(_this.node || _this); } catch (e) { /** * Fallback to cache element. * This is only happen when `motionDeadline` trigger but element removed. */ return _this.$cacheEle; } }; _this.addEventListener = function ($ele) { if (!$ele) return; $ele.addEventListener(_motion.transitionEndName, _this.onMotionEnd); $ele.addEventListener(_motion.animationEndName, _this.onMotionEnd); }; _this.removeEventListener = function ($ele) { if (!$ele) return; $ele.removeEventListener(_motion.transitionEndName, _this.onMotionEnd); $ele.removeEventListener(_motion.animationEndName, _this.onMotionEnd); }; _this.updateStatus = function (styleFunc, additionalState, event, callback) { var statusStyle = styleFunc ? styleFunc(_this.getElement(), event) : null; if (statusStyle === false || _this._destroyed) return; var nextStep = void 0; if (callback) { nextStep = function nextStep() { _this.nextFrame(callback); }; } _this.setState((0, _extends3['default'])({ statusStyle: typeof statusStyle === 'object' ? statusStyle : null, newStatus: false }, additionalState), nextStep); // Trigger before next frame & after `componentDidMount` }; _this.updateActiveStatus = function (styleFunc, currentStatus) { // `setState` use `postMessage` to trigger at the end of frame. // Let's use requestAnimationFrame to update new state in next frame. _this.nextFrame(function () { var status = _this.state.status; if (status !== currentStatus) return; var motionDeadline = _this.props.motionDeadline; _this.updateStatus(styleFunc, { statusActive: true }); if (motionDeadline > 0) { setTimeout(function () { _this.onMotionEnd({ deadline: true }); }, motionDeadline); } }); }; _this.nextFrame = function (func) { _this.cancelNextFrame(); _this.raf = (0, _raf2['default'])(func); }; _this.cancelNextFrame = function () { if (_this.raf) { _raf2['default'].cancel(_this.raf); _this.raf = null; } }; _this.state = { status: STATUS_NONE, statusActive: false, newStatus: false, statusStyle: null }; _this.$cacheEle = null; _this.node = null; _this.raf = null; return _this; } (0, _createClass3['default'])(CSSMotion, [{ key: 'componentDidMount', value: function componentDidMount() { this.onDomUpdate(); } }, { key: 'componentDidUpdate', value: function componentDidUpdate() { this.onDomUpdate(); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { this._destroyed = true; this.removeEventListener(this.$cacheEle); this.cancelNextFrame(); } }, { key: 'render', value: function render() { var _classNames; var _state = this.state, status = _state.status, statusActive = _state.statusActive, statusStyle = _state.statusStyle; var _props = this.props, children = _props.children, motionName = _props.motionName, visible = _props.visible, removeOnLeave = _props.removeOnLeave, leavedClassName = _props.leavedClassName, eventProps = _props.eventProps; if (!children) return null; if (status === STATUS_NONE || !isSupportTransition(this.props)) { if (visible) { return children((0, _extends3['default'])({}, eventProps), this.setNodeRef); } else if (!removeOnLeave) { return children((0, _extends3['default'])({}, eventProps, { className: leavedClassName }), this.setNodeRef); } return null; } return children((0, _extends3['default'])({}, eventProps, { className: (0, _classnames2['default'])((_classNames = {}, (0, _defineProperty3['default'])(_classNames, (0, _motion.getTransitionName)(motionName, status), status !== STATUS_NONE), (0, _defineProperty3['default'])(_classNames, (0, _motion.getTransitionName)(motionName, status + '-active'), status !== STATUS_NONE && statusActive), (0, _defineProperty3['default'])(_classNames, motionName, typeof motionName === 'string'), _classNames)), style: statusStyle }), this.setNodeRef); } }], [{ key: 'getDerivedStateFromProps', value: function getDerivedStateFromProps(props, _ref) { var prevProps = _ref.prevProps, prevStatus = _ref.status; if (!isSupportTransition(props)) return {}; var visible = props.visible, motionAppear = props.motionAppear, motionEnter = props.motionEnter, motionLeave = props.motionLeave, motionLeaveImmediately = props.motionLeaveImmediately; var newState = { prevProps: props }; // Clean up status if prop set to false if (prevStatus === STATUS_APPEAR && !motionAppear || prevStatus === STATUS_ENTER && !motionEnter || prevStatus === STATUS_LEAVE && !motionLeave) { newState.status = STATUS_NONE; newState.statusActive = false; newState.newStatus = false; } // Appear if (!prevProps && visible && motionAppear) { newState.status = STATUS_APPEAR; newState.statusActive = false; newState.newStatus = true; } // Enter if (prevProps && !prevProps.visible && visible && motionEnter) { newState.status = STATUS_ENTER; newState.statusActive = false; newState.newStatus = true; } // Leave if (prevProps && prevProps.visible && !visible && motionLeave || !prevProps && motionLeaveImmediately && !visible && motionLeave) { newState.status = STATUS_LEAVE; newState.statusActive = false; newState.newStatus = true; } return newState; } }]); return CSSMotion; }(_react2['default'].Component); CSSMotion.propTypes = (0, _extends3['default'])({}, MotionPropTypes, { internalRef: _propTypes2['default'].oneOfType([_propTypes2['default'].object, _propTypes2['default'].func]) }); CSSMotion.defaultProps = { visible: true, motionEnter: true, motionAppear: true, motionLeave: true, removeOnLeave: true }; (0, _reactLifecyclesCompat.polyfill)(CSSMotion); if (!forwardRef) { return CSSMotion; } return _react2['default'].forwardRef(function (props, ref) { return _react2['default'].createElement(CSSMotion, (0, _extends3['default'])({ internalRef: ref }, props)); }); } exports['default'] = genCSSMotion(_motion.supportTransition);