@tanstack/router-core 1.171.3 → 1.171.4
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/cjs/index.cjs +0 -3
- package/dist/cjs/index.d.cts +1 -2
- package/dist/cjs/rewrite.cjs +1 -6
- package/dist/cjs/rewrite.cjs.map +1 -1
- package/dist/cjs/rewrite.d.cts +0 -4
- package/dist/cjs/router.cjs +9 -2
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +5 -1
- package/dist/cjs/scroll-restoration-inline.cjs +1 -1
- package/dist/cjs/scroll-restoration-inline.cjs.map +1 -1
- package/dist/cjs/scroll-restoration-inline.d.cts +0 -2
- package/dist/cjs/scroll-restoration-script/server.cjs +2 -6
- package/dist/cjs/scroll-restoration-script/server.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.cjs +113 -107
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.d.cts +0 -10
- package/dist/esm/index.d.ts +1 -2
- package/dist/esm/index.js +2 -3
- package/dist/esm/rewrite.d.ts +0 -4
- package/dist/esm/rewrite.js +1 -6
- package/dist/esm/rewrite.js.map +1 -1
- package/dist/esm/router.d.ts +5 -1
- package/dist/esm/router.js +9 -3
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration-inline.d.ts +0 -2
- package/dist/esm/scroll-restoration-inline.js +1 -1
- package/dist/esm/scroll-restoration-inline.js.map +1 -1
- package/dist/esm/scroll-restoration-script/server.js +2 -6
- package/dist/esm/scroll-restoration-script/server.js.map +1 -1
- package/dist/esm/scroll-restoration.d.ts +0 -10
- package/dist/esm/scroll-restoration.js +114 -107
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +0 -3
- package/src/rewrite.ts +1 -8
- package/src/router.ts +28 -4
- package/src/scroll-restoration-inline.ts +7 -16
- package/src/scroll-restoration-script/server.ts +1 -10
- package/src/scroll-restoration.ts +188 -167
- package/dist/cjs/hash-scroll.cjs +0 -20
- package/dist/cjs/hash-scroll.cjs.map +0 -1
- package/dist/cjs/hash-scroll.d.cts +0 -7
- package/dist/esm/hash-scroll.d.ts +0 -7
- package/dist/esm/hash-scroll.js +0 -20
- package/dist/esm/hash-scroll.js.map +0 -1
- package/src/hash-scroll.ts +0 -21
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/scroll-restoration-inline.ts?script-string
|
|
2
|
-
var scroll_restoration_inline_default = "function(
|
|
2
|
+
var scroll_restoration_inline_default = "function(i){let l;try{l=JSON.parse(sessionStorage.getItem(i.storageKey)||\"{}\")}catch(e){console.error(e);return}const c=i.key||window.history.state?.__TSR_key,o=c?l[c]:void 0;let f=!1;if(o&&typeof o==\"object\")for(const e in o){const t=o[e];if(!t||typeof t!=\"object\")continue;const r=t.scrollX,s=t.scrollY;if(!(!Number.isFinite(r)||!Number.isFinite(s))){if(e===\"window\")window.scrollTo({top:s,left:r}),f=!0;else if(e){let n;try{n=document.querySelector(e)}catch{continue}n&&(n.scrollLeft=r,n.scrollTop=s)}}}if(f)return;const w=window.location.hash.split(\"#\",2)[1];if(w){const e=window.history.state?.__hashScrollIntoViewOptions??!0;if(e){const t=document.getElementById(w);t&&t.scrollIntoView(e)}return}window.scrollTo({top:0,left:0})}";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { scroll_restoration_inline_default as default };
|
|
5
5
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scroll-restoration-inline.js","names":[],"sources":["../../src/scroll-restoration-inline.ts?script-string"],"sourcesContent":["export default function (options: {
|
|
1
|
+
{"version":3,"file":"scroll-restoration-inline.js","names":[],"sources":["../../src/scroll-restoration-inline.ts?script-string"],"sourcesContent":["export default function (options: { storageKey: string; key?: string }) {\n let byKey\n\n try {\n byKey = JSON.parse(sessionStorage.getItem(options.storageKey) || '{}')\n } catch (error) {\n console.error(error)\n return\n }\n\n const resolvedKey = options.key || window.history.state?.__TSR_key\n const elementEntries = resolvedKey ? byKey[resolvedKey] : undefined\n let windowRestored = false\n\n if (elementEntries && typeof elementEntries === 'object') {\n for (const elementSelector in elementEntries) {\n const entry = elementEntries[elementSelector]\n\n if (!entry || typeof entry !== 'object') {\n continue\n }\n\n const scrollX = entry.scrollX\n const scrollY = entry.scrollY\n\n if (!Number.isFinite(scrollX) || !Number.isFinite(scrollY)) {\n continue\n }\n\n if (elementSelector === 'window') {\n window.scrollTo({\n top: scrollY,\n left: scrollX,\n })\n windowRestored = true\n } else if (elementSelector) {\n let element\n\n try {\n element = document.querySelector(elementSelector)\n } catch {\n continue\n }\n\n if (element) {\n element.scrollLeft = scrollX\n element.scrollTop = scrollY\n }\n }\n }\n }\n\n if (windowRestored) return\n\n const hash = window.location.hash.split('#', 2)[1]\n\n if (hash) {\n const hashScrollIntoViewOptions =\n window.history.state?.__hashScrollIntoViewOptions ?? true\n\n if (hashScrollIntoViewOptions) {\n const el = document.getElementById(hash)\n if (el) {\n el.scrollIntoView(hashScrollIntoViewOptions)\n }\n }\n\n return\n }\n\n window.scrollTo({ top: 0, left: 0 })\n}\n"],"mappings":";AAAA,IAAA,oCAAe"}
|
|
@@ -2,12 +2,9 @@ import { escapeHtml } from "../utils.js";
|
|
|
2
2
|
import { defaultGetScrollRestorationKey, storageKey } from "../scroll-restoration.js";
|
|
3
3
|
import scroll_restoration_inline_default from "../scroll-restoration-inline.js";
|
|
4
4
|
//#region src/scroll-restoration-script/server.ts
|
|
5
|
-
const defaultInlineScrollRestorationScript = `(${scroll_restoration_inline_default})(${escapeHtml(JSON.stringify({
|
|
6
|
-
storageKey,
|
|
7
|
-
shouldScrollRestoration: true
|
|
8
|
-
}))})`;
|
|
5
|
+
const defaultInlineScrollRestorationScript = `(${scroll_restoration_inline_default})(${escapeHtml(JSON.stringify({ storageKey }))})`;
|
|
9
6
|
function getScrollRestorationScript(options) {
|
|
10
|
-
if (options.storageKey === "tsr-scroll-restoration-v1_3" && options.
|
|
7
|
+
if (options.storageKey === "tsr-scroll-restoration-v1_3" && options.key === void 0) return defaultInlineScrollRestorationScript;
|
|
11
8
|
return `(${scroll_restoration_inline_default})(${escapeHtml(JSON.stringify(options))})`;
|
|
12
9
|
}
|
|
13
10
|
function getScrollRestorationScriptForRouter(router) {
|
|
@@ -19,7 +16,6 @@ function getScrollRestorationScriptForRouter(router) {
|
|
|
19
16
|
if (userKey === defaultGetScrollRestorationKey(location)) return defaultInlineScrollRestorationScript;
|
|
20
17
|
return getScrollRestorationScript({
|
|
21
18
|
storageKey,
|
|
22
|
-
shouldScrollRestoration: true,
|
|
23
19
|
key: userKey
|
|
24
20
|
});
|
|
25
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":[],"sources":["../../../src/scroll-restoration-script/server.ts"],"sourcesContent":["import minifiedScrollRestorationScript from '../scroll-restoration-inline?script-string'\nimport {\n defaultGetScrollRestorationKey,\n storageKey,\n} from '../scroll-restoration'\nimport { escapeHtml } from '../utils'\nimport type { AnyRouter } from '../router'\n\ntype InlineScrollRestorationScriptOptions = {\n storageKey: string\n key?: string\n
|
|
1
|
+
{"version":3,"file":"server.js","names":[],"sources":["../../../src/scroll-restoration-script/server.ts"],"sourcesContent":["import minifiedScrollRestorationScript from '../scroll-restoration-inline?script-string'\nimport {\n defaultGetScrollRestorationKey,\n storageKey,\n} from '../scroll-restoration'\nimport { escapeHtml } from '../utils'\nimport type { AnyRouter } from '../router'\n\ntype InlineScrollRestorationScriptOptions = {\n storageKey: string\n key?: string\n}\n\nconst defaultInlineScrollRestorationScript = `(${minifiedScrollRestorationScript})(${escapeHtml(\n JSON.stringify({\n storageKey,\n } satisfies InlineScrollRestorationScriptOptions),\n)})`\n\nfunction getScrollRestorationScript(\n options: InlineScrollRestorationScriptOptions,\n) {\n if (options.storageKey === storageKey && options.key === undefined) {\n return defaultInlineScrollRestorationScript\n }\n\n return `(${minifiedScrollRestorationScript})(${escapeHtml(JSON.stringify(options))})`\n}\n\nexport function getScrollRestorationScriptForRouter(router: AnyRouter) {\n if (\n typeof router.options.scrollRestoration === 'function' &&\n !router.options.scrollRestoration({ location: router.latestLocation })\n ) {\n return null\n }\n\n const getKey = router.options.getScrollRestorationKey\n if (!getKey) {\n return defaultInlineScrollRestorationScript\n }\n\n const location = router.latestLocation\n const userKey = getKey(location)\n const defaultKey = defaultGetScrollRestorationKey(location)\n\n if (userKey === defaultKey) {\n return defaultInlineScrollRestorationScript\n }\n\n return getScrollRestorationScript({\n storageKey,\n key: userKey,\n })\n}\n"],"mappings":";;;;AAaA,MAAM,uCAAuC,IAAI,kCAAgC,IAAI,WACnF,KAAK,UAAU,EACb,YACD,CAAgD,CAClD,CAAC;AAEF,SAAS,2BACP,SACA;AACA,KAAI,QAAQ,eAAA,iCAA6B,QAAQ,QAAQ,KAAA,EACvD,QAAO;AAGT,QAAO,IAAI,kCAAgC,IAAI,WAAW,KAAK,UAAU,QAAQ,CAAC,CAAC;;AAGrF,SAAgB,oCAAoC,QAAmB;AACrE,KACE,OAAO,OAAO,QAAQ,sBAAsB,cAC5C,CAAC,OAAO,QAAQ,kBAAkB,EAAE,UAAU,OAAO,gBAAgB,CAAC,CAEtE,QAAO;CAGT,MAAM,SAAS,OAAO,QAAQ;AAC9B,KAAI,CAAC,OACH,QAAO;CAGT,MAAM,WAAW,OAAO;CACxB,MAAM,UAAU,OAAO,SAAS;AAGhC,KAAI,YAFe,+BAA+B,SAAS,CAGzD,QAAO;AAGT,QAAO,2BAA2B;EAChC;EACA,KAAK;EACN,CAAC"}
|
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import { AnyRouter } from './router.js';
|
|
2
2
|
import { ParsedLocation } from './location.js';
|
|
3
|
-
import { NonNullableUpdater } from './utils.js';
|
|
4
3
|
export type ScrollRestorationEntry = {
|
|
5
4
|
scrollX: number;
|
|
6
5
|
scrollY: number;
|
|
7
6
|
};
|
|
8
|
-
type ScrollRestorationByElement = Record<string, ScrollRestorationEntry>;
|
|
9
|
-
type ScrollRestorationByKey = Record<string, ScrollRestorationByElement>;
|
|
10
|
-
type ScrollRestorationCache = {
|
|
11
|
-
readonly state: ScrollRestorationByKey;
|
|
12
|
-
set: (updater: NonNullableUpdater<ScrollRestorationByKey>) => void;
|
|
13
|
-
persist: () => void;
|
|
14
|
-
};
|
|
15
7
|
export type ScrollRestorationOptions = {
|
|
16
8
|
getKey?: (location: ParsedLocation) => string;
|
|
17
9
|
scrollBehavior?: ScrollToOptions['behavior'];
|
|
18
10
|
};
|
|
19
11
|
export declare const storageKey = "tsr-scroll-restoration-v1_3";
|
|
20
|
-
export declare const scrollRestorationCache: ScrollRestorationCache | null;
|
|
21
12
|
/**
|
|
22
13
|
* The default `getKey` function for `useScrollRestoration`.
|
|
23
14
|
* It returns the `key` from the location state or the `href` of the location.
|
|
@@ -35,4 +26,3 @@ export declare function getElementScrollRestorationEntry(router: AnyRouter, opti
|
|
|
35
26
|
getKey?: (location: ParsedLocation) => string;
|
|
36
27
|
}): ScrollRestorationEntry | undefined;
|
|
37
28
|
export declare function setupScrollRestoration(router: AnyRouter, force?: boolean): void;
|
|
38
|
-
export {};
|
|
@@ -1,40 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { locationHistoryActions } from "./router.js";
|
|
2
2
|
import { isServer } from "@tanstack/router-core/isServer";
|
|
3
3
|
//#region src/scroll-restoration.ts
|
|
4
4
|
function getSafeSessionStorage() {
|
|
5
5
|
try {
|
|
6
|
-
return
|
|
6
|
+
return sessionStorage;
|
|
7
7
|
} catch {
|
|
8
8
|
return;
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
const storageKey = "tsr-scroll-restoration-v1_3";
|
|
12
|
+
const safeSessionStorage = getSafeSessionStorage();
|
|
12
13
|
function createScrollRestorationCache() {
|
|
13
|
-
const safeSessionStorage = getSafeSessionStorage();
|
|
14
|
-
if (!safeSessionStorage) return null;
|
|
15
|
-
let state = {};
|
|
16
14
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
safeSessionStorage.setItem(storageKey, JSON.stringify(state));
|
|
23
|
-
} catch {
|
|
24
|
-
if (process.env.NODE_ENV !== "production") console.warn("[ts-router] Could not persist scroll restoration state to sessionStorage.");
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
return {
|
|
28
|
-
get state() {
|
|
29
|
-
return state;
|
|
30
|
-
},
|
|
31
|
-
set: (updater) => {
|
|
32
|
-
state = functionalUpdate(updater, state) || state;
|
|
33
|
-
},
|
|
34
|
-
persist
|
|
35
|
-
};
|
|
15
|
+
return JSON.parse(safeSessionStorage?.getItem("tsr-scroll-restoration-v1_3") || "{}");
|
|
16
|
+
} catch {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
36
19
|
}
|
|
37
|
-
|
|
20
|
+
function persistScrollRestorationCache() {
|
|
21
|
+
try {
|
|
22
|
+
safeSessionStorage?.setItem(storageKey, JSON.stringify(scrollRestorationCache));
|
|
23
|
+
} catch {
|
|
24
|
+
if (process.env.NODE_ENV !== "production") console.warn("[ts-router] Could not persist scroll restoration state to sessionStorage.");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const scrollRestorationCache = /* @__PURE__ */ createScrollRestorationCache();
|
|
28
|
+
const scrollRestorationIdAttribute = "data-scroll-restoration-id";
|
|
38
29
|
/**
|
|
39
30
|
* The default `getKey` function for `useScrollRestoration`.
|
|
40
31
|
* It returns the `key` from the location state or the `href` of the location.
|
|
@@ -44,144 +35,160 @@ const scrollRestorationCache = createScrollRestorationCache();
|
|
|
44
35
|
const defaultGetScrollRestorationKey = (location) => {
|
|
45
36
|
return location.state.__TSR_key || location.href;
|
|
46
37
|
};
|
|
47
|
-
function
|
|
48
|
-
const
|
|
38
|
+
function getScrollRestorationSelector(element) {
|
|
39
|
+
const attrId = element.getAttribute(scrollRestorationIdAttribute);
|
|
40
|
+
if (attrId) return `[${scrollRestorationIdAttribute}="${attrId}"]`;
|
|
41
|
+
let selector = "";
|
|
42
|
+
let el = element;
|
|
49
43
|
let parent;
|
|
50
44
|
while (parent = el.parentNode) {
|
|
51
|
-
|
|
45
|
+
let index = 1;
|
|
46
|
+
let sibling = el;
|
|
47
|
+
while (sibling = sibling.previousElementSibling) index++;
|
|
48
|
+
const part = `${el.localName}:nth-child(${index})`;
|
|
49
|
+
selector = selector ? `${part} > ${selector}` : part;
|
|
52
50
|
el = parent;
|
|
53
51
|
}
|
|
54
|
-
return
|
|
52
|
+
return selector;
|
|
55
53
|
}
|
|
56
54
|
function getElementScrollRestorationEntry(router, options) {
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
55
|
+
const entries = scrollRestorationCache[(options.getKey || defaultGetScrollRestorationKey)(router.latestLocation)];
|
|
56
|
+
if (!entries) return;
|
|
57
|
+
if (options.id) return entries[`[${scrollRestorationIdAttribute}="${options.id}"]`];
|
|
59
58
|
const element = options.getElement?.();
|
|
60
59
|
if (!element) return;
|
|
61
|
-
return
|
|
60
|
+
return entries[element === window ? windowScrollTarget : getScrollRestorationSelector(element)];
|
|
62
61
|
}
|
|
63
62
|
let ignoreScroll = false;
|
|
64
63
|
const windowScrollTarget = "window";
|
|
65
|
-
|
|
64
|
+
function getElement(selector) {
|
|
65
|
+
try {
|
|
66
|
+
return typeof selector === "function" ? selector() : document.querySelector(selector);
|
|
67
|
+
} catch {}
|
|
68
|
+
}
|
|
69
|
+
function getScrollToTopElements(scrollToTopSelectors) {
|
|
70
|
+
const elements = [];
|
|
71
|
+
for (const selector of scrollToTopSelectors) {
|
|
72
|
+
if (selector === windowScrollTarget) continue;
|
|
73
|
+
const element = getElement(selector);
|
|
74
|
+
if (element) elements.push(element);
|
|
75
|
+
}
|
|
76
|
+
return elements;
|
|
77
|
+
}
|
|
66
78
|
function setupScrollRestoration(router, force) {
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
if (force ?? router.options.scrollRestoration ?? false) router.isScrollRestoring = true;
|
|
70
|
-
if ((isServer ?? router.isServer) || router.isScrollRestorationSetup || !cache) return;
|
|
79
|
+
if (force ?? router.options.scrollRestoration) router.isScrollRestoring = true;
|
|
80
|
+
if ((isServer ?? router.isServer) || router.isScrollRestorationSetup) return;
|
|
71
81
|
router.isScrollRestorationSetup = true;
|
|
72
82
|
ignoreScroll = false;
|
|
73
83
|
const getKey = router.options.getScrollRestorationKey || defaultGetScrollRestorationKey;
|
|
74
84
|
const trackedScrollEntries = /* @__PURE__ */ new Map();
|
|
75
|
-
|
|
85
|
+
const setTrackedScrollEntry = (target, scrollX, scrollY) => {
|
|
86
|
+
const entry = trackedScrollEntries.get(target) || {};
|
|
87
|
+
entry.scrollX = scrollX;
|
|
88
|
+
entry.scrollY = scrollY;
|
|
89
|
+
trackedScrollEntries.set(target, entry);
|
|
90
|
+
};
|
|
91
|
+
history.scrollRestoration = "manual";
|
|
76
92
|
const onScroll = (event) => {
|
|
77
93
|
if (ignoreScroll || !router.isScrollRestoring) return;
|
|
78
|
-
if (event.target === document
|
|
79
|
-
scrollX: window.scrollX || 0,
|
|
80
|
-
scrollY: window.scrollY || 0
|
|
81
|
-
});
|
|
94
|
+
if (event.target === document) setTrackedScrollEntry(windowScrollTarget, scrollX, scrollY);
|
|
82
95
|
else {
|
|
83
96
|
const target = event.target;
|
|
84
|
-
|
|
85
|
-
scrollX: target.scrollLeft || 0,
|
|
86
|
-
scrollY: target.scrollTop || 0
|
|
87
|
-
});
|
|
97
|
+
setTrackedScrollEntry(target, target.scrollLeft, target.scrollTop);
|
|
88
98
|
}
|
|
89
99
|
};
|
|
90
100
|
const snapshotCurrentScrollTargets = (restoreKey) => {
|
|
91
|
-
if (!router.isScrollRestoring
|
|
92
|
-
const keyEntry =
|
|
93
|
-
for (const [target, position] of trackedScrollEntries)
|
|
94
|
-
|
|
95
|
-
if (target === windowScrollTarget) selector = windowScrollTarget;
|
|
96
|
-
else if (target.isConnected) {
|
|
97
|
-
const attrId = target.getAttribute(scrollRestorationIdAttribute);
|
|
98
|
-
selector = attrId ? `[${scrollRestorationIdAttribute}="${attrId}"]` : getCssSelector(target);
|
|
99
|
-
}
|
|
100
|
-
if (!selector) continue;
|
|
101
|
-
keyEntry[selector] = position;
|
|
102
|
-
}
|
|
101
|
+
if (!router.isScrollRestoring) return;
|
|
102
|
+
const keyEntry = scrollRestorationCache[restoreKey] ||= {};
|
|
103
|
+
for (const [target, position] of trackedScrollEntries) if (target === windowScrollTarget) keyEntry[windowScrollTarget] = position;
|
|
104
|
+
else if (target.isConnected) keyEntry[getScrollRestorationSelector(target)] = position;
|
|
103
105
|
};
|
|
104
106
|
document.addEventListener("scroll", onScroll, true);
|
|
105
107
|
router.subscribe("onBeforeLoad", (event) => {
|
|
106
|
-
|
|
108
|
+
if (event.fromLocation) snapshotCurrentScrollTargets(getKey(event.fromLocation));
|
|
107
109
|
trackedScrollEntries.clear();
|
|
108
110
|
});
|
|
109
|
-
|
|
111
|
+
addEventListener("pagehide", () => {
|
|
110
112
|
snapshotCurrentScrollTargets(getKey(router.stores.resolvedLocation.get() ?? router.stores.location.get()));
|
|
111
|
-
|
|
113
|
+
persistScrollRestorationCache();
|
|
112
114
|
});
|
|
113
115
|
router.subscribe("onRendered", (event) => {
|
|
114
|
-
const cacheKey = getKey(event.toLocation);
|
|
115
116
|
const behavior = router.options.scrollRestorationBehavior;
|
|
116
117
|
const scrollToTopSelectors = router.options.scrollToTopSelectors;
|
|
118
|
+
const shouldResetScroll = router.resetNextScroll;
|
|
119
|
+
let scrollToTopElements;
|
|
117
120
|
trackedScrollEntries.clear();
|
|
118
|
-
if (!router.resetNextScroll
|
|
119
|
-
router.resetNextScroll = true;
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
121
|
+
if (!shouldResetScroll) router.resetNextScroll = true;
|
|
122
122
|
if (typeof router.options.scrollRestoration === "function" && !router.options.scrollRestoration({ location: router.latestLocation })) return;
|
|
123
|
+
const cacheKey = getKey(event.toLocation);
|
|
124
|
+
const fromCacheKey = event.fromLocation && getKey(event.fromLocation);
|
|
125
|
+
if (router.isScrollRestoring && fromCacheKey && fromCacheKey !== cacheKey) {
|
|
126
|
+
const fromElementEntries = scrollRestorationCache[fromCacheKey];
|
|
127
|
+
if (fromElementEntries) {
|
|
128
|
+
let toElementEntries = scrollRestorationCache[cacheKey];
|
|
129
|
+
for (const elementSelector in fromElementEntries) {
|
|
130
|
+
if (elementSelector === windowScrollTarget) {
|
|
131
|
+
if (shouldResetScroll) continue;
|
|
132
|
+
} else {
|
|
133
|
+
const element = getElement(elementSelector);
|
|
134
|
+
if (!element) continue;
|
|
135
|
+
if (shouldResetScroll && scrollToTopSelectors) {
|
|
136
|
+
scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors);
|
|
137
|
+
if (scrollToTopElements.includes(element)) continue;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (!toElementEntries) toElementEntries = scrollRestorationCache[cacheKey] = {};
|
|
141
|
+
toElementEntries[elementSelector] ??= fromElementEntries[elementSelector];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (!shouldResetScroll) return;
|
|
123
146
|
ignoreScroll = true;
|
|
124
147
|
try {
|
|
125
|
-
const
|
|
126
|
-
|
|
148
|
+
const hash = event.toLocation.hash;
|
|
149
|
+
const hashScrollIntoViewOptions = event.toLocation.state.__hashScrollIntoViewOptions ?? true;
|
|
150
|
+
const action = locationHistoryActions.get(event.toLocation);
|
|
151
|
+
const skipWindowRestore = hash && hashScrollIntoViewOptions && (action === "PUSH" || action === "REPLACE");
|
|
152
|
+
const elementEntries = router.isScrollRestoring ? scrollRestorationCache[cacheKey] : void 0;
|
|
153
|
+
let windowRestored = false;
|
|
127
154
|
if (elementEntries) for (const elementSelector in elementEntries) {
|
|
128
|
-
const
|
|
129
|
-
if (!isPlainObject(entry)) continue;
|
|
130
|
-
const { scrollX, scrollY } = entry;
|
|
131
|
-
if (!Number.isFinite(scrollX) || !Number.isFinite(scrollY)) continue;
|
|
155
|
+
const { scrollX, scrollY } = elementEntries[elementSelector];
|
|
132
156
|
if (elementSelector === windowScrollTarget) {
|
|
133
|
-
|
|
157
|
+
if (skipWindowRestore) continue;
|
|
158
|
+
scrollTo({
|
|
134
159
|
top: scrollY,
|
|
135
160
|
left: scrollX,
|
|
136
161
|
behavior
|
|
137
162
|
});
|
|
138
|
-
|
|
139
|
-
} else
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
element = document.querySelector(elementSelector);
|
|
143
|
-
} catch {
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
163
|
+
windowRestored = true;
|
|
164
|
+
} else {
|
|
165
|
+
const element = getElement(elementSelector);
|
|
146
166
|
if (element) {
|
|
147
167
|
element.scrollLeft = scrollX;
|
|
148
168
|
element.scrollTop = scrollY;
|
|
149
|
-
restored = true;
|
|
150
169
|
}
|
|
151
170
|
}
|
|
152
171
|
}
|
|
153
|
-
if (!
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
behavior
|
|
166
|
-
};
|
|
167
|
-
window.scrollTo(scrollOptions);
|
|
168
|
-
if (scrollToTopSelectors) for (const selector of scrollToTopSelectors) {
|
|
169
|
-
if (selector === windowScrollTarget) continue;
|
|
170
|
-
const element = typeof selector === "function" ? selector() : document.querySelector(selector);
|
|
171
|
-
if (element) element.scrollTo(scrollOptions);
|
|
172
|
-
}
|
|
172
|
+
if (!windowRestored) if (hash) {
|
|
173
|
+
if (hashScrollIntoViewOptions) document.getElementById(hash)?.scrollIntoView(hashScrollIntoViewOptions);
|
|
174
|
+
} else {
|
|
175
|
+
const scrollOptions = {
|
|
176
|
+
top: 0,
|
|
177
|
+
left: 0,
|
|
178
|
+
behavior
|
|
179
|
+
};
|
|
180
|
+
scrollTo(scrollOptions);
|
|
181
|
+
if (scrollToTopSelectors) {
|
|
182
|
+
scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors);
|
|
183
|
+
for (const element of scrollToTopElements) element.scrollTo(scrollOptions);
|
|
173
184
|
}
|
|
174
185
|
}
|
|
175
186
|
} finally {
|
|
176
187
|
ignoreScroll = false;
|
|
177
188
|
}
|
|
178
|
-
if (router.isScrollRestoring) cache.set((state) => {
|
|
179
|
-
state[cacheKey] ||= {};
|
|
180
|
-
return state;
|
|
181
|
-
});
|
|
182
189
|
});
|
|
183
190
|
}
|
|
184
191
|
//#endregion
|
|
185
|
-
export { defaultGetScrollRestorationKey, getElementScrollRestorationEntry,
|
|
192
|
+
export { defaultGetScrollRestorationKey, getElementScrollRestorationEntry, setupScrollRestoration, storageKey };
|
|
186
193
|
|
|
187
194
|
//# sourceMappingURL=scroll-restoration.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scroll-restoration.js","names":[],"sources":["../../src/scroll-restoration.ts"],"sourcesContent":["import { isServer } from '@tanstack/router-core/isServer'\nimport { functionalUpdate, isPlainObject } from './utils'\nimport type { AnyRouter } from './router'\nimport type { ParsedLocation } from './location'\nimport type { NonNullableUpdater } from './utils'\n\nexport type ScrollRestorationEntry = { scrollX: number; scrollY: number }\n\ntype ScrollRestorationByElement = Record<string, ScrollRestorationEntry>\n\ntype ScrollRestorationByKey = Record<string, ScrollRestorationByElement>\n\ntype ScrollRestorationCache = {\n readonly state: ScrollRestorationByKey\n set: (updater: NonNullableUpdater<ScrollRestorationByKey>) => void\n persist: () => void\n}\n\nexport type ScrollRestorationOptions = {\n getKey?: (location: ParsedLocation) => string\n scrollBehavior?: ScrollToOptions['behavior']\n}\n\nfunction getSafeSessionStorage() {\n try {\n return typeof window !== 'undefined' &&\n typeof window.sessionStorage === 'object'\n ? window.sessionStorage\n : undefined\n } catch {\n // silent\n return undefined\n }\n}\n\n// SessionStorage key used to store scroll positions across navigations.\nexport const storageKey = 'tsr-scroll-restoration-v1_3'\n\nfunction createScrollRestorationCache(): ScrollRestorationCache | null {\n const safeSessionStorage = getSafeSessionStorage()\n if (!safeSessionStorage) {\n return null\n }\n\n let state: ScrollRestorationByKey = {}\n\n try {\n const parsed = JSON.parse(safeSessionStorage.getItem(storageKey) || '{}')\n if (isPlainObject(parsed)) {\n state = parsed as ScrollRestorationByKey\n }\n } catch {\n // ignore invalid session storage payloads\n }\n\n const persist = () => {\n try {\n safeSessionStorage.setItem(storageKey, JSON.stringify(state))\n } catch {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[ts-router] Could not persist scroll restoration state to sessionStorage.',\n )\n }\n }\n }\n\n return {\n get state() {\n return state\n },\n set: (updater) => {\n state = functionalUpdate(updater, state) || state\n },\n persist,\n }\n}\n\nexport const scrollRestorationCache = createScrollRestorationCache()\n\n/**\n * The default `getKey` function for `useScrollRestoration`.\n * It returns the `key` from the location state or the `href` of the location.\n *\n * The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.\n */\nexport const defaultGetScrollRestorationKey = (location: ParsedLocation) => {\n return location.state.__TSR_key! || location.href\n}\n\nfunction getCssSelector(el: any): string {\n const path = []\n let parent: HTMLElement\n while ((parent = el.parentNode)) {\n path.push(\n `${el.tagName}:nth-child(${Array.prototype.indexOf.call(parent.children, el) + 1})`,\n )\n el = parent\n }\n return `${path.reverse().join(' > ')}`.toLowerCase()\n}\n\nexport function getElementScrollRestorationEntry(\n router: AnyRouter,\n options: (\n | {\n id: string\n getElement?: () => Window | Element | undefined | null\n }\n | {\n id?: string\n getElement: () => Window | Element | undefined | null\n }\n ) & {\n getKey?: (location: ParsedLocation) => string\n },\n): ScrollRestorationEntry | undefined {\n const getKey = options.getKey || defaultGetScrollRestorationKey\n const restoreKey = getKey(router.latestLocation)\n\n if (options.id) {\n return scrollRestorationCache?.state[restoreKey]?.[\n `[${scrollRestorationIdAttribute}=\"${options.id}\"]`\n ]\n }\n\n const element = options.getElement?.()\n if (!element) {\n return\n }\n\n return scrollRestorationCache?.state[restoreKey]?.[\n element instanceof Window ? windowScrollTarget : getCssSelector(element)\n ]\n}\n\nlet ignoreScroll = false\nconst windowScrollTarget = 'window'\nconst scrollRestorationIdAttribute = 'data-scroll-restoration-id'\ntype ScrollTarget = typeof windowScrollTarget | Element\n\nexport function setupScrollRestoration(router: AnyRouter, force?: boolean) {\n if (!scrollRestorationCache && !(isServer ?? router.isServer)) {\n return\n }\n\n const cache = scrollRestorationCache\n\n const shouldScrollRestoration =\n force ?? router.options.scrollRestoration ?? false\n\n if (shouldScrollRestoration) {\n router.isScrollRestoring = true\n }\n\n if (\n (isServer ?? router.isServer) ||\n router.isScrollRestorationSetup ||\n !cache\n ) {\n return\n }\n\n router.isScrollRestorationSetup = true\n ignoreScroll = false\n\n const getKey =\n router.options.getScrollRestorationKey || defaultGetScrollRestorationKey\n const trackedScrollEntries = new Map<ScrollTarget, ScrollRestorationEntry>()\n\n window.history.scrollRestoration = 'manual'\n\n const onScroll = (event: Event) => {\n if (ignoreScroll || !router.isScrollRestoring) {\n return\n }\n\n if (event.target === document || event.target === window) {\n trackedScrollEntries.set(windowScrollTarget, {\n scrollX: window.scrollX || 0,\n scrollY: window.scrollY || 0,\n })\n } else {\n const target = event.target as Element\n trackedScrollEntries.set(target, {\n scrollX: target.scrollLeft || 0,\n scrollY: target.scrollTop || 0,\n })\n }\n }\n\n // Snapshot the current page's tracked scroll targets before navigation or unload.\n const snapshotCurrentScrollTargets = (restoreKey?: string) => {\n if (\n !router.isScrollRestoring ||\n !restoreKey ||\n trackedScrollEntries.size === 0 ||\n !cache\n ) {\n return\n }\n\n const keyEntry = (cache.state[restoreKey] ||=\n {} as ScrollRestorationByElement)\n\n for (const [target, position] of trackedScrollEntries) {\n let selector: string | undefined\n\n if (target === windowScrollTarget) {\n selector = windowScrollTarget\n } else if (target.isConnected) {\n const attrId = target.getAttribute(scrollRestorationIdAttribute)\n selector = attrId\n ? `[${scrollRestorationIdAttribute}=\"${attrId}\"]`\n : getCssSelector(target)\n }\n\n if (!selector) {\n continue\n }\n\n keyEntry[selector] = position\n }\n }\n\n document.addEventListener('scroll', onScroll, true)\n router.subscribe('onBeforeLoad', (event) => {\n snapshotCurrentScrollTargets(\n event.fromLocation ? getKey(event.fromLocation) : undefined,\n )\n trackedScrollEntries.clear()\n })\n window.addEventListener('pagehide', () => {\n snapshotCurrentScrollTargets(\n getKey(\n router.stores.resolvedLocation.get() ?? router.stores.location.get(),\n ),\n )\n cache.persist()\n })\n\n // Restore destination scroll after the new route has rendered.\n router.subscribe('onRendered', (event) => {\n const cacheKey = getKey(event.toLocation)\n const behavior = router.options.scrollRestorationBehavior\n const scrollToTopSelectors = router.options.scrollToTopSelectors\n trackedScrollEntries.clear()\n\n if (!router.resetNextScroll) {\n router.resetNextScroll = true\n return\n }\n\n if (\n typeof router.options.scrollRestoration === 'function' &&\n !router.options.scrollRestoration({ location: router.latestLocation })\n ) {\n return\n }\n\n ignoreScroll = true\n\n try {\n const elementEntries = router.isScrollRestoring\n ? cache.state[cacheKey]\n : undefined\n let restored = false\n\n if (elementEntries) {\n for (const elementSelector in elementEntries) {\n const entry = elementEntries[elementSelector]\n\n if (!isPlainObject(entry)) {\n continue\n }\n\n const { scrollX, scrollY } = entry as {\n scrollX?: unknown\n scrollY?: unknown\n }\n\n if (!Number.isFinite(scrollX) || !Number.isFinite(scrollY)) {\n continue\n }\n\n if (elementSelector === windowScrollTarget) {\n window.scrollTo({\n top: scrollY as number,\n left: scrollX as number,\n behavior,\n })\n restored = true\n } else if (elementSelector) {\n let element\n\n try {\n element = document.querySelector(elementSelector)\n } catch {\n continue\n }\n\n if (element) {\n element.scrollLeft = scrollX as number\n element.scrollTop = scrollY as number\n restored = true\n }\n }\n }\n }\n\n if (!restored) {\n const hash = router.history.location.hash.slice(1)\n\n if (hash) {\n const hashScrollIntoViewOptions =\n window.history.state?.__hashScrollIntoViewOptions ?? true\n\n if (hashScrollIntoViewOptions) {\n const el = document.getElementById(hash)\n if (el) {\n el.scrollIntoView(hashScrollIntoViewOptions)\n }\n }\n } else {\n const scrollOptions = {\n top: 0,\n left: 0,\n behavior,\n }\n\n window.scrollTo(scrollOptions)\n if (scrollToTopSelectors) {\n for (const selector of scrollToTopSelectors) {\n if (selector === windowScrollTarget) continue\n const element =\n typeof selector === 'function'\n ? selector()\n : document.querySelector(selector)\n if (element) {\n element.scrollTo(scrollOptions)\n }\n }\n }\n }\n }\n } finally {\n ignoreScroll = false\n }\n\n if (router.isScrollRestoring) {\n cache.set((state) => {\n state[cacheKey] ||= {} as ScrollRestorationByElement\n return state\n })\n }\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scroll-restoration.js","names":[],"sources":["../../src/scroll-restoration.ts"],"sourcesContent":["import { isServer } from '@tanstack/router-core/isServer'\nimport { locationHistoryActions } from './router'\nimport type { AnyRouter } from './router'\nimport type { ParsedLocation } from './location'\n\nexport type ScrollRestorationEntry = { scrollX: number; scrollY: number }\n\ntype ScrollRestorationByElement = Record<string, ScrollRestorationEntry>\n\ntype ScrollRestorationByKey = Record<string, ScrollRestorationByElement>\n\nexport type ScrollRestorationOptions = {\n getKey?: (location: ParsedLocation) => string\n scrollBehavior?: ScrollToOptions['behavior']\n}\n\nfunction getSafeSessionStorage() {\n try {\n // Accessing sessionStorage itself can throw SecurityError in locked-down\n // contexts, e.g. sandboxed/opaque origins or blocked storage policies.\n return sessionStorage\n } catch {\n return\n }\n}\n\n// SessionStorage key used to store scroll positions across navigations.\nexport const storageKey = 'tsr-scroll-restoration-v1_3'\nconst safeSessionStorage = getSafeSessionStorage()\n\nfunction createScrollRestorationCache() {\n try {\n return JSON.parse(\n safeSessionStorage?.getItem(storageKey) || '{}',\n ) as ScrollRestorationByKey\n } catch {\n // ignore invalid session storage payloads\n return {}\n }\n}\n\nfunction persistScrollRestorationCache() {\n try {\n safeSessionStorage?.setItem(\n storageKey,\n JSON.stringify(scrollRestorationCache),\n )\n } catch {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[ts-router] Could not persist scroll restoration state to sessionStorage.',\n )\n }\n }\n}\n\nconst scrollRestorationCache = /* @__PURE__ */ createScrollRestorationCache()\nconst scrollRestorationIdAttribute = 'data-scroll-restoration-id'\n\n/**\n * The default `getKey` function for `useScrollRestoration`.\n * It returns the `key` from the location state or the `href` of the location.\n *\n * The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.\n */\nexport const defaultGetScrollRestorationKey = (location: ParsedLocation) => {\n return location.state.__TSR_key! || location.href\n}\n\nfunction getScrollRestorationSelector(element: Element): string {\n const attrId = element.getAttribute(scrollRestorationIdAttribute)\n if (attrId) {\n return `[${scrollRestorationIdAttribute}=\"${attrId}\"]`\n }\n\n let selector = ''\n let el: any = element\n let parent: HTMLElement\n\n while ((parent = el.parentNode)) {\n let index = 1\n let sibling = el\n while ((sibling = sibling.previousElementSibling)) {\n index++\n }\n\n const part = `${el.localName}:nth-child(${index})`\n selector = selector ? `${part} > ${selector}` : part\n el = parent\n }\n\n return selector\n}\n\nexport function getElementScrollRestorationEntry(\n router: AnyRouter,\n options: (\n | {\n id: string\n getElement?: () => Window | Element | undefined | null\n }\n | {\n id?: string\n getElement: () => Window | Element | undefined | null\n }\n ) & {\n getKey?: (location: ParsedLocation) => string\n },\n): ScrollRestorationEntry | undefined {\n const getKey = options.getKey || defaultGetScrollRestorationKey\n const restoreKey = getKey(router.latestLocation)\n const entries = scrollRestorationCache[restoreKey]\n\n if (!entries) {\n return\n }\n\n if (options.id) {\n return entries[`[${scrollRestorationIdAttribute}=\"${options.id}\"]`]\n }\n\n const element = options.getElement?.()\n if (!element) {\n return\n }\n\n return entries[\n element === window\n ? windowScrollTarget\n : getScrollRestorationSelector(element as Element)\n ]\n}\n\nlet ignoreScroll = false\nconst windowScrollTarget = 'window'\ntype ScrollTarget = typeof windowScrollTarget | Element\n\nfunction getElement(selector: string | (() => Element | null | undefined)) {\n try {\n return typeof selector === 'function'\n ? selector()\n : document.querySelector(selector)\n } catch {}\n return\n}\n\nfunction getScrollToTopElements(\n scrollToTopSelectors: NonNullable<\n AnyRouter['options']['scrollToTopSelectors']\n >,\n): Array<Element> {\n const elements: Array<Element> = []\n\n for (const selector of scrollToTopSelectors) {\n if (selector === windowScrollTarget) {\n continue\n }\n\n const element = getElement(selector)\n if (element) {\n elements.push(element)\n }\n }\n\n return elements\n}\n\nexport function setupScrollRestoration(router: AnyRouter, force?: boolean) {\n // Keep hash/top scrolling active even when sessionStorage is unavailable.\n\n if (force ?? router.options.scrollRestoration) {\n router.isScrollRestoring = true\n }\n\n if ((isServer ?? router.isServer) || router.isScrollRestorationSetup) {\n return\n }\n\n router.isScrollRestorationSetup = true\n ignoreScroll = false\n\n const getKey =\n router.options.getScrollRestorationKey || defaultGetScrollRestorationKey\n const trackedScrollEntries = new Map<ScrollTarget, ScrollRestorationEntry>()\n const setTrackedScrollEntry = (\n target: ScrollTarget,\n scrollX: number,\n scrollY: number,\n ) => {\n const entry =\n trackedScrollEntries.get(target) || ({} as ScrollRestorationEntry)\n entry.scrollX = scrollX\n entry.scrollY = scrollY\n trackedScrollEntries.set(target, entry)\n }\n\n history.scrollRestoration = 'manual'\n\n const onScroll = (event: Event) => {\n if (ignoreScroll || !router.isScrollRestoring) {\n return\n }\n\n if (event.target === document) {\n setTrackedScrollEntry(windowScrollTarget, scrollX, scrollY)\n } else {\n const target = event.target as Element\n setTrackedScrollEntry(target, target.scrollLeft, target.scrollTop)\n }\n }\n\n // Snapshot the current page's tracked scroll targets before navigation or unload.\n const snapshotCurrentScrollTargets = (restoreKey: string) => {\n if (!router.isScrollRestoring) {\n return\n }\n\n const keyEntry = (scrollRestorationCache[restoreKey] ||=\n {} as ScrollRestorationByElement)\n\n for (const [target, position] of trackedScrollEntries) {\n if (target === windowScrollTarget) {\n keyEntry[windowScrollTarget] = position\n } else if (target.isConnected) {\n keyEntry[getScrollRestorationSelector(target)] = position\n }\n }\n }\n\n document.addEventListener('scroll', onScroll, true)\n router.subscribe('onBeforeLoad', (event) => {\n if (event.fromLocation) {\n snapshotCurrentScrollTargets(getKey(event.fromLocation))\n }\n trackedScrollEntries.clear()\n })\n addEventListener('pagehide', () => {\n snapshotCurrentScrollTargets(\n getKey(\n router.stores.resolvedLocation.get() ?? router.stores.location.get(),\n ),\n )\n persistScrollRestorationCache()\n })\n\n // Restore destination scroll after the new route has rendered.\n router.subscribe('onRendered', (event) => {\n const behavior = router.options.scrollRestorationBehavior\n const scrollToTopSelectors = router.options.scrollToTopSelectors\n const shouldResetScroll = router.resetNextScroll\n let scrollToTopElements: Array<Element> | undefined\n trackedScrollEntries.clear()\n\n if (!shouldResetScroll) {\n router.resetNextScroll = true\n }\n\n if (\n typeof router.options.scrollRestoration === 'function' &&\n !router.options.scrollRestoration({ location: router.latestLocation })\n ) {\n return\n }\n\n const cacheKey = getKey(event.toLocation)\n const fromCacheKey = event.fromLocation && getKey(event.fromLocation)\n\n if (router.isScrollRestoring && fromCacheKey && fromCacheKey !== cacheKey) {\n const fromElementEntries = scrollRestorationCache[fromCacheKey]\n\n if (fromElementEntries) {\n let toElementEntries = scrollRestorationCache[cacheKey]\n\n for (const elementSelector in fromElementEntries) {\n if (elementSelector === windowScrollTarget) {\n if (shouldResetScroll) {\n continue\n }\n } else {\n const element = getElement(elementSelector)\n if (!element) {\n continue\n }\n\n if (shouldResetScroll && scrollToTopSelectors) {\n scrollToTopElements ??=\n getScrollToTopElements(scrollToTopSelectors)\n if (scrollToTopElements.includes(element)) {\n continue\n }\n }\n }\n\n if (!toElementEntries) {\n toElementEntries = scrollRestorationCache[cacheKey] =\n {} as ScrollRestorationByElement\n }\n\n toElementEntries[elementSelector] ??=\n fromElementEntries[elementSelector]!\n }\n }\n }\n\n if (!shouldResetScroll) {\n return\n }\n\n ignoreScroll = true\n\n try {\n const hash = event.toLocation.hash\n const hashScrollIntoViewOptions =\n event.toLocation.state.__hashScrollIntoViewOptions ?? true\n const action = locationHistoryActions.get(event.toLocation)\n const skipWindowRestore =\n hash &&\n hashScrollIntoViewOptions &&\n (action === 'PUSH' || action === 'REPLACE')\n\n const elementEntries = router.isScrollRestoring\n ? scrollRestorationCache[cacheKey]\n : undefined\n let windowRestored = false\n\n if (elementEntries) {\n for (const elementSelector in elementEntries) {\n const { scrollX, scrollY } = elementEntries[elementSelector]!\n\n if (elementSelector === windowScrollTarget) {\n if (skipWindowRestore) {\n continue\n }\n\n scrollTo({\n top: scrollY,\n left: scrollX,\n behavior,\n })\n windowRestored = true\n } else {\n const element = getElement(elementSelector)\n if (element) {\n element.scrollLeft = scrollX\n element.scrollTop = scrollY\n }\n }\n }\n }\n\n if (!windowRestored) {\n if (hash) {\n if (hashScrollIntoViewOptions) {\n document\n .getElementById(hash)\n ?.scrollIntoView(hashScrollIntoViewOptions)\n }\n } else {\n const scrollOptions = {\n top: 0,\n left: 0,\n behavior,\n }\n\n scrollTo(scrollOptions)\n if (scrollToTopSelectors) {\n scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors)\n for (const element of scrollToTopElements) {\n element.scrollTo(scrollOptions)\n }\n }\n }\n }\n } finally {\n ignoreScroll = false\n }\n })\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -407,12 +407,9 @@ export {
|
|
|
407
407
|
defaultGetScrollRestorationKey,
|
|
408
408
|
getElementScrollRestorationEntry,
|
|
409
409
|
storageKey,
|
|
410
|
-
scrollRestorationCache,
|
|
411
410
|
setupScrollRestoration,
|
|
412
411
|
} from './scroll-restoration'
|
|
413
412
|
|
|
414
|
-
export { handleHashScroll } from './hash-scroll'
|
|
415
|
-
|
|
416
413
|
export type {
|
|
417
414
|
ScrollRestorationOptions,
|
|
418
415
|
ScrollRestorationEntry,
|
package/src/rewrite.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { joinPaths, trimPath } from './path'
|
|
2
2
|
import type { LocationRewrite } from './router'
|
|
3
3
|
|
|
4
|
-
/** Compose multiple rewrite pairs into a single in/out rewrite. */
|
|
5
4
|
/** Compose multiple rewrite pairs into a single in/out rewrite. */
|
|
6
5
|
export function composeRewrites(rewrites: Array<LocationRewrite>) {
|
|
7
6
|
return {
|
|
@@ -20,7 +19,6 @@ export function composeRewrites(rewrites: Array<LocationRewrite>) {
|
|
|
20
19
|
} satisfies LocationRewrite
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
/** Create a rewrite pair that strips/adds a basepath on input/output. */
|
|
24
22
|
/** Create a rewrite pair that strips/adds a basepath on input/output. */
|
|
25
23
|
export function rewriteBasepath(opts: {
|
|
26
24
|
basepath: string
|
|
@@ -28,13 +26,10 @@ export function rewriteBasepath(opts: {
|
|
|
28
26
|
}) {
|
|
29
27
|
const trimmedBasepath = trimPath(opts.basepath)
|
|
30
28
|
const normalizedBasepath = `/${trimmedBasepath}`
|
|
31
|
-
const normalizedBasepathWithSlash = `${normalizedBasepath}/`
|
|
32
29
|
const checkBasepath = opts.caseSensitive
|
|
33
30
|
? normalizedBasepath
|
|
34
31
|
: normalizedBasepath.toLowerCase()
|
|
35
|
-
const checkBasepathWithSlash =
|
|
36
|
-
? normalizedBasepathWithSlash
|
|
37
|
-
: normalizedBasepathWithSlash.toLowerCase()
|
|
32
|
+
const checkBasepathWithSlash = `${checkBasepath}/`
|
|
38
33
|
|
|
39
34
|
return {
|
|
40
35
|
input: ({ url }) => {
|
|
@@ -58,7 +53,6 @@ export function rewriteBasepath(opts: {
|
|
|
58
53
|
} satisfies LocationRewrite
|
|
59
54
|
}
|
|
60
55
|
|
|
61
|
-
/** Execute a location input rewrite if provided. */
|
|
62
56
|
/** Execute a location input rewrite if provided. */
|
|
63
57
|
export function executeRewriteInput(
|
|
64
58
|
rewrite: LocationRewrite | undefined,
|
|
@@ -75,7 +69,6 @@ export function executeRewriteInput(
|
|
|
75
69
|
return url
|
|
76
70
|
}
|
|
77
71
|
|
|
78
|
-
/** Execute a location output rewrite if provided. */
|
|
79
72
|
/** Execute a location output rewrite if provided. */
|
|
80
73
|
export function executeRewriteOutput(
|
|
81
74
|
rewrite: LocationRewrite | undefined,
|
package/src/router.ts
CHANGED
|
@@ -52,6 +52,7 @@ import type {
|
|
|
52
52
|
import type { SearchParser, SearchSerializer } from './searchParams'
|
|
53
53
|
import type { AnyRedirect, ResolvedRedirect } from './redirect'
|
|
54
54
|
import type {
|
|
55
|
+
HistoryAction,
|
|
55
56
|
HistoryLocation,
|
|
56
57
|
HistoryState,
|
|
57
58
|
ParsedHistoryState,
|
|
@@ -740,7 +741,10 @@ export type GetMatchRoutesFn = (pathname: string) => {
|
|
|
740
741
|
|
|
741
742
|
export type EmitFn = (routerEvent: RouterEvent) => void
|
|
742
743
|
|
|
743
|
-
export type LoadFn = (opts?: {
|
|
744
|
+
export type LoadFn = (opts?: {
|
|
745
|
+
sync?: boolean
|
|
746
|
+
action?: { type: HistoryAction }
|
|
747
|
+
}) => Promise<void>
|
|
744
748
|
|
|
745
749
|
export type CommitLocationFn = ({
|
|
746
750
|
viewTransition,
|
|
@@ -883,6 +887,11 @@ export function getLocationChangeInfo(
|
|
|
883
887
|
return { fromLocation, toLocation, pathChanged, hrefChanged, hashChanged }
|
|
884
888
|
}
|
|
885
889
|
|
|
890
|
+
export const locationHistoryActions = new WeakMap<
|
|
891
|
+
ParsedLocation,
|
|
892
|
+
HistoryAction
|
|
893
|
+
>()
|
|
894
|
+
|
|
886
895
|
export type CreateRouterFn = <
|
|
887
896
|
TRouteTree extends AnyRoute,
|
|
888
897
|
TTrailingSlashOption extends TrailingSlashOption = 'never',
|
|
@@ -2116,6 +2125,7 @@ export class RouterCore<
|
|
|
2116
2125
|
ignoreBlocker,
|
|
2117
2126
|
...next
|
|
2118
2127
|
}) => {
|
|
2128
|
+
let historyAction: HistoryAction | undefined
|
|
2119
2129
|
const isSameState = () => {
|
|
2120
2130
|
// the following props are ignored but may still be provided when navigating,
|
|
2121
2131
|
// temporarily add the previous values to the next state so they don't affect
|
|
@@ -2191,7 +2201,9 @@ export class RouterCore<
|
|
|
2191
2201
|
|
|
2192
2202
|
this.shouldViewTransition = viewTransition
|
|
2193
2203
|
|
|
2194
|
-
|
|
2204
|
+
historyAction = next.replace ? 'REPLACE' : 'PUSH'
|
|
2205
|
+
|
|
2206
|
+
this.history[historyAction === 'REPLACE' ? 'replace' : 'push'](
|
|
2195
2207
|
nextHistory.publicHref,
|
|
2196
2208
|
nextHistory.state,
|
|
2197
2209
|
{ ignoreBlocker },
|
|
@@ -2201,7 +2213,13 @@ export class RouterCore<
|
|
|
2201
2213
|
this.resetNextScroll = next.resetScroll ?? true
|
|
2202
2214
|
|
|
2203
2215
|
if (!this.history.subscribers.size) {
|
|
2204
|
-
this.load(
|
|
2216
|
+
this.load(
|
|
2217
|
+
historyAction
|
|
2218
|
+
? {
|
|
2219
|
+
action: { type: historyAction },
|
|
2220
|
+
}
|
|
2221
|
+
: undefined,
|
|
2222
|
+
)
|
|
2205
2223
|
}
|
|
2206
2224
|
|
|
2207
2225
|
return this.commitLocationPromise
|
|
@@ -2403,7 +2421,8 @@ export class RouterCore<
|
|
|
2403
2421
|
})
|
|
2404
2422
|
}
|
|
2405
2423
|
|
|
2406
|
-
load: LoadFn = async (opts
|
|
2424
|
+
load: LoadFn = async (opts): Promise<void> => {
|
|
2425
|
+
const historyAction = opts?.action?.type
|
|
2407
2426
|
let redirect: AnyRedirect | undefined
|
|
2408
2427
|
let notFound: NotFoundError | undefined
|
|
2409
2428
|
let loadPromise: Promise<void>
|
|
@@ -2415,6 +2434,11 @@ export class RouterCore<
|
|
|
2415
2434
|
this.startTransition(async () => {
|
|
2416
2435
|
try {
|
|
2417
2436
|
this.beforeLoad()
|
|
2437
|
+
if (historyAction) {
|
|
2438
|
+
locationHistoryActions.set(this.latestLocation, historyAction)
|
|
2439
|
+
} else {
|
|
2440
|
+
locationHistoryActions.delete(this.latestLocation)
|
|
2441
|
+
}
|
|
2418
2442
|
const next = this.latestLocation
|
|
2419
2443
|
const prevLocation = this.stores.resolvedLocation.get()
|
|
2420
2444
|
const locationChangeInfo = getLocationChangeInfo(next, prevLocation)
|