@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/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.161",
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.161",
47
- "@tanstack/react-store": "0.0.1-beta.161"
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['__types']['allParams'],
81
+ TParentRoute['types']['allParams'],
80
82
  TParams
81
83
  >,
82
- TParentContext extends RouteConstraints['TParentContext'] = TParentRoute['__types']['routeContext'],
83
- TAllParentContext extends RouteConstraints['TAllParentContext'] = TParentRoute['__types']['context'],
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['__types']['context'],
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
- >['__types']['allParams'],
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
- >['__types']['context'],
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
- >['__types']['routeContext'],
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
- >['__types']['fullSearchSchema'],
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>['__types']['allParams'],
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
+ }