source: node_modules/react-router-dom/dist/index.js@ d24f17c

main
Last change on this file since d24f17c was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

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