xtap-pool / explorer /src /lib /router.ts
osolmaz's picture
osolmaz HF Staff
deploy: eaa4b86
879c715 verified
Raw
History Blame Contribute Delete
1.82 kB
import { useEffect, useState } from "react";
/** Parsed explorer location: the feed shell, the graph index, or one free label. */
export type Route = { kind: "home" } | { kind: "graph" } | { kind: "free-label"; slug: string };
/** Map a pathname onto an explorer route; unknown paths fall back to the feed. */
export function parseRoute(pathname: string): Route {
const segments = pathname.split("/").filter((segment) => segment !== "");
if (segments[0] !== "graph") return { kind: "home" };
const slug = segments[1];
if (slug === undefined) return { kind: "graph" };
try {
return { kind: "free-label", slug: decodeURIComponent(slug) };
} catch {
return { kind: "graph" };
}
}
/** SPA navigation: push the path and let useRoute listeners re-render. */
export function navigate(path: string): void {
window.history.pushState(null, "", path);
window.dispatchEvent(new PopStateEvent("popstate"));
}
/** Clicks the SPA should handle itself (primary button, no modifier keys). */
export function isPlainLeftClick(event: {
defaultPrevented: boolean;
button: number;
metaKey: boolean;
ctrlKey: boolean;
shiftKey: boolean;
altKey: boolean;
}): boolean {
if (event.defaultPrevented || event.button !== 0) return false;
return !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;
}
/** Current route, updated on navigate() pushes and browser back/forward. */
export function useRoute(): Route {
const [pathname, setPathname] = useState(() => window.location.pathname);
useEffect(() => {
const onPopState = (): void => {
setPathname(window.location.pathname);
};
window.addEventListener("popstate", onPopState);
return (): void => {
window.removeEventListener("popstate", onPopState);
};
}, []);
return parseRoute(pathname);
}