@tanstack/react-router 0.0.1-beta.161 → 0.0.1-beta.163
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/index.js +2 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/scroll-restoration.js +164 -0
- package/build/cjs/scroll-restoration.js.map +1 -0
- package/build/esm/index.js +126 -1
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +107 -63
- package/build/types/index.d.ts +10 -8
- package/build/umd/index.development.js +184 -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 +3 -3
- package/src/index.tsx +12 -13
- package/src/scroll-restoration.tsx +163 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-router",
|
|
3
3
|
"author": "Tanner Linsley",
|
|
4
|
-
"version": "0.0.1-beta.
|
|
4
|
+
"version": "0.0.1-beta.163",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "tanstack/router",
|
|
7
7
|
"homepage": "https://tanstack.com/router",
|
|
@@ -43,8 +43,8 @@
|
|
|
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/router-core": "0.0.1-beta.
|
|
47
|
-
"@tanstack/react-store": "0.0.1-beta.
|
|
46
|
+
"@tanstack/router-core": "0.0.1-beta.163",
|
|
47
|
+
"@tanstack/react-store": "0.0.1-beta.163"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "rollup --config rollup.config.js"
|
package/src/index.tsx
CHANGED
|
@@ -40,6 +40,8 @@ import {
|
|
|
40
40
|
//
|
|
41
41
|
|
|
42
42
|
export * from '@tanstack/router-core'
|
|
43
|
+
export * from './scroll-restoration'
|
|
44
|
+
|
|
43
45
|
export { useStore }
|
|
44
46
|
|
|
45
47
|
declare module '@tanstack/router-core' {
|
|
@@ -76,14 +78,14 @@ declare module '@tanstack/router-core' {
|
|
|
76
78
|
string
|
|
77
79
|
>,
|
|
78
80
|
TAllParams extends RouteConstraints['TAllParams'] = MergeParamsFromParent<
|
|
79
|
-
TParentRoute['
|
|
81
|
+
TParentRoute['types']['allParams'],
|
|
80
82
|
TParams
|
|
81
83
|
>,
|
|
82
|
-
TParentContext extends RouteConstraints['TParentContext'] = TParentRoute['
|
|
83
|
-
TAllParentContext extends RouteConstraints['TAllParentContext'] = TParentRoute['
|
|
84
|
+
TParentContext extends RouteConstraints['TParentContext'] = TParentRoute['types']['routeContext'],
|
|
85
|
+
TAllParentContext extends RouteConstraints['TAllParentContext'] = TParentRoute['types']['context'],
|
|
84
86
|
TRouteContext extends RouteConstraints['TRouteContext'] = RouteContext,
|
|
85
87
|
TAllContext extends RouteConstraints['TAllContext'] = MergeParamsFromParent<
|
|
86
|
-
TParentRoute['
|
|
88
|
+
TParentRoute['types']['context'],
|
|
87
89
|
TRouteContext
|
|
88
90
|
>,
|
|
89
91
|
TRouterContext extends RouteConstraints['TRouterContext'] = AnyContext,
|
|
@@ -246,7 +248,7 @@ export type MakeMatchRouteOptions<
|
|
|
246
248
|
params?: RouteByPath<
|
|
247
249
|
RegisteredRouter['routeTree'],
|
|
248
250
|
ResolveRelativePath<TFrom, NoInfer<TTo>>
|
|
249
|
-
>['
|
|
251
|
+
>['types']['allParams'],
|
|
250
252
|
) => ReactNode)
|
|
251
253
|
| React.ReactNode
|
|
252
254
|
}
|
|
@@ -612,10 +614,7 @@ export type RouteFromIdOrRoute<T> = T extends ParseRoute<
|
|
|
612
614
|
export function useLoader<
|
|
613
615
|
TFrom extends RouteIds<RegisteredRouter['routeTree']>,
|
|
614
616
|
TStrict extends boolean = true,
|
|
615
|
-
TLoader = RouteById<
|
|
616
|
-
RegisteredRouter['routeTree'],
|
|
617
|
-
TFrom
|
|
618
|
-
>['__types']['loader'],
|
|
617
|
+
TLoader = RouteById<RegisteredRouter['routeTree'], TFrom>['types']['loader'],
|
|
619
618
|
TSelected = TLoader,
|
|
620
619
|
>(opts?: {
|
|
621
620
|
from: TFrom
|
|
@@ -636,7 +635,7 @@ export function useRouterContext<
|
|
|
636
635
|
TContext = RouteById<
|
|
637
636
|
RegisteredRouter['routeTree'],
|
|
638
637
|
TFrom
|
|
639
|
-
>['
|
|
638
|
+
>['types']['context'],
|
|
640
639
|
TSelected = TContext,
|
|
641
640
|
>(opts?: {
|
|
642
641
|
from: TFrom
|
|
@@ -656,7 +655,7 @@ export function useRouteContext<
|
|
|
656
655
|
TRouteContext = RouteById<
|
|
657
656
|
RegisteredRouter['routeTree'],
|
|
658
657
|
TFrom
|
|
659
|
-
>['
|
|
658
|
+
>['types']['routeContext'],
|
|
660
659
|
TSelected = TRouteContext,
|
|
661
660
|
>(opts?: {
|
|
662
661
|
from: TFrom
|
|
@@ -677,7 +676,7 @@ export function useSearch<
|
|
|
677
676
|
TSearch = RouteById<
|
|
678
677
|
RegisteredRouter['routeTree'],
|
|
679
678
|
TFrom
|
|
680
|
-
>['
|
|
679
|
+
>['types']['fullSearchSchema'],
|
|
681
680
|
TSelected = TSearch,
|
|
682
681
|
>(opts?: {
|
|
683
682
|
from: TFrom
|
|
@@ -696,7 +695,7 @@ export function useSearch<
|
|
|
696
695
|
export function useParams<
|
|
697
696
|
TFrom extends RouteIds<RegisteredRouter['routeTree']> = '/',
|
|
698
697
|
TDefaultSelected = AllParams<RegisteredRouter['routeTree']> &
|
|
699
|
-
RouteById<RegisteredRouter['routeTree'], TFrom>['
|
|
698
|
+
RouteById<RegisteredRouter['routeTree'], TFrom>['types']['allParams'],
|
|
700
699
|
TSelected = TDefaultSelected,
|
|
701
700
|
>(opts?: {
|
|
702
701
|
from: TFrom
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { ParsedLocation, useRouter } from '.'
|
|
3
|
+
|
|
4
|
+
const useLayoutEffect =
|
|
5
|
+
typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect
|
|
6
|
+
|
|
7
|
+
const windowKey = 'window'
|
|
8
|
+
const delimiter = '___'
|
|
9
|
+
|
|
10
|
+
let weakScrolledElementsByRestoreKey: Record<string, WeakSet<any>> = {}
|
|
11
|
+
|
|
12
|
+
const cache = (() => {
|
|
13
|
+
if (typeof window === 'undefined') {
|
|
14
|
+
return {
|
|
15
|
+
set: () => {},
|
|
16
|
+
get: () => {},
|
|
17
|
+
} as never
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const storageKey = 'tsr-scroll-restoration-v1'
|
|
21
|
+
|
|
22
|
+
let cache = JSON.parse(window.sessionStorage.getItem(storageKey) || '{}')
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
current: cache,
|
|
26
|
+
set: (key: string, value: any) => {
|
|
27
|
+
cache[key] = value
|
|
28
|
+
window.sessionStorage.setItem(storageKey, JSON.stringify(cache))
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
})()
|
|
32
|
+
|
|
33
|
+
export function ScrollRestoration() {
|
|
34
|
+
const router = useRouter()
|
|
35
|
+
const getKey = (location: ParsedLocation) => location.key as string
|
|
36
|
+
|
|
37
|
+
const pathDidChangeRef = React.useRef(false)
|
|
38
|
+
|
|
39
|
+
const restoreScrollPositions = () => {
|
|
40
|
+
const restoreKey = getKey(router.state.location)
|
|
41
|
+
let windowRestored = false
|
|
42
|
+
|
|
43
|
+
for (const cacheKey in cache.current) {
|
|
44
|
+
const entry = cache.current[cacheKey]!
|
|
45
|
+
const [key, elementSelector] = cacheKey.split(delimiter)
|
|
46
|
+
if (key === restoreKey) {
|
|
47
|
+
if (elementSelector === windowKey) {
|
|
48
|
+
windowRestored = true
|
|
49
|
+
window.scrollTo(entry.scrollX, entry.scrollY)
|
|
50
|
+
} else if (elementSelector) {
|
|
51
|
+
const element = document.querySelector(elementSelector)
|
|
52
|
+
if (element) {
|
|
53
|
+
element.scrollLeft = entry.scrollX
|
|
54
|
+
element.scrollTop = entry.scrollY
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!windowRestored) {
|
|
61
|
+
window.scrollTo(0, 0)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
useLayoutEffect(() => {
|
|
66
|
+
const { history } = window
|
|
67
|
+
if (history.scrollRestoration) {
|
|
68
|
+
history.scrollRestoration = 'manual'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const onScroll = (event: Event) => {
|
|
72
|
+
const restoreKey = getKey(router.state.resolvedLocation)
|
|
73
|
+
|
|
74
|
+
if (!weakScrolledElementsByRestoreKey[restoreKey]) {
|
|
75
|
+
weakScrolledElementsByRestoreKey[restoreKey] = new WeakSet()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const set = weakScrolledElementsByRestoreKey[restoreKey]!
|
|
79
|
+
|
|
80
|
+
if (set.has(event.target)) return
|
|
81
|
+
set.add(event.target)
|
|
82
|
+
|
|
83
|
+
const cacheKey = [
|
|
84
|
+
restoreKey,
|
|
85
|
+
event.target === document || event.target === window
|
|
86
|
+
? windowKey
|
|
87
|
+
: getCssSelector(event.target),
|
|
88
|
+
].join(delimiter)
|
|
89
|
+
|
|
90
|
+
if (!cache.current[cacheKey]) {
|
|
91
|
+
cache.set(cacheKey, {
|
|
92
|
+
scrollX: NaN,
|
|
93
|
+
scrollY: NaN,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const getCssSelector = (el: any): string => {
|
|
99
|
+
let path = [],
|
|
100
|
+
parent
|
|
101
|
+
while ((parent = el.parentNode)) {
|
|
102
|
+
path.unshift(
|
|
103
|
+
`${el.tagName}:nth-child(${
|
|
104
|
+
([].indexOf as any).call(parent.children, el) + 1
|
|
105
|
+
})`,
|
|
106
|
+
)
|
|
107
|
+
el = parent
|
|
108
|
+
}
|
|
109
|
+
return `${path.join(' > ')}`.toLowerCase()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const onPathWillChange = (from: ParsedLocation) => {
|
|
113
|
+
const restoreKey = getKey(from)
|
|
114
|
+
for (const cacheKey in cache.current) {
|
|
115
|
+
const entry = cache.current[cacheKey]!
|
|
116
|
+
const [key, elementSelector] = cacheKey.split(delimiter)
|
|
117
|
+
if (restoreKey === key) {
|
|
118
|
+
if (elementSelector === windowKey) {
|
|
119
|
+
entry.scrollX = window.scrollX || 0
|
|
120
|
+
entry.scrollY = window.scrollY || 0
|
|
121
|
+
} else if (elementSelector) {
|
|
122
|
+
const element = document.querySelector(elementSelector)
|
|
123
|
+
entry.scrollX = element?.scrollLeft || 0
|
|
124
|
+
entry.scrollY = element?.scrollTop || 0
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
cache.set(cacheKey, entry)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const onPathChange = () => {
|
|
133
|
+
pathDidChangeRef.current = true
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (typeof document !== 'undefined') {
|
|
137
|
+
document.addEventListener('scroll', onScroll, true)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const unsubOnBeforeLoad = router.subscribe('onBeforeLoad', (event) => {
|
|
141
|
+
if (event.pathChanged) onPathWillChange(event.from)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const unsubOnLoad = router.subscribe('onLoad', (event) => {
|
|
145
|
+
if (event.pathChanged) onPathChange()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
return () => {
|
|
149
|
+
document.removeEventListener('scroll', onScroll)
|
|
150
|
+
unsubOnBeforeLoad()
|
|
151
|
+
unsubOnLoad()
|
|
152
|
+
}
|
|
153
|
+
}, [])
|
|
154
|
+
|
|
155
|
+
useLayoutEffect(() => {
|
|
156
|
+
if (pathDidChangeRef.current) {
|
|
157
|
+
pathDidChangeRef.current = false
|
|
158
|
+
restoreScrollPositions()
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
return null
|
|
163
|
+
}
|