SwitchTransition.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import _inheritsLoose from "@babel/runtime/helpers/esm/inheritsLoose";
  2. var _leaveRenders, _enterRenders;
  3. import React from 'react';
  4. import PropTypes from 'prop-types';
  5. import { ENTERED, ENTERING, EXITING } from './Transition';
  6. import TransitionGroupContext from './TransitionGroupContext';
  7. function areChildrenDifferent(oldChildren, newChildren) {
  8. if (oldChildren === newChildren) return false;
  9. if (React.isValidElement(oldChildren) && React.isValidElement(newChildren) && oldChildren.key != null && oldChildren.key === newChildren.key) {
  10. return false;
  11. }
  12. return true;
  13. }
  14. /**
  15. * Enum of modes for SwitchTransition component
  16. * @enum { string }
  17. */
  18. export var modes = {
  19. out: 'out-in',
  20. in: 'in-out'
  21. };
  22. var callHook = function callHook(element, name, cb) {
  23. return function () {
  24. var _element$props;
  25. element.props[name] && (_element$props = element.props)[name].apply(_element$props, arguments);
  26. cb();
  27. };
  28. };
  29. var leaveRenders = (_leaveRenders = {}, _leaveRenders[modes.out] = function (_ref) {
  30. var current = _ref.current,
  31. changeState = _ref.changeState;
  32. return React.cloneElement(current, {
  33. in: false,
  34. onExited: callHook(current, 'onExited', function () {
  35. changeState(ENTERING, null);
  36. })
  37. });
  38. }, _leaveRenders[modes.in] = function (_ref2) {
  39. var current = _ref2.current,
  40. changeState = _ref2.changeState,
  41. children = _ref2.children;
  42. return [current, React.cloneElement(children, {
  43. in: true,
  44. onEntered: callHook(children, 'onEntered', function () {
  45. changeState(ENTERING);
  46. })
  47. })];
  48. }, _leaveRenders);
  49. var enterRenders = (_enterRenders = {}, _enterRenders[modes.out] = function (_ref3) {
  50. var children = _ref3.children,
  51. changeState = _ref3.changeState;
  52. return React.cloneElement(children, {
  53. in: true,
  54. onEntered: callHook(children, 'onEntered', function () {
  55. changeState(ENTERED, React.cloneElement(children, {
  56. in: true
  57. }));
  58. })
  59. });
  60. }, _enterRenders[modes.in] = function (_ref4) {
  61. var current = _ref4.current,
  62. children = _ref4.children,
  63. changeState = _ref4.changeState;
  64. return [React.cloneElement(current, {
  65. in: false,
  66. onExited: callHook(current, 'onExited', function () {
  67. changeState(ENTERED, React.cloneElement(children, {
  68. in: true
  69. }));
  70. })
  71. }), React.cloneElement(children, {
  72. in: true
  73. })];
  74. }, _enterRenders);
  75. /**
  76. * A transition component inspired by the [vue transition modes](https://vuejs.org/v2/guide/transitions.html#Transition-Modes).
  77. * You can use it when you want to control the render between state transitions.
  78. * 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.
  79. *
  80. * If the `out-in` mode is selected, the `SwitchTransition` waits until the old child leaves and then inserts a new child.
  81. * 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.
  82. *
  83. * **Note**: If you want the animation to happen simultaneously
  84. * (that is, to have the old child removed and a new child inserted **at the same time**),
  85. * you should use
  86. * [`TransitionGroup`](https://reactcommunity.org/react-transition-group/transition-group)
  87. * instead.
  88. *
  89. * ```jsx
  90. * function App() {
  91. * const [state, setState] = useState(false);
  92. * return (
  93. * <SwitchTransition>
  94. * <CSSTransition
  95. * key={state ? "Goodbye, world!" : "Hello, world!"}
  96. * addEndListener={(node, done) => node.addEventListener("transitionend", done, false)}
  97. * classNames='fade'
  98. * >
  99. * <button onClick={() => setState(state => !state)}>
  100. * {state ? "Goodbye, world!" : "Hello, world!"}
  101. * </button>
  102. * </CSSTransition>
  103. * </SwitchTransition>
  104. * );
  105. * }
  106. * ```
  107. *
  108. * ```css
  109. * .fade-enter{
  110. * opacity: 0;
  111. * }
  112. * .fade-exit{
  113. * opacity: 1;
  114. * }
  115. * .fade-enter-active{
  116. * opacity: 1;
  117. * }
  118. * .fade-exit-active{
  119. * opacity: 0;
  120. * }
  121. * .fade-enter-active,
  122. * .fade-exit-active{
  123. * transition: opacity 500ms;
  124. * }
  125. * ```
  126. */
  127. var SwitchTransition = /*#__PURE__*/function (_React$Component) {
  128. _inheritsLoose(SwitchTransition, _React$Component);
  129. function SwitchTransition() {
  130. var _this;
  131. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  132. args[_key] = arguments[_key];
  133. }
  134. _this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this;
  135. _this.state = {
  136. status: ENTERED,
  137. current: null
  138. };
  139. _this.appeared = false;
  140. _this.changeState = function (status, current) {
  141. if (current === void 0) {
  142. current = _this.state.current;
  143. }
  144. _this.setState({
  145. status: status,
  146. current: current
  147. });
  148. };
  149. return _this;
  150. }
  151. var _proto = SwitchTransition.prototype;
  152. _proto.componentDidMount = function componentDidMount() {
  153. this.appeared = true;
  154. };
  155. SwitchTransition.getDerivedStateFromProps = function getDerivedStateFromProps(props, state) {
  156. if (props.children == null) {
  157. return {
  158. current: null
  159. };
  160. }
  161. if (state.status === ENTERING && props.mode === modes.in) {
  162. return {
  163. status: ENTERING
  164. };
  165. }
  166. if (state.current && areChildrenDifferent(state.current, props.children)) {
  167. return {
  168. status: EXITING
  169. };
  170. }
  171. return {
  172. current: React.cloneElement(props.children, {
  173. in: true
  174. })
  175. };
  176. };
  177. _proto.render = function render() {
  178. var _this$props = this.props,
  179. children = _this$props.children,
  180. mode = _this$props.mode,
  181. _this$state = this.state,
  182. status = _this$state.status,
  183. current = _this$state.current;
  184. var data = {
  185. children: children,
  186. current: current,
  187. changeState: this.changeState,
  188. status: status
  189. };
  190. var component;
  191. switch (status) {
  192. case ENTERING:
  193. component = enterRenders[mode](data);
  194. break;
  195. case EXITING:
  196. component = leaveRenders[mode](data);
  197. break;
  198. case ENTERED:
  199. component = current;
  200. }
  201. return /*#__PURE__*/React.createElement(TransitionGroupContext.Provider, {
  202. value: {
  203. isMounting: !this.appeared
  204. }
  205. }, component);
  206. };
  207. return SwitchTransition;
  208. }(React.Component);
  209. SwitchTransition.propTypes = process.env.NODE_ENV !== "production" ? {
  210. /**
  211. * Transition modes.
  212. * `out-in`: Current element transitions out first, then when complete, the new element transitions in.
  213. * `in-out`: New element transitions in first, then when complete, the current element transitions out.
  214. *
  215. * @type {'out-in'|'in-out'}
  216. */
  217. mode: PropTypes.oneOf([modes.in, modes.out]),
  218. /**
  219. * Any `Transition` or `CSSTransition` component.
  220. */
  221. children: PropTypes.oneOfType([PropTypes.element.isRequired])
  222. } : {};
  223. SwitchTransition.defaultProps = {
  224. mode: modes.out
  225. };
  226. export default SwitchTransition;