shelving
Version:
Toolkit for using data in JavaScript.
49 lines (48 loc) • 2.46 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import { useEffect } from "react";
import { useInstance } from "../../react/useInstance.js";
import { useStore } from "../../react/useStore.js";
import { MetaContext, requireMeta } from "../misc/MetaContext.js";
import { NavigationContext } from "./NavigationContext.js";
import { NavigationStore } from "./NavigationStore.js";
/**
* Top-level navigation provider.
* - Owns a single `NavigationStore` initialised from the surrounding `<Meta>` url/base.
* - Intercepts same-origin anchor clicks (excluding `download` anchors) and turns them into `forward()` calls.
* - Listens for `popstate` to sync the store with browser back/forward.
* - Publishes the live URL via `<Meta url={…} params={…}>` so descendant `<Router>`s re-render on navigation.
*
* Exactly one `<Navigation>` per app — nested routers share this single store.
*
* TODO: switch click/popstate handling to the browser Navigation API when broadly supported.
*/
export function Navigation({ children, ...meta }) {
const { url, root, ...merged } = requireMeta(meta);
const nav = useInstance(NavigationStore, url, root);
useStore(nav);
useEffect(() => {
if (typeof document === "undefined" || typeof window === "undefined")
return;
const onClick = (e) => {
if (e.target instanceof Element) {
const anchor = e.target.closest("a") ||
(!e.target.closest("button, label") && e.target.closest(".targeted")?.querySelector("a.target[href]"));
if (anchor instanceof HTMLAnchorElement && anchor.origin === window.location.origin && !anchor.hasAttribute("download")) {
e.preventDefault();
nav.forward(anchor.href);
return false; // `return false` stops iOS web app opening every link in a new window.
}
}
};
const onPopState = () => {
nav.value = window.location.href;
};
document.addEventListener("click", onClick);
window.addEventListener("popstate", onPopState);
return () => {
document.removeEventListener("click", onClick);
window.removeEventListener("popstate", onPopState);
};
}, [nav]);
return (_jsx(NavigationContext, { value: nav, children: _jsx(MetaContext, { value: { url: nav.value, root, ...merged }, children: children }) }));
}