@tanstack/react-router
Advanced tools
Comparing version
@@ -33,4 +33,3 @@ export { default as invariant } from 'tiny-invariant'; | ||
export type { RouterProps, CommitLocationOptions, MatchLocation, NavigateFn, BuildLocationFn, } from './RouterProvider.js'; | ||
export { useScrollRestoration, useElementScrollRestoration, ScrollRestoration, } from './scroll-restoration.js'; | ||
export type { ScrollRestorationOptions } from './scroll-restoration.js'; | ||
export { useElementScrollRestoration, ScrollRestoration, } from './ScrollRestoration.js'; | ||
export type { UseBlockerOpts, ShouldBlockFn } from './useBlocker.js'; | ||
@@ -37,0 +36,0 @@ export { useBlocker, Block } from './useBlocker.js'; |
@@ -20,3 +20,3 @@ import { default as default2 } from "tiny-invariant"; | ||
import { RouterContextProvider, RouterProvider } from "./RouterProvider.js"; | ||
import { ScrollRestoration, useElementScrollRestoration, useScrollRestoration } from "./scroll-restoration.js"; | ||
import { ScrollRestoration, useElementScrollRestoration } from "./ScrollRestoration.js"; | ||
import { Block, useBlocker } from "./useBlocker.js"; | ||
@@ -141,3 +141,2 @@ import { Navigate, useNavigate } from "./useNavigate.js"; | ||
useRouterState, | ||
useScrollRestoration, | ||
useSearch, | ||
@@ -144,0 +143,0 @@ useStableCallback, |
"use client"; | ||
import { jsx } from "react/jsx-runtime"; | ||
import { jsxs, Fragment, jsx } from "react/jsx-runtime"; | ||
import * as React from "react"; | ||
import invariant from "tiny-invariant"; | ||
import warning from "tiny-warning"; | ||
import { pick, createControlledPromise, rootRouteId } from "@tanstack/router-core"; | ||
import { rootRouteId, getLocationChangeInfo, pick, createControlledPromise } from "@tanstack/router-core"; | ||
import { ErrorComponent, CatchBoundary } from "./CatchBoundary.js"; | ||
@@ -15,2 +15,3 @@ import { useRouterState } from "./useRouterState.js"; | ||
import { renderRouteNotFound } from "./renderRouteNotFound.js"; | ||
import { ScrollRestoration } from "./scroll-restoration.js"; | ||
const Match = React.memo(function MatchImpl({ | ||
@@ -49,26 +50,63 @@ matchId | ||
}); | ||
return /* @__PURE__ */ jsx(matchContext.Provider, { value: matchId, children: /* @__PURE__ */ jsx(ResolvedSuspenseBoundary, { fallback: pendingElement, children: /* @__PURE__ */ jsx( | ||
ResolvedCatchBoundary, | ||
const parentRouteId = useRouterState({ | ||
select: (s) => { | ||
var _a2; | ||
const index = s.matches.findIndex((d) => d.id === matchId); | ||
return (_a2 = s.matches[index - 1]) == null ? void 0 : _a2.routeId; | ||
} | ||
}); | ||
return /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
/* @__PURE__ */ jsx(matchContext.Provider, { value: matchId, children: /* @__PURE__ */ jsx(ResolvedSuspenseBoundary, { fallback: pendingElement, children: /* @__PURE__ */ jsx( | ||
ResolvedCatchBoundary, | ||
{ | ||
getResetKey: () => resetKey, | ||
errorComponent: routeErrorComponent || ErrorComponent, | ||
onCatch: (error, errorInfo) => { | ||
if (isNotFound(error)) throw error; | ||
warning(false, `Error in route match: ${matchId}`); | ||
routeOnCatch == null ? void 0 : routeOnCatch(error, errorInfo); | ||
}, | ||
children: /* @__PURE__ */ jsx( | ||
ResolvedNotFoundBoundary, | ||
{ | ||
fallback: (error) => { | ||
if (!routeNotFoundComponent || error.routeId && error.routeId !== routeId || !error.routeId && !route.isRoot) | ||
throw error; | ||
return React.createElement(routeNotFoundComponent, error); | ||
}, | ||
children: /* @__PURE__ */ jsx(MatchInner, { matchId }) | ||
} | ||
) | ||
} | ||
) }) }), | ||
parentRouteId === rootRouteId ? /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
/* @__PURE__ */ jsx(OnRendered, {}), | ||
/* @__PURE__ */ jsx(ScrollRestoration, {}) | ||
] }) : null | ||
] }); | ||
}); | ||
function OnRendered() { | ||
var _a; | ||
const router = useRouter(); | ||
const prevLocationRef = React.useRef( | ||
void 0 | ||
); | ||
return /* @__PURE__ */ jsx( | ||
"script", | ||
{ | ||
getResetKey: () => resetKey, | ||
errorComponent: routeErrorComponent || ErrorComponent, | ||
onCatch: (error, errorInfo) => { | ||
if (isNotFound(error)) throw error; | ||
warning(false, `Error in route match: ${matchId}`); | ||
routeOnCatch == null ? void 0 : routeOnCatch(error, errorInfo); | ||
}, | ||
children: /* @__PURE__ */ jsx( | ||
ResolvedNotFoundBoundary, | ||
{ | ||
fallback: (error) => { | ||
if (!routeNotFoundComponent || error.routeId && error.routeId !== routeId || !error.routeId && !route.isRoot) | ||
throw error; | ||
return React.createElement(routeNotFoundComponent, error); | ||
}, | ||
children: /* @__PURE__ */ jsx(MatchInner, { matchId }) | ||
suppressHydrationWarning: true, | ||
ref: (el) => { | ||
if (el) { | ||
router.emit({ | ||
type: "onRendered", | ||
...getLocationChangeInfo(router.state) | ||
}); | ||
} else { | ||
prevLocationRef.current = router.state.resolvedLocation; | ||
} | ||
) | ||
} | ||
) }) }); | ||
}); | ||
} | ||
}, | ||
(_a = router.state.resolvedLocation) == null ? void 0 : _a.state.key | ||
); | ||
} | ||
const MatchInner = React.memo(function MatchInnerImpl({ | ||
@@ -75,0 +113,0 @@ matchId |
@@ -50,3 +50,6 @@ import { jsx, jsxs } from "react/jsx-runtime"; | ||
useRouterState({ | ||
select: (s) => [s.location.href, s.resolvedLocation.href, s.status], | ||
select: (s) => { | ||
var _a; | ||
return [s.location.href, (_a = s.resolvedLocation) == null ? void 0 : _a.href, s.status]; | ||
}, | ||
structuralSharing: true | ||
@@ -53,0 +56,0 @@ }); |
@@ -335,2 +335,10 @@ import { Store, NoInfer } from '@tanstack/react-store'; | ||
defaultRemountDeps?: (opts: MakeRemountDepsOptionsUnion<TRouteTree>) => any; | ||
/** | ||
* If `false`, scroll restoration will be disabled | ||
* | ||
* @default true | ||
*/ | ||
scrollRestoration?: boolean; | ||
getScrollRestorationKey?: (location: ParsedLocation) => string; | ||
scrollRestorationBehavior?: ScrollBehavior; | ||
} | ||
@@ -350,3 +358,3 @@ export interface RouterErrorSerializer<TSerializedError> { | ||
location: ParsedLocation<FullSearchSchema<TRouteTree>>; | ||
resolvedLocation: ParsedLocation<FullSearchSchema<TRouteTree>>; | ||
resolvedLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>; | ||
statusCode: number; | ||
@@ -380,38 +388,25 @@ redirect?: ResolvedRedirect; | ||
export declare const componentTypes: readonly ["component", "errorComponent", "pendingComponent", "notFoundComponent"]; | ||
type NavigationEventInfo = { | ||
fromLocation?: ParsedLocation; | ||
toLocation: ParsedLocation; | ||
pathChanged: boolean; | ||
hrefChanged: boolean; | ||
hashChanged: boolean; | ||
}; | ||
export type RouterEvents = { | ||
onBeforeNavigate: { | ||
type: 'onBeforeNavigate'; | ||
fromLocation: ParsedLocation; | ||
toLocation: ParsedLocation; | ||
pathChanged: boolean; | ||
hrefChanged: boolean; | ||
}; | ||
} & NavigationEventInfo; | ||
onBeforeLoad: { | ||
type: 'onBeforeLoad'; | ||
fromLocation: ParsedLocation; | ||
toLocation: ParsedLocation; | ||
pathChanged: boolean; | ||
hrefChanged: boolean; | ||
}; | ||
} & NavigationEventInfo; | ||
onLoad: { | ||
type: 'onLoad'; | ||
fromLocation: ParsedLocation; | ||
toLocation: ParsedLocation; | ||
pathChanged: boolean; | ||
hrefChanged: boolean; | ||
}; | ||
} & NavigationEventInfo; | ||
onResolved: { | ||
type: 'onResolved'; | ||
fromLocation: ParsedLocation; | ||
toLocation: ParsedLocation; | ||
pathChanged: boolean; | ||
hrefChanged: boolean; | ||
}; | ||
} & NavigationEventInfo; | ||
onBeforeRouteMount: { | ||
type: 'onBeforeRouteMount'; | ||
fromLocation: ParsedLocation; | ||
toLocation: ParsedLocation; | ||
pathChanged: boolean; | ||
hrefChanged: boolean; | ||
}; | ||
} & NavigationEventInfo; | ||
onInjectedHtml: { | ||
@@ -421,2 +416,5 @@ type: 'onInjectedHtml'; | ||
}; | ||
onRendered: { | ||
type: 'onRendered'; | ||
} & NavigationEventInfo; | ||
}; | ||
@@ -442,2 +440,4 @@ export type RouterEvent = RouterEvents[keyof RouterEvents]; | ||
viewTransitionPromise?: ControlledPromise<true>; | ||
isScrollRestoring: boolean; | ||
isScrollRestorationSetup: boolean; | ||
__store: Store<RouterState<TRouteTree>>; | ||
@@ -444,0 +444,0 @@ options: PickAsRequired<RouterOptions<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory, TDehydrated>, 'stringifySearch' | 'parseSearch' | 'context'>; |
@@ -19,3 +19,3 @@ import { jsx } from "react/jsx-runtime"; | ||
${jsesc(children.toString(), { quotes: "backtick" })}\`)` : "", | ||
'if (typeof __TSR__ !== "undefined") __TSR__.cleanScripts()' | ||
'if (typeof __TSR_SSR__ !== "undefined") __TSR_SSR__.cleanScripts()' | ||
].filter(Boolean).join("\n") | ||
@@ -22,0 +22,0 @@ } |
@@ -1,2 +0,13 @@ | ||
import { ParsedLocation } from '@tanstack/router-core'; | ||
import { AnyRouter } from './router.js'; | ||
import { NonNullableUpdater, ParsedLocation } from '@tanstack/router-core'; | ||
export type ScrollRestorationEntry = { | ||
scrollX: number; | ||
scrollY: number; | ||
}; | ||
export type ScrollRestorationByElement = Record<string, ScrollRestorationEntry>; | ||
export type ScrollRestorationByKey = Record<string, ScrollRestorationByElement>; | ||
export type ScrollRestorationCache = { | ||
state: ScrollRestorationByKey; | ||
set: (updater: NonNullableUpdater<ScrollRestorationByKey>) => void; | ||
}; | ||
export type ScrollRestorationOptions = { | ||
@@ -6,15 +17,14 @@ getKey?: (location: ParsedLocation) => string; | ||
}; | ||
export declare function useScrollRestoration(options?: ScrollRestorationOptions): void; | ||
export declare function ScrollRestoration(props: ScrollRestorationOptions): null; | ||
export declare function useElementScrollRestoration(options: ({ | ||
id: string; | ||
getElement?: () => Element | undefined | null; | ||
} | { | ||
id?: string; | ||
getElement: () => Element | undefined | null; | ||
}) & { | ||
getKey?: (location: ParsedLocation) => string; | ||
}): { | ||
scrollX: number; | ||
scrollY: number; | ||
} | undefined; | ||
export declare const storageKey = "tsr-scroll-restoration-v1_3"; | ||
export declare const scrollRestorationCache: ScrollRestorationCache; | ||
/** | ||
* 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. | ||
*/ | ||
export declare const defaultGetScrollRestorationKey: (location: ParsedLocation) => string; | ||
export declare function getCssSelector(el: any): string; | ||
export declare function restoreScroll(storageKey: string, key?: string, behavior?: ScrollToOptions['behavior'], shouldScrollRestoration?: boolean): void; | ||
export declare function setupScrollRestoration(router: AnyRouter, force?: boolean): void; | ||
export declare function ScrollRestoration(): import("react/jsx-runtime").JSX.Element | null; |
@@ -1,174 +0,194 @@ | ||
import * as React from "react"; | ||
import { jsx } from "react/jsx-runtime"; | ||
import { functionalUpdate } from "@tanstack/router-core"; | ||
import { useRouter } from "./useRouter.js"; | ||
const useLayoutEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect; | ||
const windowKey = "window"; | ||
const delimiter = "___"; | ||
let weakScrolledElements = /* @__PURE__ */ new WeakSet(); | ||
import { ScriptOnce } from "./ScriptOnce.js"; | ||
const storageKey = "tsr-scroll-restoration-v1_3"; | ||
const sessionsStorage = typeof window !== "undefined" && window.sessionStorage; | ||
const cache = sessionsStorage ? (() => { | ||
const storageKey = "tsr-scroll-restoration-v2"; | ||
const state = JSON.parse( | ||
window.sessionStorage.getItem(storageKey) || "null" | ||
) || { cached: {}, next: {} }; | ||
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, | ||
set: (updater) => { | ||
cache.state = functionalUpdate(updater, cache.state); | ||
window.sessionStorage.setItem(storageKey, JSON.stringify(cache.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 | ||
functionalUpdate(updater, scrollRestorationCache.state) || scrollRestorationCache.state, window.sessionStorage.setItem( | ||
storageKey, | ||
JSON.stringify(scrollRestorationCache.state) | ||
)) | ||
}; | ||
})() : void 0; | ||
const defaultGetKey = (location) => { | ||
const defaultGetScrollRestorationKey = (location) => { | ||
return location.state.key || location.href; | ||
}; | ||
function useScrollRestoration(options) { | ||
const router = useRouter(); | ||
useLayoutEffect(() => { | ||
const getKey = (options == null ? void 0 : options.getKey) || defaultGetKey; | ||
const { history } = window; | ||
history.scrollRestoration = "manual"; | ||
const onScroll = (event) => { | ||
if (weakScrolledElements.has(event.target)) return; | ||
weakScrolledElements.add(event.target); | ||
let elementSelector = ""; | ||
if (event.target === document || event.target === window) { | ||
elementSelector = windowKey; | ||
} else { | ||
const attrId = event.target.getAttribute( | ||
"data-scroll-restoration-id" | ||
); | ||
if (attrId) { | ||
elementSelector = `[data-scroll-restoration-id="${attrId}"]`; | ||
} else { | ||
elementSelector = getCssSelector(event.target); | ||
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) { | ||
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") { | ||
console.log("windowRestored", entry); | ||
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; | ||
} | ||
} | ||
} | ||
if (!cache.state.next[elementSelector]) { | ||
cache.set((c) => ({ | ||
...c, | ||
next: { | ||
...c.next, | ||
[elementSelector]: { | ||
scrollX: NaN, | ||
scrollY: NaN | ||
} | ||
} | ||
})); | ||
} | ||
}; | ||
if (typeof document !== "undefined") { | ||
document.addEventListener("scroll", onScroll, true); | ||
return; | ||
} | ||
const unsubOnBeforeLoad = router.subscribe("onBeforeLoad", (event) => { | ||
if (event.hrefChanged) { | ||
const restoreKey = getKey(event.fromLocation); | ||
for (const elementSelector in cache.state.next) { | ||
const entry = cache.state.next[elementSelector]; | ||
if (elementSelector === windowKey) { | ||
entry.scrollX = window.scrollX || 0; | ||
entry.scrollY = window.scrollY || 0; | ||
} else if (elementSelector) { | ||
const element = document.querySelector(elementSelector); | ||
entry.scrollX = (element == null ? void 0 : element.scrollLeft) || 0; | ||
entry.scrollY = (element == null ? void 0 : element.scrollTop) || 0; | ||
} | ||
cache.set((c) => { | ||
const next = { ...c.next }; | ||
delete next[elementSelector]; | ||
return { | ||
...c, | ||
next, | ||
cached: { | ||
...c.cached, | ||
[[restoreKey, elementSelector].join(delimiter)]: entry | ||
} | ||
}; | ||
}); | ||
const hash = window.location.hash.split("#")[1]; | ||
if (hash) { | ||
const hashScrollIntoViewOptions = (window.history.state || {}).__hashScrollIntoViewOptions ?? true; | ||
if (hashScrollIntoViewOptions) { | ||
console.log("scrollIntoView"); | ||
const el = document.getElementById(hash); | ||
if (el) { | ||
el.scrollIntoView(hashScrollIntoViewOptions); | ||
} | ||
} | ||
return; | ||
} | ||
window.scrollTo({ | ||
top: 0, | ||
left: 0, | ||
behavior | ||
}); | ||
const unsubOnBeforeRouteMount = router.subscribe( | ||
"onBeforeRouteMount", | ||
(event) => { | ||
if (event.hrefChanged) { | ||
if (!router.resetNextScroll) { | ||
return; | ||
} | ||
router.resetNextScroll = true; | ||
const restoreKey = getKey(event.toLocation); | ||
let windowRestored = false; | ||
for (const cacheKey in cache.state.cached) { | ||
const entry = cache.state.cached[cacheKey]; | ||
const [key, elementSelector] = cacheKey.split(delimiter); | ||
if (key === restoreKey) { | ||
if (elementSelector === windowKey) { | ||
windowRestored = true; | ||
window.scrollTo({ | ||
top: entry.scrollY, | ||
left: entry.scrollX, | ||
behavior: options == null ? void 0 : options.scrollBehavior | ||
}); | ||
} else if (elementSelector) { | ||
const element = document.querySelector(elementSelector); | ||
if (element) { | ||
element.scrollLeft = entry.scrollX; | ||
element.scrollTop = entry.scrollY; | ||
} | ||
} | ||
} | ||
} | ||
if (!windowRestored) { | ||
window.scrollTo(0, 0); | ||
} | ||
cache.set((c) => ({ ...c, next: {} })); | ||
weakScrolledElements = /* @__PURE__ */ new WeakSet(); | ||
})(); | ||
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 | ||
); | ||
return () => { | ||
document.removeEventListener("scroll", onScroll); | ||
unsubOnBeforeLoad(); | ||
unsubOnBeforeRouteMount(); | ||
}; | ||
}, [options == null ? void 0 : options.getKey, options == null ? void 0 : options.scrollBehavior, router]); | ||
if (router.isScrollRestoring) { | ||
scrollRestorationCache.set((state) => { | ||
state[cacheKey] = state[cacheKey] || {}; | ||
return state; | ||
}); | ||
} | ||
}); | ||
} | ||
function ScrollRestoration(props) { | ||
useScrollRestoration(props); | ||
return null; | ||
} | ||
function useElementScrollRestoration(options) { | ||
var _a; | ||
function ScrollRestoration() { | ||
const router = useRouter(); | ||
const getKey = options.getKey || defaultGetKey; | ||
let elementSelector = ""; | ||
if (options.id) { | ||
elementSelector = `[data-scroll-restoration-id="${options.id}"]`; | ||
} else { | ||
const element = (_a = options.getElement) == null ? void 0 : _a.call(options); | ||
if (!element) { | ||
return; | ||
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__ */ jsx( | ||
ScriptOnce, | ||
{ | ||
children: `(${restoreScroll.toString()})(${JSON.stringify(storageKey)},${JSON.stringify(resolvedKey)}, undefined, true)`, | ||
log: false | ||
} | ||
elementSelector = getCssSelector(element); | ||
} | ||
const restoreKey = getKey(router.latestLocation); | ||
const cacheKey = [restoreKey, elementSelector].join(delimiter); | ||
return cache.state.cached[cacheKey]; | ||
); | ||
} | ||
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(); | ||
} | ||
export { | ||
ScrollRestoration, | ||
useElementScrollRestoration, | ||
useScrollRestoration | ||
defaultGetScrollRestorationKey, | ||
getCssSelector, | ||
restoreScroll, | ||
scrollRestorationCache, | ||
setupScrollRestoration, | ||
storageKey | ||
}; | ||
//# sourceMappingURL=scroll-restoration.js.map |
import * as React from "react"; | ||
import { trimPathRight } from "@tanstack/router-core"; | ||
import { trimPathRight, getLocationChangeInfo } from "@tanstack/router-core"; | ||
import { usePrevious, useLayoutEffect } from "./utils.js"; | ||
@@ -64,13 +64,6 @@ import { useRouter } from "./useRouter.js"; | ||
if (previousIsLoading && !isLoading) { | ||
const toLocation = router.state.location; | ||
const fromLocation = router.state.resolvedLocation; | ||
const pathChanged = fromLocation.pathname !== toLocation.pathname; | ||
const hrefChanged = fromLocation.href !== toLocation.href; | ||
router.emit({ | ||
type: "onLoad", | ||
// When the new URL has committed, when the new matches have been loaded into state.matches | ||
fromLocation, | ||
toLocation, | ||
pathChanged, | ||
hrefChanged | ||
...getLocationChangeInfo(router.state) | ||
}); | ||
@@ -81,12 +74,5 @@ } | ||
if (previousIsPagePending && !isPagePending) { | ||
const toLocation = router.state.location; | ||
const fromLocation = router.state.resolvedLocation; | ||
const pathChanged = fromLocation.pathname !== toLocation.pathname; | ||
const hrefChanged = fromLocation.href !== toLocation.href; | ||
router.emit({ | ||
type: "onBeforeRouteMount", | ||
fromLocation, | ||
toLocation, | ||
pathChanged, | ||
hrefChanged | ||
...getLocationChangeInfo(router.state) | ||
}); | ||
@@ -97,12 +83,5 @@ } | ||
if (previousIsAnyPending && !isAnyPending) { | ||
const toLocation = router.state.location; | ||
const fromLocation = router.state.resolvedLocation; | ||
const pathChanged = fromLocation.pathname !== toLocation.pathname; | ||
const hrefChanged = fromLocation.href !== toLocation.href; | ||
router.emit({ | ||
type: "onResolved", | ||
fromLocation, | ||
toLocation, | ||
pathChanged, | ||
hrefChanged | ||
...getLocationChangeInfo(router.state) | ||
}); | ||
@@ -114,11 +93,2 @@ router.__store.setState((s) => ({ | ||
})); | ||
if (typeof document !== "undefined" && document.querySelector) { | ||
const hashScrollIntoViewOptions = router.state.location.state.__hashScrollIntoViewOptions ?? true; | ||
if (hashScrollIntoViewOptions && router.state.location.hash !== "") { | ||
const el = document.getElementById(router.state.location.hash); | ||
if (el) { | ||
el.scrollIntoView(hashScrollIntoViewOptions); | ||
} | ||
} | ||
} | ||
} | ||
@@ -125,0 +95,0 @@ }, [isAnyPending, previousIsAnyPending, router]); |
{ | ||
"name": "@tanstack/react-router", | ||
"version": "1.98.0", | ||
"version": "1.98.1", | ||
"description": "Modern and scalable routing for React applications", | ||
@@ -56,4 +56,4 @@ "author": "Tanner Linsley", | ||
"tiny-warning": "^1.0.3", | ||
"@tanstack/history": "1.98.0", | ||
"@tanstack/router-core": "^1.98.0" | ||
"@tanstack/history": "1.98.1", | ||
"@tanstack/router-core": "^1.98.1" | ||
}, | ||
@@ -60,0 +60,0 @@ "devDependencies": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1494049
1.45%253
2.85%18582
1.49%8
33.33%+ Added
- Removed
Updated