@solidjs/router 0.10.9 → 0.11.0
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 +8 -8
- package/dist/components.d.ts +1 -1
- package/dist/components.jsx +7 -7
- package/dist/data/action.d.ts +1 -1
- package/dist/data/action.js +4 -4
- package/dist/data/cache.d.ts +2 -4
- package/dist/data/cache.js +56 -52
- package/dist/data/events.d.ts +1 -1
- package/dist/data/events.js +7 -5
- package/dist/data/index.d.ts +4 -4
- package/dist/data/index.js +4 -4
- package/dist/data/response.d.ts +3 -2
- package/dist/data/response.js +13 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +147 -82
- package/dist/index.jsx +6 -6
- package/dist/lifecycle.d.ts +4 -1
- package/dist/lifecycle.js +37 -0
- package/dist/routers/HashRouter.d.ts +1 -1
- package/dist/routers/HashRouter.js +11 -6
- package/dist/routers/MemoryRouter.d.ts +2 -2
- package/dist/routers/MemoryRouter.js +1 -1
- package/dist/routers/Router.d.ts +1 -1
- package/dist/routers/Router.js +23 -10
- package/dist/routers/StaticRouter.d.ts +1 -1
- package/dist/routers/StaticRouter.js +1 -1
- package/dist/routers/components.d.ts +1 -1
- package/dist/routers/components.jsx +10 -9
- package/dist/routers/createRouter.d.ts +2 -2
- package/dist/routers/createRouter.js +1 -1
- package/dist/routers/index.d.ts +11 -11
- package/dist/routers/index.js +6 -6
- package/dist/routing.d.ts +2 -2
- package/dist/routing.js +12 -10
- package/dist/types.d.ts +13 -4
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +1 -0
- package/package.json +18 -21
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isServer, getRequestEvent, createComponent as createComponent$1, 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, createRoot, Show, sharedConfig, $TRACK, splitProps, createResource } from 'solid-js';
|
|
2
|
+
import { getOwner, runWithOwner, createMemo, createContext, onCleanup, useContext, untrack, createSignal, createRenderEffect, on, startTransition, resetErrorBoundaries, createComponent, children, mergeProps, createRoot, Show, getListener, sharedConfig, $TRACK, splitProps, createResource } from 'solid-js';
|
|
3
3
|
import { createStore, reconcile } from 'solid-js/store';
|
|
4
4
|
|
|
5
5
|
function createBeforeLeave() {
|
|
@@ -36,8 +36,49 @@ function createBeforeLeave() {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// The following supports browser initiated blocking (eg back/forward)
|
|
40
|
+
|
|
41
|
+
let depth;
|
|
42
|
+
function saveCurrentDepth() {
|
|
43
|
+
if (!window.history.state || window.history.state._depth == null) {
|
|
44
|
+
window.history.replaceState({
|
|
45
|
+
...window.history.state,
|
|
46
|
+
_depth: window.history.length - 1
|
|
47
|
+
}, "");
|
|
48
|
+
}
|
|
49
|
+
depth = window.history.state._depth;
|
|
50
|
+
}
|
|
51
|
+
if (!isServer) {
|
|
52
|
+
saveCurrentDepth();
|
|
53
|
+
}
|
|
54
|
+
function keepDepth(state) {
|
|
55
|
+
return {
|
|
56
|
+
...state,
|
|
57
|
+
_depth: window.history.state && window.history.state._depth
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function notifyIfNotBlocked(notify, block) {
|
|
61
|
+
let ignore = false;
|
|
62
|
+
return () => {
|
|
63
|
+
const prevDepth = depth;
|
|
64
|
+
saveCurrentDepth();
|
|
65
|
+
const delta = prevDepth == null ? null : depth - prevDepth;
|
|
66
|
+
if (ignore) {
|
|
67
|
+
ignore = false;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (delta && block(delta)) {
|
|
71
|
+
ignore = true;
|
|
72
|
+
window.history.go(-delta);
|
|
73
|
+
} else {
|
|
74
|
+
notify();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
39
79
|
const hasSchemeRegex = /^(?:[a-z0-9]+:)?\/\//i;
|
|
40
80
|
const trimPathRegex = /^\/+|(\/)\/+$/g;
|
|
81
|
+
const mockBase = "http://sr";
|
|
41
82
|
const redirectStatusCodes = new Set([204, 301, 302, 303, 307, 308]);
|
|
42
83
|
function normalizePath(path, omitSlash = false) {
|
|
43
84
|
const s = path.replace(trimPathRegex, "$1");
|
|
@@ -257,7 +298,7 @@ function createRoutes(routeDef, base = "") {
|
|
|
257
298
|
const path = joinPaths(base, originalPath);
|
|
258
299
|
let pattern = isLeaf ? path : path.split("/*", 1)[0];
|
|
259
300
|
pattern = pattern.split("/").map(s => {
|
|
260
|
-
return s.startsWith(
|
|
301
|
+
return s.startsWith(":") || s.startsWith("*") ? s : encodeURIComponent(s);
|
|
261
302
|
}).join("/");
|
|
262
303
|
acc.push({
|
|
263
304
|
...shared,
|
|
@@ -327,7 +368,7 @@ function getRouteMatches(branches, location) {
|
|
|
327
368
|
return [];
|
|
328
369
|
}
|
|
329
370
|
function createLocation(path, state) {
|
|
330
|
-
const origin = new URL(
|
|
371
|
+
const origin = new URL(mockBase);
|
|
331
372
|
const url = createMemo(prev => {
|
|
332
373
|
const path_ = path();
|
|
333
374
|
try {
|
|
@@ -443,7 +484,7 @@ function createRouterContext(integration, getBranches, options = {}) {
|
|
|
443
484
|
untrack(() => {
|
|
444
485
|
if (typeof to === "number") {
|
|
445
486
|
if (!to) ; else if (utils.go) {
|
|
446
|
-
|
|
487
|
+
utils.go(to);
|
|
447
488
|
} else {
|
|
448
489
|
console.warn("Router integration does not support relative routing");
|
|
449
490
|
}
|
|
@@ -470,12 +511,12 @@ function createRouterContext(integration, getBranches, options = {}) {
|
|
|
470
511
|
if (resolvedTo !== current || nextState !== state()) {
|
|
471
512
|
if (isServer) {
|
|
472
513
|
const e = getRequestEvent();
|
|
473
|
-
e && (e.response =
|
|
514
|
+
e && (e.response = {
|
|
474
515
|
status: 302,
|
|
475
|
-
headers: {
|
|
516
|
+
headers: new Headers({
|
|
476
517
|
Location: resolvedTo
|
|
477
|
-
}
|
|
478
|
-
})
|
|
518
|
+
})
|
|
519
|
+
});
|
|
479
520
|
setSource({
|
|
480
521
|
value: resolvedTo,
|
|
481
522
|
replace,
|
|
@@ -553,7 +594,7 @@ function createRouterContext(integration, getBranches, options = {}) {
|
|
|
553
594
|
}
|
|
554
595
|
function initFromFlash() {
|
|
555
596
|
const e = getRequestEvent();
|
|
556
|
-
return e && e.
|
|
597
|
+
return e && e.router && e.router.submission ? [e.router.submission] : [];
|
|
557
598
|
}
|
|
558
599
|
}
|
|
559
600
|
function createRouteContext(router, parent, outlet, match, params) {
|
|
@@ -622,7 +663,7 @@ function Routes(props) {
|
|
|
622
663
|
const matches = createMemo(() => getRouteMatches(props.branches, props.routerState.location.pathname));
|
|
623
664
|
if (isServer) {
|
|
624
665
|
const e = getRequestEvent();
|
|
625
|
-
e && (e.
|
|
666
|
+
e && ((e.router || (e.router = {})).matches || (e.router.matches = matches().map(({
|
|
626
667
|
route,
|
|
627
668
|
path,
|
|
628
669
|
params
|
|
@@ -632,7 +673,7 @@ function Routes(props) {
|
|
|
632
673
|
match: path,
|
|
633
674
|
params,
|
|
634
675
|
metadata: route.metadata
|
|
635
|
-
})));
|
|
676
|
+
}))));
|
|
636
677
|
}
|
|
637
678
|
const params = createMemoObject(() => {
|
|
638
679
|
const m = matches();
|
|
@@ -779,7 +820,7 @@ if (!isServer) {
|
|
|
779
820
|
setInterval(() => {
|
|
780
821
|
const now = Date.now();
|
|
781
822
|
for (let [k, v] of cacheMap.entries()) {
|
|
782
|
-
if (!v[3].
|
|
823
|
+
if (!v[3].count && now - v[0] > CACHE_TIMEOUT) {
|
|
783
824
|
cacheMap.delete(k);
|
|
784
825
|
}
|
|
785
826
|
}
|
|
@@ -787,31 +828,29 @@ if (!isServer) {
|
|
|
787
828
|
}
|
|
788
829
|
function getCache() {
|
|
789
830
|
if (!isServer) return cacheMap;
|
|
790
|
-
const req = getRequestEvent()
|
|
831
|
+
const req = getRequestEvent();
|
|
791
832
|
if (!req) throw new Error("Cannot find cache context");
|
|
792
|
-
return req.
|
|
833
|
+
return (req.router || (req.router = {})).cache || (req.router.cache = new Map());
|
|
793
834
|
}
|
|
794
835
|
function revalidate(key, force = true) {
|
|
795
836
|
return startTransition(() => {
|
|
796
837
|
const now = Date.now();
|
|
797
838
|
cacheKeyOp(key, entry => {
|
|
798
839
|
force && (entry[0] = 0); //force cache miss
|
|
799
|
-
|
|
840
|
+
entry[3][1](now); // retrigger live signals
|
|
800
841
|
});
|
|
801
842
|
});
|
|
802
843
|
}
|
|
803
|
-
|
|
804
844
|
function cacheKeyOp(key, fn) {
|
|
805
845
|
key && !Array.isArray(key) && (key = [key]);
|
|
806
846
|
for (let k of cacheMap.keys()) {
|
|
807
847
|
if (key === undefined || matchKey(k, key)) fn(cacheMap.get(k));
|
|
808
848
|
}
|
|
809
849
|
}
|
|
810
|
-
function revalidateSignals(set, time) {
|
|
811
|
-
for (let s of set) s[1](time);
|
|
812
|
-
}
|
|
813
850
|
function cache(fn, name, options) {
|
|
814
851
|
const [store, setStore] = createStore({});
|
|
852
|
+
// prioritize GET for server functions
|
|
853
|
+
if (fn.GET) fn = fn.GET;
|
|
815
854
|
const cachedFn = (...args) => {
|
|
816
855
|
const cache = getCache();
|
|
817
856
|
const intent = getIntent();
|
|
@@ -820,27 +859,24 @@ function cache(fn, name, options) {
|
|
|
820
859
|
const now = Date.now();
|
|
821
860
|
const key = name + hashKey(args);
|
|
822
861
|
let cached = cache.get(key);
|
|
823
|
-
let
|
|
824
|
-
if (
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
});
|
|
828
|
-
|
|
829
|
-
onCleanup(() => cached[3].delete(version));
|
|
830
|
-
version[0](); // track it;
|
|
862
|
+
let tracking;
|
|
863
|
+
if (getListener() && !isServer) {
|
|
864
|
+
tracking = true;
|
|
865
|
+
onCleanup(() => cached[3].count--);
|
|
831
866
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
867
|
+
if (cached && (isServer || intent === "native" || cached[0] && cached[3].count || Date.now() - cached[0] < PRELOAD_TIMEOUT)) {
|
|
868
|
+
if (tracking) {
|
|
869
|
+
cached[3].count++;
|
|
870
|
+
cached[3][0](); // track
|
|
871
|
+
}
|
|
835
872
|
if (cached[2] === "preload" && intent !== "preload") {
|
|
836
873
|
cached[0] = now;
|
|
837
874
|
}
|
|
838
875
|
let res = cached[1];
|
|
839
876
|
if (intent !== "preload") {
|
|
840
877
|
res = "then" in cached[1] ? cached[1].then(handleResponse(false), handleResponse(true)) : handleResponse(false)(cached[1]);
|
|
841
|
-
!isServer && intent === "navigate" && startTransition(() =>
|
|
878
|
+
!isServer && intent === "navigate" && startTransition(() => cached[3][1](cached[0])); // update version
|
|
842
879
|
}
|
|
843
|
-
|
|
844
880
|
return res;
|
|
845
881
|
}
|
|
846
882
|
let res = !isServer && sharedConfig.context && sharedConfig.load ? sharedConfig.load(key) // hydrating
|
|
@@ -855,31 +891,38 @@ function cache(fn, name, options) {
|
|
|
855
891
|
cached[0] = now;
|
|
856
892
|
cached[1] = res;
|
|
857
893
|
cached[2] = intent;
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
}
|
|
894
|
+
!isServer && intent === "navigate" && startTransition(() => cached[3][1](cached[0])); // update version
|
|
895
|
+
} else {
|
|
896
|
+
cache.set(key, cached = [now, res, intent, createSignal(now)]);
|
|
897
|
+
cached[3].count = 0;
|
|
898
|
+
}
|
|
899
|
+
if (tracking) {
|
|
900
|
+
cached[3].count++;
|
|
901
|
+
cached[3][0](); // track
|
|
902
|
+
}
|
|
863
903
|
if (intent !== "preload") {
|
|
864
904
|
res = "then" in res ? res.then(handleResponse(false), handleResponse(true)) : handleResponse(false)(res);
|
|
865
905
|
}
|
|
866
906
|
return res;
|
|
867
907
|
function handleResponse(error) {
|
|
868
|
-
return v => {
|
|
869
|
-
if (v instanceof Response
|
|
870
|
-
if (
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
908
|
+
return async v => {
|
|
909
|
+
if (v instanceof Response) {
|
|
910
|
+
if (redirectStatusCodes.has(v.status)) {
|
|
911
|
+
if (navigate) {
|
|
912
|
+
startTransition(() => {
|
|
913
|
+
let url = v.headers.get(LocationHeader);
|
|
914
|
+
if (url && url.startsWith("/")) {
|
|
915
|
+
navigate(url, {
|
|
916
|
+
replace: true
|
|
917
|
+
});
|
|
918
|
+
} else if (!isServer && url) {
|
|
919
|
+
window.location.href = url;
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
return;
|
|
881
924
|
}
|
|
882
|
-
|
|
925
|
+
if (v.customBody) v = await v.customBody();
|
|
883
926
|
}
|
|
884
927
|
if (error) throw v;
|
|
885
928
|
if (isServer) return v;
|
|
@@ -896,20 +939,14 @@ cache.set = (key, value) => {
|
|
|
896
939
|
const cache = getCache();
|
|
897
940
|
const now = Date.now();
|
|
898
941
|
let cached = cache.get(key);
|
|
899
|
-
let version;
|
|
900
|
-
if (getOwner()) {
|
|
901
|
-
version = createSignal(now, {
|
|
902
|
-
equals: (p, v) => v - p < 50 // margin of error
|
|
903
|
-
});
|
|
904
|
-
|
|
905
|
-
onCleanup(() => cached[3].delete(version));
|
|
906
|
-
}
|
|
907
942
|
if (cached) {
|
|
908
943
|
cached[0] = now;
|
|
909
944
|
cached[1] = value;
|
|
910
945
|
cached[2] = "preload";
|
|
911
|
-
|
|
912
|
-
|
|
946
|
+
} else {
|
|
947
|
+
cache.set(key, cached = [now, value,, createSignal(now)]);
|
|
948
|
+
cached[3].count = 0;
|
|
949
|
+
}
|
|
913
950
|
};
|
|
914
951
|
cache.clear = () => getCache().clear();
|
|
915
952
|
function matchKey(key, keys) {
|
|
@@ -1003,7 +1040,7 @@ function toAction(fn, url) {
|
|
|
1003
1040
|
const newFn = function (...passedArgs) {
|
|
1004
1041
|
return fn.call(this, ...args, ...passedArgs);
|
|
1005
1042
|
};
|
|
1006
|
-
const uri = new URL(url,
|
|
1043
|
+
const uri = new URL(url, mockBase);
|
|
1007
1044
|
uri.searchParams.set("args", hashKey(args));
|
|
1008
1045
|
return toAction(newFn, (uri.origin === "https://action" ? uri.origin : "") + uri.pathname + uri.search);
|
|
1009
1046
|
};
|
|
@@ -1048,7 +1085,7 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
|
|
|
1048
1085
|
function handleAnchor(evt) {
|
|
1049
1086
|
if (evt.defaultPrevented || evt.button !== 0 || evt.metaKey || evt.altKey || evt.ctrlKey || evt.shiftKey) return;
|
|
1050
1087
|
const a = evt.composedPath().find(el => el instanceof Node && el.nodeName.toUpperCase() === "A");
|
|
1051
|
-
if (!a || explicitLinks && !a.
|
|
1088
|
+
if (!a || explicitLinks && !a.hasAttribute("link")) return;
|
|
1052
1089
|
const svg = isSvg(a);
|
|
1053
1090
|
const href = svg ? a.href.baseVal : a.href;
|
|
1054
1091
|
const target = svg ? a.target.baseVal : a.target;
|
|
@@ -1099,10 +1136,11 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
|
|
|
1099
1136
|
}
|
|
1100
1137
|
}
|
|
1101
1138
|
function handleFormSubmit(evt) {
|
|
1102
|
-
let actionRef = evt.submitter && evt.submitter.hasAttribute("formaction") ? evt.submitter.
|
|
1139
|
+
let actionRef = evt.submitter && evt.submitter.hasAttribute("formaction") ? evt.submitter.getAttribute("formaction") : evt.target.getAttribute("action");
|
|
1103
1140
|
if (!actionRef) return;
|
|
1104
1141
|
if (!actionRef.startsWith("https://action/")) {
|
|
1105
|
-
|
|
1142
|
+
// normalize server actions
|
|
1143
|
+
const url = new URL(actionRef, mockBase);
|
|
1106
1144
|
actionRef = router.parsePath(url.pathname + url.search);
|
|
1107
1145
|
if (!actionRef.startsWith(actionBase)) return;
|
|
1108
1146
|
}
|
|
@@ -1141,11 +1179,13 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
|
|
|
1141
1179
|
|
|
1142
1180
|
function Router(props) {
|
|
1143
1181
|
if (isServer) return StaticRouter(props);
|
|
1182
|
+
const getSource = () => ({
|
|
1183
|
+
value: window.location.pathname + window.location.search + window.location.hash,
|
|
1184
|
+
state: window.history.state
|
|
1185
|
+
});
|
|
1186
|
+
const beforeLeave = createBeforeLeave();
|
|
1144
1187
|
return createRouter({
|
|
1145
|
-
get:
|
|
1146
|
-
value: window.location.pathname + window.location.search + window.location.hash,
|
|
1147
|
-
state: history.state
|
|
1148
|
-
}),
|
|
1188
|
+
get: getSource,
|
|
1149
1189
|
set({
|
|
1150
1190
|
value,
|
|
1151
1191
|
replace,
|
|
@@ -1153,16 +1193,27 @@ function Router(props) {
|
|
|
1153
1193
|
state
|
|
1154
1194
|
}) {
|
|
1155
1195
|
if (replace) {
|
|
1156
|
-
window.history.replaceState(state, "", value);
|
|
1196
|
+
window.history.replaceState(keepDepth(state), "", value);
|
|
1157
1197
|
} else {
|
|
1158
1198
|
window.history.pushState(state, "", value);
|
|
1159
1199
|
}
|
|
1160
1200
|
scrollToHash(window.location.hash.slice(1), scroll);
|
|
1201
|
+
saveCurrentDepth();
|
|
1161
1202
|
},
|
|
1162
|
-
init: notify => bindEvent(window, "popstate", (
|
|
1203
|
+
init: notify => bindEvent(window, "popstate", notifyIfNotBlocked(notify, delta => {
|
|
1204
|
+
if (delta && delta < 0) {
|
|
1205
|
+
return !beforeLeave.confirm(delta);
|
|
1206
|
+
} else {
|
|
1207
|
+
const s = getSource();
|
|
1208
|
+
return !beforeLeave.confirm(s.value, {
|
|
1209
|
+
state: s.state
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
})),
|
|
1163
1213
|
create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
|
|
1164
1214
|
utils: {
|
|
1165
|
-
go: delta => window.history.go(delta)
|
|
1215
|
+
go: delta => window.history.go(delta),
|
|
1216
|
+
beforeLeave
|
|
1166
1217
|
}
|
|
1167
1218
|
})(props);
|
|
1168
1219
|
}
|
|
@@ -1179,8 +1230,10 @@ function hashParser(str) {
|
|
|
1179
1230
|
return to;
|
|
1180
1231
|
}
|
|
1181
1232
|
function HashRouter(props) {
|
|
1233
|
+
const getSource = () => window.location.hash.slice(1);
|
|
1234
|
+
const beforeLeave = createBeforeLeave();
|
|
1182
1235
|
return createRouter({
|
|
1183
|
-
get:
|
|
1236
|
+
get: getSource,
|
|
1184
1237
|
set({
|
|
1185
1238
|
value,
|
|
1186
1239
|
replace,
|
|
@@ -1188,20 +1241,22 @@ function HashRouter(props) {
|
|
|
1188
1241
|
state
|
|
1189
1242
|
}) {
|
|
1190
1243
|
if (replace) {
|
|
1191
|
-
window.history.replaceState(state, "", "#" + value);
|
|
1244
|
+
window.history.replaceState(keepDepth(state), "", "#" + value);
|
|
1192
1245
|
} else {
|
|
1193
1246
|
window.location.hash = value;
|
|
1194
1247
|
}
|
|
1195
1248
|
const hashIndex = value.indexOf("#");
|
|
1196
1249
|
const hash = hashIndex >= 0 ? value.slice(hashIndex + 1) : "";
|
|
1197
1250
|
scrollToHash(hash, scroll);
|
|
1251
|
+
saveCurrentDepth();
|
|
1198
1252
|
},
|
|
1199
|
-
init: notify => bindEvent(window, "hashchange", (
|
|
1253
|
+
init: notify => bindEvent(window, "hashchange", notifyIfNotBlocked(notify, delta => !beforeLeave.confirm(delta && delta < 0 ? delta : getSource()))),
|
|
1200
1254
|
create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
|
|
1201
1255
|
utils: {
|
|
1202
1256
|
go: delta => window.history.go(delta),
|
|
1203
1257
|
renderPath: path => `#${path}`,
|
|
1204
|
-
parsePath: hashParser
|
|
1258
|
+
parsePath: hashParser,
|
|
1259
|
+
beforeLeave
|
|
1205
1260
|
}
|
|
1206
1261
|
})(props);
|
|
1207
1262
|
}
|
|
@@ -1273,10 +1328,10 @@ function A(props) {
|
|
|
1273
1328
|
const location = useLocation();
|
|
1274
1329
|
const isActive = createMemo(() => {
|
|
1275
1330
|
const to_ = to();
|
|
1276
|
-
if (to_ === undefined) return false;
|
|
1331
|
+
if (to_ === undefined) return [false, false];
|
|
1277
1332
|
const path = normalizePath(to_.split(/[?#]/, 1)[0]).toLowerCase();
|
|
1278
1333
|
const loc = normalizePath(location.pathname).toLowerCase();
|
|
1279
|
-
return props.end ? path === loc : loc.startsWith(path);
|
|
1334
|
+
return [props.end ? path === loc : loc.startsWith(path), path === loc];
|
|
1280
1335
|
});
|
|
1281
1336
|
return (() => {
|
|
1282
1337
|
const _el$ = _tmpl$();
|
|
@@ -1292,14 +1347,14 @@ function A(props) {
|
|
|
1292
1347
|
...(props.class && {
|
|
1293
1348
|
[props.class]: true
|
|
1294
1349
|
}),
|
|
1295
|
-
[props.inactiveClass]: !isActive(),
|
|
1296
|
-
[props.activeClass]: isActive(),
|
|
1350
|
+
[props.inactiveClass]: !isActive()[0],
|
|
1351
|
+
[props.activeClass]: isActive()[0],
|
|
1297
1352
|
...rest.classList
|
|
1298
1353
|
};
|
|
1299
1354
|
},
|
|
1300
1355
|
"link": "",
|
|
1301
1356
|
get ["aria-current"]() {
|
|
1302
|
-
return isActive() ? "page" : undefined;
|
|
1357
|
+
return isActive()[1] ? "page" : undefined;
|
|
1303
1358
|
}
|
|
1304
1359
|
}), false, false);
|
|
1305
1360
|
return _el$;
|
|
@@ -1412,5 +1467,15 @@ function reload(init) {
|
|
|
1412
1467
|
} : {})
|
|
1413
1468
|
});
|
|
1414
1469
|
}
|
|
1470
|
+
function json(data, init) {
|
|
1471
|
+
const headers = new Headers((init || {}).headers);
|
|
1472
|
+
headers.set("Content-Type", "application/json");
|
|
1473
|
+
const response = new Response(JSON.stringify(data), {
|
|
1474
|
+
...init,
|
|
1475
|
+
headers
|
|
1476
|
+
});
|
|
1477
|
+
response.customBody = () => data;
|
|
1478
|
+
return response;
|
|
1479
|
+
}
|
|
1415
1480
|
|
|
1416
|
-
export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createMemoryHistory, createRouter, redirect, reload, revalidate, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
|
|
1481
|
+
export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
|
package/dist/index.jsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export * from "./routers";
|
|
2
|
-
export * from "./components";
|
|
3
|
-
export * from "./lifecycle";
|
|
4
|
-
export { useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing";
|
|
5
|
-
export { mergeSearchString as _mergeSearchString } from "./utils";
|
|
6
|
-
export * from "./data";
|
|
1
|
+
export * from "./routers/index.js";
|
|
2
|
+
export * from "./components.jsx";
|
|
3
|
+
export * from "./lifecycle.js";
|
|
4
|
+
export { useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
|
|
5
|
+
export { mergeSearchString as _mergeSearchString } from "./utils.js";
|
|
6
|
+
export * from "./data/index.js";
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
import { BeforeLeaveLifecycle } from "./types";
|
|
1
|
+
import { BeforeLeaveLifecycle, LocationChange } from "./types.js";
|
|
2
2
|
export declare function createBeforeLeave(): BeforeLeaveLifecycle;
|
|
3
|
+
export declare function saveCurrentDepth(): void;
|
|
4
|
+
export declare function keepDepth(state: any): any;
|
|
5
|
+
export declare function notifyIfNotBlocked(notify: (value?: string | LocationChange) => void, block: (delta: number | null) => boolean): () => void;
|
package/dist/lifecycle.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isServer } from "solid-js/web";
|
|
1
2
|
export function createBeforeLeave() {
|
|
2
3
|
let listeners = new Set();
|
|
3
4
|
function subscribe(listener) {
|
|
@@ -30,3 +31,39 @@ export function createBeforeLeave() {
|
|
|
30
31
|
confirm
|
|
31
32
|
};
|
|
32
33
|
}
|
|
34
|
+
// The following supports browser initiated blocking (eg back/forward)
|
|
35
|
+
let depth;
|
|
36
|
+
export function saveCurrentDepth() {
|
|
37
|
+
if (!window.history.state || window.history.state._depth == null) {
|
|
38
|
+
window.history.replaceState({ ...window.history.state, _depth: window.history.length - 1 }, "");
|
|
39
|
+
}
|
|
40
|
+
depth = window.history.state._depth;
|
|
41
|
+
}
|
|
42
|
+
if (!isServer) {
|
|
43
|
+
saveCurrentDepth();
|
|
44
|
+
}
|
|
45
|
+
export function keepDepth(state) {
|
|
46
|
+
return {
|
|
47
|
+
...state,
|
|
48
|
+
_depth: window.history.state && window.history.state._depth
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function notifyIfNotBlocked(notify, block) {
|
|
52
|
+
let ignore = false;
|
|
53
|
+
return () => {
|
|
54
|
+
const prevDepth = depth;
|
|
55
|
+
saveCurrentDepth();
|
|
56
|
+
const delta = prevDepth == null ? null : depth - prevDepth;
|
|
57
|
+
if (ignore) {
|
|
58
|
+
ignore = false;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (delta && block(delta)) {
|
|
62
|
+
ignore = true;
|
|
63
|
+
window.history.go(-delta);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
notify();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JSX } from "solid-js";
|
|
2
|
-
import type { BaseRouterProps } from "./components";
|
|
2
|
+
import type { BaseRouterProps } from "./components.js";
|
|
3
3
|
export declare function hashParser(str: string): string;
|
|
4
4
|
export type HashRouterProps = BaseRouterProps & {
|
|
5
5
|
actionBase?: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { setupNativeEvents } from "../data/events";
|
|
2
|
-
import { createRouter, scrollToHash, bindEvent } from "./createRouter";
|
|
1
|
+
import { setupNativeEvents } from "../data/events.js";
|
|
2
|
+
import { createRouter, scrollToHash, bindEvent } from "./createRouter.js";
|
|
3
|
+
import { createBeforeLeave, keepDepth, notifyIfNotBlocked, saveCurrentDepth } from "../lifecycle.js";
|
|
3
4
|
export function hashParser(str) {
|
|
4
5
|
const to = str.replace(/^.*?#/, "");
|
|
5
6
|
// Hash-only hrefs like `#foo` from plain anchors will come in as `/#foo` whereas a link to
|
|
@@ -12,11 +13,13 @@ export function hashParser(str) {
|
|
|
12
13
|
return to;
|
|
13
14
|
}
|
|
14
15
|
export function HashRouter(props) {
|
|
16
|
+
const getSource = () => window.location.hash.slice(1);
|
|
17
|
+
const beforeLeave = createBeforeLeave();
|
|
15
18
|
return createRouter({
|
|
16
|
-
get:
|
|
19
|
+
get: getSource,
|
|
17
20
|
set({ value, replace, scroll, state }) {
|
|
18
21
|
if (replace) {
|
|
19
|
-
window.history.replaceState(state, "", "#" + value);
|
|
22
|
+
window.history.replaceState(keepDepth(state), "", "#" + value);
|
|
20
23
|
}
|
|
21
24
|
else {
|
|
22
25
|
window.location.hash = value;
|
|
@@ -24,13 +27,15 @@ export function HashRouter(props) {
|
|
|
24
27
|
const hashIndex = value.indexOf("#");
|
|
25
28
|
const hash = hashIndex >= 0 ? value.slice(hashIndex + 1) : "";
|
|
26
29
|
scrollToHash(hash, scroll);
|
|
30
|
+
saveCurrentDepth();
|
|
27
31
|
},
|
|
28
|
-
init: notify => bindEvent(window, "hashchange", (
|
|
32
|
+
init: notify => bindEvent(window, "hashchange", notifyIfNotBlocked(notify, delta => !beforeLeave.confirm(delta && delta < 0 ? delta : getSource()))),
|
|
29
33
|
create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
|
|
30
34
|
utils: {
|
|
31
35
|
go: delta => window.history.go(delta),
|
|
32
36
|
renderPath: path => `#${path}`,
|
|
33
|
-
parsePath: hashParser
|
|
37
|
+
parsePath: hashParser,
|
|
38
|
+
beforeLeave
|
|
34
39
|
}
|
|
35
40
|
})(props);
|
|
36
41
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { LocationChange } from "../types";
|
|
2
|
-
import type { BaseRouterProps } from "./components";
|
|
1
|
+
import type { LocationChange } from "../types.js";
|
|
2
|
+
import type { BaseRouterProps } from "./components.jsx";
|
|
3
3
|
import type { JSX } from "solid-js";
|
|
4
4
|
export type MemoryHistory = {
|
|
5
5
|
get: () => string;
|
package/dist/routers/Router.d.ts
CHANGED
package/dist/routers/Router.js
CHANGED
|
@@ -1,28 +1,41 @@
|
|
|
1
1
|
import { isServer } from "solid-js/web";
|
|
2
|
-
import { createRouter, scrollToHash, bindEvent } from "./createRouter";
|
|
3
|
-
import { StaticRouter } from "./StaticRouter";
|
|
4
|
-
import { setupNativeEvents } from "../data/events";
|
|
2
|
+
import { createRouter, scrollToHash, bindEvent } from "./createRouter.js";
|
|
3
|
+
import { StaticRouter } from "./StaticRouter.js";
|
|
4
|
+
import { setupNativeEvents } from "../data/events.js";
|
|
5
|
+
import { createBeforeLeave, keepDepth, notifyIfNotBlocked, saveCurrentDepth } from "../lifecycle.js";
|
|
5
6
|
export function Router(props) {
|
|
6
7
|
if (isServer)
|
|
7
8
|
return StaticRouter(props);
|
|
9
|
+
const getSource = () => ({
|
|
10
|
+
value: window.location.pathname + window.location.search + window.location.hash,
|
|
11
|
+
state: window.history.state
|
|
12
|
+
});
|
|
13
|
+
const beforeLeave = createBeforeLeave();
|
|
8
14
|
return createRouter({
|
|
9
|
-
get:
|
|
10
|
-
value: window.location.pathname + window.location.search + window.location.hash,
|
|
11
|
-
state: history.state
|
|
12
|
-
}),
|
|
15
|
+
get: getSource,
|
|
13
16
|
set({ value, replace, scroll, state }) {
|
|
14
17
|
if (replace) {
|
|
15
|
-
window.history.replaceState(state, "", value);
|
|
18
|
+
window.history.replaceState(keepDepth(state), "", value);
|
|
16
19
|
}
|
|
17
20
|
else {
|
|
18
21
|
window.history.pushState(state, "", value);
|
|
19
22
|
}
|
|
20
23
|
scrollToHash(window.location.hash.slice(1), scroll);
|
|
24
|
+
saveCurrentDepth();
|
|
21
25
|
},
|
|
22
|
-
init: notify => bindEvent(window, "popstate", (
|
|
26
|
+
init: notify => bindEvent(window, "popstate", notifyIfNotBlocked(notify, delta => {
|
|
27
|
+
if (delta && delta < 0) {
|
|
28
|
+
return !beforeLeave.confirm(delta);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const s = getSource();
|
|
32
|
+
return !beforeLeave.confirm(s.value, { state: s.state });
|
|
33
|
+
}
|
|
34
|
+
})),
|
|
23
35
|
create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
|
|
24
36
|
utils: {
|
|
25
|
-
go: delta => window.history.go(delta)
|
|
37
|
+
go: delta => window.history.go(delta),
|
|
38
|
+
beforeLeave
|
|
26
39
|
}
|
|
27
40
|
})(props);
|
|
28
41
|
}
|