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

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.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/build/cjs/CatchBoundary.js +125 -0
  3. package/build/cjs/CatchBoundary.js.map +1 -0
  4. package/build/cjs/Matches.js +223 -0
  5. package/build/cjs/Matches.js.map +1 -0
  6. package/build/cjs/RouterProvider.js +1020 -0
  7. package/build/cjs/RouterProvider.js.map +1 -0
  8. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +1 -19
  9. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
  10. package/build/cjs/fileRoute.js +29 -0
  11. package/build/cjs/fileRoute.js.map +1 -0
  12. package/build/cjs/index.js +124 -0
  13. package/build/cjs/index.js.map +1 -0
  14. package/build/cjs/lazyRouteComponent.js +57 -0
  15. package/build/cjs/lazyRouteComponent.js.map +1 -0
  16. package/build/cjs/link.js +150 -0
  17. package/build/cjs/link.js.map +1 -0
  18. package/build/cjs/path.js +211 -0
  19. package/build/cjs/path.js.map +1 -0
  20. package/build/cjs/qss.js +65 -0
  21. package/build/cjs/qss.js.map +1 -0
  22. package/build/cjs/redirects.js +27 -0
  23. package/build/cjs/redirects.js.map +1 -0
  24. package/build/cjs/route.js +133 -0
  25. package/build/cjs/route.js.map +1 -0
  26. package/build/cjs/router.js +203 -0
  27. package/build/cjs/router.js.map +1 -0
  28. package/build/cjs/searchParams.js +83 -0
  29. package/build/cjs/searchParams.js.map +1 -0
  30. package/build/cjs/useBlocker.js +64 -0
  31. package/build/cjs/useBlocker.js.map +1 -0
  32. package/build/cjs/useNavigate.js +78 -0
  33. package/build/cjs/useNavigate.js.map +1 -0
  34. package/build/cjs/useParams.js +28 -0
  35. package/build/cjs/useParams.js.map +1 -0
  36. package/build/cjs/useSearch.js +27 -0
  37. package/build/cjs/useSearch.js.map +1 -0
  38. package/build/cjs/utils.js +225 -0
  39. package/build/cjs/utils.js.map +1 -0
  40. package/build/esm/index.js +1894 -2565
  41. package/build/esm/index.js.map +1 -1
  42. package/build/stats-html.html +59 -49
  43. package/build/stats-react.json +815 -43
  44. package/build/types/CatchBoundary.d.ts +33 -0
  45. package/build/types/Matches.d.ts +31 -0
  46. package/build/types/RouterProvider.d.ts +78 -0
  47. package/build/types/awaited.d.ts +0 -0
  48. package/build/types/defer.d.ts +0 -0
  49. package/build/types/fileRoute.d.ts +32 -0
  50. package/build/types/history.d.ts +7 -0
  51. package/build/types/index.d.ts +24 -104
  52. package/build/types/injectHtml.d.ts +0 -0
  53. package/build/types/lazyRouteComponent.d.ts +2 -0
  54. package/build/types/link.d.ts +105 -0
  55. package/build/types/location.d.ts +14 -0
  56. package/build/types/path.d.ts +16 -0
  57. package/build/types/qss.d.ts +2 -0
  58. package/build/types/redirects.d.ts +10 -0
  59. package/build/types/route.d.ts +261 -0
  60. package/build/types/routeInfo.d.ts +22 -0
  61. package/build/types/router.d.ts +117 -0
  62. package/build/types/scroll-restoration.d.ts +0 -0
  63. package/build/types/searchParams.d.ts +7 -0
  64. package/build/types/useBlocker.d.ts +8 -0
  65. package/build/types/useNavigate.d.ts +20 -0
  66. package/build/types/useParams.d.ts +7 -0
  67. package/build/types/useSearch.d.ts +7 -0
  68. package/build/types/utils.d.ts +65 -0
  69. package/build/umd/index.development.js +2131 -2469
  70. package/build/umd/index.development.js.map +1 -1
  71. package/build/umd/index.production.js +4 -4
  72. package/build/umd/index.production.js.map +1 -1
  73. package/package.json +9 -10
  74. package/src/CatchBoundary.tsx +97 -0
  75. package/src/Matches.tsx +315 -0
  76. package/src/RouterProvider.tsx +1457 -0
  77. package/src/awaited.tsx +40 -0
  78. package/src/defer.ts +55 -0
  79. package/src/fileRoute.ts +145 -0
  80. package/src/history.ts +8 -0
  81. package/src/index.tsx +28 -693
  82. package/src/injectHtml.ts +28 -0
  83. package/src/lazyRouteComponent.tsx +33 -0
  84. package/src/link.tsx +507 -0
  85. package/src/location.ts +15 -0
  86. package/src/path.ts +256 -0
  87. package/src/qss.ts +53 -0
  88. package/src/redirects.ts +31 -0
  89. package/src/route.ts +786 -0
  90. package/src/routeInfo.ts +68 -0
  91. package/src/router.ts +374 -0
  92. package/src/scroll-restoration.tsx +205 -0
  93. package/src/searchParams.ts +79 -0
  94. package/src/useBlocker.tsx +34 -0
  95. package/src/useNavigate.tsx +109 -0
  96. package/src/useParams.tsx +25 -0
  97. package/src/useSearch.tsx +25 -0
  98. package/src/utils.ts +337 -0
  99. package/build/cjs/react-router/src/index.js +0 -466
  100. package/build/cjs/react-router/src/index.js.map +0 -1
  101. package/build/cjs/router-core/build/esm/index.js +0 -2523
  102. package/build/cjs/router-core/build/esm/index.js.map +0 -1
