191 lines
7.1 KiB
Plaintext
191 lines
7.1 KiB
Plaintext
const require_utils = require("./utils.cjs");
|
|
let _tanstack_router_core_isServer = require("@tanstack/router-core/isServer");
|
|
//#region src/scroll-restoration.ts
|
|
function getSafeSessionStorage() {
|
|
try {
|
|
return typeof window !== "undefined" && typeof window.sessionStorage === "object" ? window.sessionStorage : void 0;
|
|
} catch {
|
|
return;
|
|
}
|
|
}
|
|
const storageKey = "tsr-scroll-restoration-v1_3";
|
|
function createScrollRestorationCache() {
|
|
const safeSessionStorage = getSafeSessionStorage();
|
|
if (!safeSessionStorage) return null;
|
|
let state = {};
|
|
try {
|
|
const parsed = JSON.parse(safeSessionStorage.getItem("tsr-scroll-restoration-v1_3") || "{}");
|
|
if (require_utils.isPlainObject(parsed)) state = parsed;
|
|
} catch {}
|
|
const persist = () => {
|
|
try {
|
|
safeSessionStorage.setItem(storageKey, JSON.stringify(state));
|
|
} catch {
|
|
if (process.env.NODE_ENV !== "production") console.warn("[ts-router] Could not persist scroll restoration state to sessionStorage.");
|
|
}
|
|
};
|
|
return {
|
|
get state() {
|
|
return state;
|
|
},
|
|
set: (updater) => {
|
|
state = require_utils.functionalUpdate(updater, state) || state;
|
|
},
|
|
persist
|
|
};
|
|
}
|
|
const scrollRestorationCache = createScrollRestorationCache();
|
|
/**
|
|
* The default `getKey` function for `useScrollRestoration`.
|
|
* It returns the `key` from the location state or the `href` of the location.
|
|
*
|
|
* The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.
|
|
*/
|
|
const defaultGetScrollRestorationKey = (location) => {
|
|
return location.state.__TSR_key || location.href;
|
|
};
|
|
function getCssSelector(el) {
|
|
const path = [];
|
|
let parent;
|
|
while (parent = el.parentNode) {
|
|
path.push(`${el.tagName}:nth-child(${Array.prototype.indexOf.call(parent.children, el) + 1})`);
|
|
el = parent;
|
|
}
|
|
return `${path.reverse().join(" > ")}`.toLowerCase();
|
|
}
|
|
function getElementScrollRestorationEntry(router, options) {
|
|
const restoreKey = (options.getKey || defaultGetScrollRestorationKey)(router.latestLocation);
|
|
if (options.id) return scrollRestorationCache?.state[restoreKey]?.[`[${scrollRestorationIdAttribute}="${options.id}"]`];
|
|
const element = options.getElement?.();
|
|
if (!element) return;
|
|
return scrollRestorationCache?.state[restoreKey]?.[element instanceof Window ? windowScrollTarget : getCssSelector(element)];
|
|
}
|
|
let ignoreScroll = false;
|
|
const windowScrollTarget = "window";
|
|
const scrollRestorationIdAttribute = "data-scroll-restoration-id";
|
|
function setupScrollRestoration(router, force) {
|
|
if (!scrollRestorationCache && !(_tanstack_router_core_isServer.isServer ?? router.isServer)) return;
|
|
const cache = scrollRestorationCache;
|
|
if (force ?? router.options.scrollRestoration ?? false) router.isScrollRestoring = true;
|
|
if ((_tanstack_router_core_isServer.isServer ?? router.isServer) || router.isScrollRestorationSetup || !cache) return;
|
|
router.isScrollRestorationSetup = true;
|
|
ignoreScroll = false;
|
|
const getKey = router.options.getScrollRestorationKey || defaultGetScrollRestorationKey;
|
|
const trackedScrollEntries = /* @__PURE__ */ new Map();
|
|
window.history.scrollRestoration = "manual";
|
|
const onScroll = (event) => {
|
|
if (ignoreScroll || !router.isScrollRestoring) return;
|
|
if (event.target === document || event.target === window) trackedScrollEntries.set(windowScrollTarget, {
|
|
scrollX: window.scrollX || 0,
|
|
scrollY: window.scrollY || 0
|
|
});
|
|
else {
|
|
const target = event.target;
|
|
trackedScrollEntries.set(target, {
|
|
scrollX: target.scrollLeft || 0,
|
|
scrollY: target.scrollTop || 0
|
|
});
|
|
}
|
|
};
|
|
const snapshotCurrentScrollTargets = (restoreKey) => {
|
|
if (!router.isScrollRestoring || !restoreKey || trackedScrollEntries.size === 0 || !cache) return;
|
|
const keyEntry = cache.state[restoreKey] ||= {};
|
|
for (const [target, position] of trackedScrollEntries) {
|
|
let selector;
|
|
if (target === windowScrollTarget) selector = windowScrollTarget;
|
|
else if (target.isConnected) {
|
|
const attrId = target.getAttribute(scrollRestorationIdAttribute);
|
|
selector = attrId ? `[${scrollRestorationIdAttribute}="${attrId}"]` : getCssSelector(target);
|
|
}
|
|
if (!selector) continue;
|
|
keyEntry[selector] = position;
|
|
}
|
|
};
|
|
document.addEventListener("scroll", onScroll, true);
|
|
router.subscribe("onBeforeLoad", (event) => {
|
|
snapshotCurrentScrollTargets(event.fromLocation ? getKey(event.fromLocation) : void 0);
|
|
trackedScrollEntries.clear();
|
|
});
|
|
window.addEventListener("pagehide", () => {
|
|
snapshotCurrentScrollTargets(getKey(router.stores.resolvedLocation.get() ?? router.stores.location.get()));
|
|
cache.persist();
|
|
});
|
|
router.subscribe("onRendered", (event) => {
|
|
const cacheKey = getKey(event.toLocation);
|
|
const behavior = router.options.scrollRestorationBehavior;
|
|
const scrollToTopSelectors = router.options.scrollToTopSelectors;
|
|
trackedScrollEntries.clear();
|
|
if (!router.resetNextScroll) {
|
|
router.resetNextScroll = true;
|
|
return;
|
|
}
|
|
if (typeof router.options.scrollRestoration === "function" && !router.options.scrollRestoration({ location: router.latestLocation })) return;
|
|
ignoreScroll = true;
|
|
try {
|
|
const elementEntries = router.isScrollRestoring ? cache.state[cacheKey] : void 0;
|
|
let restored = false;
|
|
if (elementEntries) for (const elementSelector in elementEntries) {
|
|
const entry = elementEntries[elementSelector];
|
|
if (!require_utils.isPlainObject(entry)) continue;
|
|
const { scrollX, scrollY } = entry;
|
|
if (!Number.isFinite(scrollX) || !Number.isFinite(scrollY)) continue;
|
|
if (elementSelector === windowScrollTarget) {
|
|
window.scrollTo({
|
|
top: scrollY,
|
|
left: scrollX,
|
|
behavior
|
|
});
|
|
restored = true;
|
|
} else if (elementSelector) {
|
|
let element;
|
|
try {
|
|
element = document.querySelector(elementSelector);
|
|
} catch {
|
|
continue;
|
|
}
|
|
if (element) {
|
|
element.scrollLeft = scrollX;
|
|
element.scrollTop = scrollY;
|
|
restored = true;
|
|
}
|
|
}
|
|
}
|
|
if (!restored) {
|
|
const hash = router.history.location.hash.slice(1);
|
|
if (hash) {
|
|
const hashScrollIntoViewOptions = window.history.state?.__hashScrollIntoViewOptions ?? true;
|
|
if (hashScrollIntoViewOptions) {
|
|
const el = document.getElementById(hash);
|
|
if (el) el.scrollIntoView(hashScrollIntoViewOptions);
|
|
}
|
|
} else {
|
|
const scrollOptions = {
|
|
top: 0,
|
|
left: 0,
|
|
behavior
|
|
};
|
|
window.scrollTo(scrollOptions);
|
|
if (scrollToTopSelectors) for (const selector of scrollToTopSelectors) {
|
|
if (selector === windowScrollTarget) continue;
|
|
const element = typeof selector === "function" ? selector() : document.querySelector(selector);
|
|
if (element) element.scrollTo(scrollOptions);
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
ignoreScroll = false;
|
|
}
|
|
if (router.isScrollRestoring) cache.set((state) => {
|
|
state[cacheKey] ||= {};
|
|
return state;
|
|
});
|
|
});
|
|
}
|
|
//#endregion
|
|
exports.defaultGetScrollRestorationKey = defaultGetScrollRestorationKey;
|
|
exports.getElementScrollRestorationEntry = getElementScrollRestorationEntry;
|
|
exports.scrollRestorationCache = scrollRestorationCache;
|
|
exports.setupScrollRestoration = setupScrollRestoration;
|
|
exports.storageKey = storageKey;
|
|
|
|
//# sourceMappingURL=scroll-restoration.cjs.map |