1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
* Area tab strip (GL #487): every four-jobs area is one page whose views are
* tabs. This element listens for `lctx:view` and renders the sibling tabs of
* the active view; outside an area (Home) it stays hidden. Tabs are plain
* hash links (`#area/tab`), so middle-click/copy-link keep working and the
* router's lazy per-view loaders stay untouched.
*/
class CockpitAreaTabs extends HTMLElement {
connectedCallback() {
if (this._ready) return;
this._ready = true;
this._onViewEvent = this._onViewEvent.bind(this);
document.addEventListener('lctx:view', this._onViewEvent);
this.hidden = true;
}
disconnectedCallback() {
document.removeEventListener('lctx:view', this._onViewEvent);
}
_onViewEvent(e) {
const d = e.detail || {};
if (!d.areaId || !window.LctxRouter || !window.LctxRouter.COCKPIT_AREAS) {
this.hidden = true;
this.innerHTML = '';
return;
}
this._render(d.areaId, d.viewId);
}
_render(areaId, activeViewId) {
const areas = window.LctxRouter.COCKPIT_AREAS;
let area = null;
for (let i = 0; i < areas.length; i++) {
if (areas[i].id === areaId) { area = areas[i]; break; }
}
if (!area) {
this.hidden = true;
return;
}
let html =
'<div class="area-tabs" role="tablist" aria-label="' +
area.label.replace(/"/g, '"') + ' tabs">';
for (let i = 0; i < area.tabs.length; i++) {
const t = area.tabs[i];
const on = t.view === activeViewId;
html +=
'<a class="area-tab' + (on ? ' active' : '') + '" role="tab" ' +
'aria-selected="' + (on ? 'true' : 'false') + '" ' +
'href="#' + area.id + '/' + t.tab + '" data-view="' + t.view + '">' +
t.label + '</a>';
}
html += '</div>';
this.innerHTML = html;
this.hidden = false;
this._bindKeys();
}
// Roving arrow-key support per WAI-ARIA tabs pattern; activation stays on
// click/Enter because tabs are links and switching loads data lazily.
_bindKeys() {
const tabs = [...this.querySelectorAll('.area-tab')];
tabs.forEach(function (tab, idx) {
tab.addEventListener('keydown', function (e) {
let next = -1;
if (e.key === 'ArrowRight') next = (idx + 1) % tabs.length;
else if (e.key === 'ArrowLeft') next = (idx - 1 + tabs.length) % tabs.length;
else if (e.key === 'Home') next = 0;
else if (e.key === 'End') next = tabs.length - 1;
if (next >= 0) {
e.preventDefault();
tabs[next].focus();
}
});
});
}
}
customElements.define('cockpit-area-tabs', CockpitAreaTabs);
export { CockpitAreaTabs };