@tanstack/router-core 1.168.4 → 1.168.6
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/hash-scroll.cjs +20 -0
- package/dist/cjs/hash-scroll.cjs.map +1 -0
- package/dist/cjs/hash-scroll.d.cts +7 -0
- package/dist/cjs/index.cjs +3 -3
- package/dist/cjs/index.d.cts +2 -1
- package/dist/cjs/scroll-restoration-inline.cjs +6 -0
- package/dist/cjs/scroll-restoration-inline.cjs.map +1 -0
- package/dist/cjs/scroll-restoration-inline.d.cts +6 -0
- package/dist/cjs/scroll-restoration-script/client.cjs +9 -0
- package/dist/cjs/scroll-restoration-script/client.cjs.map +1 -0
- package/dist/cjs/scroll-restoration-script/client.d.cts +2 -0
- package/dist/cjs/scroll-restoration-script/server.cjs +30 -0
- package/dist/cjs/scroll-restoration-script/server.cjs.map +1 -0
- package/dist/cjs/scroll-restoration-script/server.d.cts +2 -0
- package/dist/cjs/scroll-restoration.cjs +124 -142
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.d.cts +15 -38
- package/dist/cjs/ssr/tsrScript.cjs +1 -1
- package/dist/esm/hash-scroll.d.ts +7 -0
- package/dist/esm/hash-scroll.js +20 -0
- package/dist/esm/hash-scroll.js.map +1 -0
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +3 -2
- package/dist/esm/scroll-restoration-inline.d.ts +6 -0
- package/dist/esm/scroll-restoration-inline.js +6 -0
- package/dist/esm/scroll-restoration-inline.js.map +1 -0
- package/dist/esm/scroll-restoration-script/client.d.ts +2 -0
- package/dist/esm/scroll-restoration-script/client.js +8 -0
- package/dist/esm/scroll-restoration-script/client.js.map +1 -0
- package/dist/esm/scroll-restoration-script/server.d.ts +2 -0
- package/dist/esm/scroll-restoration-script/server.js +29 -0
- package/dist/esm/scroll-restoration-script/server.js.map +1 -0
- package/dist/esm/scroll-restoration.d.ts +15 -38
- package/dist/esm/scroll-restoration.js +125 -141
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/ssr/tsrScript.js +1 -1
- package/package.json +21 -1
- package/src/hash-scroll.ts +21 -0
- package/src/index.ts +3 -3
- package/src/scroll-restoration-inline.ts +81 -0
- package/src/scroll-restoration-script/client.ts +5 -0
- package/src/scroll-restoration-script/server.ts +64 -0
- package/src/scroll-restoration.ts +226 -272
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
import { AnyRouter } from './router.cjs';
|
|
2
2
|
import { ParsedLocation } from './location.cjs';
|
|
3
3
|
import { NonNullableUpdater } from './utils.cjs';
|
|
4
|
-
import { HistoryLocation } from '@tanstack/history';
|
|
5
4
|
export type ScrollRestorationEntry = {
|
|
6
5
|
scrollX: number;
|
|
7
6
|
scrollY: number;
|
|
8
7
|
};
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
state: ScrollRestorationByKey;
|
|
8
|
+
type ScrollRestorationByElement = Record<string, ScrollRestorationEntry>;
|
|
9
|
+
type ScrollRestorationByKey = Record<string, ScrollRestorationByElement>;
|
|
10
|
+
type ScrollRestorationCache = {
|
|
11
|
+
readonly state: ScrollRestorationByKey;
|
|
13
12
|
set: (updater: NonNullableUpdater<ScrollRestorationByKey>) => void;
|
|
13
|
+
persist: () => void;
|
|
14
14
|
};
|
|
15
15
|
export type ScrollRestorationOptions = {
|
|
16
16
|
getKey?: (location: ParsedLocation) => string;
|
|
17
17
|
scrollBehavior?: ScrollToOptions['behavior'];
|
|
18
18
|
};
|
|
19
|
-
/** SessionStorage key used to persist scroll restoration state. */
|
|
20
|
-
/** SessionStorage key used to store scroll positions across navigations. */
|
|
21
|
-
/** SessionStorage key used to store scroll positions across navigations. */
|
|
22
19
|
export declare const storageKey = "tsr-scroll-restoration-v1_3";
|
|
23
|
-
/** In-memory handle to the persisted scroll restoration cache. */
|
|
24
20
|
export declare const scrollRestorationCache: ScrollRestorationCache | null;
|
|
25
21
|
/**
|
|
26
22
|
* The default `getKey` function for `useScrollRestoration`.
|
|
@@ -28,34 +24,15 @@ export declare const scrollRestorationCache: ScrollRestorationCache | null;
|
|
|
28
24
|
*
|
|
29
25
|
* The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.
|
|
30
26
|
*/
|
|
31
|
-
/**
|
|
32
|
-
* Default scroll restoration cache key: location state key or full href.
|
|
33
|
-
*/
|
|
34
27
|
export declare const defaultGetScrollRestorationKey: (location: ParsedLocation) => string;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}): void;
|
|
45
|
-
/** Setup global listeners and hooks to support scroll restoration. */
|
|
46
|
-
/** Setup global listeners and hooks to support scroll restoration. */
|
|
28
|
+
export declare function getElementScrollRestorationEntry(router: AnyRouter, options: ({
|
|
29
|
+
id: string;
|
|
30
|
+
getElement?: () => Window | Element | undefined | null;
|
|
31
|
+
} | {
|
|
32
|
+
id?: string;
|
|
33
|
+
getElement: () => Window | Element | undefined | null;
|
|
34
|
+
}) & {
|
|
35
|
+
getKey?: (location: ParsedLocation) => string;
|
|
36
|
+
}): ScrollRestorationEntry | undefined;
|
|
47
37
|
export declare function setupScrollRestoration(router: AnyRouter, force?: boolean): void;
|
|
48
|
-
|
|
49
|
-
* @private
|
|
50
|
-
* Handles hash-based scrolling after navigation completes.
|
|
51
|
-
* To be used in framework-specific <Transitioner> components during the onResolved event.
|
|
52
|
-
*
|
|
53
|
-
* Provides hash scrolling for programmatic navigation when default browser handling is prevented.
|
|
54
|
-
* @param router The router instance containing current location and state
|
|
55
|
-
*/
|
|
56
|
-
/**
|
|
57
|
-
* @private
|
|
58
|
-
* Handles hash-based scrolling after navigation completes.
|
|
59
|
-
* To be used in framework-specific Transitioners.
|
|
60
|
-
*/
|
|
61
|
-
export declare function handleHashScroll(router: AnyRouter): void;
|
|
38
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/ssr/tsrScript.ts?script-string
|
|
2
|
-
var tsrScript_default = "self.$_TSR={h(){this.hydrated=!0,this.c()},e(){this.streamEnded=!0,this.c()},c(){this.hydrated&&this.streamEnded&&(delete self.$_TSR,delete self.$R.tsr)},p(e){this.initialized?e():this.buffer.push(e)},buffer:[]}
|
|
2
|
+
var tsrScript_default = "self.$_TSR={h(){this.hydrated=!0,this.c()},e(){this.streamEnded=!0,this.c()},c(){this.hydrated&&this.streamEnded&&(delete self.$_TSR,delete self.$R.tsr)},p(e){this.initialized?e():this.buffer.push(e)},buffer:[]}";
|
|
3
3
|
//#endregion
|
|
4
4
|
exports.default = tsrScript_default;
|
|
5
5
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AnyRouter } from './router.js';
|
|
2
|
+
/**
|
|
3
|
+
* @private
|
|
4
|
+
* Handles hash-based scrolling after navigation completes.
|
|
5
|
+
* To be used in framework-specific <Transitioner> components during the onResolved event.
|
|
6
|
+
*/
|
|
7
|
+
export declare function handleHashScroll(router: AnyRouter): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/hash-scroll.ts
|
|
2
|
+
/**
|
|
3
|
+
* @private
|
|
4
|
+
* Handles hash-based scrolling after navigation completes.
|
|
5
|
+
* To be used in framework-specific <Transitioner> components during the onResolved event.
|
|
6
|
+
*/
|
|
7
|
+
function handleHashScroll(router) {
|
|
8
|
+
if (typeof document !== "undefined" && document.querySelector) {
|
|
9
|
+
const location = router.stores.location.state;
|
|
10
|
+
const hashScrollIntoViewOptions = location.state.__hashScrollIntoViewOptions ?? true;
|
|
11
|
+
if (hashScrollIntoViewOptions && location.hash !== "") {
|
|
12
|
+
const el = document.getElementById(location.hash);
|
|
13
|
+
if (el) el.scrollIntoView(hashScrollIntoViewOptions);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { handleHashScroll };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=hash-scroll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash-scroll.js","names":[],"sources":["../../src/hash-scroll.ts"],"sourcesContent":["import type { AnyRouter } from './router'\n\n/**\n * @private\n * Handles hash-based scrolling after navigation completes.\n * To be used in framework-specific <Transitioner> components during the onResolved event.\n */\nexport function handleHashScroll(router: AnyRouter) {\n if (typeof document !== 'undefined' && (document as any).querySelector) {\n const location = router.stores.location.state\n const hashScrollIntoViewOptions =\n location.state.__hashScrollIntoViewOptions ?? true\n\n if (hashScrollIntoViewOptions && location.hash !== '') {\n const el = document.getElementById(location.hash)\n if (el) {\n el.scrollIntoView(hashScrollIntoViewOptions)\n }\n }\n }\n}\n"],"mappings":";;;;;;AAOA,SAAgB,iBAAiB,QAAmB;AAClD,KAAI,OAAO,aAAa,eAAgB,SAAiB,eAAe;EACtE,MAAM,WAAW,OAAO,OAAO,SAAS;EACxC,MAAM,4BACJ,SAAS,MAAM,+BAA+B;AAEhD,MAAI,6BAA6B,SAAS,SAAS,IAAI;GACrD,MAAM,KAAK,SAAS,eAAe,SAAS,KAAK;AACjD,OAAI,GACF,IAAG,eAAe,0BAA0B"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -40,7 +40,8 @@ export type { Redirect, RedirectOptions, RedirectOptionsRoute, RedirectFnRoute,
|
|
|
40
40
|
export { redirect, isRedirect, isResolvedRedirect, parseRedirect, } from './redirect.js';
|
|
41
41
|
export type { NotFoundError } from './not-found.js';
|
|
42
42
|
export { isNotFound, notFound } from './not-found.js';
|
|
43
|
-
export { defaultGetScrollRestorationKey,
|
|
43
|
+
export { defaultGetScrollRestorationKey, getElementScrollRestorationEntry, storageKey, scrollRestorationCache, setupScrollRestoration, } from './scroll-restoration.js';
|
|
44
|
+
export { handleHashScroll } from './hash-scroll.js';
|
|
44
45
|
export type { ScrollRestorationOptions, ScrollRestorationEntry, } from './scroll-restoration.js';
|
|
45
46
|
export type { ValidateFromPath, ValidateToPath, ValidateSearch, ValidateParams, InferFrom, InferTo, InferMaskTo, InferMaskFrom, ValidateNavigateOptions, ValidateNavigateOptionsArray, ValidateRedirectOptions, ValidateRedirectOptionsArray, ValidateId, InferStrict, InferShouldThrow, InferSelected, ValidateUseSearchResult, ValidateUseParamsResult, } from './typePrimitives.js';
|
|
46
47
|
export type { AnySerializationAdapter, SerializationAdapter, ValidateSerializableInput, ValidateSerializableInputResult, SerializerExtensions, ValidateSerializable, RegisteredSerializableInput, SerializableExtensions, DefaultSerializable, Serializable, TSR_SERIALIZABLE, TsrSerializable, } from './ssr/serializer/transformer.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { DEFAULT_PROTOCOL_ALLOWLIST, buildDevStylesUrl, createControlledPromise,
|
|
|
2
2
|
import { invariant } from "./invariant.js";
|
|
3
3
|
import { cleanPath, exactPathTest, interpolatePath, joinPaths, removeTrailingSlash, resolvePath, trimPath, trimPathLeft, trimPathRight } from "./path.js";
|
|
4
4
|
import { isNotFound, notFound } from "./not-found.js";
|
|
5
|
-
import { defaultGetScrollRestorationKey,
|
|
5
|
+
import { defaultGetScrollRestorationKey, getElementScrollRestorationEntry, scrollRestorationCache, setupScrollRestoration, storageKey } from "./scroll-restoration.js";
|
|
6
6
|
import { decode, encode } from "./qss.js";
|
|
7
7
|
import { defaultParseSearch, defaultStringifySearch, parseSearchWith, stringifySearchWith } from "./searchParams.js";
|
|
8
8
|
import { rootRouteId } from "./root.js";
|
|
@@ -17,7 +17,8 @@ import { isMatch } from "./Matches.js";
|
|
|
17
17
|
import { BaseRootRoute, BaseRoute, BaseRouteApi } from "./route.js";
|
|
18
18
|
import { createRouterConfig } from "./config.js";
|
|
19
19
|
import { retainSearchParams, stripSearchParams } from "./searchMiddleware.js";
|
|
20
|
+
import { handleHashScroll } from "./hash-scroll.js";
|
|
20
21
|
import { createSerializationAdapter, makeSerovalPlugin, makeSsrSerovalPlugin } from "./ssr/serializer/transformer.js";
|
|
21
22
|
import { RawStream, createRawStreamDeserializePlugin, createRawStreamRPCPlugin } from "./ssr/serializer/RawStream.js";
|
|
22
23
|
import { defaultSerovalPlugins } from "./ssr/serializer/seroval-plugins.js";
|
|
23
|
-
export { BaseRootRoute, BaseRoute, BaseRouteApi, DEFAULT_PROTOCOL_ALLOWLIST, PathParamError, RawStream, RouterCore, SearchParamError, TSR_DEFERRED_PROMISE, buildDevStylesUrl, cleanPath, composeRewrites, createControlledPromise, createNonReactiveMutableStore, createNonReactiveReadonlyStore, createRawStreamDeserializePlugin, createRawStreamRPCPlugin, createRouterConfig, createSerializationAdapter, decode, deepEqual, defaultGetScrollRestorationKey, defaultParseSearch, defaultSerializeError, defaultSerovalPlugins, defaultStringifySearch, defer, encode, escapeHtml, exactPathTest, executeRewriteInput, functionalUpdate, getAssetCrossOrigin,
|
|
24
|
+
export { BaseRootRoute, BaseRoute, BaseRouteApi, DEFAULT_PROTOCOL_ALLOWLIST, PathParamError, RawStream, RouterCore, SearchParamError, TSR_DEFERRED_PROMISE, buildDevStylesUrl, cleanPath, composeRewrites, createControlledPromise, createNonReactiveMutableStore, createNonReactiveReadonlyStore, createRawStreamDeserializePlugin, createRawStreamRPCPlugin, createRouterConfig, createSerializationAdapter, decode, deepEqual, defaultGetScrollRestorationKey, defaultParseSearch, defaultSerializeError, defaultSerovalPlugins, defaultStringifySearch, defer, encode, escapeHtml, exactPathTest, executeRewriteInput, functionalUpdate, getAssetCrossOrigin, getElementScrollRestorationEntry, getInitialRouterState, getLocationChangeInfo, getMatchedRoutes, handleHashScroll, interpolatePath, invariant, isDangerousProtocol, isMatch, isModuleNotFoundError, isNotFound, isPlainArray, isPlainObject, isRedirect, isResolvedRedirect, joinPaths, lazyFn, makeSerovalPlugin, makeSsrSerovalPlugin, notFound, parseRedirect, parseSearchWith, preloadWarning, redirect, removeTrailingSlash, replaceEqualDeep, resolveManifestAssetLink, resolvePath, retainSearchParams, rootRouteId, scrollRestorationCache, setupScrollRestoration, storageKey, stringifySearchWith, stripSearchParams, trailingSlashOptions, trimPath, trimPathLeft, trimPathRight };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/scroll-restoration-inline.ts?script-string
|
|
2
|
+
var scroll_restoration_inline_default = "function(t){let s;try{s=JSON.parse(sessionStorage.getItem(t.storageKey)||\"{}\")}catch(e){console.error(e);return}const c=t.key||window.history.state?.__TSR_key,r=c?s[c]:void 0;if(t.shouldScrollRestoration&&r&&typeof r==\"object\"&&Object.keys(r).length>0){for(const e in r){const o=r[e];if(!o||typeof o!=\"object\")continue;const l=o.scrollX,i=o.scrollY;if(!(!Number.isFinite(l)||!Number.isFinite(i))){if(e===\"window\")window.scrollTo({top:i,left:l,behavior:t.behavior});else if(e){let n;try{n=document.querySelector(e)}catch{continue}n&&(n.scrollLeft=l,n.scrollTop=i)}}}return}const a=window.location.hash.split(\"#\",2)[1];if(a){const e=window.history.state?.__hashScrollIntoViewOptions??!0;if(e){const o=document.getElementById(a);o&&o.scrollIntoView(e)}return}window.scrollTo({top:0,left:0,behavior:t.behavior})}";
|
|
3
|
+
//#endregion
|
|
4
|
+
export { scroll_restoration_inline_default as default };
|
|
5
|
+
|
|
6
|
+
//# sourceMappingURL=scroll-restoration-inline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-restoration-inline.js","names":[],"sources":["../../src/scroll-restoration-inline.ts?script-string"],"sourcesContent":["export default function (options: {\n storageKey: string\n key?: string\n behavior?: ScrollToOptions['behavior']\n shouldScrollRestoration?: boolean\n}) {\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\n if (\n options.shouldScrollRestoration &&\n elementEntries &&\n typeof elementEntries === 'object' &&\n Object.keys(elementEntries).length > 0\n ) {\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 behavior: options.behavior,\n })\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 return\n }\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, behavior: options.behavior })\n}\n"],"mappings":";AAAA,IAAA,oCAAe"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":[],"sources":["../../../src/scroll-restoration-script/client.ts"],"sourcesContent":["import type { AnyRouter } from '../router'\n\nexport function getScrollRestorationScriptForRouter(_router: AnyRouter) {\n return null\n}\n"],"mappings":";AAEA,SAAgB,oCAAoC,SAAoB;AACtE,QAAO"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { escapeHtml } from "../utils.js";
|
|
2
|
+
import { defaultGetScrollRestorationKey, storageKey } from "../scroll-restoration.js";
|
|
3
|
+
import scroll_restoration_inline_default from "../scroll-restoration-inline.js";
|
|
4
|
+
//#region src/scroll-restoration-script/server.ts
|
|
5
|
+
var defaultInlineScrollRestorationScript = `(${scroll_restoration_inline_default})(${escapeHtml(JSON.stringify({
|
|
6
|
+
storageKey,
|
|
7
|
+
shouldScrollRestoration: true
|
|
8
|
+
}))})`;
|
|
9
|
+
function getScrollRestorationScript(options) {
|
|
10
|
+
if (options.storageKey === "tsr-scroll-restoration-v1_3" && options.shouldScrollRestoration === true && options.key === void 0 && options.behavior === void 0) return defaultInlineScrollRestorationScript;
|
|
11
|
+
return `(${scroll_restoration_inline_default})(${escapeHtml(JSON.stringify(options))})`;
|
|
12
|
+
}
|
|
13
|
+
function getScrollRestorationScriptForRouter(router) {
|
|
14
|
+
if (typeof router.options.scrollRestoration === "function" && !router.options.scrollRestoration({ location: router.latestLocation })) return null;
|
|
15
|
+
const getKey = router.options.getScrollRestorationKey;
|
|
16
|
+
if (!getKey) return defaultInlineScrollRestorationScript;
|
|
17
|
+
const location = router.latestLocation;
|
|
18
|
+
const userKey = getKey(location);
|
|
19
|
+
if (userKey === defaultGetScrollRestorationKey(location)) return defaultInlineScrollRestorationScript;
|
|
20
|
+
return getScrollRestorationScript({
|
|
21
|
+
storageKey,
|
|
22
|
+
shouldScrollRestoration: true,
|
|
23
|
+
key: userKey
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { getScrollRestorationScriptForRouter };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +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 behavior?: ScrollToOptions['behavior']\n shouldScrollRestoration?: boolean\n}\n\nconst defaultInlineScrollRestorationScript = `(${minifiedScrollRestorationScript})(${escapeHtml(\n JSON.stringify({\n storageKey,\n shouldScrollRestoration: true,\n } satisfies InlineScrollRestorationScriptOptions),\n)})`\n\nfunction getScrollRestorationScript(\n options: InlineScrollRestorationScriptOptions,\n) {\n if (\n options.storageKey === storageKey &&\n options.shouldScrollRestoration === true &&\n options.key === undefined &&\n options.behavior === undefined\n ) {\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 shouldScrollRestoration: true,\n key: userKey,\n })\n}\n"],"mappings":";;;;AAeA,IAAM,uCAAuC,IAAI,kCAAgC,IAAI,WACnF,KAAK,UAAU;CACb;CACA,yBAAyB;CAC1B,CAAgD,CAClD,CAAC;AAEF,SAAS,2BACP,SACA;AACA,KACE,QAAQ,eAAA,iCACR,QAAQ,4BAA4B,QACpC,QAAQ,QAAQ,KAAA,KAChB,QAAQ,aAAa,KAAA,EAErB,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,yBAAyB;EACzB,KAAK;EACN,CAAC"}
|
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
import { AnyRouter } from './router.js';
|
|
2
2
|
import { ParsedLocation } from './location.js';
|
|
3
3
|
import { NonNullableUpdater } from './utils.js';
|
|
4
|
-
import { HistoryLocation } from '@tanstack/history';
|
|
5
4
|
export type ScrollRestorationEntry = {
|
|
6
5
|
scrollX: number;
|
|
7
6
|
scrollY: number;
|
|
8
7
|
};
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
state: ScrollRestorationByKey;
|
|
8
|
+
type ScrollRestorationByElement = Record<string, ScrollRestorationEntry>;
|
|
9
|
+
type ScrollRestorationByKey = Record<string, ScrollRestorationByElement>;
|
|
10
|
+
type ScrollRestorationCache = {
|
|
11
|
+
readonly state: ScrollRestorationByKey;
|
|
13
12
|
set: (updater: NonNullableUpdater<ScrollRestorationByKey>) => void;
|
|
13
|
+
persist: () => void;
|
|
14
14
|
};
|
|
15
15
|
export type ScrollRestorationOptions = {
|
|
16
16
|
getKey?: (location: ParsedLocation) => string;
|
|
17
17
|
scrollBehavior?: ScrollToOptions['behavior'];
|
|
18
18
|
};
|
|
19
|
-
/** SessionStorage key used to persist scroll restoration state. */
|
|
20
|
-
/** SessionStorage key used to store scroll positions across navigations. */
|
|
21
|
-
/** SessionStorage key used to store scroll positions across navigations. */
|
|
22
19
|
export declare const storageKey = "tsr-scroll-restoration-v1_3";
|
|
23
|
-
/** In-memory handle to the persisted scroll restoration cache. */
|
|
24
20
|
export declare const scrollRestorationCache: ScrollRestorationCache | null;
|
|
25
21
|
/**
|
|
26
22
|
* The default `getKey` function for `useScrollRestoration`.
|
|
@@ -28,34 +24,15 @@ export declare const scrollRestorationCache: ScrollRestorationCache | null;
|
|
|
28
24
|
*
|
|
29
25
|
* The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.
|
|
30
26
|
*/
|
|
31
|
-
/**
|
|
32
|
-
* Default scroll restoration cache key: location state key or full href.
|
|
33
|
-
*/
|
|
34
27
|
export declare const defaultGetScrollRestorationKey: (location: ParsedLocation) => string;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}): void;
|
|
45
|
-
/** Setup global listeners and hooks to support scroll restoration. */
|
|
46
|
-
/** Setup global listeners and hooks to support scroll restoration. */
|
|
28
|
+
export declare function getElementScrollRestorationEntry(router: AnyRouter, options: ({
|
|
29
|
+
id: string;
|
|
30
|
+
getElement?: () => Window | Element | undefined | null;
|
|
31
|
+
} | {
|
|
32
|
+
id?: string;
|
|
33
|
+
getElement: () => Window | Element | undefined | null;
|
|
34
|
+
}) & {
|
|
35
|
+
getKey?: (location: ParsedLocation) => string;
|
|
36
|
+
}): ScrollRestorationEntry | undefined;
|
|
47
37
|
export declare function setupScrollRestoration(router: AnyRouter, force?: boolean): void;
|
|
48
|
-
|
|
49
|
-
* @private
|
|
50
|
-
* Handles hash-based scrolling after navigation completes.
|
|
51
|
-
* To be used in framework-specific <Transitioner> components during the onResolved event.
|
|
52
|
-
*
|
|
53
|
-
* Provides hash scrolling for programmatic navigation when default browser handling is prevented.
|
|
54
|
-
* @param router The router instance containing current location and state
|
|
55
|
-
*/
|
|
56
|
-
/**
|
|
57
|
-
* @private
|
|
58
|
-
* Handles hash-based scrolling after navigation completes.
|
|
59
|
-
* To be used in framework-specific Transitioners.
|
|
60
|
-
*/
|
|
61
|
-
export declare function handleHashScroll(router: AnyRouter): void;
|
|
38
|
+
export {};
|
|
@@ -1,42 +1,39 @@
|
|
|
1
|
-
import { functionalUpdate } from "./utils.js";
|
|
1
|
+
import { functionalUpdate, isPlainObject } from "./utils.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
|
-
|
|
7
|
-
} catch {
|
|
6
|
+
return typeof window !== "undefined" && typeof window.sessionStorage === "object" ? window.sessionStorage : void 0;
|
|
7
|
+
} catch {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
8
10
|
}
|
|
9
|
-
/** SessionStorage key used to persist scroll restoration state. */
|
|
10
|
-
/** SessionStorage key used to store scroll positions across navigations. */
|
|
11
|
-
/** SessionStorage key used to store scroll positions across navigations. */
|
|
12
11
|
var storageKey = "tsr-scroll-restoration-v1_3";
|
|
13
|
-
var throttle = (fn, wait) => {
|
|
14
|
-
let timeout;
|
|
15
|
-
return (...args) => {
|
|
16
|
-
if (!timeout) timeout = setTimeout(() => {
|
|
17
|
-
fn(...args);
|
|
18
|
-
timeout = null;
|
|
19
|
-
}, wait);
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
12
|
function createScrollRestorationCache() {
|
|
23
13
|
const safeSessionStorage = getSafeSessionStorage();
|
|
24
14
|
if (!safeSessionStorage) return null;
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
let state = {};
|
|
16
|
+
try {
|
|
17
|
+
const parsed = JSON.parse(safeSessionStorage.getItem("tsr-scroll-restoration-v1_3") || "{}");
|
|
18
|
+
if (isPlainObject(parsed)) state = parsed;
|
|
19
|
+
} catch {}
|
|
20
|
+
const persist = () => {
|
|
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
27
|
return {
|
|
28
|
-
state
|
|
28
|
+
get state() {
|
|
29
|
+
return state;
|
|
30
|
+
},
|
|
29
31
|
set: (updater) => {
|
|
30
32
|
state = functionalUpdate(updater, state) || state;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
} catch {
|
|
34
|
-
console.warn("[ts-router] Could not persist scroll restoration state to sessionStorage.");
|
|
35
|
-
}
|
|
36
|
-
}
|
|
33
|
+
},
|
|
34
|
+
persist
|
|
37
35
|
};
|
|
38
36
|
}
|
|
39
|
-
/** In-memory handle to the persisted scroll restoration cache. */
|
|
40
37
|
var scrollRestorationCache = createScrollRestorationCache();
|
|
41
38
|
/**
|
|
42
39
|
* The default `getKey` function for `useScrollRestoration`.
|
|
@@ -44,13 +41,9 @@ var scrollRestorationCache = createScrollRestorationCache();
|
|
|
44
41
|
*
|
|
45
42
|
* The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.
|
|
46
43
|
*/
|
|
47
|
-
/**
|
|
48
|
-
* Default scroll restoration cache key: location state key or full href.
|
|
49
|
-
*/
|
|
50
44
|
var defaultGetScrollRestorationKey = (location) => {
|
|
51
45
|
return location.state.__TSR_key || location.href;
|
|
52
46
|
};
|
|
53
|
-
/** Best-effort nth-child CSS selector for a given element. */
|
|
54
47
|
function getCssSelector(el) {
|
|
55
48
|
const path = [];
|
|
56
49
|
let parent;
|
|
@@ -60,144 +53,135 @@ function getCssSelector(el) {
|
|
|
60
53
|
}
|
|
61
54
|
return `${path.reverse().join(" > ")}`.toLowerCase();
|
|
62
55
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
console.error(error);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
const resolvedKey = key || window.history.state?.__TSR_key;
|
|
73
|
-
const elementEntries = byKey[resolvedKey];
|
|
74
|
-
ignoreScroll = true;
|
|
75
|
-
scroll: {
|
|
76
|
-
if (shouldScrollRestoration && elementEntries && Object.keys(elementEntries).length > 0) {
|
|
77
|
-
for (const elementSelector in elementEntries) {
|
|
78
|
-
const entry = elementEntries[elementSelector];
|
|
79
|
-
if (elementSelector === "window") window.scrollTo({
|
|
80
|
-
top: entry.scrollY,
|
|
81
|
-
left: entry.scrollX,
|
|
82
|
-
behavior
|
|
83
|
-
});
|
|
84
|
-
else if (elementSelector) {
|
|
85
|
-
const element = document.querySelector(elementSelector);
|
|
86
|
-
if (element) {
|
|
87
|
-
element.scrollLeft = entry.scrollX;
|
|
88
|
-
element.scrollTop = entry.scrollY;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
break scroll;
|
|
93
|
-
}
|
|
94
|
-
const hash = (location ?? window.location).hash.split("#", 2)[1];
|
|
95
|
-
if (hash) {
|
|
96
|
-
const hashScrollIntoViewOptions = window.history.state?.__hashScrollIntoViewOptions ?? true;
|
|
97
|
-
if (hashScrollIntoViewOptions) {
|
|
98
|
-
const el = document.getElementById(hash);
|
|
99
|
-
if (el) el.scrollIntoView(hashScrollIntoViewOptions);
|
|
100
|
-
}
|
|
101
|
-
break scroll;
|
|
102
|
-
}
|
|
103
|
-
const scrollOptions = {
|
|
104
|
-
top: 0,
|
|
105
|
-
left: 0,
|
|
106
|
-
behavior
|
|
107
|
-
};
|
|
108
|
-
window.scrollTo(scrollOptions);
|
|
109
|
-
if (scrollToTopSelectors) for (const selector of scrollToTopSelectors) {
|
|
110
|
-
if (selector === "window") continue;
|
|
111
|
-
const element = typeof selector === "function" ? selector() : document.querySelector(selector);
|
|
112
|
-
if (element) element.scrollTo(scrollOptions);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
ignoreScroll = false;
|
|
56
|
+
function getElementScrollRestorationEntry(router, options) {
|
|
57
|
+
const restoreKey = (options.getKey || defaultGetScrollRestorationKey)(router.latestLocation);
|
|
58
|
+
if (options.id) return scrollRestorationCache?.state[restoreKey]?.[`[${scrollRestorationIdAttribute}="${options.id}"]`];
|
|
59
|
+
const element = options.getElement?.();
|
|
60
|
+
if (!element) return;
|
|
61
|
+
return scrollRestorationCache?.state[restoreKey]?.[element instanceof Window ? windowScrollTarget : getCssSelector(element)];
|
|
116
62
|
}
|
|
117
|
-
|
|
118
|
-
|
|
63
|
+
var ignoreScroll = false;
|
|
64
|
+
var windowScrollTarget = "window";
|
|
65
|
+
var scrollRestorationIdAttribute = "data-scroll-restoration-id";
|
|
119
66
|
function setupScrollRestoration(router, force) {
|
|
120
67
|
if (!scrollRestorationCache && !(isServer ?? router.isServer)) return;
|
|
68
|
+
const cache = scrollRestorationCache;
|
|
121
69
|
if (force ?? router.options.scrollRestoration ?? false) router.isScrollRestoring = true;
|
|
122
|
-
if ((isServer ?? router.isServer) || router.isScrollRestorationSetup || !
|
|
70
|
+
if ((isServer ?? router.isServer) || router.isScrollRestorationSetup || !cache) return;
|
|
123
71
|
router.isScrollRestorationSetup = true;
|
|
124
72
|
ignoreScroll = false;
|
|
125
73
|
const getKey = router.options.getScrollRestorationKey || defaultGetScrollRestorationKey;
|
|
74
|
+
const trackedScrollEntries = /* @__PURE__ */ new Map();
|
|
126
75
|
window.history.scrollRestoration = "manual";
|
|
127
76
|
const onScroll = (event) => {
|
|
128
77
|
if (ignoreScroll || !router.isScrollRestoring) return;
|
|
129
|
-
|
|
130
|
-
|
|
78
|
+
if (event.target === document || event.target === window) trackedScrollEntries.set(windowScrollTarget, {
|
|
79
|
+
scrollX: window.scrollX || 0,
|
|
80
|
+
scrollY: window.scrollY || 0
|
|
81
|
+
});
|
|
131
82
|
else {
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
83
|
+
const target = event.target;
|
|
84
|
+
trackedScrollEntries.set(target, {
|
|
85
|
+
scrollX: target.scrollLeft || 0,
|
|
86
|
+
scrollY: target.scrollTop || 0
|
|
87
|
+
});
|
|
135
88
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
elementEntry.scrollX = element.scrollLeft || 0;
|
|
147
|
-
elementEntry.scrollY = element.scrollTop || 0;
|
|
148
|
-
}
|
|
89
|
+
};
|
|
90
|
+
const snapshotCurrentScrollTargets = (restoreKey) => {
|
|
91
|
+
if (!router.isScrollRestoring || !restoreKey || trackedScrollEntries.size === 0 || !cache) return;
|
|
92
|
+
const keyEntry = cache.state[restoreKey] ||= {};
|
|
93
|
+
for (const [target, position] of trackedScrollEntries) {
|
|
94
|
+
let selector;
|
|
95
|
+
if (target === windowScrollTarget) selector = windowScrollTarget;
|
|
96
|
+
else if (target.isConnected) {
|
|
97
|
+
const attrId = target.getAttribute(scrollRestorationIdAttribute);
|
|
98
|
+
selector = attrId ? `[${scrollRestorationIdAttribute}="${attrId}"]` : getCssSelector(target);
|
|
149
99
|
}
|
|
150
|
-
|
|
151
|
-
|
|
100
|
+
if (!selector) continue;
|
|
101
|
+
keyEntry[selector] = position;
|
|
102
|
+
}
|
|
152
103
|
};
|
|
153
|
-
|
|
104
|
+
document.addEventListener("scroll", onScroll, true);
|
|
105
|
+
router.subscribe("onBeforeLoad", (event) => {
|
|
106
|
+
snapshotCurrentScrollTargets(event.fromLocation ? getKey(event.fromLocation) : void 0);
|
|
107
|
+
trackedScrollEntries.clear();
|
|
108
|
+
});
|
|
109
|
+
window.addEventListener("pagehide", () => {
|
|
110
|
+
snapshotCurrentScrollTargets(getKey(router.stores.resolvedLocation.state ?? router.stores.location.state));
|
|
111
|
+
cache.persist();
|
|
112
|
+
});
|
|
154
113
|
router.subscribe("onRendered", (event) => {
|
|
155
114
|
const cacheKey = getKey(event.toLocation);
|
|
115
|
+
const behavior = router.options.scrollRestorationBehavior;
|
|
116
|
+
const scrollToTopSelectors = router.options.scrollToTopSelectors;
|
|
117
|
+
trackedScrollEntries.clear();
|
|
156
118
|
if (!router.resetNextScroll) {
|
|
157
119
|
router.resetNextScroll = true;
|
|
158
120
|
return;
|
|
159
121
|
}
|
|
160
|
-
if (typeof router.options.scrollRestoration === "function")
|
|
161
|
-
|
|
122
|
+
if (typeof router.options.scrollRestoration === "function" && !router.options.scrollRestoration({ location: router.latestLocation })) return;
|
|
123
|
+
ignoreScroll = true;
|
|
124
|
+
try {
|
|
125
|
+
const elementEntries = router.isScrollRestoring ? cache.state[cacheKey] : void 0;
|
|
126
|
+
let restored = false;
|
|
127
|
+
if (elementEntries) for (const elementSelector in elementEntries) {
|
|
128
|
+
const entry = elementEntries[elementSelector];
|
|
129
|
+
if (!isPlainObject(entry)) continue;
|
|
130
|
+
const { scrollX, scrollY } = entry;
|
|
131
|
+
if (!Number.isFinite(scrollX) || !Number.isFinite(scrollY)) continue;
|
|
132
|
+
if (elementSelector === windowScrollTarget) {
|
|
133
|
+
window.scrollTo({
|
|
134
|
+
top: scrollY,
|
|
135
|
+
left: scrollX,
|
|
136
|
+
behavior
|
|
137
|
+
});
|
|
138
|
+
restored = true;
|
|
139
|
+
} else if (elementSelector) {
|
|
140
|
+
let element;
|
|
141
|
+
try {
|
|
142
|
+
element = document.querySelector(elementSelector);
|
|
143
|
+
} catch {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (element) {
|
|
147
|
+
element.scrollLeft = scrollX;
|
|
148
|
+
element.scrollTop = scrollY;
|
|
149
|
+
restored = true;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (!restored) {
|
|
154
|
+
const hash = router.history.location.hash.slice(1);
|
|
155
|
+
if (hash) {
|
|
156
|
+
const hashScrollIntoViewOptions = window.history.state?.__hashScrollIntoViewOptions ?? true;
|
|
157
|
+
if (hashScrollIntoViewOptions) {
|
|
158
|
+
const el = document.getElementById(hash);
|
|
159
|
+
if (el) el.scrollIntoView(hashScrollIntoViewOptions);
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
const scrollOptions = {
|
|
163
|
+
top: 0,
|
|
164
|
+
left: 0,
|
|
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
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
} finally {
|
|
176
|
+
ignoreScroll = false;
|
|
162
177
|
}
|
|
163
|
-
|
|
164
|
-
storageKey,
|
|
165
|
-
key: cacheKey,
|
|
166
|
-
behavior: router.options.scrollRestorationBehavior,
|
|
167
|
-
shouldScrollRestoration: router.isScrollRestoring,
|
|
168
|
-
scrollToTopSelectors: router.options.scrollToTopSelectors,
|
|
169
|
-
location: router.history.location
|
|
170
|
-
});
|
|
171
|
-
if (router.isScrollRestoring) scrollRestorationCache.set((state) => {
|
|
178
|
+
if (router.isScrollRestoring) cache.set((state) => {
|
|
172
179
|
state[cacheKey] ||= {};
|
|
173
180
|
return state;
|
|
174
181
|
});
|
|
175
182
|
});
|
|
176
183
|
}
|
|
177
|
-
/**
|
|
178
|
-
* @private
|
|
179
|
-
* Handles hash-based scrolling after navigation completes.
|
|
180
|
-
* To be used in framework-specific <Transitioner> components during the onResolved event.
|
|
181
|
-
*
|
|
182
|
-
* Provides hash scrolling for programmatic navigation when default browser handling is prevented.
|
|
183
|
-
* @param router The router instance containing current location and state
|
|
184
|
-
*/
|
|
185
|
-
/**
|
|
186
|
-
* @private
|
|
187
|
-
* Handles hash-based scrolling after navigation completes.
|
|
188
|
-
* To be used in framework-specific Transitioners.
|
|
189
|
-
*/
|
|
190
|
-
function handleHashScroll(router) {
|
|
191
|
-
if (typeof document !== "undefined" && document.querySelector) {
|
|
192
|
-
const location = router.stores.location.state;
|
|
193
|
-
const hashScrollIntoViewOptions = location.state.__hashScrollIntoViewOptions ?? true;
|
|
194
|
-
if (hashScrollIntoViewOptions && location.hash !== "") {
|
|
195
|
-
const el = document.getElementById(location.hash);
|
|
196
|
-
if (el) el.scrollIntoView(hashScrollIntoViewOptions);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
184
|
//#endregion
|
|
201
|
-
export { defaultGetScrollRestorationKey,
|
|
185
|
+
export { defaultGetScrollRestorationKey, getElementScrollRestorationEntry, scrollRestorationCache, setupScrollRestoration, storageKey };
|
|
202
186
|
|
|
203
187
|
//# sourceMappingURL=scroll-restoration.js.map
|