/** * Copyright 2013-2015, 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. * * @typechecks * @providesModule ReactCSSTransitionGroupChild */ 'use strict'; var React = require("./React"); var CSSCore = require("./CSSCore"); var ReactTransitionEvents = require("./ReactTransitionEvents"); var onlyChild = require("./onlyChild"); var warning = require("./warning"); // We don't remove the element from the DOM until we receive an animationend or // transitionend event. If the user screws up and forgets to add an animation // their node will be stuck in the DOM forever, so we detect if an animation // does not start and if it doesn't, we just call the end listener immediately. var TICK = 17; var NO_EVENT_TIMEOUT = 5000; var noEventListener = null; if ("production" !== process.env.NODE_ENV) { noEventListener = function() { ("production" !== process.env.NODE_ENV ? warning( false, 'transition(): tried to perform an animation without ' + 'an animationend or transitionend event after timeout (' + '%sms). You should either disable this ' + 'transition in JS or add a CSS animation/transition.', NO_EVENT_TIMEOUT ) : null); }; } var ReactCSSTransitionGroupChild = React.createClass({ displayName: 'ReactCSSTransitionGroupChild', transition: function(animationType, finishCallback) { var node = this.getDOMNode(); var className = this.props.name + '-' + animationType; var activeClassName = className + '-active'; var noEventTimeout = null; var endListener = function(e) { if (e && e.target !== node) { return; } if ("production" !== process.env.NODE_ENV) { clearTimeout(noEventTimeout); } CSSCore.removeClass(node, className); CSSCore.removeClass(node, activeClassName); ReactTransitionEvents.removeEndEventListener(node, endListener); // Usually this optional callback is used for informing an owner of // a leave animation and telling it to remove the child. if (finishCallback) { finishCallback(); } }; ReactTransitionEvents.addEndEventListener(node, endListener); CSSCore.addClass(node, className); // Need to do this to actually trigger a transition. this.queueClass(activeClassName); if ("production" !== process.env.NODE_ENV) { noEventTimeout = setTimeout(noEventListener, NO_EVENT_TIMEOUT); } }, queueClass: function(className) { this.classNameQueue.push(className); if (!this.timeout) { this.timeout = setTimeout(this.flushClassNameQueue, TICK); } }, flushClassNameQueue: function() { if (this.isMounted()) { this.classNameQueue.forEach( CSSCore.addClass.bind(CSSCore, this.getDOMNode()) ); } this.classNameQueue.length = 0; this.timeout = null; }, componentWillMount: function() { this.classNameQueue = []; }, componentWillUnmount: function() { if (this.timeout) { clearTimeout(this.timeout); } }, componentWillAppear: function(done) { if (this.props.appear) { this.transition('appear', done); } else { done(); } }, componentWillEnter: function(done) { if (this.props.enter) { this.transition('enter', done); } else { done(); } }, componentWillLeave: function(done) { if (this.props.leave) { this.transition('leave', done); } else { done(); } }, render: function() { return onlyChild(this.props.children); } }); module.exports = ReactCSSTransitionGroupChild;