rc-animate
Advanced tools
Comparing version 2.4.4 to 3.0.0-rc.0
import _extends from 'babel-runtime/helpers/extends'; | ||
import _defineProperty from 'babel-runtime/helpers/defineProperty'; | ||
import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; | ||
import _createClass from 'babel-runtime/helpers/createClass'; | ||
import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn'; | ||
@@ -9,346 +7,204 @@ import _inherits from 'babel-runtime/helpers/inherits'; | ||
import PropTypes from 'prop-types'; | ||
import { toArrayChildren, mergeChildren, findShownChildInChildrenByKey, findChildInChildrenByKey, isSameChildren } from './ChildrenUtils'; | ||
import { polyfill } from 'react-lifecycles-compat'; | ||
import toArray from 'rc-util/es/Children/toArray'; | ||
import warning from 'fbjs/lib/warning'; | ||
import AnimateChild from './AnimateChild'; | ||
import { cloneProps, mergeChildren } from './util'; | ||
var defaultKey = 'rc_animate_' + Date.now(); | ||
import animUtil from './util'; | ||
var clonePropList = ['children']; | ||
function getChildrenFromProps(props) { | ||
var children = props.children; | ||
if (React.isValidElement(children)) { | ||
if (!children.key) { | ||
return React.cloneElement(children, { | ||
key: defaultKey | ||
}); | ||
} | ||
} | ||
return children; | ||
} | ||
/** | ||
* Default use `AnimateChild` as component. | ||
* Here can also pass customize `ChildComponent` for test usage. | ||
*/ | ||
export function genAnimate(ChildComponent) { | ||
var Animate = function (_React$Component) { | ||
_inherits(Animate, _React$Component); | ||
function noop() {} | ||
function Animate() { | ||
var _temp, _this, _ret; | ||
var Animate = function (_React$Component) { | ||
_inherits(Animate, _React$Component); | ||
_classCallCheck(this, Animate); | ||
// eslint-disable-line | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
function Animate(props) { | ||
_classCallCheck(this, Animate); | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.state = { | ||
appeared: true, | ||
mergedChildren: [] | ||
}, _this.onChildLeaved = function (key) { | ||
// Remove child which not exist anymore | ||
if (!_this.hasChild(key)) { | ||
var mergedChildren = _this.state.mergedChildren; | ||
var _this = _possibleConstructorReturn(this, (Animate.__proto__ || Object.getPrototypeOf(Animate)).call(this, props)); | ||
_this.setState({ | ||
mergedChildren: mergedChildren.filter(function (node) { | ||
return node.key !== key; | ||
}) | ||
}); | ||
} | ||
}, _this.hasChild = function (key) { | ||
var children = _this.props.children; | ||
_initialiseProps.call(_this); | ||
_this.currentlyAnimatingKeys = {}; | ||
_this.keysToEnter = []; | ||
_this.keysToLeave = []; | ||
return toArray(children).some(function (node) { | ||
return node && node.key === key; | ||
}); | ||
}, _temp), _possibleConstructorReturn(_this, _ret); | ||
} | ||
// [Legacy] Not sure usage | ||
// commit: https://github.com/react-component/animate/commit/0a1cbfd647407498b10a8c6602a2dea80b42e324 | ||
// eslint-disable-line | ||
_this.state = { | ||
children: toArrayChildren(getChildrenFromProps(props)) | ||
}; | ||
Animate.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) { | ||
var _prevState$prevProps = prevState.prevProps, | ||
prevProps = _prevState$prevProps === undefined ? {} : _prevState$prevProps; | ||
_this.childrenRefs = {}; | ||
return _this; | ||
} | ||
var newState = { | ||
prevProps: cloneProps(nextProps, clonePropList) | ||
}; | ||
var showProp = nextProps.showProp; | ||
_createClass(Animate, [{ | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
var _this2 = this; | ||
var showProp = this.props.showProp; | ||
var children = this.state.children; | ||
if (showProp) { | ||
children = children.filter(function (child) { | ||
return !!child.props[showProp]; | ||
}); | ||
function processState(propName, updater) { | ||
if (prevProps[propName] !== nextProps[propName]) { | ||
updater(nextProps[propName]); | ||
return true; | ||
} | ||
return false; | ||
} | ||
children.forEach(function (child) { | ||
if (child) { | ||
_this2.performAppear(child.key); | ||
} | ||
}); | ||
} | ||
}, { | ||
key: 'componentWillReceiveProps', | ||
value: function componentWillReceiveProps(nextProps) { | ||
var _this3 = this; | ||
this.nextProps = nextProps; | ||
var nextChildren = toArrayChildren(getChildrenFromProps(nextProps)); | ||
var props = this.props; | ||
// exclusive needs immediate response | ||
if (props.exclusive) { | ||
Object.keys(this.currentlyAnimatingKeys).forEach(function (key) { | ||
_this3.stop(key); | ||
processState('children', function (children) { | ||
var currentChildren = toArray(children).filter(function (node) { | ||
return node; | ||
}); | ||
} | ||
var showProp = props.showProp; | ||
var currentlyAnimatingKeys = this.currentlyAnimatingKeys; | ||
// last props children if exclusive | ||
var currentChildren = props.exclusive ? toArrayChildren(getChildrenFromProps(props)) : this.state.children; | ||
// in case destroy in showProp mode | ||
var newChildren = []; | ||
if (showProp) { | ||
currentChildren.forEach(function (currentChild) { | ||
var nextChild = currentChild && findChildInChildrenByKey(nextChildren, currentChild.key); | ||
var newChild = void 0; | ||
if ((!nextChild || !nextChild.props[showProp]) && currentChild.props[showProp]) { | ||
newChild = React.cloneElement(nextChild || currentChild, _defineProperty({}, showProp, true)); | ||
} else { | ||
newChild = nextChild; | ||
var prevChildren = prevState.mergedChildren.filter(function (node) { | ||
// Remove prev child if not show anymore | ||
if (currentChildren.every(function (_ref) { | ||
var key = _ref.key; | ||
return key !== node.key; | ||
}) && showProp && !node.props[showProp]) { | ||
return false; | ||
} | ||
if (newChild) { | ||
newChildren.push(newChild); | ||
} | ||
return true; | ||
}); | ||
nextChildren.forEach(function (nextChild) { | ||
if (!nextChild || !findChildInChildrenByKey(currentChildren, nextChild.key)) { | ||
newChildren.push(nextChild); | ||
} | ||
}); | ||
} else { | ||
newChildren = mergeChildren(currentChildren, nextChildren); | ||
} | ||
// need render to avoid update | ||
this.setState({ | ||
children: newChildren | ||
// Merge prev children to keep the animation | ||
newState.mergedChildren = mergeChildren(prevChildren, currentChildren); | ||
}); | ||
nextChildren.forEach(function (child) { | ||
var key = child && child.key; | ||
if (child && currentlyAnimatingKeys[key]) { | ||
return; | ||
return newState; | ||
}; | ||
Animate.prototype.componentDidMount = function componentDidMount() { | ||
// No need to re-render | ||
this.state.appeared = false; | ||
}; | ||
Animate.prototype.render = function render() { | ||
var _this2 = this; | ||
var _state = this.state, | ||
appeared = _state.appeared, | ||
mergedChildren = _state.mergedChildren; | ||
var _props = this.props, | ||
Component = _props.component, | ||
componentProps = _props.componentProps, | ||
className = _props.className, | ||
style = _props.style, | ||
showProp = _props.showProp; | ||
var $children = mergedChildren.map(function (node) { | ||
if (mergedChildren.length > 1 && !node.key) { | ||
warning(false, 'must set key for <rc-animate> children'); | ||
return null; | ||
} | ||
var hasPrev = child && findChildInChildrenByKey(currentChildren, key); | ||
if (showProp) { | ||
var showInNext = child.props[showProp]; | ||
if (hasPrev) { | ||
var showInNow = findShownChildInChildrenByKey(currentChildren, key, showProp); | ||
if (!showInNow && showInNext) { | ||
_this3.keysToEnter.push(key); | ||
} | ||
} else if (showInNext) { | ||
_this3.keysToEnter.push(key); | ||
} | ||
} else if (!hasPrev) { | ||
_this3.keysToEnter.push(key); | ||
var show = true; | ||
if (!_this2.hasChild(node.key)) { | ||
show = false; | ||
} else if (showProp) { | ||
show = node.props[showProp]; | ||
} | ||
}); | ||
currentChildren.forEach(function (child) { | ||
var key = child && child.key; | ||
if (child && currentlyAnimatingKeys[key]) { | ||
return; | ||
} | ||
var hasNext = child && findChildInChildrenByKey(nextChildren, key); | ||
if (showProp) { | ||
var showInNow = child.props[showProp]; | ||
if (hasNext) { | ||
var showInNext = findShownChildInChildrenByKey(nextChildren, key, showProp); | ||
if (!showInNext && showInNow) { | ||
_this3.keysToLeave.push(key); | ||
} | ||
} else if (showInNow) { | ||
_this3.keysToLeave.push(key); | ||
} | ||
} else if (!hasNext) { | ||
_this3.keysToLeave.push(key); | ||
} | ||
var key = node.key || defaultKey; | ||
return React.createElement( | ||
ChildComponent, | ||
_extends({}, _this2.props, { | ||
appeared: appeared, | ||
show: show, | ||
className: node.props.className, | ||
style: node.props.style, | ||
key: key, | ||
animateKey: node.key // Keep trans origin key | ||
, onChildLeaved: _this2.onChildLeaved | ||
}), | ||
node | ||
); | ||
}); | ||
} | ||
}, { | ||
key: 'componentDidUpdate', | ||
value: function componentDidUpdate() { | ||
var keysToEnter = this.keysToEnter; | ||
this.keysToEnter = []; | ||
keysToEnter.forEach(this.performEnter); | ||
var keysToLeave = this.keysToLeave; | ||
this.keysToLeave = []; | ||
keysToLeave.forEach(this.performLeave); | ||
} | ||
}, { | ||
key: 'isValidChildByKey', | ||
value: function isValidChildByKey(currentChildren, key) { | ||
var showProp = this.props.showProp; | ||
if (showProp) { | ||
return findShownChildInChildrenByKey(currentChildren, key, showProp); | ||
} | ||
return findChildInChildrenByKey(currentChildren, key); | ||
} | ||
}, { | ||
key: 'stop', | ||
value: function stop(key) { | ||
delete this.currentlyAnimatingKeys[key]; | ||
var component = this.childrenRefs[key]; | ||
if (component) { | ||
component.stop(); | ||
} | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var _this4 = this; | ||
var props = this.props; | ||
this.nextProps = props; | ||
var stateChildren = this.state.children; | ||
var children = null; | ||
if (stateChildren) { | ||
children = stateChildren.map(function (child) { | ||
if (child === null || child === undefined) { | ||
return child; | ||
} | ||
if (!child.key) { | ||
throw new Error('must set key for <rc-animate> children'); | ||
} | ||
return React.createElement( | ||
AnimateChild, | ||
{ | ||
key: child.key, | ||
ref: function ref(node) { | ||
return _this4.childrenRefs[child.key] = node; | ||
}, | ||
animation: props.animation, | ||
transitionName: props.transitionName, | ||
transitionEnter: props.transitionEnter, | ||
transitionAppear: props.transitionAppear, | ||
transitionLeave: props.transitionLeave | ||
}, | ||
child | ||
); | ||
}); | ||
} | ||
var Component = props.component; | ||
// Wrap with component | ||
if (Component) { | ||
var passedProps = props; | ||
var passedProps = this.props; | ||
if (typeof Component === 'string') { | ||
passedProps = _extends({ | ||
className: props.className, | ||
style: props.style | ||
}, props.componentProps); | ||
className: className, | ||
style: style | ||
}, componentProps); | ||
} | ||
return React.createElement( | ||
Component, | ||
passedProps, | ||
children | ||
$children | ||
); | ||
} | ||
return children[0] || null; | ||
} | ||
}]); | ||
return Animate; | ||
}(React.Component); | ||
return $children[0] || null; | ||
}; | ||
Animate.isAnimate = true; | ||
Animate.propTypes = { | ||
component: PropTypes.any, | ||
componentProps: PropTypes.object, | ||
animation: PropTypes.object, | ||
transitionName: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), | ||
transitionEnter: PropTypes.bool, | ||
transitionAppear: PropTypes.bool, | ||
exclusive: PropTypes.bool, | ||
transitionLeave: PropTypes.bool, | ||
onEnd: PropTypes.func, | ||
onEnter: PropTypes.func, | ||
onLeave: PropTypes.func, | ||
onAppear: PropTypes.func, | ||
showProp: PropTypes.string | ||
}; | ||
Animate.defaultProps = { | ||
animation: {}, | ||
component: 'span', | ||
componentProps: {}, | ||
transitionEnter: true, | ||
transitionLeave: true, | ||
transitionAppear: false, | ||
onEnd: noop, | ||
onEnter: noop, | ||
onLeave: noop, | ||
onAppear: noop | ||
}; | ||
return Animate; | ||
}(React.Component); | ||
var _initialiseProps = function _initialiseProps() { | ||
var _this5 = this; | ||
this.performEnter = function (key) { | ||
// may already remove by exclusive | ||
if (_this5.childrenRefs[key]) { | ||
_this5.currentlyAnimatingKeys[key] = true; | ||
_this5.childrenRefs[key].componentWillEnter(_this5.handleDoneAdding.bind(_this5, key, 'enter')); | ||
} | ||
Animate.isAnimate = true; | ||
Animate.propTypes = { | ||
component: PropTypes.any, | ||
componentProps: PropTypes.object, | ||
animation: PropTypes.object, | ||
transitionName: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), | ||
transitionEnter: PropTypes.bool, | ||
transitionAppear: PropTypes.bool, | ||
exclusive: PropTypes.bool, | ||
transitionLeave: PropTypes.bool, | ||
onEnd: PropTypes.func, | ||
onEnter: PropTypes.func, | ||
onLeave: PropTypes.func, | ||
onAppear: PropTypes.func, | ||
showProp: PropTypes.string, | ||
children: PropTypes.node, | ||
style: PropTypes.object, | ||
className: PropTypes.string | ||
}; | ||
this.performAppear = function (key) { | ||
if (_this5.childrenRefs[key]) { | ||
_this5.currentlyAnimatingKeys[key] = true; | ||
_this5.childrenRefs[key].componentWillAppear(_this5.handleDoneAdding.bind(_this5, key, 'appear')); | ||
} | ||
Animate.defaultProps = { | ||
animation: {}, | ||
component: 'span', | ||
componentProps: {}, | ||
transitionEnter: true, | ||
transitionLeave: true, | ||
transitionAppear: false | ||
}; | ||
this.handleDoneAdding = function (key, type) { | ||
var props = _this5.props; | ||
delete _this5.currentlyAnimatingKeys[key]; | ||
// if update on exclusive mode, skip check | ||
if (props.exclusive && props !== _this5.nextProps) { | ||
return; | ||
} | ||
var currentChildren = toArrayChildren(getChildrenFromProps(props)); | ||
if (!_this5.isValidChildByKey(currentChildren, key)) { | ||
// exclusive will not need this | ||
_this5.performLeave(key); | ||
} else { | ||
if (type === 'appear') { | ||
if (animUtil.allowAppearCallback(props)) { | ||
props.onAppear(key); | ||
props.onEnd(key, true); | ||
} | ||
} else { | ||
if (animUtil.allowEnterCallback(props)) { | ||
props.onEnter(key); | ||
props.onEnd(key, true); | ||
} | ||
} | ||
} | ||
}; | ||
this.performLeave = function (key) { | ||
// may already remove by exclusive | ||
if (_this5.childrenRefs[key]) { | ||
_this5.currentlyAnimatingKeys[key] = true; | ||
_this5.childrenRefs[key].componentWillLeave(_this5.handleDoneLeaving.bind(_this5, key)); | ||
} | ||
}; | ||
polyfill(Animate); | ||
this.handleDoneLeaving = function (key) { | ||
var props = _this5.props; | ||
delete _this5.currentlyAnimatingKeys[key]; | ||
// if update on exclusive mode, skip check | ||
if (props.exclusive && props !== _this5.nextProps) { | ||
return; | ||
} | ||
var currentChildren = toArrayChildren(getChildrenFromProps(props)); | ||
// in case state change is too fast | ||
if (_this5.isValidChildByKey(currentChildren, key)) { | ||
_this5.performEnter(key); | ||
} else { | ||
var end = function end() { | ||
if (animUtil.allowLeaveCallback(props)) { | ||
props.onLeave(key); | ||
props.onEnd(key, false); | ||
} | ||
}; | ||
if (!isSameChildren(_this5.state.children, currentChildren, props.showProp)) { | ||
_this5.setState({ | ||
children: currentChildren | ||
}, end); | ||
} else { | ||
end(); | ||
} | ||
} | ||
}; | ||
}; | ||
return Animate; | ||
} | ||
export default Animate; | ||
export default genAnimate(AnimateChild); |
@@ -1,4 +0,2 @@ | ||
import _typeof from 'babel-runtime/helpers/typeof'; | ||
import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; | ||
import _createClass from 'babel-runtime/helpers/createClass'; | ||
import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn'; | ||
@@ -9,105 +7,440 @@ import _inherits from 'babel-runtime/helpers/inherits'; | ||
import PropTypes from 'prop-types'; | ||
import cssAnimate, { isCssAnimationSupported } from 'css-animation'; | ||
import animUtil from './util'; | ||
import { polyfill } from 'react-lifecycles-compat'; | ||
import classNames from 'classnames'; | ||
import classes from 'component-classes'; | ||
import raf from 'raf'; | ||
var transitionMap = { | ||
enter: 'transitionEnter', | ||
appear: 'transitionAppear', | ||
leave: 'transitionLeave' | ||
}; | ||
import { getStyleValue, cloneProps, getTransitionName, supportTransition, animationEndName, transitionEndName } from './util'; | ||
var AnimateChild = function (_React$Component) { | ||
_inherits(AnimateChild, _React$Component); | ||
var clonePropList = ['appeared', 'show', 'exclusive', 'children', 'animation']; | ||
function AnimateChild() { | ||
_classCallCheck(this, AnimateChild); | ||
/** | ||
* AnimateChild only accept one child node. | ||
* `transitionSupport` is used for none transition test case. | ||
* Default we use browser transition event support check. | ||
*/ | ||
export function genAnimateChild(transitionSupport) { | ||
var AnimateChild = function (_React$Component) { | ||
_inherits(AnimateChild, _React$Component); | ||
return _possibleConstructorReturn(this, (AnimateChild.__proto__ || Object.getPrototypeOf(AnimateChild)).apply(this, arguments)); | ||
} | ||
function AnimateChild() { | ||
_classCallCheck(this, AnimateChild); | ||
_createClass(AnimateChild, [{ | ||
key: 'componentWillUnmount', | ||
value: function componentWillUnmount() { | ||
this.stop(); | ||
// [Legacy] Since old code addListener on the element. | ||
// To avoid break the behaviour that component not handle animation/transition | ||
// also can handle the animate, let keep the logic. | ||
var _this = _possibleConstructorReturn(this, _React$Component.call(this)); | ||
_this.state = { | ||
child: null, | ||
eventQueue: [], | ||
eventActive: false | ||
}; | ||
_this.onDomUpdated = function () { | ||
var eventActive = _this.state.eventActive; | ||
var _this$props = _this.props, | ||
transitionName = _this$props.transitionName, | ||
animation = _this$props.animation, | ||
onChildLeaved = _this$props.onChildLeaved, | ||
animateKey = _this$props.animateKey; | ||
var $ele = _this.getDomElement(); | ||
// Skip if dom element not ready | ||
if (!$ele) return; | ||
// [Legacy] Add animation/transition event by dom level | ||
if (transitionSupport && _this.$prevEle !== $ele) { | ||
_this.cleanDomEvent(); | ||
_this.$prevEle = $ele; | ||
_this.$prevEle.addEventListener(animationEndName, _this.onMotionEnd); | ||
_this.$prevEle.addEventListener(transitionEndName, _this.onMotionEnd); | ||
} | ||
var currentEvent = _this.getCurrentEvent(); | ||
if (currentEvent.empty) { | ||
// Additional process the leave event | ||
if (currentEvent.lastEventType === 'leave') { | ||
onChildLeaved(animateKey); | ||
} | ||
return; | ||
} | ||
var eventType = currentEvent.eventType, | ||
restQueue = currentEvent.restQueue; | ||
var nodeClasses = classes($ele); | ||
// [Legacy] Since origin code use js to set `className`. | ||
// This caused that any component without support `className` can be forced set. | ||
// Let's keep the logic. | ||
function legacyAppendClass() { | ||
if (!transitionSupport) return; | ||
var basicClassName = getTransitionName(transitionName, '' + eventType); | ||
if (basicClassName) nodeClasses.add(basicClassName); | ||
if (eventActive) { | ||
var activeClassName = getTransitionName(transitionName, eventType + '-active'); | ||
if (activeClassName) nodeClasses.add(activeClassName); | ||
} | ||
} | ||
if (_this.currentEvent && _this.currentEvent.type === eventType) { | ||
legacyAppendClass(); | ||
return; | ||
} | ||
// Clear timeout for legacy check | ||
clearTimeout(_this.timeout); | ||
// Clean up last event environment | ||
if (_this.currentEvent && _this.currentEvent.animateObj && _this.currentEvent.animateObj.stop) { | ||
_this.currentEvent.animateObj.stop(); | ||
} | ||
// Clean up last transition class | ||
if (_this.currentEvent) { | ||
var basicClassName = getTransitionName(transitionName, '' + _this.currentEvent.type); | ||
var activeClassName = getTransitionName(transitionName, _this.currentEvent.type + '-active'); | ||
if (basicClassName) nodeClasses.remove(basicClassName); | ||
if (activeClassName) nodeClasses.remove(activeClassName); | ||
} | ||
// New event come | ||
_this.currentEvent = { | ||
type: eventType | ||
}; | ||
var animationHandler = (animation || {})[eventType]; | ||
// =============== Check if has customize animation =============== | ||
if (animationHandler) { | ||
_this.currentEvent.animateObj = animationHandler($ele, function () { | ||
_this.onMotionEnd({ target: $ele }); | ||
}); | ||
// Do next step if not animate object provided | ||
if (!_this.currentEvent.animateObj) { | ||
_this.nextEvent(restQueue); | ||
} | ||
// ==================== Use transition instead ==================== | ||
} else if (transitionSupport) { | ||
legacyAppendClass(); | ||
if (!eventActive) { | ||
// Trigger `eventActive` in next frame | ||
raf(function () { | ||
if (_this.currentEvent && _this.currentEvent.type === eventType && !_this._destroy) { | ||
_this.setState({ eventActive: true }, function () { | ||
// [Legacy] Handle timeout if browser transition event not handle | ||
var transitionDelay = getStyleValue($ele, 'transition-delay') || 0; | ||
var transitionDuration = getStyleValue($ele, 'transition-duration') || 0; | ||
var animationDelay = getStyleValue($ele, 'animation-delay') || 0; | ||
var animationDuration = getStyleValue($ele, 'animation-duration') || 0; | ||
var totalTime = Math.max(transitionDuration + transitionDelay, animationDuration + animationDelay); | ||
if (totalTime >= 0) { | ||
_this.timeout = setTimeout(function () { | ||
_this.onMotionEnd({ target: $ele }); | ||
}, totalTime * 1000); | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
// ======================= Just next action ======================= | ||
} else { | ||
_this.onMotionEnd({ target: $ele }); | ||
} | ||
}; | ||
_this.onMotionEnd = function (_ref) { | ||
var target = _ref.target; | ||
var _this$props2 = _this.props, | ||
transitionName = _this$props2.transitionName, | ||
onChildLeaved = _this$props2.onChildLeaved, | ||
animateKey = _this$props2.animateKey, | ||
onAppear = _this$props2.onAppear, | ||
onEnter = _this$props2.onEnter, | ||
onLeave = _this$props2.onLeave, | ||
onEnd = _this$props2.onEnd; | ||
var currentEvent = _this.getCurrentEvent(); | ||
if (currentEvent.empty) return; | ||
// Clear timeout for legacy check | ||
clearTimeout(_this.timeout); | ||
var restQueue = currentEvent.restQueue; | ||
var $ele = _this.getDomElement(); | ||
if (!_this.currentEvent || $ele !== target) return; | ||
if (_this.currentEvent.animateObj && _this.currentEvent.animateObj.stop) { | ||
_this.currentEvent.animateObj.stop(); | ||
} | ||
// [Legacy] Same as above, we need call js to remove the class | ||
if (transitionSupport && _this.currentEvent) { | ||
var basicClassName = getTransitionName(transitionName, _this.currentEvent.type); | ||
var activeClassName = getTransitionName(transitionName, _this.currentEvent.type + '-active'); | ||
var nodeClasses = classes($ele); | ||
if (basicClassName) nodeClasses.remove(basicClassName); | ||
if (activeClassName) nodeClasses.remove(activeClassName); | ||
} | ||
// Additional process the leave event | ||
if (_this.currentEvent && _this.currentEvent.type === 'leave') { | ||
onChildLeaved(animateKey); | ||
} | ||
// [Legacy] Trigger on event when it's last event | ||
if (_this.currentEvent && !restQueue.length) { | ||
if (_this.currentEvent.type === 'appear' && onAppear) { | ||
onAppear(animateKey); | ||
} else if (_this.currentEvent.type === 'enter' && onEnter) { | ||
onEnter(animateKey); | ||
} else if (_this.currentEvent.type === 'leave' && onLeave) { | ||
onLeave(animateKey); | ||
} | ||
if (onEnd) { | ||
// OnEnd(key, isShow) | ||
onEnd(animateKey, _this.currentEvent.type !== 'leave'); | ||
} | ||
} | ||
_this.currentEvent = null; | ||
// Next queue | ||
_this.nextEvent(restQueue); | ||
}; | ||
_this.getDomElement = function () { | ||
if (_this._destroy) return null; | ||
return ReactDOM.findDOMNode(_this); | ||
}; | ||
_this.getCurrentEvent = function () { | ||
var _this$state$eventQueu = _this.state.eventQueue, | ||
eventQueue = _this$state$eventQueu === undefined ? [] : _this$state$eventQueu; | ||
var _this$props3 = _this.props, | ||
animation = _this$props3.animation, | ||
exclusive = _this$props3.exclusive, | ||
transitionAppear = _this$props3.transitionAppear, | ||
transitionEnter = _this$props3.transitionEnter, | ||
transitionLeave = _this$props3.transitionLeave; | ||
function hasEventHandler(eventType) { | ||
return eventType === 'appear' && (transitionAppear || animation.appear) || eventType === 'enter' && (transitionEnter || animation.enter) || eventType === 'leave' && (transitionLeave || animation.leave); | ||
} | ||
var event = null; | ||
// If is exclusive, only check the last event | ||
if (exclusive) { | ||
var eventType = eventQueue[eventQueue.length - 1]; | ||
if (hasEventHandler(eventType)) { | ||
event = { | ||
eventType: eventType, | ||
restQueue: [] | ||
}; | ||
} | ||
} else { | ||
// Loop check the queue until find match | ||
var cloneQueue = eventQueue.slice(); | ||
while (cloneQueue.length) { | ||
var _cloneQueue = cloneQueue, | ||
_eventType = _cloneQueue[0], | ||
restQueue = _cloneQueue.slice(1); | ||
if (hasEventHandler(_eventType)) { | ||
event = { | ||
eventType: _eventType, | ||
restQueue: restQueue | ||
}; | ||
break; | ||
} | ||
cloneQueue = restQueue; | ||
} | ||
} | ||
if (!event) { | ||
event = { | ||
empty: true, | ||
lastEventType: eventQueue[eventQueue.length - 1] | ||
}; | ||
} | ||
return event; | ||
}; | ||
_this.nextEvent = function (restQueue) { | ||
// Next queue | ||
if (!_this._destroy) { | ||
_this.setState({ | ||
eventQueue: restQueue, | ||
eventActive: false | ||
}); | ||
} | ||
}; | ||
_this.cleanDomEvent = function () { | ||
if (_this.$prevEle && transitionSupport) { | ||
_this.$prevEle.removeEventListener(animationEndName, _this.onMotionEnd); | ||
_this.$prevEle.removeEventListener(transitionEndName, _this.onMotionEnd); | ||
} | ||
}; | ||
_this.$prevEle = null; | ||
_this.currentEvent = null; | ||
_this.timeout = null; | ||
return _this; | ||
} | ||
}, { | ||
key: 'componentWillEnter', | ||
value: function componentWillEnter(done) { | ||
if (animUtil.isEnterSupported(this.props)) { | ||
this.transition('enter', done); | ||
} else { | ||
done(); | ||
AnimateChild.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) { | ||
var _prevState$prevProps = prevState.prevProps, | ||
prevProps = _prevState$prevProps === undefined ? {} : _prevState$prevProps; | ||
var appeared = nextProps.appeared; | ||
var newState = { | ||
prevProps: cloneProps(nextProps, clonePropList) | ||
}; | ||
function processState(propName, updater) { | ||
if (prevProps[propName] !== nextProps[propName]) { | ||
if (updater) { | ||
updater(nextProps[propName]); | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
} | ||
}, { | ||
key: 'componentWillAppear', | ||
value: function componentWillAppear(done) { | ||
if (animUtil.isAppearSupported(this.props)) { | ||
this.transition('appear', done); | ||
} else { | ||
done(); | ||
function pushEvent(eventType) { | ||
var eventQueue = newState.eventQueue || prevState.eventQueue.slice(); | ||
var matchIndex = eventQueue.indexOf(eventType); | ||
// Clean the rest event if eventType match | ||
if (matchIndex !== -1) { | ||
eventQueue = eventQueue.slice(0, matchIndex); | ||
} | ||
eventQueue.push(eventType); | ||
newState.eventQueue = eventQueue; | ||
} | ||
} | ||
}, { | ||
key: 'componentWillLeave', | ||
value: function componentWillLeave(done) { | ||
if (animUtil.isLeaveSupported(this.props)) { | ||
this.transition('leave', done); | ||
// Child update. Only set child. | ||
processState('children', function (child) { | ||
newState.child = child; | ||
}); | ||
processState('appeared', function (isAppeared) { | ||
if (isAppeared) { | ||
pushEvent('appear'); | ||
} | ||
}); | ||
// Show update | ||
processState('show', function (show) { | ||
if (!appeared) { | ||
if (show) { | ||
pushEvent('enter'); | ||
} else { | ||
pushEvent('leave'); | ||
} | ||
} | ||
}); | ||
return newState; | ||
}; | ||
AnimateChild.prototype.componentDidMount = function componentDidMount() { | ||
this.onDomUpdated(); | ||
}; | ||
AnimateChild.prototype.componentDidUpdate = function componentDidUpdate() { | ||
this.onDomUpdated(); | ||
}; | ||
AnimateChild.prototype.componentWillUnmount = function componentWillUnmount() { | ||
clearTimeout(this.timeout); | ||
this._destroy = true; | ||
this.cleanDomEvent(); | ||
}; | ||
AnimateChild.prototype.render = function render() { | ||
var _state = this.state, | ||
child = _state.child, | ||
eventActive = _state.eventActive; | ||
var _props = this.props, | ||
showProp = _props.showProp, | ||
transitionName = _props.transitionName; | ||
var _ref2 = child.props || {}, | ||
className = _ref2.className; | ||
var currentEvent = this.getCurrentEvent(); | ||
// Class name | ||
var connectClassName = transitionSupport && this.currentEvent ? classNames(className, getTransitionName(transitionName, this.currentEvent.type), eventActive && getTransitionName(transitionName, this.currentEvent.type + '-active')) : className; | ||
var show = true; | ||
// Keep show when is in transition or has customize animate | ||
if (transitionSupport && (!currentEvent.empty || this.currentEvent && this.currentEvent.animateObj)) { | ||
show = true; | ||
} else { | ||
// always sync, do not interupt with react component life cycle | ||
// update hidden -> animate hidden -> | ||
// didUpdate -> animate leave -> unmount (if animate is none) | ||
done(); | ||
show = child.props[showProp]; | ||
} | ||
} | ||
}, { | ||
key: 'transition', | ||
value: function transition(animationType, finishCallback) { | ||
var _this2 = this; | ||
var node = ReactDOM.findDOMNode(this); | ||
var props = this.props; | ||
var transitionName = props.transitionName; | ||
var nameIsObj = (typeof transitionName === 'undefined' ? 'undefined' : _typeof(transitionName)) === 'object'; | ||
this.stop(); | ||
var end = function end() { | ||
_this2.stopper = null; | ||
finishCallback(); | ||
// Clone child | ||
var newChildProps = { | ||
className: connectClassName | ||
}; | ||
if ((isCssAnimationSupported || !props.animation[animationType]) && transitionName && props[transitionMap[animationType]]) { | ||
var name = nameIsObj ? transitionName[animationType] : transitionName + '-' + animationType; | ||
var activeName = name + '-active'; | ||
if (nameIsObj && transitionName[animationType + 'Active']) { | ||
activeName = transitionName[animationType + 'Active']; | ||
} | ||
this.stopper = cssAnimate(node, { | ||
name: name, | ||
active: activeName | ||
}, end); | ||
} else { | ||
this.stopper = props.animation[animationType](node, end); | ||
if (showProp) { | ||
newChildProps[showProp] = show; | ||
} | ||
} | ||
}, { | ||
key: 'stop', | ||
value: function stop() { | ||
var stopper = this.stopper; | ||
if (stopper) { | ||
this.stopper = null; | ||
stopper.stop(); | ||
} | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
return this.props.children; | ||
} | ||
}]); | ||
return React.cloneElement(child, newChildProps); | ||
}; | ||
return AnimateChild; | ||
}(React.Component); | ||
AnimateChild.propTypes = { | ||
transitionName: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), | ||
transitionAppear: PropTypes.bool, | ||
transitionEnter: PropTypes.bool, | ||
transitionLeave: PropTypes.bool, | ||
exclusive: PropTypes.bool, | ||
appeared: PropTypes.bool, | ||
showProp: PropTypes.string, | ||
animateKey: PropTypes.any, | ||
animation: PropTypes.object, | ||
onChildLeaved: PropTypes.func, | ||
onEnd: PropTypes.func, | ||
onAppear: PropTypes.func, | ||
onEnter: PropTypes.func, | ||
onLeave: PropTypes.func | ||
}; | ||
polyfill(AnimateChild); | ||
return AnimateChild; | ||
}(React.Component); | ||
} | ||
AnimateChild.propTypes = { | ||
children: PropTypes.any | ||
}; | ||
export default AnimateChild; | ||
export default genAnimateChild(supportTransition); |
183
es/util.js
@@ -1,21 +0,164 @@ | ||
var util = { | ||
isAppearSupported: function isAppearSupported(props) { | ||
return props.transitionName && props.transitionAppear || props.animation.appear; | ||
}, | ||
isEnterSupported: function isEnterSupported(props) { | ||
return props.transitionName && props.transitionEnter || props.animation.enter; | ||
}, | ||
isLeaveSupported: function isLeaveSupported(props) { | ||
return props.transitionName && props.transitionLeave || props.animation.leave; | ||
}, | ||
allowAppearCallback: function allowAppearCallback(props) { | ||
return props.transitionAppear || props.animation.appear; | ||
}, | ||
allowEnterCallback: function allowEnterCallback(props) { | ||
return props.transitionEnter || props.animation.enter; | ||
}, | ||
allowLeaveCallback: function allowLeaveCallback(props) { | ||
return props.transitionLeave || props.animation.leave; | ||
import toArray from 'rc-util/es/Children/toArray'; | ||
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment'; | ||
// =================== Style ==================== | ||
var stylePrefixes = ['-webkit-', '-moz-', '-o-', 'ms-', '']; | ||
export function getStyleProperty(node, name) { | ||
// old ff need null, https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle | ||
var style = window.getComputedStyle(node, null); | ||
var ret = ''; | ||
for (var i = 0; i < stylePrefixes.length; i++) { | ||
ret = style.getPropertyValue(stylePrefixes[i] + name); | ||
if (ret) { | ||
break; | ||
} | ||
} | ||
}; | ||
export default util; | ||
return ret; | ||
} | ||
export function getStyleValue(node, name) { | ||
return parseFloat(getStyleProperty(node, name)); | ||
} | ||
// ================= Transition ================= | ||
// Event wrapper. Copy from react source code | ||
function makePrefixMap(styleProp, eventName) { | ||
var prefixes = {}; | ||
prefixes[styleProp.toLowerCase()] = eventName.toLowerCase(); | ||
prefixes['Webkit' + styleProp] = 'webkit' + eventName; | ||
prefixes['Moz' + styleProp] = 'moz' + eventName; | ||
prefixes['ms' + styleProp] = 'MS' + eventName; | ||
prefixes['O' + styleProp] = 'o' + eventName.toLowerCase(); | ||
return prefixes; | ||
} | ||
export function getVendorPrefixes(domSupport, win) { | ||
var prefixes = { | ||
animationend: makePrefixMap('Animation', 'AnimationEnd'), | ||
transitionend: makePrefixMap('Transition', 'TransitionEnd') | ||
}; | ||
if (domSupport) { | ||
if (!('AnimationEvent' in win)) { | ||
delete prefixes.animationend.animation; | ||
} | ||
if (!('TransitionEvent' in win)) { | ||
delete prefixes.transitionend.transition; | ||
} | ||
} | ||
return prefixes; | ||
} | ||
var vendorPrefixes = getVendorPrefixes(canUseDOM, window); | ||
var style = {}; | ||
if (canUseDOM) { | ||
style = document.createElement('div').style; | ||
} | ||
var prefixedEventNames = {}; | ||
export function getVendorPrefixedEventName(eventName) { | ||
if (prefixedEventNames[eventName]) { | ||
return prefixedEventNames[eventName]; | ||
} | ||
var prefixMap = vendorPrefixes[eventName]; | ||
if (prefixMap) { | ||
var stylePropList = Object.keys(prefixMap); | ||
var len = stylePropList.length; | ||
for (var i = 0; i < len; i += 1) { | ||
var styleProp = stylePropList[i]; | ||
if (Object.prototype.hasOwnProperty.call(prefixMap, styleProp) && styleProp in style) { | ||
prefixedEventNames[eventName] = prefixMap[styleProp]; | ||
return prefixedEventNames[eventName]; | ||
} | ||
} | ||
} | ||
return ''; | ||
} | ||
export var animationEndName = getVendorPrefixedEventName('animationend'); | ||
export var transitionEndName = getVendorPrefixedEventName('transitionend'); | ||
export var supportTransition = !!(animationEndName && transitionEndName); | ||
// ==================== Node ==================== | ||
/** | ||
* [Legacy] Find the same children in both prev & next list. | ||
* Insert not find one before the find one, otherwise in the end. For example: | ||
* - prev: [1,2,3] | ||
* - next: [2,4] | ||
* -> [1,2,4,3] | ||
*/ | ||
export function mergeChildren(prev, next) { | ||
var prevList = toArray(prev); | ||
var nextList = toArray(next); | ||
// Skip if is single children | ||
if (prevList.length === 1 && nextList.length === 1 && prevList[0].key === nextList[0].key) { | ||
return nextList; | ||
} | ||
var mergeList = []; | ||
var nextChildrenMap = {}; | ||
var missMatchChildrenList = []; | ||
// Fill matched prev node into next node map | ||
prevList.forEach(function (prevNode) { | ||
if (prevNode && nextList.some(function (_ref) { | ||
var key = _ref.key; | ||
return key === prevNode.key; | ||
})) { | ||
if (missMatchChildrenList.length) { | ||
nextChildrenMap[prevNode.key] = missMatchChildrenList; | ||
missMatchChildrenList = []; | ||
} | ||
} else { | ||
missMatchChildrenList.push(prevNode); | ||
} | ||
}); | ||
// Insert prev node before the matched next node | ||
nextList.forEach(function (nextNode) { | ||
if (nextNode && nextChildrenMap[nextNode.key]) { | ||
mergeList = mergeList.concat(nextChildrenMap[nextNode.key]); | ||
} | ||
mergeList.push(nextNode); | ||
}); | ||
mergeList = mergeList.concat(missMatchChildrenList); | ||
return mergeList; | ||
} | ||
export function cloneProps(props, propList) { | ||
var newProps = {}; | ||
propList.forEach(function (prop) { | ||
if (prop in props) { | ||
newProps[prop] = props[prop]; | ||
} | ||
}); | ||
return newProps; | ||
} | ||
export function getTransitionName(transitionName, transitionType) { | ||
if (!transitionName) return null; | ||
if (typeof transitionName === 'object') { | ||
var type = transitionType.replace(/-\w/g, function (match) { | ||
return match[1].toUpperCase(); | ||
}); | ||
return transitionName[type]; | ||
} | ||
return transitionName + '-' + transitionType; | ||
} |
# History | ||
---- | ||
## 3.0.0 / 2018-06-11 | ||
- refactor realization | ||
## 2.3.0 / 2016-07-05 | ||
@@ -5,0 +9,0 @@ |
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.__esModule = true; | ||
@@ -11,6 +9,2 @@ var _extends2 = require('babel-runtime/helpers/extends'); | ||
var _defineProperty2 = require('babel-runtime/helpers/defineProperty'); | ||
var _defineProperty3 = _interopRequireDefault(_defineProperty2); | ||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); | ||
@@ -20,6 +14,2 @@ | ||
var _createClass2 = require('babel-runtime/helpers/createClass'); | ||
var _createClass3 = _interopRequireDefault(_createClass2); | ||
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); | ||
@@ -33,2 +23,4 @@ | ||
exports.genAnimate = genAnimate; | ||
var _react = require('react'); | ||
@@ -42,4 +34,12 @@ | ||
var _ChildrenUtils = require('./ChildrenUtils'); | ||
var _reactLifecyclesCompat = require('react-lifecycles-compat'); | ||
var _toArray = require('rc-util/lib/Children/toArray'); | ||
var _toArray2 = _interopRequireDefault(_toArray); | ||
var _warning = require('fbjs/lib/warning'); | ||
var _warning2 = _interopRequireDefault(_warning); | ||
var _AnimateChild = require('./AnimateChild'); | ||
@@ -51,348 +51,199 @@ | ||
var _util2 = _interopRequireDefault(_util); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
var defaultKey = 'rc_animate_' + Date.now(); | ||
var clonePropList = ['children']; | ||
/** | ||
* Default use `AnimateChild` as component. | ||
* Here can also pass customize `ChildComponent` for test usage. | ||
*/ | ||
function genAnimate(ChildComponent) { | ||
var Animate = function (_React$Component) { | ||
(0, _inherits3['default'])(Animate, _React$Component); | ||
function getChildrenFromProps(props) { | ||
var children = props.children; | ||
if (_react2['default'].isValidElement(children)) { | ||
if (!children.key) { | ||
return _react2['default'].cloneElement(children, { | ||
key: defaultKey | ||
}); | ||
} | ||
} | ||
return children; | ||
} | ||
function Animate() { | ||
var _temp, _this, _ret; | ||
function noop() {} | ||
(0, _classCallCheck3['default'])(this, Animate); | ||
var Animate = function (_React$Component) { | ||
(0, _inherits3['default'])(Animate, _React$Component); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
// eslint-disable-line | ||
return _ret = (_temp = (_this = (0, _possibleConstructorReturn3['default'])(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.state = { | ||
appeared: true, | ||
mergedChildren: [] | ||
}, _this.onChildLeaved = function (key) { | ||
// Remove child which not exist anymore | ||
if (!_this.hasChild(key)) { | ||
var mergedChildren = _this.state.mergedChildren; | ||
function Animate(props) { | ||
(0, _classCallCheck3['default'])(this, Animate); | ||
_this.setState({ | ||
mergedChildren: mergedChildren.filter(function (node) { | ||
return node.key !== key; | ||
}) | ||
}); | ||
} | ||
}, _this.hasChild = function (key) { | ||
var children = _this.props.children; | ||
var _this = (0, _possibleConstructorReturn3['default'])(this, (Animate.__proto__ || Object.getPrototypeOf(Animate)).call(this, props)); | ||
_initialiseProps.call(_this); | ||
return (0, _toArray2['default'])(children).some(function (node) { | ||
return node && node.key === key; | ||
}); | ||
}, _temp), (0, _possibleConstructorReturn3['default'])(_this, _ret); | ||
} | ||
// [Legacy] Not sure usage | ||
// commit: https://github.com/react-component/animate/commit/0a1cbfd647407498b10a8c6602a2dea80b42e324 | ||
// eslint-disable-line | ||
_this.currentlyAnimatingKeys = {}; | ||
_this.keysToEnter = []; | ||
_this.keysToLeave = []; | ||
Animate.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) { | ||
var _prevState$prevProps = prevState.prevProps, | ||
prevProps = _prevState$prevProps === undefined ? {} : _prevState$prevProps; | ||
_this.state = { | ||
children: (0, _ChildrenUtils.toArrayChildren)(getChildrenFromProps(props)) | ||
}; | ||
var newState = { | ||
prevProps: (0, _util.cloneProps)(nextProps, clonePropList) | ||
}; | ||
var showProp = nextProps.showProp; | ||
_this.childrenRefs = {}; | ||
return _this; | ||
} | ||
(0, _createClass3['default'])(Animate, [{ | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
var _this2 = this; | ||
var showProp = this.props.showProp; | ||
var children = this.state.children; | ||
if (showProp) { | ||
children = children.filter(function (child) { | ||
return !!child.props[showProp]; | ||
}); | ||
function processState(propName, updater) { | ||
if (prevProps[propName] !== nextProps[propName]) { | ||
updater(nextProps[propName]); | ||
return true; | ||
} | ||
return false; | ||
} | ||
children.forEach(function (child) { | ||
if (child) { | ||
_this2.performAppear(child.key); | ||
} | ||
}); | ||
} | ||
}, { | ||
key: 'componentWillReceiveProps', | ||
value: function componentWillReceiveProps(nextProps) { | ||
var _this3 = this; | ||
this.nextProps = nextProps; | ||
var nextChildren = (0, _ChildrenUtils.toArrayChildren)(getChildrenFromProps(nextProps)); | ||
var props = this.props; | ||
// exclusive needs immediate response | ||
if (props.exclusive) { | ||
Object.keys(this.currentlyAnimatingKeys).forEach(function (key) { | ||
_this3.stop(key); | ||
processState('children', function (children) { | ||
var currentChildren = (0, _toArray2['default'])(children).filter(function (node) { | ||
return node; | ||
}); | ||
} | ||
var showProp = props.showProp; | ||
var currentlyAnimatingKeys = this.currentlyAnimatingKeys; | ||
// last props children if exclusive | ||
var currentChildren = props.exclusive ? (0, _ChildrenUtils.toArrayChildren)(getChildrenFromProps(props)) : this.state.children; | ||
// in case destroy in showProp mode | ||
var newChildren = []; | ||
if (showProp) { | ||
currentChildren.forEach(function (currentChild) { | ||
var nextChild = currentChild && (0, _ChildrenUtils.findChildInChildrenByKey)(nextChildren, currentChild.key); | ||
var newChild = void 0; | ||
if ((!nextChild || !nextChild.props[showProp]) && currentChild.props[showProp]) { | ||
newChild = _react2['default'].cloneElement(nextChild || currentChild, (0, _defineProperty3['default'])({}, showProp, true)); | ||
} else { | ||
newChild = nextChild; | ||
var prevChildren = prevState.mergedChildren.filter(function (node) { | ||
// Remove prev child if not show anymore | ||
if (currentChildren.every(function (_ref) { | ||
var key = _ref.key; | ||
return key !== node.key; | ||
}) && showProp && !node.props[showProp]) { | ||
return false; | ||
} | ||
if (newChild) { | ||
newChildren.push(newChild); | ||
} | ||
return true; | ||
}); | ||
nextChildren.forEach(function (nextChild) { | ||
if (!nextChild || !(0, _ChildrenUtils.findChildInChildrenByKey)(currentChildren, nextChild.key)) { | ||
newChildren.push(nextChild); | ||
} | ||
}); | ||
} else { | ||
newChildren = (0, _ChildrenUtils.mergeChildren)(currentChildren, nextChildren); | ||
} | ||
// need render to avoid update | ||
this.setState({ | ||
children: newChildren | ||
// Merge prev children to keep the animation | ||
newState.mergedChildren = (0, _util.mergeChildren)(prevChildren, currentChildren); | ||
}); | ||
nextChildren.forEach(function (child) { | ||
var key = child && child.key; | ||
if (child && currentlyAnimatingKeys[key]) { | ||
return; | ||
return newState; | ||
}; | ||
Animate.prototype.componentDidMount = function componentDidMount() { | ||
// No need to re-render | ||
this.state.appeared = false; | ||
}; | ||
Animate.prototype.render = function render() { | ||
var _this2 = this; | ||
var _state = this.state, | ||
appeared = _state.appeared, | ||
mergedChildren = _state.mergedChildren; | ||
var _props = this.props, | ||
Component = _props.component, | ||
componentProps = _props.componentProps, | ||
className = _props.className, | ||
style = _props.style, | ||
showProp = _props.showProp; | ||
var $children = mergedChildren.map(function (node) { | ||
if (mergedChildren.length > 1 && !node.key) { | ||
(0, _warning2['default'])(false, 'must set key for <rc-animate> children'); | ||
return null; | ||
} | ||
var hasPrev = child && (0, _ChildrenUtils.findChildInChildrenByKey)(currentChildren, key); | ||
if (showProp) { | ||
var showInNext = child.props[showProp]; | ||
if (hasPrev) { | ||
var showInNow = (0, _ChildrenUtils.findShownChildInChildrenByKey)(currentChildren, key, showProp); | ||
if (!showInNow && showInNext) { | ||
_this3.keysToEnter.push(key); | ||
} | ||
} else if (showInNext) { | ||
_this3.keysToEnter.push(key); | ||
} | ||
} else if (!hasPrev) { | ||
_this3.keysToEnter.push(key); | ||
var show = true; | ||
if (!_this2.hasChild(node.key)) { | ||
show = false; | ||
} else if (showProp) { | ||
show = node.props[showProp]; | ||
} | ||
}); | ||
currentChildren.forEach(function (child) { | ||
var key = child && child.key; | ||
if (child && currentlyAnimatingKeys[key]) { | ||
return; | ||
} | ||
var hasNext = child && (0, _ChildrenUtils.findChildInChildrenByKey)(nextChildren, key); | ||
if (showProp) { | ||
var showInNow = child.props[showProp]; | ||
if (hasNext) { | ||
var showInNext = (0, _ChildrenUtils.findShownChildInChildrenByKey)(nextChildren, key, showProp); | ||
if (!showInNext && showInNow) { | ||
_this3.keysToLeave.push(key); | ||
} | ||
} else if (showInNow) { | ||
_this3.keysToLeave.push(key); | ||
} | ||
} else if (!hasNext) { | ||
_this3.keysToLeave.push(key); | ||
} | ||
var key = node.key || defaultKey; | ||
return _react2['default'].createElement( | ||
ChildComponent, | ||
(0, _extends3['default'])({}, _this2.props, { | ||
appeared: appeared, | ||
show: show, | ||
className: node.props.className, | ||
style: node.props.style, | ||
key: key, | ||
animateKey: node.key // Keep trans origin key | ||
, onChildLeaved: _this2.onChildLeaved | ||
}), | ||
node | ||
); | ||
}); | ||
} | ||
}, { | ||
key: 'componentDidUpdate', | ||
value: function componentDidUpdate() { | ||
var keysToEnter = this.keysToEnter; | ||
this.keysToEnter = []; | ||
keysToEnter.forEach(this.performEnter); | ||
var keysToLeave = this.keysToLeave; | ||
this.keysToLeave = []; | ||
keysToLeave.forEach(this.performLeave); | ||
} | ||
}, { | ||
key: 'isValidChildByKey', | ||
value: function isValidChildByKey(currentChildren, key) { | ||
var showProp = this.props.showProp; | ||
if (showProp) { | ||
return (0, _ChildrenUtils.findShownChildInChildrenByKey)(currentChildren, key, showProp); | ||
} | ||
return (0, _ChildrenUtils.findChildInChildrenByKey)(currentChildren, key); | ||
} | ||
}, { | ||
key: 'stop', | ||
value: function stop(key) { | ||
delete this.currentlyAnimatingKeys[key]; | ||
var component = this.childrenRefs[key]; | ||
if (component) { | ||
component.stop(); | ||
} | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var _this4 = this; | ||
var props = this.props; | ||
this.nextProps = props; | ||
var stateChildren = this.state.children; | ||
var children = null; | ||
if (stateChildren) { | ||
children = stateChildren.map(function (child) { | ||
if (child === null || child === undefined) { | ||
return child; | ||
} | ||
if (!child.key) { | ||
throw new Error('must set key for <rc-animate> children'); | ||
} | ||
return _react2['default'].createElement( | ||
_AnimateChild2['default'], | ||
{ | ||
key: child.key, | ||
ref: function ref(node) { | ||
return _this4.childrenRefs[child.key] = node; | ||
}, | ||
animation: props.animation, | ||
transitionName: props.transitionName, | ||
transitionEnter: props.transitionEnter, | ||
transitionAppear: props.transitionAppear, | ||
transitionLeave: props.transitionLeave | ||
}, | ||
child | ||
); | ||
}); | ||
} | ||
var Component = props.component; | ||
// Wrap with component | ||
if (Component) { | ||
var passedProps = props; | ||
var passedProps = this.props; | ||
if (typeof Component === 'string') { | ||
passedProps = (0, _extends3['default'])({ | ||
className: props.className, | ||
style: props.style | ||
}, props.componentProps); | ||
className: className, | ||
style: style | ||
}, componentProps); | ||
} | ||
return _react2['default'].createElement( | ||
Component, | ||
passedProps, | ||
children | ||
$children | ||
); | ||
} | ||
return children[0] || null; | ||
} | ||
}]); | ||
return Animate; | ||
}(_react2['default'].Component); | ||
Animate.isAnimate = true; | ||
Animate.propTypes = { | ||
component: _propTypes2['default'].any, | ||
componentProps: _propTypes2['default'].object, | ||
animation: _propTypes2['default'].object, | ||
transitionName: _propTypes2['default'].oneOfType([_propTypes2['default'].string, _propTypes2['default'].object]), | ||
transitionEnter: _propTypes2['default'].bool, | ||
transitionAppear: _propTypes2['default'].bool, | ||
exclusive: _propTypes2['default'].bool, | ||
transitionLeave: _propTypes2['default'].bool, | ||
onEnd: _propTypes2['default'].func, | ||
onEnter: _propTypes2['default'].func, | ||
onLeave: _propTypes2['default'].func, | ||
onAppear: _propTypes2['default'].func, | ||
showProp: _propTypes2['default'].string | ||
}; | ||
Animate.defaultProps = { | ||
animation: {}, | ||
component: 'span', | ||
componentProps: {}, | ||
transitionEnter: true, | ||
transitionLeave: true, | ||
transitionAppear: false, | ||
onEnd: noop, | ||
onEnter: noop, | ||
onLeave: noop, | ||
onAppear: noop | ||
}; | ||
return $children[0] || null; | ||
}; | ||
var _initialiseProps = function _initialiseProps() { | ||
var _this5 = this; | ||
return Animate; | ||
}(_react2['default'].Component); | ||
this.performEnter = function (key) { | ||
// may already remove by exclusive | ||
if (_this5.childrenRefs[key]) { | ||
_this5.currentlyAnimatingKeys[key] = true; | ||
_this5.childrenRefs[key].componentWillEnter(_this5.handleDoneAdding.bind(_this5, key, 'enter')); | ||
} | ||
Animate.isAnimate = true; | ||
Animate.propTypes = { | ||
component: _propTypes2['default'].any, | ||
componentProps: _propTypes2['default'].object, | ||
animation: _propTypes2['default'].object, | ||
transitionName: _propTypes2['default'].oneOfType([_propTypes2['default'].string, _propTypes2['default'].object]), | ||
transitionEnter: _propTypes2['default'].bool, | ||
transitionAppear: _propTypes2['default'].bool, | ||
exclusive: _propTypes2['default'].bool, | ||
transitionLeave: _propTypes2['default'].bool, | ||
onEnd: _propTypes2['default'].func, | ||
onEnter: _propTypes2['default'].func, | ||
onLeave: _propTypes2['default'].func, | ||
onAppear: _propTypes2['default'].func, | ||
showProp: _propTypes2['default'].string, | ||
children: _propTypes2['default'].node, | ||
style: _propTypes2['default'].object, | ||
className: _propTypes2['default'].string | ||
}; | ||
this.performAppear = function (key) { | ||
if (_this5.childrenRefs[key]) { | ||
_this5.currentlyAnimatingKeys[key] = true; | ||
_this5.childrenRefs[key].componentWillAppear(_this5.handleDoneAdding.bind(_this5, key, 'appear')); | ||
} | ||
Animate.defaultProps = { | ||
animation: {}, | ||
component: 'span', | ||
componentProps: {}, | ||
transitionEnter: true, | ||
transitionLeave: true, | ||
transitionAppear: false | ||
}; | ||
this.handleDoneAdding = function (key, type) { | ||
var props = _this5.props; | ||
delete _this5.currentlyAnimatingKeys[key]; | ||
// if update on exclusive mode, skip check | ||
if (props.exclusive && props !== _this5.nextProps) { | ||
return; | ||
} | ||
var currentChildren = (0, _ChildrenUtils.toArrayChildren)(getChildrenFromProps(props)); | ||
if (!_this5.isValidChildByKey(currentChildren, key)) { | ||
// exclusive will not need this | ||
_this5.performLeave(key); | ||
} else { | ||
if (type === 'appear') { | ||
if (_util2['default'].allowAppearCallback(props)) { | ||
props.onAppear(key); | ||
props.onEnd(key, true); | ||
} | ||
} else { | ||
if (_util2['default'].allowEnterCallback(props)) { | ||
props.onEnter(key); | ||
props.onEnd(key, true); | ||
} | ||
} | ||
} | ||
}; | ||
this.performLeave = function (key) { | ||
// may already remove by exclusive | ||
if (_this5.childrenRefs[key]) { | ||
_this5.currentlyAnimatingKeys[key] = true; | ||
_this5.childrenRefs[key].componentWillLeave(_this5.handleDoneLeaving.bind(_this5, key)); | ||
} | ||
}; | ||
(0, _reactLifecyclesCompat.polyfill)(Animate); | ||
this.handleDoneLeaving = function (key) { | ||
var props = _this5.props; | ||
delete _this5.currentlyAnimatingKeys[key]; | ||
// if update on exclusive mode, skip check | ||
if (props.exclusive && props !== _this5.nextProps) { | ||
return; | ||
} | ||
var currentChildren = (0, _ChildrenUtils.toArrayChildren)(getChildrenFromProps(props)); | ||
// in case state change is too fast | ||
if (_this5.isValidChildByKey(currentChildren, key)) { | ||
_this5.performEnter(key); | ||
} else { | ||
var end = function end() { | ||
if (_util2['default'].allowLeaveCallback(props)) { | ||
props.onLeave(key); | ||
props.onEnd(key, false); | ||
} | ||
}; | ||
if (!(0, _ChildrenUtils.isSameChildren)(_this5.state.children, currentChildren, props.showProp)) { | ||
_this5.setState({ | ||
children: currentChildren | ||
}, end); | ||
} else { | ||
end(); | ||
} | ||
} | ||
}; | ||
}; | ||
return Animate; | ||
} | ||
exports['default'] = Animate; | ||
module.exports = exports['default']; | ||
exports['default'] = genAnimate(_AnimateChild2['default']); |
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.__esModule = true; | ||
var _typeof2 = require('babel-runtime/helpers/typeof'); | ||
var _typeof3 = _interopRequireDefault(_typeof2); | ||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); | ||
@@ -15,6 +9,2 @@ | ||
var _createClass2 = require('babel-runtime/helpers/createClass'); | ||
var _createClass3 = _interopRequireDefault(_createClass2); | ||
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); | ||
@@ -28,2 +18,4 @@ | ||
exports.genAnimateChild = genAnimateChild; | ||
var _react = require('react'); | ||
@@ -41,111 +33,451 @@ | ||
var _cssAnimation = require('css-animation'); | ||
var _reactLifecyclesCompat = require('react-lifecycles-compat'); | ||
var _cssAnimation2 = _interopRequireDefault(_cssAnimation); | ||
var _classnames = require('classnames'); | ||
var _classnames2 = _interopRequireDefault(_classnames); | ||
var _componentClasses = require('component-classes'); | ||
var _componentClasses2 = _interopRequireDefault(_componentClasses); | ||
var _raf = require('raf'); | ||
var _raf2 = _interopRequireDefault(_raf); | ||
var _util = require('./util'); | ||
var _util2 = _interopRequireDefault(_util); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
var transitionMap = { | ||
enter: 'transitionEnter', | ||
appear: 'transitionAppear', | ||
leave: 'transitionLeave' | ||
}; | ||
var clonePropList = ['appeared', 'show', 'exclusive', 'children', 'animation']; | ||
var AnimateChild = function (_React$Component) { | ||
(0, _inherits3['default'])(AnimateChild, _React$Component); | ||
/** | ||
* AnimateChild only accept one child node. | ||
* `transitionSupport` is used for none transition test case. | ||
* Default we use browser transition event support check. | ||
*/ | ||
function genAnimateChild(transitionSupport) { | ||
var AnimateChild = function (_React$Component) { | ||
(0, _inherits3['default'])(AnimateChild, _React$Component); | ||
function AnimateChild() { | ||
(0, _classCallCheck3['default'])(this, AnimateChild); | ||
return (0, _possibleConstructorReturn3['default'])(this, (AnimateChild.__proto__ || Object.getPrototypeOf(AnimateChild)).apply(this, arguments)); | ||
} | ||
function AnimateChild() { | ||
(0, _classCallCheck3['default'])(this, AnimateChild); | ||
(0, _createClass3['default'])(AnimateChild, [{ | ||
key: 'componentWillUnmount', | ||
value: function componentWillUnmount() { | ||
this.stop(); | ||
// [Legacy] Since old code addListener on the element. | ||
// To avoid break the behaviour that component not handle animation/transition | ||
// also can handle the animate, let keep the logic. | ||
var _this = (0, _possibleConstructorReturn3['default'])(this, _React$Component.call(this)); | ||
_this.state = { | ||
child: null, | ||
eventQueue: [], | ||
eventActive: false | ||
}; | ||
_this.onDomUpdated = function () { | ||
var eventActive = _this.state.eventActive; | ||
var _this$props = _this.props, | ||
transitionName = _this$props.transitionName, | ||
animation = _this$props.animation, | ||
onChildLeaved = _this$props.onChildLeaved, | ||
animateKey = _this$props.animateKey; | ||
var $ele = _this.getDomElement(); | ||
// Skip if dom element not ready | ||
if (!$ele) return; | ||
// [Legacy] Add animation/transition event by dom level | ||
if (transitionSupport && _this.$prevEle !== $ele) { | ||
_this.cleanDomEvent(); | ||
_this.$prevEle = $ele; | ||
_this.$prevEle.addEventListener(_util.animationEndName, _this.onMotionEnd); | ||
_this.$prevEle.addEventListener(_util.transitionEndName, _this.onMotionEnd); | ||
} | ||
var currentEvent = _this.getCurrentEvent(); | ||
if (currentEvent.empty) { | ||
// Additional process the leave event | ||
if (currentEvent.lastEventType === 'leave') { | ||
onChildLeaved(animateKey); | ||
} | ||
return; | ||
} | ||
var eventType = currentEvent.eventType, | ||
restQueue = currentEvent.restQueue; | ||
var nodeClasses = (0, _componentClasses2['default'])($ele); | ||
// [Legacy] Since origin code use js to set `className`. | ||
// This caused that any component without support `className` can be forced set. | ||
// Let's keep the logic. | ||
function legacyAppendClass() { | ||
if (!transitionSupport) return; | ||
var basicClassName = (0, _util.getTransitionName)(transitionName, '' + eventType); | ||
if (basicClassName) nodeClasses.add(basicClassName); | ||
if (eventActive) { | ||
var activeClassName = (0, _util.getTransitionName)(transitionName, eventType + '-active'); | ||
if (activeClassName) nodeClasses.add(activeClassName); | ||
} | ||
} | ||
if (_this.currentEvent && _this.currentEvent.type === eventType) { | ||
legacyAppendClass(); | ||
return; | ||
} | ||
// Clear timeout for legacy check | ||
clearTimeout(_this.timeout); | ||
// Clean up last event environment | ||
if (_this.currentEvent && _this.currentEvent.animateObj && _this.currentEvent.animateObj.stop) { | ||
_this.currentEvent.animateObj.stop(); | ||
} | ||
// Clean up last transition class | ||
if (_this.currentEvent) { | ||
var basicClassName = (0, _util.getTransitionName)(transitionName, '' + _this.currentEvent.type); | ||
var activeClassName = (0, _util.getTransitionName)(transitionName, _this.currentEvent.type + '-active'); | ||
if (basicClassName) nodeClasses.remove(basicClassName); | ||
if (activeClassName) nodeClasses.remove(activeClassName); | ||
} | ||
// New event come | ||
_this.currentEvent = { | ||
type: eventType | ||
}; | ||
var animationHandler = (animation || {})[eventType]; | ||
// =============== Check if has customize animation =============== | ||
if (animationHandler) { | ||
_this.currentEvent.animateObj = animationHandler($ele, function () { | ||
_this.onMotionEnd({ target: $ele }); | ||
}); | ||
// Do next step if not animate object provided | ||
if (!_this.currentEvent.animateObj) { | ||
_this.nextEvent(restQueue); | ||
} | ||
// ==================== Use transition instead ==================== | ||
} else if (transitionSupport) { | ||
legacyAppendClass(); | ||
if (!eventActive) { | ||
// Trigger `eventActive` in next frame | ||
(0, _raf2['default'])(function () { | ||
if (_this.currentEvent && _this.currentEvent.type === eventType && !_this._destroy) { | ||
_this.setState({ eventActive: true }, function () { | ||
// [Legacy] Handle timeout if browser transition event not handle | ||
var transitionDelay = (0, _util.getStyleValue)($ele, 'transition-delay') || 0; | ||
var transitionDuration = (0, _util.getStyleValue)($ele, 'transition-duration') || 0; | ||
var animationDelay = (0, _util.getStyleValue)($ele, 'animation-delay') || 0; | ||
var animationDuration = (0, _util.getStyleValue)($ele, 'animation-duration') || 0; | ||
var totalTime = Math.max(transitionDuration + transitionDelay, animationDuration + animationDelay); | ||
if (totalTime >= 0) { | ||
_this.timeout = setTimeout(function () { | ||
_this.onMotionEnd({ target: $ele }); | ||
}, totalTime * 1000); | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
// ======================= Just next action ======================= | ||
} else { | ||
_this.onMotionEnd({ target: $ele }); | ||
} | ||
}; | ||
_this.onMotionEnd = function (_ref) { | ||
var target = _ref.target; | ||
var _this$props2 = _this.props, | ||
transitionName = _this$props2.transitionName, | ||
onChildLeaved = _this$props2.onChildLeaved, | ||
animateKey = _this$props2.animateKey, | ||
onAppear = _this$props2.onAppear, | ||
onEnter = _this$props2.onEnter, | ||
onLeave = _this$props2.onLeave, | ||
onEnd = _this$props2.onEnd; | ||
var currentEvent = _this.getCurrentEvent(); | ||
if (currentEvent.empty) return; | ||
// Clear timeout for legacy check | ||
clearTimeout(_this.timeout); | ||
var restQueue = currentEvent.restQueue; | ||
var $ele = _this.getDomElement(); | ||
if (!_this.currentEvent || $ele !== target) return; | ||
if (_this.currentEvent.animateObj && _this.currentEvent.animateObj.stop) { | ||
_this.currentEvent.animateObj.stop(); | ||
} | ||
// [Legacy] Same as above, we need call js to remove the class | ||
if (transitionSupport && _this.currentEvent) { | ||
var basicClassName = (0, _util.getTransitionName)(transitionName, _this.currentEvent.type); | ||
var activeClassName = (0, _util.getTransitionName)(transitionName, _this.currentEvent.type + '-active'); | ||
var nodeClasses = (0, _componentClasses2['default'])($ele); | ||
if (basicClassName) nodeClasses.remove(basicClassName); | ||
if (activeClassName) nodeClasses.remove(activeClassName); | ||
} | ||
// Additional process the leave event | ||
if (_this.currentEvent && _this.currentEvent.type === 'leave') { | ||
onChildLeaved(animateKey); | ||
} | ||
// [Legacy] Trigger on event when it's last event | ||
if (_this.currentEvent && !restQueue.length) { | ||
if (_this.currentEvent.type === 'appear' && onAppear) { | ||
onAppear(animateKey); | ||
} else if (_this.currentEvent.type === 'enter' && onEnter) { | ||
onEnter(animateKey); | ||
} else if (_this.currentEvent.type === 'leave' && onLeave) { | ||
onLeave(animateKey); | ||
} | ||
if (onEnd) { | ||
// OnEnd(key, isShow) | ||
onEnd(animateKey, _this.currentEvent.type !== 'leave'); | ||
} | ||
} | ||
_this.currentEvent = null; | ||
// Next queue | ||
_this.nextEvent(restQueue); | ||
}; | ||
_this.getDomElement = function () { | ||
if (_this._destroy) return null; | ||
return _reactDom2['default'].findDOMNode(_this); | ||
}; | ||
_this.getCurrentEvent = function () { | ||
var _this$state$eventQueu = _this.state.eventQueue, | ||
eventQueue = _this$state$eventQueu === undefined ? [] : _this$state$eventQueu; | ||
var _this$props3 = _this.props, | ||
animation = _this$props3.animation, | ||
exclusive = _this$props3.exclusive, | ||
transitionAppear = _this$props3.transitionAppear, | ||
transitionEnter = _this$props3.transitionEnter, | ||
transitionLeave = _this$props3.transitionLeave; | ||
function hasEventHandler(eventType) { | ||
return eventType === 'appear' && (transitionAppear || animation.appear) || eventType === 'enter' && (transitionEnter || animation.enter) || eventType === 'leave' && (transitionLeave || animation.leave); | ||
} | ||
var event = null; | ||
// If is exclusive, only check the last event | ||
if (exclusive) { | ||
var eventType = eventQueue[eventQueue.length - 1]; | ||
if (hasEventHandler(eventType)) { | ||
event = { | ||
eventType: eventType, | ||
restQueue: [] | ||
}; | ||
} | ||
} else { | ||
// Loop check the queue until find match | ||
var cloneQueue = eventQueue.slice(); | ||
while (cloneQueue.length) { | ||
var _cloneQueue = cloneQueue, | ||
_eventType = _cloneQueue[0], | ||
restQueue = _cloneQueue.slice(1); | ||
if (hasEventHandler(_eventType)) { | ||
event = { | ||
eventType: _eventType, | ||
restQueue: restQueue | ||
}; | ||
break; | ||
} | ||
cloneQueue = restQueue; | ||
} | ||
} | ||
if (!event) { | ||
event = { | ||
empty: true, | ||
lastEventType: eventQueue[eventQueue.length - 1] | ||
}; | ||
} | ||
return event; | ||
}; | ||
_this.nextEvent = function (restQueue) { | ||
// Next queue | ||
if (!_this._destroy) { | ||
_this.setState({ | ||
eventQueue: restQueue, | ||
eventActive: false | ||
}); | ||
} | ||
}; | ||
_this.cleanDomEvent = function () { | ||
if (_this.$prevEle && transitionSupport) { | ||
_this.$prevEle.removeEventListener(_util.animationEndName, _this.onMotionEnd); | ||
_this.$prevEle.removeEventListener(_util.transitionEndName, _this.onMotionEnd); | ||
} | ||
}; | ||
_this.$prevEle = null; | ||
_this.currentEvent = null; | ||
_this.timeout = null; | ||
return _this; | ||
} | ||
}, { | ||
key: 'componentWillEnter', | ||
value: function componentWillEnter(done) { | ||
if (_util2['default'].isEnterSupported(this.props)) { | ||
this.transition('enter', done); | ||
} else { | ||
done(); | ||
AnimateChild.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) { | ||
var _prevState$prevProps = prevState.prevProps, | ||
prevProps = _prevState$prevProps === undefined ? {} : _prevState$prevProps; | ||
var appeared = nextProps.appeared; | ||
var newState = { | ||
prevProps: (0, _util.cloneProps)(nextProps, clonePropList) | ||
}; | ||
function processState(propName, updater) { | ||
if (prevProps[propName] !== nextProps[propName]) { | ||
if (updater) { | ||
updater(nextProps[propName]); | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
} | ||
}, { | ||
key: 'componentWillAppear', | ||
value: function componentWillAppear(done) { | ||
if (_util2['default'].isAppearSupported(this.props)) { | ||
this.transition('appear', done); | ||
} else { | ||
done(); | ||
function pushEvent(eventType) { | ||
var eventQueue = newState.eventQueue || prevState.eventQueue.slice(); | ||
var matchIndex = eventQueue.indexOf(eventType); | ||
// Clean the rest event if eventType match | ||
if (matchIndex !== -1) { | ||
eventQueue = eventQueue.slice(0, matchIndex); | ||
} | ||
eventQueue.push(eventType); | ||
newState.eventQueue = eventQueue; | ||
} | ||
} | ||
}, { | ||
key: 'componentWillLeave', | ||
value: function componentWillLeave(done) { | ||
if (_util2['default'].isLeaveSupported(this.props)) { | ||
this.transition('leave', done); | ||
// Child update. Only set child. | ||
processState('children', function (child) { | ||
newState.child = child; | ||
}); | ||
processState('appeared', function (isAppeared) { | ||
if (isAppeared) { | ||
pushEvent('appear'); | ||
} | ||
}); | ||
// Show update | ||
processState('show', function (show) { | ||
if (!appeared) { | ||
if (show) { | ||
pushEvent('enter'); | ||
} else { | ||
pushEvent('leave'); | ||
} | ||
} | ||
}); | ||
return newState; | ||
}; | ||
AnimateChild.prototype.componentDidMount = function componentDidMount() { | ||
this.onDomUpdated(); | ||
}; | ||
AnimateChild.prototype.componentDidUpdate = function componentDidUpdate() { | ||
this.onDomUpdated(); | ||
}; | ||
AnimateChild.prototype.componentWillUnmount = function componentWillUnmount() { | ||
clearTimeout(this.timeout); | ||
this._destroy = true; | ||
this.cleanDomEvent(); | ||
}; | ||
AnimateChild.prototype.render = function render() { | ||
var _state = this.state, | ||
child = _state.child, | ||
eventActive = _state.eventActive; | ||
var _props = this.props, | ||
showProp = _props.showProp, | ||
transitionName = _props.transitionName; | ||
var _ref2 = child.props || {}, | ||
className = _ref2.className; | ||
var currentEvent = this.getCurrentEvent(); | ||
// Class name | ||
var connectClassName = transitionSupport && this.currentEvent ? (0, _classnames2['default'])(className, (0, _util.getTransitionName)(transitionName, this.currentEvent.type), eventActive && (0, _util.getTransitionName)(transitionName, this.currentEvent.type + '-active')) : className; | ||
var show = true; | ||
// Keep show when is in transition or has customize animate | ||
if (transitionSupport && (!currentEvent.empty || this.currentEvent && this.currentEvent.animateObj)) { | ||
show = true; | ||
} else { | ||
// always sync, do not interupt with react component life cycle | ||
// update hidden -> animate hidden -> | ||
// didUpdate -> animate leave -> unmount (if animate is none) | ||
done(); | ||
show = child.props[showProp]; | ||
} | ||
} | ||
}, { | ||
key: 'transition', | ||
value: function transition(animationType, finishCallback) { | ||
var _this2 = this; | ||
var node = _reactDom2['default'].findDOMNode(this); | ||
var props = this.props; | ||
var transitionName = props.transitionName; | ||
var nameIsObj = (typeof transitionName === 'undefined' ? 'undefined' : (0, _typeof3['default'])(transitionName)) === 'object'; | ||
this.stop(); | ||
var end = function end() { | ||
_this2.stopper = null; | ||
finishCallback(); | ||
// Clone child | ||
var newChildProps = { | ||
className: connectClassName | ||
}; | ||
if ((_cssAnimation.isCssAnimationSupported || !props.animation[animationType]) && transitionName && props[transitionMap[animationType]]) { | ||
var name = nameIsObj ? transitionName[animationType] : transitionName + '-' + animationType; | ||
var activeName = name + '-active'; | ||
if (nameIsObj && transitionName[animationType + 'Active']) { | ||
activeName = transitionName[animationType + 'Active']; | ||
} | ||
this.stopper = (0, _cssAnimation2['default'])(node, { | ||
name: name, | ||
active: activeName | ||
}, end); | ||
} else { | ||
this.stopper = props.animation[animationType](node, end); | ||
if (showProp) { | ||
newChildProps[showProp] = show; | ||
} | ||
} | ||
}, { | ||
key: 'stop', | ||
value: function stop() { | ||
var stopper = this.stopper; | ||
if (stopper) { | ||
this.stopper = null; | ||
stopper.stop(); | ||
} | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
return this.props.children; | ||
} | ||
}]); | ||
return _react2['default'].cloneElement(child, newChildProps); | ||
}; | ||
return AnimateChild; | ||
}(_react2['default'].Component); | ||
AnimateChild.propTypes = { | ||
transitionName: _propTypes2['default'].oneOfType([_propTypes2['default'].string, _propTypes2['default'].object]), | ||
transitionAppear: _propTypes2['default'].bool, | ||
transitionEnter: _propTypes2['default'].bool, | ||
transitionLeave: _propTypes2['default'].bool, | ||
exclusive: _propTypes2['default'].bool, | ||
appeared: _propTypes2['default'].bool, | ||
showProp: _propTypes2['default'].string, | ||
animateKey: _propTypes2['default'].any, | ||
animation: _propTypes2['default'].object, | ||
onChildLeaved: _propTypes2['default'].func, | ||
onEnd: _propTypes2['default'].func, | ||
onAppear: _propTypes2['default'].func, | ||
onEnter: _propTypes2['default'].func, | ||
onLeave: _propTypes2['default'].func | ||
}; | ||
(0, _reactLifecyclesCompat.polyfill)(AnimateChild); | ||
return AnimateChild; | ||
}(_react2['default'].Component); | ||
} | ||
AnimateChild.propTypes = { | ||
children: _propTypes2['default'].any | ||
}; | ||
exports['default'] = AnimateChild; | ||
module.exports = exports['default']; | ||
exports['default'] = genAnimateChild(_util.supportTransition); |
204
lib/util.js
@@ -1,27 +0,181 @@ | ||
"use strict"; | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var util = { | ||
isAppearSupported: function isAppearSupported(props) { | ||
return props.transitionName && props.transitionAppear || props.animation.appear; | ||
}, | ||
isEnterSupported: function isEnterSupported(props) { | ||
return props.transitionName && props.transitionEnter || props.animation.enter; | ||
}, | ||
isLeaveSupported: function isLeaveSupported(props) { | ||
return props.transitionName && props.transitionLeave || props.animation.leave; | ||
}, | ||
allowAppearCallback: function allowAppearCallback(props) { | ||
return props.transitionAppear || props.animation.appear; | ||
}, | ||
allowEnterCallback: function allowEnterCallback(props) { | ||
return props.transitionEnter || props.animation.enter; | ||
}, | ||
allowLeaveCallback: function allowLeaveCallback(props) { | ||
return props.transitionLeave || props.animation.leave; | ||
exports.__esModule = true; | ||
exports.supportTransition = exports.transitionEndName = exports.animationEndName = undefined; | ||
exports.getStyleProperty = getStyleProperty; | ||
exports.getStyleValue = getStyleValue; | ||
exports.getVendorPrefixes = getVendorPrefixes; | ||
exports.getVendorPrefixedEventName = getVendorPrefixedEventName; | ||
exports.mergeChildren = mergeChildren; | ||
exports.cloneProps = cloneProps; | ||
exports.getTransitionName = getTransitionName; | ||
var _toArray = require('rc-util/lib/Children/toArray'); | ||
var _toArray2 = _interopRequireDefault(_toArray); | ||
var _ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
// =================== Style ==================== | ||
var stylePrefixes = ['-webkit-', '-moz-', '-o-', 'ms-', '']; | ||
function getStyleProperty(node, name) { | ||
// old ff need null, https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle | ||
var style = window.getComputedStyle(node, null); | ||
var ret = ''; | ||
for (var i = 0; i < stylePrefixes.length; i++) { | ||
ret = style.getPropertyValue(stylePrefixes[i] + name); | ||
if (ret) { | ||
break; | ||
} | ||
} | ||
}; | ||
exports["default"] = util; | ||
module.exports = exports['default']; | ||
return ret; | ||
} | ||
function getStyleValue(node, name) { | ||
return parseFloat(getStyleProperty(node, name)); | ||
} | ||
// ================= Transition ================= | ||
// Event wrapper. Copy from react source code | ||
function makePrefixMap(styleProp, eventName) { | ||
var prefixes = {}; | ||
prefixes[styleProp.toLowerCase()] = eventName.toLowerCase(); | ||
prefixes['Webkit' + styleProp] = 'webkit' + eventName; | ||
prefixes['Moz' + styleProp] = 'moz' + eventName; | ||
prefixes['ms' + styleProp] = 'MS' + eventName; | ||
prefixes['O' + styleProp] = 'o' + eventName.toLowerCase(); | ||
return prefixes; | ||
} | ||
function getVendorPrefixes(domSupport, win) { | ||
var prefixes = { | ||
animationend: makePrefixMap('Animation', 'AnimationEnd'), | ||
transitionend: makePrefixMap('Transition', 'TransitionEnd') | ||
}; | ||
if (domSupport) { | ||
if (!('AnimationEvent' in win)) { | ||
delete prefixes.animationend.animation; | ||
} | ||
if (!('TransitionEvent' in win)) { | ||
delete prefixes.transitionend.transition; | ||
} | ||
} | ||
return prefixes; | ||
} | ||
var vendorPrefixes = getVendorPrefixes(_ExecutionEnvironment.canUseDOM, window); | ||
var style = {}; | ||
if (_ExecutionEnvironment.canUseDOM) { | ||
style = document.createElement('div').style; | ||
} | ||
var prefixedEventNames = {}; | ||
function getVendorPrefixedEventName(eventName) { | ||
if (prefixedEventNames[eventName]) { | ||
return prefixedEventNames[eventName]; | ||
} | ||
var prefixMap = vendorPrefixes[eventName]; | ||
if (prefixMap) { | ||
var stylePropList = Object.keys(prefixMap); | ||
var len = stylePropList.length; | ||
for (var i = 0; i < len; i += 1) { | ||
var styleProp = stylePropList[i]; | ||
if (Object.prototype.hasOwnProperty.call(prefixMap, styleProp) && styleProp in style) { | ||
prefixedEventNames[eventName] = prefixMap[styleProp]; | ||
return prefixedEventNames[eventName]; | ||
} | ||
} | ||
} | ||
return ''; | ||
} | ||
var animationEndName = exports.animationEndName = getVendorPrefixedEventName('animationend'); | ||
var transitionEndName = exports.transitionEndName = getVendorPrefixedEventName('transitionend'); | ||
var supportTransition = exports.supportTransition = !!(animationEndName && transitionEndName); | ||
// ==================== Node ==================== | ||
/** | ||
* [Legacy] Find the same children in both prev & next list. | ||
* Insert not find one before the find one, otherwise in the end. For example: | ||
* - prev: [1,2,3] | ||
* - next: [2,4] | ||
* -> [1,2,4,3] | ||
*/ | ||
function mergeChildren(prev, next) { | ||
var prevList = (0, _toArray2['default'])(prev); | ||
var nextList = (0, _toArray2['default'])(next); | ||
// Skip if is single children | ||
if (prevList.length === 1 && nextList.length === 1 && prevList[0].key === nextList[0].key) { | ||
return nextList; | ||
} | ||
var mergeList = []; | ||
var nextChildrenMap = {}; | ||
var missMatchChildrenList = []; | ||
// Fill matched prev node into next node map | ||
prevList.forEach(function (prevNode) { | ||
if (prevNode && nextList.some(function (_ref) { | ||
var key = _ref.key; | ||
return key === prevNode.key; | ||
})) { | ||
if (missMatchChildrenList.length) { | ||
nextChildrenMap[prevNode.key] = missMatchChildrenList; | ||
missMatchChildrenList = []; | ||
} | ||
} else { | ||
missMatchChildrenList.push(prevNode); | ||
} | ||
}); | ||
// Insert prev node before the matched next node | ||
nextList.forEach(function (nextNode) { | ||
if (nextNode && nextChildrenMap[nextNode.key]) { | ||
mergeList = mergeList.concat(nextChildrenMap[nextNode.key]); | ||
} | ||
mergeList.push(nextNode); | ||
}); | ||
mergeList = mergeList.concat(missMatchChildrenList); | ||
return mergeList; | ||
} | ||
function cloneProps(props, propList) { | ||
var newProps = {}; | ||
propList.forEach(function (prop) { | ||
if (prop in props) { | ||
newProps[prop] = props[prop]; | ||
} | ||
}); | ||
return newProps; | ||
} | ||
function getTransitionName(transitionName, transitionType) { | ||
if (!transitionName) return null; | ||
if (typeof transitionName === 'object') { | ||
var type = transitionType.replace(/-\w/g, function (match) { | ||
return match[1].toUpperCase(); | ||
}); | ||
return transitionName[type]; | ||
} | ||
return transitionName + '-' + transitionType; | ||
} |
{ | ||
"name": "rc-animate", | ||
"version": "2.4.4", | ||
"version": "3.0.0-rc.0", | ||
"description": "css-transition ui component for react", | ||
@@ -46,8 +46,7 @@ "keywords": [ | ||
"expect.js": "0.3.x", | ||
"jquery": "~1.11.3", | ||
"object-assign": "4.x", | ||
"jquery": "^3.3.1", | ||
"pre-commit": "1.x", | ||
"rc-test": "6.x", | ||
"rc-tools": "6.x", | ||
"react": "^16.0.0", | ||
"rc-tools": "8.x", | ||
"react": "^16.3.0", | ||
"react-dom": "^16.0.0", | ||
@@ -61,5 +60,10 @@ "velocity-animate": "~1.2.2" | ||
"babel-runtime": "6.x", | ||
"css-animation": "^1.3.2", | ||
"prop-types": "15.x" | ||
"classnames": "^2.2.5", | ||
"component-classes": "^1.2.6", | ||
"fbjs": "^0.8.16", | ||
"prop-types": "15.x", | ||
"raf": "^3.4.0", | ||
"rc-util": "^4.5.0", | ||
"react-lifecycles-compat": "^3.0.4" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
62149
9
1363
8
10
1
2
+ Addedclassnames@^2.2.5
+ Addedcomponent-classes@^1.2.6
+ Addedfbjs@^0.8.16
+ Addedraf@^3.4.0
+ Addedrc-util@^4.5.0
+ Addedadd-dom-event-listener@1.1.0(transitive)
+ Addedasap@2.0.6(transitive)
+ Addedclassnames@2.5.1(transitive)
+ Addedcore-js@1.2.7(transitive)
+ Addedencoding@0.1.13(transitive)
+ Addedfbjs@0.8.18(transitive)
+ Addediconv-lite@0.6.3(transitive)
+ Addedis-stream@1.1.0(transitive)
+ Addedisomorphic-fetch@2.2.1(transitive)
+ Addednode-fetch@1.7.3(transitive)
+ Addedperformance-now@2.1.0(transitive)
+ Addedpromise@7.3.1(transitive)
+ Addedraf@3.4.1(transitive)
+ Addedrc-util@4.21.1(transitive)
+ Addedreact-lifecycles-compat@3.0.4(transitive)
+ Addedsafer-buffer@2.1.2(transitive)
+ Addedsetimmediate@1.0.5(transitive)
+ Addedshallowequal@1.1.0(transitive)
+ Addedua-parser-js@0.7.39(transitive)
+ Addedwhatwg-fetch@3.6.20(transitive)
- Removedcss-animation@^1.3.2
- Removedcss-animation@1.6.1(transitive)