@tanstack/react-router
Version:
Modern and scalable routing for React applications
206 lines (205 loc) • 7 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const jsxRuntime = require("react/jsx-runtime");
const routerCore = require("@tanstack/router-core");
const useRouter = require("./useRouter.cjs");
const ScriptOnce = require("./ScriptOnce.cjs");
const storageKey = "tsr-scroll-restoration-v1_3";
let sessionsStorage = false;
try {
sessionsStorage = typeof window !== "undefined" && typeof window.sessionStorage === "object";
} catch {
}
const throttle = (fn, wait) => {
let timeout;
return (...args) => {
if (!timeout) {
timeout = setTimeout(() => {
fn(...args);
timeout = null;
}, wait);
}
};
};
const scrollRestorationCache = sessionsStorage ? (() => {
const state = JSON.parse(window.sessionStorage.getItem(storageKey) || "null") || {};
return {
state,
// This setter is simply to make sure that we set the sessionStorage right
// after the state is updated. It doesn't necessarily need to be a functional
// update.
set: (updater) => (scrollRestorationCache.state = // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
routerCore.functionalUpdate(updater, scrollRestorationCache.state) || scrollRestorationCache.state, window.sessionStorage.setItem(
storageKey,
JSON.stringify(scrollRestorationCache.state)
))
};
})() : void 0;
const defaultGetScrollRestorationKey = (location) => {
return location.state.key || location.href;
};
function getCssSelector(el) {
const path = [];
let parent;
while (parent = el.parentNode) {
path.unshift(
`${el.tagName}:nth-child(${[].indexOf.call(parent.children, el) + 1})`
);
el = parent;
}
return `${path.join(" > ")}`.toLowerCase();
}
let ignoreScroll = false;
function restoreScroll(storageKey2, key, behavior, shouldScrollRestoration, scrollToTopSelectors) {
var _a;
let byKey;
try {
byKey = JSON.parse(sessionStorage.getItem(storageKey2) || "{}");
} catch (error) {
console.error(error);
return;
}
const resolvedKey = key || ((_a = window.history.state) == null ? void 0 : _a.key);
const elementEntries = byKey[resolvedKey];
ignoreScroll = true;
(() => {
if (shouldScrollRestoration && elementEntries) {
for (const elementSelector in elementEntries) {
const entry = elementEntries[elementSelector];
if (elementSelector === "window") {
window.scrollTo({
top: entry.scrollY,
left: entry.scrollX,
behavior
});
} else if (elementSelector) {
const element = document.querySelector(elementSelector);
if (element) {
element.scrollLeft = entry.scrollX;
element.scrollTop = entry.scrollY;
}
}
}
return;
}
const hash = window.location.hash.split("#")[1];
if (hash) {
const hashScrollIntoViewOptions = (window.history.state || {}).__hashScrollIntoViewOptions ?? true;
if (hashScrollIntoViewOptions) {
const el = document.getElementById(hash);
if (el) {
el.scrollIntoView(hashScrollIntoViewOptions);
}
}
return;
}
[
"window",
...(scrollToTopSelectors == null ? void 0 : scrollToTopSelectors.filter((d) => d !== "window")) ?? []
].forEach((selector) => {
const element = selector === "window" ? window : document.querySelector(selector);
if (element) {
element.scrollTo({
top: 0,
left: 0,
behavior
});
}
});
})();
ignoreScroll = false;
}
function setupScrollRestoration(router, force) {
const shouldScrollRestoration = force ?? router.options.scrollRestoration ?? false;
if (shouldScrollRestoration) {
router.isScrollRestoring = true;
}
if (typeof document === "undefined" || router.isScrollRestorationSetup) {
return;
}
router.isScrollRestorationSetup = true;
ignoreScroll = false;
const getKey = router.options.getScrollRestorationKey || defaultGetScrollRestorationKey;
window.history.scrollRestoration = "manual";
const onScroll = (event) => {
if (ignoreScroll || !router.isScrollRestoring) {
return;
}
let elementSelector = "";
if (event.target === document || event.target === window) {
elementSelector = "window";
} else {
const attrId = event.target.getAttribute(
"data-scroll-restoration-id"
);
if (attrId) {
elementSelector = `[data-scroll-restoration-id="${attrId}"]`;
} else {
elementSelector = getCssSelector(event.target);
}
}
const restoreKey = getKey(router.state.location);
scrollRestorationCache.set((state) => {
const keyEntry = state[restoreKey] = state[restoreKey] || {};
const elementEntry = keyEntry[elementSelector] = keyEntry[elementSelector] || {};
if (elementSelector === "window") {
elementEntry.scrollX = window.scrollX || 0;
elementEntry.scrollY = window.scrollY || 0;
} else if (elementSelector) {
const element = document.querySelector(elementSelector);
if (element) {
elementEntry.scrollX = element.scrollLeft || 0;
elementEntry.scrollY = element.scrollTop || 0;
}
}
return state;
});
};
if (typeof document !== "undefined") {
document.addEventListener("scroll", throttle(onScroll, 100), true);
}
router.subscribe("onRendered", (event) => {
const cacheKey = getKey(event.toLocation);
if (!router.resetNextScroll) {
router.resetNextScroll = true;
return;
}
restoreScroll(
storageKey,
cacheKey,
router.options.scrollRestorationBehavior,
router.isScrollRestoring,
router.options.scrollToTopSelectors
);
if (router.isScrollRestoring) {
scrollRestorationCache.set((state) => {
state[cacheKey] = state[cacheKey] || {};
return state;
});
}
});
}
function ScrollRestoration() {
const router = useRouter.useRouter();
const getKey = router.options.getScrollRestorationKey || defaultGetScrollRestorationKey;
const userKey = getKey(router.latestLocation);
const resolvedKey = userKey !== defaultGetScrollRestorationKey(router.latestLocation) ? userKey : null;
if (!router.isScrollRestoring || !router.isServer) {
return null;
}
return /* @__PURE__ */ jsxRuntime.jsx(
ScriptOnce.ScriptOnce,
{
children: `(${restoreScroll.toString()})(${JSON.stringify(storageKey)},${JSON.stringify(resolvedKey)}, undefined, true)`,
log: false
}
);
}
exports.ScrollRestoration = ScrollRestoration;
exports.defaultGetScrollRestorationKey = defaultGetScrollRestorationKey;
exports.getCssSelector = getCssSelector;
exports.restoreScroll = restoreScroll;
exports.scrollRestorationCache = scrollRestorationCache;
exports.setupScrollRestoration = setupScrollRestoration;
exports.storageKey = storageKey;
//# sourceMappingURL=scroll-restoration.cjs.map