<commit_before>/**
 * Adapted from ReactCSSTransitionGroup.js by Facebook
 *
 * @providesModule ReactCSSTransitionReplace
 */

import React from 'react';
import ReactDOM from 'react-dom';
import objectAssign from 'react/lib/Object.assign';

import ReactCSSTransitionGroupChild from 'react/lib/ReactCSSTransitionGroupChild';

const reactCSSTransitionGroupChild = React.createFactory(ReactCSSTransitionGroupChild);

function createTransitionTimeoutPropValidator(transitionType) {
  const timeoutPropName = 'transition' + transitionType + 'Timeout';
  const enabledPropName = 'transition' + transitionType;

  return function(props) {
    // If the transition is enabled
    if (props[enabledPropName]) {
      // If no timeout duration is provided
      if (!props[timeoutPropName]) {
        return new Error(timeoutPropName + ' wasn\'t supplied to ReactCSSTransitionReplace: '
          + 'this can cause unreliable animations and won\'t be supported in '
          + 'a future version of React. See '
          + 'https://fb.me/react-animation-transition-group-timeout for more ' + 'information.');

        // If the duration isn't a number
      }
      else if (typeof props[timeoutPropName] !== 'number') {
        return new Error(timeoutPropName + ' must be a number (in milliseconds)');
      }
    }
  };
}

export default class ReactCSSTransitionReplace extends React.Component {

  static propTypes = {
    transitionName: React.PropTypes.string.isRequired,
    transitionAppear: React.PropTypes.bool,
    transitionEnter: React.PropTypes.bool,
    transitionLeave: React.PropTypes.bool,
    transitionAppearTimeout: createTransitionTimeoutPropValidator('Appear'),
    transitionEnterTimeout: createTransitionTimeoutPropValidator('Enter'),
    transitionLeaveTimeout: createTransitionTimeoutPropValidator('Leave'),
    overflowHidden: React.PropTypes.bool
  };

  static defaultProps = {
    transitionAppear: false,
    transitionEnter: true,
    transitionLeave: true,
    overflowHidden: true,
    component: 'span'
  };

  state = {
    currentChild: React.Children.only(this.props.children),
    nextChild: null
  };

  componentDidMount() {
    if (this.props.transitionAppear && this.state.currentChild) {
      this.appearCurrent();
    }
  }

  componentWillReceiveProps(nextProps) {
    const nextChild = nextProps.children ? React.Children.only(nextProps.children) : null;
    const currentChild = this.state.currentChild;

    if (currentChild && nextChild && nextChild.key === currentChild.key) {
      // Nothing changed, but we are re-rendering so update the currentChild.
      return this.setState({
        currentChild: nextChild
      });
    }

    // Set the next child to start the transition,
    // also set the current and next height to trigger its transition.
    this.setState({
      nextChild,
      height: ReactDOM.findDOMNode(this.refs.curr).offsetHeight
    }, () => this.setState({
      height: ReactDOM.findDOMNode(this.refs.next).offsetHeight
    }));
  }

  componentDidUpdate() {
    if (this.state.nextChild && !this.isTransitioning) {
      this.enterNext();
      this.leaveCurrent();
    }
  }

  _wrapChild(child, moreProps) {
    // We need to provide this childFactory so that
    // ReactCSSTransitionReplaceChild can receive updates to name,
    // enter, and leave while it is leaving.
    return reactCSSTransitionGroupChild(objectAssign({
      name: this.props.transitionName,
      appear: this.props.transitionAppear,
      enter: this.props.transitionEnter,
      leave: this.props.transitionLeave,
      appearTimeout: this.props.transitionAppearTimeout,
      enterTimeout: this.props.transitionEnterTimeout,
      leaveTimeout: this.props.transitionLeaveTimeout
    }, moreProps), child);
  }

  appearCurrent() {
    this.refs.curr.componentWillAppear(this._handleDoneAppearing);
    this.isTransitioning = true;
  }

  _handleDoneAppearing = () => {
    this.isTransitioning = false;
  }

  enterNext() {
    this.refs.next.componentWillEnter(this._handleDoneEntering);
    this.isTransitioning = true;
  }

