@tanstack/react-router 0.0.1-beta.211 → 0.0.1-beta.212

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.
@@ -1,205 +1,192 @@
1
- // import * as React from 'react'
2
- // import { useRouter } from './react'
3
-
4
- // const useLayoutEffect =
5
- // typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect
6
-
7
- // import { AnyRouter, RouterState } from './router'
8
- // import { ParsedLocation } from './location'
9
-
10
- // const windowKey = 'window'
11
- // const delimiter = '___'
12
-
13
- // let weakScrolledElementsByRestoreKey: Record<string, WeakSet<any>> = {}
14
-
15
- // type CacheValue = Record<string, { scrollX: number; scrollY: number }>
16
-
17
- // type Cache = {
18
- // current: CacheValue
19
- // set: (key: string, value: any) => void
20
- // }
21
-
22
- // let cache: Cache
23
-
24
- // let pathDidChange = false
25
-
26
- // const sessionsStorage = typeof window !== 'undefined' && window.sessionStorage
27
-
28
- // export type ScrollRestorationOptions = {
29
- // getKey?: (location: ParsedLocation) => string
30
- // }
31
-
32
- // const defaultGetKey = (location: ParsedLocation) => location.state.key!
33
-
34
- // export function watchScrollPositions(
35
- // router: AnyRouter,
36
- // state: RouterState,
37
- // opts?: ScrollRestorationOptions,
38
- // ) {
39
- // const getKey = opts?.getKey || defaultGetKey
40
-
41
- // if (sessionsStorage) {
42
- // if (!cache) {
43
- // cache = (() => {
44
- // const storageKey = 'tsr-scroll-restoration-v1'
45
-
46
- // const current: CacheValue = JSON.parse(
47
- // window.sessionStorage.getItem(storageKey) || '{}',
48
- // )
49
-
50
- // return {
51
- // current,
52
- // set: (key: string, value: any) => {
53
- // current[key] = value
54
- // window.sessionStorage.setItem(storageKey, JSON.stringify(cache))
55
- // },
56
- // }
57
- // })()
58
- // }
59
- // }
60
-
61
- // const { history } = window
62
- // if (history.scrollRestoration) {
63
- // history.scrollRestoration = 'manual'
64
- // }
65
-
66
- // const onScroll = (event: Event) => {
67
- // const restoreKey = getKey(state.resolvedLocation)
68
-
69
- // if (!weakScrolledElementsByRestoreKey[restoreKey]) {
70
- // weakScrolledElementsByRestoreKey[restoreKey] = new WeakSet()
71
- // }
72
-
73
- // const set = weakScrolledElementsByRestoreKey[restoreKey]!
74
-
75
- // if (set.has(event.target)) return
76
- // set.add(event.target)
77
-
78
- // const cacheKey = [
79
- // restoreKey,
80
- // event.target === document || event.target === window
81
- // ? windowKey
82
- // : getCssSelector(event.target),
83
- // ].join(delimiter)
84
-
85
- // if (!cache.current[cacheKey]) {
86
- // cache.set(cacheKey, {
87
- // scrollX: NaN,
88
- // scrollY: NaN,
89
- // })
90
- // }
91
- // }
92
-
93
- // const getCssSelector = (el: any): string => {
94
- // let path = [],
95
- // parent
96
- // while ((parent = el.parentNode)) {
97
- // path.unshift(
98
- // `${el.tagName}:nth-child(${
99
- // ([].indexOf as any).call(parent.children, el) + 1
100
- // })`,
101
- // )
102
- // el = parent
103
- // }
104
- // return `${path.join(' > ')}`.toLowerCase()
105
- // }
106
-
107
- // const onPathWillChange = (from: ParsedLocation) => {
108
- // const restoreKey = getKey(from)
109
- // for (const cacheKey in cache.current) {
110
- // const entry = cache.current[cacheKey]!
111
- // const [key, elementSelector] = cacheKey.split(delimiter)
112
- // if (restoreKey === key) {
113
- // if (elementSelector === windowKey) {
114
- // entry.scrollX = window.scrollX || 0
115
- // entry.scrollY = window.scrollY || 0
116
- // } else if (elementSelector) {
117
- // const element = document.querySelector(elementSelector)
118
- // entry.scrollX = element?.scrollLeft || 0
119
- // entry.scrollY = element?.scrollTop || 0
120
- // }
121
-
122
- // cache.set(cacheKey, entry)
123
- // }
124
- // }
125
- // }
126
-
127
- // const onPathChange = () => {
128
- // pathDidChange = true
129
- // }
130
-
131
- // if (typeof document !== 'undefined') {
132
- // document.addEventListener('scroll', onScroll, true)
133
- // }
134
-
135
- // const unsubOnBeforeLoad = router.subscribe('onBeforeLoad', (event) => {
136
- // if (event.pathChanged) onPathWillChange(event.from)
137
- // })
138
-
139
- // const unsubOnLoad = router.subscribe('onLoad', (event) => {
140
- // if (event.pathChanged) onPathChange()
141
- // })
142
-
143
- // return () => {
144
- // document.removeEventListener('scroll', onScroll)
145
- // unsubOnBeforeLoad()
146
- // unsubOnLoad()
147
- // }
148
- // }
149
-
150
- // export function restoreScrollPositions(
151
- // router: AnyRouter,
152
- // state: RouterState,
153
- // opts?: ScrollRestorationOptions,
154
- // ) {
155
- // if (pathDidChange) {
156
- // if (!router.resetNextScroll) {
157
- // return
158
- // }
159
-
160
- // const getKey = opts?.getKey || defaultGetKey
161
-
162
- // pathDidChange = false
163
-
164
- // const restoreKey = getKey(state.location)
165
- // let windowRestored = false
166
-
167
- // for (const cacheKey in cache.current) {
168
- // const entry = cache.current[cacheKey]!
169
- // const [key, elementSelector] = cacheKey.split(delimiter)
170
- // if (key === restoreKey) {
171
- // if (elementSelector === windowKey) {
172
- // windowRestored = true
173
- // window.scrollTo(entry.scrollX, entry.scrollY)
174
- // } else if (elementSelector) {
175
- // const element = document.querySelector(elementSelector)
176
- // if (element) {
177
- // element.scrollLeft = entry.scrollX
178
- // element.scrollTop = entry.scrollY
179
- // }
180
- // }
181
- // }
182
- // }
183
-
184
- // if (!windowRestored) {
185
- // window.scrollTo(0, 0)
186
- // }
187
- // }
188
- // }
189
-
190
- // export function useScrollRestoration(options?: ScrollRestorationOptions) {
191
- // const { router, state } = useRouter()
192
-
193
- // useLayoutEffect(() => {
194
- // return watchScrollPositions(router, state, options)
195
- // }, [])
196
-
197
- // useLayoutEffect(() => {
198
- // restoreScrollPositions(router, state, options)
199
- // })
200
- // }
201
-
202
- // export function ScrollRestoration(props: ScrollRestorationOptions) {
203
- // useScrollRestoration(props)
204
- // return null
205
- // }
1
+ import * as React from 'react'
2
+
3
+ const useLayoutEffect =
4
+ typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect
5
+
6
+ import { ParsedLocation } from './location'
7
+ import { useRouter } from './RouterProvider'
8
+ import { NonNullableUpdater, functionalUpdate } from './utils'
9
+
10
+ const windowKey = 'window'
11
+ const delimiter = '___'
12
+
13
+ let weakScrolledElements = new WeakSet<any>()
14
+
15
+ type CacheValue = Record<string, { scrollX: number; scrollY: number }>
16
+ type CacheState = {
17
+ cached: CacheValue
18
+ next: CacheValue
19
+ }
20
+
21
+ type Cache = {
22
+ state: CacheState
23
+ set: (updater: NonNullableUpdater<CacheState>) => void
24
+ }
25
+
26
+ let cache: Cache
27
+
28
+ const sessionsStorage = typeof window !== 'undefined' && window.sessionStorage
29
+
30
+ export type ScrollRestorationOptions = {
31
+ getKey?: (location: ParsedLocation) => string
32
+ }
33
+
34
+ const defaultGetKey = (location: ParsedLocation) => location.state.key!
35
+
36
+ export function useScrollRestoration(options?: ScrollRestorationOptions) {
37
+ const { state, subscribe, resetNextScrollRef } = useRouter()
38
+
39
+ useLayoutEffect(() => {
40
+ const getKey = options?.getKey || defaultGetKey
41
+
42
+ if (sessionsStorage) {
43
+ if (!cache) {
44
+ cache = (() => {
45
+ const storageKey = 'tsr-scroll-restoration-v2'
46
+
47
+ const state: CacheState = JSON.parse(
48
+ window.sessionStorage.getItem(storageKey) || 'null',
49
+ ) || { cached: {}, next: {} }
50
+
51
+ return {
52
+ state,
53
+ set: (updater) => {
54
+ cache.state = functionalUpdate(updater, cache.state)
55
+ window.sessionStorage.setItem(
56
+ storageKey,
57
+ JSON.stringify(cache.state),
58
+ )
59
+ },
60
+ }
61
+ })()
62
+ }
63
+ }
64
+
65
+ const { history } = window
66
+ if (history.scrollRestoration) {
67
+ history.scrollRestoration = 'manual'
68
+ }
69
+
70
+ const onScroll = (event: Event) => {
71
+ if (weakScrolledElements.has(event.target)) return
72
+ weakScrolledElements.add(event.target)
73
+
74
+ const elementSelector =
75
+ event.target === document || event.target === window
76
+ ? windowKey
77
+ : getCssSelector(event.target)
78
+
79
+ if (!cache.state.next[elementSelector]) {
80
+ cache.set((c) => ({
81
+ ...c,
82
+ next: {
83
+ ...c.next,
84
+ [elementSelector]: {
85
+ scrollX: NaN,
86
+ scrollY: NaN,
87
+ },
88
+ },
89
+ }))
90
+ }
91
+ }
92
+
93
+ const getCssSelector = (el: any): string => {
94
+ let path = [],
95
+ parent
96
+ while ((parent = el.parentNode)) {
97
+ path.unshift(
98
+ `${el.tagName}:nth-child(${
99
+ ([].indexOf as any).call(parent.children, el) + 1
100
+ })`,
101
+ )
102
+ el = parent
103
+ }
104
+ return `${path.join(' > ')}`.toLowerCase()
105
+ }
106
+
107
+ if (typeof document !== 'undefined') {
108
+ document.addEventListener('scroll', onScroll, true)
109
+ }
110
+
111
+ const unsubOnBeforeLoad = subscribe('onBeforeLoad', (event) => {
112
+ if (event.pathChanged) {
113
+ const restoreKey = getKey(event.fromLocation)
114
+ for (const elementSelector in cache.state.next) {
115
+ const entry = cache.state.next[elementSelector]!
116
+ if (elementSelector === windowKey) {
117
+ entry.scrollX = window.scrollX || 0
118
+ entry.scrollY = window.scrollY || 0
119
+ } else if (elementSelector) {
120
+ const element = document.querySelector(elementSelector)
121
+ entry.scrollX = element?.scrollLeft || 0
122
+ entry.scrollY = element?.scrollTop || 0
123
+ }
124
+
125
+ cache.set((c) => {
126
+ const next = { ...c.next }
127
+ delete next[elementSelector]
128
+
129
+ return {
130
+ ...c,
131
+ next,
132
+ cached: {
133
+ ...c.cached,
134
+ [[restoreKey, elementSelector].join(delimiter)]: entry,
135
+ },
136
+ }
137
+ })
138
+ }
139
+ }
140
+ })
141
+
142
+ const unsubOnResolved = subscribe('onResolved', (event) => {
143
+ if (event.pathChanged) {
144
+ if (!resetNextScrollRef.current) {
145
+ return
146
+ }
147
+
148
+ resetNextScrollRef.current = true
149
+
150
+ const getKey = options?.getKey || defaultGetKey
151
+
152
+ const restoreKey = getKey(event.toLocation)
153
+ let windowRestored = false
154
+
155
+ for (const cacheKey in cache.state.cached) {
156
+ const entry = cache.state.cached[cacheKey]!
157
+ const [key, elementSelector] = cacheKey.split(delimiter)
158
+ if (key === restoreKey) {
159
+ if (elementSelector === windowKey) {
160
+ windowRestored = true
161
+ window.scrollTo(entry.scrollX, entry.scrollY)
162
+ } else if (elementSelector) {
163
+ const element = document.querySelector(elementSelector)
164
+ if (element) {
165
+ element.scrollLeft = entry.scrollX
166
+ element.scrollTop = entry.scrollY
167
+ }
168
+ }
169
+ }
170
+ }
171
+
172
+ if (!windowRestored) {
173
+ window.scrollTo(0, 0)
174
+ }
175
+
176
+ cache.set((c) => ({ ...c, next: {} }))
177
+ weakScrolledElements = new WeakSet<any>()
178
+ }
179
+ })
180
+
181
+ return () => {
182
+ document.removeEventListener('scroll', onScroll)
183
+ unsubOnBeforeLoad()
184
+ unsubOnResolved()
185
+ }
186
+ }, [])
187
+ }
188
+
189
+ export function ScrollRestoration(props: ScrollRestorationOptions) {
190
+ useScrollRestoration(props)
191
+ return null
192
+ }