Tabs
A roving-tabindex tab list. Only the active tab is in the tab order
(tabindex="0", the rest -1); arrow keys move the active tab and shift focus
with it. Panels are plain elements toggled with data-show.
Markup
<div role="tablist" aria-label="Product details">
<template data-each="tabs" data-key="id">
<button
role="tab"
data-bind="data-id:item.id, class:tabClass(item.id), aria-selected:sel(item.id), tabindex:tabIndex(item.id)"
@click="select"
@keydown.left.prevent="prev"
@keydown.right.prevent="next"
data-text="item.label"
></button>
</template>
</div>
<div data-show="active === 'overview'">...</div>
<div data-show="active === 'specs'">...</div>
<div data-show="active === 'reviews'">...</div>
Component
Micra.define("tabs", {
state: {
active: "overview",
tabs: [
{ id: "overview", label: "Overview" },
{ id: "specs", label: "Specs" },
{ id: "reviews", label: "Reviews" },
],
},
sel(id) {
return this.state.active === id ? "true" : "false";
},
tabIndex(id) {
return this.state.active === id ? "0" : "-1";
},
tabClass(id) {
return this.state.active === id ? "tab tab-active" : "tab";
},
select(e) {
this.state.active = e.currentTarget.dataset.id;
},
step(delta) {
const t = this.state.tabs;
const i = t.findIndex((x) => x.id === this.state.active);
this.state.active = t[(i + delta + t.length) % t.length].id;
setTimeout(
() => this.$el.querySelector('[data-id="' + this.state.active + '"]')?.focus(),
0,
);
},
prev() {
this.step(-1);
},
next() {
this.step(1);
},
});