UNPKG

react-slide-routes

Version:
141 lines (131 loc) 4.98 kB
'use strict'; var tslib = require('tslib'); var jsxRuntime = require('@emotion/react/jsx-runtime'); var react = require('react'); var reactRouter = require('react-router'); var reactTransitionGroup = require('react-transition-group'); var react$1 = require('@emotion/react'); const getTransformStyles = (transformFn, max) => ` // back & > .back-enter { transform: ${transformFn}(-${max}); } & > .back-enter-active { transform: ${transformFn}(0); } & > .back-exit { transform: ${transformFn}(0); } & > .back-exit-active { transform: ${transformFn}(${max}); } // forward & > .forward-enter { transform: ${transformFn}(${max}); } & > .forward-enter-active { transform: ${transformFn}(0); } & > .forward-exit { transform: ${transformFn}(0); } & > .forward-exit-active { transform: ${transformFn}(-${max}); } `; const getTransitionGroupCss = (duration, timing, direction) => react$1.css ` display: grid; & > .item { grid-area: 1 / 1 / 2 / 2; &:not(:only-child) { &.${direction}-enter-active, &.${direction}-exit-active { transition: transform ${duration}ms ${timing}; } } } &.slide { overflow: hidden; ${getTransformStyles('translateX', '100%')} } &.vertical-slide { overflow: hidden; ${getTransformStyles('translateY', '100%')} } &.rotate { perspective: 2000px; & > .item { backface-visibility: hidden; } ${getTransformStyles('rotateY', '180deg')} } `; const isRouteElement = (element) => { return react.isValidElement(element) && element.type === reactRouter.Route; }; // from useRoutes: // https://github.com/remix-run/react-router/blob/f3d3e05ec00c6950720930beaf74fecbaf9dc5b6/packages/react-router/lib/hooks.tsx#L302 const useNextPath = (pathname = '') => { const { matches: parentMatches } = react.useContext(reactRouter.UNSAFE_RouteContext); const routeMatch = parentMatches[parentMatches.length - 1]; const parentPathnameBase = routeMatch ? routeMatch.pathnameBase : '/'; return parentPathnameBase === '/' ? pathname : pathname.slice(parentPathnameBase.length) || '/'; }; const getMatch = (routes, pathname) => { const matches = reactRouter.matchRoutes(routes, pathname); if (matches === null) { throw new Error(`Route ${pathname} does not match`); } const index = routes.findIndex((route) => { return matches.some((match) => match.route === route); }); return { index, route: routes[index] }; }; const SlideRoutes = (props) => { const { animation = 'slide', duration = 200, timing = 'ease', destroy = true, compare, children, } = props; // routes const routeElements = react.Children.map(children, (child) => { if (!isRouteElement(child)) { return child; } const _a = child.props, { element } = _a, restProps = tslib.__rest(_a, ["element"]); if (!element) { return child; } const nodeRef = react.createRef(); const newElement = (jsxRuntime.jsx("div", { className: "item", ref: nodeRef, children: element })); return Object.assign(Object.assign({}, child), { props: Object.assign(Object.assign({}, restProps), { element: newElement }) }); }); const routes = reactRouter.createRoutesFromElements(routeElements); if (compare) { routes.sort(compare); } const location = reactRouter.useLocation(); const routeList = reactRouter.useRoutes(routes, location); // direction const nextPath = useNextPath(location.pathname); const prevPath = react.useRef(null); const direction = react.useRef('undirected'); const nextMatch = getMatch(routes, nextPath); if (prevPath.current && prevPath.current !== nextPath) { const prevMatch = getMatch(routes, prevPath.current); const indexDiff = nextMatch.index - prevMatch.index; if (indexDiff > 0) { direction.current = 'forward'; } else if (indexDiff < 0) { direction.current = 'back'; } else if (indexDiff === 0) { direction.current = 'undirected'; } } prevPath.current = nextPath; // props const childFactory = react.useCallback((child) => react.cloneElement(child, { classNames: direction.current }), []); const cssTransitionProps = react.useMemo(() => (destroy ? { timeout: duration } : { addEndListener() { } }), [destroy, duration]); const nextEl = nextMatch.route.element; return (jsxRuntime.jsx(reactTransitionGroup.TransitionGroup, { className: `slide-routes ${animation}`, childFactory: childFactory, css: getTransitionGroupCss(duration, timing, direction.current), children: jsxRuntime.jsx(reactTransitionGroup.CSSTransition, Object.assign({ nodeRef: nextEl.props.ref || nextEl.ref }, cssTransitionProps, { children: routeList }), nextMatch.route.path || nextMatch.index) })); }; module.exports = SlideRoutes;