@solidjs/router 0.13.5 → 0.13.6
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/README.md +14 -2
- package/dist/data/action.d.ts +2 -2
- package/dist/data/action.js +2 -0
- package/dist/data/cache.js +15 -17
- package/dist/data/events.js +2 -2
- package/dist/index.js +72 -68
- package/dist/routers/Router.js +1 -1
- package/dist/routing.d.ts +3 -0
- package/dist/routing.js +46 -52
- package/dist/types.d.ts +12 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -372,8 +372,10 @@ You can nest indefinitely - just remember that only leaf nodes will become their
|
|
|
372
372
|
</div>
|
|
373
373
|
}
|
|
374
374
|
>
|
|
375
|
-
<Route
|
|
376
|
-
|
|
375
|
+
<Route
|
|
376
|
+
path="layer2"
|
|
377
|
+
component={() => <div>Innermost layer</div>}
|
|
378
|
+
/>
|
|
377
379
|
</Route>
|
|
378
380
|
</Route>
|
|
379
381
|
```
|
|
@@ -871,6 +873,16 @@ const matches = useCurrentMatches();
|
|
|
871
873
|
const breadcrumbs = createMemo(() => matches().map(m => m.route.info.breadcrumb))
|
|
872
874
|
```
|
|
873
875
|
|
|
876
|
+
### usePreloadRoute
|
|
877
|
+
|
|
878
|
+
`usePreloadRoute` returns a function that can be used to preload a route manual. This is what happens automatically with link hovering and similar focus based behavior, but it is available here as an API.
|
|
879
|
+
|
|
880
|
+
```js
|
|
881
|
+
const preload = usePreloadRoute();
|
|
882
|
+
|
|
883
|
+
preload(`/users/settings`, { preloadData: true });
|
|
884
|
+
```
|
|
885
|
+
|
|
874
886
|
### useBeforeLeave
|
|
875
887
|
|
|
876
888
|
`useBeforeLeave` takes a function that will be called prior to leaving a route. The function will be called with:
|
package/dist/data/action.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSX } from "solid-js";
|
|
2
|
-
import { Submission } from "../types.js";
|
|
2
|
+
import type { Submission, SubmissionStub } from "../types.js";
|
|
3
3
|
export type Action<T extends Array<any>, U> = (T extends [FormData] | [] ? JSX.SerializableAttributeValue : unknown) & ((...vars: T) => Promise<U>) & {
|
|
4
4
|
url: string;
|
|
5
5
|
with<A extends any[], B extends any[]>(this: (this: any, ...args: [...A, ...B]) => Promise<U>, ...args: A): Action<B, U>;
|
|
@@ -8,6 +8,6 @@ export declare const actions: Map<string, Action<any, any>>;
|
|
|
8
8
|
export declare function useSubmissions<T extends Array<any>, U>(fn: Action<T, U>, filter?: (arg: T) => boolean): Submission<T, U>[] & {
|
|
9
9
|
pending: boolean;
|
|
10
10
|
};
|
|
11
|
-
export declare function useSubmission<T extends Array<any>, U>(fn: Action<T, U>, filter?: (arg: T) => boolean): Submission<T, U
|
|
11
|
+
export declare function useSubmission<T extends Array<any>, U>(fn: Action<T, U>, filter?: (arg: T) => boolean): Submission<T, U> | SubmissionStub;
|
|
12
12
|
export declare function useAction<T extends Array<any>, U>(action: Action<T, U>): (...args: Parameters<Action<T, U>>) => Promise<U>;
|
|
13
13
|
export declare function action<T extends Array<any>, U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>;
|
package/dist/data/action.js
CHANGED
|
@@ -21,6 +21,8 @@ export function useSubmission(fn, filter) {
|
|
|
21
21
|
const submissions = useSubmissions(fn, filter);
|
|
22
22
|
return new Proxy({}, {
|
|
23
23
|
get(_, property) {
|
|
24
|
+
if (submissions.length === 0 && property === "clear" || property === "retry")
|
|
25
|
+
return (() => { });
|
|
24
26
|
return submissions[submissions.length - 1]?.[property];
|
|
25
27
|
}
|
|
26
28
|
});
|
package/dist/data/cache.js
CHANGED
|
@@ -87,15 +87,14 @@ export function cache(fn, name) {
|
|
|
87
87
|
cached[0] = now;
|
|
88
88
|
}
|
|
89
89
|
let res = cached[1];
|
|
90
|
-
if (
|
|
90
|
+
if (intent !== "preload") {
|
|
91
91
|
res =
|
|
92
92
|
"then" in cached[1]
|
|
93
93
|
? cached[1].then(handleResponse(false), handleResponse(true))
|
|
94
94
|
: handleResponse(false)(cached[1]);
|
|
95
95
|
!isServer && intent === "navigate" && startTransition(() => cached[3][1](cached[0])); // update version
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
"then" in res && res.catch(() => { });
|
|
97
|
+
inLoadFn && "then" in res && res.catch(() => { });
|
|
99
98
|
return res;
|
|
100
99
|
}
|
|
101
100
|
let res = !isServer && sharedConfig.context && sharedConfig.has(key)
|
|
@@ -120,14 +119,13 @@ export function cache(fn, name) {
|
|
|
120
119
|
if (e && e.router.dataOnly)
|
|
121
120
|
return (e.router.data[key] = res);
|
|
122
121
|
}
|
|
123
|
-
if (
|
|
122
|
+
if (intent !== "preload") {
|
|
124
123
|
res =
|
|
125
124
|
"then" in res
|
|
126
125
|
? res.then(handleResponse(false), handleResponse(true))
|
|
127
126
|
: handleResponse(false)(res);
|
|
128
127
|
}
|
|
129
|
-
|
|
130
|
-
"then" in res && res.catch(() => { });
|
|
128
|
+
inLoadFn && "then" in res && res.catch(() => { });
|
|
131
129
|
// serialize on server
|
|
132
130
|
if (isServer &&
|
|
133
131
|
sharedConfig.context &&
|
|
@@ -140,19 +138,19 @@ export function cache(fn, name) {
|
|
|
140
138
|
function handleResponse(error) {
|
|
141
139
|
return async (v) => {
|
|
142
140
|
if (v instanceof Response) {
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
const url = v.headers.get(LocationHeader);
|
|
142
|
+
if (url !== null) {
|
|
143
|
+
// client + server relative redirect
|
|
144
|
+
if (navigate && url.startsWith("/"))
|
|
145
145
|
startTransition(() => {
|
|
146
|
-
|
|
147
|
-
if (url && url.startsWith("/")) {
|
|
148
|
-
navigate(url, {
|
|
149
|
-
replace: true
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
else if (!isServer && url) {
|
|
153
|
-
window.location.href = url;
|
|
154
|
-
}
|
|
146
|
+
navigate(url, { replace: true });
|
|
155
147
|
});
|
|
148
|
+
else if (!isServer)
|
|
149
|
+
window.location.href = url;
|
|
150
|
+
else if (isServer) {
|
|
151
|
+
const e = getRequestEvent();
|
|
152
|
+
if (e)
|
|
153
|
+
e.response = { status: 302, headers: new Headers({ Location: url }) };
|
|
156
154
|
}
|
|
157
155
|
return;
|
|
158
156
|
}
|
package/dist/data/events.js
CHANGED
|
@@ -61,7 +61,7 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
|
|
|
61
61
|
url.pathname = transformUrl(url.pathname);
|
|
62
62
|
}
|
|
63
63
|
if (!preloadTimeout[url.pathname])
|
|
64
|
-
router.preloadRoute(url, a.getAttribute("preload") !== "false");
|
|
64
|
+
router.preloadRoute(url, { preloadData: a.getAttribute("preload") !== "false" });
|
|
65
65
|
}
|
|
66
66
|
function handleAnchorIn(evt) {
|
|
67
67
|
const res = handleAnchor(evt);
|
|
@@ -74,7 +74,7 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
|
|
|
74
74
|
if (preloadTimeout[url.pathname])
|
|
75
75
|
return;
|
|
76
76
|
preloadTimeout[url.pathname] = setTimeout(() => {
|
|
77
|
-
router.preloadRoute(url, a.getAttribute("preload") !== "false");
|
|
77
|
+
router.preloadRoute(url, { preloadData: a.getAttribute("preload") !== "false" });
|
|
78
78
|
delete preloadTimeout[url.pathname];
|
|
79
79
|
}, 200);
|
|
80
80
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isServer, getRequestEvent, createComponent as createComponent$1, memo, delegateEvents, spread, mergeProps as mergeProps$1, template } from 'solid-js/web';
|
|
2
|
-
import { getOwner, runWithOwner, createMemo, createContext, onCleanup, useContext, untrack, createSignal, createRenderEffect, on, startTransition, resetErrorBoundaries, createComponent, children, mergeProps, Show, createRoot, getListener, sharedConfig, $TRACK, splitProps, createResource } from 'solid-js';
|
|
2
|
+
import { getOwner, runWithOwner, createMemo, createContext, onCleanup, useContext, untrack, createSignal, createRenderEffect, on, startTransition, resetErrorBoundaries, batch, createComponent, children, mergeProps, Show, createRoot, getListener, sharedConfig, $TRACK, splitProps, createResource } from 'solid-js';
|
|
3
3
|
import { createStore, reconcile, unwrap } from 'solid-js/store';
|
|
4
4
|
|
|
5
5
|
function createBeforeLeave() {
|
|
@@ -433,13 +433,33 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
433
433
|
});
|
|
434
434
|
}
|
|
435
435
|
const [isRouting, setIsRouting] = createSignal(false);
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
436
|
+
|
|
437
|
+
// Keep track of last target, so that last call to transition wins
|
|
438
|
+
let lastTransitionTarget;
|
|
439
|
+
|
|
440
|
+
// Transition the location to a new value
|
|
441
|
+
const transition = (newIntent, newTarget) => {
|
|
442
|
+
if (newTarget.value === reference() && newTarget.state === state()) return;
|
|
443
|
+
if (lastTransitionTarget === undefined) setIsRouting(true);
|
|
444
|
+
intent = newIntent;
|
|
445
|
+
lastTransitionTarget = newTarget;
|
|
446
|
+
startTransition(() => {
|
|
447
|
+
if (lastTransitionTarget !== newTarget) return;
|
|
448
|
+
setReference(lastTransitionTarget.value);
|
|
449
|
+
setState(lastTransitionTarget.state);
|
|
450
|
+
resetErrorBoundaries();
|
|
451
|
+
if (!isServer) submissions[1]([]);
|
|
452
|
+
}).finally(() => {
|
|
453
|
+
if (lastTransitionTarget !== newTarget) return;
|
|
454
|
+
|
|
455
|
+
// Batch, in order for isRouting and final source update to happen together
|
|
456
|
+
batch(() => {
|
|
457
|
+
intent = undefined;
|
|
458
|
+
if (newIntent === "navigate") navigateEnd(lastTransitionTarget);
|
|
459
|
+
setIsRouting(false);
|
|
460
|
+
lastTransitionTarget = undefined;
|
|
461
|
+
});
|
|
462
|
+
});
|
|
443
463
|
};
|
|
444
464
|
const [reference, setReference] = createSignal(source().value);
|
|
445
465
|
const [state, setState] = createSignal(source().state);
|
|
@@ -468,24 +488,11 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
468
488
|
return resolvePath(basePath, to);
|
|
469
489
|
}
|
|
470
490
|
};
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
// Untrack this whole block so `start` doesn't cause Solid's Listener to be preserved
|
|
477
|
-
untrack(() => {
|
|
478
|
-
start(() => {
|
|
479
|
-
intent = "native";
|
|
480
|
-
if (value !== reference()) setReference(value);
|
|
481
|
-
setState(state);
|
|
482
|
-
resetErrorBoundaries();
|
|
483
|
-
submissions[1]([]);
|
|
484
|
-
}).then(() => {
|
|
485
|
-
intent = undefined;
|
|
486
|
-
});
|
|
487
|
-
});
|
|
488
|
-
});
|
|
491
|
+
|
|
492
|
+
// Create a native transition, when source updates
|
|
493
|
+
createRenderEffect(on(source, source => transition("native", source), {
|
|
494
|
+
defer: true
|
|
495
|
+
}));
|
|
489
496
|
return {
|
|
490
497
|
base: baseRoute,
|
|
491
498
|
location,
|
|
@@ -545,26 +552,15 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
545
552
|
state: nextState
|
|
546
553
|
});
|
|
547
554
|
} else if (beforeLeave.confirm(resolvedTo, options)) {
|
|
548
|
-
|
|
555
|
+
referrers.push({
|
|
549
556
|
value: current,
|
|
550
557
|
replace,
|
|
551
558
|
scroll,
|
|
552
559
|
state: state()
|
|
553
560
|
});
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
setState(nextState);
|
|
558
|
-
resetErrorBoundaries();
|
|
559
|
-
submissions[1]([]);
|
|
560
|
-
}).then(() => {
|
|
561
|
-
if (referrers.length === len) {
|
|
562
|
-
intent = undefined;
|
|
563
|
-
navigateEnd({
|
|
564
|
-
value: resolvedTo,
|
|
565
|
-
state: nextState
|
|
566
|
-
});
|
|
567
|
-
}
|
|
561
|
+
transition("navigate", {
|
|
562
|
+
value: resolvedTo,
|
|
563
|
+
state: nextState
|
|
568
564
|
});
|
|
569
565
|
}
|
|
570
566
|
}
|
|
@@ -578,17 +574,15 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
578
574
|
function navigateEnd(next) {
|
|
579
575
|
const first = referrers[0];
|
|
580
576
|
if (first) {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
});
|
|
587
|
-
}
|
|
577
|
+
setSource({
|
|
578
|
+
...next,
|
|
579
|
+
replace: first.replace,
|
|
580
|
+
scroll: first.scroll
|
|
581
|
+
});
|
|
588
582
|
referrers.length = 0;
|
|
589
583
|
}
|
|
590
584
|
}
|
|
591
|
-
function preloadRoute(url,
|
|
585
|
+
function preloadRoute(url, options = {}) {
|
|
592
586
|
const matches = getRouteMatches(branches(), url.pathname);
|
|
593
587
|
const prevIntent = intent;
|
|
594
588
|
intent = "preload";
|
|
@@ -602,7 +596,7 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
602
596
|
load
|
|
603
597
|
} = route;
|
|
604
598
|
inLoadFn = true;
|
|
605
|
-
preloadData && load && runWithOwner(getContext(), () => load({
|
|
599
|
+
options.preloadData && load && runWithOwner(getContext(), () => load({
|
|
606
600
|
params,
|
|
607
601
|
location: {
|
|
608
602
|
pathname: url.pathname,
|
|
@@ -962,10 +956,11 @@ function cache(fn, name) {
|
|
|
962
956
|
cached[0] = now;
|
|
963
957
|
}
|
|
964
958
|
let res = cached[1];
|
|
965
|
-
if (
|
|
959
|
+
if (intent !== "preload") {
|
|
966
960
|
res = "then" in cached[1] ? cached[1].then(handleResponse(false), handleResponse(true)) : handleResponse(false)(cached[1]);
|
|
967
961
|
!isServer && intent === "navigate" && startTransition(() => cached[3][1](cached[0])); // update version
|
|
968
|
-
}
|
|
962
|
+
}
|
|
963
|
+
inLoadFn && "then" in res && res.catch(() => {});
|
|
969
964
|
return res;
|
|
970
965
|
}
|
|
971
966
|
let res = !isServer && sharedConfig.context && sharedConfig.has(key) ? sharedConfig.load(key) // hydrating
|
|
@@ -987,9 +982,10 @@ function cache(fn, name) {
|
|
|
987
982
|
const e = getRequestEvent();
|
|
988
983
|
if (e && e.router.dataOnly) return e.router.data[key] = res;
|
|
989
984
|
}
|
|
990
|
-
if (
|
|
985
|
+
if (intent !== "preload") {
|
|
991
986
|
res = "then" in res ? res.then(handleResponse(false), handleResponse(true)) : handleResponse(false)(res);
|
|
992
|
-
}
|
|
987
|
+
}
|
|
988
|
+
inLoadFn && "then" in res && res.catch(() => {});
|
|
993
989
|
// serialize on server
|
|
994
990
|
if (isServer && sharedConfig.context && sharedConfig.context.async && !sharedConfig.context.noHydrate) {
|
|
995
991
|
const e = getRequestEvent();
|
|
@@ -999,18 +995,21 @@ function cache(fn, name) {
|
|
|
999
995
|
function handleResponse(error) {
|
|
1000
996
|
return async v => {
|
|
1001
997
|
if (v instanceof Response) {
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
replace: true
|
|
1009
|
-
});
|
|
1010
|
-
} else if (!isServer && url) {
|
|
1011
|
-
window.location.href = url;
|
|
1012
|
-
}
|
|
998
|
+
const url = v.headers.get(LocationHeader);
|
|
999
|
+
if (url !== null) {
|
|
1000
|
+
// client + server relative redirect
|
|
1001
|
+
if (navigate && url.startsWith("/")) startTransition(() => {
|
|
1002
|
+
navigate(url, {
|
|
1003
|
+
replace: true
|
|
1013
1004
|
});
|
|
1005
|
+
});else if (!isServer) window.location.href = url;else if (isServer) {
|
|
1006
|
+
const e = getRequestEvent();
|
|
1007
|
+
if (e) e.response = {
|
|
1008
|
+
status: 302,
|
|
1009
|
+
headers: new Headers({
|
|
1010
|
+
Location: url
|
|
1011
|
+
})
|
|
1012
|
+
};
|
|
1014
1013
|
}
|
|
1015
1014
|
return;
|
|
1016
1015
|
}
|
|
@@ -1075,6 +1074,7 @@ function useSubmission(fn, filter) {
|
|
|
1075
1074
|
const submissions = useSubmissions(fn, filter);
|
|
1076
1075
|
return new Proxy({}, {
|
|
1077
1076
|
get(_, property) {
|
|
1077
|
+
if (submissions.length === 0 && property === "clear" || property === "retry") return () => {};
|
|
1078
1078
|
return submissions[submissions.length - 1]?.[property];
|
|
1079
1079
|
}
|
|
1080
1080
|
});
|
|
@@ -1232,7 +1232,9 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
|
|
|
1232
1232
|
if (typeof transformUrl === "function") {
|
|
1233
1233
|
url.pathname = transformUrl(url.pathname);
|
|
1234
1234
|
}
|
|
1235
|
-
if (!preloadTimeout[url.pathname]) router.preloadRoute(url,
|
|
1235
|
+
if (!preloadTimeout[url.pathname]) router.preloadRoute(url, {
|
|
1236
|
+
preloadData: a.getAttribute("preload") !== "false"
|
|
1237
|
+
});
|
|
1236
1238
|
}
|
|
1237
1239
|
function handleAnchorIn(evt) {
|
|
1238
1240
|
const res = handleAnchor(evt);
|
|
@@ -1243,7 +1245,9 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
|
|
|
1243
1245
|
}
|
|
1244
1246
|
if (preloadTimeout[url.pathname]) return;
|
|
1245
1247
|
preloadTimeout[url.pathname] = setTimeout(() => {
|
|
1246
|
-
router.preloadRoute(url,
|
|
1248
|
+
router.preloadRoute(url, {
|
|
1249
|
+
preloadData: a.getAttribute("preload") !== "false"
|
|
1250
|
+
});
|
|
1247
1251
|
delete preloadTimeout[url.pathname];
|
|
1248
1252
|
}, 200);
|
|
1249
1253
|
}
|
|
@@ -1328,7 +1332,7 @@ function Router(props) {
|
|
|
1328
1332
|
} else {
|
|
1329
1333
|
window.history.pushState(state, "", value);
|
|
1330
1334
|
}
|
|
1331
|
-
scrollToHash(window.location.hash.slice(1), scroll);
|
|
1335
|
+
scrollToHash(decodeURIComponent(window.location.hash.slice(1)), scroll);
|
|
1332
1336
|
saveCurrentDepth();
|
|
1333
1337
|
},
|
|
1334
1338
|
init: notify => bindEvent(window, "popstate", notifyIfNotBlocked(notify, delta => {
|
package/dist/routers/Router.js
CHANGED
|
@@ -23,7 +23,7 @@ export function Router(props) {
|
|
|
23
23
|
else {
|
|
24
24
|
window.history.pushState(state, "", value);
|
|
25
25
|
}
|
|
26
|
-
scrollToHash(window.location.hash.slice(1), scroll);
|
|
26
|
+
scrollToHash(decodeURIComponent(window.location.hash.slice(1)), scroll);
|
|
27
27
|
saveCurrentDepth();
|
|
28
28
|
},
|
|
29
29
|
init: notify => bindEvent(window, "popstate", notifyIfNotBlocked(notify, delta => {
|
package/dist/routing.d.ts
CHANGED
|
@@ -9,6 +9,9 @@ export declare const useHref: (to: () => string | undefined) => Accessor<string
|
|
|
9
9
|
export declare const useNavigate: () => Navigator;
|
|
10
10
|
export declare const useLocation: <S = unknown>() => Location<S>;
|
|
11
11
|
export declare const useIsRouting: () => () => boolean;
|
|
12
|
+
export declare const usePreloadRoute: () => (url: URL, options: {
|
|
13
|
+
preloadData?: boolean | undefined;
|
|
14
|
+
}) => void;
|
|
12
15
|
export declare const useMatch: <S extends string>(path: () => S, matchFilters?: MatchFilters<S> | undefined) => Accessor<import("./types.js").PathMatch | undefined>;
|
|
13
16
|
export declare const useCurrentMatches: () => () => RouteMatch[];
|
|
14
17
|
export declare const useParams: <T extends Params>() => T;
|
package/dist/routing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { runWithOwner } from "solid-js";
|
|
1
|
+
import { runWithOwner, batch } from "solid-js";
|
|
2
2
|
import { createComponent, createContext, createMemo, createRenderEffect, createSignal, on, onCleanup, untrack, useContext, startTransition, resetErrorBoundaries } from "solid-js";
|
|
3
3
|
import { isServer, getRequestEvent } from "solid-js/web";
|
|
4
4
|
import { createBeforeLeave } from "./lifecycle.js";
|
|
@@ -23,6 +23,7 @@ export const useHref = (to) => {
|
|
|
23
23
|
export const useNavigate = () => useRouter().navigatorFactory();
|
|
24
24
|
export const useLocation = () => useRouter().location;
|
|
25
25
|
export const useIsRouting = () => useRouter().isRouting;
|
|
26
|
+
export const usePreloadRoute = () => useRouter().preloadRoute;
|
|
26
27
|
export const useMatch = (path, matchFilters) => {
|
|
27
28
|
const location = useLocation();
|
|
28
29
|
const matchers = createMemo(() => expandOptionals(path()).map(path => createMatcher(path, undefined, matchFilters)));
|
|
@@ -205,14 +206,36 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
205
206
|
setSource({ value: basePath, replace: true, scroll: false });
|
|
206
207
|
}
|
|
207
208
|
const [isRouting, setIsRouting] = createSignal(false);
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
209
|
+
// Keep track of last target, so that last call to transition wins
|
|
210
|
+
let lastTransitionTarget;
|
|
211
|
+
// Transition the location to a new value
|
|
212
|
+
const transition = (newIntent, newTarget) => {
|
|
213
|
+
if (newTarget.value === reference() && newTarget.state === state())
|
|
214
|
+
return;
|
|
215
|
+
if (lastTransitionTarget === undefined)
|
|
216
|
+
setIsRouting(true);
|
|
217
|
+
intent = newIntent;
|
|
218
|
+
lastTransitionTarget = newTarget;
|
|
219
|
+
startTransition(() => {
|
|
220
|
+
if (lastTransitionTarget !== newTarget)
|
|
221
|
+
return;
|
|
222
|
+
setReference(lastTransitionTarget.value);
|
|
223
|
+
setState(lastTransitionTarget.state);
|
|
224
|
+
resetErrorBoundaries();
|
|
225
|
+
if (!isServer)
|
|
226
|
+
submissions[1]([]);
|
|
227
|
+
}).finally(() => {
|
|
228
|
+
if (lastTransitionTarget !== newTarget)
|
|
229
|
+
return;
|
|
230
|
+
// Batch, in order for isRouting and final source update to happen together
|
|
231
|
+
batch(() => {
|
|
232
|
+
intent = undefined;
|
|
233
|
+
if (newIntent === "navigate")
|
|
234
|
+
navigateEnd(lastTransitionTarget);
|
|
235
|
+
setIsRouting(false);
|
|
236
|
+
lastTransitionTarget = undefined;
|
|
237
|
+
});
|
|
238
|
+
});
|
|
216
239
|
};
|
|
217
240
|
const [reference, setReference] = createSignal(source().value);
|
|
218
241
|
const [state, setState] = createSignal(source().state);
|
|
@@ -241,22 +264,8 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
241
264
|
return resolvePath(basePath, to);
|
|
242
265
|
}
|
|
243
266
|
};
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
// Untrack this whole block so `start` doesn't cause Solid's Listener to be preserved
|
|
247
|
-
untrack(() => {
|
|
248
|
-
start(() => {
|
|
249
|
-
intent = "native";
|
|
250
|
-
if (value !== reference())
|
|
251
|
-
setReference(value);
|
|
252
|
-
setState(state);
|
|
253
|
-
resetErrorBoundaries();
|
|
254
|
-
submissions[1]([]);
|
|
255
|
-
}).then(() => {
|
|
256
|
-
intent = undefined;
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
});
|
|
267
|
+
// Create a native transition, when source updates
|
|
268
|
+
createRenderEffect(on(source, source => transition("native", source), { defer: true }));
|
|
260
269
|
return {
|
|
261
270
|
base: baseRoute,
|
|
262
271
|
location,
|
|
@@ -307,21 +316,10 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
307
316
|
setSource({ value: resolvedTo, replace, scroll, state: nextState });
|
|
308
317
|
}
|
|
309
318
|
else if (beforeLeave.confirm(resolvedTo, options)) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
setState(nextState);
|
|
315
|
-
resetErrorBoundaries();
|
|
316
|
-
submissions[1]([]);
|
|
317
|
-
}).then(() => {
|
|
318
|
-
if (referrers.length === len) {
|
|
319
|
-
intent = undefined;
|
|
320
|
-
navigateEnd({
|
|
321
|
-
value: resolvedTo,
|
|
322
|
-
state: nextState
|
|
323
|
-
});
|
|
324
|
-
}
|
|
319
|
+
referrers.push({ value: current, replace, scroll, state: state() });
|
|
320
|
+
transition("navigate", {
|
|
321
|
+
value: resolvedTo,
|
|
322
|
+
state: nextState
|
|
325
323
|
});
|
|
326
324
|
}
|
|
327
325
|
}
|
|
@@ -335,17 +333,15 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
335
333
|
function navigateEnd(next) {
|
|
336
334
|
const first = referrers[0];
|
|
337
335
|
if (first) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
});
|
|
344
|
-
}
|
|
336
|
+
setSource({
|
|
337
|
+
...next,
|
|
338
|
+
replace: first.replace,
|
|
339
|
+
scroll: first.scroll
|
|
340
|
+
});
|
|
345
341
|
referrers.length = 0;
|
|
346
342
|
}
|
|
347
343
|
}
|
|
348
|
-
function preloadRoute(url,
|
|
344
|
+
function preloadRoute(url, options = {}) {
|
|
349
345
|
const matches = getRouteMatches(branches(), url.pathname);
|
|
350
346
|
const prevIntent = intent;
|
|
351
347
|
intent = "preload";
|
|
@@ -356,7 +352,7 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
356
352
|
route.component.preload();
|
|
357
353
|
const { load } = route;
|
|
358
354
|
inLoadFn = true;
|
|
359
|
-
preloadData &&
|
|
355
|
+
options.preloadData &&
|
|
360
356
|
load &&
|
|
361
357
|
runWithOwner(getContext(), () => load({
|
|
362
358
|
params,
|
|
@@ -376,9 +372,7 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
376
372
|
}
|
|
377
373
|
function initFromFlash() {
|
|
378
374
|
const e = getRequestEvent();
|
|
379
|
-
return (e && e.router && e.router.submission
|
|
380
|
-
? [e.router.submission]
|
|
381
|
-
: []);
|
|
375
|
+
return (e && e.router && e.router.submission ? [e.router.submission] : []);
|
|
382
376
|
}
|
|
383
377
|
}
|
|
384
378
|
export function createRouteContext(router, parent, outlet, match) {
|
package/dist/types.d.ts
CHANGED
|
@@ -134,7 +134,9 @@ export interface RouterContext {
|
|
|
134
134
|
renderPath(path: string): string;
|
|
135
135
|
parsePath(str: string): string;
|
|
136
136
|
beforeLeave: BeforeLeaveLifecycle;
|
|
137
|
-
preloadRoute: (url: URL,
|
|
137
|
+
preloadRoute: (url: URL, options: {
|
|
138
|
+
preloadData?: boolean;
|
|
139
|
+
}) => void;
|
|
138
140
|
singleFlight: boolean;
|
|
139
141
|
submissions: Signal<Submission<any, any>[]>;
|
|
140
142
|
}
|
|
@@ -164,6 +166,15 @@ export type Submission<T, U> = {
|
|
|
164
166
|
clear: () => void;
|
|
165
167
|
retry: () => void;
|
|
166
168
|
};
|
|
169
|
+
export type SubmissionStub = {
|
|
170
|
+
readonly input: undefined;
|
|
171
|
+
readonly result: undefined;
|
|
172
|
+
readonly error: undefined;
|
|
173
|
+
readonly pending: undefined;
|
|
174
|
+
readonly url: undefined;
|
|
175
|
+
clear: () => void;
|
|
176
|
+
retry: () => void;
|
|
177
|
+
};
|
|
167
178
|
export interface MaybePreloadableComponent extends Component {
|
|
168
179
|
preload?: () => void;
|
|
169
180
|
}
|