-
Notifications
You must be signed in to change notification settings - Fork 1
/
tabs.ts
103 lines (84 loc) · 3.17 KB
/
tabs.ts
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
let idCounter = 0;
export default class TabsElement extends HTMLElement {
private idPrefix = 'tabs' + ++idCounter;
public connectedCallback(): void {
this.tablist.addEventListener('keydown', this.onKeyDown);
this.tablist.addEventListener('click', this.onClick);
this.selectTab(0, false);
this.tabs.forEach((tab, i) => {
const panel = this.tabpanels[i];
const tabId = tab.getAttribute('id') || this.idPrefix + '_tab_' + i;
const panelId =
panel.getAttribute('id') || this.idPrefix + '_panel_' + i;
tab.setAttribute('id', tabId);
tab.setAttribute('aria-controls', panelId);
panel.setAttribute('id', panelId);
panel.setAttribute('aria-labelledby', tabId);
panel.setAttribute('tabindex', '0');
});
}
public disconnectedCallback(): void {
this.tablist.removeEventListener('keydown', this.onKeyDown);
this.tablist.removeEventListener('click', this.onClick);
}
private selectTab(index: number, focus = true) {
if (index < 0) index += this.tabs.length;
else if (index >= this.tabs.length) index -= this.tabs.length;
this.tabs.forEach((tab, i) => {
if (i === index) {
tab.setAttribute('aria-selected', 'true');
tab.removeAttribute('tabindex');
this.tabpanels[i].hidden = false;
if (focus) tab.focus();
} else {
tab.setAttribute('aria-selected', 'false');
tab.setAttribute('tabindex', '-1');
this.tabpanels[i].hidden = true;
}
});
}
private onKeyDown = (e: KeyboardEvent) => {
const target = e.target as HTMLElement;
const index = this.tabs.indexOf(target);
const vertical =
this.tablist.getAttribute('aria-orientation') === 'vertical';
let captured = false;
switch (e.key) {
case vertical ? 'ArrowUp' : 'ArrowLeft':
this.selectTab(index - 1);
captured = true;
break;
case vertical ? 'ArrowDown' : 'ArrowRight':
this.selectTab(index + 1);
captured = true;
break;
case 'Home':
this.selectTab(0);
captured = true;
break;
case 'End':
this.selectTab(this.tabs.length - 1);
captured = true;
}
if (captured) {
e.stopPropagation();
e.preventDefault();
}
};
private onClick = (e: MouseEvent) => {
const target = e.target as HTMLElement;
const tab = target.closest<HTMLElement>('[role=tab]');
if (tab) {
this.selectTab(this.tabs.indexOf(tab));
}
};
private get tablist(): HTMLElement {
return this.querySelector('[role=tablist]')!;
}
private get tabs(): HTMLElement[] {
return Array.from(this.querySelectorAll('[role=tab]'));
}
private get tabpanels(): HTMLElement[] {
return Array.from(this.querySelectorAll('[role=tabpanel]'));
}
}