refactoring of cmaps, widgets, etc.

This commit is contained in:
2026-09-02 16:06:26 +02:00
parent 38f255c1a4
commit f0562a06cc
52 changed files with 3626 additions and 2642 deletions
+47
View File
@@ -0,0 +1,47 @@
/**
* Coordinate tabs and their aria-controls panels inside one tab list.
*
* Tabs identify their logical name with data-tab. Selecting a tab updates the
* ARIA state, panel visibility and roving keyboard focus.
*/
export class TabSet {
constructor(element) {
this.element = element;
this.tabs = Array.from(element.querySelectorAll('[role="tab"]'));
this.selectionHandlers = [];
this.element.addEventListener("click", (event) => {
const tab = event.target.closest('[role="tab"]');
if (tab && this.element.contains(tab)) this.select(tab.dataset.tab);
});
this.element.addEventListener("keydown", (event) => this.handleKeydown(event));
}
/** Select a named tab and show the panel named by its aria-controls value. */
select(name) {
for (const tab of this.tabs) {
const selected = tab.dataset.tab === name;
tab.setAttribute("aria-selected", String(selected));
tab.tabIndex = selected ? 0 : -1;
const panel = document.getElementById(tab.getAttribute("aria-controls"));
if (panel) panel.classList.toggle("hidden", !selected);
}
for (const handler of this.selectionHandlers) handler(name);
}
onSelect(handler) {
this.selectionHandlers.push(handler);
return this;
}
handleKeydown(event) {
if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
const current = this.tabs.indexOf(event.target.closest('[role="tab"]'));
if (current < 0) return;
event.preventDefault();
const next = event.key === "Home" ? 0 : event.key === "End" ? this.tabs.length - 1 :
(current + (event.key === "ArrowRight" ? 1 : -1) + this.tabs.length) % this.tabs.length;
this.select(this.tabs[next].dataset.tab);
this.tabs[next].focus();
}
}