  _handleDoneEntering = () => {
    this.isTransitioning = false;
    this.setState({
      currentChild: this.state.nextChild,
      nextChild: null,
      height: null
    });
  }

  leaveCurrent() {
    this.refs.curr.componentWillLeave(this._handleDoneLeaving);
  }

  // When the leave transition timeOut expires the animation classes are removed, so the
  // element must be removed from the DOM if the enter transition is still in progress.
  _handleDoneLeaving = () => {
    if (this.isTransitioning) {
      this.setState({
        currentChild: null
      });
    }
  };

  render() {
    const { currentChild, nextChild, height } = this.state;
    const childrenToRender = [];

    const { overflowHidden, ...containerProps } = this.props;

    if (currentChild) {
      childrenToRender.push(this._wrapChild(currentChild, {
        ref: 'curr', key: 'curr'
      }));
    }

    if (nextChild) {
      objectAssign(containerProps, {
        className: `${containerProps.className || ''} ${containerProps.transitionName}-height`,
        style: objectAssign({}, containerProps.style, {
          position: 'relative',
          display: 'block',
          height
        })
      });

      if (overflowHidden) {
        containerProps.style.overflow = 'hidden';
      }

      childrenToRender.push(
        React.createElement('span',
          {
            style: {
              position: 'absolute',
              left: 0,
              top: 0
            },
            key: 'next'
          },
          this._wrapChild(nextChild, {ref: 'next'})
        )
      );
    }

    return React.createElement(this.props.component, containerProps, childrenToRender);
  }
}<commit_msg>Support no children so the child component can be removed / added.<commit_after>/**
 * Adapted from ReactCSSTransitionGroup.js by Facebook
 *
 * @providesModule ReactCSSTransitionReplace
 */

import React from 'react';
import ReactDOM from 'react-dom';
import objectAssign from 'react/lib/Object.assign';

import ReactCSSTransitionGroupChild from 'react/lib/ReactCSSTransitionGroupChild';

const reactCSSTransitionGroupChild = React.createFactory(ReactCSSTransitionGroupChild);

const TICK = 17;


function createTransitionTimeoutPropValidator(transitionType) {
  const timeoutPropName = 'transition' + transitionType + 'Timeout';
  const enabledPropName = 'transition' + transitionType;

  return function(props) {
    // If the transition is enabled
    if (props[enabledPropName]) {
      // If no timeout duration is provided
      if (!props[timeoutPropName]) {
        return new Error(timeoutPropName + ' wasn\'t supplied to ReactCSSTransitionReplace: '
          + 'this can cause unreliable animations and won\'t be supported in '
          + 'a future version of React. See '
          + 'https://fb.me/react-animation-transition-group-timeout for more ' + 'information.');

        // If the duration isn't a number
      }
      else if (typeof props[timeoutPropName] !== 'number') {
        return new Error(timeoutPropName + ' must be a number (in milliseconds)');
      }
    }
  };
}

export default class ReactCSSTransitionReplace extends React.Component {

  static propTypes = {
    transitionName: React.PropTypes.string.isRequired,
    transitionAppear: React.PropTypes.bool,
    transitionEnter: React.PropTypes.bool,
    transitionLeave: React.PropTypes.bool,
    transitionAppearTimeout: createTransitionTimeoutPropValidator('Appear'),
    transitionEnterTimeout: createTransitionTimeoutPropValidator('Enter'),
    transitionLeaveTimeout: createTransitionTimeoutPropValidator('Leave'),
    overflowHidden: React.PropTypes.bool
  };

  static defaultProps = {
    transitionAppear: false,
    transitionEnter: true,
    transitionLeave: true,
    overflowHidden: true,
    component: 'span'
  };

  state = {
    currentChild: this.props.children ? React.Children.only(this.props.children) : null,
    nextChild: null,
    height: null
  };

  componentDidMount() {
    if (this.props.transitionAppear && this.state.currentChild) {
      this.appearCurrent();
    }
  }

  componentWillUnmount() {
    if (this.timeout) {
      clearTimeout(this.timeout);
    }
  }

