@keenmate/svelte-spa-router 1.0.2 → 1.0.4
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/.editorconfig +3 -0
- package/Router.svelte +94 -81
- package/package.json +1 -1
package/.editorconfig
CHANGED
|
@@ -7,6 +7,9 @@ trim_trailing_whitespace = true
|
|
|
7
7
|
charset = utf-8
|
|
8
8
|
ij_javascript_use_semicolon_after_statement = false
|
|
9
9
|
ij_typescript_use_semicolon_after_statement = false
|
|
10
|
+
ij_html_quote_style = double
|
|
11
|
+
ij_javascript_use_double_quotes = true
|
|
12
|
+
ij_typescript_use_double_quotes = true
|
|
10
13
|
|
|
11
14
|
[*.{xaml,cshtml,html,xml}]
|
|
12
15
|
indent_style = tab
|
package/Router.svelte
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script context="module">
|
|
2
|
-
import {derived, get, readable, writable} from
|
|
3
|
-
import {tick} from
|
|
4
|
-
import {SvelteSPARouterNavigationEvent} from
|
|
5
|
-
import {joinPaths} from
|
|
2
|
+
import {derived, get, readable, writable} from "svelte/store"
|
|
3
|
+
import {tick} from "svelte"
|
|
4
|
+
import {SvelteSPARouterNavigationEvent} from "./constants.js"
|
|
5
|
+
import {joinPaths} from "./helpers/url-helpers.js"
|
|
6
6
|
|
|
7
7
|
export const HashRoutingEnabled = writable(true)
|
|
8
|
-
export const BasePath = writable(
|
|
8
|
+
export const BasePath = writable("/")
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Returns the current location from the hash.
|
|
@@ -16,13 +16,21 @@
|
|
|
16
16
|
function getLocation() {
|
|
17
17
|
const hashRoutingEnabled = get(HashRoutingEnabled)
|
|
18
18
|
const basePath = get(BasePath)
|
|
19
|
-
let location
|
|
19
|
+
let location = ""
|
|
20
|
+
let querystring = ""
|
|
20
21
|
|
|
21
22
|
if (hashRoutingEnabled) {
|
|
22
|
-
const hashPosition = window.location.href.indexOf(
|
|
23
|
+
const hashPosition = window.location.href.indexOf("#/")
|
|
23
24
|
location = (hashPosition > -1) ?
|
|
24
25
|
window.location.href.substr(hashPosition + 1) :
|
|
25
|
-
|
|
26
|
+
"/"
|
|
27
|
+
|
|
28
|
+
// Check if there's a querystring
|
|
29
|
+
const qsPosition = location.indexOf("?")
|
|
30
|
+
if (qsPosition > -1) {
|
|
31
|
+
querystring = location.substr(qsPosition + 1)
|
|
32
|
+
location = location.substr(0, qsPosition)
|
|
33
|
+
}
|
|
26
34
|
}
|
|
27
35
|
else {
|
|
28
36
|
const startsWithPrefix = window.location.pathname.startsWith(basePath)
|
|
@@ -31,15 +39,11 @@
|
|
|
31
39
|
BasePath)}"`)
|
|
32
40
|
}
|
|
33
41
|
|
|
34
|
-
location =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
let querystring = ''
|
|
40
|
-
if (qsPosition > -1) {
|
|
41
|
-
querystring = location.substr(qsPosition + 1)
|
|
42
|
-
location = location.substr(0, qsPosition)
|
|
42
|
+
location = "/" + window.location.pathname.substring(basePath.length).replace(/^\//, "")
|
|
43
|
+
// console.log("SAPA ROUTER Parsing querystring", window.location.search)
|
|
44
|
+
if (window.location.search) {
|
|
45
|
+
querystring = window.location.search.substring(1)
|
|
46
|
+
}
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
return {location, querystring}
|
|
@@ -55,11 +59,11 @@
|
|
|
55
59
|
set(getLocation())
|
|
56
60
|
|
|
57
61
|
const eventName = get(HashRoutingEnabled) ?
|
|
58
|
-
|
|
62
|
+
"hashchange" :
|
|
59
63
|
SvelteSPARouterNavigationEvent
|
|
60
|
-
console.log(
|
|
64
|
+
// console.log("Setting loc")
|
|
61
65
|
const update = () => {
|
|
62
|
-
console.log(
|
|
66
|
+
console.log("Updating location", getLocation())
|
|
63
67
|
set(getLocation())
|
|
64
68
|
}
|
|
65
69
|
window.addEventListener(eventName, update, false)
|
|
@@ -125,18 +129,18 @@
|
|
|
125
129
|
return jediForcePush(location, true)
|
|
126
130
|
}
|
|
127
131
|
|
|
128
|
-
async function jediForcePush(
|
|
132
|
+
async function jediForcePush(location_, shouldReplace = false) {
|
|
129
133
|
const hashRoutingEnabled = get(HashRoutingEnabled)
|
|
130
134
|
const basePath = get(BasePath)
|
|
131
135
|
|
|
132
136
|
if (hashRoutingEnabled) {
|
|
133
|
-
if (!
|
|
134
|
-
throw Error(
|
|
137
|
+
if (!location_ || location_.length < 1 || !/^(\/|#\/)/.test(location_)) {
|
|
138
|
+
throw Error("Invalid parameter location")
|
|
135
139
|
}
|
|
136
140
|
}
|
|
137
141
|
else {
|
|
138
|
-
if (!
|
|
139
|
-
throw Error(
|
|
142
|
+
if (!location_ || location_.length < 1 || !/^\//.test(location_)) {
|
|
143
|
+
throw Error("Invalid parameter location")
|
|
140
144
|
}
|
|
141
145
|
}
|
|
142
146
|
|
|
@@ -158,28 +162,29 @@
|
|
|
158
162
|
doNavigate(newHistoryState, undefined)
|
|
159
163
|
|
|
160
164
|
if (!shouldReplace) {
|
|
161
|
-
window.location.hash = (
|
|
165
|
+
window.location.hash = (location_.charAt(0) === "#" ? "" : "#") + location_
|
|
162
166
|
}
|
|
163
167
|
else {
|
|
164
|
-
window.dispatchEvent(new Event(
|
|
168
|
+
window.dispatchEvent(new Event("hashchange"))
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
if (location_ !== get(location)) {
|
|
172
|
+
if (!location_.startsWith(basePath)) {
|
|
173
|
+
location_ = joinPaths(basePath, location_)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// console.log(
|
|
177
|
+
// "Before navigate",
|
|
178
|
+
// window.history,
|
|
179
|
+
// window.history.pushState,
|
|
180
|
+
// doNavigate,
|
|
181
|
+
// newHistoryState,
|
|
182
|
+
// undefined,
|
|
183
|
+
// location_
|
|
184
|
+
// )
|
|
185
|
+
// window.history.pushState(newHistoryState, undefined, location)
|
|
186
|
+
doNavigate(newHistoryState, undefined, location_)
|
|
165
187
|
}
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
if (!location.startsWith(basePath)) {
|
|
169
|
-
location = joinPaths(basePath, location)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
console.log(
|
|
173
|
-
'Before navigate',
|
|
174
|
-
window.history,
|
|
175
|
-
window.history.pushState,
|
|
176
|
-
doNavigate,
|
|
177
|
-
newHistoryState,
|
|
178
|
-
undefined,
|
|
179
|
-
location
|
|
180
|
-
)
|
|
181
|
-
// window.history.pushState(newHistoryState, undefined, location)
|
|
182
|
-
doNavigate(newHistoryState, undefined, location)
|
|
183
188
|
|
|
184
189
|
window.dispatchEvent(new Event(SvelteSPARouterNavigationEvent))
|
|
185
190
|
}
|
|
@@ -209,21 +214,29 @@
|
|
|
209
214
|
opts = linkOpts(opts)
|
|
210
215
|
|
|
211
216
|
// Only apply to <a> tags
|
|
212
|
-
if (!node || !node.tagName || node.tagName.toLowerCase() !=
|
|
213
|
-
throw Error(
|
|
217
|
+
if (!node || !node.tagName || node.tagName.toLowerCase() != "a") {
|
|
218
|
+
throw Error("Action 'link' can only be used with <a> tags")
|
|
214
219
|
}
|
|
215
220
|
|
|
216
221
|
updateLink(node, opts)
|
|
217
222
|
|
|
218
223
|
if (!get(HashRoutingEnabled)) {
|
|
219
|
-
node.addEventListener(
|
|
224
|
+
node.addEventListener("click", ev => {
|
|
220
225
|
ev.stopImmediatePropagation()
|
|
221
226
|
ev.preventDefault()
|
|
222
227
|
|
|
223
|
-
const
|
|
228
|
+
const linkTarget = ev.target.getAttribute("target")
|
|
229
|
+
if (linkTarget && linkTarget !== "_self") {
|
|
230
|
+
// prevent pushState when link is perhaps going outside of this window
|
|
231
|
+
window.open(node.getAttribute("href"), linkTarget)
|
|
232
|
+
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const shouldReplace = typeof opts !== "string" && opts.shouldReplace
|
|
224
237
|
|
|
225
|
-
jediForcePush(node.getAttribute(
|
|
226
|
-
window.dispatchEvent(new Event(SvelteSPARouterNavigationEvent))
|
|
238
|
+
jediForcePush(node.getAttribute("href"), shouldReplace)
|
|
239
|
+
// window.dispatchEvent(new Event(SvelteSPARouterNavigationEvent))
|
|
227
240
|
}, {capture: true})
|
|
228
241
|
}
|
|
229
242
|
|
|
@@ -255,15 +268,15 @@
|
|
|
255
268
|
function updateLink(node, opts) {
|
|
256
269
|
const basePath = get(BasePath)
|
|
257
270
|
|
|
258
|
-
let href = opts.href || node.getAttribute(
|
|
271
|
+
let href = opts.href || node.getAttribute("href")
|
|
259
272
|
|
|
260
273
|
if (get(HashRoutingEnabled)) {
|
|
261
|
-
if (href && href.charAt(0) ==
|
|
274
|
+
if (href && href.charAt(0) == "/") {
|
|
262
275
|
// Add # to the href attribute
|
|
263
|
-
href =
|
|
276
|
+
href = "#" + href
|
|
264
277
|
}
|
|
265
|
-
else if (!href || href.length < 2 || href.slice(0, 2) !=
|
|
266
|
-
throw Error(
|
|
278
|
+
else if (!href || href.length < 2 || href.slice(0, 2) != "#/") {
|
|
279
|
+
throw Error("Invalid value for \"href\" attribute: " + href)
|
|
267
280
|
}
|
|
268
281
|
}
|
|
269
282
|
else {
|
|
@@ -272,19 +285,19 @@
|
|
|
272
285
|
}
|
|
273
286
|
}
|
|
274
287
|
|
|
275
|
-
node.setAttribute(
|
|
276
|
-
node.addEventListener(
|
|
288
|
+
node.setAttribute("href", href)
|
|
289
|
+
node.addEventListener("click", (event) => {
|
|
277
290
|
// Prevent default anchor onclick behaviour
|
|
278
291
|
event.preventDefault()
|
|
279
292
|
if (!opts.disabled) {
|
|
280
|
-
scrollstateHistoryHandler(event.currentTarget.getAttribute(
|
|
293
|
+
scrollstateHistoryHandler(event.currentTarget.getAttribute("href"))
|
|
281
294
|
}
|
|
282
295
|
})
|
|
283
296
|
}
|
|
284
297
|
|
|
285
298
|
// Internal function that ensures the argument of the link action is always an object
|
|
286
299
|
function linkOpts(val) {
|
|
287
|
-
if (val && typeof val ==
|
|
300
|
+
if (val && typeof val == "string") {
|
|
288
301
|
return {
|
|
289
302
|
href: val
|
|
290
303
|
}
|
|
@@ -328,8 +341,8 @@
|
|
|
328
341
|
{/if}
|
|
329
342
|
|
|
330
343
|
<script>
|
|
331
|
-
import {onDestroy, createEventDispatcher, afterUpdate} from
|
|
332
|
-
import {parse} from
|
|
344
|
+
import {onDestroy, createEventDispatcher, afterUpdate} from "svelte"
|
|
345
|
+
import {parse} from "regexparam"
|
|
333
346
|
|
|
334
347
|
/**
|
|
335
348
|
* Dictionary of all routes, in the format `'/path': component`.
|
|
@@ -351,7 +364,7 @@
|
|
|
351
364
|
/**
|
|
352
365
|
* Optional prefix for the routes in this router. This is useful for example in the case of nested routers.
|
|
353
366
|
*/
|
|
354
|
-
export let prefix =
|
|
367
|
+
export let prefix = ""
|
|
355
368
|
|
|
356
369
|
/**
|
|
357
370
|
* If set to true, the router will restore scroll positions on back navigation
|
|
@@ -370,16 +383,16 @@
|
|
|
370
383
|
* @param {SvelteComponent|WrappedComponent} component - Svelte component for the route, optionally wrapped
|
|
371
384
|
*/
|
|
372
385
|
constructor(path, component) {
|
|
373
|
-
if (!component || (typeof component !=
|
|
374
|
-
throw Error(
|
|
386
|
+
if (!component || (typeof component != "function" && (typeof component != "object" || component._sveltesparouter !== true))) {
|
|
387
|
+
throw Error("Invalid component object")
|
|
375
388
|
}
|
|
376
389
|
|
|
377
390
|
// Path must be a regular or expression, or a string starting with '/' or '*'
|
|
378
391
|
if (!path ||
|
|
379
|
-
(typeof path ==
|
|
380
|
-
(typeof path ==
|
|
392
|
+
(typeof path == "string" && (path.length < 1 || (path.charAt(0) != "/" && path.charAt(0) != "*"))) ||
|
|
393
|
+
(typeof path == "object" && !(path instanceof RegExp))
|
|
381
394
|
) {
|
|
382
|
-
throw Error(
|
|
395
|
+
throw Error("Invalid value for \"path\" argument - strings must start with / or *")
|
|
383
396
|
}
|
|
384
397
|
|
|
385
398
|
const {pattern, keys} = parse(path)
|
|
@@ -387,7 +400,7 @@
|
|
|
387
400
|
this.path = path
|
|
388
401
|
|
|
389
402
|
// Check if the component is wrapped and we have conditions
|
|
390
|
-
if (typeof component ==
|
|
403
|
+
if (typeof component == "object" && component._sveltesparouter === true) {
|
|
391
404
|
this.component = component.component
|
|
392
405
|
this.conditions = component.conditions || []
|
|
393
406
|
this.userData = component.userData
|
|
@@ -416,9 +429,9 @@
|
|
|
416
429
|
// If there's a prefix, check if it matches the start of the path.
|
|
417
430
|
// If not, bail early, else remove it before we run the matching.
|
|
418
431
|
if (prefix) {
|
|
419
|
-
if (typeof prefix ==
|
|
432
|
+
if (typeof prefix == "string") {
|
|
420
433
|
if (path.startsWith(prefix)) {
|
|
421
|
-
path = path.substr(prefix.length) ||
|
|
434
|
+
path = path.substr(prefix.length) || "/"
|
|
422
435
|
}
|
|
423
436
|
else {
|
|
424
437
|
return null
|
|
@@ -427,7 +440,7 @@
|
|
|
427
440
|
else if (prefix instanceof RegExp) {
|
|
428
441
|
const match = path.match(prefix)
|
|
429
442
|
if (match && match[0]) {
|
|
430
|
-
path = path.substr(match[0].length) ||
|
|
443
|
+
path = path.substr(match[0].length) || "/"
|
|
431
444
|
}
|
|
432
445
|
else {
|
|
433
446
|
return null
|
|
@@ -451,7 +464,7 @@
|
|
|
451
464
|
while (i < this._keys.length) {
|
|
452
465
|
// In the match parameters, URL-decode all values
|
|
453
466
|
try {
|
|
454
|
-
out[this._keys[i]] = decodeURIComponent(matches[i + 1] ||
|
|
467
|
+
out[this._keys[i]] = decodeURIComponent(matches[i + 1] || "") || null
|
|
455
468
|
} catch (e) {
|
|
456
469
|
out[this._keys[i]] = null
|
|
457
470
|
}
|
|
@@ -522,7 +535,7 @@
|
|
|
522
535
|
let previousScrollState = null
|
|
523
536
|
|
|
524
537
|
// Update history.scrollRestoration depending on restoreScrollState
|
|
525
|
-
$: history.scrollRestoration = restoreScrollState ?
|
|
538
|
+
$: history.scrollRestoration = restoreScrollState ? "manual" : "auto"
|
|
526
539
|
let popStateChanged = null
|
|
527
540
|
if (restoreScrollState) {
|
|
528
541
|
popStateChanged = (event) => {
|
|
@@ -537,7 +550,7 @@
|
|
|
537
550
|
}
|
|
538
551
|
}
|
|
539
552
|
// This is removed in the destroy() invocation below
|
|
540
|
-
window.addEventListener(
|
|
553
|
+
window.addEventListener("popstate", popStateChanged)
|
|
541
554
|
|
|
542
555
|
afterUpdate(() => {
|
|
543
556
|
restoreScroll(previousScrollState)
|
|
@@ -570,7 +583,7 @@
|
|
|
570
583
|
location: newLoc.location,
|
|
571
584
|
querystring: newLoc.querystring,
|
|
572
585
|
userData: routesList[i].userData,
|
|
573
|
-
params: (match && typeof match ==
|
|
586
|
+
params: (match && typeof match == "object" && Object.keys(match).length) ? match : null
|
|
574
587
|
}
|
|
575
588
|
|
|
576
589
|
// Check if the route can be loaded - if all conditions succeed
|
|
@@ -579,13 +592,13 @@
|
|
|
579
592
|
component = null
|
|
580
593
|
componentObj = null
|
|
581
594
|
// Trigger an event to notify the user, then exit
|
|
582
|
-
dispatchNextTick(
|
|
595
|
+
dispatchNextTick("conditionsFailed", detail)
|
|
583
596
|
return
|
|
584
597
|
}
|
|
585
598
|
|
|
586
599
|
// Trigger an event to alert that we're loading the route
|
|
587
600
|
// We need to clone the object on every event invocation so we don't risk the object to be modified in the next tick
|
|
588
|
-
dispatchNextTick(
|
|
601
|
+
dispatchNextTick("routeLoading", Object.assign({}, detail))
|
|
589
602
|
|
|
590
603
|
// If there's a component to show while we're loading the route, display it
|
|
591
604
|
const obj = routesList[i].component
|
|
@@ -599,7 +612,7 @@
|
|
|
599
612
|
|
|
600
613
|
// Trigger the routeLoaded event for the loading component
|
|
601
614
|
// Create a copy of detail so we don't modify the object for the dynamic route (and the dynamic route doesn't modify our object too)
|
|
602
|
-
dispatchNextTick(
|
|
615
|
+
dispatchNextTick("routeLoaded", Object.assign({}, detail, {
|
|
603
616
|
component: component,
|
|
604
617
|
name: component.name,
|
|
605
618
|
params: componentParams
|
|
@@ -626,7 +639,7 @@
|
|
|
626
639
|
|
|
627
640
|
// Set componentParams only if we have a match, to avoid a warning similar to `<Component> was created with unknown prop 'params'`
|
|
628
641
|
// Of course, this assumes that developers always add a "params" prop when they are expecting parameters
|
|
629
|
-
if (match && typeof match ==
|
|
642
|
+
if (match && typeof match == "object" && Object.keys(match).length) {
|
|
630
643
|
componentParams = match
|
|
631
644
|
}
|
|
632
645
|
else {
|
|
@@ -638,7 +651,7 @@
|
|
|
638
651
|
|
|
639
652
|
// Dispatch the routeLoaded event then exit
|
|
640
653
|
// We need to clone the object on every event invocation so we don't risk the object to be modified in the next tick
|
|
641
|
-
dispatchNextTick(
|
|
654
|
+
dispatchNextTick("routeLoaded", Object.assign({}, detail, {
|
|
642
655
|
component: component,
|
|
643
656
|
name: component.name,
|
|
644
657
|
params: componentParams
|
|
@@ -656,6 +669,6 @@
|
|
|
656
669
|
|
|
657
670
|
onDestroy(() => {
|
|
658
671
|
unsubscribeLoc()
|
|
659
|
-
popStateChanged && window.removeEventListener(
|
|
672
|
+
popStateChanged && window.removeEventListener("popstate", popStateChanged)
|
|
660
673
|
})
|
|
661
674
|
</script>
|