UNPKG

react-view-router

Version:
239 lines (237 loc) 6.95 kB
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } import React from 'react'; import PropTypes from 'prop-types'; import TransitionGroupContext from 'react-transition-group/TransitionGroupContext'; import { ENTERED, ENTERING, EXITING } from './Transition'; function areChildrenDifferent(oldChildren, newChildren) { if (oldChildren === newChildren) return false; if (/*#__PURE__*/React.isValidElement(oldChildren) && /*#__PURE__*/React.isValidElement(newChildren) && oldChildren.key != null && oldChildren.key === newChildren.key) { return false; } return true; } /** * Enum of modes for SwitchTransition component * @enum { string } */ export const modes = { out: 'out-in', in: 'in-out', together: 'together' }; const callHook = (element, name, cb) => (...args) => { element.props[name] && element.props[name](...args); cb(); }; const leaveRenders = { [modes.out]: ({ current, changeState }) => /*#__PURE__*/React.cloneElement(current, { in: false, onExited: callHook(current, 'onExited', () => { changeState(ENTERING, null); }) }), [modes.in]: ({ current, changeState, children }) => [current, /*#__PURE__*/React.cloneElement(children, { in: true, onEntered: callHook(children, 'onEntered', () => { changeState(ENTERING); }) })], [modes.together]: ({ current, changeState, children }) => [/*#__PURE__*/React.cloneElement(current, { in: false }), /*#__PURE__*/React.cloneElement(children, { in: true, onEntered: callHook(children, 'onEntered', () => { changeState(ENTERED, /*#__PURE__*/React.cloneElement(children, { in: true })); }) })] }; const enterRenders = { [modes.out]: ({ children, changeState }) => /*#__PURE__*/React.cloneElement(children, { in: true, onEntered: callHook(children, 'onEntered', () => { changeState(ENTERED, /*#__PURE__*/React.cloneElement(children, { in: true })); }) }), [modes.in]: ({ current, children, changeState }) => [/*#__PURE__*/React.cloneElement(current, { in: false, onExited: callHook(current, 'onExited', () => { changeState(ENTERED, /*#__PURE__*/React.cloneElement(children, { in: true })); }) }), /*#__PURE__*/React.cloneElement(children, { in: true })] }; /** * 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; * } * ``` */ class SwitchTransition extends React.Component { constructor(...args) { super(...args); _defineProperty(this, "state", { status: ENTERED, current: null }); _defineProperty(this, "appeared", false); _defineProperty(this, "changeState", (status, current = this.state.current) => { this.setState({ status, current }); }); } componentDidMount() { this.appeared = true; } static 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: /*#__PURE__*/React.cloneElement(props.children, { in: true }) }; } render() { const { props: { children, mode }, state: { status, current } } = this; const data = { children, current, changeState: this.changeState, status }; let component; // eslint-disable-next-line default-case 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); } } SwitchTransition.propTypes = { /** * 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'|'together'} */ mode: PropTypes.oneOf([modes.in, modes.out, modes.together]), /** * Any `Transition` or `CSSTransition` component. */ children: PropTypes.oneOfType([PropTypes.element.isRequired]) }; SwitchTransition.defaultProps = { mode: modes.out }; export default SwitchTransition;