@tanstack/router-core 0.0.1-beta.162 → 0.0.1-beta.164
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/build/cjs/history.js +11 -11
- package/build/cjs/history.js.map +1 -1
- package/build/cjs/index.js +3 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/route.js.map +1 -1
- package/build/cjs/router.js +47 -29
- package/build/cjs/router.js.map +1 -1
- package/build/cjs/scroll-restoration.js +136 -0
- package/build/cjs/scroll-restoration.js.map +1 -0
- package/build/esm/index.js +178 -41
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +143 -118
- package/build/types/index.d.ts +30 -12
- package/build/umd/index.development.js +179 -40
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +2 -2
- package/build/umd/index.production.js.map +1 -1
- package/package.json +2 -2
- package/src/history.ts +14 -14
- package/src/index.ts +1 -0
- package/src/route.ts +1 -10
- package/src/router.ts +82 -39
- package/src/scroll-restoration.ts +175 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/router-core",
|
|
3
3
|
"author": "Tanner Linsley",
|
|
4
|
-
"version": "0.0.1-beta.
|
|
4
|
+
"version": "0.0.1-beta.164",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "tanstack/router",
|
|
7
7
|
"homepage": "https://tanstack.com/router",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"tiny-invariant": "^1.3.1",
|
|
44
44
|
"tiny-warning": "^1.0.3",
|
|
45
45
|
"@gisatcz/cross-package-react-context": "^0.2.0",
|
|
46
|
-
"@tanstack/react-store": "0.0.1-beta.
|
|
46
|
+
"@tanstack/react-store": "0.0.1-beta.164"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "rollup --config rollup.config.js",
|
package/src/history.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
export interface RouterHistory {
|
|
6
6
|
location: RouterLocation
|
|
7
|
-
|
|
7
|
+
subscribe: (cb: () => void) => () => void
|
|
8
8
|
push: (path: string, state?: any) => void
|
|
9
9
|
replace: (path: string, state?: any) => void
|
|
10
10
|
go: (index: number) => void
|
|
@@ -45,7 +45,7 @@ const stopBlocking = () => {
|
|
|
45
45
|
|
|
46
46
|
function createHistory(opts: {
|
|
47
47
|
getLocation: () => RouterLocation
|
|
48
|
-
|
|
48
|
+
subscriber: false | ((onUpdate: () => void) => () => void)
|
|
49
49
|
pushState: (path: string, state: any) => void
|
|
50
50
|
replaceState: (path: string, state: any) => void
|
|
51
51
|
go: (n: number) => void
|
|
@@ -55,7 +55,7 @@ function createHistory(opts: {
|
|
|
55
55
|
}): RouterHistory {
|
|
56
56
|
let location = opts.getLocation()
|
|
57
57
|
let unsub = () => {}
|
|
58
|
-
let
|
|
58
|
+
let subscribers = new Set<() => void>()
|
|
59
59
|
let blockers: BlockerFn[] = []
|
|
60
60
|
let queue: (() => void)[] = []
|
|
61
61
|
|
|
@@ -72,7 +72,7 @@ function createHistory(opts: {
|
|
|
72
72
|
queue.shift()?.()
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
if (!opts.
|
|
75
|
+
if (!opts.subscriber) {
|
|
76
76
|
onUpdate()
|
|
77
77
|
}
|
|
78
78
|
}
|
|
@@ -84,25 +84,25 @@ function createHistory(opts: {
|
|
|
84
84
|
|
|
85
85
|
const onUpdate = () => {
|
|
86
86
|
location = opts.getLocation()
|
|
87
|
-
|
|
87
|
+
subscribers.forEach((subscriber) => subscriber())
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
return {
|
|
91
91
|
get location() {
|
|
92
92
|
return location
|
|
93
93
|
},
|
|
94
|
-
|
|
95
|
-
if (
|
|
94
|
+
subscribe: (cb: () => void) => {
|
|
95
|
+
if (subscribers.size === 0) {
|
|
96
96
|
unsub =
|
|
97
|
-
typeof opts.
|
|
98
|
-
? opts.
|
|
97
|
+
typeof opts.subscriber === 'function'
|
|
98
|
+
? opts.subscriber(onUpdate)
|
|
99
99
|
: () => {}
|
|
100
100
|
}
|
|
101
|
-
|
|
101
|
+
subscribers.add(cb)
|
|
102
102
|
|
|
103
103
|
return () => {
|
|
104
|
-
|
|
105
|
-
if (
|
|
104
|
+
subscribers.delete(cb)
|
|
105
|
+
if (subscribers.size === 0) {
|
|
106
106
|
unsub()
|
|
107
107
|
}
|
|
108
108
|
}
|
|
@@ -166,7 +166,7 @@ export function createBrowserHistory(opts?: {
|
|
|
166
166
|
|
|
167
167
|
return createHistory({
|
|
168
168
|
getLocation,
|
|
169
|
-
|
|
169
|
+
subscriber: (onUpdate) => {
|
|
170
170
|
window.addEventListener(pushStateEvent, onUpdate)
|
|
171
171
|
window.addEventListener(popStateEvent, onUpdate)
|
|
172
172
|
|
|
@@ -234,7 +234,7 @@ export function createMemoryHistory(
|
|
|
234
234
|
|
|
235
235
|
return createHistory({
|
|
236
236
|
getLocation,
|
|
237
|
-
|
|
237
|
+
subscriber: false,
|
|
238
238
|
pushState: (path, state) => {
|
|
239
239
|
currentState = {
|
|
240
240
|
...state,
|
package/src/index.ts
CHANGED
package/src/route.ts
CHANGED
|
@@ -312,7 +312,7 @@ export type UpdatableRouteOptions<
|
|
|
312
312
|
gcMaxAge?: number
|
|
313
313
|
// This async function is called before a route is loaded.
|
|
314
314
|
// If an error is thrown here, the route's loader will not be called.
|
|
315
|
-
// If thrown during a navigation, the navigation will be cancelled and the error will be passed to the `
|
|
315
|
+
// If thrown during a navigation, the navigation will be cancelled and the error will be passed to the `onError` function.
|
|
316
316
|
// If thrown during a preload event, the error will be logged to the console.
|
|
317
317
|
beforeLoad?: (
|
|
318
318
|
opts: LoaderContext<
|
|
@@ -323,15 +323,6 @@ export type UpdatableRouteOptions<
|
|
|
323
323
|
TContext
|
|
324
324
|
>,
|
|
325
325
|
) => Promise<void> | void
|
|
326
|
-
// This function will be called if the route's loader throws an error **during an attempted navigation**.
|
|
327
|
-
// If you want to redirect due to an error, call `router.navigate()` from within this function.
|
|
328
|
-
onBeforeLoadError?: (err: any) => void
|
|
329
|
-
// This function will be called if the route's validateSearch option throws an error **during an attempted validation**.
|
|
330
|
-
// If you want to redirect due to an error, call `router.navigate()` from within this function.
|
|
331
|
-
// If you want to display the errorComponent, rethrow the error
|
|
332
|
-
onValidateSearchError?: (err: any) => void
|
|
333
|
-
onParseParamsError?: (err: any) => void
|
|
334
|
-
onLoadError?: (err: any) => void
|
|
335
326
|
onError?: (err: any) => void
|
|
336
327
|
// This function is called
|
|
337
328
|
// when moving from an inactive state to an active one. Likewise, when moving from
|
package/src/router.ts
CHANGED
|
@@ -171,7 +171,6 @@ export interface RouterOptions<
|
|
|
171
171
|
routeTree?: TRouteTree
|
|
172
172
|
basepath?: string
|
|
173
173
|
createRoute?: (opts: { route: AnyRoute; router: AnyRouter }) => void
|
|
174
|
-
onRouteChange?: () => void
|
|
175
174
|
context?: TRouteTree['types']['routerContext']
|
|
176
175
|
Wrap?: React.ComponentType<{
|
|
177
176
|
children: React.ReactNode
|
|
@@ -197,7 +196,7 @@ export interface RouterState<
|
|
|
197
196
|
lastUpdated: number
|
|
198
197
|
}
|
|
199
198
|
|
|
200
|
-
export type ListenerFn = () => void
|
|
199
|
+
export type ListenerFn<TEvent extends RouterEvent> = (event: TEvent) => void
|
|
201
200
|
|
|
202
201
|
export interface BuildNextOptions {
|
|
203
202
|
to?: string | number | null
|
|
@@ -249,6 +248,28 @@ export const componentTypes = [
|
|
|
249
248
|
'pendingComponent',
|
|
250
249
|
] as const
|
|
251
250
|
|
|
251
|
+
export type RouterEvents = {
|
|
252
|
+
onBeforeLoad: {
|
|
253
|
+
type: 'onBeforeLoad'
|
|
254
|
+
from: ParsedLocation
|
|
255
|
+
to: ParsedLocation
|
|
256
|
+
pathChanged: boolean
|
|
257
|
+
}
|
|
258
|
+
onLoad: {
|
|
259
|
+
type: 'onLoad'
|
|
260
|
+
from: ParsedLocation
|
|
261
|
+
to: ParsedLocation
|
|
262
|
+
pathChanged: boolean
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export type RouterEvent = RouterEvents[keyof RouterEvents]
|
|
267
|
+
|
|
268
|
+
export type RouterListener<TRouterEvent extends RouterEvent> = {
|
|
269
|
+
eventType: TRouterEvent['type']
|
|
270
|
+
fn: ListenerFn<TRouterEvent>
|
|
271
|
+
}
|
|
272
|
+
|
|
252
273
|
export class Router<
|
|
253
274
|
TRouteTree extends AnyRoute = AnyRoute,
|
|
254
275
|
TDehydrated extends Record<string, any> = Record<string, any>,
|
|
@@ -343,6 +364,32 @@ export class Router<
|
|
|
343
364
|
}
|
|
344
365
|
}
|
|
345
366
|
|
|
367
|
+
subscribers = new Set<RouterListener<RouterEvent>>()
|
|
368
|
+
|
|
369
|
+
subscribe = <TType extends keyof RouterEvents>(
|
|
370
|
+
eventType: TType,
|
|
371
|
+
fn: ListenerFn<RouterEvents[TType]>,
|
|
372
|
+
) => {
|
|
373
|
+
const listener: RouterListener<any> = {
|
|
374
|
+
eventType,
|
|
375
|
+
fn,
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
this.subscribers.add(listener)
|
|
379
|
+
|
|
380
|
+
return () => {
|
|
381
|
+
this.subscribers.delete(listener)
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
#emit = (routerEvent: RouterEvent) => {
|
|
386
|
+
this.subscribers.forEach((listener) => {
|
|
387
|
+
if (listener.eventType === routerEvent.type) {
|
|
388
|
+
listener.fn(routerEvent)
|
|
389
|
+
}
|
|
390
|
+
})
|
|
391
|
+
}
|
|
392
|
+
|
|
346
393
|
reset = () => {
|
|
347
394
|
this.__store.setState((s) => Object.assign(s, getInitialRouterState()))
|
|
348
395
|
}
|
|
@@ -384,7 +431,7 @@ export class Router<
|
|
|
384
431
|
location: parsedLocation as any,
|
|
385
432
|
}))
|
|
386
433
|
|
|
387
|
-
this.#unsubHistory = this.history.
|
|
434
|
+
this.#unsubHistory = this.history.subscribe(() => {
|
|
388
435
|
this.safeLoad({
|
|
389
436
|
next: this.#parseLocation(this.state.location),
|
|
390
437
|
})
|
|
@@ -435,6 +482,11 @@ export class Router<
|
|
|
435
482
|
|
|
436
483
|
load = async (opts?: { next?: ParsedLocation; throwOnError?: boolean }) => {
|
|
437
484
|
const promise = new Promise<void>(async (resolve, reject) => {
|
|
485
|
+
const prevLocation = this.state.resolvedLocation
|
|
486
|
+
const pathDidChange = !!(
|
|
487
|
+
opts?.next && prevLocation!.href !== opts.next.href
|
|
488
|
+
)
|
|
489
|
+
|
|
438
490
|
let latestPromise: Promise<void> | undefined | null
|
|
439
491
|
|
|
440
492
|
const checkLatest = (): undefined | Promise<void> | null => {
|
|
@@ -448,6 +500,13 @@ export class Router<
|
|
|
448
500
|
|
|
449
501
|
let pendingMatches!: RouteMatch<any, any>[]
|
|
450
502
|
|
|
503
|
+
this.#emit({
|
|
504
|
+
type: 'onBeforeLoad',
|
|
505
|
+
from: prevLocation,
|
|
506
|
+
to: opts?.next ?? this.state.location,
|
|
507
|
+
pathChanged: pathDidChange,
|
|
508
|
+
})
|
|
509
|
+
|
|
451
510
|
this.__store.batch(() => {
|
|
452
511
|
if (opts?.next) {
|
|
453
512
|
// Ingest the new location
|
|
@@ -489,8 +548,6 @@ export class Router<
|
|
|
489
548
|
return latestPromise
|
|
490
549
|
}
|
|
491
550
|
|
|
492
|
-
const prevLocation = this.state.resolvedLocation
|
|
493
|
-
|
|
494
551
|
this.__store.setState((s) => ({
|
|
495
552
|
...s,
|
|
496
553
|
status: 'idle',
|
|
@@ -499,9 +556,12 @@ export class Router<
|
|
|
499
556
|
pendingMatchIds: [],
|
|
500
557
|
}))
|
|
501
558
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
559
|
+
this.#emit({
|
|
560
|
+
type: 'onLoad',
|
|
561
|
+
from: prevLocation,
|
|
562
|
+
to: this.state.location,
|
|
563
|
+
pathChanged: pathDidChange,
|
|
564
|
+
})
|
|
505
565
|
|
|
506
566
|
resolve()
|
|
507
567
|
} catch (err) {
|
|
@@ -844,19 +904,16 @@ export class Router<
|
|
|
844
904
|
for (const [index, match] of resolvedMatches.entries()) {
|
|
845
905
|
const route = this.getRoute(match.routeId)
|
|
846
906
|
|
|
847
|
-
const handleError = (
|
|
848
|
-
err
|
|
849
|
-
handler: undefined | ((err: any) => void),
|
|
850
|
-
) => {
|
|
907
|
+
const handleError = (err: any, code: string) => {
|
|
908
|
+
err.routerCode = code
|
|
851
909
|
firstBadMatchIndex = firstBadMatchIndex ?? index
|
|
852
|
-
handler = handler || route.options.onError
|
|
853
910
|
|
|
854
911
|
if (isRedirect(err)) {
|
|
855
912
|
throw err
|
|
856
913
|
}
|
|
857
914
|
|
|
858
915
|
try {
|
|
859
|
-
|
|
916
|
+
route.options.onError?.(err)
|
|
860
917
|
} catch (errorHandlerErr) {
|
|
861
918
|
err = errorHandlerErr
|
|
862
919
|
|
|
@@ -874,11 +931,11 @@ export class Router<
|
|
|
874
931
|
}
|
|
875
932
|
|
|
876
933
|
if (match.paramsError) {
|
|
877
|
-
handleError(match.paramsError,
|
|
934
|
+
handleError(match.paramsError, 'PARSE_PARAMS')
|
|
878
935
|
}
|
|
879
936
|
|
|
880
937
|
if (match.searchError) {
|
|
881
|
-
handleError(match.searchError,
|
|
938
|
+
handleError(match.searchError, 'VALIDATE_SEARCH')
|
|
882
939
|
}
|
|
883
940
|
|
|
884
941
|
let didError = false
|
|
@@ -889,7 +946,7 @@ export class Router<
|
|
|
889
946
|
preload: !!opts?.preload,
|
|
890
947
|
})
|
|
891
948
|
} catch (err) {
|
|
892
|
-
handleError(err,
|
|
949
|
+
handleError(err, 'BEFORE_LOAD')
|
|
893
950
|
didError = true
|
|
894
951
|
}
|
|
895
952
|
|
|
@@ -968,34 +1025,20 @@ export class Router<
|
|
|
968
1025
|
if ((latestPromise = checkLatest())) return await latestPromise
|
|
969
1026
|
|
|
970
1027
|
this.setRouteMatchData(match.id, () => loader, opts)
|
|
971
|
-
} catch (
|
|
972
|
-
let latestError = loaderError
|
|
1028
|
+
} catch (error) {
|
|
973
1029
|
if ((latestPromise = checkLatest())) return await latestPromise
|
|
974
|
-
if (handleIfRedirect(
|
|
975
|
-
|
|
976
|
-
if (route.options.onLoadError) {
|
|
977
|
-
try {
|
|
978
|
-
route.options.onLoadError(loaderError)
|
|
979
|
-
} catch (onLoadError) {
|
|
980
|
-
latestError = onLoadError
|
|
981
|
-
if (handleIfRedirect(onLoadError)) return
|
|
982
|
-
}
|
|
983
|
-
}
|
|
1030
|
+
if (handleIfRedirect(error)) return
|
|
984
1031
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
route.options.onError(latestError)
|
|
991
|
-
} catch (onErrorError) {
|
|
992
|
-
if (handleIfRedirect(onErrorError)) return
|
|
993
|
-
}
|
|
1032
|
+
try {
|
|
1033
|
+
route.options.onError?.(error)
|
|
1034
|
+
} catch (onErrorError) {
|
|
1035
|
+
error = onErrorError
|
|
1036
|
+
if (handleIfRedirect(onErrorError)) return
|
|
994
1037
|
}
|
|
995
1038
|
|
|
996
1039
|
this.setRouteMatch(match.id, (s) => ({
|
|
997
1040
|
...s,
|
|
998
|
-
error
|
|
1041
|
+
error,
|
|
999
1042
|
status: 'error',
|
|
1000
1043
|
isFetching: false,
|
|
1001
1044
|
updatedAt: Date.now(),
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { AnyRouter, ParsedLocation } from './router'
|
|
2
|
+
|
|
3
|
+
const windowKey = 'window'
|
|
4
|
+
const delimiter = '___'
|
|
5
|
+
|
|
6
|
+
let weakScrolledElementsByRestoreKey: Record<string, WeakSet<any>> = {}
|
|
7
|
+
|
|
8
|
+
type CacheValue = Record<string, { scrollX: number; scrollY: number }>
|
|
9
|
+
|
|
10
|
+
type Cache = {
|
|
11
|
+
current: CacheValue
|
|
12
|
+
set: (key: string, value: any) => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let cache: Cache
|
|
16
|
+
|
|
17
|
+
let pathDidChange = false
|
|
18
|
+
|
|
19
|
+
const sessionsStorage = typeof window !== 'undefined' && window.sessionStorage
|
|
20
|
+
|
|
21
|
+
export type ScrollRestorationOptions = {
|
|
22
|
+
getKey?: (location: ParsedLocation) => string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const defaultGetKey = (location: ParsedLocation) => location.key!
|
|
26
|
+
|
|
27
|
+
export function watchScrollPositions(
|
|
28
|
+
router: AnyRouter,
|
|
29
|
+
opts?: ScrollRestorationOptions,
|
|
30
|
+
) {
|
|
31
|
+
const getKey = opts?.getKey || defaultGetKey
|
|
32
|
+
|
|
33
|
+
if (sessionsStorage) {
|
|
34
|
+
if (!cache) {
|
|
35
|
+
cache = (() => {
|
|
36
|
+
const storageKey = 'tsr-scroll-restoration-v1'
|
|
37
|
+
|
|
38
|
+
const current: CacheValue = JSON.parse(
|
|
39
|
+
window.sessionStorage.getItem(storageKey) || '{}',
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
current,
|
|
44
|
+
set: (key: string, value: any) => {
|
|
45
|
+
current[key] = value
|
|
46
|
+
window.sessionStorage.setItem(storageKey, JSON.stringify(cache))
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
})()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const { history } = window
|
|
54
|
+
if (history.scrollRestoration) {
|
|
55
|
+
history.scrollRestoration = 'manual'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const onScroll = (event: Event) => {
|
|
59
|
+
const restoreKey = getKey(router.state.resolvedLocation)
|
|
60
|
+
|
|
61
|
+
if (!weakScrolledElementsByRestoreKey[restoreKey]) {
|
|
62
|
+
weakScrolledElementsByRestoreKey[restoreKey] = new WeakSet()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const set = weakScrolledElementsByRestoreKey[restoreKey]!
|
|
66
|
+
|
|
67
|
+
if (set.has(event.target)) return
|
|
68
|
+
set.add(event.target)
|
|
69
|
+
|
|
70
|
+
const cacheKey = [
|
|
71
|
+
restoreKey,
|
|
72
|
+
event.target === document || event.target === window
|
|
73
|
+
? windowKey
|
|
74
|
+
: getCssSelector(event.target),
|
|
75
|
+
].join(delimiter)
|
|
76
|
+
|
|
77
|
+
if (!cache.current[cacheKey]) {
|
|
78
|
+
cache.set(cacheKey, {
|
|
79
|
+
scrollX: NaN,
|
|
80
|
+
scrollY: NaN,
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const getCssSelector = (el: any): string => {
|
|
86
|
+
let path = [],
|
|
87
|
+
parent
|
|
88
|
+
while ((parent = el.parentNode)) {
|
|
89
|
+
path.unshift(
|
|
90
|
+
`${el.tagName}:nth-child(${
|
|
91
|
+
([].indexOf as any).call(parent.children, el) + 1
|
|
92
|
+
})`,
|
|
93
|
+
)
|
|
94
|
+
el = parent
|
|
95
|
+
}
|
|
96
|
+
return `${path.join(' > ')}`.toLowerCase()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const onPathWillChange = (from: ParsedLocation) => {
|
|
100
|
+
const restoreKey = getKey(from)
|
|
101
|
+
for (const cacheKey in cache.current) {
|
|
102
|
+
const entry = cache.current[cacheKey]!
|
|
103
|
+
const [key, elementSelector] = cacheKey.split(delimiter)
|
|
104
|
+
if (restoreKey === key) {
|
|
105
|
+
if (elementSelector === windowKey) {
|
|
106
|
+
entry.scrollX = window.scrollX || 0
|
|
107
|
+
entry.scrollY = window.scrollY || 0
|
|
108
|
+
} else if (elementSelector) {
|
|
109
|
+
const element = document.querySelector(elementSelector)
|
|
110
|
+
entry.scrollX = element?.scrollLeft || 0
|
|
111
|
+
entry.scrollY = element?.scrollTop || 0
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
cache.set(cacheKey, entry)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const onPathChange = () => {
|
|
120
|
+
pathDidChange = true
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (typeof document !== 'undefined') {
|
|
124
|
+
document.addEventListener('scroll', onScroll, true)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const unsubOnBeforeLoad = router.subscribe('onBeforeLoad', (event) => {
|
|
128
|
+
if (event.pathChanged) onPathWillChange(event.from)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const unsubOnLoad = router.subscribe('onLoad', (event) => {
|
|
132
|
+
if (event.pathChanged) onPathChange()
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
return () => {
|
|
136
|
+
document.removeEventListener('scroll', onScroll)
|
|
137
|
+
unsubOnBeforeLoad()
|
|
138
|
+
unsubOnLoad()
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function restoreScrollPositions(
|
|
143
|
+
router: AnyRouter,
|
|
144
|
+
opts?: ScrollRestorationOptions,
|
|
145
|
+
) {
|
|
146
|
+
if (pathDidChange) {
|
|
147
|
+
const getKey = opts?.getKey || defaultGetKey
|
|
148
|
+
|
|
149
|
+
pathDidChange = false
|
|
150
|
+
|
|
151
|
+
const restoreKey = getKey(router.state.location)
|
|
152
|
+
let windowRestored = false
|
|
153
|
+
|
|
154
|
+
for (const cacheKey in cache.current) {
|
|
155
|
+
const entry = cache.current[cacheKey]!
|
|
156
|
+
const [key, elementSelector] = cacheKey.split(delimiter)
|
|
157
|
+
if (key === restoreKey) {
|
|
158
|
+
if (elementSelector === windowKey) {
|
|
159
|
+
windowRestored = true
|
|
160
|
+
window.scrollTo(entry.scrollX, entry.scrollY)
|
|
161
|
+
} else if (elementSelector) {
|
|
162
|
+
const element = document.querySelector(elementSelector)
|
|
163
|
+
if (element) {
|
|
164
|
+
element.scrollLeft = entry.scrollX
|
|
165
|
+
element.scrollTop = entry.scrollY
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!windowRestored) {
|
|
172
|
+
window.scrollTo(0, 0)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|