OwlCyberSecurity - MANAGER
Edit File: react-router-dom.development.js
/** * React Router DOM v6.30.1 * * 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 * as React from 'react'; import * as ReactDOM from 'react-dom'; import { UNSAFE_mapRouteProperties, UNSAFE_logV6DeprecationWarnings, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, Router, UNSAFE_useRoutesImpl, UNSAFE_NavigationContext, useHref, useResolvedPath, useLocation, useNavigate, createPath, UNSAFE_useRouteId, UNSAFE_RouteContext, useMatches, useNavigation, useBlocker } from 'react-router'; export { AbortedDeferredError, Await, MemoryRouter, Navigate, NavigationType, Outlet, Route, Router, Routes, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, UNSAFE_LocationContext, UNSAFE_NavigationContext, UNSAFE_RouteContext, UNSAFE_useRouteId, createMemoryRouter, createPath, createRoutesFromChildren, createRoutesFromElements, defer, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, redirectDocument, renderMatches, replace, resolvePath, useActionData, useAsyncError, useAsyncValue, useBlocker, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes } from 'react-router'; import { stripBasename, UNSAFE_warning, createRouter, createBrowserHistory, createHashHistory, UNSAFE_ErrorResponseImpl, UNSAFE_invariant, joinPaths, IDLE_FETCHER, matchPath } from '@remix-run/router'; export { UNSAFE_ErrorResponseImpl } from '@remix-run/router'; const defaultMethod = "get"; const defaultEncType = "application/x-www-form-urlencoded"; function isHtmlElement(object) { return object != null && typeof object.tagName === "string"; } function isButtonElement(object) { return isHtmlElement(object) && object.tagName.toLowerCase() === "button"; } function isFormElement(object) { return isHtmlElement(object) && object.tagName.toLowerCase() === "form"; } function isInputElement(object) { return isHtmlElement(object) && object.tagName.toLowerCase() === "input"; } function isModifiedEvent(event) { return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); } function shouldProcessLinkClick(event, target) { return event.button === 0 && ( // Ignore everything but left clicks !target || target === "_self") && // Let browser handle "target=_blank" etc. !isModifiedEvent(event) // Ignore clicks with modifier keys ; } /** * 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]]); }, [])); } function getSearchParamsForLocation(locationSearch, defaultSearchParams) { let searchParams = createSearchParams(locationSearch); if (defaultSearchParams) { // Use `defaultSearchParams.forEach(...)` here instead of iterating of // `defaultSearchParams.keys()` to work-around a bug in Firefox related to // web extensions. Relevant Bugzilla tickets: // https://bugzilla.mozilla.org/show_bug.cgi?id=1414602 // https://bugzilla.mozilla.org/show_bug.cgi?id=1023984 defaultSearchParams.forEach((_, key) => { if (!searchParams.has(key)) { defaultSearchParams.getAll(key).forEach(value => { searchParams.append(key, value); }); } }); } return searchParams; } // Thanks https://github.com/sindresorhus/type-fest! // One-time check for submitter support let _formDataSupportsSubmitter = null; function isFormDataSubmitterSupported() { if (_formDataSupportsSubmitter === null) { try { new FormData(document.createElement("form"), // @ts-expect-error if FormData supports the submitter parameter, this will throw 0); _formDataSupportsSubmitter = false; } catch (e) { _formDataSupportsSubmitter = true; } } return _formDataSupportsSubmitter; } /** * Submit options shared by both navigations and fetchers */ /** * Submit options available to fetchers */ /** * Submit options available to navigations */ const supportedFormEncTypes = new Set(["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"]); function getFormEncType(encType) { if (encType != null && !supportedFormEncTypes.has(encType)) { UNSAFE_warning(false, `"${encType}" is not a valid \`encType\` for \`<Form>\`/\`<fetcher.Form>\` ` + `and will default to "${defaultEncType}"`) ; return null; } return encType; } function getFormSubmissionInfo(target, basename) { let method; let action; let encType; let formData; let body; if (isFormElement(target)) { // When grabbing the action from the element, it will have had the basename // prefixed to ensure non-JS scenarios work, so strip it since we'll // re-prefix in the router let attr = target.getAttribute("action"); action = attr ? stripBasename(attr, basename) : null; method = target.getAttribute("method") || defaultMethod; encType = getFormEncType(target.getAttribute("enctype")) || defaultEncType; formData = new FormData(target); } else if (isButtonElement(target) || isInputElement(target) && (target.type === "submit" || target.type === "image")) { let form = target.form; if (form == null) { throw new Error(`Cannot submit a <button> or <input type="submit"> without a <form>`); } // <button>/<input type="submit"> may override attributes of <form> // When grabbing the action from the element, it will have had the basename // prefixed to ensure non-JS scenarios work, so strip it since we'll // re-prefix in the router let attr = target.getAttribute("formaction") || form.getAttribute("action"); action = attr ? stripBasename(attr, basename) : null; method = target.getAttribute("formmethod") || form.getAttribute("method") || defaultMethod; encType = getFormEncType(target.getAttribute("formenctype")) || getFormEncType(form.getAttribute("enctype")) || defaultEncType; // Build a FormData object populated from a form and submitter formData = new FormData(form, target); // If this browser doesn't support the `FormData(el, submitter)` format, // then tack on the submitter value at the end. This is a lightweight // solution that is not 100% spec compliant. For complete support in older // browsers, consider using the `formdata-submitter-polyfill` package if (!isFormDataSubmitterSupported()) { let { name, type, value } = target; if (type === "image") { let prefix = name ? `${name}.` : ""; formData.append(`${prefix}x`, "0"); formData.append(`${prefix}y`, "0"); } else if (name) { formData.append(name, value); } } } else if (isHtmlElement(target)) { throw new Error(`Cannot submit element that is not <form>, <button>, or ` + `<input type="submit|image">`); } else { method = defaultMethod; action = null; encType = defaultEncType; body = target; } // Send body for <Form encType="text/plain" so we encode it into text if (formData && encType === "text/plain") { body = formData; formData = undefined; } return { action, method: method.toLowerCase(), encType, formData, body }; } /** * 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. */ //#endregion // HEY YOU! DON'T TOUCH THIS VARIABLE! // // It is replaced with the proper version at build time via a babel plugin in // the rollup config. // // Export a global property onto the window for React Router detection by the // Core Web Vitals Technology Report. This way they can configure the `wappalyzer` // to detect and properly classify live websites as being built with React Router: // https://github.com/HTTPArchive/wappalyzer/blob/main/src/technologies/r.json const REACT_ROUTER_VERSION = "6"; try { window.__reactRouterVersion = REACT_ROUTER_VERSION; } catch (e) { // no-op } //////////////////////////////////////////////////////////////////////////////// //#region Routers //////////////////////////////////////////////////////////////////////////////// function createBrowserRouter(routes, opts) { return createRouter({ basename: opts?.basename, future: { ...opts?.future, v7_prependBasename: true }, history: createBrowserHistory({ window: opts?.window }), hydrationData: opts?.hydrationData || parseHydrationData(), routes, mapRouteProperties: UNSAFE_mapRouteProperties, dataStrategy: opts?.dataStrategy, patchRoutesOnNavigation: opts?.patchRoutesOnNavigation, window: opts?.window }).initialize(); } function createHashRouter(routes, opts) { return createRouter({ basename: opts?.basename, future: { ...opts?.future, v7_prependBasename: true }, history: createHashHistory({ window: opts?.window }), hydrationData: opts?.hydrationData || parseHydrationData(), routes, mapRouteProperties: UNSAFE_mapRouteProperties, dataStrategy: opts?.dataStrategy, patchRoutesOnNavigation: opts?.patchRoutesOnNavigation, window: opts?.window }).initialize(); } function parseHydrationData() { let state = window?.__staticRouterHydrationData; if (state && state.errors) { state = { ...state, errors: deserializeErrors(state.errors) }; } return state; } function deserializeErrors(errors) { if (!errors) return null; let entries = Object.entries(errors); let serialized = {}; for (let [key, val] of entries) { // Hey you! If you change this, please change the corresponding logic in // serializeErrors in react-router-dom/server.tsx :) if (val && val.__type === "RouteErrorResponse") { serialized[key] = new UNSAFE_ErrorResponseImpl(val.status, val.statusText, val.data, val.internal === true); } else if (val && val.__type === "Error") { // Attempt to reconstruct the right type of Error (i.e., ReferenceError) if (val.__subType) { let ErrorConstructor = window[val.__subType]; if (typeof ErrorConstructor === "function") { try { // @ts-expect-error let error = new ErrorConstructor(val.message); // Wipe away the client-side stack trace. Nothing to fill it in with // because we don't serialize SSR stack traces for security reasons error.stack = ""; serialized[key] = error; } catch (e) { // no-op - fall through and create a normal Error } } } if (serialized[key] == null) { let error = new Error(val.message); // Wipe away the client-side stack trace. Nothing to fill it in with // because we don't serialize SSR stack traces for security reasons error.stack = ""; serialized[key] = error; } } else { serialized[key] = val; } } return serialized; } //#endregion //////////////////////////////////////////////////////////////////////////////// //#region Contexts //////////////////////////////////////////////////////////////////////////////// const ViewTransitionContext = /*#__PURE__*/React.createContext({ isTransitioning: false }); { ViewTransitionContext.displayName = "ViewTransition"; } // TODO: (v7) Change the useFetcher data from `any` to `unknown` const FetchersContext = /*#__PURE__*/React.createContext(new Map()); { FetchersContext.displayName = "Fetchers"; } //#endregion //////////////////////////////////////////////////////////////////////////////// //#region Components //////////////////////////////////////////////////////////////////////////////// /** Webpack + React 17 fails to compile on any of the following because webpack complains that `startTransition` doesn't exist in `React`: * import { startTransition } from "react" * import * as React from from "react"; "startTransition" in React ? React.startTransition(() => setState()) : setState() * import * as React from from "react"; "startTransition" in React ? React["startTransition"](() => setState()) : setState() Moving it to a constant such as the following solves the Webpack/React 17 issue: * import * as React from from "react"; const START_TRANSITION = "startTransition"; START_TRANSITION in React ? React[START_TRANSITION](() => setState()) : setState() However, that introduces webpack/terser minification issues in production builds in React 18 where minification/obfuscation ends up removing the call of React.startTransition entirely from the first half of the ternary. Grabbing this exported reference once up front resolves that issue. See https://github.com/remix-run/react-router/issues/10579 */ const START_TRANSITION = "startTransition"; const startTransitionImpl = React[START_TRANSITION]; const FLUSH_SYNC = "flushSync"; const flushSyncImpl = ReactDOM[FLUSH_SYNC]; const USE_ID = "useId"; const useIdImpl = React[USE_ID]; function startTransitionSafe(cb) { if (startTransitionImpl) { startTransitionImpl(cb); } else { cb(); } } function flushSyncSafe(cb) { if (flushSyncImpl) { flushSyncImpl(cb); } else { cb(); } } class Deferred { status = "pending"; // @ts-expect-error - no initializer // @ts-expect-error - no initializer constructor() { this.promise = new Promise((resolve, reject) => { this.resolve = value => { if (this.status === "pending") { this.status = "resolved"; resolve(value); } }; this.reject = reason => { if (this.status === "pending") { this.status = "rejected"; reject(reason); } }; }); } } /** * Given a Remix Router instance, render the appropriate UI */ function RouterProvider({ fallbackElement, router, future }) { let [state, setStateImpl] = React.useState(router.state); let [pendingState, setPendingState] = React.useState(); let [vtContext, setVtContext] = React.useState({ isTransitioning: false }); let [renderDfd, setRenderDfd] = React.useState(); let [transition, setTransition] = React.useState(); let [interruption, setInterruption] = React.useState(); let fetcherData = React.useRef(new Map()); let { v7_startTransition } = future || {}; let optInStartTransition = React.useCallback(cb => { if (v7_startTransition) { startTransitionSafe(cb); } else { cb(); } }, [v7_startTransition]); let setState = React.useCallback((newState, { deletedFetchers, flushSync: flushSync, viewTransitionOpts: viewTransitionOpts }) => { newState.fetchers.forEach((fetcher, key) => { if (fetcher.data !== undefined) { fetcherData.current.set(key, fetcher.data); } }); deletedFetchers.forEach(key => fetcherData.current.delete(key)); let isViewTransitionUnavailable = router.window == null || router.window.document == null || typeof router.window.document.startViewTransition !== "function"; // If this isn't a view transition or it's not available in this browser, // just update and be done with it if (!viewTransitionOpts || isViewTransitionUnavailable) { if (flushSync) { flushSyncSafe(() => setStateImpl(newState)); } else { optInStartTransition(() => setStateImpl(newState)); } return; } // flushSync + startViewTransition if (flushSync) { // Flush through the context to mark DOM elements as transition=ing flushSyncSafe(() => { // Cancel any pending transitions if (transition) { renderDfd && renderDfd.resolve(); transition.skipTransition(); } setVtContext({ isTransitioning: true, flushSync: true, currentLocation: viewTransitionOpts.currentLocation, nextLocation: viewTransitionOpts.nextLocation }); }); // Update the DOM let t = router.window.document.startViewTransition(() => { flushSyncSafe(() => setStateImpl(newState)); }); // Clean up after the animation completes t.finished.finally(() => { flushSyncSafe(() => { setRenderDfd(undefined); setTransition(undefined); setPendingState(undefined); setVtContext({ isTransitioning: false }); }); }); flushSyncSafe(() => setTransition(t)); return; } // startTransition + startViewTransition if (transition) { // Interrupting an in-progress transition, cancel and let everything flush // out, and then kick off a new transition from the interruption state renderDfd && renderDfd.resolve(); transition.skipTransition(); setInterruption({ state: newState, currentLocation: viewTransitionOpts.currentLocation, nextLocation: viewTransitionOpts.nextLocation }); } else { // Completed navigation update with opted-in view transitions, let 'er rip setPendingState(newState); setVtContext({ isTransitioning: true, flushSync: false, currentLocation: viewTransitionOpts.currentLocation, nextLocation: viewTransitionOpts.nextLocation }); } }, [router.window, transition, renderDfd, fetcherData, optInStartTransition]); // Need to use a layout effect here so we are subscribed early enough to // pick up on any render-driven redirects/navigations (useEffect/<Navigate>) React.useLayoutEffect(() => router.subscribe(setState), [router, setState]); // When we start a view transition, create a Deferred we can use for the // eventual "completed" render React.useEffect(() => { if (vtContext.isTransitioning && !vtContext.flushSync) { setRenderDfd(new Deferred()); } }, [vtContext]); // Once the deferred is created, kick off startViewTransition() to update the // DOM and then wait on the Deferred to resolve (indicating the DOM update has // happened) React.useEffect(() => { if (renderDfd && pendingState && router.window) { let newState = pendingState; let renderPromise = renderDfd.promise; let transition = router.window.document.startViewTransition(async () => { optInStartTransition(() => setStateImpl(newState)); await renderPromise; }); transition.finished.finally(() => { setRenderDfd(undefined); setTransition(undefined); setPendingState(undefined); setVtContext({ isTransitioning: false }); }); setTransition(transition); } }, [optInStartTransition, pendingState, renderDfd, router.window]); // When the new location finally renders and is committed to the DOM, this // effect will run to resolve the transition React.useEffect(() => { if (renderDfd && pendingState && state.location.key === pendingState.location.key) { renderDfd.resolve(); } }, [renderDfd, transition, state.location, pendingState]); // If we get interrupted with a new navigation during a transition, we skip // the active transition, let it cleanup, then kick it off again here React.useEffect(() => { if (!vtContext.isTransitioning && interruption) { setPendingState(interruption.state); setVtContext({ isTransitioning: true, flushSync: false, currentLocation: interruption.currentLocation, nextLocation: interruption.nextLocation }); setInterruption(undefined); } }, [vtContext.isTransitioning, interruption]); React.useEffect(() => { UNSAFE_warning(fallbackElement == null || !router.future.v7_partialHydration, "`<RouterProvider fallbackElement>` is deprecated when using " + "`v7_partialHydration`, use a `HydrateFallback` component instead") ; // Only log this once on initial mount // eslint-disable-next-line react-hooks/exhaustive-deps }, []); let navigator = React.useMemo(() => { return { createHref: router.createHref, encodeLocation: router.encodeLocation, go: n => router.navigate(n), push: (to, state, opts) => router.navigate(to, { state, preventScrollReset: opts?.preventScrollReset }), replace: (to, state, opts) => router.navigate(to, { replace: true, state, preventScrollReset: opts?.preventScrollReset }) }; }, [router]); let basename = router.basename || "/"; let dataRouterContext = React.useMemo(() => ({ router, navigator, static: false, basename }), [router, navigator, basename]); let routerFuture = React.useMemo(() => ({ v7_relativeSplatPath: router.future.v7_relativeSplatPath }), [router.future.v7_relativeSplatPath]); React.useEffect(() => UNSAFE_logV6DeprecationWarnings(future, router.future), [future, router.future]); // The fragment and {null} here are important! We need them to keep React 18's // useId happy when we are server-rendering since we may have a <script> here // containing the hydrated server-side staticContext (from StaticRouterProvider). // useId relies on the component tree structure to generate deterministic id's // so we need to ensure it remains the same on the client even though // we don't need the <script> tag return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(UNSAFE_DataRouterContext.Provider, { value: dataRouterContext }, /*#__PURE__*/React.createElement(UNSAFE_DataRouterStateContext.Provider, { value: state }, /*#__PURE__*/React.createElement(FetchersContext.Provider, { value: fetcherData.current }, /*#__PURE__*/React.createElement(ViewTransitionContext.Provider, { value: vtContext }, /*#__PURE__*/React.createElement(Router, { basename: basename, location: state.location, navigationType: state.historyAction, navigator: navigator, future: routerFuture }, state.initialized || router.future.v7_partialHydration ? /*#__PURE__*/React.createElement(MemoizedDataRoutes, { routes: router.routes, future: router.future, state: state }) : fallbackElement))))), null); } // Memoize to avoid re-renders when updating `ViewTransitionContext` const MemoizedDataRoutes = /*#__PURE__*/React.memo(DataRoutes); function DataRoutes({ routes, future, state }) { return UNSAFE_useRoutesImpl(routes, undefined, state, future); } /** * A `<Router>` for use in web browsers. Provides the cleanest URLs. */ function BrowserRouter({ basename, children, future, window }) { let historyRef = React.useRef(); if (historyRef.current == null) { historyRef.current = createBrowserHistory({ window, v5Compat: true }); } let history = historyRef.current; let [state, setStateImpl] = React.useState({ action: history.action, location: history.location }); let { v7_startTransition } = future || {}; let setState = React.useCallback(newState => { v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState); }, [setStateImpl, v7_startTransition]); React.useLayoutEffect(() => history.listen(setState), [history, setState]); React.useEffect(() => UNSAFE_logV6DeprecationWarnings(future), [future]); return /*#__PURE__*/React.createElement(Router, { basename: basename, children: children, location: state.location, navigationType: state.action, navigator: history, future: future }); } /** * 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, future, window }) { let historyRef = React.useRef(); if (historyRef.current == null) { historyRef.current = createHashHistory({ window, v5Compat: true }); } let history = historyRef.current; let [state, setStateImpl] = React.useState({ action: history.action, location: history.location }); let { v7_startTransition } = future || {}; let setState = React.useCallback(newState => { v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState); }, [setStateImpl, v7_startTransition]); React.useLayoutEffect(() => history.listen(setState), [history, setState]); React.useEffect(() => UNSAFE_logV6DeprecationWarnings(future), [future]); return /*#__PURE__*/React.createElement(Router, { basename: basename, children: children, location: state.location, navigationType: state.action, navigator: history, future: future }); } /** * 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, future, history }) { let [state, setStateImpl] = React.useState({ action: history.action, location: history.location }); let { v7_startTransition } = future || {}; let setState = React.useCallback(newState => { v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState); }, [setStateImpl, v7_startTransition]); React.useLayoutEffect(() => history.listen(setState), [history, setState]); React.useEffect(() => UNSAFE_logV6DeprecationWarnings(future), [future]); return /*#__PURE__*/React.createElement(Router, { basename: basename, children: children, location: state.location, navigationType: state.action, navigator: history, future: future }); } { HistoryRouter.displayName = "unstable_HistoryRouter"; } const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined"; const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i; /** * The public API for rendering a history-aware `<a>`. */ const Link = /*#__PURE__*/React.forwardRef(function LinkWithRef({ onClick, relative, reloadDocument, replace, state, target, to, preventScrollReset, viewTransition, ...rest }, ref) { let { basename } = React.useContext(UNSAFE_NavigationContext); // Rendered into <a href> for absolute URLs let absoluteHref; let isExternal = false; if (typeof to === "string" && ABSOLUTE_URL_REGEX.test(to)) { // Render the absolute href server- and client-side absoluteHref = to; // Only check for external origins client-side if (isBrowser) { try { let currentUrl = new URL(window.location.href); let targetUrl = to.startsWith("//") ? new URL(currentUrl.protocol + to) : new URL(to); let path = stripBasename(targetUrl.pathname, basename); if (targetUrl.origin === currentUrl.origin && path != null) { // Strip the protocol/origin/basename for same-origin absolute URLs to = path + targetUrl.search + targetUrl.hash; } else { isExternal = true; } } catch (e) { // We can't do external URL detection without a valid URL UNSAFE_warning(false, `<Link to="${to}"> contains an invalid URL which will probably break ` + `when clicked - please update to a valid URL path.`) ; } } } // Rendered into <a href> for relative URLs let href = useHref(to, { relative }); let internalOnClick = useLinkClickHandler(to, { replace, state, target, preventScrollReset, relative, viewTransition }); function handleClick(event) { if (onClick) onClick(event); if (!event.defaultPrevented) { internalOnClick(event); } } return ( /*#__PURE__*/ // eslint-disable-next-line jsx-a11y/anchor-has-content React.createElement("a", Object.assign({}, rest, { href: absoluteHref || href, onClick: isExternal || reloadDocument ? onClick : handleClick, ref: ref, target: target })) ); }); { Link.displayName = "Link"; } /** * A `<Link>` wrapper that knows if it's "active" or not. */ const NavLink = /*#__PURE__*/React.forwardRef(function NavLinkWithRef({ "aria-current": ariaCurrentProp = "page", caseSensitive = false, className: classNameProp = "", end = false, style: styleProp, to, viewTransition, children, ...rest }, ref) { let path = useResolvedPath(to, { relative: rest.relative }); let location = useLocation(); let routerState = React.useContext(UNSAFE_DataRouterStateContext); let { navigator, basename } = React.useContext(UNSAFE_NavigationContext); let isTransitioning = routerState != null && // Conditional usage is OK here because the usage of a data router is static // eslint-disable-next-line react-hooks/rules-of-hooks useViewTransitionState(path) && viewTransition === true; let toPathname = navigator.encodeLocation ? navigator.encodeLocation(path).pathname : path.pathname; let locationPathname = location.pathname; let nextLocationPathname = routerState && routerState.navigation && routerState.navigation.location ? routerState.navigation.location.pathname : null; if (!caseSensitive) { locationPathname = locationPathname.toLowerCase(); nextLocationPathname = nextLocationPathname ? nextLocationPathname.toLowerCase() : null; toPathname = toPathname.toLowerCase(); } if (nextLocationPathname && basename) { nextLocationPathname = stripBasename(nextLocationPathname, basename) || nextLocationPathname; } // If the `to` has a trailing slash, look at that exact spot. Otherwise, // we're looking for a slash _after_ what's in `to`. For example: // // <NavLink to="/users"> and <NavLink to="/users/"> // both want to look for a / at index 6 to match URL `/users/matt` const endSlashPosition = toPathname !== "/" && toPathname.endsWith("/") ? toPathname.length - 1 : toPathname.length; let isActive = locationPathname === toPathname || !end && locationPathname.startsWith(toPathname) && locationPathname.charAt(endSlashPosition) === "/"; let isPending = nextLocationPathname != null && (nextLocationPathname === toPathname || !end && nextLocationPathname.startsWith(toPathname) && nextLocationPathname.charAt(toPathname.length) === "/"); let renderProps = { isActive, isPending, isTransitioning }; let ariaCurrent = isActive ? ariaCurrentProp : undefined; let className; if (typeof classNameProp === "function") { className = classNameProp(renderProps); } 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, isPending ? "pending" : null, isTransitioning ? "transitioning" : null].filter(Boolean).join(" "); } let style = typeof styleProp === "function" ? styleProp(renderProps) : styleProp; return /*#__PURE__*/React.createElement(Link, Object.assign({}, rest, { "aria-current": ariaCurrent, className: className, ref: ref, style: style, to: to, viewTransition: viewTransition }), typeof children === "function" ? children(renderProps) : children); }); { NavLink.displayName = "NavLink"; } /** * Form props shared by navigations and fetchers */ /** * Form props available to fetchers */ /** * Form props available to navigations */ /** * A `@remix-run/router`-aware `<form>`. It behaves like a normal form except * that the interaction with the server is with `fetch` instead of new document * requests, allowing components to add nicer UX to the page as the form is * submitted and returns with data. */ const Form = /*#__PURE__*/React.forwardRef(({ fetcherKey, navigate, reloadDocument, replace, state, method: _method = defaultMethod, action, onSubmit, relative, preventScrollReset, viewTransition, ...props }, forwardedRef) => { let submit = useSubmit(); let formAction = useFormAction(action, { relative }); let formMethod = _method.toLowerCase() === "get" ? "get" : "post"; let submitHandler = event => { onSubmit && onSubmit(event); if (event.defaultPrevented) return; event.preventDefault(); let submitter = event.nativeEvent.submitter; let submitMethod = submitter?.getAttribute("formmethod") || _method; submit(submitter || event.currentTarget, { fetcherKey, method: submitMethod, navigate, replace, state, relative, preventScrollReset, viewTransition }); }; return /*#__PURE__*/React.createElement("form", Object.assign({ ref: forwardedRef, method: formMethod, action: formAction, onSubmit: reloadDocument ? onSubmit : submitHandler }, props)); }); { Form.displayName = "Form"; } /** * This component will emulate the browser's scroll restoration on location * changes. */ function ScrollRestoration({ getKey, storageKey }) { useScrollRestoration({ getKey, storageKey }); return null; } { ScrollRestoration.displayName = "ScrollRestoration"; } //#endregion //////////////////////////////////////////////////////////////////////////////// //#region Hooks //////////////////////////////////////////////////////////////////////////////// var DataRouterHook = /*#__PURE__*/function (DataRouterHook) { DataRouterHook["UseScrollRestoration"] = "useScrollRestoration"; DataRouterHook["UseSubmit"] = "useSubmit"; DataRouterHook["UseSubmitFetcher"] = "useSubmitFetcher"; DataRouterHook["UseFetcher"] = "useFetcher"; DataRouterHook["useViewTransitionState"] = "useViewTransitionState"; return DataRouterHook; }(DataRouterHook || {}); var DataRouterStateHook = /*#__PURE__*/function (DataRouterStateHook) { DataRouterStateHook["UseFetcher"] = "useFetcher"; DataRouterStateHook["UseFetchers"] = "useFetchers"; DataRouterStateHook["UseScrollRestoration"] = "useScrollRestoration"; return DataRouterStateHook; }(DataRouterStateHook || {}); // Internal hooks function getDataRouterConsoleError(hookName) { return `${hookName} must be used within a data router. See https://reactrouter.com/v6/routers/picking-a-router.`; } function useDataRouterContext(hookName) { let ctx = React.useContext(UNSAFE_DataRouterContext); !ctx ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : void 0; return ctx; } function useDataRouterState(hookName) { let state = React.useContext(UNSAFE_DataRouterStateContext); !state ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : void 0; return state; } // External 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, preventScrollReset, relative, viewTransition } = {}) { let navigate = useNavigate(); let location = useLocation(); let path = useResolvedPath(to, { relative }); return React.useCallback(event => { if (shouldProcessLinkClick(event, target)) { event.preventDefault(); // If the URL hasn't changed, a regular <a> will do a replace instead of // a push, so do the same here unless the replace prop is explicitly set let replace = replaceProp !== undefined ? replaceProp : createPath(location) === createPath(path); navigate(to, { replace, state, preventScrollReset, relative, viewTransition }); } }, [location, navigate, path, replaceProp, state, target, to, preventScrollReset, relative, viewTransition]); } /** * A convenient wrapper for reading and writing search parameters via the * URLSearchParams interface. */ function useSearchParams(defaultInit) { UNSAFE_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.`) ; let defaultSearchParamsRef = React.useRef(createSearchParams(defaultInit)); let hasSetSearchParamsRef = React.useRef(false); let location = useLocation(); let searchParams = React.useMemo(() => // Only merge in the defaults if we haven't yet called setSearchParams. // Once we call that we want those to take precedence, otherwise you can't // remove a param with setSearchParams({}) if it has an initial value getSearchParamsForLocation(location.search, hasSetSearchParamsRef.current ? null : defaultSearchParamsRef.current), [location.search]); let navigate = useNavigate(); let setSearchParams = React.useCallback((nextInit, navigateOptions) => { const newSearchParams = createSearchParams(typeof nextInit === "function" ? nextInit(searchParams) : nextInit); hasSetSearchParamsRef.current = true; navigate("?" + newSearchParams, navigateOptions); }, [navigate, searchParams]); return [searchParams, setSearchParams]; } /** * Submits a HTML `<form>` to the server without reloading the page. */ /** * Submits a fetcher `<form>` to the server without reloading the page. */ function validateClientSideSubmission() { if (typeof document === "undefined") { throw new Error("You are calling submit during the server render. " + "Try calling submit within a `useEffect` or callback instead."); } } let fetcherId = 0; let getUniqueFetcherId = () => `__${String(++fetcherId)}__`; /** * Returns a function that may be used to programmatically submit a form (or * some arbitrary data) to the server. */ function useSubmit() { let { router } = useDataRouterContext(DataRouterHook.UseSubmit); let { basename } = React.useContext(UNSAFE_NavigationContext); let currentRouteId = UNSAFE_useRouteId(); return React.useCallback((target, options = {}) => { validateClientSideSubmission(); let { action, method, encType, formData, body } = getFormSubmissionInfo(target, basename); if (options.navigate === false) { let key = options.fetcherKey || getUniqueFetcherId(); router.fetch(key, currentRouteId, options.action || action, { preventScrollReset: options.preventScrollReset, formData, body, formMethod: options.method || method, formEncType: options.encType || encType, flushSync: options.flushSync }); } else { router.navigate(options.action || action, { preventScrollReset: options.preventScrollReset, formData, body, formMethod: options.method || method, formEncType: options.encType || encType, replace: options.replace, state: options.state, fromRouteId: currentRouteId, flushSync: options.flushSync, viewTransition: options.viewTransition }); } }, [router, basename, currentRouteId]); } // v7: Eventually we should deprecate this entirely in favor of using the // router method directly? function useFormAction(action, { relative } = {}) { let { basename } = React.useContext(UNSAFE_NavigationContext); let routeContext = React.useContext(UNSAFE_RouteContext); !routeContext ? UNSAFE_invariant(false, "useFormAction must be used inside a RouteContext") : void 0; let [match] = routeContext.matches.slice(-1); // Shallow clone path so we can modify it below, otherwise we modify the // object referenced by useMemo inside useResolvedPath let path = { ...useResolvedPath(action ? action : ".", { relative }) }; // If no action was specified, browsers will persist current search params // when determining the path, so match that behavior // https://github.com/remix-run/remix/issues/927 let location = useLocation(); if (action == null) { // Safe to write to this directly here since if action was undefined, we // would have called useResolvedPath(".") which will never include a search path.search = location.search; // When grabbing search params from the URL, remove any included ?index param // since it might not apply to our contextual route. We add it back based // on match.route.index below let params = new URLSearchParams(path.search); let indexValues = params.getAll("index"); let hasNakedIndexParam = indexValues.some(v => v === ""); if (hasNakedIndexParam) { params.delete("index"); indexValues.filter(v => v).forEach(v => params.append("index", v)); let qs = params.toString(); path.search = qs ? `?${qs}` : ""; } } if ((!action || action === ".") && match.route.index) { path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index"; } // If we're operating within a basename, prepend it to the pathname prior // to creating the form action. If this is a root navigation, then just use // the raw basename which allows the basename to have full control over the // presence of a trailing slash on root actions if (basename !== "/") { path.pathname = path.pathname === "/" ? basename : joinPaths([basename, path.pathname]); } return createPath(path); } // TODO: (v7) Change the useFetcher generic default from `any` to `unknown` /** * Interacts with route loaders and actions without causing a navigation. Great * for any interaction that stays on the same page. */ function useFetcher({ key } = {}) { let { router } = useDataRouterContext(DataRouterHook.UseFetcher); let state = useDataRouterState(DataRouterStateHook.UseFetcher); let fetcherData = React.useContext(FetchersContext); let route = React.useContext(UNSAFE_RouteContext); let routeId = route.matches[route.matches.length - 1]?.route.id; !fetcherData ? UNSAFE_invariant(false, `useFetcher must be used inside a FetchersContext`) : void 0; !route ? UNSAFE_invariant(false, `useFetcher must be used inside a RouteContext`) : void 0; !(routeId != null) ? UNSAFE_invariant(false, `useFetcher can only be used on routes that contain a unique "id"`) : void 0; // Fetcher key handling // OK to call conditionally to feature detect `useId` // eslint-disable-next-line react-hooks/rules-of-hooks let defaultKey = useIdImpl ? useIdImpl() : ""; let [fetcherKey, setFetcherKey] = React.useState(key || defaultKey); if (key && key !== fetcherKey) { setFetcherKey(key); } else if (!fetcherKey) { // We will only fall through here when `useId` is not available setFetcherKey(getUniqueFetcherId()); } // Registration/cleanup React.useEffect(() => { router.getFetcher(fetcherKey); return () => { // Tell the router we've unmounted - if v7_fetcherPersist is enabled this // will not delete immediately but instead queue up a delete after the // fetcher returns to an `idle` state router.deleteFetcher(fetcherKey); }; }, [router, fetcherKey]); // Fetcher additions let load = React.useCallback((href, opts) => { !routeId ? UNSAFE_invariant(false, "No routeId available for fetcher.load()") : void 0; router.fetch(fetcherKey, routeId, href, opts); }, [fetcherKey, routeId, router]); let submitImpl = useSubmit(); let submit = React.useCallback((target, opts) => { submitImpl(target, { ...opts, navigate: false, fetcherKey }); }, [fetcherKey, submitImpl]); let FetcherForm = React.useMemo(() => { let FetcherForm = /*#__PURE__*/React.forwardRef((props, ref) => { return /*#__PURE__*/React.createElement(Form, Object.assign({}, props, { navigate: false, fetcherKey: fetcherKey, ref: ref })); }); { FetcherForm.displayName = "fetcher.Form"; } return FetcherForm; }, [fetcherKey]); // Exposed FetcherWithComponents let fetcher = state.fetchers.get(fetcherKey) || IDLE_FETCHER; let data = fetcherData.get(fetcherKey); let fetcherWithComponents = React.useMemo(() => ({ Form: FetcherForm, submit, load, ...fetcher, data }), [FetcherForm, submit, load, fetcher, data]); return fetcherWithComponents; } /** * Provides all fetchers currently on the page. Useful for layouts and parent * routes that need to provide pending/optimistic UI regarding the fetch. */ function useFetchers() { let state = useDataRouterState(DataRouterStateHook.UseFetchers); return Array.from(state.fetchers.entries()).map(([key, fetcher]) => ({ ...fetcher, key })); } const SCROLL_RESTORATION_STORAGE_KEY = "react-router-scroll-positions"; let savedScrollPositions = {}; /** * When rendered inside a RouterProvider, will restore scroll positions on navigations */ function useScrollRestoration({ getKey, storageKey } = {}) { let { router } = useDataRouterContext(DataRouterHook.UseScrollRestoration); let { restoreScrollPosition, preventScrollReset } = useDataRouterState(DataRouterStateHook.UseScrollRestoration); let { basename } = React.useContext(UNSAFE_NavigationContext); let location = useLocation(); let matches = useMatches(); let navigation = useNavigation(); // Trigger manual scroll restoration while we're active React.useEffect(() => { window.history.scrollRestoration = "manual"; return () => { window.history.scrollRestoration = "auto"; }; }, []); // Save positions on pagehide usePageHide(React.useCallback(() => { if (navigation.state === "idle") { let key = (getKey ? getKey(location, matches) : null) || location.key; savedScrollPositions[key] = window.scrollY; } try { sessionStorage.setItem(storageKey || SCROLL_RESTORATION_STORAGE_KEY, JSON.stringify(savedScrollPositions)); } catch (error) { UNSAFE_warning(false, `Failed to save scroll positions in sessionStorage, <ScrollRestoration /> will not work properly (${error}).`) ; } window.history.scrollRestoration = "auto"; }, [storageKey, getKey, navigation.state, location, matches])); // Read in any saved scroll locations if (typeof document !== "undefined") { // eslint-disable-next-line react-hooks/rules-of-hooks React.useLayoutEffect(() => { try { let sessionPositions = sessionStorage.getItem(storageKey || SCROLL_RESTORATION_STORAGE_KEY); if (sessionPositions) { savedScrollPositions = JSON.parse(sessionPositions); } } catch (e) { // no-op, use default empty object } }, [storageKey]); // Enable scroll restoration in the router // eslint-disable-next-line react-hooks/rules-of-hooks React.useLayoutEffect(() => { let getKeyWithoutBasename = getKey && basename !== "/" ? (location, matches) => getKey( // Strip the basename to match useLocation() { ...location, pathname: stripBasename(location.pathname, basename) || location.pathname }, matches) : getKey; let disableScrollRestoration = router?.enableScrollRestoration(savedScrollPositions, () => window.scrollY, getKeyWithoutBasename); return () => disableScrollRestoration && disableScrollRestoration(); }, [router, basename, getKey]); // Restore scrolling when state.restoreScrollPosition changes // eslint-disable-next-line react-hooks/rules-of-hooks React.useLayoutEffect(() => { // Explicit false means don't do anything (used for submissions) if (restoreScrollPosition === false) { return; } // been here before, scroll to it if (typeof restoreScrollPosition === "number") { window.scrollTo(0, restoreScrollPosition); return; } // try to scroll to the hash if (location.hash) { let el = document.getElementById(decodeURIComponent(location.hash.slice(1))); if (el) { el.scrollIntoView(); return; } } // Don't reset if this navigation opted out if (preventScrollReset === true) { return; } // otherwise go to the top on new locations window.scrollTo(0, 0); }, [location, restoreScrollPosition, preventScrollReset]); } } /** * Setup a callback to be fired on the window's `beforeunload` event. This is * useful for saving some data to `window.localStorage` just before the page * refreshes. * * Note: The `callback` argument should be a function created with * `React.useCallback()`. */ function useBeforeUnload(callback, options) { let { capture } = options || {}; React.useEffect(() => { let opts = capture != null ? { capture } : undefined; window.addEventListener("beforeunload", callback, opts); return () => { window.removeEventListener("beforeunload", callback, opts); }; }, [callback, capture]); } /** * Setup a callback to be fired on the window's `pagehide` event. This is * useful for saving some data to `window.localStorage` just before the page * refreshes. This event is better supported than beforeunload across browsers. * * Note: The `callback` argument should be a function created with * `React.useCallback()`. */ function usePageHide(callback, options) { let { capture } = options || {}; React.useEffect(() => { let opts = capture != null ? { capture } : undefined; window.addEventListener("pagehide", callback, opts); return () => { window.removeEventListener("pagehide", callback, opts); }; }, [callback, capture]); } /** * Wrapper around useBlocker to show a window.confirm prompt to users instead * of building a custom UI with useBlocker. * * Warning: This has *a lot of rough edges* and behaves very differently (and * very incorrectly in some cases) across browsers if user click addition * back/forward navigations while the confirm is open. Use at your own risk. */ function usePrompt({ when, message }) { let blocker = useBlocker(when); React.useEffect(() => { if (blocker.state === "blocked") { let proceed = window.confirm(message); if (proceed) { // This timeout is needed to avoid a weird "race" on POP navigations // between the `window.history` revert navigation and the result of // `window.confirm` setTimeout(blocker.proceed, 0); } else { blocker.reset(); } } }, [blocker, message]); React.useEffect(() => { if (blocker.state === "blocked" && !when) { blocker.reset(); } }, [blocker, when]); } /** * Return a boolean indicating if there is an active view transition to the * given href. You can use this value to render CSS classes or viewTransitionName * styles onto your elements * * @param href The destination href * @param [opts.relative] Relative routing type ("route" | "path") */ function useViewTransitionState(to, opts = {}) { let vtContext = React.useContext(ViewTransitionContext); !(vtContext != null) ? UNSAFE_invariant(false, "`useViewTransitionState` must be used within `react-router-dom`'s `RouterProvider`. " + "Did you accidentally import `RouterProvider` from `react-router`?") : void 0; let { basename } = useDataRouterContext(DataRouterHook.useViewTransitionState); let path = useResolvedPath(to, { relative: opts.relative }); if (!vtContext.isTransitioning) { return false; } let currentPath = stripBasename(vtContext.currentLocation.pathname, basename) || vtContext.currentLocation.pathname; let nextPath = stripBasename(vtContext.nextLocation.pathname, basename) || vtContext.nextLocation.pathname; // Transition is active if we're going to or coming from the indicated // destination. This ensures that other PUSH navigations that reverse // an indicated transition apply. I.e., on the list view you have: // // <NavLink to="/details/1" viewTransition> // // If you click the breadcrumb back to the list view: // // <NavLink to="/list" viewTransition> // // We should apply the transition because it's indicated as active going // from /list -> /details/1 and therefore should be active on the reverse // (even though this isn't strictly a POP reverse) return matchPath(path.pathname, nextPath) != null || matchPath(path.pathname, currentPath) != null; } //#endregion export { BrowserRouter, Form, HashRouter, Link, NavLink, RouterProvider, ScrollRestoration, FetchersContext as UNSAFE_FetchersContext, ViewTransitionContext as UNSAFE_ViewTransitionContext, useScrollRestoration as UNSAFE_useScrollRestoration, createBrowserRouter, createHashRouter, createSearchParams, HistoryRouter as unstable_HistoryRouter, usePrompt as unstable_usePrompt, useBeforeUnload, useFetcher, useFetchers, useFormAction, useLinkClickHandler, useSearchParams, useSubmit, useViewTransitionState }; //# sourceMappingURL=react-router-dom.development.js.map