fix(nav): keep active menu state synced during client-side navigation
This commit is contained in:
@@ -15,12 +15,52 @@ export default function GlassHeader({ lang: propLang }: GlassHeaderProps) {
|
|||||||
const [lang, setLang] = useState<Lang>(propLang || defaultLang);
|
const [lang, setLang] = useState<Lang>(propLang || defaultLang);
|
||||||
const [currentPath, setCurrentPath] = useState("");
|
const [currentPath, setCurrentPath] = useState("");
|
||||||
|
|
||||||
|
const normalizePath = (path: string) => {
|
||||||
|
const clean = path.split("#")[0].split("?")[0];
|
||||||
|
if (!clean) return "/";
|
||||||
|
if (clean === "/") return "/";
|
||||||
|
return clean.endsWith("/") ? clean.slice(0, -1) : clean;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentPath(window.location.pathname);
|
const updatePath = () => setCurrentPath(normalizePath(window.location.pathname));
|
||||||
const htmlLang = document.documentElement.lang as Lang;
|
updatePath();
|
||||||
if (htmlLang && (!propLang || htmlLang !== lang)) {
|
|
||||||
setLang(htmlLang);
|
const syncLang = () => {
|
||||||
}
|
const htmlLang = document.documentElement.lang as Lang;
|
||||||
|
if (htmlLang && (!propLang || htmlLang !== lang)) {
|
||||||
|
setLang(htmlLang);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
syncLang();
|
||||||
|
|
||||||
|
const originalPushState = history.pushState.bind(history);
|
||||||
|
const originalReplaceState = history.replaceState.bind(history);
|
||||||
|
|
||||||
|
history.pushState = function (...args) {
|
||||||
|
originalPushState(...args);
|
||||||
|
window.dispatchEvent(new Event("locationchange"));
|
||||||
|
};
|
||||||
|
history.replaceState = function (...args) {
|
||||||
|
originalReplaceState(...args);
|
||||||
|
window.dispatchEvent(new Event("locationchange"));
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("popstate", updatePath);
|
||||||
|
window.addEventListener("locationchange", updatePath);
|
||||||
|
document.addEventListener("astro:after-swap", updatePath as EventListener);
|
||||||
|
document.addEventListener("astro:page-load", updatePath as EventListener);
|
||||||
|
document.addEventListener("astro:page-load", syncLang as EventListener);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
history.pushState = originalPushState;
|
||||||
|
history.replaceState = originalReplaceState;
|
||||||
|
window.removeEventListener("popstate", updatePath);
|
||||||
|
window.removeEventListener("locationchange", updatePath);
|
||||||
|
document.removeEventListener("astro:after-swap", updatePath as EventListener);
|
||||||
|
document.removeEventListener("astro:page-load", updatePath as EventListener);
|
||||||
|
document.removeEventListener("astro:page-load", syncLang as EventListener);
|
||||||
|
};
|
||||||
}, [propLang, lang]);
|
}, [propLang, lang]);
|
||||||
|
|
||||||
const t = useTranslations(lang);
|
const t = useTranslations(lang);
|
||||||
@@ -48,10 +88,13 @@ export default function GlassHeader({ lang: propLang }: GlassHeaderProps) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const isActive = (path: string) => {
|
const isActive = (path: string) => {
|
||||||
if (path === '/' || path === '/zh' || path === '/zh/') {
|
const normalizedPath = normalizePath(path);
|
||||||
return currentPath === path;
|
|
||||||
|
if (normalizedPath === '/' || normalizedPath === '/zh') {
|
||||||
|
return currentPath === normalizedPath;
|
||||||
}
|
}
|
||||||
return currentPath.startsWith(path);
|
|
||||||
|
return currentPath === normalizedPath || currentPath.startsWith(`${normalizedPath}/`);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user