fix(nav): keep active menu state synced during client-side navigation
This commit is contained in:
@@ -14,13 +14,53 @@ import { cn } from "@/lib/utils";
|
||||
export default function GlassHeader({ lang: propLang }: GlassHeaderProps) {
|
||||
const [lang, setLang] = useState<Lang>(propLang || defaultLang);
|
||||
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(() => {
|
||||
setCurrentPath(window.location.pathname);
|
||||
const htmlLang = document.documentElement.lang as Lang;
|
||||
if (htmlLang && (!propLang || htmlLang !== lang)) {
|
||||
setLang(htmlLang);
|
||||
}
|
||||
const updatePath = () => setCurrentPath(normalizePath(window.location.pathname));
|
||||
updatePath();
|
||||
|
||||
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]);
|
||||
|
||||
const t = useTranslations(lang);
|
||||
@@ -48,10 +88,13 @@ export default function GlassHeader({ lang: propLang }: GlassHeaderProps) {
|
||||
];
|
||||
|
||||
const isActive = (path: string) => {
|
||||
if (path === '/' || path === '/zh' || path === '/zh/') {
|
||||
return currentPath === path;
|
||||
const normalizedPath = normalizePath(path);
|
||||
|
||||
if (normalizedPath === '/' || normalizedPath === '/zh') {
|
||||
return currentPath === normalizedPath;
|
||||
}
|
||||
return currentPath.startsWith(path);
|
||||
|
||||
return currentPath === normalizedPath || currentPath.startsWith(`${normalizedPath}/`);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user