Files
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

65 lines
2.4 KiB
TypeScript

import { getTracker } from './memoryHistoryTracker';
/**
* 按候选顺序在历史栈里查找第一个匹配的 pathname,并 popTo 到它。
* 常用于"Up 导航"语义——一个子页可能从多个父页进入,按声明顺序尝试落到最合适的父页。
*
* inclusive=false(默认):保留目标条目,落点即为该条目;
* inclusive=true:连同目标条目一起 pop,落点为其下方一条。
*
* 返回是否成功 popTo 了某个候选;返回 false 时调用方通常 fallback 到单步 `back()`。
*/
export function memoryHistoryPopToFirstMatch(
navigator: unknown,
pathnames: readonly string[],
options?: { inclusive?: boolean },
): boolean {
for (const pathname of pathnames) {
const mem = navigator as { go?: (delta: number) => void } | null;
if (!mem || typeof mem.go !== 'function') return false;
const tracker = getTracker(mem as object);
if (!tracker) return false;
const delta = tracker.findPopToDelta(pathname, options?.inclusive ?? false);
if (delta > 0) {
memoryHistoryPopTo(navigator, pathname, options);
return true;
}
}
return false;
}
/**
* Pop back through the MemoryRouter history to a target pathname.
*
* Uses the shadow HistoryTracker (synced via useEffect in useAppNavigationHandler)
* to search backwards through the stack, then calls `navigator.go(-delta)`.
*
* The caller is expected to call `navigate(targetUrl)` AFTER this function returns,
* which will push/replace at the new position. MemoryHistory's `push()` automatically
* trims forward entries, so stale entries are cleaned up on the next push.
*
* - `popTo` containing '?' matches full path+search
* - otherwise matches pathname only
*/
export function memoryHistoryPopTo(
navigator: unknown,
popTo: string,
options?: { inclusive?: boolean },
): void {
const mem = navigator as { go?: (delta: number) => void } | null;
if (!mem || typeof mem.go !== 'function') return;
const tracker = getTracker(mem as object);
if (!tracker) return;
const delta = tracker.findPopToDelta(popTo, options?.inclusive ?? false);
if (delta <= 0) return;
mem.go(-delta);
// Keep the shadow tracker in sync immediately — the React-level sync
// will also fire after re-render, but the caller may call navigate()
// (which triggers findPopToDelta again) before the next render cycle.
tracker.index = Math.max(0, tracker.index - delta);
tracker.stack.length = tracker.index + 1;
}