/** * Copyright 2013-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ 'use strict'; var _assign = require('object-assign'); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var React = require('./React'); var ReactTransitionChildMapping = require('./ReactTransitionChildMapping'); var propTypesFactory = require('prop-types/factory'); var PropTypes = propTypesFactory(React.isValidElement); var emptyFunction = require('fbjs/lib/emptyFunction'); /** * A basis for animations. When children are declaratively added or removed, * special lifecycle hooks are called. * See https://facebook.github.io/react/docs/animation.html#low-level-api-reacttransitiongroup */ var ReactTransitionGroup = function (_React$Component) { _inherits(ReactTransitionGroup, _React$Component); function ReactTransitionGroup() { var _temp, _this, _ret; _classCallCheck(this, ReactTransitionGroup); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _ret = (_temp = (_this = _possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.state = { // TODO: can we get useful debug information to show at this point? children: ReactTransitionChildMapping.getChildMapping(_this.props.children) }, _this.performAppear = function (key) { _this.currentlyTransitioningKeys[key] = true; var component = _this.refs[key]; if (component.componentWillAppear) { component.componentWillAppear(_this._handleDoneAppearing.bind(_this, key)); } else { _this._handleDoneAppearing(key); } }, _this._handleDoneAppearing = function (key) { var component = _this.refs[key]; if (component.componentDidAppear) { component.componentDidAppear(); } delete _this.currentlyTransitioningKeys[key]; var currentChildMapping = ReactTransitionChildMapping.getChildMapping(_this.props.children); if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) { // This was removed before it had fully appeared. Remove it. _this.performLeave(key); } }, _this.performEnter = function (key) { _this.currentlyTransitioningKeys[key] = true; var component = _this.refs[key]; if (component.componentWillEnter) { component.componentWillEnter(_this._handleDoneEntering.bind(_this, key)); } else { _this._handleDoneEntering(key); } }, _this._handleDoneEntering = function (key) { var component = _this.refs[key]; if (component.componentDidEnter) { component.componentDidEnter(); } delete _this.currentlyTransitioningKeys[key]; var currentChildMapping = ReactTransitionChildMapping.getChildMapping(_this.props.children); if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) { // This was removed before it had fully entered. Remove it. _this.performLeave(key); } }, _this.performLeave = function (key) { _this.currentlyTransitioningKeys[key] = true; var component = _this.refs[key]; if (component.componentWillLeave) { component.componentWillLeave(_this._handleDoneLeaving.bind(_this, key)); } else { // Note that this is somewhat dangerous b/c it calls setState() // again, effectively mutating the component before all the work // is done. _this._handleDoneLeaving(key); } }, _this._handleDoneLeaving = function (key) { var component = _this.refs[key]; if (component.componentDidLeave) { component.componentDidLeave(); } delete _this.currentlyTransitioningKeys[key]; var currentChildMapping = ReactTransitionChildMapping.getChildMapping(_this.props.children); if (currentChildMapping && currentChildMapping.hasOwnProperty(key)) { // This entered again before it fully left. Add it again. _this.performEnter(key); } else { _this.setState(function (state) { var newChildren = _assign({}, state.children); delete newChildren[key]; return { children: newChildren }; }); } }, _temp), _possibleConstructorReturn(_this, _ret); } ReactTransitionGroup.prototype.componentWillMount = function componentWillMount() { this.currentlyTransitioningKeys = {}; this.keysToEnter = []; this.keysToLeave = []; }; ReactTransitionGroup.prototype.componentDidMount = function componentDidMount() { var initialChildMapping = this.state.children; for (var key in initialChildMapping) { if (initialChildMapping[key]) { this.performAppear(key); } } }; ReactTransitionGroup.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { var nextChildMapping = ReactTransitionChildMapping.getChildMapping(nextProps.children); var prevChildMapping = this.state.children; this.setState({ children: ReactTransitionChildMapping.mergeChildMappings(prevChildMapping, nextChildMapping) }); var key; for (key in nextChildMapping) { var hasPrev = prevChildMapping && prevChildMapping.hasOwnProperty(key); if (nextChildMapping[key] && !hasPrev && !this.currentlyTransitioningKeys[key]) { this.keysToEnter.push(key); } } for (key in prevChildMapping) { var hasNext = nextChildMapping && nextChildMapping.hasOwnProperty(key); if (prevChildMapping[key] && !hasNext && !this.currentlyTransitioningKeys[key]) { this.keysToLeave.push(key); } } // If we want to someday check for reordering, we could do it here. }; ReactTransitionGroup.prototype.componentDidUpdate = function componentDidUpdate() { var keysToEnter = this.keysToEnter; this.keysToEnter = []; keysToEnter.forEach(this.performEnter); var keysToLeave = this.keysToLeave; this.keysToLeave = []; keysToLeave.forEach(this.performLeave); }; ReactTransitionGroup.prototype.render = function render() { // TODO: we could get rid of the need for the wrapper node // by cloning a single child var childrenToRender = []; for (var key in this.state.children) { var child = this.state.children[key]; if (child) { // You may need to apply reactive updates to a child as it is leaving. // The normal React way to do it won't work since the child will have // already been removed. In case you need this behavior you can provide // a childFactory function to wrap every child, even the ones that are // leaving. childrenToRender.push(React.cloneElement(this.props.childFactory(child), { ref: key, key: key })); } } // Do not forward ReactTransitionGroup props to primitive DOM nodes var props = _assign({}, this.props); delete props.transitionLeave; delete props.transitionName; delete props.transitionAppear; delete props.transitionEnter; delete props.childFactory; delete props.transitionLeaveTimeout; delete props.transitionEnterTimeout; delete props.transitionAppearTimeout; delete props.component; return React.createElement(this.props.component, props, childrenToRender); }; return ReactTransitionGroup; }(React.Component); ReactTransitionGroup.displayName = 'ReactTransitionGroup'; ReactTransitionGroup.propTypes = { component: PropTypes.any, childFactory: PropTypes.func }; ReactTransitionGroup.defaultProps = { component: 'span', childFactory: emptyFunction.thatReturnsArgument }; module.exports = ReactTransitionGroup;