react-router-loading
Version:
Wrapper for react-router that allows you to load data before switching the screen
291 lines (290 loc) • 9.29 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __objRest = (source, exclude) => {
var target = {};
for (var prop in source)
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
target[prop] = source[prop];
if (source != null && __getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(source)) {
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
target[prop] = source[prop];
}
return target;
};
import React, { createContext, useMemo, useContext, useState, useRef, useEffect, useCallback } from "react";
import _topbar from "topbar";
import { Route as Route$1, matchRoutes, useRoutes, UNSAFE_LocationContext, useLocation, useNavigationType } from "react-router";
const LoadingContext = createContext({
start: () => {
},
done: () => {
},
restart: () => {
}
});
LoadingContext.displayName = "LoadingContext";
const LoadingGetterContext = createContext(false);
LoadingGetterContext.displayName = "LoadingGetterContext";
var jsxRuntime = { exports: {} };
var reactJsxRuntime_production_min = {};
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var f = React, k = Symbol.for("react.element"), l = Symbol.for("react.fragment"), m = Object.prototype.hasOwnProperty, n = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, p = { key: true, ref: true, __self: true, __source: true };
function q(c, a, g) {
var b, d = {}, e = null, h = null;
g !== void 0 && (e = "" + g);
a.key !== void 0 && (e = "" + a.key);
a.ref !== void 0 && (h = a.ref);
for (b in a)
m.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]);
if (c && c.defaultProps)
for (b in a = c.defaultProps, a)
d[b] === void 0 && (d[b] = a[b]);
return { $$typeof: k, type: c, key: e, ref: h, props: d, _owner: n.current };
}
reactJsxRuntime_production_min.Fragment = l;
reactJsxRuntime_production_min.jsx = q;
reactJsxRuntime_production_min.jsxs = q;
{
jsxRuntime.exports = reactJsxRuntime_production_min;
}
const jsx = jsxRuntime.exports.jsx;
const jsxs = jsxRuntime.exports.jsxs;
const Fragment = jsxRuntime.exports.Fragment;
const Route = (_a) => {
var _b = _a, {
loading
} = _b, props = __objRest(_b, [
"loading"
]);
return /* @__PURE__ */ jsx(Route$1, __spreadValues({}, props));
};
const DefaultLoadingScreen = () => /* @__PURE__ */ jsx("div", {
style: {
position: "fixed",
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: "white"
}
});
function createRoutesFromChildren(children) {
const routes = [];
React.Children.forEach(children, (element) => {
if (!React.isValidElement(element)) {
return;
}
if (element.type === React.Fragment) {
routes.push.apply(routes, createRoutesFromChildren(element.props.children));
return;
}
const route = {
caseSensitive: element.props.caseSensitive,
element: element.props.element,
index: element.props.index,
path: element.props.path,
loading: element.props.loading
};
if (element.props.children) {
route.children = createRoutesFromChildren(element.props.children);
}
routes.push(route);
});
return routes;
}
const isPathsDifferent = (first, second) => first.pathname !== second.pathname;
const isPathsEqual = (first, second) => first.pathname === second.pathname;
const isSearchDifferent = (first, second) => first.search !== second.search;
const isLoadable = (location, routes) => {
const matches = matchRoutes(routes, location);
if (!matches || matches.length === 0)
return false;
const lastMatch = matches[matches.length - 1];
return lastMatch.route.loading;
};
const RouteWrapper = ({
routes,
location,
navigationType,
hidden
}) => {
const element = useRoutes(routes, location);
return /* @__PURE__ */ jsx("div", {
style: hidden ? {
display: "none"
} : void 0,
children: useMemo(() => /* @__PURE__ */ jsx(UNSAFE_LocationContext.Provider, {
value: {
location,
navigationType
},
children: element
}), [location])
});
};
const LOADING_PATHNAME = "__loading";
const LoadingRoutes = ({
children,
loadingScreen: LoadingScreen,
maxLoadingTime = 0
}) => {
const location = useLocation();
const navigationType = useNavigationType();
const loadingContext = useContext(LoadingContext);
const isCurrentlyLoading = useContext(LoadingGetterContext);
const routes = useMemo(() => createRoutesFromChildren(children), [children]);
const [current, setCurrent] = useState(() => {
const isFirstPageLoadable = isLoadable(location, routes);
const firstLocation = isFirstPageLoadable ? __spreadProps(__spreadValues({}, location), {
pathname: LOADING_PATHNAME
}) : location;
return {
location: firstLocation,
navigationType
};
});
const [next, setNext] = useState(current);
const timeout = useRef();
useEffect(() => {
if (isPathsDifferent(location, next.location)) {
const isPageLoadable = isLoadable(location, routes);
setNext({
location: __spreadValues({}, location),
navigationType
});
if (!isPageLoadable) {
loadingContext.done();
setCurrent({
location: __spreadValues({}, location),
navigationType
});
} else {
if (!isCurrentlyLoading)
loadingContext.start();
else
loadingContext.restart();
}
}
if (isPathsEqual(location, current.location)) {
loadingContext.done();
if (isSearchDifferent(location, current.location))
setCurrent({
location: __spreadValues({}, location),
navigationType
});
}
}, [location]);
useEffect(() => {
if (!isCurrentlyLoading && isPathsDifferent(current.location, next.location))
setCurrent(next);
}, [isCurrentlyLoading]);
useEffect(() => {
if (maxLoadingTime > 0) {
if (timeout.current) {
clearTimeout(timeout.current);
timeout.current = void 0;
}
if (isPathsDifferent(current.location, next.location)) {
timeout.current = setTimeout(() => {
loadingContext.done();
}, maxLoadingTime);
}
}
}, [current, next]);
return useMemo(() => /* @__PURE__ */ jsxs(Fragment, {
children: [current.location.pathname !== LOADING_PATHNAME ? /* @__PURE__ */ jsx(RouteWrapper, {
routes,
location: current.location,
navigationType: current.navigationType
}, current.location.pathname) : LoadingScreen ? /* @__PURE__ */ jsx(LoadingScreen, {}) : /* @__PURE__ */ jsx(DefaultLoadingScreen, {}), isPathsDifferent(current.location, next.location) && /* @__PURE__ */ jsx(RouteWrapper, {
routes,
location: next.location,
navigationType: next.navigationType,
hidden: true
}, next.location.pathname)]
}), [current, next]);
};
const LoadingMiddleware = ({
children,
isLoading = false
}) => {
const [loading, setLoading] = useState(isLoading);
const isFirstRender = useRef(true);
const start = useCallback(() => {
topbar.show();
setLoading(true);
}, []);
const done = useCallback(() => {
topbar.hide();
setLoading(false);
}, []);
const restart = useCallback(() => {
topbar.hide();
topbar.show();
}, []);
useEffect(() => {
if (!isFirstRender.current) {
if (isLoading && !loading)
start();
else if (loading)
done();
} else {
isFirstRender.current = false;
}
}, [isLoading]);
const loadingProvider = useMemo(() => /* @__PURE__ */ jsx(LoadingContext.Provider, {
value: {
start,
done,
restart
},
children
}), []);
return /* @__PURE__ */ jsx(LoadingGetterContext.Provider, {
value: loading,
children: loadingProvider
});
};
const Routes = ({
children,
loadingScreen,
maxLoadingTime,
isLoading
}) => /* @__PURE__ */ jsx(LoadingMiddleware, {
isLoading,
children: /* @__PURE__ */ jsx(LoadingRoutes, {
loadingScreen,
maxLoadingTime,
children
})
});
const topbar = _topbar;
const useLoadingContext = () => useContext(LoadingContext);
export { LoadingContext, LoadingGetterContext, Route, Routes, topbar, useLoadingContext };
//# sourceMappingURL=react-router-loading.es.js.map