257 lines
6.9 KiB
JavaScript
257 lines
6.9 KiB
JavaScript
|
import _inheritsLoose from "@babel/runtime/helpers/esm/inheritsLoose";
|
||
|
|
||
|
var _leaveRenders, _enterRenders;
|
||
|
|
||
|
import React from 'react';
|
||
|
import PropTypes from 'prop-types';
|
||
|
import { ENTERED, ENTERING, EXITING } from './Transition';
|
||
|
import TransitionGroupContext from './TransitionGroupContext';
|
||
|
|
||
|
function areChildrenDifferent(oldChildren, newChildren) {
|
||
|
if (oldChildren === newChildren) return false;
|
||
|
|
||
|
if (React.isValidElement(oldChildren) && React.isValidElement(newChildren) && oldChildren.key != null && oldChildren.key === newChildren.key) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
/**
|
||
|
* Enum of modes for SwitchTransition component
|
||
|
* @enum { string }
|
||
|
*/
|
||
|
|
||
|
|
||
|
export var modes = {
|
||
|
out: 'out-in',
|
||
|
in: 'in-out'
|
||
|
};
|
||
|
|
||
|
var callHook = function callHook(element, name, cb) {
|
||
|
return function () {
|
||
|
var _element$props;
|
||
|
|
||
|
element.props[name] && (_element$props = element.props)[name].apply(_element$props, arguments);
|
||
|
cb();
|
||
|
};
|
||
|
};
|
||
|
|
||
|
var leaveRenders = (_leaveRenders = {}, _leaveRenders[modes.out] = function (_ref) {
|
||
|
var current = _ref.current,
|
||
|
changeState = _ref.changeState;
|
||
|
return React.cloneElement(current, {
|
||
|
in: false,
|
||
|
onExited: callHook(current, 'onExited', function () {
|
||
|
changeState(ENTERING, null);
|
||
|
})
|
||
|
});
|
||
|
}, _leaveRenders[modes.in] = function (_ref2) {
|
||
|
var current = _ref2.current,
|
||
|
changeState = _ref2.changeState,
|
||
|
children = _ref2.children;
|
||
|
return [current, React.cloneElement(children, {
|
||
|
in: true,
|
||
|
onEntered: callHook(children, 'onEntered', function () {
|
||
|
changeState(ENTERING);
|
||
|
})
|
||
|
})];
|
||
|
}, _leaveRenders);
|
||
|
var enterRenders = (_enterRenders = {}, _enterRenders[modes.out] = function (_ref3) {
|
||
|
var children = _ref3.children,
|
||
|
changeState = _ref3.changeState;
|
||
|
return React.cloneElement(children, {
|
||
|
in: true,
|
||
|
onEntered: callHook(children, 'onEntered', function () {
|
||
|
changeState(ENTERED, React.cloneElement(children, {
|
||
|
in: true
|
||
|
}));
|
||
|
})
|
||
|
});
|
||
|
}, _enterRenders[modes.in] = function (_ref4) {
|
||
|
var current = _ref4.current,
|
||
|
children = _ref4.children,
|
||
|
changeState = _ref4.changeState;
|
||
|
return [React.cloneElement(current, {
|
||
|
in: false,
|
||
|
onExited: callHook(current, 'onExited', function () {
|
||
|
changeState(ENTERED, React.cloneElement(children, {
|
||
|
in: true
|
||
|
}));
|
||
|
})
|
||
|
}), React.cloneElement(children, {
|
||
|
in: true
|
||
|
})];
|
||
|
}, _enterRenders);
|
||
|
/**
|
||
|
* A transition component inspired by the [vue transition modes](https://vuejs.org/v2/guide/transitions.html#Transition-Modes).
|
||
|
* You can use it when you want to control the render between state transitions.
|
||
|
* Based on the selected mode and the child's key which is the `Transition` or `CSSTransition` component, the `SwitchTransition` makes a consistent transition between them.
|
||
|
*
|
||
|
* If the `out-in` mode is selected, the `SwitchTransition` waits until the old child leaves and then inserts a new child.
|
||
|
* If the `in-out` mode is selected, the `SwitchTransition` inserts a new child first, waits for the new child to enter and then removes the old child.
|
||
|
*
|
||
|
* **Note**: If you want the animation to happen simultaneously
|
||
|
* (that is, to have the old child removed and a new child inserted **at the same time**),
|
||
|
* you should use
|
||
|
* [`TransitionGroup`](https://reactcommunity.org/react-transition-group/transition-group)
|
||
|
* instead.
|
||
|
*
|
||
|
* ```jsx
|
||
|
* function App() {
|
||
|
* const [state, setState] = useState(false);
|
||
|
* return (
|
||
|
* <SwitchTransition>
|
||
|
* <CSSTransition
|
||
|
* key={state ? "Goodbye, world!" : "Hello, world!"}
|
||
|
* addEndListener={(node, done) => node.addEventListener("transitionend", done, false)}
|
||
|
* classNames='fade'
|
||
|
* >
|
||
|
* <button onClick={() => setState(state => !state)}>
|
||
|
* {state ? "Goodbye, world!" : "Hello, world!"}
|
||
|
* </button>
|
||
|
* </CSSTransition>
|
||
|
* </SwitchTransition>
|
||
|
* );
|
||
|
* }
|
||
|
* ```
|
||
|
*
|
||
|
* ```css
|
||
|
* .fade-enter{
|
||
|
* opacity: 0;
|
||
|
* }
|
||
|
* .fade-exit{
|
||
|
* opacity: 1;
|
||
|
* }
|
||
|
* .fade-enter-active{
|
||
|
* opacity: 1;
|
||
|
* }
|
||
|
* .fade-exit-active{
|
||
|
* opacity: 0;
|
||
|
* }
|
||
|
* .fade-enter-active,
|
||
|
* .fade-exit-active{
|
||
|
* transition: opacity 500ms;
|
||
|
* }
|
||
|
* ```
|
||
|
*/
|
||
|
|
||
|
var SwitchTransition = /*#__PURE__*/function (_React$Component) {
|
||
|
_inheritsLoose(SwitchTransition, _React$Component);
|
||
|
|
||
|
function SwitchTransition() {
|
||
|
var _this;
|
||
|
|
||
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||
|
args[_key] = arguments[_key];
|
||
|
}
|
||
|
|
||
|
_this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this;
|
||
|
_this.state = {
|
||
|
status: ENTERED,
|
||
|
current: null
|
||
|
};
|
||
|
_this.appeared = false;
|
||
|
|
||
|
_this.changeState = function (status, current) {
|
||
|
if (current === void 0) {
|
||
|
current = _this.state.current;
|
||
|
}
|
||
|
|
||
|
_this.setState({
|
||
|
status: status,
|
||
|
current: current
|
||
|
});
|
||
|
};
|
||
|
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
var _proto = SwitchTransition.prototype;
|
||
|
|
||
|
_proto.componentDidMount = function componentDidMount() {
|
||
|
this.appeared = true;
|
||
|
};
|
||
|
|
||
|
SwitchTransition.getDerivedStateFromProps = function getDerivedStateFromProps(props, state) {
|
||
|
if (props.children == null) {
|
||
|
return {
|
||
|
current: null
|
||
|
};
|
||
|
}
|
||
|
|
||
|
if (state.status === ENTERING && props.mode === modes.in) {
|
||
|
return {
|
||
|
status: ENTERING
|
||
|
};
|
||
|
}
|
||
|
|
||
|
if (state.current && areChildrenDifferent(state.current, props.children)) {
|
||
|
return {
|
||
|
status: EXITING
|
||
|
};
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
current: React.cloneElement(props.children, {
|
||
|
in: true
|
||
|
})
|
||
|
};
|
||
|
};
|
||
|
|
||
|
_proto.render = function render() {
|
||
|
var _this$props = this.props,
|
||
|
children = _this$props.children,
|
||
|
mode = _this$props.mode,
|
||
|
_this$state = this.state,
|
||
|
status = _this$state.status,
|
||
|
current = _this$state.current;
|
||
|
var data = {
|
||
|
children: children,
|
||
|
current: current,
|
||
|
changeState: this.changeState,
|
||
|
status: status
|
||
|
};
|
||
|
var component;
|
||
|
|
||
|
switch (status) {
|
||
|
case ENTERING:
|
||
|
component = enterRenders[mode](data);
|
||
|
break;
|
||
|
|
||
|
case EXITING:
|
||
|
component = leaveRenders[mode](data);
|
||
|
break;
|
||
|
|
||
|
case ENTERED:
|
||
|
component = current;
|
||
|
}
|
||
|
|
||
|
return /*#__PURE__*/React.createElement(TransitionGroupContext.Provider, {
|
||
|
value: {
|
||
|
isMounting: !this.appeared
|
||
|
}
|
||
|
}, component);
|
||
|
};
|
||
|
|
||
|
return SwitchTransition;
|
||
|
}(React.Component);
|
||
|
|
||
|
SwitchTransition.propTypes = process.env.NODE_ENV !== "production" ? {
|
||
|
/**
|
||
|
* Transition modes.
|
||
|
* `out-in`: Current element transitions out first, then when complete, the new element transitions in.
|
||
|
* `in-out`: New element transitions in first, then when complete, the current element transitions out.
|
||
|
*
|
||
|
* @type {'out-in'|'in-out'}
|
||
|
*/
|
||
|
mode: PropTypes.oneOf([modes.in, modes.out]),
|
||
|
|
||
|
/**
|
||
|
* Any `Transition` or `CSSTransition` component.
|
||
|
*/
|
||
|
children: PropTypes.oneOfType([PropTypes.element.isRequired])
|
||
|
} : {};
|
||
|
SwitchTransition.defaultProps = {
|
||
|
mode: modes.out
|
||
|
};
|
||
|
export default SwitchTransition;
|