tanstack-router-cache 0.1.6 → 0.1.8
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/dist/components/cached-outlet.cjs +12 -0
- package/dist/components/cached-outlet.js +12 -0
- package/dist/components/off-screen-in.cjs +130 -0
- package/dist/components/off-screen-in.js +130 -0
- package/dist/components/off-screen.cjs +8 -0
- package/dist/components/off-screen.js +8 -0
- package/dist/components/restore-cached-href.cjs +28 -0
- package/dist/components/restore-cached-href.d.ts +0 -1
- package/dist/components/restore-cached-href.js +28 -0
- package/dist/components/route-cache-manager.cjs +485 -0
- package/dist/components/route-cache-manager.js +485 -0
- package/dist/components/router-cache-outlet.cjs +9 -0
- package/dist/components/router-cache-outlet.js +9 -0
- package/dist/contexts/router-cache.cjs +237 -0
- package/dist/contexts/router-cache.d.ts +40 -0
- package/dist/contexts/router-cache.js +235 -0
- package/dist/dom/dismiss-transient-ui.cjs +230 -0
- package/dist/dom/dismiss-transient-ui.js +228 -0
- package/dist/hooks/use-event-listener.cjs +76 -0
- package/dist/hooks/use-event-listener.js +76 -0
- package/dist/hooks/use-route-cache-active.cjs +19 -0
- package/dist/hooks/use-route-cache-active.js +19 -0
- package/dist/hooks/use-route-cache-activity.cjs +12 -0
- package/dist/hooks/use-route-cache-activity.js +12 -0
- package/dist/hooks/use-route-cache-effect.cjs +38 -0
- package/dist/hooks/use-route-cache-effect.js +38 -0
- package/dist/hooks/use-route-cache-error-boundary.cjs +23 -0
- package/dist/hooks/use-route-cache-error-boundary.js +23 -0
- package/dist/hooks/use-route-cache-navigation.cjs +36 -0
- package/dist/hooks/use-route-cache-navigation.js +36 -0
- package/dist/hooks/use-router-cache-debug.cjs +85 -0
- package/dist/hooks/use-router-cache-debug.js +85 -0
- package/dist/hooks/use-router-cache.cjs +32 -0
- package/dist/hooks/use-router-cache.js +32 -0
- package/dist/hooks/use-update.cjs +8 -0
- package/dist/hooks/use-update.js +8 -0
- package/dist/index.cjs +18 -1402
- package/dist/index.d.ts +9 -1
- package/dist/index.js +10 -1395
- package/dist/pathname.cjs +8 -0
- package/dist/pathname.js +8 -0
- package/dist/route-cache-static-data.cjs +43 -0
- package/dist/route-cache-static-data.d.ts +25 -0
- package/dist/route-cache-static-data.js +41 -0
- package/dist/types.d.ts +50 -0
- package/docs/architecture.md +8 -5
- package/docs/cache-behavior.md +17 -0
- package/docs/components.md +1 -2
- package/docs/getting-started.md +38 -3
- package/docs/releases.md +0 -9
- package/docs/types.md +12 -0
- package/package.json +4 -6
package/dist/index.cjs
CHANGED
|
@@ -1,1403 +1,19 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (currentEntries.length !== nextEntries.length) return false;
|
|
21
|
-
const nextValuesByKey = new Map(nextEntries);
|
|
22
|
-
for (const [key, value] of currentEntries) if (!Object.is(value, nextValuesByKey.get(key))) return false;
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
function isCacheEnabledRouteData(route) {
|
|
26
|
-
return route?.staticData.routeCache === true;
|
|
27
|
-
}
|
|
28
|
-
function filterRouterCacheRoutes(routes) {
|
|
29
|
-
return Object.fromEntries(Object.entries(routes).flatMap(([pathname, route]) => isCacheEnabledRouteData(route) ? [[normalizeCachedRoutePathname(pathname), route]] : []));
|
|
30
|
-
}
|
|
31
|
-
function isSameCachedRouteData(current, next) {
|
|
32
|
-
if (!current) return false;
|
|
33
|
-
return current.routeId === next.routeId && current.href === next.href && current.matchId === next.matchId && current.ready === next.ready && current.routerSnapshot === next.routerSnapshot && shallowEqualRouteStaticData(current.staticData, next.staticData);
|
|
34
|
-
}
|
|
35
|
-
function normalizeLimit(limit) {
|
|
36
|
-
if (typeof limit !== "number" || Number.isNaN(limit)) return DEFAULT_MAX_ENTRIES;
|
|
37
|
-
if (!Number.isFinite(limit)) return DEFAULT_MAX_ENTRIES;
|
|
38
|
-
return Math.max(Math.trunc(limit), 0);
|
|
39
|
-
}
|
|
40
|
-
function getCachedRouteTimestamp(route) {
|
|
41
|
-
return route.lastVisibleAt ?? route.createdAt ?? 0;
|
|
42
|
-
}
|
|
43
|
-
function sortCachedRouteEntries([leftPathname, leftRoute], [rightPathname, rightRoute]) {
|
|
44
|
-
const timestampDifference = getCachedRouteTimestamp(leftRoute) - getCachedRouteTimestamp(rightRoute);
|
|
45
|
-
if (timestampDifference !== 0) return timestampDifference;
|
|
46
|
-
return leftPathname.localeCompare(rightPathname);
|
|
47
|
-
}
|
|
48
|
-
function getUnmarkedCachedRouteEntries(routes, keysToDelete) {
|
|
49
|
-
return Object.entries(routes).filter(([pathname]) => !keysToDelete.has(pathname));
|
|
50
|
-
}
|
|
51
|
-
function getEntriesByRouteId(entries) {
|
|
52
|
-
const entriesByRouteId = /* @__PURE__ */ new Map();
|
|
53
|
-
for (const entry of entries) {
|
|
54
|
-
const routeId = entry[1].routeId;
|
|
55
|
-
if (!routeId) continue;
|
|
56
|
-
const existingEntries = entriesByRouteId.get(routeId);
|
|
57
|
-
if (existingEntries) {
|
|
58
|
-
existingEntries.push(entry);
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
entriesByRouteId.set(routeId, [entry]);
|
|
62
|
-
}
|
|
63
|
-
return entriesByRouteId;
|
|
64
|
-
}
|
|
65
|
-
function markEntriesForDeletion(entries, keysToDelete, protectedKeys, excessEntryCount) {
|
|
66
|
-
if (excessEntryCount <= 0) return;
|
|
67
|
-
const evictableEntries = entries.filter(([pathname]) => !protectedKeys.has(pathname)).sort(sortCachedRouteEntries);
|
|
68
|
-
let remainingExcess = excessEntryCount;
|
|
69
|
-
for (const [pathname] of evictableEntries) {
|
|
70
|
-
if (remainingExcess <= 0) break;
|
|
71
|
-
if (!keysToDelete.has(pathname)) {
|
|
72
|
-
keysToDelete.add(pathname);
|
|
73
|
-
remainingExcess -= 1;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
function applyPerRouteEntryLimit(routes, maxEntriesPerRouteId, keysToDelete, protectedKeys) {
|
|
78
|
-
if (maxEntriesPerRouteId === DEFAULT_MAX_ENTRIES) return;
|
|
79
|
-
const entriesByRouteId = getEntriesByRouteId(getUnmarkedCachedRouteEntries(routes, keysToDelete));
|
|
80
|
-
for (const entries of entriesByRouteId.values()) markEntriesForDeletion(entries, keysToDelete, protectedKeys, entries.length - maxEntriesPerRouteId);
|
|
81
|
-
}
|
|
82
|
-
function applyGlobalEntryLimit(routes, maxEntries, keysToDelete, protectedKeys) {
|
|
83
|
-
if (maxEntries === DEFAULT_MAX_ENTRIES) return;
|
|
84
|
-
const remainingEntries = getUnmarkedCachedRouteEntries(routes, keysToDelete);
|
|
85
|
-
markEntriesForDeletion(remainingEntries, keysToDelete, protectedKeys, remainingEntries.length - maxEntries);
|
|
86
|
-
}
|
|
87
|
-
function applyCachedRouteLimits(routes, config, protectedKeys) {
|
|
88
|
-
if (config.maxEntries === DEFAULT_MAX_ENTRIES && config.maxEntriesPerRouteId === DEFAULT_MAX_ENTRIES) return routes;
|
|
89
|
-
const keysToDelete = /* @__PURE__ */ new Set();
|
|
90
|
-
applyPerRouteEntryLimit(routes, config.maxEntriesPerRouteId, keysToDelete, protectedKeys);
|
|
91
|
-
applyGlobalEntryLimit(routes, config.maxEntries, keysToDelete, protectedKeys);
|
|
92
|
-
if (keysToDelete.size === 0) return routes;
|
|
93
|
-
const nextRoutes = { ...routes };
|
|
94
|
-
for (const pathname of keysToDelete) delete nextRoutes[pathname];
|
|
95
|
-
return nextRoutes;
|
|
96
|
-
}
|
|
97
|
-
function createCachedRouteData(current, next) {
|
|
98
|
-
const now = Date.now();
|
|
99
|
-
return {
|
|
100
|
-
...next,
|
|
101
|
-
createdAt: current?.createdAt ?? next.createdAt ?? now,
|
|
102
|
-
lastVisibleAt: next.lastVisibleAt ?? now
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
function getNextCachedRoutesState(params) {
|
|
106
|
-
const normalizedKey = normalizeCachedRoutePathname(params.key);
|
|
107
|
-
if (params.cacheConfig.maxEntries === 0) return params.state === EMPTY_CACHED_ROUTES ? params.state : EMPTY_CACHED_ROUTES;
|
|
108
|
-
if (!isCacheEnabledRouteData(params.value)) {
|
|
109
|
-
if (!Object.hasOwn(params.state, normalizedKey)) return params.state;
|
|
110
|
-
const nextState = { ...params.state };
|
|
111
|
-
delete nextState[normalizedKey];
|
|
112
|
-
return nextState;
|
|
113
|
-
}
|
|
114
|
-
const nextRouteData = createCachedRouteData(params.state[normalizedKey], params.value);
|
|
115
|
-
if (isSameCachedRouteData(params.state[normalizedKey], nextRouteData)) return params.state;
|
|
116
|
-
return applyCachedRouteLimits({
|
|
117
|
-
...params.state,
|
|
118
|
-
[normalizedKey]: nextRouteData
|
|
119
|
-
}, params.cacheConfig, new Set([normalizedKey]));
|
|
120
|
-
}
|
|
121
|
-
function RouterCacheProvider({ cacheScopeKey = "__default__", children, defaultCachedRoutes = EMPTY_CACHED_ROUTES, maxEntries = DEFAULT_MAX_ENTRIES, maxEntriesPerRouteId = DEFAULT_MAX_ENTRIES }) {
|
|
122
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouterCacheProviderScope, {
|
|
123
|
-
defaultCachedRoutes,
|
|
124
|
-
maxEntries,
|
|
125
|
-
maxEntriesPerRouteId,
|
|
126
|
-
children
|
|
127
|
-
}, cacheScopeKey ?? "__default__");
|
|
128
|
-
}
|
|
129
|
-
function RouterCacheProviderScope({ children, defaultCachedRoutes = EMPTY_CACHED_ROUTES, maxEntries, maxEntriesPerRouteId }) {
|
|
130
|
-
const cacheConfigRef = (0, react.useRef)({
|
|
131
|
-
maxEntries: normalizeLimit(maxEntries),
|
|
132
|
-
maxEntriesPerRouteId: normalizeLimit(maxEntriesPerRouteId)
|
|
133
|
-
});
|
|
134
|
-
const initialCachedRoutesRef = (0, react.useRef)(null);
|
|
135
|
-
if (initialCachedRoutesRef.current === null) initialCachedRoutesRef.current = applyCachedRouteLimits(filterRouterCacheRoutes(defaultCachedRoutes), cacheConfigRef.current, /* @__PURE__ */ new Set());
|
|
136
|
-
const [cachedRoutes, setCachedRoutes] = (0, react.useState)(() => initialCachedRoutesRef.current ?? EMPTY_CACHED_ROUTES);
|
|
137
|
-
const [erroredRouteCounts, setErroredRouteCounts] = (0, react.useState)({});
|
|
138
|
-
const updateCachedRoutes = (key, value) => {
|
|
139
|
-
setCachedRoutes((state) => getNextCachedRoutesState({
|
|
140
|
-
cacheConfig: cacheConfigRef.current,
|
|
141
|
-
key,
|
|
142
|
-
state,
|
|
143
|
-
value
|
|
144
|
-
}));
|
|
145
|
-
};
|
|
146
|
-
const deleteCachedRoutes = (keys) => {
|
|
147
|
-
setCachedRoutes((state) => {
|
|
148
|
-
let changed = false;
|
|
149
|
-
const newState = { ...state };
|
|
150
|
-
for (const key of keys) {
|
|
151
|
-
const normalizedKey = normalizeCachedRoutePathname(key);
|
|
152
|
-
if (Object.hasOwn(newState, normalizedKey)) {
|
|
153
|
-
delete newState[normalizedKey];
|
|
154
|
-
changed = true;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return changed ? newState : state;
|
|
158
|
-
});
|
|
159
|
-
};
|
|
160
|
-
const touchCachedRoutes = (keys) => {
|
|
161
|
-
setCachedRoutes((state) => {
|
|
162
|
-
let changed = false;
|
|
163
|
-
const touchedAt = Date.now();
|
|
164
|
-
const nextState = { ...state };
|
|
165
|
-
for (const key of keys) {
|
|
166
|
-
const normalizedKey = normalizeCachedRoutePathname(key);
|
|
167
|
-
const route = nextState[normalizedKey];
|
|
168
|
-
if (!route || route.lastVisibleAt === touchedAt) continue;
|
|
169
|
-
nextState[normalizedKey] = {
|
|
170
|
-
...route,
|
|
171
|
-
lastVisibleAt: touchedAt
|
|
172
|
-
};
|
|
173
|
-
changed = true;
|
|
174
|
-
}
|
|
175
|
-
return changed ? nextState : state;
|
|
176
|
-
});
|
|
177
|
-
};
|
|
178
|
-
const retainErroredRoute = (pathname) => {
|
|
179
|
-
const normalizedPathname = normalizeCachedRoutePathname(pathname);
|
|
180
|
-
setErroredRouteCounts((state) => ({
|
|
181
|
-
...state,
|
|
182
|
-
[normalizedPathname]: (state[normalizedPathname] ?? 0) + 1
|
|
183
|
-
}));
|
|
184
|
-
setCachedRoutes((state) => {
|
|
185
|
-
if (!Object.hasOwn(state, normalizedPathname)) return state;
|
|
186
|
-
const nextState = { ...state };
|
|
187
|
-
delete nextState[normalizedPathname];
|
|
188
|
-
return nextState;
|
|
189
|
-
});
|
|
190
|
-
};
|
|
191
|
-
const releaseErroredRoute = (pathname) => {
|
|
192
|
-
const normalizedPathname = normalizeCachedRoutePathname(pathname);
|
|
193
|
-
setErroredRouteCounts((state) => {
|
|
194
|
-
const currentCount = state[normalizedPathname] ?? 0;
|
|
195
|
-
if (currentCount <= 1) {
|
|
196
|
-
if (!Object.hasOwn(state, normalizedPathname)) return state;
|
|
197
|
-
const nextState = { ...state };
|
|
198
|
-
delete nextState[normalizedPathname];
|
|
199
|
-
return nextState;
|
|
200
|
-
}
|
|
201
|
-
return {
|
|
202
|
-
...state,
|
|
203
|
-
[normalizedPathname]: currentCount - 1
|
|
204
|
-
};
|
|
205
|
-
});
|
|
206
|
-
};
|
|
207
|
-
const contextValue = {
|
|
208
|
-
cachedRoutes,
|
|
209
|
-
erroredRouteCounts,
|
|
210
|
-
setCachedRoutes: updateCachedRoutes,
|
|
211
|
-
deleteCachedRoutes,
|
|
212
|
-
touchCachedRoutes,
|
|
213
|
-
retainErroredRoute,
|
|
214
|
-
releaseErroredRoute
|
|
215
|
-
};
|
|
216
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouterCacheContext.Provider, {
|
|
217
|
-
value: contextValue,
|
|
218
|
-
children
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
function useRouterCacheContext() {
|
|
222
|
-
const context = (0, react.use)(RouterCacheContext);
|
|
223
|
-
if (!context) throw new Error("RouterCacheContext is not available");
|
|
224
|
-
return context;
|
|
225
|
-
}
|
|
226
|
-
function useOptionalRouterCacheContext() {
|
|
227
|
-
return (0, react.use)(RouterCacheContext);
|
|
228
|
-
}
|
|
229
|
-
//#endregion
|
|
230
|
-
//#region src/hooks/use-event-listener.ts
|
|
231
|
-
const EVENT_BUCKET_KEYS = ["on", "once"];
|
|
232
|
-
const instance = class RouterCacheEvent {
|
|
233
|
-
static instance;
|
|
234
|
-
listeners = {
|
|
235
|
-
activeChange: /* @__PURE__ */ new Set(),
|
|
236
|
-
cachedNavigationCancel: /* @__PURE__ */ new Set(),
|
|
237
|
-
cachedNavigationComplete: /* @__PURE__ */ new Set(),
|
|
238
|
-
cachedNavigationStart: /* @__PURE__ */ new Set()
|
|
239
|
-
};
|
|
240
|
-
constructor() {}
|
|
241
|
-
static getInstance() {
|
|
242
|
-
if (!RouterCacheEvent.instance) RouterCacheEvent.instance = new RouterCacheEvent();
|
|
243
|
-
return RouterCacheEvent.instance;
|
|
244
|
-
}
|
|
245
|
-
on(eventName, handler) {
|
|
246
|
-
this.listeners[eventName].add(handler);
|
|
247
|
-
return this;
|
|
248
|
-
}
|
|
249
|
-
once(eventName, handler) {
|
|
250
|
-
const onceHandler = (...args) => {
|
|
251
|
-
this.off(eventName, onceHandler);
|
|
252
|
-
handler(...args);
|
|
253
|
-
};
|
|
254
|
-
this.listeners[eventName].add(onceHandler);
|
|
255
|
-
return this;
|
|
256
|
-
}
|
|
257
|
-
off(eventName, handler) {
|
|
258
|
-
this.listeners[eventName].delete(handler);
|
|
259
|
-
return this;
|
|
260
|
-
}
|
|
261
|
-
emit(eventName, ...args) {
|
|
262
|
-
const handlers = this.listeners[eventName];
|
|
263
|
-
for (const handler of handlers) handler(...args);
|
|
264
|
-
return handlers.size > 0;
|
|
265
|
-
}
|
|
266
|
-
}.getInstance();
|
|
267
|
-
function syncEventHandler(type, eventName, handler) {
|
|
268
|
-
if (type === "on") {
|
|
269
|
-
instance.on(eventName, handler);
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
instance.off(eventName, handler);
|
|
273
|
-
}
|
|
274
|
-
function syncEventBucket(type, bucket) {
|
|
275
|
-
if (bucket.activeChange) syncEventHandler(type, "activeChange", bucket.activeChange);
|
|
276
|
-
if (bucket.cachedNavigationCancel) syncEventHandler(type, "cachedNavigationCancel", bucket.cachedNavigationCancel);
|
|
277
|
-
if (bucket.cachedNavigationComplete) syncEventHandler(type, "cachedNavigationComplete", bucket.cachedNavigationComplete);
|
|
278
|
-
if (bucket.cachedNavigationStart) syncEventHandler(type, "cachedNavigationStart", bucket.cachedNavigationStart);
|
|
279
|
-
}
|
|
280
|
-
function syncEventHandlers(type, events) {
|
|
281
|
-
if (!events) return;
|
|
282
|
-
for (const key of EVENT_BUCKET_KEYS) {
|
|
283
|
-
const bucket = events[key];
|
|
284
|
-
if (!bucket) continue;
|
|
285
|
-
syncEventBucket(type, bucket);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
function useEventListener(events) {
|
|
289
|
-
(0, react.useEffect)(() => {
|
|
290
|
-
const registerEvent = () => {
|
|
291
|
-
syncEventHandlers("on", events);
|
|
292
|
-
return () => {
|
|
293
|
-
syncEventHandlers("off", events);
|
|
294
|
-
};
|
|
295
|
-
};
|
|
296
|
-
const unregister = registerEvent();
|
|
297
|
-
return () => {
|
|
298
|
-
unregister?.();
|
|
299
|
-
};
|
|
300
|
-
}, [events]);
|
|
301
|
-
return { eventListener: instance };
|
|
302
|
-
}
|
|
303
|
-
//#endregion
|
|
304
|
-
//#region src/hooks/use-router-cache-debug.ts
|
|
305
|
-
const DYNAMIC_SEGMENT_PATTERNS = [
|
|
306
|
-
/^\d{4,}$/u,
|
|
307
|
-
/^[0-9a-f]{8,}$/iu,
|
|
308
|
-
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu
|
|
309
|
-
];
|
|
310
|
-
function isDynamicLookingSegment(segment) {
|
|
311
|
-
for (const pattern of DYNAMIC_SEGMENT_PATTERNS) if (pattern.test(segment)) return true;
|
|
312
|
-
return false;
|
|
313
|
-
}
|
|
314
|
-
function isDynamicLookingPathname(pathname) {
|
|
315
|
-
return pathname.split("/").filter(Boolean).some(isDynamicLookingSegment);
|
|
316
|
-
}
|
|
317
|
-
function countHiddenContainers() {
|
|
318
|
-
if (typeof document === "undefined") return 0;
|
|
319
|
-
return document.querySelectorAll("[data-router-cache-container=\"true\"][data-router-cache-mode=\"hidden\"]").length;
|
|
320
|
-
}
|
|
321
|
-
function buildSnapshot(cachedRoutes, visiblePathname) {
|
|
322
|
-
const cachedRoutePathnames = Object.keys(cachedRoutes).sort((a, b) => a.localeCompare(b));
|
|
323
|
-
const dynamicLookingRoutePathnames = cachedRoutePathnames.filter(isDynamicLookingPathname);
|
|
324
|
-
const hiddenCachedRoutePathnames = cachedRoutePathnames.filter((pathname) => pathname !== visiblePathname);
|
|
325
|
-
return {
|
|
326
|
-
cachedRoutePathnames,
|
|
327
|
-
dynamicLookingRouteCount: dynamicLookingRoutePathnames.length,
|
|
328
|
-
dynamicLookingRoutePathnames,
|
|
329
|
-
hiddenCachedRouteCount: hiddenCachedRoutePathnames.length,
|
|
330
|
-
hiddenContainerCount: countHiddenContainers(),
|
|
331
|
-
totalCachedRouteCount: cachedRoutePathnames.length,
|
|
332
|
-
visiblePathname
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
function getDebugWindow() {
|
|
336
|
-
return globalThis.window;
|
|
337
|
-
}
|
|
338
|
-
function isProduction() {
|
|
339
|
-
return globalThis.process?.env?.NODE_ENV === "production";
|
|
340
|
-
}
|
|
341
|
-
function useRouterCacheDebug(cachedRoutes, visiblePathname) {
|
|
342
|
-
const warningThresholdRef = (0, react.useRef)(null);
|
|
343
|
-
const lastWarnedCountRef = (0, react.useRef)(null);
|
|
344
|
-
const lastSnapshotRef = (0, react.useRef)(null);
|
|
345
|
-
if (lastSnapshotRef.current === null) lastSnapshotRef.current = buildSnapshot(cachedRoutes, visiblePathname);
|
|
346
|
-
const snapshot = (0, react.useMemo)(() => buildSnapshot(cachedRoutes, visiblePathname), [cachedRoutes, visiblePathname]);
|
|
347
|
-
(0, react.useEffect)(() => {
|
|
348
|
-
if (isProduction()) return;
|
|
349
|
-
const debugWindow = getDebugWindow();
|
|
350
|
-
if (!debugWindow) return;
|
|
351
|
-
const refresh = () => {
|
|
352
|
-
lastSnapshotRef.current = buildSnapshot(cachedRoutes, visiblePathname);
|
|
353
|
-
return lastSnapshotRef.current;
|
|
354
|
-
};
|
|
355
|
-
const api = {
|
|
356
|
-
getSnapshot: () => lastSnapshotRef.current ?? snapshot,
|
|
357
|
-
lastSnapshot: snapshot,
|
|
358
|
-
refresh,
|
|
359
|
-
setWarningThreshold: (nextThreshold) => {
|
|
360
|
-
warningThresholdRef.current = typeof nextThreshold === "number" ? nextThreshold : null;
|
|
361
|
-
lastWarnedCountRef.current = null;
|
|
362
|
-
},
|
|
363
|
-
warningThreshold: warningThresholdRef.current
|
|
364
|
-
};
|
|
365
|
-
debugWindow.__TANSTACK_ROUTER_CACHE_DEBUG__ = api;
|
|
366
|
-
const rafId = globalThis.requestAnimationFrame(() => {
|
|
367
|
-
const nextSnapshot = refresh();
|
|
368
|
-
debugWindow.__TANSTACK_ROUTER_CACHE_DEBUG__ = {
|
|
369
|
-
...api,
|
|
370
|
-
lastSnapshot: nextSnapshot,
|
|
371
|
-
warningThreshold: warningThresholdRef.current
|
|
372
|
-
};
|
|
373
|
-
const warningThreshold = warningThresholdRef.current;
|
|
374
|
-
if (typeof warningThreshold === "number" && nextSnapshot.totalCachedRouteCount > warningThreshold && lastWarnedCountRef.current !== nextSnapshot.totalCachedRouteCount) lastWarnedCountRef.current = nextSnapshot.totalCachedRouteCount;
|
|
375
|
-
});
|
|
376
|
-
return () => {
|
|
377
|
-
globalThis.cancelAnimationFrame(rafId);
|
|
378
|
-
if (debugWindow.__TANSTACK_ROUTER_CACHE_DEBUG__ === api) debugWindow.__TANSTACK_ROUTER_CACHE_DEBUG__ = void 0;
|
|
379
|
-
};
|
|
380
|
-
}, [
|
|
381
|
-
cachedRoutes,
|
|
382
|
-
snapshot,
|
|
383
|
-
visiblePathname
|
|
384
|
-
]);
|
|
385
|
-
}
|
|
386
|
-
//#endregion
|
|
387
|
-
//#region src/components/cached-outlet.tsx
|
|
388
|
-
function CachedOutlet(props) {
|
|
389
|
-
const { matchId, routerSnapshot } = props;
|
|
390
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tanstack_react_router.RouterContextProvider, {
|
|
391
|
-
router: routerSnapshot,
|
|
392
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tanstack_react_router.Match, { matchId })
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
//#endregion
|
|
396
|
-
//#region src/dom/dismiss-transient-ui.ts
|
|
397
|
-
const documentStates = /* @__PURE__ */ new WeakMap();
|
|
398
|
-
const PERSISTENT_EXTERNAL_ATTRIBUTE = "data-router-cache-persistent-external";
|
|
399
|
-
function isBlurTarget(element) {
|
|
400
|
-
return element instanceof HTMLElement;
|
|
401
|
-
}
|
|
402
|
-
function getOrCreateDocumentState(documentObject) {
|
|
403
|
-
const existingState = documentStates.get(documentObject);
|
|
404
|
-
if (existingState) return existingState;
|
|
405
|
-
const state = {
|
|
406
|
-
observer: null,
|
|
407
|
-
ownerByElement: /* @__PURE__ */ new WeakMap(),
|
|
408
|
-
routes: /* @__PURE__ */ new Map(),
|
|
409
|
-
visiblePathname: null
|
|
410
|
-
};
|
|
411
|
-
if (typeof MutationObserver !== "undefined") {
|
|
412
|
-
state.observer = new MutationObserver((mutations) => {
|
|
413
|
-
handleDomMutations(mutations, state);
|
|
414
|
-
});
|
|
415
|
-
state.observer.observe(documentObject.body ?? documentObject.documentElement, {
|
|
416
|
-
childList: true,
|
|
417
|
-
subtree: true
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
documentStates.set(documentObject, state);
|
|
421
|
-
return state;
|
|
422
|
-
}
|
|
423
|
-
function getOrCreateRouteState(state, pathname) {
|
|
424
|
-
const existingRouteState = state.routes.get(pathname);
|
|
425
|
-
if (existingRouteState) return existingRouteState;
|
|
426
|
-
const routeState = {
|
|
427
|
-
hiddenElements: /* @__PURE__ */ new Map(),
|
|
428
|
-
ownedElements: /* @__PURE__ */ new Set()
|
|
429
|
-
};
|
|
430
|
-
state.routes.set(pathname, routeState);
|
|
431
|
-
return routeState;
|
|
432
|
-
}
|
|
433
|
-
function getHoveredElements(documentObject) {
|
|
434
|
-
try {
|
|
435
|
-
return Array.from(documentObject.querySelectorAll(":hover")).reverse();
|
|
436
|
-
} catch {
|
|
437
|
-
return [];
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
function getOwnedExternalElements(documentObject, pathname) {
|
|
441
|
-
const routeState = getOrCreateRouteState(getOrCreateDocumentState(documentObject), pathname);
|
|
442
|
-
return Array.from(routeState.ownedElements).filter((element) => element.isConnected);
|
|
443
|
-
}
|
|
444
|
-
function dispatchEscapeKeyboardEvent(target, documentObject, type) {
|
|
445
|
-
const keyboardEventConstructor = documentObject.defaultView?.KeyboardEvent ?? KeyboardEvent;
|
|
446
|
-
target.dispatchEvent(new keyboardEventConstructor(type, {
|
|
447
|
-
bubbles: true,
|
|
448
|
-
cancelable: true,
|
|
449
|
-
code: "Escape",
|
|
450
|
-
key: "Escape",
|
|
451
|
-
keyCode: 27,
|
|
452
|
-
which: 27
|
|
453
|
-
}));
|
|
454
|
-
}
|
|
455
|
-
function dispatchHoverExitEvent(target, documentObject, type) {
|
|
456
|
-
const eventConstructor = type.startsWith("pointer") ? documentObject.defaultView?.PointerEvent ?? documentObject.defaultView?.MouseEvent ?? MouseEvent : documentObject.defaultView?.MouseEvent ?? MouseEvent;
|
|
457
|
-
target.dispatchEvent(new eventConstructor(type, {
|
|
458
|
-
bubbles: type.endsWith("out"),
|
|
459
|
-
cancelable: true,
|
|
460
|
-
composed: true,
|
|
461
|
-
relatedTarget: documentObject.body
|
|
462
|
-
}));
|
|
463
|
-
}
|
|
464
|
-
function dispatchHoverExitEvents(hoveredElements, documentObject) {
|
|
465
|
-
for (const hoveredElement of hoveredElements) {
|
|
466
|
-
dispatchHoverExitEvent(hoveredElement, documentObject, "pointerout");
|
|
467
|
-
dispatchHoverExitEvent(hoveredElement, documentObject, "pointerleave");
|
|
468
|
-
dispatchHoverExitEvent(hoveredElement, documentObject, "mouseout");
|
|
469
|
-
dispatchHoverExitEvent(hoveredElement, documentObject, "mouseleave");
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
function focusDocumentBody(documentObject) {
|
|
473
|
-
const { body } = documentObject;
|
|
474
|
-
const previousTabIndex = body.getAttribute("tabindex");
|
|
475
|
-
if (previousTabIndex === null) body.setAttribute("tabindex", "-1");
|
|
476
|
-
body.focus();
|
|
477
|
-
if (previousTabIndex === null) body.removeAttribute("tabindex");
|
|
478
|
-
}
|
|
479
|
-
function getRouterCacheContainerAncestor(element) {
|
|
480
|
-
const container = element?.closest("[data-router-cache-container=\"true\"]");
|
|
481
|
-
return container instanceof HTMLElement ? container : null;
|
|
482
|
-
}
|
|
483
|
-
function isElementInsideAnyRouterCacheContainer(element) {
|
|
484
|
-
return getRouterCacheContainerAncestor(element) !== null;
|
|
485
|
-
}
|
|
486
|
-
function containsRouterCacheContainer(element) {
|
|
487
|
-
return element.querySelector("[data-router-cache-container=\"true\"]") instanceof HTMLElement;
|
|
488
|
-
}
|
|
489
|
-
function isElementConnectedOutsideRouterCacheContainer(element) {
|
|
490
|
-
return element.isConnected && !isElementInsideAnyRouterCacheContainer(element);
|
|
491
|
-
}
|
|
492
|
-
function isPersistentExternalElement(element) {
|
|
493
|
-
return element.closest(`[${PERSISTENT_EXTERNAL_ATTRIBUTE}="true"]`) instanceof HTMLElement;
|
|
494
|
-
}
|
|
495
|
-
function getTrackableElementsFromNode(node) {
|
|
496
|
-
if (node instanceof HTMLElement) return [node];
|
|
497
|
-
if (node instanceof DocumentFragment) return Array.from(node.children).flatMap((child) => child instanceof HTMLElement ? [child] : []);
|
|
498
|
-
return [];
|
|
499
|
-
}
|
|
500
|
-
function forEachRemovedNode(mutations, callback) {
|
|
501
|
-
for (const mutation of mutations) for (const removedNode of mutation.removedNodes) callback(removedNode);
|
|
502
|
-
}
|
|
503
|
-
function forEachAddedNode(mutations, callback) {
|
|
504
|
-
for (const mutation of mutations) for (const addedNode of mutation.addedNodes) callback(addedNode);
|
|
505
|
-
}
|
|
506
|
-
function handleDomMutations(mutations, state) {
|
|
507
|
-
forEachRemovedNode(mutations, (removedNode) => {
|
|
508
|
-
untrackRemovedNode(removedNode, state);
|
|
509
|
-
});
|
|
510
|
-
if (!state.visiblePathname) return;
|
|
511
|
-
forEachAddedNode(mutations, (addedNode) => {
|
|
512
|
-
trackAddedNode(addedNode, state.visiblePathname, state);
|
|
513
|
-
});
|
|
514
|
-
}
|
|
515
|
-
function hasTrackedAncestor(element, state) {
|
|
516
|
-
let ancestor = element.parentElement;
|
|
517
|
-
while (ancestor) {
|
|
518
|
-
if (state.ownerByElement.get(ancestor)) return true;
|
|
519
|
-
ancestor = ancestor.parentElement;
|
|
520
|
-
}
|
|
521
|
-
return false;
|
|
522
|
-
}
|
|
523
|
-
function restoreOwnedExternalElement(routeState, element) {
|
|
524
|
-
const snapshot = routeState.hiddenElements.get(element);
|
|
525
|
-
if (!snapshot) return;
|
|
526
|
-
element.style.setProperty("display", snapshot.display, snapshot.displayPriority);
|
|
527
|
-
if (snapshot.ariaHidden === null) element.removeAttribute("aria-hidden");
|
|
528
|
-
else element.setAttribute("aria-hidden", snapshot.ariaHidden);
|
|
529
|
-
element.inert = snapshot.inert;
|
|
530
|
-
routeState.hiddenElements.delete(element);
|
|
531
|
-
}
|
|
532
|
-
function trackExternalElement(pathname, element, state) {
|
|
533
|
-
if (!isElementConnectedOutsideRouterCacheContainer(element) || containsRouterCacheContainer(element) || isPersistentExternalElement(element) || hasTrackedAncestor(element, state)) return;
|
|
534
|
-
const currentOwner = state.ownerByElement.get(element);
|
|
535
|
-
if (currentOwner === pathname) return;
|
|
536
|
-
if (currentOwner) {
|
|
537
|
-
const previousRouteState = state.routes.get(currentOwner);
|
|
538
|
-
if (previousRouteState) {
|
|
539
|
-
restoreOwnedExternalElement(previousRouteState, element);
|
|
540
|
-
previousRouteState.ownedElements.delete(element);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
getOrCreateRouteState(state, pathname).ownedElements.add(element);
|
|
544
|
-
state.ownerByElement.set(element, pathname);
|
|
545
|
-
}
|
|
546
|
-
function trackAddedNode(node, pathname, state) {
|
|
547
|
-
const elements = getTrackableElementsFromNode(node);
|
|
548
|
-
for (const element of elements) trackExternalElement(pathname, element, state);
|
|
549
|
-
}
|
|
550
|
-
function untrackElement(routeState, state, element) {
|
|
551
|
-
routeState.hiddenElements.delete(element);
|
|
552
|
-
routeState.ownedElements.delete(element);
|
|
553
|
-
state.ownerByElement.delete(element);
|
|
554
|
-
}
|
|
555
|
-
function untrackRemovedNode(node, state) {
|
|
556
|
-
if (!(node instanceof HTMLElement)) return;
|
|
557
|
-
for (const routeState of state.routes.values()) for (const element of Array.from(routeState.ownedElements)) if (node === element || node.contains(element)) untrackElement(routeState, state, element);
|
|
558
|
-
}
|
|
559
|
-
function hideOwnedExternalElements(documentObject, pathname) {
|
|
560
|
-
const routeState = getOrCreateRouteState(getOrCreateDocumentState(documentObject), pathname);
|
|
561
|
-
for (const element of Array.from(routeState.ownedElements)) {
|
|
562
|
-
if (!isElementConnectedOutsideRouterCacheContainer(element)) {
|
|
563
|
-
routeState.ownedElements.delete(element);
|
|
564
|
-
routeState.hiddenElements.delete(element);
|
|
565
|
-
continue;
|
|
566
|
-
}
|
|
567
|
-
if (routeState.hiddenElements.has(element)) continue;
|
|
568
|
-
routeState.hiddenElements.set(element, {
|
|
569
|
-
ariaHidden: element.getAttribute("aria-hidden"),
|
|
570
|
-
display: element.style.getPropertyValue("display"),
|
|
571
|
-
displayPriority: element.style.getPropertyPriority("display"),
|
|
572
|
-
inert: element.inert
|
|
573
|
-
});
|
|
574
|
-
element.style.setProperty("display", "none", "important");
|
|
575
|
-
element.setAttribute("aria-hidden", "true");
|
|
576
|
-
element.inert = true;
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
function showOwnedExternalElements(documentObject, pathname) {
|
|
580
|
-
const routeState = getOrCreateRouteState(getOrCreateDocumentState(documentObject), pathname);
|
|
581
|
-
for (const [element] of Array.from(routeState.hiddenElements)) {
|
|
582
|
-
if (!element.isConnected) {
|
|
583
|
-
routeState.hiddenElements.delete(element);
|
|
584
|
-
routeState.ownedElements.delete(element);
|
|
585
|
-
continue;
|
|
586
|
-
}
|
|
587
|
-
restoreOwnedExternalElement(routeState, element);
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
function initializeTransientUiTracking(documentObject) {
|
|
591
|
-
getOrCreateDocumentState(documentObject);
|
|
592
|
-
}
|
|
593
|
-
function syncTransientUiRouteActivity(pathname, mode) {
|
|
594
|
-
if (typeof document === "undefined") return;
|
|
595
|
-
const documentState = getOrCreateDocumentState(document);
|
|
596
|
-
getOrCreateRouteState(documentState, pathname);
|
|
597
|
-
if (mode === "visible") {
|
|
598
|
-
documentState.visiblePathname = pathname;
|
|
599
|
-
showOwnedExternalElements(document, pathname);
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
if (documentState.visiblePathname === pathname) documentState.visiblePathname = null;
|
|
603
|
-
dispatchHoverExitEvents(getOwnedExternalElements(document, pathname), document);
|
|
604
|
-
hideOwnedExternalElements(document, pathname);
|
|
605
|
-
}
|
|
606
|
-
function dismissTransientUi(container, pathname) {
|
|
607
|
-
if (typeof document === "undefined" || !container) return;
|
|
608
|
-
initializeTransientUiTracking(document);
|
|
609
|
-
const hoveredElements = getHoveredElements(document);
|
|
610
|
-
const hoverExitTargets = Array.from(new Set([...hoveredElements, ...getOwnedExternalElements(document, pathname)]));
|
|
611
|
-
const activeElement = document.activeElement;
|
|
612
|
-
const activeElementBelongsToHoveredTree = activeElement instanceof Element && hoverExitTargets.some((hoveredElement) => hoveredElement === activeElement || hoveredElement.contains(activeElement));
|
|
613
|
-
if (activeElement instanceof Element && (container.contains(activeElement) || activeElementBelongsToHoveredTree) && isBlurTarget(activeElement)) activeElement.blur();
|
|
614
|
-
dispatchHoverExitEvents(hoverExitTargets, document);
|
|
615
|
-
dispatchEscapeKeyboardEvent(document, document, "keydown");
|
|
616
|
-
dispatchEscapeKeyboardEvent(document, document, "keyup");
|
|
617
|
-
hideOwnedExternalElements(document, pathname);
|
|
618
|
-
const nextActiveElement = document.activeElement;
|
|
619
|
-
const nextActiveElementBelongsToHoveredTree = nextActiveElement instanceof Element && hoverExitTargets.some((hoveredElement) => hoveredElement === nextActiveElement || hoveredElement.contains(nextActiveElement));
|
|
620
|
-
if (nextActiveElement instanceof Element && (container.contains(nextActiveElement) || nextActiveElementBelongsToHoveredTree)) focusDocumentBody(document);
|
|
621
|
-
}
|
|
622
|
-
//#endregion
|
|
623
|
-
//#region src/components/off-screen-in.tsx
|
|
624
|
-
const windowScrollPositions = /* @__PURE__ */ new Map();
|
|
625
|
-
const IMMEDIATE_SCROLL_RESTORE_DELAY = 0;
|
|
626
|
-
const EARLY_SCROLL_RESTORE_DELAY = 80;
|
|
627
|
-
const MIDDLE_SCROLL_RESTORE_DELAY = 240;
|
|
628
|
-
const LATE_SCROLL_RESTORE_DELAY = 600;
|
|
629
|
-
const FINAL_SCROLL_RESTORE_DELAY = 1e3;
|
|
630
|
-
const SCROLL_TRACKING_INTERVAL_MS = 200;
|
|
631
|
-
const SCROLL_KEYS = new Set([
|
|
632
|
-
" ",
|
|
633
|
-
"ArrowDown",
|
|
634
|
-
"ArrowLeft",
|
|
635
|
-
"ArrowRight",
|
|
636
|
-
"ArrowUp",
|
|
637
|
-
"End",
|
|
638
|
-
"Home",
|
|
639
|
-
"PageDown",
|
|
640
|
-
"PageUp"
|
|
641
|
-
]);
|
|
642
|
-
const SCROLL_RESTORE_DELAYS = [
|
|
643
|
-
IMMEDIATE_SCROLL_RESTORE_DELAY,
|
|
644
|
-
EARLY_SCROLL_RESTORE_DELAY,
|
|
645
|
-
MIDDLE_SCROLL_RESTORE_DELAY,
|
|
646
|
-
LATE_SCROLL_RESTORE_DELAY,
|
|
647
|
-
FINAL_SCROLL_RESTORE_DELAY
|
|
648
|
-
];
|
|
649
|
-
function OffScreenIn(props) {
|
|
650
|
-
const { mode, children, containerRef, pathname } = props;
|
|
651
|
-
const localContainerRef = (0, react.useRef)(null);
|
|
652
|
-
const attachContainerRef = (node) => {
|
|
653
|
-
localContainerRef.current = node;
|
|
654
|
-
if (containerRef) containerRef.current = node;
|
|
655
|
-
};
|
|
656
|
-
(0, react.useLayoutEffect)(() => {
|
|
657
|
-
if (typeof document === "undefined") return;
|
|
658
|
-
initializeTransientUiTracking(document);
|
|
659
|
-
}, []);
|
|
660
|
-
(0, react.useLayoutEffect)(() => {
|
|
661
|
-
if (!pathname) return;
|
|
662
|
-
syncTransientUiRouteActivity(pathname, mode);
|
|
663
|
-
}, [mode, pathname]);
|
|
664
|
-
(0, react.useLayoutEffect)(() => {
|
|
665
|
-
const visibleContainer = localContainerRef.current;
|
|
666
|
-
return () => {
|
|
667
|
-
if (!pathname || mode !== "visible") return;
|
|
668
|
-
dismissTransientUi(visibleContainer, pathname);
|
|
669
|
-
};
|
|
670
|
-
}, [mode, pathname]);
|
|
671
|
-
(0, react.useLayoutEffect)(() => {
|
|
672
|
-
if (typeof globalThis === "undefined" || !pathname) return;
|
|
673
|
-
const saveScrollPosition = () => {
|
|
674
|
-
windowScrollPositions.set(pathname, {
|
|
675
|
-
x: globalThis.scrollX,
|
|
676
|
-
y: globalThis.scrollY
|
|
677
|
-
});
|
|
678
|
-
};
|
|
679
|
-
if (mode === "hidden") return;
|
|
680
|
-
const savedPosition = windowScrollPositions.get(pathname);
|
|
681
|
-
let userRequestedScroll = false;
|
|
682
|
-
let scrollTrackingIntervalId;
|
|
683
|
-
const startScrollTracking = () => {
|
|
684
|
-
scrollTrackingIntervalId ??= globalThis.setInterval(saveScrollPosition, SCROLL_TRACKING_INTERVAL_MS);
|
|
685
|
-
};
|
|
686
|
-
const handleScroll = () => {
|
|
687
|
-
if (!savedPosition || userRequestedScroll) saveScrollPosition();
|
|
688
|
-
};
|
|
689
|
-
const markUserRequestedScroll = () => {
|
|
690
|
-
userRequestedScroll = true;
|
|
691
|
-
};
|
|
692
|
-
const handleKeyDown = (event) => {
|
|
693
|
-
if (SCROLL_KEYS.has(event.key)) markUserRequestedScroll();
|
|
694
|
-
};
|
|
695
|
-
globalThis.addEventListener("scroll", handleScroll, { passive: true });
|
|
696
|
-
globalThis.addEventListener("wheel", markUserRequestedScroll, { passive: true });
|
|
697
|
-
globalThis.addEventListener("touchstart", markUserRequestedScroll, { passive: true });
|
|
698
|
-
globalThis.addEventListener("pointerdown", markUserRequestedScroll, { passive: true });
|
|
699
|
-
globalThis.addEventListener("keydown", handleKeyDown);
|
|
700
|
-
if (!savedPosition) {
|
|
701
|
-
startScrollTracking();
|
|
702
|
-
saveScrollPosition();
|
|
703
|
-
return () => {
|
|
704
|
-
if (scrollTrackingIntervalId !== void 0) globalThis.clearInterval(scrollTrackingIntervalId);
|
|
705
|
-
globalThis.removeEventListener("scroll", handleScroll);
|
|
706
|
-
globalThis.removeEventListener("wheel", markUserRequestedScroll);
|
|
707
|
-
globalThis.removeEventListener("touchstart", markUserRequestedScroll);
|
|
708
|
-
globalThis.removeEventListener("pointerdown", markUserRequestedScroll);
|
|
709
|
-
globalThis.removeEventListener("keydown", handleKeyDown);
|
|
710
|
-
};
|
|
711
|
-
}
|
|
712
|
-
const restoreScrollPosition = () => {
|
|
713
|
-
if (userRequestedScroll) return;
|
|
714
|
-
globalThis.scrollTo(savedPosition.x, savedPosition.y);
|
|
715
|
-
saveScrollPosition();
|
|
716
|
-
startScrollTracking();
|
|
717
|
-
};
|
|
718
|
-
const timeoutIds = [];
|
|
719
|
-
let rafId = globalThis.requestAnimationFrame(() => {
|
|
720
|
-
rafId = globalThis.requestAnimationFrame(() => {
|
|
721
|
-
restoreScrollPosition();
|
|
722
|
-
for (const delay of SCROLL_RESTORE_DELAYS) timeoutIds.push(globalThis.setTimeout(restoreScrollPosition, delay));
|
|
723
|
-
});
|
|
724
|
-
});
|
|
725
|
-
return () => {
|
|
726
|
-
globalThis.cancelAnimationFrame(rafId);
|
|
727
|
-
for (const timeoutId of timeoutIds) globalThis.clearTimeout(timeoutId);
|
|
728
|
-
if (scrollTrackingIntervalId !== void 0) globalThis.clearInterval(scrollTrackingIntervalId);
|
|
729
|
-
globalThis.removeEventListener("scroll", handleScroll);
|
|
730
|
-
globalThis.removeEventListener("wheel", markUserRequestedScroll);
|
|
731
|
-
globalThis.removeEventListener("touchstart", markUserRequestedScroll);
|
|
732
|
-
globalThis.removeEventListener("pointerdown", markUserRequestedScroll);
|
|
733
|
-
globalThis.removeEventListener("keydown", handleKeyDown);
|
|
734
|
-
};
|
|
735
|
-
}, [mode, pathname]);
|
|
736
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Activity, {
|
|
737
|
-
mode,
|
|
738
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
739
|
-
className: "flex min-h-full w-full flex-1 flex-col",
|
|
740
|
-
"data-router-cache-container": "true",
|
|
741
|
-
"data-router-cache-mode": mode,
|
|
742
|
-
"data-router-cache-pathname": pathname,
|
|
743
|
-
ref: attachContainerRef,
|
|
744
|
-
children
|
|
745
|
-
})
|
|
746
|
-
});
|
|
747
|
-
}
|
|
748
|
-
//#endregion
|
|
749
|
-
//#region src/components/off-screen.tsx
|
|
750
|
-
function OffScreen(props) {
|
|
751
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OffScreenIn, { ...props });
|
|
752
|
-
}
|
|
753
|
-
//#endregion
|
|
754
|
-
//#region src/components/restore-cached-href.ts
|
|
755
|
-
const FALLBACK_ORIGIN = "http://tanstack-router-cache.local";
|
|
756
|
-
function parseHref(href) {
|
|
757
|
-
try {
|
|
758
|
-
const url = new URL(href, FALLBACK_ORIGIN);
|
|
759
|
-
return {
|
|
760
|
-
hasHash: url.hash.length > 0,
|
|
761
|
-
hasSearch: url.search.length > 0,
|
|
762
|
-
pathname: url.pathname
|
|
763
|
-
};
|
|
764
|
-
} catch {
|
|
765
|
-
return null;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
function isBareRouteHref(href, currentPathname) {
|
|
769
|
-
const candidateHref = href ?? currentPathname;
|
|
770
|
-
const parsedHref = parseHref(candidateHref);
|
|
771
|
-
if (!parsedHref) return candidateHref === currentPathname;
|
|
772
|
-
return parsedHref.pathname === currentPathname && !parsedHref.hasSearch && !parsedHref.hasHash;
|
|
773
|
-
}
|
|
774
|
-
function shouldRestoreCachedHref({ cachedHref, currentHref, currentPathname, isRouteCacheEnabled, previousPathname }) {
|
|
775
|
-
if (!(isRouteCacheEnabled && cachedHref)) return false;
|
|
776
|
-
if (!previousPathname || previousPathname === currentPathname) return false;
|
|
777
|
-
if (!isBareRouteHref(currentHref, currentPathname)) return false;
|
|
778
|
-
return cachedHref !== (currentHref ?? currentPathname);
|
|
779
|
-
}
|
|
780
|
-
//#endregion
|
|
781
|
-
//#region src/components/route-cache-manager.tsx
|
|
782
|
-
const LIVE_ROUTER_METHODS = [
|
|
783
|
-
"buildLocation",
|
|
784
|
-
"commitLocation",
|
|
785
|
-
"invalidate",
|
|
786
|
-
"loadRouteChunk",
|
|
787
|
-
"navigate",
|
|
788
|
-
"preloadRoute"
|
|
789
|
-
];
|
|
790
|
-
const routerSnapshotUpdaters = /* @__PURE__ */ new WeakMap();
|
|
791
|
-
function createSnapshotStore(value) {
|
|
792
|
-
let currentValue = value;
|
|
793
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
794
|
-
return {
|
|
795
|
-
get: () => currentValue,
|
|
796
|
-
set: (nextValue) => {
|
|
797
|
-
if (Object.is(currentValue, nextValue)) return;
|
|
798
|
-
currentValue = nextValue;
|
|
799
|
-
for (const listener of listeners) listener(currentValue);
|
|
800
|
-
},
|
|
801
|
-
subscribe: (listener) => {
|
|
802
|
-
if (listener) listeners.add(listener);
|
|
803
|
-
return { unsubscribe: listener ? () => {
|
|
804
|
-
listeners.delete(listener);
|
|
805
|
-
} : () => void 0 };
|
|
806
|
-
}
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
function createRouterSnapshotData({ matches, router, routerLocation, routerResolvedLocation }) {
|
|
810
|
-
const snapshotMatches = matches.reduce((snapshot, match) => {
|
|
811
|
-
const nextMatch = snapshotMatch(match, routerLocation);
|
|
812
|
-
if (isRouterMatch(nextMatch)) snapshot.push(nextMatch);
|
|
813
|
-
return snapshot;
|
|
814
|
-
}, []);
|
|
815
|
-
return {
|
|
816
|
-
matches: snapshotMatches,
|
|
817
|
-
state: {
|
|
818
|
-
...router.stores.__store.get(),
|
|
819
|
-
matches: snapshotMatches,
|
|
820
|
-
location: routerLocation,
|
|
821
|
-
resolvedLocation: routerResolvedLocation
|
|
822
|
-
}
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
|
-
function createMatchStore(match) {
|
|
826
|
-
return Object.assign(createSnapshotStore(match), { routeId: match.routeId });
|
|
827
|
-
}
|
|
828
|
-
function syncRouterSnapshot(routerSnapshot, input) {
|
|
829
|
-
const update = routerSnapshotUpdaters.get(routerSnapshot);
|
|
830
|
-
if (!update) return false;
|
|
831
|
-
update(input);
|
|
832
|
-
return true;
|
|
833
|
-
}
|
|
834
|
-
function getLiveRouterMethodDescriptors(router) {
|
|
835
|
-
return Object.fromEntries(LIVE_ROUTER_METHODS.flatMap((methodName) => {
|
|
836
|
-
const method = router[methodName];
|
|
837
|
-
if (typeof method !== "function") return [];
|
|
838
|
-
return [[methodName, { value: method.bind(router) }]];
|
|
839
|
-
}));
|
|
840
|
-
}
|
|
841
|
-
function toRouterLocation(location) {
|
|
842
|
-
return { ...location };
|
|
843
|
-
}
|
|
844
|
-
function isReadyCachedRoute(route) {
|
|
845
|
-
return Boolean(route && isRouteCacheEnabled(route.staticData) && route.ready && route.matchId && route.routerSnapshot);
|
|
846
|
-
}
|
|
847
|
-
function isRouteCacheEnabled(staticData) {
|
|
848
|
-
return staticData?.routeCache === true;
|
|
849
|
-
}
|
|
850
|
-
function hasErroredRouteMatch(matches) {
|
|
851
|
-
return matches.some((match) => match?.status === "error");
|
|
852
|
-
}
|
|
853
|
-
function hasRetainedRouteError(erroredRouteCounts, pathname) {
|
|
854
|
-
return (erroredRouteCounts[pathname] ?? 0) > 0;
|
|
855
|
-
}
|
|
856
|
-
function hasCurrentRouterMatchError({ matches, resolvedPathname, routerPathname }) {
|
|
857
|
-
return routerPathname === resolvedPathname && hasErroredRouteMatch(matches);
|
|
858
|
-
}
|
|
859
|
-
function hasCurrentRouteError({ erroredRouteCounts, matches, resolvedPathname, routerPathname }) {
|
|
860
|
-
return hasRetainedRouteError(erroredRouteCounts, routerPathname) || hasCurrentRouterMatchError({
|
|
861
|
-
matches,
|
|
862
|
-
resolvedPathname,
|
|
863
|
-
routerPathname
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
function isRouterMatch(match) {
|
|
867
|
-
return Boolean(match?.id && match.routeId);
|
|
868
|
-
}
|
|
869
|
-
function snapshotMatch(match, routerLocation) {
|
|
870
|
-
if (!isRouterMatch(match)) return;
|
|
871
|
-
const isLocationMatch = match.pathname ? normalizeCachedRoutePathname(match.pathname) === normalizeCachedRoutePathname(routerLocation.pathname) : false;
|
|
872
|
-
return {
|
|
873
|
-
...match,
|
|
874
|
-
...isLocationMatch ? {
|
|
875
|
-
_strictSearch: routerLocation.search,
|
|
876
|
-
search: routerLocation.search
|
|
877
|
-
} : {},
|
|
878
|
-
_nonReactive: { ...match._nonReactive }
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
function isCurrentUnmanagedCachedRoute(route, routerHref) {
|
|
882
|
-
return Boolean(route && isRouteCacheEnabled(route.staticData) && route.ready && route.routerSnapshot && route.href === routerHref);
|
|
883
|
-
}
|
|
884
|
-
function syncReadyCachedRoute({ matchId, matches, routeId, route, routerLocation, router, routerResolvedLocation, routerHref, routerPathname, setCachedRoutes, staticData }) {
|
|
885
|
-
const routerSnapshotInput = {
|
|
886
|
-
matches,
|
|
887
|
-
router,
|
|
888
|
-
routerLocation,
|
|
889
|
-
routerResolvedLocation
|
|
890
|
-
};
|
|
891
|
-
let routerSnapshot = matchId ? route?.routerSnapshot : void 0;
|
|
892
|
-
if (routerSnapshot && !syncRouterSnapshot(routerSnapshot, routerSnapshotInput)) {
|
|
893
|
-
if (isCurrentUnmanagedCachedRoute(route, routerHref)) return;
|
|
894
|
-
routerSnapshot = void 0;
|
|
895
|
-
}
|
|
896
|
-
if (matchId && !routerSnapshot) routerSnapshot = createRouterSnapshot(routerSnapshotInput);
|
|
897
|
-
setCachedRoutes(routerPathname, {
|
|
898
|
-
href: routerHref,
|
|
899
|
-
matchId,
|
|
900
|
-
routeId,
|
|
901
|
-
ready: true,
|
|
902
|
-
routerSnapshot,
|
|
903
|
-
staticData
|
|
904
|
-
});
|
|
905
|
-
}
|
|
906
|
-
function syncCachedRouteState({ isCurrentMatchReady, isCurrentMatchResolved, deleteCachedRoutes, isCurrentRouteErrored, matchId, matches, route, routeId, router, routerHref, routerLocation, routerPathname, routerResolvedLocation, setCachedRoutes, staticData }) {
|
|
907
|
-
if (isCurrentRouteErrored) {
|
|
908
|
-
if (route) deleteCachedRoutes([routerPathname]);
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
if (!isCurrentMatchResolved) return;
|
|
912
|
-
if (staticData && isRouteCacheEnabled(staticData)) {
|
|
913
|
-
if (!isCurrentMatchReady) return;
|
|
914
|
-
syncReadyCachedRoute({
|
|
915
|
-
matchId,
|
|
916
|
-
matches,
|
|
917
|
-
routeId,
|
|
918
|
-
route,
|
|
919
|
-
routerLocation,
|
|
920
|
-
router,
|
|
921
|
-
routerResolvedLocation,
|
|
922
|
-
routerHref,
|
|
923
|
-
routerPathname,
|
|
924
|
-
setCachedRoutes,
|
|
925
|
-
staticData
|
|
926
|
-
});
|
|
927
|
-
return;
|
|
928
|
-
}
|
|
929
|
-
if (route) deleteCachedRoutes([routerPathname]);
|
|
930
|
-
}
|
|
931
|
-
function createRouterSnapshot(input) {
|
|
932
|
-
const { router, routerLocation, routerResolvedLocation } = input;
|
|
933
|
-
const snapshotData = createRouterSnapshotData(input);
|
|
934
|
-
const snapshotMatches = snapshotData.matches;
|
|
935
|
-
const matchStores = new Map(snapshotMatches.map((match) => [match.id, createMatchStore(match)]));
|
|
936
|
-
const routeMatchStoreCache = /* @__PURE__ */ new Map();
|
|
937
|
-
const stores = {
|
|
938
|
-
...router.stores,
|
|
939
|
-
status: createSnapshotStore(router.stores.status.get()),
|
|
940
|
-
loadedAt: createSnapshotStore(router.stores.loadedAt.get()),
|
|
941
|
-
isLoading: createSnapshotStore(router.stores.isLoading.get()),
|
|
942
|
-
isTransitioning: createSnapshotStore(router.stores.isTransitioning.get()),
|
|
943
|
-
location: createSnapshotStore(routerLocation),
|
|
944
|
-
resolvedLocation: createSnapshotStore(routerResolvedLocation),
|
|
945
|
-
statusCode: createSnapshotStore(router.stores.statusCode.get()),
|
|
946
|
-
redirect: createSnapshotStore(router.stores.redirect.get()),
|
|
947
|
-
matchesId: createSnapshotStore(snapshotMatches.map((match) => match.id)),
|
|
948
|
-
pendingIds: createSnapshotStore([]),
|
|
949
|
-
cachedIds: createSnapshotStore([]),
|
|
950
|
-
matches: createSnapshotStore(snapshotMatches),
|
|
951
|
-
pendingMatches: createSnapshotStore([]),
|
|
952
|
-
cachedMatches: createSnapshotStore([]),
|
|
953
|
-
firstId: createSnapshotStore(snapshotMatches[0]?.id),
|
|
954
|
-
hasPending: createSnapshotStore(snapshotMatches.some((match) => match.status === "pending")),
|
|
955
|
-
matchRouteDeps: createSnapshotStore({
|
|
956
|
-
locationHref: routerLocation.href,
|
|
957
|
-
resolvedLocationHref: routerResolvedLocation?.href,
|
|
958
|
-
status: snapshotData.state.status
|
|
959
|
-
}),
|
|
960
|
-
__store: createSnapshotStore(snapshotData.state),
|
|
961
|
-
matchStores,
|
|
962
|
-
pendingMatchStores: /* @__PURE__ */ new Map(),
|
|
963
|
-
cachedMatchStores: /* @__PURE__ */ new Map(),
|
|
964
|
-
getRouteMatchStore: (routeId) => {
|
|
965
|
-
let cached = routeMatchStoreCache.get(routeId);
|
|
966
|
-
if (!cached) {
|
|
967
|
-
cached = createSnapshotStore(snapshotMatches.find((match) => match.routeId === routeId));
|
|
968
|
-
routeMatchStoreCache.set(routeId, cached);
|
|
969
|
-
}
|
|
970
|
-
return cached;
|
|
971
|
-
},
|
|
972
|
-
setMatches: () => void 0,
|
|
973
|
-
setPending: () => void 0,
|
|
974
|
-
setCached: () => void 0
|
|
975
|
-
};
|
|
976
|
-
const updateSnapshot = (nextInput) => {
|
|
977
|
-
const nextData = createRouterSnapshotData(nextInput);
|
|
978
|
-
const nextMatches = nextData.matches;
|
|
979
|
-
const nextMatchIds = new Set(nextMatches.map((match) => match.id));
|
|
980
|
-
const nextMatchesByRouteId = new Map(nextMatches.map((match) => [match.routeId, match]));
|
|
981
|
-
for (const match of nextMatches) {
|
|
982
|
-
const store = matchStores.get(match.id);
|
|
983
|
-
if (store) {
|
|
984
|
-
store.set(match);
|
|
985
|
-
continue;
|
|
986
|
-
}
|
|
987
|
-
matchStores.set(match.id, createMatchStore(match));
|
|
988
|
-
}
|
|
989
|
-
for (const [matchId] of matchStores) if (!nextMatchIds.has(matchId)) matchStores.delete(matchId);
|
|
990
|
-
stores.status.set(nextInput.router.stores.status.get());
|
|
991
|
-
stores.loadedAt.set(nextInput.router.stores.loadedAt.get());
|
|
992
|
-
stores.isLoading.set(nextInput.router.stores.isLoading.get());
|
|
993
|
-
stores.isTransitioning.set(nextInput.router.stores.isTransitioning.get());
|
|
994
|
-
stores.location.set(nextInput.routerLocation);
|
|
995
|
-
stores.resolvedLocation.set(nextInput.routerResolvedLocation);
|
|
996
|
-
stores.statusCode.set(nextInput.router.stores.statusCode.get());
|
|
997
|
-
stores.redirect.set(nextInput.router.stores.redirect.get());
|
|
998
|
-
stores.matchesId.set(nextMatches.map((match) => match.id));
|
|
999
|
-
stores.pendingIds.set([]);
|
|
1000
|
-
stores.cachedIds.set([]);
|
|
1001
|
-
stores.matches.set(nextMatches);
|
|
1002
|
-
stores.pendingMatches.set([]);
|
|
1003
|
-
stores.cachedMatches.set([]);
|
|
1004
|
-
stores.firstId.set(nextMatches[0]?.id);
|
|
1005
|
-
stores.hasPending.set(nextMatches.some((match) => match.status === "pending"));
|
|
1006
|
-
stores.matchRouteDeps.set({
|
|
1007
|
-
locationHref: nextInput.routerLocation.href,
|
|
1008
|
-
resolvedLocationHref: nextInput.routerResolvedLocation?.href,
|
|
1009
|
-
status: nextData.state.status
|
|
1010
|
-
});
|
|
1011
|
-
stores.__store.set(nextData.state);
|
|
1012
|
-
for (const [routeId, store] of routeMatchStoreCache) store.set(nextMatchesByRouteId.get(routeId));
|
|
1013
|
-
};
|
|
1014
|
-
const routerSnapshot = Object.create(router);
|
|
1015
|
-
Object.defineProperties(routerSnapshot, {
|
|
1016
|
-
stores: { value: stores },
|
|
1017
|
-
latestLocation: { get: () => stores.location.get() },
|
|
1018
|
-
getMatch: { value: (matchId) => matchStores.get(matchId)?.get() },
|
|
1019
|
-
updateMatch: { value: () => void 0 },
|
|
1020
|
-
...getLiveRouterMethodDescriptors(router)
|
|
1021
|
-
});
|
|
1022
|
-
routerSnapshotUpdaters.set(routerSnapshot, updateSnapshot);
|
|
1023
|
-
return routerSnapshot;
|
|
1024
|
-
}
|
|
1025
|
-
function getRouterCacheStaticData(childMatches, isCurrentMatchResolved) {
|
|
1026
|
-
if (!isCurrentMatchResolved) return;
|
|
1027
|
-
for (let i = childMatches.length - 1; i >= 0; i--) {
|
|
1028
|
-
const match = childMatches[i];
|
|
1029
|
-
if (match?.staticData && isRouteCacheEnabled(match.staticData)) return match.staticData;
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
function restoreCachedHref(router, href) {
|
|
1033
|
-
router.navigate({
|
|
1034
|
-
href,
|
|
1035
|
-
replace: true,
|
|
1036
|
-
resetScroll: false
|
|
1037
|
-
}).catch(() => void 0);
|
|
1038
|
-
}
|
|
1039
|
-
function renderCachedRoute({ bypassCachedPathname, pathname, route, routerPathname }) {
|
|
1040
|
-
if (pathname === bypassCachedPathname) return null;
|
|
1041
|
-
const content = route.matchId && route.routerSnapshot ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CachedOutlet, {
|
|
1042
|
-
matchId: route.matchId,
|
|
1043
|
-
routerSnapshot: route.routerSnapshot
|
|
1044
|
-
}) : null;
|
|
1045
|
-
if (!content) return null;
|
|
1046
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OffScreen, {
|
|
1047
|
-
mode: routerPathname === pathname ? "visible" : "hidden",
|
|
1048
|
-
pathname,
|
|
1049
|
-
children: content
|
|
1050
|
-
}, pathname);
|
|
1051
|
-
}
|
|
1052
|
-
function buildRouteCacheModes(cachedRoutes, routerPathname) {
|
|
1053
|
-
const nextModes = /* @__PURE__ */ new Map();
|
|
1054
|
-
for (const pathname of Object.keys(cachedRoutes)) nextModes.set(pathname, routerPathname === pathname ? "visible" : "hidden");
|
|
1055
|
-
return nextModes;
|
|
1056
|
-
}
|
|
1057
|
-
function syncCachedRouteActivityEvents(params) {
|
|
1058
|
-
const nextModes = buildRouteCacheModes(params.cachedRoutes, params.routerPathname);
|
|
1059
|
-
for (const [pathname, mode] of nextModes) {
|
|
1060
|
-
const previousMode = params.previousRouteCacheModes.get(pathname);
|
|
1061
|
-
if (previousMode === void 0 && mode === "hidden") continue;
|
|
1062
|
-
if (previousMode === mode) continue;
|
|
1063
|
-
params.eventListener.emit("activeChange", {
|
|
1064
|
-
pathname,
|
|
1065
|
-
mode
|
|
1066
|
-
});
|
|
1067
|
-
}
|
|
1068
|
-
for (const [pathname] of params.previousRouteCacheModes) {
|
|
1069
|
-
if (nextModes.has(pathname)) continue;
|
|
1070
|
-
params.eventListener.emit("activeChange", {
|
|
1071
|
-
pathname,
|
|
1072
|
-
mode: "hidden"
|
|
1073
|
-
});
|
|
1074
|
-
}
|
|
1075
|
-
return nextModes;
|
|
1076
|
-
}
|
|
1077
|
-
function RouteCacheManager() {
|
|
1078
|
-
const { cachedRoutes, deleteCachedRoutes, erroredRouteCounts, setCachedRoutes, touchCachedRoutes } = useRouterCacheContext();
|
|
1079
|
-
const { eventListener } = useEventListener();
|
|
1080
|
-
const pendingCachedNavigationRef = (0, react.useRef)(null);
|
|
1081
|
-
const previousPathnameRef = (0, react.useRef)(void 0);
|
|
1082
|
-
const previousHrefRef = (0, react.useRef)(void 0);
|
|
1083
|
-
const previousRouteCacheModesRef = (0, react.useRef)(null);
|
|
1084
|
-
previousRouteCacheModesRef.current ??= /* @__PURE__ */ new Map();
|
|
1085
|
-
const previousVisiblePathnameRef = (0, react.useRef)(void 0);
|
|
1086
|
-
const routerLocation = (0, _tanstack_react_router.useRouterState)({ select: (state) => toRouterLocation(state.location) });
|
|
1087
|
-
const routerHref = routerLocation.href;
|
|
1088
|
-
const routerPathname = normalizeCachedRoutePathname(routerLocation.pathname);
|
|
1089
|
-
const routerResolvedLocation = (0, _tanstack_react_router.useRouterState)({ select: (state) => state.resolvedLocation ? toRouterLocation(state.resolvedLocation) : void 0 });
|
|
1090
|
-
const resolvedPathname = normalizeCachedRoutePathname(routerResolvedLocation?.pathname ?? routerPathname);
|
|
1091
|
-
const destinationRoute = cachedRoutes[routerPathname];
|
|
1092
|
-
const matches = (0, _tanstack_react_router.useMatches)();
|
|
1093
|
-
const childMatches = (0, _tanstack_react_router.useChildMatches)();
|
|
1094
|
-
const router = (0, _tanstack_react_router.useRouter)();
|
|
1095
|
-
const currentMatch = matches.length ? matches.at(-1) : void 0;
|
|
1096
|
-
const outletRootMatch = childMatches.length ? childMatches[0] : currentMatch;
|
|
1097
|
-
const isCurrentMatchResolved = (currentMatch?.pathname ? normalizeCachedRoutePathname(currentMatch.pathname) : void 0) === routerPathname;
|
|
1098
|
-
const isCurrentMatchReady = isCurrentMatchResolved && currentMatch?.status === "success";
|
|
1099
|
-
const staticData = getRouterCacheStaticData(childMatches, isCurrentMatchResolved);
|
|
1100
|
-
const matchId = isCurrentMatchResolved && outletRootMatch ? outletRootMatch.id : void 0;
|
|
1101
|
-
const routeId = isCurrentMatchResolved && outletRootMatch ? outletRootMatch.routeId : void 0;
|
|
1102
|
-
const currentCachedRoute = cachedRoutes[routerPathname];
|
|
1103
|
-
const isCurrentRouteErrored = hasCurrentRouteError({
|
|
1104
|
-
erroredRouteCounts,
|
|
1105
|
-
matches,
|
|
1106
|
-
resolvedPathname,
|
|
1107
|
-
routerPathname
|
|
1108
|
-
});
|
|
1109
|
-
const bypassCachedPathname = isCurrentRouteErrored ? routerPathname : void 0;
|
|
1110
|
-
const previousPathname = previousPathnameRef.current;
|
|
1111
|
-
previousHrefRef.current;
|
|
1112
|
-
const shouldRestoreDestinationHref = shouldRestoreCachedHref({
|
|
1113
|
-
cachedHref: destinationRoute?.href,
|
|
1114
|
-
currentHref: routerHref,
|
|
1115
|
-
currentPathname: routerPathname,
|
|
1116
|
-
isRouteCacheEnabled: isRouteCacheEnabled(destinationRoute?.staticData),
|
|
1117
|
-
previousPathname
|
|
1118
|
-
});
|
|
1119
|
-
(0, react.useLayoutEffect)(() => {
|
|
1120
|
-
if (shouldRestoreDestinationHref) return;
|
|
1121
|
-
syncCachedRouteState({
|
|
1122
|
-
isCurrentMatchReady,
|
|
1123
|
-
isCurrentMatchResolved,
|
|
1124
|
-
isCurrentRouteErrored,
|
|
1125
|
-
matchId,
|
|
1126
|
-
matches,
|
|
1127
|
-
routeId,
|
|
1128
|
-
route: currentCachedRoute,
|
|
1129
|
-
routerLocation,
|
|
1130
|
-
router,
|
|
1131
|
-
routerResolvedLocation,
|
|
1132
|
-
routerHref,
|
|
1133
|
-
routerPathname,
|
|
1134
|
-
deleteCachedRoutes,
|
|
1135
|
-
setCachedRoutes,
|
|
1136
|
-
staticData
|
|
1137
|
-
});
|
|
1138
|
-
}, [
|
|
1139
|
-
currentCachedRoute,
|
|
1140
|
-
deleteCachedRoutes,
|
|
1141
|
-
isCurrentRouteErrored,
|
|
1142
|
-
isCurrentMatchReady,
|
|
1143
|
-
isCurrentMatchResolved,
|
|
1144
|
-
matchId,
|
|
1145
|
-
matches,
|
|
1146
|
-
routeId,
|
|
1147
|
-
router,
|
|
1148
|
-
routerLocation,
|
|
1149
|
-
routerResolvedLocation,
|
|
1150
|
-
routerHref,
|
|
1151
|
-
routerPathname,
|
|
1152
|
-
staticData,
|
|
1153
|
-
setCachedRoutes,
|
|
1154
|
-
shouldRestoreDestinationHref
|
|
1155
|
-
]);
|
|
1156
|
-
(0, react.useLayoutEffect)(() => {
|
|
1157
|
-
const lastVisitedPathname = previousPathnameRef.current;
|
|
1158
|
-
const pendingNavigation = pendingCachedNavigationRef.current;
|
|
1159
|
-
if (pendingNavigation && pendingNavigation.pathname !== routerPathname) {
|
|
1160
|
-
eventListener.emit("cachedNavigationCancel", pendingNavigation);
|
|
1161
|
-
pendingCachedNavigationRef.current = null;
|
|
1162
|
-
}
|
|
1163
|
-
if (!lastVisitedPathname || lastVisitedPathname === routerPathname) return;
|
|
1164
|
-
if (!isReadyCachedRoute(destinationRoute)) return;
|
|
1165
|
-
if (pendingCachedNavigationRef.current?.pathname === routerPathname) return;
|
|
1166
|
-
const nextNavigation = {
|
|
1167
|
-
pathname: routerPathname,
|
|
1168
|
-
startedAt: performance.now()
|
|
1169
|
-
};
|
|
1170
|
-
pendingCachedNavigationRef.current = nextNavigation;
|
|
1171
|
-
eventListener.emit("cachedNavigationStart", nextNavigation);
|
|
1172
|
-
}, [
|
|
1173
|
-
destinationRoute,
|
|
1174
|
-
eventListener,
|
|
1175
|
-
routerPathname
|
|
1176
|
-
]);
|
|
1177
|
-
(0, react.useLayoutEffect)(() => {
|
|
1178
|
-
previousRouteCacheModesRef.current ??= /* @__PURE__ */ new Map();
|
|
1179
|
-
previousRouteCacheModesRef.current = syncCachedRouteActivityEvents({
|
|
1180
|
-
cachedRoutes,
|
|
1181
|
-
eventListener,
|
|
1182
|
-
previousRouteCacheModes: previousRouteCacheModesRef.current,
|
|
1183
|
-
routerPathname
|
|
1184
|
-
});
|
|
1185
|
-
}, [
|
|
1186
|
-
cachedRoutes,
|
|
1187
|
-
eventListener,
|
|
1188
|
-
routerPathname
|
|
1189
|
-
]);
|
|
1190
|
-
(0, react.useLayoutEffect)(() => {
|
|
1191
|
-
if (previousVisiblePathnameRef.current === routerPathname || !cachedRoutes[routerPathname]) {
|
|
1192
|
-
previousVisiblePathnameRef.current = routerPathname;
|
|
1193
|
-
return;
|
|
1194
|
-
}
|
|
1195
|
-
previousVisiblePathnameRef.current = routerPathname;
|
|
1196
|
-
touchCachedRoutes([routerPathname]);
|
|
1197
|
-
}, [
|
|
1198
|
-
cachedRoutes,
|
|
1199
|
-
touchCachedRoutes,
|
|
1200
|
-
routerPathname
|
|
1201
|
-
]);
|
|
1202
|
-
(0, react.useEffect)(() => {
|
|
1203
|
-
const pendingNavigation = pendingCachedNavigationRef.current;
|
|
1204
|
-
if (routerPathname !== pendingNavigation?.pathname) return;
|
|
1205
|
-
let firstFrameId = 0;
|
|
1206
|
-
let secondFrameId = 0;
|
|
1207
|
-
firstFrameId = globalThis.requestAnimationFrame(() => {
|
|
1208
|
-
const visibleAt = performance.now();
|
|
1209
|
-
secondFrameId = globalThis.requestAnimationFrame(() => {
|
|
1210
|
-
const paintedAt = performance.now();
|
|
1211
|
-
if (pendingCachedNavigationRef.current?.pathname !== pendingNavigation.pathname) return;
|
|
1212
|
-
pendingCachedNavigationRef.current = null;
|
|
1213
|
-
eventListener.emit("cachedNavigationComplete", {
|
|
1214
|
-
...pendingNavigation,
|
|
1215
|
-
duration: paintedAt - pendingNavigation.startedAt,
|
|
1216
|
-
paintedAt,
|
|
1217
|
-
visibleAt
|
|
1218
|
-
});
|
|
1219
|
-
});
|
|
1220
|
-
});
|
|
1221
|
-
return () => {
|
|
1222
|
-
globalThis.cancelAnimationFrame(firstFrameId);
|
|
1223
|
-
globalThis.cancelAnimationFrame(secondFrameId);
|
|
1224
|
-
};
|
|
1225
|
-
}, [eventListener, routerPathname]);
|
|
1226
|
-
(0, react.useLayoutEffect)(() => {
|
|
1227
|
-
if (!(shouldRestoreDestinationHref && destinationRoute?.href)) return;
|
|
1228
|
-
restoreCachedHref(router, destinationRoute.href);
|
|
1229
|
-
}, [
|
|
1230
|
-
destinationRoute?.href,
|
|
1231
|
-
router,
|
|
1232
|
-
shouldRestoreDestinationHref
|
|
1233
|
-
]);
|
|
1234
|
-
(0, react.useEffect)(() => {
|
|
1235
|
-
previousPathnameRef.current = routerPathname;
|
|
1236
|
-
previousHrefRef.current = routerHref;
|
|
1237
|
-
}, [routerHref, routerPathname]);
|
|
1238
|
-
const shouldRenderLiveOutlet = routerPathname === bypassCachedPathname || !isReadyCachedRoute(destinationRoute);
|
|
1239
|
-
useRouterCacheDebug(cachedRoutes, routerPathname);
|
|
1240
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [Object.entries(cachedRoutes).map(([pathname, route]) => renderCachedRoute({
|
|
1241
|
-
bypassCachedPathname,
|
|
1242
|
-
pathname,
|
|
1243
|
-
route,
|
|
1244
|
-
routerPathname
|
|
1245
|
-
})), shouldRenderLiveOutlet ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tanstack_react_router.Outlet, {}, `live-${routerPathname}`) : null] });
|
|
1246
|
-
}
|
|
1247
|
-
//#endregion
|
|
1248
|
-
//#region src/components/router-cache-outlet.tsx
|
|
1249
|
-
function RouterCacheOutlet(props) {
|
|
1250
|
-
const { children } = props;
|
|
1251
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCacheManager, {}), children] });
|
|
1252
|
-
}
|
|
1253
|
-
//#endregion
|
|
1254
|
-
//#region src/hooks/use-route-cache-active.ts
|
|
1255
|
-
function useRoutePathname(pathname) {
|
|
1256
|
-
const locationPathname = (0, _tanstack_react_router.useLocation)({ select: (location) => location.pathname });
|
|
1257
|
-
return normalizeCachedRoutePathname(pathname ?? locationPathname);
|
|
1258
|
-
}
|
|
1259
|
-
function useRouteCacheActive(pathname) {
|
|
1260
|
-
const routePathname = useRoutePathname(pathname);
|
|
1261
|
-
const [isActive, setIsActive] = (0, react.useState)(true);
|
|
1262
|
-
useEventListener({ on: { activeChange: ({ pathname: changedPathname, mode }) => {
|
|
1263
|
-
if (changedPathname === routePathname) setIsActive(mode === "visible");
|
|
1264
|
-
} } });
|
|
1265
|
-
return isActive;
|
|
1266
|
-
}
|
|
1267
|
-
//#endregion
|
|
1268
|
-
//#region src/hooks/use-route-cache-activity.ts
|
|
1269
|
-
function useRouteCacheActivity(fn) {
|
|
1270
|
-
const routePathname = (0, _tanstack_react_router.useLocation)({ select: (location) => location.pathname });
|
|
1271
|
-
useEventListener({ on: { activeChange: ({ pathname, mode, callback }) => {
|
|
1272
|
-
if (pathname === routePathname) fn(mode === "visible");
|
|
1273
|
-
callback?.();
|
|
1274
|
-
} } });
|
|
1275
|
-
}
|
|
1276
|
-
//#endregion
|
|
1277
|
-
//#region src/hooks/use-route-cache-effect.ts
|
|
1278
|
-
function areDependenciesEqual(previousDeps, nextDeps) {
|
|
1279
|
-
if (previousDeps?.length !== nextDeps.length) return false;
|
|
1280
|
-
for (let index = 0; index < nextDeps.length; index += 1) if (!Object.is(previousDeps[index], nextDeps[index])) return false;
|
|
1281
|
-
return true;
|
|
1282
|
-
}
|
|
1283
|
-
function useRouteCacheEffect(activeCallback, deps = []) {
|
|
1284
|
-
const returnValue = (0, react.useRef)(void 0);
|
|
1285
|
-
const isActiveRef = (0, react.useRef)(false);
|
|
1286
|
-
const previousCallbackRef = (0, react.useRef)(void 0);
|
|
1287
|
-
const previousDepsRef = (0, react.useRef)(void 0);
|
|
1288
|
-
(0, react.useEffect)(() => {
|
|
1289
|
-
const callbackChanged = previousCallbackRef.current !== activeCallback;
|
|
1290
|
-
const dependenciesChanged = !areDependenciesEqual(previousDepsRef.current, deps);
|
|
1291
|
-
if (!(callbackChanged || dependenciesChanged)) return;
|
|
1292
|
-
previousCallbackRef.current = activeCallback;
|
|
1293
|
-
previousDepsRef.current = deps;
|
|
1294
|
-
if (!isActiveRef.current) return;
|
|
1295
|
-
returnValue.current?.();
|
|
1296
|
-
returnValue.current = activeCallback();
|
|
1297
|
-
});
|
|
1298
|
-
(0, react.useEffect)(() => () => {
|
|
1299
|
-
returnValue.current?.();
|
|
1300
|
-
}, []);
|
|
1301
|
-
useRouteCacheActivity((active) => {
|
|
1302
|
-
if (active) {
|
|
1303
|
-
isActiveRef.current = true;
|
|
1304
|
-
returnValue.current = activeCallback();
|
|
1305
|
-
return;
|
|
1306
|
-
}
|
|
1307
|
-
isActiveRef.current = false;
|
|
1308
|
-
returnValue.current?.();
|
|
1309
|
-
});
|
|
1310
|
-
}
|
|
1311
|
-
//#endregion
|
|
1312
|
-
//#region src/hooks/use-route-cache-error-boundary.ts
|
|
1313
|
-
function useRouteCacheErrorBoundary(pathname) {
|
|
1314
|
-
const context = useOptionalRouterCacheContext();
|
|
1315
|
-
const routePathname = (0, _tanstack_react_router.useLocation)({ select: (location) => location.pathname });
|
|
1316
|
-
(0, react.useEffect)(() => {
|
|
1317
|
-
if (!context) return;
|
|
1318
|
-
const targetPathname = normalizeCachedRoutePathname(pathname ?? routePathname);
|
|
1319
|
-
context.retainErroredRoute(targetPathname);
|
|
1320
|
-
return () => {
|
|
1321
|
-
context.releaseErroredRoute(targetPathname);
|
|
1322
|
-
};
|
|
1323
|
-
}, [
|
|
1324
|
-
context,
|
|
1325
|
-
pathname,
|
|
1326
|
-
routePathname
|
|
1327
|
-
]);
|
|
1328
|
-
}
|
|
1329
|
-
//#endregion
|
|
1330
|
-
//#region src/hooks/use-route-cache-navigation.ts
|
|
1331
|
-
const INITIAL_STATE = {
|
|
1332
|
-
activeNavigation: null,
|
|
1333
|
-
lastCompletedNavigation: null
|
|
1334
|
-
};
|
|
1335
|
-
function useRouteCacheNavigation() {
|
|
1336
|
-
const [state, setState] = (0, react.useState)(INITIAL_STATE);
|
|
1337
|
-
useEventListener({ on: {
|
|
1338
|
-
cachedNavigationStart: (navigation) => {
|
|
1339
|
-
setState((current) => ({
|
|
1340
|
-
...current,
|
|
1341
|
-
activeNavigation: navigation
|
|
1342
|
-
}));
|
|
1343
|
-
},
|
|
1344
|
-
cachedNavigationCancel: (navigation) => {
|
|
1345
|
-
setState((current) => {
|
|
1346
|
-
if (current.activeNavigation?.pathname !== navigation.pathname) return current;
|
|
1347
|
-
return {
|
|
1348
|
-
...current,
|
|
1349
|
-
activeNavigation: null
|
|
1350
|
-
};
|
|
1351
|
-
});
|
|
1352
|
-
},
|
|
1353
|
-
cachedNavigationComplete: (navigation) => {
|
|
1354
|
-
setState((current) => ({
|
|
1355
|
-
activeNavigation: current.activeNavigation?.pathname === navigation.pathname ? null : current.activeNavigation,
|
|
1356
|
-
lastCompletedNavigation: navigation
|
|
1357
|
-
}));
|
|
1358
|
-
}
|
|
1359
|
-
} });
|
|
1360
|
-
return state;
|
|
1361
|
-
}
|
|
1362
|
-
//#endregion
|
|
1363
|
-
//#region src/hooks/use-update.ts
|
|
1364
|
-
function useUpdate() {
|
|
1365
|
-
const [, setState] = (0, react.useState)({});
|
|
1366
|
-
return (0, react.useCallback)(() => setState({}), []);
|
|
1367
|
-
}
|
|
1368
|
-
//#endregion
|
|
1369
|
-
//#region src/hooks/use-router-cache.ts
|
|
1370
|
-
function useRouterCache() {
|
|
1371
|
-
const { cachedRoutes, deleteCachedRoutes } = useRouterCacheContext();
|
|
1372
|
-
const update = useUpdate();
|
|
1373
|
-
useEventListener({ on: { activeChange: () => {
|
|
1374
|
-
update();
|
|
1375
|
-
} } });
|
|
1376
|
-
const destroy = (pathname) => {
|
|
1377
|
-
deleteCachedRoutes(Array.isArray(pathname) ? pathname : [pathname]);
|
|
1378
|
-
};
|
|
1379
|
-
const destroyAll = () => {
|
|
1380
|
-
deleteCachedRoutes(Object.keys(cachedRoutes));
|
|
1381
|
-
};
|
|
1382
|
-
const invalidateWhere = (predicate) => {
|
|
1383
|
-
const pathnames = Object.entries(cachedRoutes).flatMap(([pathname, route]) => predicate(pathname, route) ? [pathname] : []);
|
|
1384
|
-
if (pathnames.length > 0) deleteCachedRoutes(pathnames);
|
|
1385
|
-
return pathnames;
|
|
1386
|
-
};
|
|
1387
|
-
return {
|
|
1388
|
-
cachedRoutes,
|
|
1389
|
-
destroy,
|
|
1390
|
-
destroyAll,
|
|
1391
|
-
invalidateWhere,
|
|
1392
|
-
isCached: (pathname) => Object.hasOwn(cachedRoutes, normalizeCachedRoutePathname(pathname))
|
|
1393
|
-
};
|
|
1394
|
-
}
|
|
1395
|
-
//#endregion
|
|
1396
|
-
exports.RouterCacheOutlet = RouterCacheOutlet;
|
|
1397
|
-
exports.RouterCacheProvider = RouterCacheProvider;
|
|
1398
|
-
exports.useRouteCacheActive = useRouteCacheActive;
|
|
1399
|
-
exports.useRouteCacheActivity = useRouteCacheActivity;
|
|
1400
|
-
exports.useRouteCacheEffect = useRouteCacheEffect;
|
|
1401
|
-
exports.useRouteCacheErrorBoundary = useRouteCacheErrorBoundary;
|
|
1402
|
-
exports.useRouteCacheNavigation = useRouteCacheNavigation;
|
|
1403
|
-
exports.useRouterCache = useRouterCache;
|
|
2
|
+
const require_route_cache_static_data = require("./route-cache-static-data.cjs");
|
|
3
|
+
const require_router_cache = require("./contexts/router-cache.cjs");
|
|
4
|
+
const require_router_cache_outlet = require("./components/router-cache-outlet.cjs");
|
|
5
|
+
const require_use_route_cache_active = require("./hooks/use-route-cache-active.cjs");
|
|
6
|
+
const require_use_route_cache_activity = require("./hooks/use-route-cache-activity.cjs");
|
|
7
|
+
const require_use_route_cache_effect = require("./hooks/use-route-cache-effect.cjs");
|
|
8
|
+
const require_use_route_cache_error_boundary = require("./hooks/use-route-cache-error-boundary.cjs");
|
|
9
|
+
const require_use_route_cache_navigation = require("./hooks/use-route-cache-navigation.cjs");
|
|
10
|
+
const require_use_router_cache = require("./hooks/use-router-cache.cjs");
|
|
11
|
+
exports.RouterCacheOutlet = require_router_cache_outlet.RouterCacheOutlet;
|
|
12
|
+
exports.RouterCacheProvider = require_router_cache.RouterCacheProvider;
|
|
13
|
+
exports.defineRouteCache = require_route_cache_static_data.defineRouteCache;
|
|
14
|
+
exports.useRouteCacheActive = require_use_route_cache_active.useRouteCacheActive;
|
|
15
|
+
exports.useRouteCacheActivity = require_use_route_cache_activity.useRouteCacheActivity;
|
|
16
|
+
exports.useRouteCacheEffect = require_use_route_cache_effect.useRouteCacheEffect;
|
|
17
|
+
exports.useRouteCacheErrorBoundary = require_use_route_cache_error_boundary.useRouteCacheErrorBoundary;
|
|
18
|
+
exports.useRouteCacheNavigation = require_use_route_cache_navigation.useRouteCacheNavigation;
|
|
19
|
+
exports.useRouterCache = require_use_router_cache.useRouterCache;
|