Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/frontend/packages/shared/src/tabs/hive-tab-strip.js
iris ee5c057d8f dashboard: the build pages are tabs of the dashboard
The build lifecycle used to be its own page with three sub-tabs. They are
now dashboard tabs (rebuild, meta, buildlogs) beside SW4RM and
P3RM1SS10NS: builds.js is a module tabs.js imports, so the dashboard's one
tab strip, /api/state snapshot and event stream (plus the meta_inputs_changed
and meta_update_running kinds) serve them, and the page, its bundle entry,
stylesheet and home tile are gone. The SW4RM banner's "view queue" link
points at the #rebuild tab, and the rebuild tab carries a count pill of the
rebuilds in flight.

An empty tab-count pill drew a "0": its display beat the UA [hidden] rule.
2026-10-15 09:50:49 +02:00

68 lines
2.8 KiB
JavaScript

// hive-tab-strip.js — <hive-tab-strip>, the markup-owning tab-strip custom
// element behind the logs/core sub-page tabbars. Owns
// rendering the <a class="hive-tab"> markup from a declarative `tabs` list
// instead of every page hand-writing the same <nav><a>...</a></nav>
// boilerplate, then wires the existing createTabStrip() behaviour
// (@hive/shared/tabs.js) over what it just rendered — no behaviour
// duplication, this is markup only.
//
// Light-DOM (no shadow root): the existing .hive-tabbar/.hive-tab/
// .tab-count CSS (tabs.css + dashboard.css) already targets plain classes
// on the tabbar/tab elements directly, and tab *panes* stay page-owned
// content this element never touches — no slotting/scoping need the way
// <hive-menu>/<hive-side-panel> have.
//
// Host keeps its own id/class/role (same as the <nav> it replaces, so
// existing CSS selectors + #id lookups keep matching); pass a `prefix`
// attribute (e.g. `prefix="logs"`). `configure({ tabs, defaultId, onShow })`
// then renders one <a> per tab —
// <a class="hive-tab" id="${prefix}-tab-${id}" href="#${id}" role="tab"
// aria-controls="${prefix}-pane-${id}" data-tab="${id}">${label}</a>
// — same id/aria-controls scheme the hand-written markup used, so each
// page's panes (aria-labelledby="${prefix}-tab-${id}") don't need to
// change. A `badgeId` tab also gets a nested `<span class="tab-count"
// id="${badgeId}" hidden>` (e.g. a rebuild-queue count
// pill), then wires up createTabStrip() and returns exactly what it
// returns ({ show, active }) — every existing `.active()`/`.show(id)`
// call site keeps working unchanged. Call configure() exactly once per
// element (throws if called twice), mirroring the old call sites.
import { el } from "../dom.js";
import { createTabStrip } from "./tabs.js";
class HiveTabStrip extends HTMLElement {
configure({ tabs, defaultId, onShow }) {
if (this._api) {
throw new Error(
"hive-tab-strip: configure() called twice on the same element",
);
}
const prefix = this.getAttribute("prefix");
if (!prefix) {
throw new Error("hive-tab-strip: missing required `prefix` attribute");
}
for (const tab of tabs) {
const a = el(
"a",
{
class: "hive-tab",
id: `${prefix}-tab-${tab.id}`,
href: `#${tab.id}`,
role: "tab",
"aria-controls": `${prefix}-pane-${tab.id}`,
"data-tab": tab.id,
},
tab.label,
);
if (tab.badgeId) {
a.append(
el("span", { class: "tab-count", id: tab.badgeId, hidden: "" }),
);
}
this.append(a);
}
this._api = createTabStrip(this, { defaultId, onShow });
return this._api;
}
}
customElements.define("hive-tab-strip", HiveTabStrip);