UNPKG

react-widgets

Version:
217 lines (161 loc) 5.69 kB
/** React.DOM */ /** * A streamlined version of TransitionGroup built for managing at most two 'active' children * also provides additional hooks for animation start/end * https://github.com/facebook/react/blob/master/src/addons/transitions/ReactTransitionGroup.js * relevent code is licensed accordingly */ "use strict"; var React = require('react') , cloneWithProps = require('../util/transferProps').cloneWithProps , $ = require('../util/dom') , _ = require('lodash'); module.exports = React.createClass({ displayName: 'ReplaceTransitionGroup', propTypes: { component: React.PropTypes.func, childFactory: React.PropTypes.func, onAnimating: React.PropTypes.func, onAnimate: React.PropTypes.func, }, getDefaultProps: function() { return { component: React.DOM.span, childFactory: function(a){ return a }, onAnimating: _.noop, onAnimate: _.noop }; }, getInitialState: function() { return { children: React.Children.map(this.props.children, function(c){ return c }) }; }, componentWillReceiveProps: function(nextProps) { var nextChildMapping = React.Children.map(nextProps.children, function(c){ return c }) , prevChildMapping = this.state.children , newChildren = {}; //console.log(prevChildMapping, nextChildMapping) this.setState({ children: _.extend({}, prevChildMapping, nextChildMapping) }); _.any(nextChildMapping, function(v, key){ var hasPrev = prevChildMapping && _.has(prevChildMapping, key) , isNext = !hasPrev && !this.currentlyTransitioningKeys[key]; if ( isNext ) { this.next = key; } return isNext }, this) _.any(prevChildMapping, function(v, key){ var hasNext = nextChildMapping && _.has(nextChildMapping, key) , isCurrent = !hasNext && !this.currentlyTransitioningKeys[key]; if ( isCurrent ) { this.current = key; } return isCurrent }, this) }, componentWillMount: function() { this.currentlyTransitioningKeys = {}; this.current = null; this.next = null; }, componentDidUpdate: function() { var current = this.current , next = this.next , first = this.refs[current || next] , node = this.getDOMNode() , el = first && first.getDOMNode() , ht, wt; if( el ) { ht = $.height(el) + 'px' wt = $.width(el) + 'px' $.css(node, { overflow: 'hidden', height: ht, width: wt }) } this.props.onAnimating(); this.next = null; this.current = null; if ( next ) this.performEnter(next) if ( current) this.performLeave(current) }, 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); }, _tryFinish: function(){ var node = this.getDOMNode() if ( this.isTransitioning() ) return $.css(node, { overflow: 'visible', height: '', width: '' }) this.props.onAnimate() }, _handleDoneEntering: function(key) { var component = this.refs[key]; if (component.componentDidEnter) component.componentDidEnter(); delete this.currentlyTransitioningKeys[key]; var currentChildMapping = React.Children.map(this.props.children, function(c){ return c }) if (!currentChildMapping || !_.has(currentChildMapping, key)) this.performLeave(key); // This was removed before it had fully entered. Remove it. this._tryFinish() }, isTransitioning: function(){ return Object.keys(this.currentlyTransitioningKeys).length !== 0 }, performLeave: function(key) { var component = this.refs[key]; this.currentlyTransitioningKeys[key] = true; if (component.componentWillLeave) component.componentWillLeave(this._handleDoneLeaving.bind(this, key)); else this._handleDoneLeaving(key); }, _handleDoneLeaving: function(key) { var component = this.refs[key]; if (component.componentDidLeave) component.componentDidLeave(); delete this.currentlyTransitioningKeys[key]; var currentChildMapping = React.Children.map(this.props.children, function(c){ return c }) if (currentChildMapping && currentChildMapping.hasOwnProperty(key)) this.performEnter(key); // This entered again before it fully left. Add it again. else { var newChildren = _.extend({}, this.state.children); delete newChildren[key]; this.setState({children: newChildren}); } this._tryFinish() }, render: function() { var childrenToRender = {}; for (var key in this.state.children) { var child = this.state.children[key]; if (child) { childrenToRender[key] = cloneWithProps( this.props.childFactory(child), { ref: key } ); } } //console.log(Object.keys(childrenToRender)) // if ( _.size(childrenToRender) === 0) // return React.DOM.noscript() // if ( _.size(childrenToRender) === 1) // for( var k in childrenToRender) // return this.transferPropsTo(childrenToRender[k]) return this.transferPropsTo(this.props.component(null, childrenToRender)); } });