| import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose"; |
| import _inheritsLoose from "@babel/runtime/helpers/esm/inheritsLoose"; |
| import PropTypes from 'prop-types'; |
| import React from 'react'; |
| import ReactDOM from 'react-dom'; |
| import config from './config'; |
| import { timeoutsShape } from './utils/PropTypes'; |
| import TransitionGroupContext from './TransitionGroupContext'; |
| import { forceReflow } from './utils/reflow'; |
| export var UNMOUNTED = 'unmounted'; |
| export var EXITED = 'exited'; |
| export var ENTERING = 'entering'; |
| export var ENTERED = 'entered'; |
| export var EXITING = 'exiting'; |
| /** |
| * The Transition component lets you describe a transition from one component |
| * state to another _over time_ with a simple declarative API. Most commonly |
| * it's used to animate the mounting and unmounting of a component, but can also |
| * be used to describe in-place transition states as well. |
| * |
| * --- |
| * |
| * **Note**: `Transition` is a platform-agnostic base component. If you're using |
| * transitions in CSS, you'll probably want to use |
| * [`CSSTransition`](https://reactcommunity.org/react-transition-group/css-transition) |
| * instead. It inherits all the features of `Transition`, but contains |
| * additional features necessary to play nice with CSS transitions (hence the |
| * name of the component). |
| * |
| * --- |
| * |
| * By default the `Transition` component does not alter the behavior of the |
| * component it renders, it only tracks "enter" and "exit" states for the |
| * components. It's up to you to give meaning and effect to those states. For |
| * example we can add styles to a component when it enters or exits: |
| * |
| * ```jsx |
| * import { Transition } from 'react-transition-group'; |
| * |
| * const duration = 300; |
| * |
| * const defaultStyle = { |
| * transition: `opacity ${duration}ms ease-in-out`, |
| * opacity: 0, |
| * } |
| * |
| * const transitionStyles = { |
| * entering: { opacity: 1 }, |
| * entered: { opacity: 1 }, |
| * exiting: { opacity: 0 }, |
| * exited: { opacity: 0 }, |
| * }; |
| * |
| * const Fade = ({ in: inProp }) => ( |
| * <Transition in={inProp} timeout={duration}> |
| * {state => ( |
| * <div style={{ |
| * ...defaultStyle, |
| * ...transitionStyles[state] |
| * }}> |
| * I'm a fade Transition! |
| * </div> |
| * )} |
| * </Transition> |
| * ); |
| * ``` |
| * |
| * There are 4 main states a Transition can be in: |
| * - `'entering'` |
| * - `'entered'` |
| * - `'exiting'` |
| * - `'exited'` |
| * |
| * Transition state is toggled via the `in` prop. When `true` the component |
| * begins the "Enter" stage. During this stage, the component will shift from |
| * its current transition state, to `'entering'` for the duration of the |
| * transition and then to the `'entered'` stage once it's complete. Let's take |
| * the following example (we'll use the |
| * [useState](https://reactjs.org/docs/hooks-reference.html#usestate) hook): |
| * |
| * ```jsx |
| * function App() { |
| * const [inProp, setInProp] = useState(false); |
| * return ( |
| * <div> |
| * <Transition in={inProp} timeout={500}> |
| * {state => ( |
| * // ... |
| * )} |
| * </Transition> |
| * <button onClick={() => setInProp(true)}> |
| * Click to Enter |
| * </button> |
| * </div> |
| * ); |
| * } |
| * ``` |
| * |
| * When the button is clicked the component will shift to the `'entering'` state |
| * and stay there for 500ms (the value of `timeout`) before it finally switches |
| * to `'entered'`. |
| * |
| * When `in` is `false` the same thing happens except the state moves from |
| * `'exiting'` to `'exited'`. |
| */ |
| |
| var Transition = /*#__PURE__*/function (_React$Component) { |
| _inheritsLoose(Transition, _React$Component); |
| |
| function Transition(props, context) { |
| var _this; |
| |
| _this = _React$Component.call(this, props, context) || this; |
| var parentGroup = context; // In the context of a TransitionGroup all enters are really appears |
| |
| var appear = parentGroup && !parentGroup.isMounting ? props.enter : props.appear; |
| var initialStatus; |
| _this.appearStatus = null; |
| |
| if (props.in) { |
| if (appear) { |
| initialStatus = EXITED; |
| _this.appearStatus = ENTERING; |
| } else { |
| initialStatus = ENTERED; |
| } |
| } else { |
| if (props.unmountOnExit || props.mountOnEnter) { |
| initialStatus = UNMOUNTED; |
| } else { |
| initialStatus = EXITED; |
| } |
| } |
| |
| _this.state = { |
| status: initialStatus |
| }; |
| _this.nextCallback = null; |
| return _this; |
| } |
| |
| Transition.getDerivedStateFromProps = function getDerivedStateFromProps(_ref, prevState) { |
| var nextIn = _ref.in; |
| |
| if (nextIn && prevState.status === UNMOUNTED) { |
| return { |
| status: EXITED |
| }; |
| } |
| |
| return null; |
| } // getSnapshotBeforeUpdate(prevProps) { |
| // let nextStatus = null |
| // if (prevProps !== this.props) { |
| // const { status } = this.state |
| // if (this.props.in) { |
| // if (status !== ENTERING && status !== ENTERED) { |
| // nextStatus = ENTERING |
| // } |
| // } else { |
| // if (status === ENTERING || status === ENTERED) { |
| // nextStatus = EXITING |
| // } |
| // } |
| // } |
| // return { nextStatus } |
| // } |
| ; |
| |
| var _proto = Transition.prototype; |
| |
| _proto.componentDidMount = function componentDidMount() { |
| this.updateStatus(true, this.appearStatus); |
| }; |
| |
| _proto.componentDidUpdate = function componentDidUpdate(prevProps) { |
| var nextStatus = null; |
| |
| if (prevProps !== this.props) { |
| var status = this.state.status; |
| |
| if (this.props.in) { |
| if (status !== ENTERING && status !== ENTERED) { |
| nextStatus = ENTERING; |
| } |
| } else { |
| if (status === ENTERING || status === ENTERED) { |
| nextStatus = EXITING; |
| } |
| } |
| } |
| |
| this.updateStatus(false, nextStatus); |
| }; |
| |
| _proto.componentWillUnmount = function componentWillUnmount() { |
| this.cancelNextCallback(); |
| }; |
| |
| _proto.getTimeouts = function getTimeouts() { |
| var timeout = this.props.timeout; |
| var exit, enter, appear; |
| exit = enter = appear = timeout; |
| |
| if (timeout != null && typeof timeout !== 'number') { |
| exit = timeout.exit; |
| enter = timeout.enter; // TODO: remove fallback for next major |
| |
| appear = timeout.appear !== undefined ? timeout.appear : enter; |
| } |
| |
| return { |
| exit: exit, |
| enter: enter, |
| appear: appear |
| }; |
| }; |
| |
| _proto.updateStatus = function updateStatus(mounting, nextStatus) { |
| if (mounting === void 0) { |
| mounting = false; |
| } |
| |
| if (nextStatus !== null) { |
| // nextStatus will always be ENTERING or EXITING. |
| this.cancelNextCallback(); |
| |
| if (nextStatus === ENTERING) { |
| if (this.props.unmountOnExit || this.props.mountOnEnter) { |
| var node = this.props.nodeRef ? this.props.nodeRef.current : ReactDOM.findDOMNode(this); // https://github.com/reactjs/react-transition-group/pull/749 |
| // With unmountOnExit or mountOnEnter, the enter animation should happen at the transition between `exited` and `entering`. |
| // To make the animation happen, we have to separate each rendering and avoid being processed as batched. |
| |
| if (node) forceReflow(node); |
| } |
| |
| this.performEnter(mounting); |
| } else { |
| this.performExit(); |
| } |
| } else if (this.props.unmountOnExit && this.state.status === EXITED) { |
| this.setState({ |
| status: UNMOUNTED |
| }); |
| } |
| }; |
| |
| _proto.performEnter = function performEnter(mounting) { |
| var _this2 = this; |
| |
| var enter = this.props.enter; |
| var appearing = this.context ? this.context.isMounting : mounting; |
| |
| var _ref2 = this.props.nodeRef ? [appearing] : [ReactDOM.findDOMNode(this), appearing], |
| maybeNode = _ref2[0], |
| maybeAppearing = _ref2[1]; |
| |
| var timeouts = this.getTimeouts(); |
| var enterTimeout = appearing ? timeouts.appear : timeouts.enter; // no enter animation skip right to ENTERED |
| // if we are mounting and running this it means appear _must_ be set |
| |
| if (!mounting && !enter || config.disabled) { |
| this.safeSetState({ |
| status: ENTERED |
| }, function () { |
| _this2.props.onEntered(maybeNode); |
| }); |
| return; |
| } |
| |
| this.props.onEnter(maybeNode, maybeAppearing); |
| this.safeSetState({ |
| status: ENTERING |
| }, function () { |
| _this2.props.onEntering(maybeNode, maybeAppearing); |
| |
| _this2.onTransitionEnd(enterTimeout, function () { |
| _this2.safeSetState({ |
| status: ENTERED |
| }, function () { |
| _this2.props.onEntered(maybeNode, maybeAppearing); |
| }); |
| }); |
| }); |
| }; |
| |
| _proto.performExit = function performExit() { |
| var _this3 = this; |
| |
| var exit = this.props.exit; |
| var timeouts = this.getTimeouts(); |
| var maybeNode = this.props.nodeRef ? undefined : ReactDOM.findDOMNode(this); // no exit animation skip right to EXITED |
| |
| if (!exit || config.disabled) { |
| this.safeSetState({ |
| status: EXITED |
| }, function () { |
| _this3.props.onExited(maybeNode); |
| }); |
| return; |
| } |
| |
| this.props.onExit(maybeNode); |
| this.safeSetState({ |
| status: EXITING |
| }, function () { |
| _this3.props.onExiting(maybeNode); |
| |
| _this3.onTransitionEnd(timeouts.exit, function () { |
| _this3.safeSetState({ |
| status: EXITED |
| }, function () { |
| _this3.props.onExited(maybeNode); |
| }); |
| }); |
| }); |
| }; |
| |
| _proto.cancelNextCallback = function cancelNextCallback() { |
| if (this.nextCallback !== null) { |
| this.nextCallback.cancel(); |
| this.nextCallback = null; |
| } |
| }; |
| |
| _proto.safeSetState = function safeSetState(nextState, callback) { |
| // This shouldn't be necessary, but there are weird race conditions with |
| // setState callbacks and unmounting in testing, so always make sure that |
| // we can cancel any pending setState callbacks after we unmount. |
| callback = this.setNextCallback(callback); |
| this.setState(nextState, callback); |
| }; |
| |
| _proto.setNextCallback = function setNextCallback(callback) { |
| var _this4 = this; |
| |
| var active = true; |
| |
| this.nextCallback = function (event) { |
| if (active) { |
| active = false; |
| _this4.nextCallback = null; |
| callback(event); |
| } |
| }; |
| |
| this.nextCallback.cancel = function () { |
| active = false; |
| }; |
| |
| return this.nextCallback; |
| }; |
| |
| _proto.onTransitionEnd = function onTransitionEnd(timeout, handler) { |
| this.setNextCallback(handler); |
| var node = this.props.nodeRef ? this.props.nodeRef.current : ReactDOM.findDOMNode(this); |
| var doesNotHaveTimeoutOrListener = timeout == null && !this.props.addEndListener; |
| |
| if (!node || doesNotHaveTimeoutOrListener) { |
| setTimeout(this.nextCallback, 0); |
| return; |
| } |
| |
| if (this.props.addEndListener) { |
| var _ref3 = this.props.nodeRef ? [this.nextCallback] : [node, this.nextCallback], |
| maybeNode = _ref3[0], |
| maybeNextCallback = _ref3[1]; |
| |
| this.props.addEndListener(maybeNode, maybeNextCallback); |
| } |
| |
| if (timeout != null) { |
| setTimeout(this.nextCallback, timeout); |
| } |
| }; |
| |
| _proto.render = function render() { |
| var status = this.state.status; |
| |
| if (status === UNMOUNTED) { |
| return null; |
| } |
| |
| var _this$props = this.props, |
| children = _this$props.children, |
| _in = _this$props.in, |
| _mountOnEnter = _this$props.mountOnEnter, |
| _unmountOnExit = _this$props.unmountOnExit, |
| _appear = _this$props.appear, |
| _enter = _this$props.enter, |
| _exit = _this$props.exit, |
| _timeout = _this$props.timeout, |
| _addEndListener = _this$props.addEndListener, |
| _onEnter = _this$props.onEnter, |
| _onEntering = _this$props.onEntering, |
| _onEntered = _this$props.onEntered, |
| _onExit = _this$props.onExit, |
| _onExiting = _this$props.onExiting, |
| _onExited = _this$props.onExited, |
| _nodeRef = _this$props.nodeRef, |
| childProps = _objectWithoutPropertiesLoose(_this$props, ["children", "in", "mountOnEnter", "unmountOnExit", "appear", "enter", "exit", "timeout", "addEndListener", "onEnter", "onEntering", "onEntered", "onExit", "onExiting", "onExited", "nodeRef"]); |
| |
| return ( |
| /*#__PURE__*/ |
| // allows for nested Transitions |
| React.createElement(TransitionGroupContext.Provider, { |
| value: null |
| }, typeof children === 'function' ? children(status, childProps) : React.cloneElement(React.Children.only(children), childProps)) |
| ); |
| }; |
| |
| return Transition; |
| }(React.Component); |
| |
| Transition.contextType = TransitionGroupContext; |
| Transition.propTypes = process.env.NODE_ENV !== "production" ? { |
| /** |
| * A React reference to DOM element that need to transition: |
| * https://stackoverflow.com/a/51127130/4671932 |
| * |
| * - When `nodeRef` prop is used, `node` is not passed to callback functions |
| * (e.g. `onEnter`) because user already has direct access to the node. |
| * - When changing `key` prop of `Transition` in a `TransitionGroup` a new |
| * `nodeRef` need to be provided to `Transition` with changed `key` prop |
| * (see |
| * [test/CSSTransition-test.js](https://github.com/reactjs/react-transition-group/blob/13435f897b3ab71f6e19d724f145596f5910581c/test/CSSTransition-test.js#L362-L437)). |
| */ |
| nodeRef: PropTypes.shape({ |
| current: typeof Element === 'undefined' ? PropTypes.any : function (propValue, key, componentName, location, propFullName, secret) { |
| var value = propValue[key]; |
| return PropTypes.instanceOf(value && 'ownerDocument' in value ? value.ownerDocument.defaultView.Element : Element)(propValue, key, componentName, location, propFullName, secret); |
| } |
| }), |
| |
| /** |
| * A `function` child can be used instead of a React element. This function is |
| * called with the current transition status (`'entering'`, `'entered'`, |
| * `'exiting'`, `'exited'`), which can be used to apply context |
| * specific props to a component. |
| * |
| * ```jsx |
| * <Transition in={this.state.in} timeout={150}> |
| * {state => ( |
| * <MyComponent className={`fade fade-${state}`} /> |
| * )} |
| * </Transition> |
| * ``` |
| */ |
| children: PropTypes.oneOfType([PropTypes.func.isRequired, PropTypes.element.isRequired]).isRequired, |
| |
| /** |
| * Show the component; triggers the enter or exit states |
| */ |
| in: PropTypes.bool, |
| |
| /** |
| * By default the child component is mounted immediately along with |
| * the parent `Transition` component. If you want to "lazy mount" the component on the |
| * first `in={true}` you can set `mountOnEnter`. After the first enter transition the component will stay |
| * mounted, even on "exited", unless you also specify `unmountOnExit`. |
| */ |
| mountOnEnter: PropTypes.bool, |
| |
| /** |
| * By default the child component stays mounted after it reaches the `'exited'` state. |
| * Set `unmountOnExit` if you'd prefer to unmount the component after it finishes exiting. |
| */ |
| unmountOnExit: PropTypes.bool, |
| |
| /** |
| * By default the child component does not perform the enter transition when |
| * it first mounts, regardless of the value of `in`. If you want this |
| * behavior, set both `appear` and `in` to `true`. |
| * |
| * > **Note**: there are no special appear states like `appearing`/`appeared`, this prop |
| * > only adds an additional enter transition. However, in the |
| * > `<CSSTransition>` component that first enter transition does result in |
| * > additional `.appear-*` classes, that way you can choose to style it |
| * > differently. |
| */ |
| appear: PropTypes.bool, |
| |
| /** |
| * Enable or disable enter transitions. |
| */ |
| enter: PropTypes.bool, |
| |
| /** |
| * Enable or disable exit transitions. |
| */ |
| exit: PropTypes.bool, |
| |
| /** |
| * The duration of the transition, in milliseconds. |
| * Required unless `addEndListener` is provided. |
| * |
| * You may specify a single timeout for all transitions: |
| * |
| * ```jsx |
| * timeout={500} |
| * ``` |
| * |
| * or individually: |
| * |
| * ```jsx |
| * timeout={{ |
| * appear: 500, |
| * enter: 300, |
| * exit: 500, |
| * }} |
| * ``` |
| * |
| * - `appear` defaults to the value of `enter` |
| * - `enter` defaults to `0` |
| * - `exit` defaults to `0` |
| * |
| * @type {number | { enter?: number, exit?: number, appear?: number }} |
| */ |
| timeout: function timeout(props) { |
| var pt = timeoutsShape; |
| if (!props.addEndListener) pt = pt.isRequired; |
| |
| for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { |
| args[_key - 1] = arguments[_key]; |
| } |
| |
| return pt.apply(void 0, [props].concat(args)); |
| }, |
| |
| /** |
| * Add a custom transition end trigger. Called with the transitioning |
| * DOM node and a `done` callback. Allows for more fine grained transition end |
| * logic. Timeouts are still used as a fallback if provided. |
| * |
| * **Note**: when `nodeRef` prop is passed, `node` is not passed. |
| * |
| * ```jsx |
| * addEndListener={(node, done) => { |
| * // use the css transitionend event to mark the finish of a transition |
| * node.addEventListener('transitionend', done, false); |
| * }} |
| * ``` |
| */ |
| addEndListener: PropTypes.func, |
| |
| /** |
| * Callback fired before the "entering" status is applied. An extra parameter |
| * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount |
| * |
| * **Note**: when `nodeRef` prop is passed, `node` is not passed. |
| * |
| * @type Function(node: HtmlElement, isAppearing: bool) -> void |
| */ |
| onEnter: PropTypes.func, |
| |
| /** |
| * Callback fired after the "entering" status is applied. An extra parameter |
| * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount |
| * |
| * **Note**: when `nodeRef` prop is passed, `node` is not passed. |
| * |
| * @type Function(node: HtmlElement, isAppearing: bool) |
| */ |
| onEntering: PropTypes.func, |
| |
| /** |
| * Callback fired after the "entered" status is applied. An extra parameter |
| * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount |
| * |
| * **Note**: when `nodeRef` prop is passed, `node` is not passed. |
| * |
| * @type Function(node: HtmlElement, isAppearing: bool) -> void |
| */ |
| onEntered: PropTypes.func, |
| |
| /** |
| * Callback fired before the "exiting" status is applied. |
| * |
| * **Note**: when `nodeRef` prop is passed, `node` is not passed. |
| * |
| * @type Function(node: HtmlElement) -> void |
| */ |
| onExit: PropTypes.func, |
| |
| /** |
| * Callback fired after the "exiting" status is applied. |
| * |
| * **Note**: when `nodeRef` prop is passed, `node` is not passed. |
| * |
| * @type Function(node: HtmlElement) -> void |
| */ |
| onExiting: PropTypes.func, |
| |
| /** |
| * Callback fired after the "exited" status is applied. |
| * |
| * **Note**: when `nodeRef` prop is passed, `node` is not passed |
| * |
| * @type Function(node: HtmlElement) -> void |
| */ |
| onExited: PropTypes.func |
| } : {}; // Name the function so it is clearer in the documentation |
| |
| function noop() {} |
| |
| Transition.defaultProps = { |
| in: false, |
| mountOnEnter: false, |
| unmountOnExit: false, |
| appear: false, |
| enter: true, |
| exit: true, |
| onEnter: noop, |
| onEntering: noop, |
| onEntered: noop, |
| onExit: noop, |
| onExiting: noop, |
| onExited: noop |
| }; |
| Transition.UNMOUNTED = UNMOUNTED; |
| Transition.EXITED = EXITED; |
| Transition.ENTERING = ENTERING; |
| Transition.ENTERED = ENTERED; |
| Transition.EXITING = EXITING; |
| export default Transition; |