You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

347 lines
11 KiB

import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
import _createClass from "@babel/runtime/helpers/esm/createClass";
import _assertThisInitialized from "@babel/runtime/helpers/esm/assertThisInitialized";
import _inherits from "@babel/runtime/helpers/esm/inherits";
import _createSuper from "@babel/runtime/helpers/esm/createSuper";
import _typeof from "@babel/runtime/helpers/esm/typeof";
/* eslint-disable react/default-props-match-prop-types, react/no-multi-comp, react/prop-types */
import React from 'react';
import findDOMNode from "rc-util/es/Dom/findDOMNode";
import { fillRef } from "rc-util/es/ref";
import classNames from 'classnames';
import raf from 'raf';
import { getTransitionName, animationEndName, transitionEndName, supportTransition } from './util/motion';
var STATUS_NONE = 'none';
var STATUS_APPEAR = 'appear';
var STATUS_ENTER = 'enter';
var STATUS_LEAVE = 'leave';
/**
* `transitionSupport` is used for none transition test case.
* Default we use browser transition event support check.
*/
export function genCSSMotion(config) {
var transitionSupport = config;
var forwardRef = !!React.forwardRef;
if (_typeof(config) === 'object') {
transitionSupport = config.transitionSupport;
forwardRef = 'forwardRef' in config ? config.forwardRef : forwardRef;
}
function isSupportTransition(props) {
return !!(props.motionName && transitionSupport);
}
var CSSMotion = /*#__PURE__*/function (_React$Component) {
_inherits(CSSMotion, _React$Component);
var _super = _createSuper(CSSMotion);
function CSSMotion() {
var _this;
_classCallCheck(this, CSSMotion);
_this = _super.apply(this, arguments);
_this.$cacheEle = null;
_this.node = null;
_this.raf = null;
_this.destroyed = false;
_this.deadlineId = null;
_this.state = {
status: STATUS_NONE,
statusActive: false,
newStatus: false,
statusStyle: null
};
_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) {
if (event && !event.deadline && event.target !== _this.getElement()) {
// event exists
// not initiated by deadline
// transitionend not fired by inner elements
return;
}
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;
fillRef(internalRef, node);
};
_this.getElement = function () {
try {
return findDOMNode(_this.node || _assertThisInitialized(_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(transitionEndName, _this.onMotionEnd);
$ele.addEventListener(animationEndName, _this.onMotionEnd);
};
_this.removeEventListener = function ($ele) {
if (!$ele) return;
$ele.removeEventListener(transitionEndName, _this.onMotionEnd);
$ele.removeEventListener(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;
if (callback) {
nextStep = function nextStep() {
_this.nextFrame(callback);
};
}
_this.setState(_objectSpread({
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) {
_this.deadlineId = setTimeout(function () {
_this.onMotionEnd({
deadline: true
});
}, motionDeadline);
}
});
};
_this.nextFrame = function (func) {
_this.cancelNextFrame();
_this.raf = raf(func);
};
_this.cancelNextFrame = function () {
if (_this.raf) {
raf.cancel(_this.raf);
_this.raf = null;
}
};
return _this;
}
_createClass(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();
clearTimeout(this.deadlineId);
}
}, {
key: "render",
value: function render() {
var _classNames;
var _this$state3 = this.state,
status = _this$state3.status,
statusActive = _this$state3.statusActive,
statusStyle = _this$state3.statusStyle;
var _this$props3 = this.props,
children = _this$props3.children,
motionName = _this$props3.motionName,
visible = _this$props3.visible,
removeOnLeave = _this$props3.removeOnLeave,
leavedClassName = _this$props3.leavedClassName,
eventProps = _this$props3.eventProps;
if (!children) return null;
if (status === STATUS_NONE || !isSupportTransition(this.props)) {
if (visible) {
return children(_objectSpread({}, eventProps), this.setNodeRef);
}
if (!removeOnLeave) {
return children(_objectSpread(_objectSpread({}, eventProps), {}, {
className: leavedClassName
}), this.setNodeRef);
}
return null;
}
return children(_objectSpread(_objectSpread({}, eventProps), {}, {
className: classNames(getTransitionName(motionName, status), (_classNames = {}, _defineProperty(_classNames, getTransitionName(motionName, "".concat(status, "-active")), statusActive), _defineProperty(_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;
}(React.Component);
CSSMotion.defaultProps = {
visible: true,
motionEnter: true,
motionAppear: true,
motionLeave: true,
removeOnLeave: true
};
if (!forwardRef) {
return CSSMotion;
}
return React.forwardRef(function (props, ref) {
return React.createElement(CSSMotion, Object.assign({
internalRef: ref
}, props));
});
}
export default genCSSMotion(supportTransition);