frontend: add <hive-tab-strip>, convert logs/credentials/core/builds tabbars
Every sub-page tabbar (logs.html, credentials.html, core.html,
builds.html) hand-wrote the same <nav class="hive-tabbar"><a
class="hive-tab">...</a></nav> boilerplate and then called
createTabStrip() on it after the fact. Add <hive-tab-strip>, a
markup-owning custom element (same reuse-boundary pattern as
<hive-menu>/<hive-side-panel>) that renders that markup from a
declarative tabs list, then wires the existing createTabStrip()
behaviour over what it just rendered — no behaviour duplication.
Convert all four sites to use it: each page's JS now calls
`.configure({ tabs, defaultId, onShow })` on the tabbar element instead
of `createTabStrip(el, opts)`, and configure() returns the identical
{ show, active } shape so nothing downstream changes. builds.js's
rebuild-queue count pill (builds-tab-count-rebuild) is expressed as a
tab's `badgeId` and renders nested in the same spot.
The dashboard's own tabbar and the two no-pane stats time-range
pickers are a different markup/behaviour shape and are intentionally
left alone.
This commit is contained in:
parent
44572d1e1a
commit
435ef193e1
10 changed files with 103 additions and 75 deletions
60
frontend/packages/shared/src/tabs/hive-tab-strip.js
Normal file
60
frontend/packages/shared/src/tabs/hive-tab-strip.js
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// hive-tab-strip.js — <hive-tab-strip>, the markup-owning tab-strip custom
|
||||
// element behind the logs/credentials/core/builds 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>` (matches builds.html's 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);
|
||||
Loading…
Reference in a new issue