react-view-router
Version:
react-view-router
254 lines (251 loc) • 11.4 kB
JavaScript
const _excluded = ["name", "transition", "transitionPrefix", "transitionZIndex", "transitionFallback", "routerView", "router", "container", "containerStyle", "onScrollToPosition", "onSavePosition"];
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
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 "core-js/modules/web.dom.iterable.js";
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
import React, { useMemo, useState } from 'react';
import { RouterView as RouterViewOrigin, useRouter, useMatchedRouteIndex, isPlainObject, isFunction, getSessionStorage, setSessionStorage, nextTick } from 'react-view-router';
import SwitchTransition from './SwitchTransition';
import CSSTransition from './CSSTransition';
import './router-view.css';
const SAVED_POSITION_KEY = '_REACT_VIEW_ROUTER_TRANSITION_POSITIONS_';
/**
* @param {import('react-view-router').Route} to
*/
function isPush(to) {
return to && (to.action === 'PUSH' || to.params.isPush || to.query.isPush);
}
/**
* @param {import('react-view-router').Route} to
* @param {import('react-view-router').Route} [prevRoute]
*/
function isPop(to, prevRoute) {
return !to && prevRoute || to.action === 'POP' || to.params.isBack || to.params.isPop || to.query.back;
}
/**
* @param {import('react-view-router').Route} to
* @param {import('react-view-router').Route} [prevRoute]
*/
function isReplace(to, prevRoute) {
return to && to.action === 'REPLACE' && !isPush(to) && !isPop(to, prevRoute);
}
/**
* @typedef {import('react-view-router').RouterView} RouterView
* @typedef {import('../types/router-view').TransitionRouterViewProps} TransitionRouterViewProps
*/
/**
* @type {React.ForwardRefExoticComponent<TransitionRouterViewProps & React.RefAttributes<RouterView>>}
*/
const RouterViewTransition = /*#__PURE__*/React.forwardRef((props, ref) => {
let {
name,
transition = 'slide',
transitionPrefix = 'react-view-router-',
transitionZIndex = 1000,
transitionFallback = '',
routerView,
router: defaultRouter,
container,
containerStyle = {},
onScrollToPosition,
onSavePosition
} = props,
restProps = _objectWithoutProperties(props, _excluded);
const router = useRouter(defaultRouter);
const matchedRouteIndex = useMatchedRouteIndex();
const savedPositionsKey = useMemo(() => `${name || 'default'}_${matchedRouteIndex}`, [name, matchedRouteIndex]);
const [$refs] = useState(() => {
const positions = getSessionStorage(SAVED_POSITION_KEY, true) || {};
if (!positions[savedPositionsKey]) positions[savedPositionsKey] = {};
return {
positions,
onScrollToPosition,
onSavePosition
};
});
$refs.onScrollToPosition = onScrollToPosition;
$refs.onSavePosition = onSavePosition;
if (routerView === RouterViewTransition) routerView = null;
const backgroundColor = useMemo(() => containerStyle && containerStyle.backgroundColor || document.defaultView.getComputedStyle(document.body, null).getPropertyValue('background-color'),
// eslint-disable-next-line react-hooks/exhaustive-deps
[containerStyle && containerStyle.backgroundColor]);
containerStyle = _objectSpread({
height: '100%'
}, containerStyle);
let containerTag = 'div';
if (isPlainObject(transition)) {
transitionZIndex = transition.zIndex || transitionZIndex;
containerStyle = Object.assign(containerStyle, transition.containerStyle);
containerTag = transition.containerTag || containerTag;
transition = transition.name || 'slide';
}
const transitionMap = useMemo(() => {
const isSlideNode = node => node && node.className.includes(transitionPrefix + 'slide');
const isFadeNode = node => node && node.className.includes(transitionPrefix + 'fade');
const _savePosition = maybeNode => {
const node = maybeNode && maybeNode.children[0];
const to = router.currentRoute;
if (node && isPush(to)) {
const from = router.prevRoute;
const key = from ? from.path : '-';
// const matchedRoute = to && to.matched[matchedRouteIndex + 1];
const position = from?.metaComputed.savePosition ? {
x: node.scrollLeft,
y: node.scrollTop
} : $refs.onSavePosition ? $refs.onSavePosition(node, {
to,
from
}) : null;
if (position && (position.x || position.y)) {
$refs.positions[savedPositionsKey][key] = position;
setSessionStorage(SAVED_POSITION_KEY, $refs.positions);
}
}
};
const _scrollToPosition = maybeNode => {
const node = maybeNode && maybeNode.children[0];
const to = router.currentRoute;
if (node && isPop(to, router.prevRoute)) {
const key = to ? to.path : '-';
const savedPosition = $refs.positions[savedPositionsKey][key];
if (savedPosition) {
if ($refs.onScrollToPosition) $refs.onScrollToPosition(node, savedPosition);else node.scrollTo(savedPosition.x, savedPosition.y);
delete $refs.positions[savedPositionsKey][key];
setSessionStorage(SAVED_POSITION_KEY, $refs.positions);
}
}
};
const transitionProps = {
addEndListener: (node, done) => {
const to = router.currentRoute;
if (!isFadeNode(node) && isReplace(to, router.prevRoute) || node.className.includes('slide-right-enter') || node.className.includes('slide-left-exit')) {
return done();
}
node.addEventListener('transitionend', done, false);
},
onEnter: maybeNode => {
// if (maybeNode && $refs.route) {
// maybeNode.childNodes.forEach(node => {
// if (!node[KEEP_ALIVE_REPLACOR]
// || node.$refs.mountRoot === maybeNode) return;
// node.mountView(maybeNode, node);
// })
// }
if (isSlideNode(maybeNode)) {
if (transitionZIndex) maybeNode.style.zIndex = transitionZIndex;
if (backgroundColor) maybeNode.style.backgroundColor = backgroundColor;
}
},
onEntering: maybeNode => {
nextTick(() => _scrollToPosition(maybeNode));
},
onEntered: maybeNode => {
if (isSlideNode(maybeNode)) {
if (transitionZIndex) maybeNode.style.zIndex = '';
if (backgroundColor) maybeNode.style.backgroundColor = '';
}
},
onExit: maybeNode => {
_savePosition(maybeNode);
if (isSlideNode(maybeNode)) {
if (transitionZIndex) maybeNode.style.zIndex = transitionZIndex;
if (backgroundColor) maybeNode.style.backgroundColor = backgroundColor;
}
},
onExited: maybeNode => {
// if (maybeNode) {
// maybeNode.childNodes.forEach(node => {
// if (!node[KEEP_ALIVE_REPLACOR]
// || node.$refs.mountRoot !== maybeNode) return;
// node.unmountView();
// })
// }
if (isSlideNode(maybeNode)) {
if (transitionZIndex) maybeNode.style.zIndex = '';
if (backgroundColor) maybeNode.style.backgroundColor = '';
}
}
};
/**
* @type {{
* [key: string]: {
* mode: string,
* props: Partial<any>
* }}
* */
const map = {
fade: {
mode: 'out-in',
props: {
classNames: transitionPrefix + 'fade'
}
},
slide: {
mode: 'in-out',
props: {
classNames: () => {
let ret = '';
const to = router.currentRoute;
if (isPush(to)) {
ret = 'slide-left';
} else if (isPop(to, router.prevRoute)) {
ret = 'slide-right';
} else {
ret = isFunction(transitionFallback) ? transitionFallback(to) : transitionFallback;
}
return ret ? `${transitionPrefix}${ret}` : ret;
}
}
},
carousel: {
mode: 'together',
props: {
classNames: () => {
let ret = '';
const to = router.currentRoute;
if (isPush(to)) {
ret = 'carousel-left';
} else if (isPop(to, router.prevRoute)) {
ret = 'carousel-right';
} else {
ret = isFunction(transitionFallback) ? transitionFallback(to) : transitionFallback;
}
return ret ? `${transitionPrefix}${ret}` : ret;
}
}
}
};
const transitionItem = map[transition];
if (transitionItem) Object.assign(transitionItem.props, transitionProps);
return transitionItem || {
mode: '',
props: {}
};
}, [$refs, matchedRouteIndex, savedPositionsKey, router, transitionFallback, transition, transitionPrefix, transitionZIndex, backgroundColor]);
return /*#__PURE__*/React.createElement(routerView || RouterViewOrigin, _objectSpread({
ref: ref ? {
ref
} : undefined,
name,
router: defaultRouter,
container: transition ? (result, route, props, view) => {
if (container) result = container(result, route, props, view);
// if (view.state.enableKeepAlive) return result;
// console.log('router-view container', route, route.path, this.transitionName, router.stacks.length);
return /*#__PURE__*/React.createElement(SwitchTransition, {
mode: transitionMap.mode
}, /*#__PURE__*/React.createElement(CSSTransition, _objectSpread({
key: route.path
}, transitionMap.props), /*#__PURE__*/React.createElement(containerTag, {
style: containerStyle
// [`data-${KEEP_ALIVE_KEEP_COPIES}`]: true,
}, result)));
} : container
}, restProps));
});
export { SAVED_POSITION_KEY };
export default RouterViewTransition;