328 lines
10 KiB
JavaScript
328 lines
10 KiB
JavaScript
/**
|
|
* React Router DOM v6.3.0
|
|
*
|
|
* Copyright (c) Remix Software Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE.md file in the root directory of this source tree.
|
|
*
|
|
* @license MIT
|
|
*/
|
|
import { useRef, useState, useLayoutEffect, createElement, forwardRef, useCallback, useMemo } from 'react';
|
|
import { createBrowserHistory, createHashHistory } from 'history';
|
|
import { Router, useHref, createPath, useLocation, useResolvedPath, useNavigate } from 'react-router';
|
|
export { MemoryRouter, Navigate, NavigationType, Outlet, Route, Router, Routes, UNSAFE_LocationContext, UNSAFE_NavigationContext, UNSAFE_RouteContext, createPath, createRoutesFromChildren, generatePath, matchPath, matchRoutes, parsePath, renderMatches, resolvePath, useHref, useInRouterContext, useLocation, useMatch, useNavigate, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRoutes } from 'react-router';
|
|
|
|
/**
|
|
* NOTE: If you refactor this to split up the modules into separate files,
|
|
* you'll need to update the rollup config for react-router-dom-v5-compat.
|
|
*/
|
|
|
|
function warning(cond, message) {
|
|
if (!cond) {
|
|
// eslint-disable-next-line no-console
|
|
if (typeof console !== "undefined") console.warn(message);
|
|
|
|
try {
|
|
// Welcome to debugging React Router!
|
|
//
|
|
// This error is thrown as a convenience so you can more easily
|
|
// find the source for a warning that appears in the console by
|
|
// enabling "pause on exceptions" in your JavaScript debugger.
|
|
throw new Error(message); // eslint-disable-next-line no-empty
|
|
} catch (e) {}
|
|
}
|
|
} ////////////////////////////////////////////////////////////////////////////////
|
|
// COMPONENTS
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* A `<Router>` for use in web browsers. Provides the cleanest URLs.
|
|
*/
|
|
function BrowserRouter({
|
|
basename,
|
|
children,
|
|
window
|
|
}) {
|
|
let historyRef = useRef();
|
|
|
|
if (historyRef.current == null) {
|
|
historyRef.current = createBrowserHistory({
|
|
window
|
|
});
|
|
}
|
|
|
|
let history = historyRef.current;
|
|
let [state, setState] = useState({
|
|
action: history.action,
|
|
location: history.location
|
|
});
|
|
useLayoutEffect(() => history.listen(setState), [history]);
|
|
return /*#__PURE__*/createElement(Router, {
|
|
basename: basename,
|
|
children: children,
|
|
location: state.location,
|
|
navigationType: state.action,
|
|
navigator: history
|
|
});
|
|
}
|
|
|
|
/**
|
|
* A `<Router>` for use in web browsers. Stores the location in the hash
|
|
* portion of the URL so it is not sent to the server.
|
|
*/
|
|
function HashRouter({
|
|
basename,
|
|
children,
|
|
window
|
|
}) {
|
|
let historyRef = useRef();
|
|
|
|
if (historyRef.current == null) {
|
|
historyRef.current = createHashHistory({
|
|
window
|
|
});
|
|
}
|
|
|
|
let history = historyRef.current;
|
|
let [state, setState] = useState({
|
|
action: history.action,
|
|
location: history.location
|
|
});
|
|
useLayoutEffect(() => history.listen(setState), [history]);
|
|
return /*#__PURE__*/createElement(Router, {
|
|
basename: basename,
|
|
children: children,
|
|
location: state.location,
|
|
navigationType: state.action,
|
|
navigator: history
|
|
});
|
|
}
|
|
|
|
/**
|
|
* A `<Router>` that accepts a pre-instantiated history object. It's important
|
|
* to note that using your own history object is highly discouraged and may add
|
|
* two versions of the history library to your bundles unless you use the same
|
|
* version of the history library that React Router uses internally.
|
|
*/
|
|
function HistoryRouter({
|
|
basename,
|
|
children,
|
|
history
|
|
}) {
|
|
const [state, setState] = useState({
|
|
action: history.action,
|
|
location: history.location
|
|
});
|
|
useLayoutEffect(() => history.listen(setState), [history]);
|
|
return /*#__PURE__*/createElement(Router, {
|
|
basename: basename,
|
|
children: children,
|
|
location: state.location,
|
|
navigationType: state.action,
|
|
navigator: history
|
|
});
|
|
}
|
|
|
|
{
|
|
HistoryRouter.displayName = "unstable_HistoryRouter";
|
|
}
|
|
|
|
function isModifiedEvent(event) {
|
|
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
|
|
}
|
|
|
|
/**
|
|
* The public API for rendering a history-aware <a>.
|
|
*/
|
|
const Link = /*#__PURE__*/forwardRef(function LinkWithRef({
|
|
onClick,
|
|
reloadDocument,
|
|
replace = false,
|
|
state,
|
|
target,
|
|
to,
|
|
...rest
|
|
}, ref) {
|
|
let href = useHref(to);
|
|
let internalOnClick = useLinkClickHandler(to, {
|
|
replace,
|
|
state,
|
|
target
|
|
});
|
|
|
|
function handleClick(event) {
|
|
if (onClick) onClick(event);
|
|
|
|
if (!event.defaultPrevented && !reloadDocument) {
|
|
internalOnClick(event);
|
|
}
|
|
}
|
|
|
|
return (
|
|
/*#__PURE__*/
|
|
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
|
createElement("a", Object.assign({}, rest, {
|
|
href: href,
|
|
onClick: handleClick,
|
|
ref: ref,
|
|
target: target
|
|
}))
|
|
);
|
|
});
|
|
|
|
{
|
|
Link.displayName = "Link";
|
|
}
|
|
|
|
/**
|
|
* A <Link> wrapper that knows if it's "active" or not.
|
|
*/
|
|
const NavLink = /*#__PURE__*/forwardRef(function NavLinkWithRef({
|
|
"aria-current": ariaCurrentProp = "page",
|
|
caseSensitive = false,
|
|
className: classNameProp = "",
|
|
end = false,
|
|
style: styleProp,
|
|
to,
|
|
children,
|
|
...rest
|
|
}, ref) {
|
|
let location = useLocation();
|
|
let path = useResolvedPath(to);
|
|
let locationPathname = location.pathname;
|
|
let toPathname = path.pathname;
|
|
|
|
if (!caseSensitive) {
|
|
locationPathname = locationPathname.toLowerCase();
|
|
toPathname = toPathname.toLowerCase();
|
|
}
|
|
|
|
let isActive = locationPathname === toPathname || !end && locationPathname.startsWith(toPathname) && locationPathname.charAt(toPathname.length) === "/";
|
|
let ariaCurrent = isActive ? ariaCurrentProp : undefined;
|
|
let className;
|
|
|
|
if (typeof classNameProp === "function") {
|
|
className = classNameProp({
|
|
isActive
|
|
});
|
|
} else {
|
|
// If the className prop is not a function, we use a default `active`
|
|
// class for <NavLink />s that are active. In v5 `active` was the default
|
|
// value for `activeClassName`, but we are removing that API and can still
|
|
// use the old default behavior for a cleaner upgrade path and keep the
|
|
// simple styling rules working as they currently do.
|
|
className = [classNameProp, isActive ? "active" : null].filter(Boolean).join(" ");
|
|
}
|
|
|
|
let style = typeof styleProp === "function" ? styleProp({
|
|
isActive
|
|
}) : styleProp;
|
|
return /*#__PURE__*/createElement(Link, Object.assign({}, rest, {
|
|
"aria-current": ariaCurrent,
|
|
className: className,
|
|
ref: ref,
|
|
style: style,
|
|
to: to
|
|
}), typeof children === "function" ? children({
|
|
isActive
|
|
}) : children);
|
|
});
|
|
|
|
{
|
|
NavLink.displayName = "NavLink";
|
|
} ////////////////////////////////////////////////////////////////////////////////
|
|
// HOOKS
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Handles the click behavior for router `<Link>` components. This is useful if
|
|
* you need to create custom `<Link>` components with the same click behavior we
|
|
* use in our exported `<Link>`.
|
|
*/
|
|
|
|
|
|
function useLinkClickHandler(to, {
|
|
target,
|
|
replace: replaceProp,
|
|
state
|
|
} = {}) {
|
|
let navigate = useNavigate();
|
|
let location = useLocation();
|
|
let path = useResolvedPath(to);
|
|
return useCallback(event => {
|
|
if (event.button === 0 && ( // Ignore everything but left clicks
|
|
!target || target === "_self") && // Let browser handle "target=_blank" etc.
|
|
!isModifiedEvent(event) // Ignore clicks with modifier keys
|
|
) {
|
|
event.preventDefault(); // If the URL hasn't changed, a regular <a> will do a replace instead of
|
|
// a push, so do the same here.
|
|
|
|
let replace = !!replaceProp || createPath(location) === createPath(path);
|
|
navigate(to, {
|
|
replace,
|
|
state
|
|
});
|
|
}
|
|
}, [location, navigate, path, replaceProp, state, target, to]);
|
|
}
|
|
/**
|
|
* A convenient wrapper for reading and writing search parameters via the
|
|
* URLSearchParams interface.
|
|
*/
|
|
|
|
function useSearchParams(defaultInit) {
|
|
warning(typeof URLSearchParams !== "undefined", `You cannot use the \`useSearchParams\` hook in a browser that does not ` + `support the URLSearchParams API. If you need to support Internet ` + `Explorer 11, we recommend you load a polyfill such as ` + `https://github.com/ungap/url-search-params\n\n` + `If you're unsure how to load polyfills, we recommend you check out ` + `https://polyfill.io/v3/ which provides some recommendations about how ` + `to load polyfills only for users that need them, instead of for every ` + `user.`) ;
|
|
let defaultSearchParamsRef = useRef(createSearchParams(defaultInit));
|
|
let location = useLocation();
|
|
let searchParams = useMemo(() => {
|
|
let searchParams = createSearchParams(location.search);
|
|
|
|
for (let key of defaultSearchParamsRef.current.keys()) {
|
|
if (!searchParams.has(key)) {
|
|
defaultSearchParamsRef.current.getAll(key).forEach(value => {
|
|
searchParams.append(key, value);
|
|
});
|
|
}
|
|
}
|
|
|
|
return searchParams;
|
|
}, [location.search]);
|
|
let navigate = useNavigate();
|
|
let setSearchParams = useCallback((nextInit, navigateOptions) => {
|
|
navigate("?" + createSearchParams(nextInit), navigateOptions);
|
|
}, [navigate]);
|
|
return [searchParams, setSearchParams];
|
|
}
|
|
|
|
/**
|
|
* Creates a URLSearchParams object using the given initializer.
|
|
*
|
|
* This is identical to `new URLSearchParams(init)` except it also
|
|
* supports arrays as values in the object form of the initializer
|
|
* instead of just strings. This is convenient when you need multiple
|
|
* values for a given key, but don't want to use an array initializer.
|
|
*
|
|
* For example, instead of:
|
|
*
|
|
* let searchParams = new URLSearchParams([
|
|
* ['sort', 'name'],
|
|
* ['sort', 'price']
|
|
* ]);
|
|
*
|
|
* you can do:
|
|
*
|
|
* let searchParams = createSearchParams({
|
|
* sort: ['name', 'price']
|
|
* });
|
|
*/
|
|
function createSearchParams(init = "") {
|
|
return new URLSearchParams(typeof init === "string" || Array.isArray(init) || init instanceof URLSearchParams ? init : Object.keys(init).reduce((memo, key) => {
|
|
let value = init[key];
|
|
return memo.concat(Array.isArray(value) ? value.map(v => [key, v]) : [[key, value]]);
|
|
}, []));
|
|
}
|
|
|
|
export { BrowserRouter, HashRouter, Link, NavLink, createSearchParams, HistoryRouter as unstable_HistoryRouter, useLinkClickHandler, useSearchParams };
|
|
//# sourceMappingURL=react-router-dom.development.js.map
|