UNPKG

one

Version:

One is a React framework for web, iOS, and Android apps.

190 lines (187 loc) • 5.48 kB
import { useIsFocused } from "@react-navigation/core"; import { useContext, useEffect, useLayoutEffect } from "react"; import { RouteInfoContext } from "../router/RouteInfoContext.mjs"; import { setLastAction } from "../router/lastAction.mjs"; import { routeInfo, subscribeToLoadingState, subscribeToRootState } from "../router/router.mjs"; const KEY = "one-sr"; const GROUP_KEY = "one-sr-groups"; const getState = () => JSON.parse(sessionStorage.getItem(KEY) || "{}"); const getGroupState = () => JSON.parse(sessionStorage.getItem(GROUP_KEY) || "{}"); let activeGroups = /* @__PURE__ */ new Set(); function registerScrollGroup(groupId) { activeGroups.add(groupId); return () => { activeGroups.delete(groupId); }; } function getGroupKey(pathname) { let longestMatch = null; for (const group of activeGroups) { if ((pathname === group || pathname.startsWith(`${group}/`)) && (!longestMatch || group.length > longestMatch.length)) { longestMatch = group; } } return longestMatch; } function restorePosition(pathname) { try { const positions = getState(); const saved = positions[pathname]; if (typeof saved === "number") { setTimeout(() => { window.scrollTo(0, saved); }); } } catch (error) { console.error(`Error restoring scroll position`, error); sessionStorage.removeItem(KEY); } } function restoreGroupPosition(groupId) { try { const positions = getGroupState(); const saved = positions[groupId]; if (typeof saved === "number") { setTimeout(() => { window.scrollTo(0, saved); }); } } catch (error) { console.error(`Error restoring scroll position for group ${groupId}`, error); sessionStorage.removeItem(GROUP_KEY); } } let didPop = false; let previousPathname = null; function rememberScrollPosition() { didPop = false; const pathname = window.location.pathname; const state = getState(); state[pathname] = window.scrollY; sessionStorage.setItem(KEY, JSON.stringify(state)); const groupKey = getGroupKey(pathname); if (groupKey) { const groupState = getGroupState(); groupState[groupKey] = window.scrollY; sessionStorage.setItem(GROUP_KEY, JSON.stringify(groupState)); } previousPathname = pathname; } let disable = null; let pendingScroll = null; let committedHref; function ScrollBehaviorRouteCommit() { const info = useContext(RouteInfoContext); const href = info?.unstable_globalHref; const focused = useIsFocused(); useLayoutEffect(() => { if (!focused) return; committedHref = href; if (pendingScroll && pendingScroll.href === href) { const { apply } = pendingScroll; pendingScroll = null; apply(); } }); return null; } function configure(props) { if (typeof window === "undefined" || !window.addEventListener) { return; } disable?.(); const initialLocation = `${window.location.pathname}${window.location.search}`; let isFirstStateChange = true; let handledLocation = initialLocation; previousPathname = window.location.pathname; const popStateController = new AbortController(); window.addEventListener("popstate", () => { didPop = true; setLastAction(); }, { signal: popStateController.signal }); const disposeOnLoadState = subscribeToLoadingState((state) => { if (state === "loading") { rememberScrollPosition(); } }); const disposeOnRootState = subscribeToRootState((state) => { const currentHref = routeInfo?.unstable_globalHref ?? ""; const currentPathname = routeInfo?.pathname ?? ""; if (isFirstStateChange) { isFirstStateChange = false; if (currentHref === initialLocation) { return; } } const { hash } = state; const location = `${currentHref}${hash || ""}`; if (location === handledLocation) { pendingScroll = null; return; } pendingScroll = null; if (state.linkOptions?.scroll === false) { handledLocation = location; previousPathname = currentPathname; return; } const restoring = didPop; const prevGroup = previousPathname ? getGroupKey(previousPathname) : null; const currentGroup = getGroupKey(currentPathname); const scrollGroup = state.linkOptions?.scrollGroup; pendingScroll = { href: currentHref, apply: () => { handledLocation = location; previousPathname = currentPathname; if (hash) { scrollToHash(hash); } else if (restoring) { if (props.disable !== "restore") { restorePosition(currentPathname); } } else if (prevGroup && currentGroup && prevGroup === currentGroup) { restoreGroupPosition(currentGroup); } else if (scrollGroup) { restoreGroupPosition(scrollGroup); } else { window.scrollTo(0, 0); } } }; if (committedHref === currentHref) { const { apply } = pendingScroll; pendingScroll = null; apply(); } }); disable = () => { pendingScroll = null; popStateController.abort(); disposeOnLoadState(); disposeOnRootState(); }; return disable; } function scrollToHash(hash) { if (!hash || !hash.startsWith("#")) return; const id = hash.slice(1); const el = document.getElementById(id); if (!el) return; el.scrollIntoView({ behavior: "instant" }); } function ScrollBehavior(props) { if (process.env.VITE_ENVIRONMENT === "client") { useEffect(() => { if (window.location.hash) { scrollToHash(window.location.hash); } }, []); useEffect(() => { return configure(props); }, [props.disable]); } return null; } export { ScrollBehavior, ScrollBehaviorRouteCommit, registerScrollGroup }; //# sourceMappingURL=ScrollBehavior.mjs.map