Drop the H0M3 tile hub and the core page. The dashboard is now the index; kept state is its fifth tab. A pages menu in the chrome links flow, stats, API docs and the swarm UI. The swarm apex URL comes from services.hyperhive.swarm.domain via HIVE_SWARM_PUBLIC_URL and the new swarm_public_url field of the state snapshot.
99 lines
2.9 KiB
JavaScript
99 lines
2.9 KiB
JavaScript
// Pages menu in the dashboard chrome: one button that opens a popover
|
|
// linking the standalone pages (flow, stats, API docs) and the swarm UI.
|
|
// Modelled on the swarm UI's `LinksMenu`. The swarm entry only exists
|
|
// when `/api/state` carries `swarm_public_url`, so a hive without a
|
|
// configured swarm domain shows no dead link.
|
|
|
|
import { $ } from "./common.js";
|
|
import { el } from "@hive/shared/dom.js";
|
|
|
|
/** Menu entries for a state snapshot. `external` opens in a new tab. */
|
|
export function menuLinks(state) {
|
|
const links = [
|
|
{ icon: "📡", label: "flow", url: "/flow.html" },
|
|
{ icon: "📊", label: "stats", url: "/stats.html" },
|
|
{ icon: "🧬", label: "api docs", url: "/api/docs/" },
|
|
];
|
|
if (state && state.swarm_public_url) {
|
|
links.push({
|
|
icon: "🐝",
|
|
label: "swarm",
|
|
url: state.swarm_public_url,
|
|
external: true,
|
|
});
|
|
}
|
|
return links;
|
|
}
|
|
|
|
let wired = false;
|
|
|
|
function closeMenu(root) {
|
|
const pop = root.querySelector(".links-menu-popover");
|
|
const btn = root.querySelector(".links-menu-button");
|
|
if (pop) pop.hidden = true;
|
|
if (btn) btn.setAttribute("aria-expanded", "false");
|
|
}
|
|
|
|
function wire(root) {
|
|
if (wired) return;
|
|
wired = true;
|
|
root.querySelector(".links-menu-button").addEventListener("click", () => {
|
|
const pop = root.querySelector(".links-menu-popover");
|
|
const btn = root.querySelector(".links-menu-button");
|
|
pop.hidden = !pop.hidden;
|
|
btn.setAttribute("aria-expanded", String(!pop.hidden));
|
|
});
|
|
// Outside click / Escape close the popover.
|
|
document.addEventListener("pointerdown", (e) => {
|
|
if (!root.contains(e.target)) closeMenu(root);
|
|
});
|
|
document.addEventListener("keydown", (e) => {
|
|
if (e.key === "Escape") closeMenu(root);
|
|
});
|
|
}
|
|
|
|
/** (Re)render the menu into `#links-menu`. Safe to call on every state
|
|
* refresh: the open/closed state lives on the popover element, which is
|
|
* kept; only its items are rebuilt. */
|
|
export function renderLinksMenu(state) {
|
|
const root = $("links-menu");
|
|
if (!root) return;
|
|
if (!root.firstChild) {
|
|
root.append(
|
|
el(
|
|
"button",
|
|
{
|
|
type: "button",
|
|
class: "links-menu-button",
|
|
"aria-haspopup": "true",
|
|
"aria-expanded": "false",
|
|
"aria-label": "pages",
|
|
title: "pages",
|
|
},
|
|
"☰",
|
|
),
|
|
el("div", { class: "links-menu-popover", role: "menu", hidden: "" }),
|
|
);
|
|
}
|
|
const pop = root.querySelector(".links-menu-popover");
|
|
pop.replaceChildren(
|
|
...menuLinks(state).map((link) => {
|
|
const attrs = {
|
|
class: "links-menu-item",
|
|
href: link.url,
|
|
role: "menuitem",
|
|
};
|
|
if (link.external) {
|
|
attrs.target = "_blank";
|
|
attrs.rel = "noreferrer";
|
|
}
|
|
return el(
|
|
"a",
|
|
attrs,
|
|
el("span", { "aria-hidden": "true" }, link.icon),
|
|
link.label,
|
|
);
|
|
}),
|
|
);
|
|
wire(root);
|
|
}
|