UNPKG

49.2 kBJavaScriptView Raw
1/**
2 * react-router v8.3.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 { ABSOLUTE_URL_REGEX } from "../router/url.js";
12import { createBrowserHistory, createHashHistory, createPath, invariant, warning } from "../router/history.js";
13import { ErrorResponseImpl, SUPPORTED_ERROR_TYPES, defaultMapRouteProperties, joinPaths, matchPath, parseToInfo, resolveTo, stripBasename } from "../router/utils.js";
14import { IDLE_FETCHER, createRouter } from "../router/router.js";
15import { DataRouterContext, DataRouterStateContext, FetchersContext, NavigationContext, RouteContext, ViewTransitionContext } from "../context.js";
16import { useBlocker, useHref, useLocation, useMatches, useNavigate, useNavigation, useResolvedPath, useRouteId } from "../hooks.js";
17import { Router, hydrationRouteProperties } from "../components.js";
18import { createSearchParams, getFormSubmissionInfo, getSearchParamsForLocation, shouldProcessLinkClick } from "./dom.js";
19import { escapeHtml } from "./ssr/markup.js";
20import { FrameworkContext, PrefetchPageLinks, mergeRefs, usePrefetchBehavior } from "./ssr/components.js";
21import * as React$1 from "react";
22//#region lib/dom/lib.tsx
23const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
24try {
25 if (isBrowser) window.__reactRouterVersion = "8.3.0";
26} catch (e) {}
27/**
28* Create a new {@link DataRouter| data router} that manages the application
29* path via [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState)
30* and [`history.replaceState`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState).
31*
32* Data Routers should not be held in React state. You should create your router
33* once outside of the React tree and pass it to {@link RouterProvider | `<RouterProvider>`}.
34* You can use `patchRoutesOnNavigation` to add additional routes programmatically.
35*
36* @public
37* @category Data Routers
38* @mode data
39* @param routes Application routes
40* @param opts Options
41* @param {DOMRouterOpts.basename} opts.basename n/a
42* @param {DOMRouterOpts.dataStrategy} opts.dataStrategy n/a
43* @param {DOMRouterOpts.future} opts.future n/a
44* @param {DOMRouterOpts.getContext} opts.getContext n/a
45* @param {DOMRouterOpts.hydrationData} opts.hydrationData n/a
46* @param {DOMRouterOpts.instrumentations} opts.instrumentations n/a
47* @param {DOMRouterOpts.patchRoutesOnNavigation} opts.patchRoutesOnNavigation n/a
48* @param {DOMRouterOpts.window} opts.window n/a
49* @returns An initialized {@link DataRouter| data router} to pass to {@link RouterProvider | `<RouterProvider>`}
50*/
51function createBrowserRouter(routes, opts) {
52 return createRouter({
53 basename: opts?.basename,
54 getContext: opts?.getContext,
55 future: opts?.future,
56 history: createBrowserHistory({ window: opts?.window }),
57 hydrationData: opts?.hydrationData || parseHydrationData(),
58 routes,
59 mapRouteProperties: defaultMapRouteProperties,
60 hydrationRouteProperties,
61 dataStrategy: opts?.dataStrategy,
62 patchRoutesOnNavigation: opts?.patchRoutesOnNavigation,
63 window: opts?.window,
64 instrumentations: opts?.instrumentations
65 }).initialize();
66}
67/**
68* Create a new {@link DataRouter| data router} that manages the application
69* path via the URL [`hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash).
70*
71* Data Routers should not be held in React state. You should create your router
72* once outside of the React tree and pass it to {@link RouterProvider | `<RouterProvider>`}.
73* You can use `patchRoutesOnNavigation` to add additional routes programmatically.
74*
75* @public
76* @category Data Routers
77* @mode data
78* @param routes Application routes
79* @param opts Options
80* @param {DOMRouterOpts.basename} opts.basename n/a
81* @param {DOMRouterOpts.future} opts.future n/a
82* @param {DOMRouterOpts.getContext} opts.getContext n/a
83* @param {DOMRouterOpts.hydrationData} opts.hydrationData n/a
84* @param {DOMRouterOpts.instrumentations} opts.instrumentations n/a
85* @param {DOMRouterOpts.dataStrategy} opts.dataStrategy n/a
86* @param {DOMRouterOpts.patchRoutesOnNavigation} opts.patchRoutesOnNavigation n/a
87* @param {DOMRouterOpts.window} opts.window n/a
88* @returns An initialized {@link DataRouter| data router} to pass to {@link RouterProvider | `<RouterProvider>`}
89*/
90function createHashRouter(routes, opts) {
91 return createRouter({
92 basename: opts?.basename,
93 getContext: opts?.getContext,
94 future: opts?.future,
95 history: createHashHistory({ window: opts?.window }),
96 hydrationData: opts?.hydrationData || parseHydrationData(),
97 routes,
98 mapRouteProperties: defaultMapRouteProperties,
99 hydrationRouteProperties,
100 dataStrategy: opts?.dataStrategy,
101 patchRoutesOnNavigation: opts?.patchRoutesOnNavigation,
102 window: opts?.window,
103 instrumentations: opts?.instrumentations
104 }).initialize();
105}
106function parseHydrationData() {
107 let state = window?.__staticRouterHydrationData;
108 if (state && state.errors) state = {
109 ...state,
110 errors: deserializeErrors(state.errors)
111 };
112 return state;
113}
114function deserializeErrors(errors) {
115 if (!errors) return null;
116 let entries = Object.entries(errors);
117 let serialized = {};
118 for (let [key, val] of entries) if (val && val.__type === "RouteErrorResponse") serialized[key] = new ErrorResponseImpl(val.status, val.statusText, val.data, val.internal === true);
119 else if (val && val.__type === "Error") {
120 if (typeof val.__subType === "string" && SUPPORTED_ERROR_TYPES.includes(val.__subType)) {
121 let ErrorConstructor = window[val.__subType];
122 if (typeof ErrorConstructor === "function") try {
123 let error = new ErrorConstructor(val.message);
124 error.stack = "";
125 serialized[key] = error;
126 } catch (e) {}
127 }
128 if (serialized[key] == null) {
129 let error = new Error(val.message);
130 error.stack = "";
131 serialized[key] = error;
132 }
133 } else serialized[key] = val;
134 return serialized;
135}
136/**
137* A declarative {@link Router | `<Router>`} using the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
138* API for client-side routing.
139*
140* @public
141* @category Declarative Routers
142* @mode declarative
143* @param props Props
144* @param {BrowserRouterProps.basename} props.basename n/a
145* @param {BrowserRouterProps.children} props.children n/a
146* @param {BrowserRouterProps.useTransitions} props.useTransitions n/a
147* @param {BrowserRouterProps.window} props.window n/a
148* @returns A declarative {@link Router | `<Router>`} using the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
149* API for client-side routing.
150*/
151function BrowserRouter({ basename, children, useTransitions, window }) {
152 let historyRef = React$1.useRef(null);
153 if (historyRef.current == null) historyRef.current = createBrowserHistory({
154 window,
155 v5Compat: true
156 });
157 let history = historyRef.current;
158 let [state, setStateImpl] = React$1.useState({
159 action: history.action,
160 location: history.location
161 });
162 let setState = React$1.useCallback((newState) => {
163 if (useTransitions === false) setStateImpl(newState);
164 else React$1.startTransition(() => setStateImpl(newState));
165 }, [useTransitions]);
166 React$1.useLayoutEffect(() => history.listen(setState), [history, setState]);
167 return /* @__PURE__ */ React$1.createElement(Router, {
168 basename,
169 children,
170 location: state.location,
171 navigationType: state.action,
172 navigator: history,
173 useTransitions
174 });
175}
176/**
177* A declarative {@link Router | `<Router>`} that stores the location in the
178* [`hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash) portion
179* of the URL so it is not sent to the server.
180*
181* @public
182* @category Declarative Routers
183* @mode declarative
184* @param props Props
185* @param {HashRouterProps.basename} props.basename n/a
186* @param {HashRouterProps.children} props.children n/a
187* @param {HashRouterProps.useTransitions} props.useTransitions n/a
188* @param {HashRouterProps.window} props.window n/a
189* @returns A declarative {@link Router | `<Router>`} using the URL [`hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash)
190* for client-side routing.
191*/
192function HashRouter({ basename, children, useTransitions, window }) {
193 let historyRef = React$1.useRef(null);
194 if (historyRef.current == null) historyRef.current = createHashHistory({
195 window,
196 v5Compat: true
197 });
198 let history = historyRef.current;
199 let [state, setStateImpl] = React$1.useState({
200 action: history.action,
201 location: history.location
202 });
203 let setState = React$1.useCallback((newState) => {
204 if (useTransitions === false) setStateImpl(newState);
205 else React$1.startTransition(() => setStateImpl(newState));
206 }, [useTransitions]);
207 React$1.useLayoutEffect(() => history.listen(setState), [history, setState]);
208 return /* @__PURE__ */ React$1.createElement(Router, {
209 basename,
210 children,
211 location: state.location,
212 navigationType: state.action,
213 navigator: history,
214 useTransitions
215 });
216}
217/**
218* A declarative {@link Router | `<Router>`} that accepts a pre-instantiated
219* `history` object.
220* It's important to note that using your own `history` object is highly discouraged
221* and may add two versions of the `history` library to your bundles unless you use
222* the same version of the `history` library that React Router uses internally.
223*
224* @name unstable_HistoryRouter
225* @public
226* @category Declarative Routers
227* @mode declarative
228* @param props Props
229* @param {HistoryRouterProps.basename} props.basename n/a
230* @param {HistoryRouterProps.children} props.children n/a
231* @param {HistoryRouterProps.history} props.history n/a
232* @param {HistoryRouterProps.useTransitions} props.useTransitions n/a
233* @returns A declarative {@link Router | `<Router>`} using the provided history
234* implementation for client-side routing.
235*/
236function HistoryRouter({ basename, children, history, useTransitions }) {
237 let [state, setStateImpl] = React$1.useState({
238 action: history.action,
239 location: history.location
240 });
241 let setState = React$1.useCallback((newState) => {
242 if (useTransitions === false) setStateImpl(newState);
243 else React$1.startTransition(() => setStateImpl(newState));
244 }, [useTransitions]);
245 React$1.useLayoutEffect(() => history.listen(setState), [history, setState]);
246 return /* @__PURE__ */ React$1.createElement(Router, {
247 basename,
248 children,
249 location: state.location,
250 navigationType: state.action,
251 navigator: history,
252 useTransitions
253 });
254}
255HistoryRouter.displayName = "unstable_HistoryRouter";
256/**
257* A progressively enhanced [`<a href>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
258* wrapper to enable navigation with client-side routing.
259*
260* @example
261* import { Link } from "react-router";
262*
263* <Link to="/dashboard">Dashboard</Link>;
264*
265* <Link
266* to={{
267* pathname: "/some/path",
268* search: "?query=string",
269* hash: "#hash",
270* }}
271* />;
272*
273* @public
274* @category Components
275* @param {LinkProps.discover} props.discover [modes: framework] n/a
276* @param {LinkProps.prefetch} props.prefetch [modes: framework] n/a
277* @param {LinkProps.preventScrollReset} props.preventScrollReset [modes: framework, data] n/a
278* @param {LinkProps.relative} props.relative n/a
279* @param {LinkProps.reloadDocument} props.reloadDocument n/a
280* @param {LinkProps.replace} props.replace n/a
281* @param {LinkProps.state} props.state n/a
282* @param {LinkProps.to} props.to n/a
283* @param {LinkProps.viewTransition} props.viewTransition [modes: framework, data] n/a
284* @param {LinkProps.defaultShouldRevalidate} props.defaultShouldRevalidate n/a
285* @param {LinkProps.mask} props.mask [modes: framework, data] n/a
286*/
287const Link = React$1.forwardRef(function LinkWithRef({ onClick, discover = "render", prefetch = "none", relative, reloadDocument, replace, mask, state, target, to, preventScrollReset, viewTransition, defaultShouldRevalidate, ...rest }, forwardedRef) {
288 let { basename, navigator, useTransitions } = React$1.useContext(NavigationContext);
289 let isAbsolute = typeof to === "string" && ABSOLUTE_URL_REGEX.test(to);
290 let parsed = parseToInfo(to, basename);
291 to = parsed.to;
292 let href = useHref(to, { relative });
293 let location = useLocation();
294 let maskedHref = null;
295 if (mask) {
296 let resolved = resolveTo(mask, [], location.mask ? location.mask.pathname : "/", true);
297 if (basename !== "/") resolved.pathname = resolved.pathname === "/" ? basename : joinPaths([basename, resolved.pathname]);
298 maskedHref = navigator.createHref(resolved);
299 }
300 let [shouldPrefetch, prefetchRef, prefetchHandlers] = usePrefetchBehavior(prefetch, rest);
301 let internalOnClick = useLinkClickHandler(to, {
302 replace,
303 mask,
304 state,
305 target,
306 preventScrollReset,
307 relative,
308 viewTransition,
309 defaultShouldRevalidate,
310 useTransitions
311 });
312 function handleClick(event) {
313 if (onClick) onClick(event);
314 if (!event.defaultPrevented) internalOnClick(event);
315 }
316 let isSpaLink = !(parsed.isExternal || reloadDocument);
317 let link = /* @__PURE__ */ React$1.createElement("a", {
318 ...rest,
319 ...prefetchHandlers,
320 href: (isSpaLink ? maskedHref : void 0) || parsed.absoluteURL || href,
321 onClick: isSpaLink ? handleClick : onClick,
322 ref: mergeRefs(forwardedRef, prefetchRef),
323 target,
324 "data-discover": !isAbsolute && discover === "render" ? "true" : void 0
325 });
326 return shouldPrefetch && !isAbsolute ? /* @__PURE__ */ React$1.createElement(React$1.Fragment, null, link, /* @__PURE__ */ React$1.createElement(PrefetchPageLinks, { page: href })) : link;
327});
328Link.displayName = "Link";
329/**
330* Wraps {@link Link | `<Link>`} with additional props for styling active and
331* pending states.
332*
333* - Automatically applies classes to the link based on its `active` and `pending`
334* states, see {@link NavLinkProps.className}
335* - Note that `pending` is only available with Framework and Data modes.
336* - Automatically applies `aria-current="page"` to the link when the link is active.
337* See [`aria-current`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current)
338* on MDN.
339* - States are additionally available through the className, style, and children
340* render props. See {@link NavLinkRenderProps}.
341*
342* @example
343* <NavLink to="/message">Messages</NavLink>
344*
345* // Using render props
346* <NavLink
347* to="/messages"
348* className={({ isActive, isPending }) =>
349* isPending ? "pending" : isActive ? "active" : ""
350* }
351* >
352* Messages
353* </NavLink>
354*
355* @public
356* @category Components
357* @param {NavLinkProps.caseSensitive} props.caseSensitive n/a
358* @param {NavLinkProps.children} props.children n/a
359* @param {NavLinkProps.className} props.className n/a
360* @param {NavLinkProps.discover} props.discover [modes: framework] n/a
361* @param {NavLinkProps.end} props.end n/a
362* @param {NavLinkProps.prefetch} props.prefetch [modes: framework] n/a
363* @param {NavLinkProps.preventScrollReset} props.preventScrollReset [modes: framework, data] n/a
364* @param {NavLinkProps.relative} props.relative n/a
365* @param {NavLinkProps.reloadDocument} props.reloadDocument n/a
366* @param {NavLinkProps.replace} props.replace n/a
367* @param {NavLinkProps.state} props.state n/a
368* @param {NavLinkProps.style} props.style n/a
369* @param {NavLinkProps.to} props.to n/a
370* @param {NavLinkProps.viewTransition} props.viewTransition [modes: framework, data] n/a
371*/
372const NavLink = React$1.forwardRef(function NavLinkWithRef({ "aria-current": ariaCurrentProp = "page", caseSensitive = false, className: classNameProp = "", end = false, style: styleProp, to, viewTransition, children, ...rest }, ref) {
373 let path = useResolvedPath(to, { relative: rest.relative });
374 let location = useLocation();
375 let routerState = React$1.useContext(DataRouterStateContext);
376 let { navigator, basename } = React$1.useContext(NavigationContext);
377 let isTransitioning = routerState != null && useViewTransitionState(path) && viewTransition === true;
378 let toPathname = navigator.encodeLocation ? navigator.encodeLocation(path).pathname : path.pathname;
379 let locationPathname = location.pathname;
380 let nextLocationPathname = routerState && routerState.navigation && routerState.navigation.location ? routerState.navigation.location.pathname : null;
381 if (!caseSensitive) {
382 locationPathname = locationPathname.toLowerCase();
383 nextLocationPathname = nextLocationPathname ? nextLocationPathname.toLowerCase() : null;
384 toPathname = toPathname.toLowerCase();
385 }
386 if (nextLocationPathname && basename) nextLocationPathname = stripBasename(nextLocationPathname, basename) || nextLocationPathname;
387 const endSlashPosition = toPathname !== "/" && toPathname.endsWith("/") ? toPathname.length - 1 : toPathname.length;
388 let isActive = locationPathname === toPathname || !end && locationPathname.startsWith(toPathname) && locationPathname.charAt(endSlashPosition) === "/";
389 let isPending = nextLocationPathname != null && (nextLocationPathname === toPathname || !end && nextLocationPathname.startsWith(toPathname) && nextLocationPathname.charAt(endSlashPosition) === "/");
390 let renderProps = {
391 isActive,
392 isPending,
393 isTransitioning
394 };
395 let ariaCurrent = isActive ? ariaCurrentProp : void 0;
396 let className;
397 if (typeof classNameProp === "function") className = classNameProp(renderProps);
398 else className = [
399 classNameProp,
400 isActive ? "active" : null,
401 isPending ? "pending" : null,
402 isTransitioning ? "transitioning" : null
403 ].filter(Boolean).join(" ");
404 let style = typeof styleProp === "function" ? styleProp(renderProps) : styleProp;
405 return /* @__PURE__ */ React$1.createElement(Link, {
406 ...rest,
407 "aria-current": ariaCurrent,
408 className,
409 ref,
410 style,
411 to,
412 viewTransition
413 }, typeof children === "function" ? children(renderProps) : children);
414});
415NavLink.displayName = "NavLink";
416/**
417* A progressively enhanced HTML [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
418* that submits data to actions via [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch),
419* activating pending states in {@link useNavigation} which enables advanced
420* user interfaces beyond a basic HTML [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form).
421* After a form's `action` completes, all data on the page is automatically
422* revalidated to keep the UI in sync with the data.
423*
424* Because it uses the HTML form API, server rendered pages are interactive at a
425* basic level before JavaScript loads. Instead of React Router managing the
426* submission, the browser manages the submission as well as the pending states
427* (like the spinning favicon). After JavaScript loads, React Router takes over
428* enabling web application user experiences.
429*
430* `Form` is most useful for submissions that should also change the URL or
431* otherwise add an entry to the browser history stack. For forms that shouldn't
432* manipulate the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
433* stack, use {@link FetcherWithComponents.Form | `<fetcher.Form>`}.
434*
435* @example
436* import { Form } from "react-router";
437*
438* function NewEvent() {
439* return (
440* <Form action="/events" method="post">
441* <input name="title" type="text" />
442* <input name="description" type="text" />
443* </Form>
444* );
445* }
446*
447* @public
448* @category Components
449* @mode framework
450* @mode data
451* @param {FormProps.action} action n/a
452* @param {FormProps.discover} discover n/a
453* @param {FormProps.encType} encType n/a
454* @param {FormProps.fetcherKey} fetcherKey n/a
455* @param {FormProps.method} method n/a
456* @param {FormProps.navigate} navigate n/a
457* @param {FormProps.preventScrollReset} preventScrollReset n/a
458* @param {FormProps.relative} relative n/a
459* @param {FormProps.reloadDocument} reloadDocument n/a
460* @param {FormProps.replace} replace n/a
461* @param {FormProps.state} state n/a
462* @param {FormProps.viewTransition} viewTransition n/a
463* @param {FormProps.defaultShouldRevalidate} defaultShouldRevalidate n/a
464* @returns A progressively enhanced [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) component
465*/
466const Form = React$1.forwardRef(({ discover = "render", fetcherKey, navigate, reloadDocument, replace, state, method = "get", action, onSubmit, relative, preventScrollReset, viewTransition, defaultShouldRevalidate, ...props }, forwardedRef) => {
467 let { useTransitions } = React$1.useContext(NavigationContext);
468 let submit = useSubmit();
469 let formAction = useFormAction(action, { relative });
470 let formMethod = method.toLowerCase() === "get" ? "get" : "post";
471 let isAbsolute = typeof action === "string" && ABSOLUTE_URL_REGEX.test(action);
472 let submitHandler = (event) => {
473 onSubmit && onSubmit(event);
474 if (event.defaultPrevented) return;
475 event.preventDefault();
476 let submitter = event.nativeEvent.submitter;
477 let submitMethod = submitter?.getAttribute("formmethod") || method;
478 let doSubmit = () => submit(submitter || event.currentTarget, {
479 fetcherKey,
480 method: submitMethod,
481 navigate,
482 replace,
483 state,
484 relative,
485 preventScrollReset,
486 viewTransition,
487 defaultShouldRevalidate
488 });
489 if (useTransitions && navigate !== false) React$1.startTransition(() => doSubmit());
490 else doSubmit();
491 };
492 return /* @__PURE__ */ React$1.createElement("form", {
493 ref: forwardedRef,
494 method: formMethod,
495 action: formAction,
496 onSubmit: reloadDocument ? onSubmit : submitHandler,
497 ...props,
498 "data-discover": !isAbsolute && discover === "render" ? "true" : void 0
499 });
500});
501Form.displayName = "Form";
502/**
503* Emulates the browser's scroll restoration on location changes. Apps should only render one of these, right before the {@link Scripts} component.
504*
505* ```tsx
506* import { ScrollRestoration } from "react-router";
507*
508* export default function Root() {
509* return (
510* <html>
511* <body>
512* <ScrollRestoration />
513* <Scripts />
514* </body>
515* </html>
516* );
517* }
518* ```
519*
520* This component renders an inline `<script>` to prevent scroll flashing. The
521* `nonce` prop will be passed down to the script tag to allow CSP nonce usage.
522* If not provided in Framework Mode, it will default to any
523* {@link ServerRouter | `<ServerRouter nonce>`} prop.
524*
525* ```tsx
526* <ScrollRestoration nonce={cspNonce} />
527* ```
528*
529* @public
530* @category Components
531* @mode framework
532* @mode data
533* @param props Props
534* @param {ScrollRestorationProps.getKey} props.getKey n/a
535* @param {ScriptsProps.nonce} props.nonce n/a
536* @param {ScrollRestorationProps.storageKey} props.storageKey n/a
537* @returns A [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
538* tag that restores scroll positions on navigation.
539*/
540function ScrollRestoration({ getKey, storageKey, ...props }) {
541 let remixContext = React$1.useContext(FrameworkContext);
542 let { basename } = React$1.useContext(NavigationContext);
543 let location = useLocation();
544 let matches = useMatches();
545 useScrollRestoration({
546 getKey,
547 storageKey
548 });
549 let ssrKey = React$1.useMemo(() => {
550 if (!remixContext || !getKey) return null;
551 let userKey = getScrollRestorationKey(location, matches, basename, getKey);
552 return userKey !== location.key ? userKey : null;
553 }, []);
554 if (!remixContext || remixContext.isSpaMode) return null;
555 let restoreScroll = ((storageKey, restoreKey) => {
556 if (!window.history.state || !window.history.state.key) {
557 let key = Math.random().toString(32).slice(2);
558 window.history.replaceState({ key }, "");
559 }
560 try {
561 let storedY = JSON.parse(sessionStorage.getItem(storageKey) || "{}")[restoreKey || window.history.state.key];
562 if (typeof storedY === "number") window.scrollTo(0, storedY);
563 } catch (error) {
564 console.error(error);
565 sessionStorage.removeItem(storageKey);
566 }
567 }).toString();
568 if (props.nonce == null && remixContext?.nonce) props.nonce = remixContext.nonce;
569 return /* @__PURE__ */ React$1.createElement("script", {
570 ...props,
571 suppressHydrationWarning: true,
572 dangerouslySetInnerHTML: { __html: `(${restoreScroll})(${escapeHtml(JSON.stringify(storageKey || SCROLL_RESTORATION_STORAGE_KEY))}, ${escapeHtml(JSON.stringify(ssrKey))})` }
573 });
574}
575ScrollRestoration.displayName = "ScrollRestoration";
576function getDataRouterConsoleError(hookName) {
577 return `${hookName} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`;
578}
579function useDataRouterContext(hookName) {
580 let ctx = React$1.useContext(DataRouterContext);
581 invariant(ctx, getDataRouterConsoleError(hookName));
582 return ctx;
583}
584function useDataRouterState(hookName) {
585 let state = React$1.useContext(DataRouterStateContext);
586 invariant(state, getDataRouterConsoleError(hookName));
587 return state;
588}
589/**
590* Handles the click behavior for router {@link Link | `<Link>`} components.This
591* is useful if you need to create custom {@link Link | `<Link>`} components with
592* the same click behavior we use in our exported {@link Link | `<Link>`}.
593*
594* @public
595* @category Hooks
596* @param to The URL to navigate to, can be a string or a partial {@link Path}.
597* @param options Options
598* @param options.preventScrollReset Whether to prevent the scroll position from
599* being reset to the top of the viewport on completion of the navigation when
600* using the {@link ScrollRestoration} component. Defaults to `false`.
601* @param options.relative The {@link RelativeRoutingType | relative routing type}
602* to use for the link. Defaults to `"route"`.
603* @param options.replace Whether to replace the current [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
604* entry instead of pushing a new one. Defaults to `false`.
605* @param options.state The state to add to the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
606* entry for this navigation. Defaults to `undefined`.
607* @param options.target The target attribute for the link. Defaults to `undefined`.
608* @param options.viewTransition Enables a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
609* for this navigation. To apply specific styles during the transition, see
610* {@link useViewTransitionState}. Defaults to `false`.
611* @param options.defaultShouldRevalidate Specify the default revalidation
612* behavior for the navigation. When not specified, loaders revalidate
613* according to the router's standard revalidation behavior.
614* @param options.mask Masked location to display in the browser instead
615* of the router location. Defaults to `undefined`.
616* @param options.useTransitions Wraps the navigation in
617* [`React.startTransition`](https://react.dev/reference/react/startTransition)
618* for concurrent rendering. Defaults to `false`.
619* @returns A click handler function that can be used in a custom {@link Link} component.
620*/
621function useLinkClickHandler(to, { target, replace: replaceProp, mask, state, preventScrollReset, relative, viewTransition, defaultShouldRevalidate, useTransitions } = {}) {
622 let navigate = useNavigate();
623 let location = useLocation();
624 let path = useResolvedPath(to, { relative });
625 return React$1.useCallback((event) => {
626 if (shouldProcessLinkClick(event, target)) {
627 event.preventDefault();
628 let replace = replaceProp !== void 0 ? replaceProp : createPath(location) === createPath(path);
629 let doNavigate = () => navigate(to, {
630 replace,
631 mask,
632 state,
633 preventScrollReset,
634 relative,
635 viewTransition,
636 defaultShouldRevalidate
637 });
638 if (useTransitions) React$1.startTransition(() => doNavigate());
639 else doNavigate();
640 }
641 }, [
642 location,
643 navigate,
644 path,
645 replaceProp,
646 mask,
647 state,
648 target,
649 to,
650 preventScrollReset,
651 relative,
652 viewTransition,
653 defaultShouldRevalidate,
654 useTransitions
655 ]);
656}
657/**
658* Returns a tuple of the current URL's [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
659* and a function to update them. Setting the search params causes a navigation.
660*
661* ```tsx
662* import { useSearchParams } from "react-router";
663*
664* export function SomeComponent() {
665* const [searchParams, setSearchParams] = useSearchParams();
666* // ...
667* }
668* ```
669*
670* ### `setSearchParams` function
671*
672* The second element of the tuple is a function that can be used to update the
673* search params. It accepts the same types as `defaultInit` and will cause a
674* navigation to the new URL.
675*
676* ```tsx
677* let [searchParams, setSearchParams] = useSearchParams();
678*
679* // a search param string
680* setSearchParams("?tab=1");
681*
682* // a shorthand object
683* setSearchParams({ tab: "1" });
684*
685* // object keys can be arrays for multiple values on the key
686* setSearchParams({ brand: ["nike", "reebok"] });
687*
688* // an array of tuples
689* setSearchParams([["tab", "1"]]);
690*
691* // a `URLSearchParams` object
692* setSearchParams(new URLSearchParams("?tab=1"));
693* ```
694*
695* It also supports a function callback like React's
696* [`setState`](https://react.dev/reference/react/useState#setstate):
697*
698* ```tsx
699* setSearchParams((searchParams) => {
700* searchParams.set("tab", "2");
701* return searchParams;
702* });
703* ```
704*
705* <docs-warning>The function callback version of `setSearchParams` does not support
706* the [queueing](https://react.dev/reference/react/useState#setstate-parameters)
707* logic that React's `setState` implements. Multiple calls to `setSearchParams`
708* in the same tick will not build on the prior value. If you need this behavior,
709* you can use `setState` manually.</docs-warning>
710*
711* ### Notes
712*
713* Note that `searchParams` is a stable reference, so you can reliably use it
714* as a dependency in React's [`useEffect`](https://react.dev/reference/react/useEffect)
715* hooks.
716*
717* ```tsx
718* useEffect(() => {
719* console.log(searchParams.get("tab"));
720* }, [searchParams]);
721* ```
722*
723* However, this also means it's mutable. If you change the object without
724* calling `setSearchParams`, its values will change between renders if some
725* other state causes the component to re-render and URL will not reflect the
726* values.
727*
728* @public
729* @category Hooks
730* @param defaultInit
731* You can initialize the search params with a default value, though it **will
732* not** change the URL on the first render.
733*
734* ```tsx
735* // a search param string
736* useSearchParams("?tab=1");
737*
738* // a shorthand object
739* useSearchParams({ tab: "1" });
740*
741* // object keys can be arrays for multiple values on the key
742* useSearchParams({ brand: ["nike", "reebok"] });
743*
744* // an array of tuples
745* useSearchParams([["tab", "1"]]);
746*
747* // a `URLSearchParams` object
748* useSearchParams(new URLSearchParams("?tab=1"));
749* ```
750* @returns A tuple of the current [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
751* and a function to update them.
752*/
753function useSearchParams(defaultInit) {
754 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.");
755 let defaultSearchParamsRef = React$1.useRef(createSearchParams(defaultInit));
756 let hasSetSearchParamsRef = React$1.useRef(false);
757 let location = useLocation();
758 let searchParams = React$1.useMemo(() => getSearchParamsForLocation(location.search, hasSetSearchParamsRef.current ? null : defaultSearchParamsRef.current), [location.search]);
759 let navigate = useNavigate();
760 return [searchParams, React$1.useCallback((nextInit, navigateOptions) => {
761 const newSearchParams = createSearchParams(typeof nextInit === "function" ? nextInit(new URLSearchParams(searchParams)) : nextInit);
762 hasSetSearchParamsRef.current = true;
763 navigate("?" + newSearchParams, navigateOptions);
764 }, [navigate, searchParams])];
765}
766let fetcherId = 0;
767let getUniqueFetcherId = () => `__${String(++fetcherId)}__`;
768/**
769* The imperative version of {@link Form | `<Form>`} that lets you submit a form
770* from code instead of a user interaction.
771*
772* @example
773* import { useSubmit } from "react-router";
774*
775* function SomeComponent() {
776* const submit = useSubmit();
777* return (
778* <Form onChange={(event) => submit(event.currentTarget)} />
779* );
780* }
781*
782* @public
783* @category Hooks
784* @mode framework
785* @mode data
786* @returns A function that can be called to submit a {@link Form} imperatively.
787*/
788function useSubmit() {
789 let { router } = useDataRouterContext("useSubmit");
790 let { basename } = React$1.useContext(NavigationContext);
791 let currentRouteId = useRouteId();
792 let routerFetch = router.fetch;
793 let routerNavigate = router.navigate;
794 return React$1.useCallback(async (target, options = {}) => {
795 let { action, method, encType, formData, body } = getFormSubmissionInfo(target, basename);
796 if (options.navigate === false) await routerFetch(options.fetcherKey || getUniqueFetcherId(), currentRouteId, options.action || action, {
797 defaultShouldRevalidate: options.defaultShouldRevalidate,
798 preventScrollReset: options.preventScrollReset,
799 formData,
800 body,
801 formMethod: options.method || method,
802 formEncType: options.encType || encType,
803 flushSync: options.flushSync
804 });
805 else await routerNavigate(options.action || action, {
806 defaultShouldRevalidate: options.defaultShouldRevalidate,
807 preventScrollReset: options.preventScrollReset,
808 formData,
809 body,
810 formMethod: options.method || method,
811 formEncType: options.encType || encType,
812 replace: options.replace,
813 state: options.state,
814 fromRouteId: currentRouteId,
815 flushSync: options.flushSync,
816 viewTransition: options.viewTransition
817 });
818 }, [
819 routerFetch,
820 routerNavigate,
821 basename,
822 currentRouteId
823 ]);
824}
825/**
826* Resolves the URL to the closest route in the component hierarchy instead of
827* the current URL of the app.
828*
829* This is used internally by {@link Form} to resolve the `action` to the closest
830* route, but can be used generically as well.
831*
832* ```ts
833* import { useFormAction } from "react-router";
834*
835* function SomeComponent() {
836* // closest route URL
837* let action = useFormAction();
838*
839* // closest route URL + "destroy"
840* let destroyAction = useFormAction("destroy");
841* }
842* ```
843*
844* <docs-info>This hook adds a `basename` if your app specifies one, so that it
845* can be used with raw `<form>` elements in a progressively enhanced way. If
846* you are using this to provide an `action` to `<Form>` or `fetcher.submit`, you
847* will need to remove the `basename` since both of those will prepend it
848* internally.</docs-info>
849*
850*
851* @public
852* @category Hooks
853* @mode framework
854* @mode data
855* @param action The action to append to the closest route URL. Defaults to the
856* closest route URL.
857* @param options Options
858* @param options.relative The relative routing type to use when resolving the
859* action. Defaults to `"route"`.
860* @returns The resolved action URL.
861*/
862function useFormAction(action, { relative } = {}) {
863 let { basename } = React$1.useContext(NavigationContext);
864 let routeContext = React$1.useContext(RouteContext);
865 invariant(routeContext, "useFormAction must be used inside a RouteContext");
866 let [match] = routeContext.matches.slice(-1);
867 let path = { ...useResolvedPath(action ? action : ".", { relative }) };
868 let location = useLocation();
869 if (action == null) {
870 path.search = location.search;
871 let params = new URLSearchParams(path.search);
872 let indexValues = params.getAll("index");
873 if (indexValues.some((v) => v === "")) {
874 params.delete("index");
875 indexValues.filter((v) => v).forEach((v) => params.append("index", v));
876 let qs = params.toString();
877 path.search = qs ? `?${qs}` : "";
878 }
879 }
880 if ((!action || action === ".") && match.route.index) path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
881 if (basename !== "/") path.pathname = path.pathname === "/" ? basename : joinPaths([basename, path.pathname]);
882 return createPath(path);
883}
884/**
885* Useful for creating complex, dynamic user interfaces that require multiple,
886* concurrent data interactions without causing a navigation.
887*
888* Fetchers track their own, independent state and can be used to load data, submit
889* forms, and generally interact with [`action`](../../start/framework/route-module#action)
890* and [`loader`](../../start/framework/route-module#loader) functions.
891*
892* @example
893* import { useFetcher } from "react-router"
894*
895* function SomeComponent() {
896* let fetcher = useFetcher()
897*
898* // states are available on the fetcher
899* fetcher.state // "idle" | "loading" | "submitting"
900* fetcher.data // the data returned from the action or loader
901*
902* // render a form
903* <fetcher.Form method="post" />
904*
905* // load data
906* fetcher.load("/some/route")
907*
908* // submit data
909* fetcher.submit(someFormRef, { method: "post" })
910* fetcher.submit(someData, {
911* method: "post",
912* encType: "application/json"
913* })
914*
915* // reset fetcher
916* fetcher.reset()
917* }
918*
919* @public
920* @category Hooks
921* @mode framework
922* @mode data
923* @param options Options
924* @param options.key A unique key to identify the fetcher.
925*
926*
927* By default, `useFetcher` generates a unique fetcher scoped to that component.
928* If you want to identify a fetcher with your own key such that you can access
929* it from elsewhere in your app, you can do that with the `key` option:
930*
931* ```tsx
932* function SomeComp() {
933* let fetcher = useFetcher({ key: "my-key" })
934* // ...
935* }
936*
937* // Somewhere else
938* function AnotherComp() {
939* // this will be the same fetcher, sharing the state across the app
940* let fetcher = useFetcher({ key: "my-key" });
941* // ...
942* }
943* ```
944* @returns A {@link FetcherWithComponents} object that contains the fetcher's state, data, and components for submitting forms and loading data.
945*/
946function useFetcher({ key } = {}) {
947 let { router } = useDataRouterContext("useFetcher");
948 let state = useDataRouterState("useFetcher");
949 let fetcherData = React$1.useContext(FetchersContext);
950 let route = React$1.useContext(RouteContext);
951 let routeId = route.matches[route.matches.length - 1]?.route.id;
952 invariant(fetcherData, `useFetcher must be used inside a FetchersContext`);
953 invariant(route, `useFetcher must be used inside a RouteContext`);
954 invariant(routeId != null, `useFetcher can only be used on routes that contain a unique "id"`);
955 let defaultKey = React$1.useId();
956 let [fetcherKey, setFetcherKey] = React$1.useState(key || defaultKey);
957 if (key && key !== fetcherKey) setFetcherKey(key);
958 let { deleteFetcher, getFetcher, resetFetcher, fetch: routerFetch } = router;
959 React$1.useEffect(() => {
960 getFetcher(fetcherKey);
961 return () => deleteFetcher(fetcherKey);
962 }, [
963 deleteFetcher,
964 getFetcher,
965 fetcherKey
966 ]);
967 let load = React$1.useCallback(async (href, opts) => {
968 invariant(routeId, "No routeId available for fetcher.load()");
969 await routerFetch(fetcherKey, routeId, href, opts);
970 }, [
971 fetcherKey,
972 routeId,
973 routerFetch
974 ]);
975 let submitImpl = useSubmit();
976 let submit = React$1.useCallback(async (target, opts) => {
977 await submitImpl(target, {
978 ...opts,
979 navigate: false,
980 fetcherKey
981 });
982 }, [fetcherKey, submitImpl]);
983 let reset = React$1.useCallback((opts) => resetFetcher(fetcherKey, opts), [resetFetcher, fetcherKey]);
984 let FetcherForm = React$1.useMemo(() => {
985 let FetcherForm = React$1.forwardRef((props, ref) => {
986 return /* @__PURE__ */ React$1.createElement(Form, {
987 ...props,
988 navigate: false,
989 fetcherKey,
990 ref
991 });
992 });
993 FetcherForm.displayName = "fetcher.Form";
994 return FetcherForm;
995 }, [fetcherKey]);
996 let fetcher = state.fetchers.get(fetcherKey) || IDLE_FETCHER;
997 let data = fetcherData.get(fetcherKey);
998 return React$1.useMemo(() => ({
999 Form: FetcherForm,
1000 submit,
1001 load,
1002 reset,
1003 ...fetcher,
1004 data
1005 }), [
1006 FetcherForm,
1007 submit,
1008 load,
1009 reset,
1010 fetcher,
1011 data
1012 ]);
1013}
1014/**
1015* Returns an array of all in-flight {@link Fetcher}s. This is useful for components
1016* throughout the app that didn't create the fetchers but want to use their submissions
1017* to participate in optimistic UI.
1018*
1019* @example
1020* import { useFetchers } from "react-router";
1021*
1022* function SomeComponent() {
1023* const fetchers = useFetchers();
1024* fetchers[0].formData; // FormData
1025* fetchers[0].state; // etc.
1026* // ...
1027* }
1028*
1029* @public
1030* @category Hooks
1031* @mode framework
1032* @mode data
1033* @returns An array of all in-flight {@link Fetcher}s, each with a unique `key`
1034* property.
1035*/
1036function useFetchers() {
1037 let state = useDataRouterState("useFetchers");
1038 return React$1.useMemo(() => Array.from(state.fetchers.entries()).map(([key, fetcher]) => ({
1039 ...fetcher,
1040 key
1041 })), [state.fetchers]);
1042}
1043const SCROLL_RESTORATION_STORAGE_KEY = "react-router-scroll-positions";
1044let savedScrollPositions = {};
1045function getScrollRestorationKey(location, matches, basename, getKey) {
1046 let key = null;
1047 if (getKey) if (basename !== "/") key = getKey({
1048 ...location,
1049 pathname: stripBasename(location.pathname, basename) || location.pathname
1050 }, matches);
1051 else key = getKey(location, matches);
1052 if (key == null) key = location.key;
1053 return key;
1054}
1055/**
1056* When rendered inside a {@link RouterProvider}, will restore scroll positions
1057* on navigations
1058*
1059* <!--
1060* Not marked `@public` because we only export as UNSAFE_ and therefore we don't
1061* maintain an .md file for this hook
1062* -->
1063*
1064* @name UNSAFE_useScrollRestoration
1065* @category Hooks
1066* @mode framework
1067* @mode data
1068* @param options Options
1069* @param options.getKey A function that returns a key to use for scroll restoration.
1070* This is useful for custom scroll restoration logic, such as using only the pathname
1071* so that subsequent navigations to prior paths will restore the scroll. Defaults
1072* to `location.key`.
1073* @param options.storageKey The key to use for storing scroll positions in
1074* `sessionStorage`. Defaults to `"react-router-scroll-positions"`.
1075* @returns {void}
1076*/
1077function useScrollRestoration({ getKey, storageKey } = {}) {
1078 let { router } = useDataRouterContext("useScrollRestoration");
1079 let { restoreScrollPosition, preventScrollReset } = useDataRouterState("useScrollRestoration");
1080 let { basename } = React$1.useContext(NavigationContext);
1081 let location = useLocation();
1082 let matches = useMatches();
1083 let navigation = useNavigation();
1084 React$1.useEffect(() => {
1085 window.history.scrollRestoration = "manual";
1086 return () => {
1087 window.history.scrollRestoration = "auto";
1088 };
1089 }, []);
1090 usePageHide(React$1.useCallback(() => {
1091 if (navigation.state === "idle") {
1092 let key = getScrollRestorationKey(location, matches, basename, getKey);
1093 savedScrollPositions[key] = window.scrollY;
1094 }
1095 try {
1096 sessionStorage.setItem(storageKey || SCROLL_RESTORATION_STORAGE_KEY, JSON.stringify(savedScrollPositions));
1097 } catch (error) {
1098 warning(false, `Failed to save scroll positions in sessionStorage, <ScrollRestoration /> will not work properly (${error}).`);
1099 }
1100 window.history.scrollRestoration = "auto";
1101 }, [
1102 navigation.state,
1103 getKey,
1104 basename,
1105 location,
1106 matches,
1107 storageKey
1108 ]));
1109 if (typeof document !== "undefined") {
1110 React$1.useLayoutEffect(() => {
1111 try {
1112 let sessionPositions = sessionStorage.getItem(storageKey || SCROLL_RESTORATION_STORAGE_KEY);
1113 if (sessionPositions) savedScrollPositions = JSON.parse(sessionPositions);
1114 } catch (e) {}
1115 }, [storageKey]);
1116 React$1.useLayoutEffect(() => {
1117 let disableScrollRestoration = router?.enableScrollRestoration(savedScrollPositions, () => window.scrollY, getKey ? (location, matches) => getScrollRestorationKey(location, matches, basename, getKey) : void 0);
1118 return () => disableScrollRestoration && disableScrollRestoration();
1119 }, [
1120 router,
1121 basename,
1122 getKey
1123 ]);
1124 React$1.useLayoutEffect(() => {
1125 if (restoreScrollPosition === false) return;
1126 if (typeof restoreScrollPosition === "number") {
1127 window.scrollTo(0, restoreScrollPosition);
1128 return;
1129 }
1130 try {
1131 if (location.hash) {
1132 let el = document.getElementById(decodeURIComponent(location.hash.slice(1)));
1133 if (el) {
1134 el.scrollIntoView();
1135 return;
1136 }
1137 }
1138 } catch {
1139 warning(false, `"${location.hash.slice(1)}" is not a decodable element ID. The view will not scroll to it.`);
1140 }
1141 if (preventScrollReset === true) return;
1142 window.scrollTo(0, 0);
1143 }, [
1144 location,
1145 restoreScrollPosition,
1146 preventScrollReset
1147 ]);
1148 }
1149}
1150/**
1151* Set up a callback to be fired on [Window's `beforeunload` event](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event).
1152*
1153* @public
1154* @category Hooks
1155* @param callback The callback to be called when the [`beforeunload` event](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event)
1156* is fired.
1157* @param options Options
1158* @param options.capture If `true`, the event will be captured during the capture
1159* phase. Defaults to `false`.
1160* @returns {void}
1161*/
1162function useBeforeUnload(callback, options) {
1163 let { capture } = options || {};
1164 React$1.useEffect(() => {
1165 let opts = capture != null ? { capture } : void 0;
1166 window.addEventListener("beforeunload", callback, opts);
1167 return () => {
1168 window.removeEventListener("beforeunload", callback, opts);
1169 };
1170 }, [callback, capture]);
1171}
1172function usePageHide(callback, options) {
1173 let { capture } = options || {};
1174 React$1.useEffect(() => {
1175 let opts = capture != null ? { capture } : void 0;
1176 window.addEventListener("pagehide", callback, opts);
1177 return () => {
1178 window.removeEventListener("pagehide", callback, opts);
1179 };
1180 }, [callback, capture]);
1181}
1182/**
1183* Wrapper around {@link useBlocker} to show a [`window.confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm)
1184* prompt to users instead of building a custom UI with {@link useBlocker}.
1185*
1186* The `unstable_` flag will not be removed because this technique has a lot of
1187* rough edges and behaves very differently (and incorrectly sometimes) across
1188* browsers if users click addition back/forward navigations while the
1189* confirmation is open. Use at your own risk.
1190*
1191* @example
1192* function ImportantForm() {
1193* let [value, setValue] = React.useState("");
1194*
1195* // Block navigating elsewhere when data has been entered into the input
1196* unstable_usePrompt({
1197* message: "Are you sure?",
1198* when: ({ currentLocation, nextLocation }) =>
1199* value !== "" &&
1200* currentLocation.pathname !== nextLocation.pathname,
1201* });
1202*
1203* return (
1204* <Form method="post">
1205* <label>
1206* Enter some important data:
1207* <input
1208* name="data"
1209* value={value}
1210* onChange={(e) => setValue(e.target.value)}
1211* />
1212* </label>
1213* <button type="submit">Save</button>
1214* </Form>
1215* );
1216* }
1217*
1218* @name unstable_usePrompt
1219* @public
1220* @category Hooks
1221* @mode framework
1222* @mode data
1223* @param options Options
1224* @param options.message The message to show in the confirmation dialog.
1225* @param options.when A boolean or a function that returns a boolean indicating
1226* whether to block the navigation. If a function is provided, it will receive an
1227* object with `currentLocation` and `nextLocation` properties.
1228* @returns {void}
1229*/
1230function usePrompt({ when, message }) {
1231 let blocker = useBlocker(when);
1232 React$1.useEffect(() => {
1233 if (blocker.state === "blocked") if (window.confirm(message)) setTimeout(blocker.proceed, 0);
1234 else blocker.reset();
1235 }, [blocker, message]);
1236 React$1.useEffect(() => {
1237 if (blocker.state === "blocked" && !when) blocker.reset();
1238 }, [blocker, when]);
1239}
1240/**
1241* This hook returns `true` when there is an active [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
1242* and the specified location matches either side of the navigation (the URL you are
1243* navigating **to** or the URL you are navigating **from**). This can be used to apply finer-grained styles to
1244* elements to further customize the view transition. This requires that view
1245* transitions have been enabled for the given navigation via {@link LinkProps.viewTransition}
1246* (or the `Form`, `submit`, or `navigate` call)
1247*
1248* @public
1249* @category Hooks
1250* @mode framework
1251* @mode data
1252* @param to The {@link To} location to compare against the active transition's current
1253* and next URLs.
1254* @param options Options
1255* @param options.relative The relative routing type to use when resolving the
1256* `to` location, defaults to `"route"`. See {@link RelativeRoutingType} for
1257* more details.
1258* @returns `true` if there is an active [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
1259* and the resolved path matches the transition's destination or source pathname, otherwise `false`.
1260*/
1261function useViewTransitionState(to, { relative } = {}) {
1262 let vtContext = React$1.useContext(ViewTransitionContext);
1263 invariant(vtContext != null, "`useViewTransitionState` must be used within `react-router/dom`'s `RouterProvider`. Did you accidentally import `RouterProvider` from `react-router`?");
1264 let { basename } = useDataRouterContext("useViewTransitionState");
1265 let path = useResolvedPath(to, { relative });
1266 if (!vtContext.isTransitioning) return false;
1267 let currentPath = stripBasename(vtContext.currentLocation.pathname, basename) || vtContext.currentLocation.pathname;
1268 let nextPath = stripBasename(vtContext.nextLocation.pathname, basename) || vtContext.nextLocation.pathname;
1269 return matchPath(path.pathname, nextPath) != null || matchPath(path.pathname, currentPath) != null;
1270}
1271//#endregion
1272export { BrowserRouter, Form, HashRouter, HistoryRouter, Link, NavLink, ScrollRestoration, createBrowserRouter, createHashRouter, useBeforeUnload, useFetcher, useFetchers, useFormAction, useLinkClickHandler, usePrompt, useScrollRestoration, useSearchParams, useSubmit, useViewTransitionState };