  componentWillReceiveProps(nextProps) {
    // Setting false indicates that the child has changed, but it is a removal so there is no next child.
    const nextChild = nextProps.children ? React.Children.only(nextProps.children) : false;
    const currentChild = this.state.currentChild;

    if (currentChild && nextChild && nextChild.key === currentChild.key) {
      // Nothing changed, but we are re-rendering so update the currentChild.
      return this.setState({
        currentChild: nextChild
      });
    }

    // Set the next child to start the transition, and set the current height.
    this.setState({
      nextChild,
      height: this.state.currentChild ? ReactDOM.findDOMNode(this.refs.curr).offsetHeight : 0
    });

    // Enqueue setting the next height to trigger the height transition.
    this.timeout = setTimeout(() => {
      this.setState({height: this.state.nextChild ? ReactDOM.findDOMNode(this.refs.next).offsetHeight : 0});
      this.timeout = null;
    }, TICK);
  }

  componentDidUpdate() {
    if (!this.isTransitioning) {
      if (this.state.nextChild) {
        this.enterNext();
      }
      if (this.state.currentChild && (this.state.nextChild || this.state.nextChild === false)) {
        this.leaveCurrent();
      }
    }
  }

  appearCurrent() {
    this.refs.curr.componentWillAppear(this._handleDoneAppearing);
    this.isTransitioning = true;
  }

  _handleDoneAppearing = () => {
    this.isTransitioning = false;
  }

  enterNext() {
    this.refs.next.componentWillEnter(this._handleDoneEntering);
    this.isTransitioning = true;
  }

  _handleDoneEntering = () => {
    this.isTransitioning = false;
    this.setState({
      currentChild: this.state.nextChild,
      nextChild: null,
      height: null
    });
  }

  leaveCurrent() {
    this.refs.curr.componentWillLeave(this._handleDoneLeaving);
    this.isTransitioning = true;
  }

  // When the leave transition time-out expires the animation classes are removed, so the
  // element must be removed from the DOM if the enter transition is still in progress.
  _handleDoneLeaving = () => {
    if (this.isTransitioning) {
      const state = {currentChild: null};

      if (!this.state.nextChild) {
        this.isTransitioning = false;
        state.height = null;
      }

      this.setState(state);
    }
  };

  _wrapChild(child, moreProps) {
    // We need to provide this childFactory so that
    // ReactCSSTransitionReplaceChild can receive updates to name,
    // enter, and leave while it is leaving.
    return reactCSSTransitionGroupChild(objectAssign({
      name: this.props.transitionName,
      appear: this.props.transitionAppear,
      enter: this.props.transitionEnter,
      leave: this.props.transitionLeave,
      appearTimeout: this.props.transitionAppearTimeout,
      enterTimeout: this.props.transitionEnterTimeout,
      leaveTimeout: this.props.transitionLeaveTimeout
    }, moreProps), child);
  }

  render() {
    const { currentChild, nextChild, height } = this.state;
    const childrenToRender = [];

    const { overflowHidden, ...containerProps } = this.props;

    if (currentChild) {
      childrenToRender.push(this._wrapChild(currentChild, {
        ref: 'curr', key: 'curr'
      }));
    }

    if (height !== null) {
      containerProps.className = `${containerProps.className || ''} ${containerProps.transitionName}-height`;
      containerProps.style = objectAssign({}, containerProps.style, {
        position: 'relative',
        display: 'block',
        height
      });

      if (overflowHidden) {
        containerProps.style.overflow = 'hidden';
      }
    }

    if (nextChild) {
      childrenToRender.push(
        React.createElement('span',
          {
            style: {
              position: 'absolute',
              left: 0,
              top: 0
            },
            key: 'next'
          },
          this._wrapChild(nextChild, {ref: 'next'})
        )
      );
    }

    return React.createElement(this.props.component, containerProps, childrenToRender);
  }
}
{'fraction_non_alphanumeric': 0.10736893361467294, 'fraction_numerical': 0.0006492452523940919, 'mean_word_length': 2.9880258899676377, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3810486', 'n_tokens_mistral': 3472, 'n_tokens_neox': 3393, 'n_words': 792}