UNPKG

49.2 kBJavaScriptView Raw
1/**
2 * react-router v8.0.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.0.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(toPathname.length) === "/");
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. Defaults to `true`.
613* @param options.mask Masked location to display in the browser instead
614* of the router location. Defaults to `undefined`.
615* @param options.useTransitions Wraps the navigation in
616* [`React.startTransition`](https://react.dev/reference/react/startTransition)
617* for concurrent rendering. Defaults to `false`.
618* @returns A click handler function that can be used in a custom {@link Link} component.
619*/
620function useLinkClickHandler(to, { target, replace: replaceProp, mask, state, preventScrollReset, relative, viewTransition, defaultShouldRevalidate, useTransitions } = {}) {
621 let navigate = useNavigate();
622 let location = useLocation();
623 let path = useResolvedPath(to, { relative });
624 return React$1.useCallback((event) => {
625 if (shouldProcessLinkClick(event, target)) {
626 event.preventDefault();
627 let replace = replaceProp !== void 0 ? replaceProp : createPath(location) === createPath(path);
628 let doNavigate = () => navigate(to, {
629 replace,
630 mask,
631 state,
632 preventScrollReset,
633 relative,
634 viewTransition,
635 defaultShouldRevalidate
636 });
637 if (useTransitions) React$1.startTransition(() => doNavigate());
638 else doNavigate();
639 }
640 }, [
641 location,
642 navigate,
643 path,
644 replaceProp,
645 mask,
646 state,
647 target,
648 to,
649 preventScrollReset,
650 relative,
651 viewTransition,
652 defaultShouldRevalidate,
653 useTransitions
654 ]);
655}
656/**
657* Returns a tuple of the current URL's [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
658* and a function to update them. Setting the search params causes a navigation.
659*
660* ```tsx
661* import { useSearchParams } from "react-router";
662*
663* export function SomeComponent() {
664* const [searchParams, setSearchParams] = useSearchParams();
665* // ...
666* }
667* ```
668*
669* ### `setSearchParams` function
670*
671* The second element of the tuple is a function that can be used to update the
672* search params. It accepts the same types as `defaultInit` and will cause a
673* navigation to the new URL.
674*
675* ```tsx
676* let [searchParams, setSearchParams] = useSearchParams();
677*
678* // a search param string
679* setSearchParams("?tab=1");
680*
681* // a shorthand object
682* setSearchParams({ tab: "1" });
683*
684* // object keys can be arrays for multiple values on the key
685* setSearchParams({ brand: ["nike", "reebok"] });
686*
687* // an array of tuples
688* setSearchParams([["tab", "1"]]);
689*
690* // a `URLSearchParams` object
691* setSearchParams(new URLSearchParams("?tab=1"));
692* ```
693*
694* It also supports a function callback like React's
695* [`setState`](https://react.dev/reference/react/useState#setstate):
696*
697* ```tsx
698* setSearchParams((searchParams) => {
699* searchParams.set("tab", "2");
700* return searchParams;
701* });
702* ```
703*
704* <docs-warning>The function callback version of `setSearchParams` does not support
705* the [queueing](https://react.dev/reference/react/useState#setstate-parameters)
706* logic that React's `setState` implements. Multiple calls to `setSearchParams`
707* in the same tick will not build on the prior value. If you need this behavior,
708* you can use `setState` manually.</docs-warning>
709*
710* ### Notes
711*
712* Note that `searchParams` is a stable reference, so you can reliably use it
713* as a dependency in React's [`useEffect`](https://react.dev/reference/react/useEffect)
714* hooks.
715*
716* ```tsx
717* useEffect(() => {
718* console.log(searchParams.get("tab"));
719* }, [searchParams]);
720* ```
721*
722* However, this also means it's mutable. If you change the object without
723* calling `setSearchParams`, its values will change between renders if some
724* other state causes the component to re-render and URL will not reflect the
725* values.
726*
727* @public
728* @category Hooks
729* @param defaultInit
730* You can initialize the search params with a default value, though it **will
731* not** change the URL on the first render.
732*
733* ```tsx
734* // a search param string
735* useSearchParams("?tab=1");
736*
737* // a shorthand object
738* useSearchParams({ tab: "1" });
739*
740* // object keys can be arrays for multiple values on the key
741* useSearchParams({ brand: ["nike", "reebok"] });
742*
743* // an array of tuples
744* useSearchParams([["tab", "1"]]);
745*
746* // a `URLSearchParams` object
747* useSearchParams(new URLSearchParams("?tab=1"));
748* ```
749* @returns A tuple of the current [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
750* and a function to update them.
751*/
752function useSearchParams(defaultInit) {
753 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.");
754 let defaultSearchParamsRef = React$1.useRef(createSearchParams(defaultInit));
755 let hasSetSearchParamsRef = React$1.useRef(false);
756 let location = useLocation();
757 let searchParams = React$1.useMemo(() => getSearchParamsForLocation(location.search, hasSetSearchParamsRef.current ? null : defaultSearchParamsRef.current), [location.search]);
758 let navigate = useNavigate();
759 return [searchParams, React$1.useCallback((nextInit, navigateOptions) => {
760 const newSearchParams = createSearchParams(typeof nextInit === "function" ? nextInit(new URLSearchParams(searchParams)) : nextInit);
761 hasSetSearchParamsRef.current = true;
762 navigate("?" + newSearchParams, navigateOptions);
763 }, [navigate, searchParams])];
764}
765let fetcherId = 0;
766let getUniqueFetcherId = () => `__${String(++fetcherId)}__`;
767/**
768* The imperative version of {@link Form | `<Form>`} that lets you submit a form
769* from code instead of a user interaction.
770*
771* @example
772* import { useSubmit } from "react-router";
773*
774* function SomeComponent() {
775* const submit = useSubmit();
776* return (
777* <Form onChange={(event) => submit(event.currentTarget)} />
778* );
779* }
780*
781* @public
782* @category Hooks
783* @mode framework
784* @mode data
785* @returns A function that can be called to submit a {@link Form} imperatively.
786*/
787function useSubmit() {
788 let { router } = useDataRouterContext("useSubmit");
789 let { basename } = React$1.useContext(NavigationContext);
790 let currentRouteId = useRouteId();
791 let routerFetch = router.fetch;
792 let routerNavigate = router.navigate;
793 return React$1.useCallback(async (target, options = {}) => {
794 let { action, method, encType, formData, body } = getFormSubmissionInfo(target, basename);
795 if (options.navigate === false) await routerFetch(options.fetcherKey || getUniqueFetcherId(), currentRouteId, options.action || action, {
796 defaultShouldRevalidate: options.defaultShouldRevalidate,
797 preventScrollReset: options.preventScrollReset,
798 formData,
799 body,
800 formMethod: options.method || method,
801 formEncType: options.encType || encType,
802 flushSync: options.flushSync
803 });
804 else await routerNavigate(options.action || action, {
805 defaultShouldRevalidate: options.defaultShouldRevalidate,
806 preventScrollReset: options.preventScrollReset,
807 formData,
808 body,
809 formMethod: options.method || method,
810 formEncType: options.encType || encType,
811 replace: options.replace,
812 state: options.state,
813 fromRouteId: currentRouteId,
814 flushSync: options.flushSync,
815 viewTransition: options.viewTransition
816 });
817 }, [
818 routerFetch,
819 routerNavigate,
820 basename,
821 currentRouteId
822 ]);
823}
824/**
825* Resolves the URL to the closest route in the component hierarchy instead of
826* the current URL of the app.
827*
828* This is used internally by {@link Form} to resolve the `action` to the closest
829* route, but can be used generically as well.
830*
831* ```ts
832* import { useFormAction } from "react-router";
833*
834* function SomeComponent() {
835* // closest route URL
836* let action = useFormAction();
837*
838* // closest route URL + "destroy"
839* let destroyAction = useFormAction("destroy");
840* }
841* ```
842*
843* <docs-info>This hook adds a `basename` if your app specifies one, so that it
844* can be used with raw `<form>` elements in a progressively enhanced way. If
845* you are using this to provide an `action` to `<Form>` or `fetcher.submit`, you
846* will need to remove the `basename` since both of those will prepend it
847* internally.</docs-info>
848*
849*
850* @public
851* @category Hooks
852* @mode framework
853* @mode data
854* @param action The action to append to the closest route URL. Defaults to the
855* closest route URL.
856* @param options Options
857* @param options.relative The relative routing type to use when resolving the
858* action. Defaults to `"route"`.
859* @returns The resolved action URL.
860*/
861function useFormAction(action, { relative } = {}) {
862 let { basename } = React$1.useContext(NavigationContext);
863 let routeContext = React$1.useContext(RouteContext);
864 invariant(routeContext, "useFormAction must be used inside a RouteContext");
865 let [match] = routeContext.matches.slice(-1);
866 let path = { ...useResolvedPath(action ? action : ".", { relative }) };
867 let location = useLocation();
868 if (action == null) {
869 path.search = location.search;
870 let params = new URLSearchParams(path.search);
871 let indexValues = params.getAll("index");
872 if (indexValues.some((v) => v === "")) {
873 params.delete("index");
874 indexValues.filter((v) => v).forEach((v) => params.append("index", v));
875 let qs = params.toString();
876 path.search = qs ? `?${qs}` : "";
877 }
878 }
879 if ((!action || action === ".") && match.route.index) path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
880 if (basename !== "/") path.pathname = path.pathname === "/" ? basename : joinPaths([basename, path.pathname]);
881 return createPath(path);
882}
883/**
884* Useful for creating complex, dynamic user interfaces that require multiple,
885* concurrent data interactions without causing a navigation.
886*
887* Fetchers track their own, independent state and can be used to load data, submit
888* forms, and generally interact with [`action`](../../start/framework/route-module#action)
889* and [`loader`](../../start/framework/route-module#loader) functions.
890*
891* @example
892* import { useFetcher } from "react-router"
893*
894* function SomeComponent() {
895* let fetcher = useFetcher()
896*
897* // states are available on the fetcher
898* fetcher.state // "idle" | "loading" | "submitting"
899* fetcher.data // the data returned from the action or loader
900*
901* // render a form
902* <fetcher.Form method="post" />
903*
904* // load data
905* fetcher.load("/some/route")
906*
907* // submit data
908* fetcher.submit(someFormRef, { method: "post" })
909* fetcher.submit(someData, {
910* method: "post",
911* encType: "application/json"
912* })
913*
914* // reset fetcher
915* fetcher.reset()
916* }
917*
918* @public
919* @category Hooks
920* @mode framework
921* @mode data
922* @param options Options
923* @param options.key A unique key to identify the fetcher.
924*
925*
926* By default, `useFetcher` generates a unique fetcher scoped to that component.
927* If you want to identify a fetcher with your own key such that you can access
928* it from elsewhere in your app, you can do that with the `key` option:
929*
930* ```tsx
931* function SomeComp() {
932* let fetcher = useFetcher({ key: "my-key" })
933* // ...
934* }
935*
936* // Somewhere else
937* function AnotherComp() {
938* // this will be the same fetcher, sharing the state across the app
939* let fetcher = useFetcher({ key: "my-key" });
940* // ...
941* }
942* ```
943* @returns A {@link FetcherWithComponents} object that contains the fetcher's state, data, and components for submitting forms and loading data.
944*/
945function useFetcher({ key } = {}) {
946 let { router } = useDataRouterContext("useFetcher");
947 let state = useDataRouterState("useFetcher");
948 let fetcherData = React$1.useContext(FetchersContext);
949 let route = React$1.useContext(RouteContext);
950 let routeId = route.matches[route.matches.length - 1]?.route.id;
951 invariant(fetcherData, `useFetcher must be used inside a FetchersContext`);
952 invariant(route, `useFetcher must be used inside a RouteContext`);
953 invariant(routeId != null, `useFetcher can only be used on routes that contain a unique "id"`);
954 let defaultKey = React$1.useId();
955 let [fetcherKey, setFetcherKey] = React$1.useState(key || defaultKey);
956 if (key && key !== fetcherKey) setFetcherKey(key);
957 let { deleteFetcher, getFetcher, resetFetcher, fetch: routerFetch } = router;
958 React$1.useEffect(() => {
959 getFetcher(fetcherKey);
960 return () => deleteFetcher(fetcherKey);
961 }, [
962 deleteFetcher,
963 getFetcher,
964 fetcherKey
965 ]);
966 let load = React$1.useCallback(async (href, opts) => {
967 invariant(routeId, "No routeId available for fetcher.load()");
968 await routerFetch(fetcherKey, routeId, href, opts);
969 }, [
970 fetcherKey,
971 routeId,
972 routerFetch
973 ]);
974 let submitImpl = useSubmit();
975 let submit = React$1.useCallback(async (target, opts) => {
976 await submitImpl(target, {
977 ...opts,
978 navigate: false,
979 fetcherKey
980 });
981 }, [fetcherKey, submitImpl]);
982 let reset = React$1.useCallback((opts) => resetFetcher(fetcherKey, opts), [resetFetcher, fetcherKey]);
983 let FetcherForm = React$1.useMemo(() => {
984 let FetcherForm = React$1.forwardRef((props, ref) => {
985 return /* @__PURE__ */ React$1.createElement(Form, {
986 ...props,
987 navigate: false,
988 fetcherKey,
989 ref
990 });
991 });
992 FetcherForm.displayName = "fetcher.Form";
993 return FetcherForm;
994 }, [fetcherKey]);
995 let fetcher = state.fetchers.get(fetcherKey) || IDLE_FETCHER;
996 let data = fetcherData.get(fetcherKey);
997 return React$1.useMemo(() => ({
998 Form: FetcherForm,
999 submit,
1000 load,
1001 reset,
1002 ...fetcher,
1003 data
1004 }), [
1005 FetcherForm,
1006 submit,
1007 load,
1008 reset,
1009 fetcher,
1010 data
1011 ]);
1012}
1013/**
1014* Returns an array of all in-flight {@link Fetcher}s. This is useful for components
1015* throughout the app that didn't create the fetchers but want to use their submissions
1016* to participate in optimistic UI.
1017*
1018* @example
1019* import { useFetchers } from "react-router";
1020*
1021* function SomeComponent() {
1022* const fetchers = useFetchers();
1023* fetchers[0].formData; // FormData
1024* fetchers[0].state; // etc.
1025* // ...
1026* }
1027*
1028* @public
1029* @category Hooks
1030* @mode framework
1031* @mode data
1032* @returns An array of all in-flight {@link Fetcher}s, each with a unique `key`
1033* property.
1034*/
1035function useFetchers() {
1036 let state = useDataRouterState("useFetchers");
1037 return React$1.useMemo(() => Array.from(state.fetchers.entries()).map(([key, fetcher]) => ({
1038 ...fetcher,
1039 key
1040 })), [state.fetchers]);
1041}
1042const SCROLL_RESTORATION_STORAGE_KEY = "react-router-scroll-positions";
1043let savedScrollPositions = {};
1044function getScrollRestorationKey(location, matches, basename, getKey) {
1045 let key = null;
1046 if (getKey) if (basename !== "/") key = getKey({
1047 ...location,
1048 pathname: stripBasename(location.pathname, basename) || location.pathname
1049 }, matches);
1050 else key = getKey(location, matches);
1051 if (key == null) key = location.key;
1052 return key;
1053}
1054/**
1055* When rendered inside a {@link RouterProvider}, will restore scroll positions
1056* on navigations
1057*
1058* <!--
1059* Not marked `@public` because we only export as UNSAFE_ and therefore we don't
1060* maintain an .md file for this hook
1061* -->
1062*
1063* @name UNSAFE_useScrollRestoration
1064* @category Hooks
1065* @mode framework
1066* @mode data
1067* @param options Options
1068* @param options.getKey A function that returns a key to use for scroll restoration.
1069* This is useful for custom scroll restoration logic, such as using only the pathname
1070* so that subsequent navigations to prior paths will restore the scroll. Defaults
1071* to `location.key`.
1072* @param options.storageKey The key to use for storing scroll positions in
1073* `sessionStorage`. Defaults to `"react-router-scroll-positions"`.
1074* @returns {void}
1075*/
1076function useScrollRestoration({ getKey, storageKey } = {}) {
1077 let { router } = useDataRouterContext("useScrollRestoration");
1078 let { restoreScrollPosition, preventScrollReset } = useDataRouterState("useScrollRestoration");
1079 let { basename } = React$1.useContext(NavigationContext);
1080 let location = useLocation();
1081 let matches = useMatches();
1082 let navigation = useNavigation();
1083 React$1.useEffect(() => {
1084 window.history.scrollRestoration = "manual";
1085 return () => {
1086 window.history.scrollRestoration = "auto";
1087 };
1088 }, []);
1089 usePageHide(React$1.useCallback(() => {
1090 if (navigation.state === "idle") {
1091 let key = getScrollRestorationKey(location, matches, basename, getKey);
1092 savedScrollPositions[key] = window.scrollY;
1093 }
1094 try {
1095 sessionStorage.setItem(storageKey || SCROLL_RESTORATION_STORAGE_KEY, JSON.stringify(savedScrollPositions));
1096 } catch (error) {
1097 warning(false, `Failed to save scroll positions in sessionStorage, <ScrollRestoration /> will not work properly (${error}).`);
1098 }
1099 window.history.scrollRestoration = "auto";
1100 }, [
1101 navigation.state,
1102 getKey,
1103 basename,
1104 location,
1105 matches,
1106 storageKey
1107 ]));
1108 if (typeof document !== "undefined") {
1109 React$1.useLayoutEffect(() => {
1110 try {
1111 let sessionPositions = sessionStorage.getItem(storageKey || SCROLL_RESTORATION_STORAGE_KEY);
1112 if (sessionPositions) savedScrollPositions = JSON.parse(sessionPositions);
1113 } catch (e) {}
1114 }, [storageKey]);
1115 React$1.useLayoutEffect(() => {
1116 let disableScrollRestoration = router?.enableScrollRestoration(savedScrollPositions, () => window.scrollY, getKey ? (location, matches) => getScrollRestorationKey(location, matches, basename, getKey) : void 0);
1117 return () => disableScrollRestoration && disableScrollRestoration();
1118 }, [
1119 router,
1120 basename,
1121 getKey
1122 ]);
1123 React$1.useLayoutEffect(() => {
1124 if (restoreScrollPosition === false) return;
1125 if (typeof restoreScrollPosition === "number") {
1126 window.scrollTo(0, restoreScrollPosition);
1127 return;
1128 }
1129 try {
1130 if (location.hash) {
1131 let el = document.getElementById(decodeURIComponent(location.hash.slice(1)));
1132 if (el) {
1133 el.scrollIntoView();
1134 return;
1135 }
1136 }
1137 } catch {
1138 warning(false, `"${location.hash.slice(1)}" is not a decodable element ID. The view will not scroll to it.`);
1139 }
1140 if (preventScrollReset === true) return;
1141 window.scrollTo(0, 0);
1142 }, [
1143 location,
1144 restoreScrollPosition,
1145 preventScrollReset
1146 ]);
1147 }
1148}
1149/**
1150* Set up a callback to be fired on [Window's `beforeunload` event](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event).
1151*
1152* @public
1153* @category Hooks
1154* @param callback The callback to be called when the [`beforeunload` event](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event)
1155* is fired.
1156* @param options Options
1157* @param options.capture If `true`, the event will be captured during the capture
1158* phase. Defaults to `false`.
1159* @returns {void}
1160*/
1161function useBeforeUnload(callback, options) {
1162 let { capture } = options || {};
1163 React$1.useEffect(() => {
1164 let opts = capture != null ? { capture } : void 0;
1165 window.addEventListener("beforeunload", callback, opts);
1166 return () => {
1167 window.removeEventListener("beforeunload", callback, opts);
1168 };
1169 }, [callback, capture]);
1170}
1171function usePageHide(callback, options) {
1172 let { capture } = options || {};
1173 React$1.useEffect(() => {
1174 let opts = capture != null ? { capture } : void 0;
1175 window.addEventListener("pagehide", callback, opts);
1176 return () => {
1177 window.removeEventListener("pagehide", callback, opts);
1178 };
1179 }, [callback, capture]);
1180}
1181/**
1182* Wrapper around {@link useBlocker} to show a [`window.confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm)
1183* prompt to users instead of building a custom UI with {@link useBlocker}.
1184*
1185* The `unstable_` flag will not be removed because this technique has a lot of
1186* rough edges and behaves very differently (and incorrectly sometimes) across
1187* browsers if users click addition back/forward navigations while the
1188* confirmation is open. Use at your own risk.
1189*
1190* @example
1191* function ImportantForm() {
1192* let [value, setValue] = React.useState("");
1193*
1194* // Block navigating elsewhere when data has been entered into the input
1195* unstable_usePrompt({
1196* message: "Are you sure?",
1197* when: ({ currentLocation, nextLocation }) =>
1198* value !== "" &&
1199* currentLocation.pathname !== nextLocation.pathname,
1200* });
1201*
1202* return (
1203* <Form method="post">
1204* <label>
1205* Enter some important data:
1206* <input
1207* name="data"
1208* value={value}
1209* onChange={(e) => setValue(e.target.value)}
1210* />
1211* </label>
1212* <button type="submit">Save</button>
1213* </Form>
1214* );
1215* }
1216*
1217* @name unstable_usePrompt
1218* @public
1219* @category Hooks
1220* @mode framework
1221* @mode data
1222* @param options Options
1223* @param options.message The message to show in the confirmation dialog.
1224* @param options.when A boolean or a function that returns a boolean indicating
1225* whether to block the navigation. If a function is provided, it will receive an
1226* object with `currentLocation` and `nextLocation` properties.
1227* @returns {void}
1228*/
1229function usePrompt({ when, message }) {
1230 let blocker = useBlocker(when);
1231 React$1.useEffect(() => {
1232 if (blocker.state === "blocked") if (window.confirm(message)) setTimeout(blocker.proceed, 0);
1233 else blocker.reset();
1234 }, [blocker, message]);
1235 React$1.useEffect(() => {
1236 if (blocker.state === "blocked" && !when) blocker.reset();
1237 }, [blocker, when]);
1238}
1239/**
1240* This hook returns `true` when there is an active [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
1241* and the specified location matches either side of the navigation (the URL you are
1242* navigating **to** or the URL you are navigating **from**). This can be used to apply finer-grained styles to
1243* elements to further customize the view transition. This requires that view
1244* transitions have been enabled for the given navigation via {@link LinkProps.viewTransition}
1245* (or the `Form`, `submit`, or `navigate` call)
1246*
1247* @public
1248* @category Hooks
1249* @mode framework
1250* @mode data
1251* @param to The {@link To} location to compare against the active transition's current
1252* and next URLs.
1253* @param options Options
1254* @param options.relative The relative routing type to use when resolving the
1255* `to` location, defaults to `"route"`. See {@link RelativeRoutingType} for
1256* more details.
1257* @returns `true` if there is an active [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
1258* and the resolved path matches the transition's destination or source pathname, otherwise `false`.
1259*/
1260function useViewTransitionState(to, { relative } = {}) {
1261 let vtContext = React$1.useContext(ViewTransitionContext);
1262 invariant(vtContext != null, "`useViewTransitionState` must be used within `react-router/dom`'s `RouterProvider`. Did you accidentally import `RouterProvider` from `react-router`?");
1263 let { basename } = useDataRouterContext("useViewTransitionState");
1264 let path = useResolvedPath(to, { relative });
1265 if (!vtContext.isTransitioning) return false;
1266 let currentPath = stripBasename(vtContext.currentLocation.pathname, basename) || vtContext.currentLocation.pathname;
1267 let nextPath = stripBasename(vtContext.nextLocation.pathname, basename) || vtContext.nextLocation.pathname;
1268 return matchPath(path.pathname, nextPath) != null || matchPath(path.pathname, currentPath) != null;
1269}
1270//#endregion
1271export { BrowserRouter, Form, HashRouter, HistoryRouter, Link, NavLink, ScrollRestoration, createBrowserRouter, createHashRouter, useBeforeUnload, useFetcher, useFetchers, useFormAction, useLinkClickHandler, usePrompt, useScrollRestoration, useSearchParams, useSubmit, useViewTransitionState };