@tanstack/react-router 0.0.1-beta.230 → 0.0.1-beta.232
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/Matches.js +4 -1
- package/build/cjs/Matches.js.map +1 -1
- package/build/cjs/RouterProvider.js +34 -37
- package/build/cjs/RouterProvider.js.map +1 -1
- package/build/cjs/awaited.js +1 -1
- package/build/cjs/awaited.js.map +1 -1
- package/build/cjs/index.js +4 -3
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/router.js +70 -70
- package/build/cjs/router.js.map +1 -1
- package/build/cjs/scroll-restoration.js +52 -29
- package/build/cjs/scroll-restoration.js.map +1 -1
- package/build/esm/index.js +155 -133
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +293 -293
- package/build/types/RouterProvider.d.ts +0 -5
- package/build/types/router.d.ts +14 -2
- package/build/types/scroll-restoration.d.ts +12 -0
- package/build/umd/index.development.js +154 -131
- 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/Matches.tsx +3 -1
- package/src/RouterProvider.tsx +42 -46
- package/src/awaited.tsx +1 -1
- package/src/router.ts +106 -84
- package/src/scroll-restoration.tsx +81 -43
|
@@ -24,11 +24,6 @@ export type MatchRouteFn<TRouteTree extends AnyRoute> = <TFrom extends RoutePath
|
|
|
24
24
|
export type BuildLocationFn<TRouteTree extends AnyRoute> = (opts: BuildNextOptions) => ParsedLocation;
|
|
25
25
|
export type InjectedHtmlEntry = string | (() => Promise<string> | string);
|
|
26
26
|
export declare const routerContext: React.Context<Router<any, Record<string, any>>>;
|
|
27
|
-
export declare class SearchParamError extends Error {
|
|
28
|
-
}
|
|
29
|
-
export declare class PathParamError extends Error {
|
|
30
|
-
}
|
|
31
|
-
export declare function getInitialRouterState(location: ParsedLocation): RouterState<any>;
|
|
32
27
|
export declare function RouterProvider<TRouteTree extends AnyRoute = RegisteredRouter['routeTree'], TDehydrated extends Record<string, any> = Record<string, any>>({ router, ...rest }: RouterProps<TRouteTree, TDehydrated>): React.JSX.Element;
|
|
33
28
|
export declare function getRouteMatch<TRouteTree extends AnyRoute>(state: RouterState<TRouteTree>, id: string): undefined | RouteMatch<TRouteTree>;
|
|
34
29
|
export declare function useRouterState<TSelected = RouterState<RegisteredRouter['routeTree']>>(opts?: {
|
package/build/types/router.d.ts
CHANGED
|
@@ -46,8 +46,13 @@ export interface RouterOptions<TRouteTree extends AnyRoute, TDehydrated extends
|
|
|
46
46
|
routeTree?: TRouteTree;
|
|
47
47
|
basepath?: string;
|
|
48
48
|
context?: TRouteTree['types']['routerContext'];
|
|
49
|
+
dehydrate?: () => TDehydrated;
|
|
50
|
+
hydrate?: (dehydrated: TDehydrated) => void;
|
|
49
51
|
routeMasks?: RouteMask<TRouteTree>[];
|
|
50
52
|
unmaskOnReload?: boolean;
|
|
53
|
+
Wrap?: (props: {
|
|
54
|
+
children: any;
|
|
55
|
+
}) => JSX.Element;
|
|
51
56
|
}
|
|
52
57
|
export interface RouterState<TRouteTree extends AnyRoute = AnyRoute> {
|
|
53
58
|
status: 'pending' | 'idle';
|
|
@@ -128,8 +133,8 @@ export declare class Router<TRouteTree extends AnyRoute = AnyRoute, TDehydrated
|
|
|
128
133
|
flatRoutes: AnyRoute[];
|
|
129
134
|
constructor(options: RouterConstructorOptions<TRouteTree, TDehydrated>);
|
|
130
135
|
startReactTransition: (fn: () => void) => void;
|
|
131
|
-
setState: (
|
|
132
|
-
|
|
136
|
+
setState: (updater: NonNullableUpdater<RouterState<TRouteTree>>) => void;
|
|
137
|
+
update: (newOptions: RouterConstructorOptions<TRouteTree, TDehydrated>) => void;
|
|
133
138
|
buildRouteTree: () => void;
|
|
134
139
|
subscribe: <TType extends keyof RouterEvents>(eventType: TType, fn: ListenerFn<RouterEvents[TType]>) => () => void;
|
|
135
140
|
emit: (routerEvent: RouterEvent) => void;
|
|
@@ -163,5 +168,12 @@ export declare class Router<TRouteTree extends AnyRoute = AnyRoute, TDehydrated
|
|
|
163
168
|
injectHtml: (html: string | (() => Promise<string> | string)) => Promise<void>;
|
|
164
169
|
dehydrateData: <T>(key: any, getData: T | (() => T | Promise<T>)) => () => T | undefined;
|
|
165
170
|
hydrateData: <T extends unknown = unknown>(key: any) => T | undefined;
|
|
171
|
+
dehydrate: () => DehydratedRouter;
|
|
172
|
+
hydrate: (__do_not_use_server_ctx?: HydrationCtx) => Promise<void>;
|
|
166
173
|
}
|
|
167
174
|
export declare function lazyFn<T extends Record<string, (...args: any[]) => any>, TKey extends keyof T = 'default'>(fn: () => Promise<T>, key?: TKey): (...args: Parameters<T[TKey]>) => Promise<ReturnType<T[TKey]>>;
|
|
175
|
+
export declare class SearchParamError extends Error {
|
|
176
|
+
}
|
|
177
|
+
export declare class PathParamError extends Error {
|
|
178
|
+
}
|
|
179
|
+
export declare function getInitialRouterState(location: ParsedLocation): RouterState<any>;
|
|
@@ -4,3 +4,15 @@ export type ScrollRestorationOptions = {
|
|
|
4
4
|
};
|
|
5
5
|
export declare function useScrollRestoration(options?: ScrollRestorationOptions): void;
|
|
6
6
|
export declare function ScrollRestoration(props: ScrollRestorationOptions): null;
|
|
7
|
+
export declare function useElementScrollRestoration(options: ({
|
|
8
|
+
id: string;
|
|
9
|
+
getElement?: () => Element | undefined | null;
|
|
10
|
+
} | {
|
|
11
|
+
id?: string;
|
|
12
|
+
getElement: () => Element | undefined | null;
|
|
13
|
+
}) & {
|
|
14
|
+
getKey?: (location: ParsedLocation) => string;
|
|
15
|
+
}): {
|
|
16
|
+
scrollX: number;
|
|
17
|
+
scrollY: number;
|
|
18
|
+
} | undefined;
|
|
@@ -976,7 +976,10 @@
|
|
|
976
976
|
const match = matches[0];
|
|
977
977
|
const routeId = match?.routeId;
|
|
978
978
|
const route = routesById[routeId];
|
|
979
|
-
const
|
|
979
|
+
const router = useRouter();
|
|
980
|
+
const locationKey = router.latestLocation.state?.key;
|
|
981
|
+
// const locationKey = useRouterState().location.state?.key
|
|
982
|
+
|
|
980
983
|
const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent;
|
|
981
984
|
const pendingElement = PendingComponent ? /*#__PURE__*/React__namespace.createElement(PendingComponent, {
|
|
982
985
|
useMatch: route.useMatch,
|
|
@@ -1120,24 +1123,12 @@
|
|
|
1120
1123
|
if (typeof document !== 'undefined') {
|
|
1121
1124
|
window.__TSR_ROUTER_CONTEXT__ = routerContext;
|
|
1122
1125
|
}
|
|
1123
|
-
class SearchParamError extends Error {}
|
|
1124
|
-
class PathParamError extends Error {}
|
|
1125
|
-
function getInitialRouterState(location) {
|
|
1126
|
-
return {
|
|
1127
|
-
status: 'idle',
|
|
1128
|
-
resolvedLocation: location,
|
|
1129
|
-
location,
|
|
1130
|
-
matches: [],
|
|
1131
|
-
pendingMatches: [],
|
|
1132
|
-
lastUpdated: Date.now()
|
|
1133
|
-
};
|
|
1134
|
-
}
|
|
1135
1126
|
function RouterProvider({
|
|
1136
1127
|
router,
|
|
1137
1128
|
...rest
|
|
1138
1129
|
}) {
|
|
1139
1130
|
// Allow the router to update options on the router instance
|
|
1140
|
-
router.
|
|
1131
|
+
router.update({
|
|
1141
1132
|
...router.options,
|
|
1142
1133
|
...rest,
|
|
1143
1134
|
context: {
|
|
@@ -1145,29 +1136,44 @@
|
|
|
1145
1136
|
...rest?.context
|
|
1146
1137
|
}
|
|
1147
1138
|
});
|
|
1139
|
+
const inner = /*#__PURE__*/React__namespace.createElement(RouterProviderInner, {
|
|
1140
|
+
router: router
|
|
1141
|
+
});
|
|
1142
|
+
if (router.options.Wrap) {
|
|
1143
|
+
return /*#__PURE__*/React__namespace.createElement(router.options.Wrap, null, inner);
|
|
1144
|
+
}
|
|
1145
|
+
return inner;
|
|
1146
|
+
}
|
|
1147
|
+
function RouterProviderInner({
|
|
1148
|
+
router
|
|
1149
|
+
}) {
|
|
1148
1150
|
const [preState, setState] = React__namespace.useState(() => router.state);
|
|
1149
1151
|
const [isTransitioning, startReactTransition] = React__namespace.useTransition();
|
|
1152
|
+
const isAnyTransitioning = isTransitioning || preState.matches.some(d => d.status === 'pending');
|
|
1150
1153
|
const state = React__namespace.useMemo(() => ({
|
|
1151
1154
|
...preState,
|
|
1152
|
-
status:
|
|
1155
|
+
status: isAnyTransitioning ? 'pending' : 'idle',
|
|
1153
1156
|
location: isTransitioning ? router.latestLocation : preState.location,
|
|
1154
1157
|
pendingMatches: router.pendingMatches
|
|
1155
1158
|
}), [preState, isTransitioning]);
|
|
1156
1159
|
router.setState = setState;
|
|
1157
1160
|
router.state = state;
|
|
1158
1161
|
router.startReactTransition = startReactTransition;
|
|
1159
|
-
|
|
1162
|
+
const tryLoad = () => {
|
|
1163
|
+
if (state.location !== router.latestLocation) {
|
|
1164
|
+
startReactTransition(() => {
|
|
1165
|
+
try {
|
|
1166
|
+
router.load();
|
|
1167
|
+
} catch (err) {
|
|
1168
|
+
console.error(err);
|
|
1169
|
+
}
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
useLayoutEffect$1(() => {
|
|
1160
1174
|
const unsub = router.history.subscribe(() => {
|
|
1161
1175
|
router.latestLocation = router.parseLocation(router.latestLocation);
|
|
1162
|
-
|
|
1163
|
-
startReactTransition(() => {
|
|
1164
|
-
try {
|
|
1165
|
-
router.load();
|
|
1166
|
-
} catch (err) {
|
|
1167
|
-
console.error(err);
|
|
1168
|
-
}
|
|
1169
|
-
});
|
|
1170
|
-
}
|
|
1176
|
+
tryLoad();
|
|
1171
1177
|
});
|
|
1172
1178
|
const nextLocation = router.buildLocation({
|
|
1173
1179
|
search: true,
|
|
@@ -1185,7 +1191,7 @@
|
|
|
1185
1191
|
unsub();
|
|
1186
1192
|
};
|
|
1187
1193
|
}, [router.history]);
|
|
1188
|
-
|
|
1194
|
+
useLayoutEffect$1(() => {
|
|
1189
1195
|
if (!isTransitioning && state.resolvedLocation !== state.location) {
|
|
1190
1196
|
router.emit({
|
|
1191
1197
|
type: 'onResolved',
|
|
@@ -1200,14 +1206,10 @@
|
|
|
1200
1206
|
}));
|
|
1201
1207
|
}
|
|
1202
1208
|
});
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
} catch (err) {
|
|
1208
|
-
console.error(err);
|
|
1209
|
-
}
|
|
1210
|
-
});
|
|
1209
|
+
useLayoutEffect$1(() => {
|
|
1210
|
+
if (!window.__TSR_DEHYDRATED__) {
|
|
1211
|
+
tryLoad();
|
|
1212
|
+
}
|
|
1211
1213
|
}, []);
|
|
1212
1214
|
return /*#__PURE__*/React__namespace.createElement(routerContext.Provider, {
|
|
1213
1215
|
value: router
|
|
@@ -1224,7 +1226,7 @@
|
|
|
1224
1226
|
return opts?.select ? opts.select(state) : state;
|
|
1225
1227
|
}
|
|
1226
1228
|
function useRouter() {
|
|
1227
|
-
const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext;
|
|
1229
|
+
const resolvedContext = typeof document !== 'undefined' ? window.__TSR_ROUTER_CONTEXT__ || routerContext : routerContext;
|
|
1228
1230
|
const value = React__namespace.useContext(resolvedContext);
|
|
1229
1231
|
warning(value, 'useRouter must be used inside a <RouterProvider> component!');
|
|
1230
1232
|
return value;
|
|
@@ -1264,7 +1266,7 @@
|
|
|
1264
1266
|
promise.__deferredState = state;
|
|
1265
1267
|
}
|
|
1266
1268
|
if (state.status === 'pending') {
|
|
1267
|
-
throw promise;
|
|
1269
|
+
throw new Promise(r => setTimeout(r, 1)).then(() => promise);
|
|
1268
1270
|
}
|
|
1269
1271
|
if (state.status === 'error') {
|
|
1270
1272
|
throw state.error;
|
|
@@ -1550,6 +1552,8 @@
|
|
|
1550
1552
|
};
|
|
1551
1553
|
}
|
|
1552
1554
|
|
|
1555
|
+
// import warning from 'tiny-warning'
|
|
1556
|
+
|
|
1553
1557
|
//
|
|
1554
1558
|
|
|
1555
1559
|
const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
|
|
@@ -1567,7 +1571,7 @@
|
|
|
1567
1571
|
// Must build in constructor
|
|
1568
1572
|
|
|
1569
1573
|
constructor(options) {
|
|
1570
|
-
this.
|
|
1574
|
+
this.update({
|
|
1571
1575
|
defaultPreloadDelay: 50,
|
|
1572
1576
|
defaultPendingMs: 1000,
|
|
1573
1577
|
defaultPendingMinMs: 500,
|
|
@@ -1577,20 +1581,22 @@
|
|
|
1577
1581
|
parseSearch: options?.parseSearch ?? defaultParseSearch
|
|
1578
1582
|
});
|
|
1579
1583
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1584
|
+
|
|
1585
|
+
// These are default implementations that can optionally be overridden
|
|
1586
|
+
// by the router provider once rendered. We provide these so that the
|
|
1587
|
+
// router can be used in a non-react environment if necessary
|
|
1588
|
+
startReactTransition = fn => fn();
|
|
1589
|
+
setState = updater => {
|
|
1590
|
+
this.state = functionalUpdate(updater, this.state);
|
|
1585
1591
|
};
|
|
1586
|
-
|
|
1592
|
+
update = newOptions => {
|
|
1587
1593
|
this.options = {
|
|
1588
1594
|
...this.options,
|
|
1589
1595
|
...newOptions
|
|
1590
1596
|
};
|
|
1591
1597
|
this.basepath = `/${trimPath(newOptions.basepath ?? '') ?? ''}`;
|
|
1592
1598
|
if (!this.history || this.options.history && this.options.history !== this.history) {
|
|
1593
|
-
this.history = this.options.history ?? createBrowserHistory();
|
|
1599
|
+
this.history = this.options.history ?? (typeof document !== 'undefined' ? createBrowserHistory() : createMemoryHistory());
|
|
1594
1600
|
this.latestLocation = this.parseLocation();
|
|
1595
1601
|
}
|
|
1596
1602
|
if (this.options.routeTree !== this.routeTree) {
|
|
@@ -2230,6 +2236,7 @@
|
|
|
2230
2236
|
// forcefully show the pending component
|
|
2231
2237
|
if (pendingPromise) {
|
|
2232
2238
|
pendingPromise.then(() => {
|
|
2239
|
+
if (latestPromise = checkLatest()) return;
|
|
2233
2240
|
didShowPending = true;
|
|
2234
2241
|
matches[index] = match = {
|
|
2235
2242
|
...match,
|
|
@@ -2249,6 +2256,7 @@
|
|
|
2249
2256
|
if (didShowPending && pendingMinMs) {
|
|
2250
2257
|
await new Promise(r => setTimeout(r, pendingMinMs));
|
|
2251
2258
|
}
|
|
2259
|
+
if (latestPromise = checkLatest()) return await latestPromise;
|
|
2252
2260
|
matches[index] = match = {
|
|
2253
2261
|
...match,
|
|
2254
2262
|
error: undefined,
|
|
@@ -2317,7 +2325,7 @@
|
|
|
2317
2325
|
// Ingest the new matches
|
|
2318
2326
|
this.setState(s => ({
|
|
2319
2327
|
...s,
|
|
2320
|
-
status: 'pending',
|
|
2328
|
+
// status: 'pending',
|
|
2321
2329
|
location: next,
|
|
2322
2330
|
matches
|
|
2323
2331
|
}));
|
|
@@ -2346,6 +2354,7 @@
|
|
|
2346
2354
|
// ...s,
|
|
2347
2355
|
// status: 'idle',
|
|
2348
2356
|
// resolvedLocation: s.location,
|
|
2357
|
+
// matches,
|
|
2349
2358
|
// }))
|
|
2350
2359
|
|
|
2351
2360
|
//
|
|
@@ -2546,63 +2555,42 @@
|
|
|
2546
2555
|
}
|
|
2547
2556
|
return undefined;
|
|
2548
2557
|
};
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
// invariant(
|
|
2586
|
-
// dehydratedMatch,
|
|
2587
|
-
// `Could not find a client-side match for dehydrated match with id: ${match.id}!`,
|
|
2588
|
-
// )
|
|
2589
|
-
|
|
2590
|
-
// if (dehydratedMatch) {
|
|
2591
|
-
// return {
|
|
2592
|
-
// ...match,
|
|
2593
|
-
// ...dehydratedMatch,
|
|
2594
|
-
// }
|
|
2595
|
-
// }
|
|
2596
|
-
// return match
|
|
2597
|
-
// })
|
|
2598
|
-
|
|
2599
|
-
// this.setState((s) => {
|
|
2600
|
-
// return {
|
|
2601
|
-
// ...s,
|
|
2602
|
-
// matches: dehydratedState.dehydratedMatches as any,
|
|
2603
|
-
// }
|
|
2604
|
-
// })
|
|
2605
|
-
// }
|
|
2558
|
+
dehydrate = () => {
|
|
2559
|
+
return {
|
|
2560
|
+
state: {
|
|
2561
|
+
dehydratedMatches: this.state.matches.map(d => pick(d, ['fetchedAt', 'invalid', 'id', 'status', 'updatedAt', 'loaderData']))
|
|
2562
|
+
}
|
|
2563
|
+
};
|
|
2564
|
+
};
|
|
2565
|
+
hydrate = async __do_not_use_server_ctx => {
|
|
2566
|
+
let _ctx = __do_not_use_server_ctx;
|
|
2567
|
+
// Client hydrates from window
|
|
2568
|
+
if (typeof document !== 'undefined') {
|
|
2569
|
+
_ctx = window.__TSR_DEHYDRATED__;
|
|
2570
|
+
}
|
|
2571
|
+
invariant(_ctx, 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?');
|
|
2572
|
+
const ctx = _ctx;
|
|
2573
|
+
this.dehydratedData = ctx.payload;
|
|
2574
|
+
this.options.hydrate?.(ctx.payload);
|
|
2575
|
+
const dehydratedState = ctx.router.state;
|
|
2576
|
+
let matches = this.matchRoutes(this.state.location.pathname, this.state.location.search).map(match => {
|
|
2577
|
+
const dehydratedMatch = dehydratedState.dehydratedMatches.find(d => d.id === match.id);
|
|
2578
|
+
invariant(dehydratedMatch, `Could not find a client-side match for dehydrated match with id: ${match.id}!`);
|
|
2579
|
+
if (dehydratedMatch) {
|
|
2580
|
+
return {
|
|
2581
|
+
...match,
|
|
2582
|
+
...dehydratedMatch
|
|
2583
|
+
};
|
|
2584
|
+
}
|
|
2585
|
+
return match;
|
|
2586
|
+
});
|
|
2587
|
+
this.setState(s => {
|
|
2588
|
+
return {
|
|
2589
|
+
...s,
|
|
2590
|
+
matches: matches
|
|
2591
|
+
};
|
|
2592
|
+
});
|
|
2593
|
+
};
|
|
2606
2594
|
|
|
2607
2595
|
// resolveMatchPromise = (matchId: string, key: string, value: any) => {
|
|
2608
2596
|
// state.matches
|
|
@@ -2623,36 +2611,43 @@
|
|
|
2623
2611
|
function isCtrlEvent(e) {
|
|
2624
2612
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
|
|
2625
2613
|
}
|
|
2614
|
+
class SearchParamError extends Error {}
|
|
2615
|
+
class PathParamError extends Error {}
|
|
2616
|
+
function getInitialRouterState(location) {
|
|
2617
|
+
return {
|
|
2618
|
+
status: 'idle',
|
|
2619
|
+
resolvedLocation: location,
|
|
2620
|
+
location,
|
|
2621
|
+
matches: [],
|
|
2622
|
+
pendingMatches: [],
|
|
2623
|
+
lastUpdated: Date.now()
|
|
2624
|
+
};
|
|
2625
|
+
}
|
|
2626
2626
|
|
|
2627
2627
|
const useLayoutEffect = typeof window !== 'undefined' ? React__namespace.useLayoutEffect : React__namespace.useEffect;
|
|
2628
2628
|
const windowKey = 'window';
|
|
2629
2629
|
const delimiter = '___';
|
|
2630
2630
|
let weakScrolledElements = new WeakSet();
|
|
2631
|
-
let cache;
|
|
2632
2631
|
const sessionsStorage = typeof window !== 'undefined' && window.sessionStorage;
|
|
2632
|
+
let cache = sessionsStorage ? (() => {
|
|
2633
|
+
const storageKey = 'tsr-scroll-restoration-v2';
|
|
2634
|
+
const state = JSON.parse(window.sessionStorage.getItem(storageKey) || 'null') || {
|
|
2635
|
+
cached: {},
|
|
2636
|
+
next: {}
|
|
2637
|
+
};
|
|
2638
|
+
return {
|
|
2639
|
+
state,
|
|
2640
|
+
set: updater => {
|
|
2641
|
+
cache.state = functionalUpdate(updater, cache.state);
|
|
2642
|
+
window.sessionStorage.setItem(storageKey, JSON.stringify(cache.state));
|
|
2643
|
+
}
|
|
2644
|
+
};
|
|
2645
|
+
})() : undefined;
|
|
2633
2646
|
const defaultGetKey = location => location.state.key;
|
|
2634
2647
|
function useScrollRestoration(options) {
|
|
2635
2648
|
const router = useRouter();
|
|
2636
2649
|
useLayoutEffect(() => {
|
|
2637
2650
|
const getKey = options?.getKey || defaultGetKey;
|
|
2638
|
-
if (sessionsStorage) {
|
|
2639
|
-
if (!cache) {
|
|
2640
|
-
cache = (() => {
|
|
2641
|
-
const storageKey = 'tsr-scroll-restoration-v2';
|
|
2642
|
-
const state = JSON.parse(window.sessionStorage.getItem(storageKey) || 'null') || {
|
|
2643
|
-
cached: {},
|
|
2644
|
-
next: {}
|
|
2645
|
-
};
|
|
2646
|
-
return {
|
|
2647
|
-
state,
|
|
2648
|
-
set: updater => {
|
|
2649
|
-
cache.state = functionalUpdate(updater, cache.state);
|
|
2650
|
-
window.sessionStorage.setItem(storageKey, JSON.stringify(cache.state));
|
|
2651
|
-
}
|
|
2652
|
-
};
|
|
2653
|
-
})();
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
2651
|
const {
|
|
2657
2652
|
history
|
|
2658
2653
|
} = window;
|
|
@@ -2662,7 +2657,17 @@
|
|
|
2662
2657
|
const onScroll = event => {
|
|
2663
2658
|
if (weakScrolledElements.has(event.target)) return;
|
|
2664
2659
|
weakScrolledElements.add(event.target);
|
|
2665
|
-
|
|
2660
|
+
let elementSelector = '';
|
|
2661
|
+
if (event.target === document || event.target === window) {
|
|
2662
|
+
elementSelector = windowKey;
|
|
2663
|
+
} else {
|
|
2664
|
+
const attrId = event.target.getAttribute('data-scroll-restoration-id');
|
|
2665
|
+
if (attrId) {
|
|
2666
|
+
elementSelector = `[data-scroll-restoration-id="${attrId}"]`;
|
|
2667
|
+
} else {
|
|
2668
|
+
elementSelector = getCssSelector(event.target);
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2666
2671
|
if (!cache.state.next[elementSelector]) {
|
|
2667
2672
|
cache.set(c => ({
|
|
2668
2673
|
...c,
|
|
@@ -2676,15 +2681,6 @@
|
|
|
2676
2681
|
}));
|
|
2677
2682
|
}
|
|
2678
2683
|
};
|
|
2679
|
-
const getCssSelector = el => {
|
|
2680
|
-
let path = [],
|
|
2681
|
-
parent;
|
|
2682
|
-
while (parent = el.parentNode) {
|
|
2683
|
-
path.unshift(`${el.tagName}:nth-child(${[].indexOf.call(parent.children, el) + 1})`);
|
|
2684
|
-
el = parent;
|
|
2685
|
-
}
|
|
2686
|
-
return `${path.join(' > ')}`.toLowerCase();
|
|
2687
|
-
};
|
|
2688
2684
|
if (typeof document !== 'undefined') {
|
|
2689
2685
|
document.addEventListener('scroll', onScroll, true);
|
|
2690
2686
|
}
|
|
@@ -2764,6 +2760,32 @@
|
|
|
2764
2760
|
useScrollRestoration(props);
|
|
2765
2761
|
return null;
|
|
2766
2762
|
}
|
|
2763
|
+
function useElementScrollRestoration(options) {
|
|
2764
|
+
const router = useRouter();
|
|
2765
|
+
const getKey = options?.getKey || defaultGetKey;
|
|
2766
|
+
let elementSelector = '';
|
|
2767
|
+
if (options.id) {
|
|
2768
|
+
elementSelector = `[data-scroll-restoration-id="${options.id}"]`;
|
|
2769
|
+
} else {
|
|
2770
|
+
const element = options.getElement?.();
|
|
2771
|
+
if (!element) {
|
|
2772
|
+
return;
|
|
2773
|
+
}
|
|
2774
|
+
elementSelector = getCssSelector(element);
|
|
2775
|
+
}
|
|
2776
|
+
const restoreKey = getKey(router.latestLocation);
|
|
2777
|
+
const cacheKey = [restoreKey, elementSelector].join(delimiter);
|
|
2778
|
+
return cache.state.cached[cacheKey];
|
|
2779
|
+
}
|
|
2780
|
+
function getCssSelector(el) {
|
|
2781
|
+
let path = [],
|
|
2782
|
+
parent;
|
|
2783
|
+
while (parent = el.parentNode) {
|
|
2784
|
+
path.unshift(`${el.tagName}:nth-child(${[].indexOf.call(parent.children, el) + 1})`);
|
|
2785
|
+
el = parent;
|
|
2786
|
+
}
|
|
2787
|
+
return `${path.join(' > ')}`.toLowerCase();
|
|
2788
|
+
}
|
|
2767
2789
|
|
|
2768
2790
|
function useBlocker(message, condition = true) {
|
|
2769
2791
|
const {
|
|
@@ -2889,6 +2911,7 @@
|
|
|
2889
2911
|
exports.typedNavigate = typedNavigate;
|
|
2890
2912
|
exports.useAwaited = useAwaited;
|
|
2891
2913
|
exports.useBlocker = useBlocker;
|
|
2914
|
+
exports.useElementScrollRestoration = useElementScrollRestoration;
|
|
2892
2915
|
exports.useLayoutEffect = useLayoutEffect$1;
|
|
2893
2916
|
exports.useLinkProps = useLinkProps;
|
|
2894
2917
|
exports.useLoaderData = useLoaderData;
|