Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

Transition.js 17 KiB

vor 3 Jahren
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. "use strict";
  2. exports.__esModule = true;
  3. exports.default = exports.EXITING = exports.ENTERED = exports.ENTERING = exports.EXITED = exports.UNMOUNTED = void 0;
  4. var PropTypes = _interopRequireWildcard(require("prop-types"));
  5. var _react = _interopRequireDefault(require("react"));
  6. var _reactDom = _interopRequireDefault(require("react-dom"));
  7. var _reactLifecyclesCompat = require("react-lifecycles-compat");
  8. var _PropTypes = require("./utils/PropTypes");
  9. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  10. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
  11. function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
  12. function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
  13. var UNMOUNTED = 'unmounted';
  14. exports.UNMOUNTED = UNMOUNTED;
  15. var EXITED = 'exited';
  16. exports.EXITED = EXITED;
  17. var ENTERING = 'entering';
  18. exports.ENTERING = ENTERING;
  19. var ENTERED = 'entered';
  20. exports.ENTERED = ENTERED;
  21. var EXITING = 'exiting';
  22. /**
  23. * The Transition component lets you describe a transition from one component
  24. * state to another _over time_ with a simple declarative API. Most commonly
  25. * it's used to animate the mounting and unmounting of a component, but can also
  26. * be used to describe in-place transition states as well.
  27. *
  28. * ---
  29. *
  30. * **Note**: `Transition` is a platform-agnostic base component. If you're using
  31. * transitions in CSS, you'll probably want to use
  32. * [`CSSTransition`](https://reactcommunity.org/react-transition-group/css-transition)
  33. * instead. It inherits all the features of `Transition`, but contains
  34. * additional features necessary to play nice with CSS transitions (hence the
  35. * name of the component).
  36. *
  37. * ---
  38. *
  39. * By default the `Transition` component does not alter the behavior of the
  40. * component it renders, it only tracks "enter" and "exit" states for the
  41. * components. It's up to you to give meaning and effect to those states. For
  42. * example we can add styles to a component when it enters or exits:
  43. *
  44. * ```jsx
  45. * import { Transition } from 'react-transition-group';
  46. *
  47. * const duration = 300;
  48. *
  49. * const defaultStyle = {
  50. * transition: `opacity ${duration}ms ease-in-out`,
  51. * opacity: 0,
  52. * }
  53. *
  54. * const transitionStyles = {
  55. * entering: { opacity: 0 },
  56. * entered: { opacity: 1 },
  57. * };
  58. *
  59. * const Fade = ({ in: inProp }) => (
  60. * <Transition in={inProp} timeout={duration}>
  61. * {state => (
  62. * <div style={{
  63. * ...defaultStyle,
  64. * ...transitionStyles[state]
  65. * }}>
  66. * I'm a fade Transition!
  67. * </div>
  68. * )}
  69. * </Transition>
  70. * );
  71. * ```
  72. *
  73. * There are 4 main states a Transition can be in:
  74. * - `'entering'`
  75. * - `'entered'`
  76. * - `'exiting'`
  77. * - `'exited'`
  78. *
  79. * Transition state is toggled via the `in` prop. When `true` the component
  80. * begins the "Enter" stage. During this stage, the component will shift from
  81. * its current transition state, to `'entering'` for the duration of the
  82. * transition and then to the `'entered'` stage once it's complete. Let's take
  83. * the following example (we'll use the
  84. * [useState](https://reactjs.org/docs/hooks-reference.html#usestate) hook):
  85. *
  86. * ```jsx
  87. * function App() {
  88. * const [inProp, setInProp] = useState(false);
  89. * return (
  90. * <div>
  91. * <Transition in={inProp} timeout={500}>
  92. * {state => (
  93. * // ...
  94. * )}
  95. * </Transition>
  96. * <button onClick={() => setInProp(true)}>
  97. * Click to Enter
  98. * </button>
  99. * </div>
  100. * );
  101. * }
  102. * ```
  103. *
  104. * When the button is clicked the component will shift to the `'entering'` state
  105. * and stay there for 500ms (the value of `timeout`) before it finally switches
  106. * to `'entered'`.
  107. *
  108. * When `in` is `false` the same thing happens except the state moves from
  109. * `'exiting'` to `'exited'`.
  110. */
  111. exports.EXITING = EXITING;
  112. var Transition =
  113. /*#__PURE__*/
  114. function (_React$Component) {
  115. _inheritsLoose(Transition, _React$Component);
  116. function Transition(props, context) {
  117. var _this;
  118. _this = _React$Component.call(this, props, context) || this;
  119. var parentGroup = context.transitionGroup; // In the context of a TransitionGroup all enters are really appears
  120. var appear = parentGroup && !parentGroup.isMounting ? props.enter : props.appear;
  121. var initialStatus;
  122. _this.appearStatus = null;
  123. if (props.in) {
  124. if (appear) {
  125. initialStatus = EXITED;
  126. _this.appearStatus = ENTERING;
  127. } else {
  128. initialStatus = ENTERED;
  129. }
  130. } else {
  131. if (props.unmountOnExit || props.mountOnEnter) {
  132. initialStatus = UNMOUNTED;
  133. } else {
  134. initialStatus = EXITED;
  135. }
  136. }
  137. _this.state = {
  138. status: initialStatus
  139. };
  140. _this.nextCallback = null;
  141. return _this;
  142. }
  143. var _proto = Transition.prototype;
  144. _proto.getChildContext = function getChildContext() {
  145. return {
  146. transitionGroup: null // allows for nested Transitions
  147. };
  148. };
  149. Transition.getDerivedStateFromProps = function getDerivedStateFromProps(_ref, prevState) {
  150. var nextIn = _ref.in;
  151. if (nextIn && prevState.status === UNMOUNTED) {
  152. return {
  153. status: EXITED
  154. };
  155. }
  156. return null;
  157. }; // getSnapshotBeforeUpdate(prevProps) {
  158. // let nextStatus = null
  159. // if (prevProps !== this.props) {
  160. // const { status } = this.state
  161. // if (this.props.in) {
  162. // if (status !== ENTERING && status !== ENTERED) {
  163. // nextStatus = ENTERING
  164. // }
  165. // } else {
  166. // if (status === ENTERING || status === ENTERED) {
  167. // nextStatus = EXITING
  168. // }
  169. // }
  170. // }
  171. // return { nextStatus }
  172. // }
  173. _proto.componentDidMount = function componentDidMount() {
  174. this.updateStatus(true, this.appearStatus);
  175. };
  176. _proto.componentDidUpdate = function componentDidUpdate(prevProps) {
  177. var nextStatus = null;
  178. if (prevProps !== this.props) {
  179. var status = this.state.status;
  180. if (this.props.in) {
  181. if (status !== ENTERING && status !== ENTERED) {
  182. nextStatus = ENTERING;
  183. }
  184. } else {
  185. if (status === ENTERING || status === ENTERED) {
  186. nextStatus = EXITING;
  187. }
  188. }
  189. }
  190. this.updateStatus(false, nextStatus);
  191. };
  192. _proto.componentWillUnmount = function componentWillUnmount() {
  193. this.cancelNextCallback();
  194. };
  195. _proto.getTimeouts = function getTimeouts() {
  196. var timeout = this.props.timeout;
  197. var exit, enter, appear;
  198. exit = enter = appear = timeout;
  199. if (timeout != null && typeof timeout !== 'number') {
  200. exit = timeout.exit;
  201. enter = timeout.enter; // TODO: remove fallback for next major
  202. appear = timeout.appear !== undefined ? timeout.appear : enter;
  203. }
  204. return {
  205. exit: exit,
  206. enter: enter,
  207. appear: appear
  208. };
  209. };
  210. _proto.updateStatus = function updateStatus(mounting, nextStatus) {
  211. if (mounting === void 0) {
  212. mounting = false;
  213. }
  214. if (nextStatus !== null) {
  215. // nextStatus will always be ENTERING or EXITING.
  216. this.cancelNextCallback();
  217. var node = _reactDom.default.findDOMNode(this);
  218. if (nextStatus === ENTERING) {
  219. this.performEnter(node, mounting);
  220. } else {
  221. this.performExit(node);
  222. }
  223. } else if (this.props.unmountOnExit && this.state.status === EXITED) {
  224. this.setState({
  225. status: UNMOUNTED
  226. });
  227. }
  228. };
  229. _proto.performEnter = function performEnter(node, mounting) {
  230. var _this2 = this;
  231. var enter = this.props.enter;
  232. var appearing = this.context.transitionGroup ? this.context.transitionGroup.isMounting : mounting;
  233. var timeouts = this.getTimeouts();
  234. var enterTimeout = appearing ? timeouts.appear : timeouts.enter; // no enter animation skip right to ENTERED
  235. // if we are mounting and running this it means appear _must_ be set
  236. if (!mounting && !enter) {
  237. this.safeSetState({
  238. status: ENTERED
  239. }, function () {
  240. _this2.props.onEntered(node);
  241. });
  242. return;
  243. }
  244. this.props.onEnter(node, appearing);
  245. this.safeSetState({
  246. status: ENTERING
  247. }, function () {
  248. _this2.props.onEntering(node, appearing);
  249. _this2.onTransitionEnd(node, enterTimeout, function () {
  250. _this2.safeSetState({
  251. status: ENTERED
  252. }, function () {
  253. _this2.props.onEntered(node, appearing);
  254. });
  255. });
  256. });
  257. };
  258. _proto.performExit = function performExit(node) {
  259. var _this3 = this;
  260. var exit = this.props.exit;
  261. var timeouts = this.getTimeouts(); // no exit animation skip right to EXITED
  262. if (!exit) {
  263. this.safeSetState({
  264. status: EXITED
  265. }, function () {
  266. _this3.props.onExited(node);
  267. });
  268. return;
  269. }
  270. this.props.onExit(node);
  271. this.safeSetState({
  272. status: EXITING
  273. }, function () {
  274. _this3.props.onExiting(node);
  275. _this3.onTransitionEnd(node, timeouts.exit, function () {
  276. _this3.safeSetState({
  277. status: EXITED
  278. }, function () {
  279. _this3.props.onExited(node);
  280. });
  281. });
  282. });
  283. };
  284. _proto.cancelNextCallback = function cancelNextCallback() {
  285. if (this.nextCallback !== null) {
  286. this.nextCallback.cancel();
  287. this.nextCallback = null;
  288. }
  289. };
  290. _proto.safeSetState = function safeSetState(nextState, callback) {
  291. // This shouldn't be necessary, but there are weird race conditions with
  292. // setState callbacks and unmounting in testing, so always make sure that
  293. // we can cancel any pending setState callbacks after we unmount.
  294. callback = this.setNextCallback(callback);
  295. this.setState(nextState, callback);
  296. };
  297. _proto.setNextCallback = function setNextCallback(callback) {
  298. var _this4 = this;
  299. var active = true;
  300. this.nextCallback = function (event) {
  301. if (active) {
  302. active = false;
  303. _this4.nextCallback = null;
  304. callback(event);
  305. }
  306. };
  307. this.nextCallback.cancel = function () {
  308. active = false;
  309. };
  310. return this.nextCallback;
  311. };
  312. _proto.onTransitionEnd = function onTransitionEnd(node, timeout, handler) {
  313. this.setNextCallback(handler);
  314. var doesNotHaveTimeoutOrListener = timeout == null && !this.props.addEndListener;
  315. if (!node || doesNotHaveTimeoutOrListener) {
  316. setTimeout(this.nextCallback, 0);
  317. return;
  318. }
  319. if (this.props.addEndListener) {
  320. this.props.addEndListener(node, this.nextCallback);
  321. }
  322. if (timeout != null) {
  323. setTimeout(this.nextCallback, timeout);
  324. }
  325. };
  326. _proto.render = function render() {
  327. var status = this.state.status;
  328. if (status === UNMOUNTED) {
  329. return null;
  330. }
  331. var _this$props = this.props,
  332. children = _this$props.children,
  333. childProps = _objectWithoutPropertiesLoose(_this$props, ["children"]); // filter props for Transtition
  334. delete childProps.in;
  335. delete childProps.mountOnEnter;
  336. delete childProps.unmountOnExit;
  337. delete childProps.appear;
  338. delete childProps.enter;
  339. delete childProps.exit;
  340. delete childProps.timeout;
  341. delete childProps.addEndListener;
  342. delete childProps.onEnter;
  343. delete childProps.onEntering;
  344. delete childProps.onEntered;
  345. delete childProps.onExit;
  346. delete childProps.onExiting;
  347. delete childProps.onExited;
  348. if (typeof children === 'function') {
  349. return children(status, childProps);
  350. }
  351. var child = _react.default.Children.only(children);
  352. return _react.default.cloneElement(child, childProps);
  353. };
  354. return Transition;
  355. }(_react.default.Component);
  356. Transition.contextTypes = {
  357. transitionGroup: PropTypes.object
  358. };
  359. Transition.childContextTypes = {
  360. transitionGroup: function transitionGroup() {}
  361. };
  362. Transition.propTypes = process.env.NODE_ENV !== "production" ? {
  363. /**
  364. * A `function` child can be used instead of a React element. This function is
  365. * called with the current transition status (`'entering'`, `'entered'`,
  366. * `'exiting'`, `'exited'`, `'unmounted'`), which can be used to apply context
  367. * specific props to a component.
  368. *
  369. * ```jsx
  370. * <Transition in={this.state.in} timeout={150}>
  371. * {state => (
  372. * <MyComponent className={`fade fade-${state}`} />
  373. * )}
  374. * </Transition>
  375. * ```
  376. */
  377. children: PropTypes.oneOfType([PropTypes.func.isRequired, PropTypes.element.isRequired]).isRequired,
  378. /**
  379. * Show the component; triggers the enter or exit states
  380. */
  381. in: PropTypes.bool,
  382. /**
  383. * By default the child component is mounted immediately along with
  384. * the parent `Transition` component. If you want to "lazy mount" the component on the
  385. * first `in={true}` you can set `mountOnEnter`. After the first enter transition the component will stay
  386. * mounted, even on "exited", unless you also specify `unmountOnExit`.
  387. */
  388. mountOnEnter: PropTypes.bool,
  389. /**
  390. * By default the child component stays mounted after it reaches the `'exited'` state.
  391. * Set `unmountOnExit` if you'd prefer to unmount the component after it finishes exiting.
  392. */
  393. unmountOnExit: PropTypes.bool,
  394. /**
  395. * Normally a component is not transitioned if it is shown when the `<Transition>` component mounts.
  396. * If you want to transition on the first mount set `appear` to `true`, and the
  397. * component will transition in as soon as the `<Transition>` mounts.
  398. *
  399. * > Note: there are no specific "appear" states. `appear` only adds an additional `enter` transition.
  400. */
  401. appear: PropTypes.bool,
  402. /**
  403. * Enable or disable enter transitions.
  404. */
  405. enter: PropTypes.bool,
  406. /**
  407. * Enable or disable exit transitions.
  408. */
  409. exit: PropTypes.bool,
  410. /**
  411. * The duration of the transition, in milliseconds.
  412. * Required unless `addEndListener` is provided.
  413. *
  414. * You may specify a single timeout for all transitions:
  415. *
  416. * ```jsx
  417. * timeout={500}
  418. * ```
  419. *
  420. * or individually:
  421. *
  422. * ```jsx
  423. * timeout={{
  424. * appear: 500,
  425. * enter: 300,
  426. * exit: 500,
  427. * }}
  428. * ```
  429. *
  430. * - `appear` defaults to the value of `enter`
  431. * - `enter` defaults to `0`
  432. * - `exit` defaults to `0`
  433. *
  434. * @type {number | { enter?: number, exit?: number, appear?: number }}
  435. */
  436. timeout: function timeout(props) {
  437. var pt = _PropTypes.timeoutsShape;
  438. if (!props.addEndListener) pt = pt.isRequired;
  439. for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  440. args[_key - 1] = arguments[_key];
  441. }
  442. return pt.apply(void 0, [props].concat(args));
  443. },
  444. /**
  445. * Add a custom transition end trigger. Called with the transitioning
  446. * DOM node and a `done` callback. Allows for more fine grained transition end
  447. * logic. **Note:** Timeouts are still used as a fallback if provided.
  448. *
  449. * ```jsx
  450. * addEndListener={(node, done) => {
  451. * // use the css transitionend event to mark the finish of a transition
  452. * node.addEventListener('transitionend', done, false);
  453. * }}
  454. * ```
  455. */
  456. addEndListener: PropTypes.func,
  457. /**
  458. * Callback fired before the "entering" status is applied. An extra parameter
  459. * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
  460. *
  461. * @type Function(node: HtmlElement, isAppearing: bool) -> void
  462. */
  463. onEnter: PropTypes.func,
  464. /**
  465. * Callback fired after the "entering" status is applied. An extra parameter
  466. * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
  467. *
  468. * @type Function(node: HtmlElement, isAppearing: bool)
  469. */
  470. onEntering: PropTypes.func,
  471. /**
  472. * Callback fired after the "entered" status is applied. An extra parameter
  473. * `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
  474. *
  475. * @type Function(node: HtmlElement, isAppearing: bool) -> void
  476. */
  477. onEntered: PropTypes.func,
  478. /**
  479. * Callback fired before the "exiting" status is applied.
  480. *
  481. * @type Function(node: HtmlElement) -> void
  482. */
  483. onExit: PropTypes.func,
  484. /**
  485. * Callback fired after the "exiting" status is applied.
  486. *
  487. * @type Function(node: HtmlElement) -> void
  488. */
  489. onExiting: PropTypes.func,
  490. /**
  491. * Callback fired after the "exited" status is applied.
  492. *
  493. * @type Function(node: HtmlElement) -> void
  494. */
  495. onExited: PropTypes.func // Name the function so it is clearer in the documentation
  496. } : {};
  497. function noop() {}
  498. Transition.defaultProps = {
  499. in: false,
  500. mountOnEnter: false,
  501. unmountOnExit: false,
  502. appear: false,
  503. enter: true,
  504. exit: true,
  505. onEnter: noop,
  506. onEntering: noop,
  507. onEntered: noop,
  508. onExit: noop,
  509. onExiting: noop,
  510. onExited: noop
  511. };
  512. Transition.UNMOUNTED = 0;
  513. Transition.EXITED = 1;
  514. Transition.ENTERING = 2;
  515. Transition.ENTERED = 3;
  516. Transition.EXITING = 4;
  517. var _default = (0, _reactLifecyclesCompat.polyfill)(Transition);
  518. exports.default = _default;