source: imaps-frontend/node_modules/react-router-dom/dist/react-router-dom.development.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 52.8 KB
RevLine 
[d565449]1/**
2 * React Router DOM v6.26.0
3 *
4 * Copyright (c) Remix Software Inc.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE.md file in the root directory of this source tree.
8 *
9 * @license MIT
10 */
11import * as React from 'react';
12import * as ReactDOM from 'react-dom';
13import { UNSAFE_mapRouteProperties, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, Router, UNSAFE_useRoutesImpl, UNSAFE_NavigationContext, useHref, useResolvedPath, useLocation, useNavigate, createPath, UNSAFE_useRouteId, UNSAFE_RouteContext, useMatches, useNavigation, useBlocker } from 'react-router';
14export { 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';
15import { stripBasename, UNSAFE_warning, createRouter, createBrowserHistory, createHashHistory, UNSAFE_ErrorResponseImpl, UNSAFE_invariant, joinPaths, IDLE_FETCHER, matchPath } from '@remix-run/router';
16export { UNSAFE_ErrorResponseImpl } from '@remix-run/router';
17
18const defaultMethod = "get";
19const defaultEncType = "application/x-www-form-urlencoded";
20function isHtmlElement(object) {
21 return object != null && typeof object.tagName === "string";
22}
23function isButtonElement(object) {
24 return isHtmlElement(object) && object.tagName.toLowerCase() === "button";
25}
26function isFormElement(object) {
27 return isHtmlElement(object) && object.tagName.toLowerCase() === "form";
28}
29function isInputElement(object) {
30 return isHtmlElement(object) && object.tagName.toLowerCase() === "input";
31}
32function isModifiedEvent(event) {
33 return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
34}
35function shouldProcessLinkClick(event, target) {
36 return event.button === 0 && (
37 // Ignore everything but left clicks
38 !target || target === "_self") &&
39 // Let browser handle "target=_blank" etc.
40 !isModifiedEvent(event) // Ignore clicks with modifier keys
41 ;
42}
43
44/**
45 * Creates a URLSearchParams object using the given initializer.
46 *
47 * This is identical to `new URLSearchParams(init)` except it also
48 * supports arrays as values in the object form of the initializer
49 * instead of just strings. This is convenient when you need multiple
50 * values for a given key, but don't want to use an array initializer.
51 *
52 * For example, instead of:
53 *
54 * let searchParams = new URLSearchParams([
55 * ['sort', 'name'],
56 * ['sort', 'price']
57 * ]);
58 *
59 * you can do:
60 *
61 * let searchParams = createSearchParams({
62 * sort: ['name', 'price']
63 * });
64 */
65function createSearchParams(init = "") {
66 return new URLSearchParams(typeof init === "string" || Array.isArray(init) || init instanceof URLSearchParams ? init : Object.keys(init).reduce((memo, key) => {
67 let value = init[key];
68 return memo.concat(Array.isArray(value) ? value.map(v => [key, v]) : [[key, value]]);
69 }, []));
70}
71function getSearchParamsForLocation(locationSearch, defaultSearchParams) {
72 let searchParams = createSearchParams(locationSearch);
73 if (defaultSearchParams) {
74 // Use `defaultSearchParams.forEach(...)` here instead of iterating of
75 // `defaultSearchParams.keys()` to work-around a bug in Firefox related to
76 // web extensions. Relevant Bugzilla tickets:
77 // https://bugzilla.mozilla.org/show_bug.cgi?id=1414602
78 // https://bugzilla.mozilla.org/show_bug.cgi?id=1023984
79 defaultSearchParams.forEach((_, key) => {
80 if (!searchParams.has(key)) {
81 defaultSearchParams.getAll(key).forEach(value => {
82 searchParams.append(key, value);
83 });
84 }
85 });
86 }
87 return searchParams;
88}
89
90// Thanks https://github.com/sindresorhus/type-fest!
91
92// One-time check for submitter support
93let _formDataSupportsSubmitter = null;
94function isFormDataSubmitterSupported() {
95 if (_formDataSupportsSubmitter === null) {
96 try {
97 new FormData(document.createElement("form"),
98 // @ts-expect-error if FormData supports the submitter parameter, this will throw
99 0);
100 _formDataSupportsSubmitter = false;
101 } catch (e) {
102 _formDataSupportsSubmitter = true;
103 }
104 }
105 return _formDataSupportsSubmitter;
106}
107
108/**
109 * Submit options shared by both navigations and fetchers
110 */
111
112/**
113 * Submit options available to fetchers
114 */
115
116/**
117 * Submit options available to navigations
118 */
119
120const supportedFormEncTypes = new Set(["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"]);
121function getFormEncType(encType) {
122 if (encType != null && !supportedFormEncTypes.has(encType)) {
123 UNSAFE_warning(false, `"${encType}" is not a valid \`encType\` for \`<Form>\`/\`<fetcher.Form>\` ` + `and will default to "${defaultEncType}"`) ;
124 return null;
125 }
126 return encType;
127}
128function getFormSubmissionInfo(target, basename) {
129 let method;
130 let action;
131 let encType;
132 let formData;
133 let body;
134 if (isFormElement(target)) {
135 // When grabbing the action from the element, it will have had the basename
136 // prefixed to ensure non-JS scenarios work, so strip it since we'll
137 // re-prefix in the router
138 let attr = target.getAttribute("action");
139 action = attr ? stripBasename(attr, basename) : null;
140 method = target.getAttribute("method") || defaultMethod;
141 encType = getFormEncType(target.getAttribute("enctype")) || defaultEncType;
142 formData = new FormData(target);
143 } else if (isButtonElement(target) || isInputElement(target) && (target.type === "submit" || target.type === "image")) {
144 let form = target.form;
145 if (form == null) {
146 throw new Error(`Cannot submit a <button> or <input type="submit"> without a <form>`);
147 }
148
149 // <button>/<input type="submit"> may override attributes of <form>
150
151 // When grabbing the action from the element, it will have had the basename
152 // prefixed to ensure non-JS scenarios work, so strip it since we'll
153 // re-prefix in the router
154 let attr = target.getAttribute("formaction") || form.getAttribute("action");
155 action = attr ? stripBasename(attr, basename) : null;
156 method = target.getAttribute("formmethod") || form.getAttribute("method") || defaultMethod;
157 encType = getFormEncType(target.getAttribute("formenctype")) || getFormEncType(form.getAttribute("enctype")) || defaultEncType;
158
159 // Build a FormData object populated from a form and submitter
160 formData = new FormData(form, target);
161
162 // If this browser doesn't support the `FormData(el, submitter)` format,
163 // then tack on the submitter value at the end. This is a lightweight
164 // solution that is not 100% spec compliant. For complete support in older
165 // browsers, consider using the `formdata-submitter-polyfill` package
166 if (!isFormDataSubmitterSupported()) {
167 let {
168 name,
169 type,
170 value
171 } = target;
172 if (type === "image") {
173 let prefix = name ? `${name}.` : "";
174 formData.append(`${prefix}x`, "0");
175 formData.append(`${prefix}y`, "0");
176 } else if (name) {
177 formData.append(name, value);
178 }
179 }
180 } else if (isHtmlElement(target)) {
181 throw new Error(`Cannot submit element that is not <form>, <button>, or ` + `<input type="submit|image">`);
182 } else {
183 method = defaultMethod;
184 action = null;
185 encType = defaultEncType;
186 body = target;
187 }
188
189 // Send body for <Form encType="text/plain" so we encode it into text
190 if (formData && encType === "text/plain") {
191 body = formData;
192 formData = undefined;
193 }
194 return {
195 action,
196 method: method.toLowerCase(),
197 encType,
198 formData,
199 body
200 };
201}
202
203/**
204 * NOTE: If you refactor this to split up the modules into separate files,
205 * you'll need to update the rollup config for react-router-dom-v5-compat.
206 */
207//#endregion
208// HEY YOU! DON'T TOUCH THIS VARIABLE!
209//
210// It is replaced with the proper version at build time via a babel plugin in
211// the rollup config.
212//
213// Export a global property onto the window for React Router detection by the
214// Core Web Vitals Technology Report. This way they can configure the `wappalyzer`
215// to detect and properly classify live websites as being built with React Router:
216// https://github.com/HTTPArchive/wappalyzer/blob/main/src/technologies/r.json
217const REACT_ROUTER_VERSION = "6";
218try {
219 window.__reactRouterVersion = REACT_ROUTER_VERSION;
220} catch (e) {
221 // no-op
222}
223
224////////////////////////////////////////////////////////////////////////////////
225//#region Routers
226////////////////////////////////////////////////////////////////////////////////
227function createBrowserRouter(routes, opts) {
228 return createRouter({
229 basename: opts?.basename,
230 future: {
231 ...opts?.future,
232 v7_prependBasename: true
233 },
234 history: createBrowserHistory({
235 window: opts?.window
236 }),
237 hydrationData: opts?.hydrationData || parseHydrationData(),
238 routes,
239 mapRouteProperties: UNSAFE_mapRouteProperties,
240 unstable_dataStrategy: opts?.unstable_dataStrategy,
241 unstable_patchRoutesOnMiss: opts?.unstable_patchRoutesOnMiss,
242 window: opts?.window
243 }).initialize();
244}
245function createHashRouter(routes, opts) {
246 return createRouter({
247 basename: opts?.basename,
248 future: {
249 ...opts?.future,
250 v7_prependBasename: true
251 },
252 history: createHashHistory({
253 window: opts?.window
254 }),
255 hydrationData: opts?.hydrationData || parseHydrationData(),
256 routes,
257 mapRouteProperties: UNSAFE_mapRouteProperties,
258 unstable_dataStrategy: opts?.unstable_dataStrategy,
259 unstable_patchRoutesOnMiss: opts?.unstable_patchRoutesOnMiss,
260 window: opts?.window
261 }).initialize();
262}
263function parseHydrationData() {
264 let state = window?.__staticRouterHydrationData;
265 if (state && state.errors) {
266 state = {
267 ...state,
268 errors: deserializeErrors(state.errors)
269 };
270 }
271 return state;
272}
273function deserializeErrors(errors) {
274 if (!errors) return null;
275 let entries = Object.entries(errors);
276 let serialized = {};
277 for (let [key, val] of entries) {
278 // Hey you! If you change this, please change the corresponding logic in
279 // serializeErrors in react-router-dom/server.tsx :)
280 if (val && val.__type === "RouteErrorResponse") {
281 serialized[key] = new UNSAFE_ErrorResponseImpl(val.status, val.statusText, val.data, val.internal === true);
282 } else if (val && val.__type === "Error") {
283 // Attempt to reconstruct the right type of Error (i.e., ReferenceError)
284 if (val.__subType) {
285 let ErrorConstructor = window[val.__subType];
286 if (typeof ErrorConstructor === "function") {
287 try {
288 // @ts-expect-error
289 let error = new ErrorConstructor(val.message);
290 // Wipe away the client-side stack trace. Nothing to fill it in with
291 // because we don't serialize SSR stack traces for security reasons
292 error.stack = "";
293 serialized[key] = error;
294 } catch (e) {
295 // no-op - fall through and create a normal Error
296 }
297 }
298 }
299 if (serialized[key] == null) {
300 let error = new Error(val.message);
301 // Wipe away the client-side stack trace. Nothing to fill it in with
302 // because we don't serialize SSR stack traces for security reasons
303 error.stack = "";
304 serialized[key] = error;
305 }
306 } else {
307 serialized[key] = val;
308 }
309 }
310 return serialized;
311}
312
313//#endregion
314
315////////////////////////////////////////////////////////////////////////////////
316//#region Contexts
317////////////////////////////////////////////////////////////////////////////////
318const ViewTransitionContext = /*#__PURE__*/React.createContext({
319 isTransitioning: false
320});
321{
322 ViewTransitionContext.displayName = "ViewTransition";
323}
324
325// TODO: (v7) Change the useFetcher data from `any` to `unknown`
326
327const FetchersContext = /*#__PURE__*/React.createContext(new Map());
328{
329 FetchersContext.displayName = "Fetchers";
330}
331
332//#endregion
333
334////////////////////////////////////////////////////////////////////////////////
335//#region Components
336////////////////////////////////////////////////////////////////////////////////
337
338/**
339 Webpack + React 17 fails to compile on any of the following because webpack
340 complains that `startTransition` doesn't exist in `React`:
341 * import { startTransition } from "react"
342 * import * as React from from "react";
343 "startTransition" in React ? React.startTransition(() => setState()) : setState()
344 * import * as React from from "react";
345 "startTransition" in React ? React["startTransition"](() => setState()) : setState()
346
347 Moving it to a constant such as the following solves the Webpack/React 17 issue:
348 * import * as React from from "react";
349 const START_TRANSITION = "startTransition";
350 START_TRANSITION in React ? React[START_TRANSITION](() => setState()) : setState()
351
352 However, that introduces webpack/terser minification issues in production builds
353 in React 18 where minification/obfuscation ends up removing the call of
354 React.startTransition entirely from the first half of the ternary. Grabbing
355 this exported reference once up front resolves that issue.
356
357 See https://github.com/remix-run/react-router/issues/10579
358*/
359const START_TRANSITION = "startTransition";
360const startTransitionImpl = React[START_TRANSITION];
361const FLUSH_SYNC = "flushSync";
362const flushSyncImpl = ReactDOM[FLUSH_SYNC];
363const USE_ID = "useId";
364const useIdImpl = React[USE_ID];
365function startTransitionSafe(cb) {
366 if (startTransitionImpl) {
367 startTransitionImpl(cb);
368 } else {
369 cb();
370 }
371}
372function flushSyncSafe(cb) {
373 if (flushSyncImpl) {
374 flushSyncImpl(cb);
375 } else {
376 cb();
377 }
378}
379class Deferred {
380 status = "pending";
381
382 // @ts-expect-error - no initializer
383
384 // @ts-expect-error - no initializer
385
386 constructor() {
387 this.promise = new Promise((resolve, reject) => {
388 this.resolve = value => {
389 if (this.status === "pending") {
390 this.status = "resolved";
391 resolve(value);
392 }
393 };
394 this.reject = reason => {
395 if (this.status === "pending") {
396 this.status = "rejected";
397 reject(reason);
398 }
399 };
400 });
401 }
402}
403
404/**
405 * Given a Remix Router instance, render the appropriate UI
406 */
407function RouterProvider({
408 fallbackElement,
409 router,
410 future
411}) {
412 let [state, setStateImpl] = React.useState(router.state);
413 let [pendingState, setPendingState] = React.useState();
414 let [vtContext, setVtContext] = React.useState({
415 isTransitioning: false
416 });
417 let [renderDfd, setRenderDfd] = React.useState();
418 let [transition, setTransition] = React.useState();
419 let [interruption, setInterruption] = React.useState();
420 let fetcherData = React.useRef(new Map());
421 let {
422 v7_startTransition
423 } = future || {};
424 let optInStartTransition = React.useCallback(cb => {
425 if (v7_startTransition) {
426 startTransitionSafe(cb);
427 } else {
428 cb();
429 }
430 }, [v7_startTransition]);
431 let setState = React.useCallback((newState, {
432 deletedFetchers,
433 unstable_flushSync: flushSync,
434 unstable_viewTransitionOpts: viewTransitionOpts
435 }) => {
436 deletedFetchers.forEach(key => fetcherData.current.delete(key));
437 newState.fetchers.forEach((fetcher, key) => {
438 if (fetcher.data !== undefined) {
439 fetcherData.current.set(key, fetcher.data);
440 }
441 });
442 let isViewTransitionUnavailable = router.window == null || router.window.document == null || typeof router.window.document.startViewTransition !== "function";
443
444 // If this isn't a view transition or it's not available in this browser,
445 // just update and be done with it
446 if (!viewTransitionOpts || isViewTransitionUnavailable) {
447 if (flushSync) {
448 flushSyncSafe(() => setStateImpl(newState));
449 } else {
450 optInStartTransition(() => setStateImpl(newState));
451 }
452 return;
453 }
454
455 // flushSync + startViewTransition
456 if (flushSync) {
457 // Flush through the context to mark DOM elements as transition=ing
458 flushSyncSafe(() => {
459 // Cancel any pending transitions
460 if (transition) {
461 renderDfd && renderDfd.resolve();
462 transition.skipTransition();
463 }
464 setVtContext({
465 isTransitioning: true,
466 flushSync: true,
467 currentLocation: viewTransitionOpts.currentLocation,
468 nextLocation: viewTransitionOpts.nextLocation
469 });
470 });
471
472 // Update the DOM
473 let t = router.window.document.startViewTransition(() => {
474 flushSyncSafe(() => setStateImpl(newState));
475 });
476
477 // Clean up after the animation completes
478 t.finished.finally(() => {
479 flushSyncSafe(() => {
480 setRenderDfd(undefined);
481 setTransition(undefined);
482 setPendingState(undefined);
483 setVtContext({
484 isTransitioning: false
485 });
486 });
487 });
488 flushSyncSafe(() => setTransition(t));
489 return;
490 }
491
492 // startTransition + startViewTransition
493 if (transition) {
494 // Interrupting an in-progress transition, cancel and let everything flush
495 // out, and then kick off a new transition from the interruption state
496 renderDfd && renderDfd.resolve();
497 transition.skipTransition();
498 setInterruption({
499 state: newState,
500 currentLocation: viewTransitionOpts.currentLocation,
501 nextLocation: viewTransitionOpts.nextLocation
502 });
503 } else {
504 // Completed navigation update with opted-in view transitions, let 'er rip
505 setPendingState(newState);
506 setVtContext({
507 isTransitioning: true,
508 flushSync: false,
509 currentLocation: viewTransitionOpts.currentLocation,
510 nextLocation: viewTransitionOpts.nextLocation
511 });
512 }
513 }, [router.window, transition, renderDfd, fetcherData, optInStartTransition]);
514
515 // Need to use a layout effect here so we are subscribed early enough to
516 // pick up on any render-driven redirects/navigations (useEffect/<Navigate>)
517 React.useLayoutEffect(() => router.subscribe(setState), [router, setState]);
518
519 // When we start a view transition, create a Deferred we can use for the
520 // eventual "completed" render
521 React.useEffect(() => {
522 if (vtContext.isTransitioning && !vtContext.flushSync) {
523 setRenderDfd(new Deferred());
524 }
525 }, [vtContext]);
526
527 // Once the deferred is created, kick off startViewTransition() to update the
528 // DOM and then wait on the Deferred to resolve (indicating the DOM update has
529 // happened)
530 React.useEffect(() => {
531 if (renderDfd && pendingState && router.window) {
532 let newState = pendingState;
533 let renderPromise = renderDfd.promise;
534 let transition = router.window.document.startViewTransition(async () => {
535 optInStartTransition(() => setStateImpl(newState));
536 await renderPromise;
537 });
538 transition.finished.finally(() => {
539 setRenderDfd(undefined);
540 setTransition(undefined);
541 setPendingState(undefined);
542 setVtContext({
543 isTransitioning: false
544 });
545 });
546 setTransition(transition);
547 }
548 }, [optInStartTransition, pendingState, renderDfd, router.window]);
549
550 // When the new location finally renders and is committed to the DOM, this
551 // effect will run to resolve the transition
552 React.useEffect(() => {
553 if (renderDfd && pendingState && state.location.key === pendingState.location.key) {
554 renderDfd.resolve();
555 }
556 }, [renderDfd, transition, state.location, pendingState]);
557
558 // If we get interrupted with a new navigation during a transition, we skip
559 // the active transition, let it cleanup, then kick it off again here
560 React.useEffect(() => {
561 if (!vtContext.isTransitioning && interruption) {
562 setPendingState(interruption.state);
563 setVtContext({
564 isTransitioning: true,
565 flushSync: false,
566 currentLocation: interruption.currentLocation,
567 nextLocation: interruption.nextLocation
568 });
569 setInterruption(undefined);
570 }
571 }, [vtContext.isTransitioning, interruption]);
572 React.useEffect(() => {
573 UNSAFE_warning(fallbackElement == null || !router.future.v7_partialHydration, "`<RouterProvider fallbackElement>` is deprecated when using " + "`v7_partialHydration`, use a `HydrateFallback` component instead") ;
574 // Only log this once on initial mount
575 // eslint-disable-next-line react-hooks/exhaustive-deps
576 }, []);
577 let navigator = React.useMemo(() => {
578 return {
579 createHref: router.createHref,
580 encodeLocation: router.encodeLocation,
581 go: n => router.navigate(n),
582 push: (to, state, opts) => router.navigate(to, {
583 state,
584 preventScrollReset: opts?.preventScrollReset
585 }),
586 replace: (to, state, opts) => router.navigate(to, {
587 replace: true,
588 state,
589 preventScrollReset: opts?.preventScrollReset
590 })
591 };
592 }, [router]);
593 let basename = router.basename || "/";
594 let dataRouterContext = React.useMemo(() => ({
595 router,
596 navigator,
597 static: false,
598 basename
599 }), [router, navigator, basename]);
600 let routerFuture = React.useMemo(() => ({
601 v7_relativeSplatPath: router.future.v7_relativeSplatPath
602 }), [router.future.v7_relativeSplatPath]);
603
604 // The fragment and {null} here are important! We need them to keep React 18's
605 // useId happy when we are server-rendering since we may have a <script> here
606 // containing the hydrated server-side staticContext (from StaticRouterProvider).
607 // useId relies on the component tree structure to generate deterministic id's
608 // so we need to ensure it remains the same on the client even though
609 // we don't need the <script> tag
610 return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(UNSAFE_DataRouterContext.Provider, {
611 value: dataRouterContext
612 }, /*#__PURE__*/React.createElement(UNSAFE_DataRouterStateContext.Provider, {
613 value: state
614 }, /*#__PURE__*/React.createElement(FetchersContext.Provider, {
615 value: fetcherData.current
616 }, /*#__PURE__*/React.createElement(ViewTransitionContext.Provider, {
617 value: vtContext
618 }, /*#__PURE__*/React.createElement(Router, {
619 basename: basename,
620 location: state.location,
621 navigationType: state.historyAction,
622 navigator: navigator,
623 future: routerFuture
624 }, state.initialized || router.future.v7_partialHydration ? /*#__PURE__*/React.createElement(MemoizedDataRoutes, {
625 routes: router.routes,
626 future: router.future,
627 state: state
628 }) : fallbackElement))))), null);
629}
630
631// Memoize to avoid re-renders when updating `ViewTransitionContext`
632const MemoizedDataRoutes = /*#__PURE__*/React.memo(DataRoutes);
633function DataRoutes({
634 routes,
635 future,
636 state
637}) {
638 return UNSAFE_useRoutesImpl(routes, undefined, state, future);
639}
640/**
641 * A `<Router>` for use in web browsers. Provides the cleanest URLs.
642 */
643function BrowserRouter({
644 basename,
645 children,
646 future,
647 window
648}) {
649 let historyRef = React.useRef();
650 if (historyRef.current == null) {
651 historyRef.current = createBrowserHistory({
652 window,
653 v5Compat: true
654 });
655 }
656 let history = historyRef.current;
657 let [state, setStateImpl] = React.useState({
658 action: history.action,
659 location: history.location
660 });
661 let {
662 v7_startTransition
663 } = future || {};
664 let setState = React.useCallback(newState => {
665 v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState);
666 }, [setStateImpl, v7_startTransition]);
667 React.useLayoutEffect(() => history.listen(setState), [history, setState]);
668 return /*#__PURE__*/React.createElement(Router, {
669 basename: basename,
670 children: children,
671 location: state.location,
672 navigationType: state.action,
673 navigator: history,
674 future: future
675 });
676}
677/**
678 * A `<Router>` for use in web browsers. Stores the location in the hash
679 * portion of the URL so it is not sent to the server.
680 */
681function HashRouter({
682 basename,
683 children,
684 future,
685 window
686}) {
687 let historyRef = React.useRef();
688 if (historyRef.current == null) {
689 historyRef.current = createHashHistory({
690 window,
691 v5Compat: true
692 });
693 }
694 let history = historyRef.current;
695 let [state, setStateImpl] = React.useState({
696 action: history.action,
697 location: history.location
698 });
699 let {
700 v7_startTransition
701 } = future || {};
702 let setState = React.useCallback(newState => {
703 v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState);
704 }, [setStateImpl, v7_startTransition]);
705 React.useLayoutEffect(() => history.listen(setState), [history, setState]);
706 return /*#__PURE__*/React.createElement(Router, {
707 basename: basename,
708 children: children,
709 location: state.location,
710 navigationType: state.action,
711 navigator: history,
712 future: future
713 });
714}
715/**
716 * A `<Router>` that accepts a pre-instantiated history object. It's important
717 * to note that using your own history object is highly discouraged and may add
718 * two versions of the history library to your bundles unless you use the same
719 * version of the history library that React Router uses internally.
720 */
721function HistoryRouter({
722 basename,
723 children,
724 future,
725 history
726}) {
727 let [state, setStateImpl] = React.useState({
728 action: history.action,
729 location: history.location
730 });
731 let {
732 v7_startTransition
733 } = future || {};
734 let setState = React.useCallback(newState => {
735 v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState);
736 }, [setStateImpl, v7_startTransition]);
737 React.useLayoutEffect(() => history.listen(setState), [history, setState]);
738 return /*#__PURE__*/React.createElement(Router, {
739 basename: basename,
740 children: children,
741 location: state.location,
742 navigationType: state.action,
743 navigator: history,
744 future: future
745 });
746}
747{
748 HistoryRouter.displayName = "unstable_HistoryRouter";
749}
750const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
751const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
752
753/**
754 * The public API for rendering a history-aware `<a>`.
755 */
756const Link = /*#__PURE__*/React.forwardRef(function LinkWithRef({
757 onClick,
758 relative,
759 reloadDocument,
760 replace,
761 state,
762 target,
763 to,
764 preventScrollReset,
765 unstable_viewTransition,
766 ...rest
767}, ref) {
768 let {
769 basename
770 } = React.useContext(UNSAFE_NavigationContext);
771
772 // Rendered into <a href> for absolute URLs
773 let absoluteHref;
774 let isExternal = false;
775 if (typeof to === "string" && ABSOLUTE_URL_REGEX.test(to)) {
776 // Render the absolute href server- and client-side
777 absoluteHref = to;
778
779 // Only check for external origins client-side
780 if (isBrowser) {
781 try {
782 let currentUrl = new URL(window.location.href);
783 let targetUrl = to.startsWith("//") ? new URL(currentUrl.protocol + to) : new URL(to);
784 let path = stripBasename(targetUrl.pathname, basename);
785 if (targetUrl.origin === currentUrl.origin && path != null) {
786 // Strip the protocol/origin/basename for same-origin absolute URLs
787 to = path + targetUrl.search + targetUrl.hash;
788 } else {
789 isExternal = true;
790 }
791 } catch (e) {
792 // We can't do external URL detection without a valid URL
793 UNSAFE_warning(false, `<Link to="${to}"> contains an invalid URL which will probably break ` + `when clicked - please update to a valid URL path.`) ;
794 }
795 }
796 }
797
798 // Rendered into <a href> for relative URLs
799 let href = useHref(to, {
800 relative
801 });
802 let internalOnClick = useLinkClickHandler(to, {
803 replace,
804 state,
805 target,
806 preventScrollReset,
807 relative,
808 unstable_viewTransition
809 });
810 function handleClick(event) {
811 if (onClick) onClick(event);
812 if (!event.defaultPrevented) {
813 internalOnClick(event);
814 }
815 }
816 return (
817 /*#__PURE__*/
818 // eslint-disable-next-line jsx-a11y/anchor-has-content
819 React.createElement("a", Object.assign({}, rest, {
820 href: absoluteHref || href,
821 onClick: isExternal || reloadDocument ? onClick : handleClick,
822 ref: ref,
823 target: target
824 }))
825 );
826});
827{
828 Link.displayName = "Link";
829}
830/**
831 * A `<Link>` wrapper that knows if it's "active" or not.
832 */
833const NavLink = /*#__PURE__*/React.forwardRef(function NavLinkWithRef({
834 "aria-current": ariaCurrentProp = "page",
835 caseSensitive = false,
836 className: classNameProp = "",
837 end = false,
838 style: styleProp,
839 to,
840 unstable_viewTransition,
841 children,
842 ...rest
843}, ref) {
844 let path = useResolvedPath(to, {
845 relative: rest.relative
846 });
847 let location = useLocation();
848 let routerState = React.useContext(UNSAFE_DataRouterStateContext);
849 let {
850 navigator,
851 basename
852 } = React.useContext(UNSAFE_NavigationContext);
853 let isTransitioning = routerState != null &&
854 // Conditional usage is OK here because the usage of a data router is static
855 // eslint-disable-next-line react-hooks/rules-of-hooks
856 useViewTransitionState(path) && unstable_viewTransition === true;
857 let toPathname = navigator.encodeLocation ? navigator.encodeLocation(path).pathname : path.pathname;
858 let locationPathname = location.pathname;
859 let nextLocationPathname = routerState && routerState.navigation && routerState.navigation.location ? routerState.navigation.location.pathname : null;
860 if (!caseSensitive) {
861 locationPathname = locationPathname.toLowerCase();
862 nextLocationPathname = nextLocationPathname ? nextLocationPathname.toLowerCase() : null;
863 toPathname = toPathname.toLowerCase();
864 }
865 if (nextLocationPathname && basename) {
866 nextLocationPathname = stripBasename(nextLocationPathname, basename) || nextLocationPathname;
867 }
868
869 // If the `to` has a trailing slash, look at that exact spot. Otherwise,
870 // we're looking for a slash _after_ what's in `to`. For example:
871 //
872 // <NavLink to="/users"> and <NavLink to="/users/">
873 // both want to look for a / at index 6 to match URL `/users/matt`
874 const endSlashPosition = toPathname !== "/" && toPathname.endsWith("/") ? toPathname.length - 1 : toPathname.length;
875 let isActive = locationPathname === toPathname || !end && locationPathname.startsWith(toPathname) && locationPathname.charAt(endSlashPosition) === "/";
876 let isPending = nextLocationPathname != null && (nextLocationPathname === toPathname || !end && nextLocationPathname.startsWith(toPathname) && nextLocationPathname.charAt(toPathname.length) === "/");
877 let renderProps = {
878 isActive,
879 isPending,
880 isTransitioning
881 };
882 let ariaCurrent = isActive ? ariaCurrentProp : undefined;
883 let className;
884 if (typeof classNameProp === "function") {
885 className = classNameProp(renderProps);
886 } else {
887 // If the className prop is not a function, we use a default `active`
888 // class for <NavLink />s that are active. In v5 `active` was the default
889 // value for `activeClassName`, but we are removing that API and can still
890 // use the old default behavior for a cleaner upgrade path and keep the
891 // simple styling rules working as they currently do.
892 className = [classNameProp, isActive ? "active" : null, isPending ? "pending" : null, isTransitioning ? "transitioning" : null].filter(Boolean).join(" ");
893 }
894 let style = typeof styleProp === "function" ? styleProp(renderProps) : styleProp;
895 return /*#__PURE__*/React.createElement(Link, Object.assign({}, rest, {
896 "aria-current": ariaCurrent,
897 className: className,
898 ref: ref,
899 style: style,
900 to: to,
901 unstable_viewTransition: unstable_viewTransition
902 }), typeof children === "function" ? children(renderProps) : children);
903});
904{
905 NavLink.displayName = "NavLink";
906}
907
908/**
909 * Form props shared by navigations and fetchers
910 */
911
912/**
913 * Form props available to fetchers
914 */
915
916/**
917 * Form props available to navigations
918 */
919
920/**
921 * A `@remix-run/router`-aware `<form>`. It behaves like a normal form except
922 * that the interaction with the server is with `fetch` instead of new document
923 * requests, allowing components to add nicer UX to the page as the form is
924 * submitted and returns with data.
925 */
926const Form = /*#__PURE__*/React.forwardRef(({
927 fetcherKey,
928 navigate,
929 reloadDocument,
930 replace,
931 state,
932 method: _method = defaultMethod,
933 action,
934 onSubmit,
935 relative,
936 preventScrollReset,
937 unstable_viewTransition,
938 ...props
939}, forwardedRef) => {
940 let submit = useSubmit();
941 let formAction = useFormAction(action, {
942 relative
943 });
944 let formMethod = _method.toLowerCase() === "get" ? "get" : "post";
945 let submitHandler = event => {
946 onSubmit && onSubmit(event);
947 if (event.defaultPrevented) return;
948 event.preventDefault();
949 let submitter = event.nativeEvent.submitter;
950 let submitMethod = submitter?.getAttribute("formmethod") || _method;
951 submit(submitter || event.currentTarget, {
952 fetcherKey,
953 method: submitMethod,
954 navigate,
955 replace,
956 state,
957 relative,
958 preventScrollReset,
959 unstable_viewTransition
960 });
961 };
962 return /*#__PURE__*/React.createElement("form", Object.assign({
963 ref: forwardedRef,
964 method: formMethod,
965 action: formAction,
966 onSubmit: reloadDocument ? onSubmit : submitHandler
967 }, props));
968});
969{
970 Form.displayName = "Form";
971}
972/**
973 * This component will emulate the browser's scroll restoration on location
974 * changes.
975 */
976function ScrollRestoration({
977 getKey,
978 storageKey
979}) {
980 useScrollRestoration({
981 getKey,
982 storageKey
983 });
984 return null;
985}
986{
987 ScrollRestoration.displayName = "ScrollRestoration";
988}
989//#endregion
990
991////////////////////////////////////////////////////////////////////////////////
992//#region Hooks
993////////////////////////////////////////////////////////////////////////////////
994var DataRouterHook = /*#__PURE__*/function (DataRouterHook) {
995 DataRouterHook["UseScrollRestoration"] = "useScrollRestoration";
996 DataRouterHook["UseSubmit"] = "useSubmit";
997 DataRouterHook["UseSubmitFetcher"] = "useSubmitFetcher";
998 DataRouterHook["UseFetcher"] = "useFetcher";
999 DataRouterHook["useViewTransitionState"] = "useViewTransitionState";
1000 return DataRouterHook;
1001}(DataRouterHook || {});
1002var DataRouterStateHook = /*#__PURE__*/function (DataRouterStateHook) {
1003 DataRouterStateHook["UseFetcher"] = "useFetcher";
1004 DataRouterStateHook["UseFetchers"] = "useFetchers";
1005 DataRouterStateHook["UseScrollRestoration"] = "useScrollRestoration";
1006 return DataRouterStateHook;
1007}(DataRouterStateHook || {}); // Internal hooks
1008function getDataRouterConsoleError(hookName) {
1009 return `${hookName} must be used within a data router. See https://reactrouter.com/routers/picking-a-router.`;
1010}
1011function useDataRouterContext(hookName) {
1012 let ctx = React.useContext(UNSAFE_DataRouterContext);
1013 !ctx ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : void 0;
1014 return ctx;
1015}
1016function useDataRouterState(hookName) {
1017 let state = React.useContext(UNSAFE_DataRouterStateContext);
1018 !state ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : void 0;
1019 return state;
1020}
1021
1022// External hooks
1023
1024/**
1025 * Handles the click behavior for router `<Link>` components. This is useful if
1026 * you need to create custom `<Link>` components with the same click behavior we
1027 * use in our exported `<Link>`.
1028 */
1029function useLinkClickHandler(to, {
1030 target,
1031 replace: replaceProp,
1032 state,
1033 preventScrollReset,
1034 relative,
1035 unstable_viewTransition
1036} = {}) {
1037 let navigate = useNavigate();
1038 let location = useLocation();
1039 let path = useResolvedPath(to, {
1040 relative
1041 });
1042 return React.useCallback(event => {
1043 if (shouldProcessLinkClick(event, target)) {
1044 event.preventDefault();
1045
1046 // If the URL hasn't changed, a regular <a> will do a replace instead of
1047 // a push, so do the same here unless the replace prop is explicitly set
1048 let replace = replaceProp !== undefined ? replaceProp : createPath(location) === createPath(path);
1049 navigate(to, {
1050 replace,
1051 state,
1052 preventScrollReset,
1053 relative,
1054 unstable_viewTransition
1055 });
1056 }
1057 }, [location, navigate, path, replaceProp, state, target, to, preventScrollReset, relative, unstable_viewTransition]);
1058}
1059
1060/**
1061 * A convenient wrapper for reading and writing search parameters via the
1062 * URLSearchParams interface.
1063 */
1064function useSearchParams(defaultInit) {
1065 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.`) ;
1066 let defaultSearchParamsRef = React.useRef(createSearchParams(defaultInit));
1067 let hasSetSearchParamsRef = React.useRef(false);
1068 let location = useLocation();
1069 let searchParams = React.useMemo(() =>
1070 // Only merge in the defaults if we haven't yet called setSearchParams.
1071 // Once we call that we want those to take precedence, otherwise you can't
1072 // remove a param with setSearchParams({}) if it has an initial value
1073 getSearchParamsForLocation(location.search, hasSetSearchParamsRef.current ? null : defaultSearchParamsRef.current), [location.search]);
1074 let navigate = useNavigate();
1075 let setSearchParams = React.useCallback((nextInit, navigateOptions) => {
1076 const newSearchParams = createSearchParams(typeof nextInit === "function" ? nextInit(searchParams) : nextInit);
1077 hasSetSearchParamsRef.current = true;
1078 navigate("?" + newSearchParams, navigateOptions);
1079 }, [navigate, searchParams]);
1080 return [searchParams, setSearchParams];
1081}
1082
1083/**
1084 * Submits a HTML `<form>` to the server without reloading the page.
1085 */
1086
1087/**
1088 * Submits a fetcher `<form>` to the server without reloading the page.
1089 */
1090
1091function validateClientSideSubmission() {
1092 if (typeof document === "undefined") {
1093 throw new Error("You are calling submit during the server render. " + "Try calling submit within a `useEffect` or callback instead.");
1094 }
1095}
1096let fetcherId = 0;
1097let getUniqueFetcherId = () => `__${String(++fetcherId)}__`;
1098
1099/**
1100 * Returns a function that may be used to programmatically submit a form (or
1101 * some arbitrary data) to the server.
1102 */
1103function useSubmit() {
1104 let {
1105 router
1106 } = useDataRouterContext(DataRouterHook.UseSubmit);
1107 let {
1108 basename
1109 } = React.useContext(UNSAFE_NavigationContext);
1110 let currentRouteId = UNSAFE_useRouteId();
1111 return React.useCallback((target, options = {}) => {
1112 validateClientSideSubmission();
1113 let {
1114 action,
1115 method,
1116 encType,
1117 formData,
1118 body
1119 } = getFormSubmissionInfo(target, basename);
1120 if (options.navigate === false) {
1121 let key = options.fetcherKey || getUniqueFetcherId();
1122 router.fetch(key, currentRouteId, options.action || action, {
1123 preventScrollReset: options.preventScrollReset,
1124 formData,
1125 body,
1126 formMethod: options.method || method,
1127 formEncType: options.encType || encType,
1128 unstable_flushSync: options.unstable_flushSync
1129 });
1130 } else {
1131 router.navigate(options.action || action, {
1132 preventScrollReset: options.preventScrollReset,
1133 formData,
1134 body,
1135 formMethod: options.method || method,
1136 formEncType: options.encType || encType,
1137 replace: options.replace,
1138 state: options.state,
1139 fromRouteId: currentRouteId,
1140 unstable_flushSync: options.unstable_flushSync,
1141 unstable_viewTransition: options.unstable_viewTransition
1142 });
1143 }
1144 }, [router, basename, currentRouteId]);
1145}
1146
1147// v7: Eventually we should deprecate this entirely in favor of using the
1148// router method directly?
1149function useFormAction(action, {
1150 relative
1151} = {}) {
1152 let {
1153 basename
1154 } = React.useContext(UNSAFE_NavigationContext);
1155 let routeContext = React.useContext(UNSAFE_RouteContext);
1156 !routeContext ? UNSAFE_invariant(false, "useFormAction must be used inside a RouteContext") : void 0;
1157 let [match] = routeContext.matches.slice(-1);
1158 // Shallow clone path so we can modify it below, otherwise we modify the
1159 // object referenced by useMemo inside useResolvedPath
1160 let path = {
1161 ...useResolvedPath(action ? action : ".", {
1162 relative
1163 })
1164 };
1165
1166 // If no action was specified, browsers will persist current search params
1167 // when determining the path, so match that behavior
1168 // https://github.com/remix-run/remix/issues/927
1169 let location = useLocation();
1170 if (action == null) {
1171 // Safe to write to this directly here since if action was undefined, we
1172 // would have called useResolvedPath(".") which will never include a search
1173 path.search = location.search;
1174
1175 // When grabbing search params from the URL, remove any included ?index param
1176 // since it might not apply to our contextual route. We add it back based
1177 // on match.route.index below
1178 let params = new URLSearchParams(path.search);
1179 if (params.has("index") && params.get("index") === "") {
1180 params.delete("index");
1181 path.search = params.toString() ? `?${params.toString()}` : "";
1182 }
1183 }
1184 if ((!action || action === ".") && match.route.index) {
1185 path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
1186 }
1187
1188 // If we're operating within a basename, prepend it to the pathname prior
1189 // to creating the form action. If this is a root navigation, then just use
1190 // the raw basename which allows the basename to have full control over the
1191 // presence of a trailing slash on root actions
1192 if (basename !== "/") {
1193 path.pathname = path.pathname === "/" ? basename : joinPaths([basename, path.pathname]);
1194 }
1195 return createPath(path);
1196}
1197// TODO: (v7) Change the useFetcher generic default from `any` to `unknown`
1198/**
1199 * Interacts with route loaders and actions without causing a navigation. Great
1200 * for any interaction that stays on the same page.
1201 */
1202function useFetcher({
1203 key
1204} = {}) {
1205 let {
1206 router
1207 } = useDataRouterContext(DataRouterHook.UseFetcher);
1208 let state = useDataRouterState(DataRouterStateHook.UseFetcher);
1209 let fetcherData = React.useContext(FetchersContext);
1210 let route = React.useContext(UNSAFE_RouteContext);
1211 let routeId = route.matches[route.matches.length - 1]?.route.id;
1212 !fetcherData ? UNSAFE_invariant(false, `useFetcher must be used inside a FetchersContext`) : void 0;
1213 !route ? UNSAFE_invariant(false, `useFetcher must be used inside a RouteContext`) : void 0;
1214 !(routeId != null) ? UNSAFE_invariant(false, `useFetcher can only be used on routes that contain a unique "id"`) : void 0;
1215
1216 // Fetcher key handling
1217 // OK to call conditionally to feature detect `useId`
1218 // eslint-disable-next-line react-hooks/rules-of-hooks
1219 let defaultKey = useIdImpl ? useIdImpl() : "";
1220 let [fetcherKey, setFetcherKey] = React.useState(key || defaultKey);
1221 if (key && key !== fetcherKey) {
1222 setFetcherKey(key);
1223 } else if (!fetcherKey) {
1224 // We will only fall through here when `useId` is not available
1225 setFetcherKey(getUniqueFetcherId());
1226 }
1227
1228 // Registration/cleanup
1229 React.useEffect(() => {
1230 router.getFetcher(fetcherKey);
1231 return () => {
1232 // Tell the router we've unmounted - if v7_fetcherPersist is enabled this
1233 // will not delete immediately but instead queue up a delete after the
1234 // fetcher returns to an `idle` state
1235 router.deleteFetcher(fetcherKey);
1236 };
1237 }, [router, fetcherKey]);
1238
1239 // Fetcher additions
1240 let load = React.useCallback((href, opts) => {
1241 !routeId ? UNSAFE_invariant(false, "No routeId available for fetcher.load()") : void 0;
1242 router.fetch(fetcherKey, routeId, href, opts);
1243 }, [fetcherKey, routeId, router]);
1244 let submitImpl = useSubmit();
1245 let submit = React.useCallback((target, opts) => {
1246 submitImpl(target, {
1247 ...opts,
1248 navigate: false,
1249 fetcherKey
1250 });
1251 }, [fetcherKey, submitImpl]);
1252 let FetcherForm = React.useMemo(() => {
1253 let FetcherForm = /*#__PURE__*/React.forwardRef((props, ref) => {
1254 return /*#__PURE__*/React.createElement(Form, Object.assign({}, props, {
1255 navigate: false,
1256 fetcherKey: fetcherKey,
1257 ref: ref
1258 }));
1259 });
1260 {
1261 FetcherForm.displayName = "fetcher.Form";
1262 }
1263 return FetcherForm;
1264 }, [fetcherKey]);
1265
1266 // Exposed FetcherWithComponents
1267 let fetcher = state.fetchers.get(fetcherKey) || IDLE_FETCHER;
1268 let data = fetcherData.get(fetcherKey);
1269 let fetcherWithComponents = React.useMemo(() => ({
1270 Form: FetcherForm,
1271 submit,
1272 load,
1273 ...fetcher,
1274 data
1275 }), [FetcherForm, submit, load, fetcher, data]);
1276 return fetcherWithComponents;
1277}
1278
1279/**
1280 * Provides all fetchers currently on the page. Useful for layouts and parent
1281 * routes that need to provide pending/optimistic UI regarding the fetch.
1282 */
1283function useFetchers() {
1284 let state = useDataRouterState(DataRouterStateHook.UseFetchers);
1285 return Array.from(state.fetchers.entries()).map(([key, fetcher]) => ({
1286 ...fetcher,
1287 key
1288 }));
1289}
1290const SCROLL_RESTORATION_STORAGE_KEY = "react-router-scroll-positions";
1291let savedScrollPositions = {};
1292
1293/**
1294 * When rendered inside a RouterProvider, will restore scroll positions on navigations
1295 */
1296function useScrollRestoration({
1297 getKey,
1298 storageKey
1299} = {}) {
1300 let {
1301 router
1302 } = useDataRouterContext(DataRouterHook.UseScrollRestoration);
1303 let {
1304 restoreScrollPosition,
1305 preventScrollReset
1306 } = useDataRouterState(DataRouterStateHook.UseScrollRestoration);
1307 let {
1308 basename
1309 } = React.useContext(UNSAFE_NavigationContext);
1310 let location = useLocation();
1311 let matches = useMatches();
1312 let navigation = useNavigation();
1313
1314 // Trigger manual scroll restoration while we're active
1315 React.useEffect(() => {
1316 window.history.scrollRestoration = "manual";
1317 return () => {
1318 window.history.scrollRestoration = "auto";
1319 };
1320 }, []);
1321
1322 // Save positions on pagehide
1323 usePageHide(React.useCallback(() => {
1324 if (navigation.state === "idle") {
1325 let key = (getKey ? getKey(location, matches) : null) || location.key;
1326 savedScrollPositions[key] = window.scrollY;
1327 }
1328 try {
1329 sessionStorage.setItem(storageKey || SCROLL_RESTORATION_STORAGE_KEY, JSON.stringify(savedScrollPositions));
1330 } catch (error) {
1331 UNSAFE_warning(false, `Failed to save scroll positions in sessionStorage, <ScrollRestoration /> will not work properly (${error}).`) ;
1332 }
1333 window.history.scrollRestoration = "auto";
1334 }, [storageKey, getKey, navigation.state, location, matches]));
1335
1336 // Read in any saved scroll locations
1337 if (typeof document !== "undefined") {
1338 // eslint-disable-next-line react-hooks/rules-of-hooks
1339 React.useLayoutEffect(() => {
1340 try {
1341 let sessionPositions = sessionStorage.getItem(storageKey || SCROLL_RESTORATION_STORAGE_KEY);
1342 if (sessionPositions) {
1343 savedScrollPositions = JSON.parse(sessionPositions);
1344 }
1345 } catch (e) {
1346 // no-op, use default empty object
1347 }
1348 }, [storageKey]);
1349
1350 // Enable scroll restoration in the router
1351 // eslint-disable-next-line react-hooks/rules-of-hooks
1352 React.useLayoutEffect(() => {
1353 let getKeyWithoutBasename = getKey && basename !== "/" ? (location, matches) => getKey(
1354 // Strip the basename to match useLocation()
1355 {
1356 ...location,
1357 pathname: stripBasename(location.pathname, basename) || location.pathname
1358 }, matches) : getKey;
1359 let disableScrollRestoration = router?.enableScrollRestoration(savedScrollPositions, () => window.scrollY, getKeyWithoutBasename);
1360 return () => disableScrollRestoration && disableScrollRestoration();
1361 }, [router, basename, getKey]);
1362
1363 // Restore scrolling when state.restoreScrollPosition changes
1364 // eslint-disable-next-line react-hooks/rules-of-hooks
1365 React.useLayoutEffect(() => {
1366 // Explicit false means don't do anything (used for submissions)
1367 if (restoreScrollPosition === false) {
1368 return;
1369 }
1370
1371 // been here before, scroll to it
1372 if (typeof restoreScrollPosition === "number") {
1373 window.scrollTo(0, restoreScrollPosition);
1374 return;
1375 }
1376
1377 // try to scroll to the hash
1378 if (location.hash) {
1379 let el = document.getElementById(decodeURIComponent(location.hash.slice(1)));
1380 if (el) {
1381 el.scrollIntoView();
1382 return;
1383 }
1384 }
1385
1386 // Don't reset if this navigation opted out
1387 if (preventScrollReset === true) {
1388 return;
1389 }
1390
1391 // otherwise go to the top on new locations
1392 window.scrollTo(0, 0);
1393 }, [location, restoreScrollPosition, preventScrollReset]);
1394 }
1395}
1396
1397/**
1398 * Setup a callback to be fired on the window's `beforeunload` event. This is
1399 * useful for saving some data to `window.localStorage` just before the page
1400 * refreshes.
1401 *
1402 * Note: The `callback` argument should be a function created with
1403 * `React.useCallback()`.
1404 */
1405function useBeforeUnload(callback, options) {
1406 let {
1407 capture
1408 } = options || {};
1409 React.useEffect(() => {
1410 let opts = capture != null ? {
1411 capture
1412 } : undefined;
1413 window.addEventListener("beforeunload", callback, opts);
1414 return () => {
1415 window.removeEventListener("beforeunload", callback, opts);
1416 };
1417 }, [callback, capture]);
1418}
1419
1420/**
1421 * Setup a callback to be fired on the window's `pagehide` event. This is
1422 * useful for saving some data to `window.localStorage` just before the page
1423 * refreshes. This event is better supported than beforeunload across browsers.
1424 *
1425 * Note: The `callback` argument should be a function created with
1426 * `React.useCallback()`.
1427 */
1428function usePageHide(callback, options) {
1429 let {
1430 capture
1431 } = options || {};
1432 React.useEffect(() => {
1433 let opts = capture != null ? {
1434 capture
1435 } : undefined;
1436 window.addEventListener("pagehide", callback, opts);
1437 return () => {
1438 window.removeEventListener("pagehide", callback, opts);
1439 };
1440 }, [callback, capture]);
1441}
1442
1443/**
1444 * Wrapper around useBlocker to show a window.confirm prompt to users instead
1445 * of building a custom UI with useBlocker.
1446 *
1447 * Warning: This has *a lot of rough edges* and behaves very differently (and
1448 * very incorrectly in some cases) across browsers if user click addition
1449 * back/forward navigations while the confirm is open. Use at your own risk.
1450 */
1451function usePrompt({
1452 when,
1453 message
1454}) {
1455 let blocker = useBlocker(when);
1456 React.useEffect(() => {
1457 if (blocker.state === "blocked") {
1458 let proceed = window.confirm(message);
1459 if (proceed) {
1460 // This timeout is needed to avoid a weird "race" on POP navigations
1461 // between the `window.history` revert navigation and the result of
1462 // `window.confirm`
1463 setTimeout(blocker.proceed, 0);
1464 } else {
1465 blocker.reset();
1466 }
1467 }
1468 }, [blocker, message]);
1469 React.useEffect(() => {
1470 if (blocker.state === "blocked" && !when) {
1471 blocker.reset();
1472 }
1473 }, [blocker, when]);
1474}
1475
1476/**
1477 * Return a boolean indicating if there is an active view transition to the
1478 * given href. You can use this value to render CSS classes or viewTransitionName
1479 * styles onto your elements
1480 *
1481 * @param href The destination href
1482 * @param [opts.relative] Relative routing type ("route" | "path")
1483 */
1484function useViewTransitionState(to, opts = {}) {
1485 let vtContext = React.useContext(ViewTransitionContext);
1486 !(vtContext != null) ? UNSAFE_invariant(false, "`unstable_useViewTransitionState` must be used within `react-router-dom`'s `RouterProvider`. " + "Did you accidentally import `RouterProvider` from `react-router`?") : void 0;
1487 let {
1488 basename
1489 } = useDataRouterContext(DataRouterHook.useViewTransitionState);
1490 let path = useResolvedPath(to, {
1491 relative: opts.relative
1492 });
1493 if (!vtContext.isTransitioning) {
1494 return false;
1495 }
1496 let currentPath = stripBasename(vtContext.currentLocation.pathname, basename) || vtContext.currentLocation.pathname;
1497 let nextPath = stripBasename(vtContext.nextLocation.pathname, basename) || vtContext.nextLocation.pathname;
1498
1499 // Transition is active if we're going to or coming from the indicated
1500 // destination. This ensures that other PUSH navigations that reverse
1501 // an indicated transition apply. I.e., on the list view you have:
1502 //
1503 // <NavLink to="/details/1" unstable_viewTransition>
1504 //
1505 // If you click the breadcrumb back to the list view:
1506 //
1507 // <NavLink to="/list" unstable_viewTransition>
1508 //
1509 // We should apply the transition because it's indicated as active going
1510 // from /list -> /details/1 and therefore should be active on the reverse
1511 // (even though this isn't strictly a POP reverse)
1512 return matchPath(path.pathname, nextPath) != null || matchPath(path.pathname, currentPath) != null;
1513}
1514
1515//#endregion
1516
1517export { 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, useViewTransitionState as unstable_useViewTransitionState, useBeforeUnload, useFetcher, useFetchers, useFormAction, useLinkClickHandler, useSearchParams, useSubmit };
1518//# sourceMappingURL=react-router-dom.development.js.map
Note: See TracBrowser for help on using the repository browser.