@@ -0,0 +1,68 @@
1
+ import { AnyRoute, Route } from './route'
2
+ import { Expand, UnionToIntersection } from './utils'
3
+
4
+ export type ParseRoute<TRouteTree extends AnyRoute> =
5
+ | TRouteTree
6
+ | ParseRouteChildren<TRouteTree>
7
+
8
+ export type ParseRouteChildren<TRouteTree extends AnyRoute> =
9
+ TRouteTree extends Route<
10
+ any,
11
+ any,
12
+ any,
13
+ any,
14
+ any,
15
+ any,
16
+ any,
17
+ any,
18
+ any,
19
+ any,
20
+ any,
21
+ any,
22
+ infer TChildren,
23
+ any
24
+ >
25
+ ? unknown extends TChildren
26
+ ? never
27
+ : TChildren extends AnyRoute[]
28
+ ? {
29
+ [TId in TChildren[number]['id'] as string]: ParseRoute<
30
+ TChildren[number]
31
+ >
32
+ }[string]
33
+ : never
34
+ : never
35
+
36
+ export type RoutesById<TRouteTree extends AnyRoute> = {
37
+ [K in ParseRoute<TRouteTree> as K['id']]: K
38
+ }
39
+
40
+ export type RouteById<TRouteTree extends AnyRoute, TId> = Extract<
41
+ ParseRoute<TRouteTree>,
42
+ { id: TId }
43
+ >
44
+
45
+ export type RouteIds<TRouteTree extends AnyRoute> = ParseRoute<TRouteTree>['id']
46
+
47
+ export type RoutesByPath<TRouteTree extends AnyRoute> = {
48
+ [K in ParseRoute<TRouteTree> as K['fullPath']]: K
49
+ }
50
+
51
+ export type RouteByPath<TRouteTree extends AnyRoute, TPath> = Extract<
52
+ ParseRoute<TRouteTree>,
53
+ { fullPath: TPath }
54
+ >
55
+
56
+ export type RoutePaths<TRouteTree extends AnyRoute> =
57
+ | ParseRoute<TRouteTree>['fullPath']
58
+ | '/'
59
+
60
+ export type FullSearchSchema<TRouteTree extends AnyRoute> = Partial<
61
+ Expand<
62
+ UnionToIntersection<ParseRoute<TRouteTree>['types']['fullSearchSchema']>
63
+ >
64
+ >
65
+
66
+ export type AllParams<TRouteTree extends AnyRoute> = Expand<
67
+ UnionToIntersection<ParseRoute<TRouteTree>['types']['allParams']>
68
+ >
package/src/router.ts ADDED
@@ -0,0 +1,374 @@
1
+ import { RouterHistory } from '@tanstack/history'
2
+
3
+ //
4
+
5
+ import {
6
+ AnySearchSchema,
7
+ AnyRoute,
8
+ AnyContext,
9
+ AnyPathParams,
10
+ RouteMask,
11
+ } from './route'
12
+ import { FullSearchSchema } from './routeInfo'
13
+ import { defaultParseSearch, defaultStringifySearch } from './searchParams'
14
+ import { PickAsRequired, Updater, NonNullableUpdater } from './utils'
15
+ import {
16
+ ErrorRouteComponent,
17
+ PendingRouteComponent,
18
+ RouteComponent,
19
+ } from './route'
20
+ import { RouteMatch } from './RouterProvider'
21
+ import { ParsedLocation } from './location'
22
+ import { LocationState } from './location'
23
+ import { SearchSerializer, SearchParser } from './searchParams'
24
+ import { RouterContext } from './RouterProvider'
25
+
26
+ //
27
+
28
+ declare global {
29
+ interface Window {
30
+ __TSR_DEHYDRATED__?: HydrationCtx
31
+ __TSR_ROUTER_CONTEXT__?: React.Context<RouterContext<any>>
32
+ }
33
+ }
34
+
35
+ export interface Register {
36
+ // router: Router
37
+ }
38
+
39
+ export type AnyRouter = Router<any, any>
40
+
41
+ export type RegisteredRouter = Register extends {
42
+ router: infer TRouter extends AnyRouter
43
+ }
44
+ ? TRouter
45
+ : AnyRouter
46
+
47
+ export type HydrationCtx = {
48
+ router: DehydratedRouter
49
+ payload: Record<string, any>
50
+ }
51
+
52
+ export type RouterContextOptions<TRouteTree extends AnyRoute> =
53
+ AnyContext extends TRouteTree['types']['routerContext']
54
+ ? {
55
+ context?: TRouteTree['types']['routerContext']
56
+ }
57
+ : {
58
+ context: TRouteTree['types']['routerContext']
59
+ }
60
+
61
+ export interface RouterOptions<
62
+ TRouteTree extends AnyRoute,
63
+ TDehydrated extends Record<string, any> = Record<string, any>,
64
+ > {
65
+ history?: RouterHistory
66
+ stringifySearch?: SearchSerializer
67
+ parseSearch?: SearchParser
68
+ defaultPreload?: false | 'intent'
69
+ defaultPreloadDelay?: number
70
+ defaultComponent?: RouteComponent<AnySearchSchema, AnyPathParams, AnyContext>
71
+ defaultErrorComponent?: ErrorRouteComponent<
72
+ AnySearchSchema,
73
+ AnyPathParams,
74
+ AnyContext
75
+ >
76
+ defaultPendingComponent?: PendingRouteComponent<
77
+ AnySearchSchema,
78
+ AnyPathParams,
79
+ AnyContext
80
+ >
81
+ defaultMaxAge?: number
82
+ defaultGcMaxAge?: number
83
+ defaultPreloadMaxAge?: number
84
+ caseSensitive?: boolean
85
+ routeTree?: TRouteTree
86
+ basepath?: string
87
+ createRoute?: (opts: { route: AnyRoute; router: AnyRouter }) => void
88
+ context?: TRouteTree['types']['routerContext']
89
+ // dehydrate?: () => TDehydrated
90
+ // hydrate?: (dehydrated: TDehydrated) => void
91
+ routeMasks?: RouteMask<TRouteTree>[]
92
+ unmaskOnReload?: boolean
93
+ }
94
+
95
+ export interface RouterState<TRouteTree extends AnyRoute = AnyRoute> {
96
+ status: 'pending' | 'idle'
97
+ matches: RouteMatch<TRouteTree>[]
98
+ pendingMatches: RouteMatch<TRouteTree>[]
99
+ location: ParsedLocation<FullSearchSchema<TRouteTree>>
100
+ resolvedLocation: undefined | ParsedLocation<FullSearchSchema<TRouteTree>>
101
+ lastUpdated: number
102
+ }
103
+
104
+ export type ListenerFn<TEvent extends RouterEvent> = (event: TEvent) => void
105
+
106
+ export interface BuildNextOptions {
107
+ to?: string | number | null
108
+ params?: true | Updater<unknown>
109
+ search?: true | Updater<unknown>
110
+ hash?: true | Updater<string>
111
+ state?: true | NonNullableUpdater<LocationState>
112
+ mask?: {
113
+ to?: string | number | null
114
+ params?: true | Updater<unknown>
115
+ search?: true | Updater<unknown>
116
+ hash?: true | Updater<string>
117
+ state?: true | NonNullableUpdater<LocationState>
118
+ unmaskOnReload?: boolean
119
+ }
120
+ from?: string
121
+ }
122
+
123
+ export interface DehydratedRouterState {
124
+ dehydratedMatches: DehydratedRouteMatch[]
125
+ }
126
+
127
+ export type DehydratedRouteMatch = Pick<
128
+ RouteMatch,
129
+ 'fetchedAt' | 'invalid' | 'id' | 'status' | 'updatedAt'
130
+ >
131
+
132
+ export interface DehydratedRouter {
133
+ state: DehydratedRouterState
134
+ }
135
+
136
+ export type RouterConstructorOptions<
137
+ TRouteTree extends AnyRoute,
138
+ TDehydrated extends Record<string, any>,
139
+ > = Omit<RouterOptions<TRouteTree, TDehydrated>, 'context'> &
140
+ RouterContextOptions<TRouteTree>
141
+
142
+ export const componentTypes = [
143
+ 'component',
144
+ 'errorComponent',
145
+ 'pendingComponent',
146
+ ] as const
147
+
148
+ export type RouterEvents = {
149
+ onBeforeLoad: {
150
+ type: 'onBeforeLoad'
151
+ from: undefined | ParsedLocation
152
+ to: ParsedLocation
153
+ pathChanged: boolean
154
+ }
155
+ onLoad: {
156
+ type: 'onLoad'
157
+ from: undefined | ParsedLocation
158
+ to: ParsedLocation
159
+ pathChanged: boolean
160
+ }
161
+ }
162
+
163
+ export type RouterEvent = RouterEvents[keyof RouterEvents]
164
+
165
+ export type RouterListener<TRouterEvent extends RouterEvent> = {
166
+ eventType: TRouterEvent['type']
167
+ fn: ListenerFn<TRouterEvent>
168
+ }
169
+
170
+ export class Router<
171
+ TRouteTree extends AnyRoute = AnyRoute,
172
+ TDehydrated extends Record<string, any> = Record<string, any>,
173
+ > {
174
+ options: PickAsRequired<
175
+ RouterOptions<TRouteTree, TDehydrated>,
176
+ 'stringifySearch' | 'parseSearch' | 'context'
177
+ >
178
+ routeTree: TRouteTree
179
+ // dehydratedData?: TDehydrated
180
+ // resetNextScroll = false
181
+ // tempLocationKey = `${Math.round(Math.random() * 10000000)}`
182
+
183
+ constructor(options: RouterConstructorOptions<TRouteTree, TDehydrated>) {
184
+ this.options = {
185
+ defaultPreloadDelay: 50,
186
+ context: undefined!,
187
+ ...options,
188
+ stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
189
+ parseSearch: options?.parseSearch ?? defaultParseSearch,
190
+ }
191
+
192
+ this.routeTree = this.options.routeTree as TRouteTree
193
+ }
194
+
195
+ subscribers = new Set<RouterListener<RouterEvent>>()
196
+
197
+ subscribe = <TType extends keyof RouterEvents>(
198
+ eventType: TType,
199
+ fn: ListenerFn<RouterEvents[TType]>,
200
+ ) => {
201
+ const listener: RouterListener<any> = {
202
+ eventType,
203
+ fn,
204
+ }
205
+
206
+ this.subscribers.add(listener)
207
+
208
+ return () => {
209
+ this.subscribers.delete(listener)
210
+ }
211
+ }
212
+
213
+ emit = (routerEvent: RouterEvent) => {
214
+ this.subscribers.forEach((listener) => {
215
+ if (listener.eventType === routerEvent.type) {
216
+ listener.fn(routerEvent)
217
+ }
218
+ })
219
+ }
220
+
221
+ // dehydrate = (): DehydratedRouter => {
222
+ // return {
223
+ // state: {
224
+ // dehydratedMatches: state.matches.map((d) =>
225
+ // pick(d, ['fetchedAt', 'invalid', 'id', 'status', 'updatedAt']),
226
+ // ),
227
+ // },
228
+ // }
229
+ // }
230
+
231
+ // hydrate = async (__do_not_use_server_ctx?: HydrationCtx) => {
232
+ // let _ctx = __do_not_use_server_ctx
233
+ // // Client hydrates from window
234
+ // if (typeof document !== 'undefined') {
235
+ // _ctx = window.__TSR_DEHYDRATED__
236
+ // }
237
+
238
+ // invariant(
239
+ // _ctx,
240
+ // 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?',
241
+ // )
242
+
243
+ // const ctx = _ctx
244
+ // this.dehydratedData = ctx.payload as any
245
+ // this.options.hydrate?.(ctx.payload as any)
246
+ // const dehydratedState = ctx.router.state
247
+
248
+ // let matches = this.matchRoutes(
249
+ // state.location.pathname,
250
+ // state.location.search,
251
+ // ).map((match) => {
252
+ // const dehydratedMatch = dehydratedState.dehydratedMatches.find(
253
+ // (d) => d.id === match.id,
254
+ // )
255
+
256
+ // invariant(
257
+ // dehydratedMatch,
258
+ // `Could not find a client-side match for dehydrated match with id: ${match.id}!`,
259
+ // )
260
+
261
+ // if (dehydratedMatch) {
262
+ // return {
263
+ // ...match,
264
+ // ...dehydratedMatch,
265
+ // }
266
+ // }
267
+ // return match
268
+ // })
269
+
270
+ // this.setState((s) => {
271
+ // return {
272
+ // ...s,
273
+ // matches: dehydratedState.dehydratedMatches as any,
274
+ // }
275
+ // })
276
+ // }
277
+
278
+ // TODO:
279
+ // injectedHtml: (string | (() => Promise<string> | string))[] = []
280
+
281
+ // TODO:
282
+ // injectHtml = async (html: string | (() => Promise<string> | string)) => {
283
+ // this.injectedHtml.push(html)
284
+ // }
285
+
286
+ // TODO:
287
+ // dehydrateData = <T>(key: any, getData: T | (() => Promise<T> | T)) => {
288
+ // if (typeof document === 'undefined') {
289
+ // const strKey = typeof key === 'string' ? key : JSON.stringify(key)
290
+
291
+ // this.injectHtml(async () => {
292
+ // const id = `__TSR_DEHYDRATED__${strKey}`
293
+ // const data =
294
+ // typeof getData === 'function' ? await (getData as any)() : getData
295
+ // return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(
296
+ // strKey,
297
+ // )}"] = ${JSON.stringify(data)}
298
+ // ;(() => {
299
+ // var el = document.getElementById('${id}')
300
+ // el.parentElement.removeChild(el)
301
+ // })()
302
+ // </script>`
303
+ // })
304
+
305
+ // return () => this.hydrateData<T>(key)
306
+ // }
307
+
308
+ // return () => undefined
309
+ // }
310
+
311
+ // hydrateData = <T = unknown>(key: any) => {
312
+ // if (typeof document !== 'undefined') {
313
+ // const strKey = typeof key === 'string' ? key : JSON.stringify(key)
314
+
315
+ // return window[`__TSR_DEHYDRATED__${strKey}` as any] as T
316
+ // }
317
+
318
+ // return undefined
319
+ // }
320
+
321
+ // resolveMatchPromise = (matchId: string, key: string, value: any) => {
322
+ // state.matches
323
+ // .find((d) => d.id === matchId)
324
+ // ?.__promisesByKey[key]?.resolve(value)
325
+ // }
326
+
327
+ // setRouteMatch = (
328
+ // id: string,
329
+ // pending: boolean,
330
+ // updater: NonNullableUpdater<RouteMatch<TRouteTree>>,
331
+ // ) => {
332
+ // const key = pending ? 'pendingMatches' : 'matches'
333
+
334
+ // this.setState((prev) => {
335
+ // return {
336
+ // ...prev,
337
+ // [key]: prev[key].map((d) => {
338
+ // if (d.id === id) {
339
+ // return functionalUpdate(updater, d)
340
+ // }
341
+
342
+ // return d
343
+ // }),
344
+ // }
345
+ // })
346
+ // }
347
+
348
+ // setPendingRouteMatch = (
349
+ // id: string,
350
+ // updater: NonNullableUpdater<RouteMatch<TRouteTree>>,
351
+ // ) => {
352
+ // this.setRouteMatch(id, true, updater)
353
+ // }
354
+ }
355
+
356
+ function escapeJSON(jsonString: string) {
357
+ return jsonString
358
+ .replace(/\\/g, '\\\\') // Escape backslashes
359
+ .replace(/'/g, "\\'") // Escape single quotes
360
+ .replace(/"/g, '\\"') // Escape double quotes
361
+ }
362
+
363
+ // A function that takes an import() argument which is a function and returns a new function that will
364
+ // proxy arguments from the caller to the imported function, retaining all type
365
+ // information along the way
366
+ export function lazyFn<
367
+ T extends Record<string, (...args: any[]) => any>,
368
+ TKey extends keyof T = 'default',
369
+ >(fn: () => Promise<T>, key?: TKey) {
370
+ return async (...args: Parameters<T[TKey]>): Promise<ReturnType<T[TKey]>> => {
371
+ const imported = await fn()
372
+ return imported[key || 'default'](...args)
373
+ }
374
+ }
@@ -0,0 +1,205 @@
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
+ // }