1 import {Component} from './component';
5 * Uses accessible attributes to drive its functionality.
6 * On tab wrapping element:
8 * On tabs (Should be a button):
11 * - aria-selected=true/false
12 * - aria-controls=<id-of-panel-section>
17 * - aria-labelledby=<id-of-tab-for-panel>
18 * - hidden (If not shown by default).
20 export class Tabs extends Component {
23 this.container = this.$el;
24 this.tabList = this.container.querySelector('[role="tablist"]');
25 this.tabs = Array.from(this.tabList.querySelectorAll('[role="tab"]'));
26 this.panels = Array.from(this.container.querySelectorAll(':scope > [role="tabpanel"], :scope > * > [role="tabpanel"]'));
27 this.activeUnder = this.$opts.activeUnder ? Number(this.$opts.activeUnder) : 10000;
30 this.container.addEventListener('click', event => {
31 const tab = event.target.closest('[role="tab"]');
32 if (tab && this.tabs.includes(tab)) {
33 this.show(tab.getAttribute('aria-controls'));
37 window.addEventListener('resize', this.updateActiveState.bind(this), {
40 this.updateActiveState();
44 for (const panel of this.panels) {
45 panel.toggleAttribute('hidden', panel.id !== sectionId);
48 for (const tab of this.tabs) {
49 const tabSection = tab.getAttribute('aria-controls');
50 const selected = tabSection === sectionId;
51 tab.setAttribute('aria-selected', selected ? 'true' : 'false');
54 this.$emit('change', {showing: sectionId});
58 const active = window.innerWidth < this.activeUnder;
59 if (active === this.active) {
73 const panelToShow = this.panels.find(p => !p.hasAttribute('hidden')) || this.panels[0];
74 this.show(panelToShow.id);
75 this.tabList.toggleAttribute('hidden', false);
79 for (const panel of this.panels) {
80 panel.removeAttribute('hidden');
82 for (const tab of this.tabs) {
83 tab.setAttribute('aria-selected', 'false');
85 this.tabList.toggleAttribute('hidden', true);