@funstack/router 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/ApiComponentsPage.tsx +10 -9
- package/dist/docs/ExamplesPage.tsx +445 -0
- package/dist/docs/GettingStartedPage.tsx +10 -33
- package/dist/docs/LearnNavigationApiPage.tsx +1 -4
- package/dist/docs/LearnNestedRoutesPage.tsx +8 -4
- package/dist/docs/LearnRscPage.tsx +75 -105
- package/dist/docs/LearnSsgPage.tsx +133 -0
- package/dist/docs/{LearnSsrPage.tsx → LearnSsrBasicPage.tsx} +38 -94
- package/dist/docs/LearnSsrWithLoadersPage.tsx +141 -0
- package/dist/docs/LearnTypeSafetyPage.tsx +16 -20
- package/dist/docs/index.md +4 -1
- package/dist/index.d.mts +37 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +230 -140
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -2
package/dist/index.mjs
CHANGED
|
@@ -1,28 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { n as routeState, t as route } from "./route-p_gr5yPI.mjs";
|
|
4
|
-
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useState, useSyncExternalStore, useTransition } from "react";
|
|
4
|
+
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore, useTransition } from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
|
|
7
7
|
//#region src/context/RouterContext.ts
|
|
8
8
|
const RouterContext = createContext(null);
|
|
9
9
|
|
|
10
|
-
//#endregion
|
|
11
|
-
//#region src/context/RouteContext.ts
|
|
12
|
-
const RouteContext = createContext(null);
|
|
13
|
-
/**
|
|
14
|
-
* Find a route context by ID in the ancestor chain.
|
|
15
|
-
* Returns the matching context or null if not found.
|
|
16
|
-
*/
|
|
17
|
-
function findRouteContextById(context, id) {
|
|
18
|
-
let current = context;
|
|
19
|
-
while (current !== null) {
|
|
20
|
-
if (current.id === id) return current;
|
|
21
|
-
current = current.parent;
|
|
22
|
-
}
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
10
|
//#endregion
|
|
27
11
|
//#region src/context/BlockerContext.ts
|
|
28
12
|
/**
|
|
@@ -61,6 +45,7 @@ function internalRoutes(routes) {
|
|
|
61
45
|
|
|
62
46
|
//#endregion
|
|
63
47
|
//#region src/core/matchRoutes.ts
|
|
48
|
+
const SKIPPED = Symbol("skipped");
|
|
64
49
|
/**
|
|
65
50
|
* Match a pathname against a route tree, returning the matched route stack.
|
|
66
51
|
* Returns null if no match is found.
|
|
@@ -68,6 +53,7 @@ function internalRoutes(routes) {
|
|
|
68
53
|
function matchRoutes(routes, pathname, options) {
|
|
69
54
|
for (const route of routes) {
|
|
70
55
|
const matched = matchRoute(route, pathname, options);
|
|
56
|
+
if (matched === SKIPPED) return null;
|
|
71
57
|
if (matched) return matched;
|
|
72
58
|
}
|
|
73
59
|
return null;
|
|
@@ -78,7 +64,15 @@ function matchRoutes(routes, pathname, options) {
|
|
|
78
64
|
function matchRoute(route, pathname, options) {
|
|
79
65
|
const hasChildren = Boolean(route.children?.length);
|
|
80
66
|
const skipLoaders = options?.skipLoaders ?? false;
|
|
81
|
-
if ((pathname === null || skipLoaders) && route.loader)
|
|
67
|
+
if ((pathname === null || skipLoaders) && route.loader) {
|
|
68
|
+
if (skipLoaders && pathname !== null) {
|
|
69
|
+
if (route.path === void 0) return SKIPPED;
|
|
70
|
+
const isExact = route.exact ?? !hasChildren;
|
|
71
|
+
const { matched } = matchPath(route.path, pathname, isExact);
|
|
72
|
+
if (matched) return SKIPPED;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
82
76
|
if (route.path === void 0) {
|
|
83
77
|
const result = {
|
|
84
78
|
route,
|
|
@@ -86,10 +80,19 @@ function matchRoute(route, pathname, options) {
|
|
|
86
80
|
pathname: ""
|
|
87
81
|
};
|
|
88
82
|
if (hasChildren) {
|
|
83
|
+
let anySkipped = false;
|
|
89
84
|
for (const child of route.children) {
|
|
90
85
|
const childMatch = matchRoute(child, pathname, options);
|
|
86
|
+
if (childMatch === SKIPPED) {
|
|
87
|
+
anySkipped = true;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
91
90
|
if (childMatch) return [result, ...childMatch];
|
|
92
91
|
}
|
|
92
|
+
if (anySkipped) {
|
|
93
|
+
if (route.component) return [result];
|
|
94
|
+
return SKIPPED;
|
|
95
|
+
}
|
|
93
96
|
if (route.component && route.requireChildren === false) return [result];
|
|
94
97
|
if ((pathname === null || skipLoaders) && route.component) return [result];
|
|
95
98
|
return null;
|
|
@@ -109,8 +112,13 @@ function matchRoute(route, pathname, options) {
|
|
|
109
112
|
let remainingPathname = pathname.slice(consumedPathname.length);
|
|
110
113
|
if (!remainingPathname.startsWith("/")) remainingPathname = "/" + remainingPathname;
|
|
111
114
|
if (remainingPathname === "") remainingPathname = "/";
|
|
115
|
+
let anyChildSkipped = false;
|
|
112
116
|
for (const child of route.children) {
|
|
113
117
|
const childMatch = matchRoute(child, remainingPathname, options);
|
|
118
|
+
if (childMatch === SKIPPED) {
|
|
119
|
+
anyChildSkipped = true;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
114
122
|
if (childMatch) return [result, ...childMatch.map((m) => ({
|
|
115
123
|
...m,
|
|
116
124
|
params: {
|
|
@@ -119,6 +127,10 @@ function matchRoute(route, pathname, options) {
|
|
|
119
127
|
}
|
|
120
128
|
}))];
|
|
121
129
|
}
|
|
130
|
+
if (anyChildSkipped) {
|
|
131
|
+
if (route.component) return [result];
|
|
132
|
+
return SKIPPED;
|
|
133
|
+
}
|
|
122
134
|
if (route.component && route.requireChildren === false) return [result];
|
|
123
135
|
if (skipLoaders && route.component) return [result];
|
|
124
136
|
return null;
|
|
@@ -463,121 +475,55 @@ function createAdapter(fallback) {
|
|
|
463
475
|
}
|
|
464
476
|
|
|
465
477
|
//#endregion
|
|
466
|
-
//#region src/Router.
|
|
478
|
+
//#region src/Router/ServerLocationSnapshot.ts
|
|
467
479
|
/**
|
|
468
|
-
* Special
|
|
480
|
+
* Special class returned as server snapshot during SSR/hydration.
|
|
469
481
|
*/
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const [isPending, startTransition] = useTransition();
|
|
479
|
-
const [locationEntryInternal, setLocationEntry] = useState(initialEntry);
|
|
480
|
-
const locationEntry = locationEntryInternal === serverSnapshotSymbol ? null : locationEntryInternal;
|
|
481
|
-
if (locationEntryInternal === serverSnapshotSymbol && initialEntry !== serverSnapshotSymbol) setLocationEntry(initialEntry);
|
|
482
|
-
useEffect(() => {
|
|
483
|
-
return adapter.subscribe((changeType) => {
|
|
484
|
-
if (changeType === "navigation") startTransition(() => {
|
|
485
|
-
setLocationEntry(adapter.getSnapshot());
|
|
486
|
-
});
|
|
487
|
-
else setLocationEntry(adapter.getSnapshot());
|
|
488
|
-
});
|
|
489
|
-
}, [adapter, startTransition]);
|
|
490
|
-
useEffect(() => {
|
|
491
|
-
return adapter.setupInterception(routes, onNavigate, blockerRegistry.checkAll);
|
|
492
|
-
}, [
|
|
493
|
-
adapter,
|
|
494
|
-
routes,
|
|
495
|
-
onNavigate,
|
|
496
|
-
blockerRegistry
|
|
497
|
-
]);
|
|
498
|
-
const navigate = useCallback((to, options) => {
|
|
499
|
-
adapter.navigate(to, options);
|
|
500
|
-
}, [adapter]);
|
|
501
|
-
const navigateAsync = useCallback((to, options) => {
|
|
502
|
-
return adapter.navigateAsync(to, options);
|
|
503
|
-
}, [adapter]);
|
|
504
|
-
const updateCurrentEntryState = useCallback((state) => {
|
|
505
|
-
adapter.updateCurrentEntryState(state);
|
|
506
|
-
}, [adapter]);
|
|
507
|
-
return useMemo(() => {
|
|
508
|
-
const matchedRoutesWithData = (() => {
|
|
509
|
-
if (locationEntry === null) {
|
|
510
|
-
const matched = matchRoutes(routes, ssrPathname ?? null, { skipLoaders: true });
|
|
511
|
-
if (!matched) return null;
|
|
512
|
-
return matched.map((m) => ({
|
|
513
|
-
...m,
|
|
514
|
-
data: void 0
|
|
515
|
-
}));
|
|
516
|
-
}
|
|
517
|
-
const { url, key } = locationEntry;
|
|
518
|
-
const matched = matchRoutes(routes, url.pathname);
|
|
519
|
-
if (!matched) return null;
|
|
520
|
-
return executeLoaders(matched, key, createLoaderRequest(url), adapter.getIdleAbortSignal());
|
|
521
|
-
})();
|
|
522
|
-
const routerContextValue = {
|
|
523
|
-
locationEntry,
|
|
524
|
-
url: locationEntry?.url ?? null,
|
|
525
|
-
isPending,
|
|
526
|
-
navigate,
|
|
527
|
-
navigateAsync,
|
|
528
|
-
updateCurrentEntryState
|
|
529
|
-
};
|
|
530
|
-
const blockerContextValue = { registry: blockerRegistry };
|
|
531
|
-
return /* @__PURE__ */ jsx(BlockerContext.Provider, {
|
|
532
|
-
value: blockerContextValue,
|
|
533
|
-
children: /* @__PURE__ */ jsx(RouterContext.Provider, {
|
|
534
|
-
value: routerContextValue,
|
|
535
|
-
children: matchedRoutesWithData ? /* @__PURE__ */ jsx(RouteRenderer, {
|
|
536
|
-
matchedRoutes: matchedRoutesWithData,
|
|
537
|
-
index: 0
|
|
538
|
-
}) : null
|
|
539
|
-
})
|
|
540
|
-
});
|
|
541
|
-
}, [
|
|
542
|
-
navigate,
|
|
543
|
-
navigateAsync,
|
|
544
|
-
updateCurrentEntryState,
|
|
545
|
-
isPending,
|
|
546
|
-
locationEntry,
|
|
547
|
-
routes,
|
|
548
|
-
adapter,
|
|
549
|
-
blockerRegistry,
|
|
550
|
-
ssrPathname
|
|
551
|
-
]);
|
|
482
|
+
var ServerLocationSnapshot = class {
|
|
483
|
+
actualLocationEntry;
|
|
484
|
+
constructor(adapter) {
|
|
485
|
+
this.actualLocationEntry = adapter.getSnapshot();
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
function isServerSnapshot(value) {
|
|
489
|
+
return value instanceof ServerLocationSnapshot;
|
|
552
490
|
}
|
|
491
|
+
const noopSubscribe = () => () => {};
|
|
492
|
+
|
|
493
|
+
//#endregion
|
|
494
|
+
//#region src/context/RouteContext.ts
|
|
495
|
+
const RouteContext = createContext(null);
|
|
553
496
|
/**
|
|
554
|
-
*
|
|
497
|
+
* Find a route context by ID in the ancestor chain.
|
|
498
|
+
* Returns the matching context or null if not found.
|
|
555
499
|
*/
|
|
556
|
-
function
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
500
|
+
function findRouteContextById(context, id) {
|
|
501
|
+
let current = context;
|
|
502
|
+
while (current !== null) {
|
|
503
|
+
if (current.id === id) return current;
|
|
504
|
+
current = current.parent;
|
|
505
|
+
}
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
//#endregion
|
|
510
|
+
//#region src/Router/useRouteStateCallbacks.ts
|
|
511
|
+
function useRouteStateCallbacks(index, internalState, url, navigateAsync, updateCurrentEntryState) {
|
|
565
512
|
const setStateSync = useCallback((stateOrUpdater) => {
|
|
566
|
-
|
|
567
|
-
const currentStates = locationEntry.state?.__routeStates ?? [];
|
|
513
|
+
const currentStates = internalState?.__routeStates ?? [];
|
|
568
514
|
const currentRouteState = currentStates[index];
|
|
569
515
|
const newState = typeof stateOrUpdater === "function" ? stateOrUpdater(currentRouteState) : stateOrUpdater;
|
|
570
516
|
const newStates = [...currentStates];
|
|
571
517
|
newStates[index] = newState;
|
|
572
518
|
updateCurrentEntryState({ __routeStates: newStates });
|
|
573
519
|
}, [
|
|
574
|
-
|
|
520
|
+
internalState,
|
|
575
521
|
index,
|
|
576
522
|
updateCurrentEntryState
|
|
577
523
|
]);
|
|
578
524
|
const setState = useCallback(async (stateOrUpdater) => {
|
|
579
|
-
if (
|
|
580
|
-
const currentStates =
|
|
525
|
+
if (url === null) return;
|
|
526
|
+
const currentStates = internalState?.__routeStates ?? [];
|
|
581
527
|
const currentRouteState = currentStates[index];
|
|
582
528
|
const newState = typeof stateOrUpdater === "function" ? stateOrUpdater(currentRouteState) : stateOrUpdater;
|
|
583
529
|
const newStates = [...currentStates];
|
|
@@ -587,39 +533,61 @@ function RouteRenderer({ matchedRoutes, index }) {
|
|
|
587
533
|
state: { __routeStates: newStates }
|
|
588
534
|
});
|
|
589
535
|
}, [
|
|
590
|
-
|
|
536
|
+
internalState,
|
|
591
537
|
index,
|
|
592
538
|
url,
|
|
593
539
|
navigateAsync
|
|
594
540
|
]);
|
|
595
541
|
const resetStateSync = useCallback(() => {
|
|
596
|
-
|
|
597
|
-
const newStates = [...locationEntry.state?.__routeStates ?? []];
|
|
542
|
+
const newStates = [...internalState?.__routeStates ?? []];
|
|
598
543
|
newStates[index] = void 0;
|
|
599
544
|
updateCurrentEntryState({ __routeStates: newStates });
|
|
600
545
|
}, [
|
|
601
|
-
|
|
546
|
+
internalState,
|
|
602
547
|
index,
|
|
603
548
|
updateCurrentEntryState
|
|
604
549
|
]);
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
550
|
+
return {
|
|
551
|
+
setState,
|
|
552
|
+
setStateSync,
|
|
553
|
+
resetState: useCallback(async () => {
|
|
554
|
+
if (url === null) return;
|
|
555
|
+
const newStates = [...internalState?.__routeStates ?? []];
|
|
556
|
+
newStates[index] = void 0;
|
|
557
|
+
await navigateAsync(url.href, {
|
|
558
|
+
replace: true,
|
|
559
|
+
state: { __routeStates: newStates }
|
|
560
|
+
});
|
|
561
|
+
}, [
|
|
562
|
+
internalState,
|
|
563
|
+
index,
|
|
564
|
+
url,
|
|
565
|
+
navigateAsync
|
|
566
|
+
]),
|
|
567
|
+
resetStateSync
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
//#endregion
|
|
572
|
+
//#region src/Router/RouteRenderer.tsx
|
|
573
|
+
/**
|
|
574
|
+
* Recursively render matched routes with proper context.
|
|
575
|
+
*/
|
|
576
|
+
function RouteRenderer({ matchedRoutes, index }) {
|
|
577
|
+
const parentRouteContext = useContext(RouteContext);
|
|
578
|
+
const match = matchedRoutes[index];
|
|
579
|
+
if (!match) return null;
|
|
580
|
+
const { route, params, pathname, data } = match;
|
|
581
|
+
const routerContext = useContext(RouterContext);
|
|
582
|
+
if (!routerContext) throw new Error("RouteRenderer must be used within RouterContext");
|
|
583
|
+
const { locationState, locationInfo, url, isPending, navigateAsync, updateCurrentEntryState } = routerContext;
|
|
584
|
+
const internalState = locationState;
|
|
585
|
+
const routeState = internalState?.__routeStates?.[index];
|
|
586
|
+
const { setState, setStateSync, resetState, resetStateSync } = useRouteStateCallbacks(index, internalState, url, navigateAsync, updateCurrentEntryState);
|
|
587
|
+
const outlet = useMemo(() => index < matchedRoutes.length - 1 ? /* @__PURE__ */ jsx(RouteRenderer, {
|
|
620
588
|
matchedRoutes,
|
|
621
589
|
index: index + 1
|
|
622
|
-
}) : null;
|
|
590
|
+
}) : null, [matchedRoutes, index]);
|
|
623
591
|
const routeId = route.id;
|
|
624
592
|
const routeContextValue = useMemo(() => ({
|
|
625
593
|
id: routeId,
|
|
@@ -650,7 +618,7 @@ function RouteRenderer({ matchedRoutes, index }) {
|
|
|
650
618
|
resetState,
|
|
651
619
|
resetStateSync
|
|
652
620
|
};
|
|
653
|
-
const info =
|
|
621
|
+
const info = locationInfo;
|
|
654
622
|
if (route.loader) return /* @__PURE__ */ jsx(Component, {
|
|
655
623
|
data,
|
|
656
624
|
params,
|
|
@@ -671,6 +639,127 @@ function RouteRenderer({ matchedRoutes, index }) {
|
|
|
671
639
|
});
|
|
672
640
|
}
|
|
673
641
|
|
|
642
|
+
//#endregion
|
|
643
|
+
//#region src/Router/index.tsx
|
|
644
|
+
function Router({ routes: inputRoutes, onNavigate, fallback = "none", ssr }) {
|
|
645
|
+
const routes = internalRoutes(inputRoutes);
|
|
646
|
+
const adapter = useMemo(() => createAdapter(fallback), [fallback]);
|
|
647
|
+
const [blockerRegistry] = useState(() => createBlockerRegistry());
|
|
648
|
+
const getSnapshot = useCallback(() => adapter.getSnapshot(), [adapter]);
|
|
649
|
+
const serverSnapshotCacheRef = useRef(null);
|
|
650
|
+
const initialEntry = useSyncExternalStore(noopSubscribe, getSnapshot, useCallback(() => {
|
|
651
|
+
return serverSnapshotCacheRef.current ??= new ServerLocationSnapshot(adapter);
|
|
652
|
+
}, [adapter]));
|
|
653
|
+
const [isPending, startTransition] = useTransition();
|
|
654
|
+
const [locationEntryInternal, setLocationEntry] = useState(initialEntry);
|
|
655
|
+
const locationEntry = isServerSnapshot(locationEntryInternal) ? null : locationEntryInternal;
|
|
656
|
+
if (isServerSnapshot(locationEntryInternal) && !isServerSnapshot(initialEntry)) setLocationEntry(initialEntry);
|
|
657
|
+
useEffect(() => {
|
|
658
|
+
return adapter.subscribe((changeType) => {
|
|
659
|
+
if (changeType === "navigation") startTransition(() => {
|
|
660
|
+
setLocationEntry(adapter.getSnapshot());
|
|
661
|
+
});
|
|
662
|
+
else setLocationEntry(adapter.getSnapshot());
|
|
663
|
+
});
|
|
664
|
+
}, [adapter, startTransition]);
|
|
665
|
+
useEffect(() => {
|
|
666
|
+
return adapter.setupInterception(routes, onNavigate, blockerRegistry.checkAll);
|
|
667
|
+
}, [
|
|
668
|
+
adapter,
|
|
669
|
+
routes,
|
|
670
|
+
onNavigate,
|
|
671
|
+
blockerRegistry
|
|
672
|
+
]);
|
|
673
|
+
const navigate = useCallback((to, options) => {
|
|
674
|
+
adapter.navigate(to, options);
|
|
675
|
+
}, [adapter]);
|
|
676
|
+
const navigateAsync = useCallback((to, options) => {
|
|
677
|
+
return adapter.navigateAsync(to, options);
|
|
678
|
+
}, [adapter]);
|
|
679
|
+
const updateCurrentEntryState = useCallback((state) => {
|
|
680
|
+
adapter.updateCurrentEntryState(state);
|
|
681
|
+
}, [adapter]);
|
|
682
|
+
const url = useMemo(() => {
|
|
683
|
+
if (locationEntry) return locationEntry.url.toString();
|
|
684
|
+
if (ssr) {
|
|
685
|
+
const origin = typeof window !== "undefined" ? window.location.origin : "http://localhost";
|
|
686
|
+
return new URL(ssr.path, origin).toString();
|
|
687
|
+
}
|
|
688
|
+
return null;
|
|
689
|
+
}, [locationEntry, ssr]);
|
|
690
|
+
/**
|
|
691
|
+
* URL object. Non-null when client-side or during SSR with ssr.path provided.
|
|
692
|
+
* Null during SSR without ssr.path.
|
|
693
|
+
*/
|
|
694
|
+
const urlObject = useMemo(() => url ? new URL(url) : null, [url]);
|
|
695
|
+
/**
|
|
696
|
+
* Whether to run loaders.
|
|
697
|
+
* 1. Loaders are always run for rendering with URL available (client-side)
|
|
698
|
+
* 2. During SSR, loaders are only run if ssr.runLoaders is true and URL is available (ssr.path provided).
|
|
699
|
+
*/
|
|
700
|
+
const runLoaders = locationEntry !== null || !!ssr?.runLoaders && urlObject !== null;
|
|
701
|
+
/**
|
|
702
|
+
* Key of location. This is used as the cache key for loader data saved in navigation entry.
|
|
703
|
+
*/
|
|
704
|
+
const locationKey = locationEntry?.key ?? (isServerSnapshot(locationEntryInternal) ? locationEntryInternal.actualLocationEntry?.key : null) ?? "ssr";
|
|
705
|
+
const matchedRoutesWithData = useMemo(() => {
|
|
706
|
+
if (!runLoaders) {
|
|
707
|
+
const matched = matchRoutes(routes, urlObject?.pathname ?? null, { skipLoaders: true });
|
|
708
|
+
if (!matched) return null;
|
|
709
|
+
return matched.map((m) => ({
|
|
710
|
+
...m,
|
|
711
|
+
data: void 0
|
|
712
|
+
}));
|
|
713
|
+
}
|
|
714
|
+
if (urlObject === null) throw new Error("Invariant failure: loaders cannot run without URL.");
|
|
715
|
+
const matched = matchRoutes(routes, urlObject.pathname);
|
|
716
|
+
if (!matched) return null;
|
|
717
|
+
return executeLoaders(matched, locationKey, createLoaderRequest(urlObject), adapter.getIdleAbortSignal());
|
|
718
|
+
}, [
|
|
719
|
+
routes,
|
|
720
|
+
adapter,
|
|
721
|
+
urlObject,
|
|
722
|
+
runLoaders,
|
|
723
|
+
locationKey
|
|
724
|
+
]);
|
|
725
|
+
const locationState = locationEntry?.state;
|
|
726
|
+
const locationInfo = locationEntry?.info;
|
|
727
|
+
const routerContextValue = useMemo(() => ({
|
|
728
|
+
locationState,
|
|
729
|
+
locationInfo,
|
|
730
|
+
url: urlObject,
|
|
731
|
+
isPending,
|
|
732
|
+
navigate,
|
|
733
|
+
navigateAsync,
|
|
734
|
+
updateCurrentEntryState
|
|
735
|
+
}), [
|
|
736
|
+
locationState,
|
|
737
|
+
locationInfo,
|
|
738
|
+
urlObject,
|
|
739
|
+
isPending,
|
|
740
|
+
navigate,
|
|
741
|
+
navigateAsync,
|
|
742
|
+
updateCurrentEntryState
|
|
743
|
+
]);
|
|
744
|
+
return useMemo(() => {
|
|
745
|
+
const blockerContextValue = { registry: blockerRegistry };
|
|
746
|
+
return /* @__PURE__ */ jsx(BlockerContext.Provider, {
|
|
747
|
+
value: blockerContextValue,
|
|
748
|
+
children: /* @__PURE__ */ jsx(RouterContext.Provider, {
|
|
749
|
+
value: routerContextValue,
|
|
750
|
+
children: matchedRoutesWithData ? /* @__PURE__ */ jsx(RouteRenderer, {
|
|
751
|
+
matchedRoutes: matchedRoutesWithData,
|
|
752
|
+
index: 0
|
|
753
|
+
}) : null
|
|
754
|
+
})
|
|
755
|
+
});
|
|
756
|
+
}, [
|
|
757
|
+
routerContextValue,
|
|
758
|
+
matchedRoutesWithData,
|
|
759
|
+
blockerRegistry
|
|
760
|
+
]);
|
|
761
|
+
}
|
|
762
|
+
|
|
674
763
|
//#endregion
|
|
675
764
|
//#region src/Outlet.tsx
|
|
676
765
|
/**
|
|
@@ -723,6 +812,7 @@ function useSearchParams() {
|
|
|
723
812
|
if (!context) throw new Error("useSearchParams must be used within a Router");
|
|
724
813
|
if (context.url === null) throw new Error("useSearchParams: URL is not available during SSR.");
|
|
725
814
|
const currentUrl = context.url;
|
|
815
|
+
const { navigate } = context;
|
|
726
816
|
return [currentUrl.searchParams, useCallback((params) => {
|
|
727
817
|
const url = new URL(currentUrl);
|
|
728
818
|
let newParams;
|
|
@@ -732,8 +822,8 @@ function useSearchParams() {
|
|
|
732
822
|
} else if (params instanceof URLSearchParams) newParams = params;
|
|
733
823
|
else newParams = new URLSearchParams(params);
|
|
734
824
|
url.search = newParams.toString();
|
|
735
|
-
|
|
736
|
-
}, [currentUrl,
|
|
825
|
+
navigate(url.pathname + url.search + url.hash, { replace: true });
|
|
826
|
+
}, [currentUrl, navigate])];
|
|
737
827
|
}
|
|
738
828
|
|
|
739
829
|
//#endregion
|