@magic-spells/tab-group 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +37 -188
- package/dist/tab-group.cjs.js +102 -109
- package/dist/tab-group.cjs.js.map +1 -1
- package/dist/tab-group.css +0 -76
- package/dist/tab-group.esm.js +103 -109
- package/dist/tab-group.esm.js.map +1 -1
- package/dist/tab-group.js +102 -109
- package/dist/tab-group.js.map +1 -1
- package/dist/tab-group.min.css +1 -1
- package/dist/tab-group.min.js +1 -1
- package/package.json +12 -13
- package/tab-group.d.ts +33 -0
- package/dist/scss/tab-group.scss +0 -125
- package/dist/scss/variables.scss +0 -0
- package/dist/tab-group.scss +0 -2
- package/src/index.scss +0 -2
- package/src/scss/tab-group.scss +0 -125
- package/src/scss/variables.scss +0 -0
- package/src/tab-group.js +0 -277
package/dist/scss/tab-group.scss
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
// Tab Group base styles
|
|
2
|
-
tab-group {
|
|
3
|
-
display: block;
|
|
4
|
-
width: 100%;
|
|
5
|
-
font-family: var(--font-family);
|
|
6
|
-
font-size: var(--font-size-base);
|
|
7
|
-
line-height: var(--line-height);
|
|
8
|
-
color: var(--color-text);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// Tab List styles
|
|
12
|
-
tab-list {
|
|
13
|
-
display: flex;
|
|
14
|
-
border-bottom: var(
|
|
15
|
-
--tab-list-border-bottom,
|
|
16
|
-
1px solid var(--color-border)
|
|
17
|
-
);
|
|
18
|
-
margin-bottom: var(--spacing-md);
|
|
19
|
-
overflow-x: auto;
|
|
20
|
-
overflow-y: hidden;
|
|
21
|
-
padding: var(--tab-list-padding, 0.5rem 0.25rem 0);
|
|
22
|
-
gap: var(--tab-list-gap, 0.25rem);
|
|
23
|
-
justify-content: var(--tab-list-justify, flex-start);
|
|
24
|
-
background-color: var(--tab-list-background, transparent);
|
|
25
|
-
background-image: var(--tab-list-background-image, none);
|
|
26
|
-
border-radius: var(--tab-list-radius, 0);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Tab Button styles
|
|
30
|
-
tab-button {
|
|
31
|
-
display: block;
|
|
32
|
-
padding: var(--spacing-sm) var(--spacing-md);
|
|
33
|
-
margin: var(--spacing-xs);
|
|
34
|
-
border: 1px solid transparent;
|
|
35
|
-
border-bottom: none;
|
|
36
|
-
background-color: var(--color-background);
|
|
37
|
-
color: var(--color-text);
|
|
38
|
-
cursor: pointer;
|
|
39
|
-
border-radius: var(
|
|
40
|
-
--tab-button-radius,
|
|
41
|
-
var(--border-radius) var(--border-radius) 0 0
|
|
42
|
-
);
|
|
43
|
-
white-space: nowrap;
|
|
44
|
-
user-select: none;
|
|
45
|
-
transition: all var(--transition-duration);
|
|
46
|
-
margin-bottom: -1px;
|
|
47
|
-
position: relative;
|
|
48
|
-
font-size: var(--font-size-base);
|
|
49
|
-
font-weight: var(--tab-button-font-weight, normal);
|
|
50
|
-
text-align: center;
|
|
51
|
-
min-width: 100px;
|
|
52
|
-
|
|
53
|
-
&:hover {
|
|
54
|
-
background-color: var(--color-hover);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
&:focus {
|
|
58
|
-
outline: none;
|
|
59
|
-
box-shadow: var(
|
|
60
|
-
--tab-button-focus-shadow,
|
|
61
|
-
0 0 0 2px var(--color-primary)
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
&[aria-selected='true'] {
|
|
66
|
-
background-color: var(
|
|
67
|
-
--tab-active-background,
|
|
68
|
-
var(--color-background)
|
|
69
|
-
);
|
|
70
|
-
border-color: var(--color-border);
|
|
71
|
-
border-bottom: 1px solid var(--color-background);
|
|
72
|
-
font-weight: var(--tab-active-font-weight, 600);
|
|
73
|
-
color: var(--tab-active-color, var(--color-primary));
|
|
74
|
-
box-shadow: var(--tab-active-shadow, none);
|
|
75
|
-
transform: var(--tab-active-transform, none);
|
|
76
|
-
|
|
77
|
-
&::after {
|
|
78
|
-
content: '';
|
|
79
|
-
position: absolute;
|
|
80
|
-
bottom: 0;
|
|
81
|
-
left: var(--tab-indicator-left, 0);
|
|
82
|
-
right: var(--tab-indicator-right, 0);
|
|
83
|
-
height: var(--tab-indicator-height, 2px);
|
|
84
|
-
background-color: var(
|
|
85
|
-
--tab-indicator-color,
|
|
86
|
-
var(--color-primary)
|
|
87
|
-
);
|
|
88
|
-
border-radius: var(--tab-indicator-radius, 0);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Tab Panel styles
|
|
94
|
-
tab-panel {
|
|
95
|
-
display: block;
|
|
96
|
-
padding: var(--panel-padding, var(--spacing-md));
|
|
97
|
-
background-color: var(--panel-background, var(--color-background));
|
|
98
|
-
border-radius: var(
|
|
99
|
-
--panel-radius,
|
|
100
|
-
0 0 var(--border-radius) var(--border-radius)
|
|
101
|
-
);
|
|
102
|
-
border: var(--panel-border, none);
|
|
103
|
-
border-top: var(--panel-border-top, none);
|
|
104
|
-
box-shadow: var(--panel-shadow, none);
|
|
105
|
-
transition: var(--panel-transition, all 0.3s ease);
|
|
106
|
-
margin: var(--panel-margin, 0);
|
|
107
|
-
min-height: var(--panel-min-height, 150px);
|
|
108
|
-
|
|
109
|
-
// When hidden (using the hidden attribute)
|
|
110
|
-
&[hidden] {
|
|
111
|
-
display: none;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Responsive adjustments
|
|
116
|
-
@media (max-width: 768px) {
|
|
117
|
-
tab-list {
|
|
118
|
-
flex-wrap: wrap;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
tab-button {
|
|
122
|
-
flex: 1 0 auto;
|
|
123
|
-
text-align: center;
|
|
124
|
-
}
|
|
125
|
-
}
|
package/dist/scss/variables.scss
DELETED
|
File without changes
|
package/dist/tab-group.scss
DELETED
package/src/index.scss
DELETED
package/src/scss/tab-group.scss
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
// Tab Group base styles
|
|
2
|
-
tab-group {
|
|
3
|
-
display: block;
|
|
4
|
-
width: 100%;
|
|
5
|
-
font-family: var(--font-family);
|
|
6
|
-
font-size: var(--font-size-base);
|
|
7
|
-
line-height: var(--line-height);
|
|
8
|
-
color: var(--color-text);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// Tab List styles
|
|
12
|
-
tab-list {
|
|
13
|
-
display: flex;
|
|
14
|
-
border-bottom: var(
|
|
15
|
-
--tab-list-border-bottom,
|
|
16
|
-
1px solid var(--color-border)
|
|
17
|
-
);
|
|
18
|
-
margin-bottom: var(--spacing-md);
|
|
19
|
-
overflow-x: auto;
|
|
20
|
-
overflow-y: hidden;
|
|
21
|
-
padding: var(--tab-list-padding, 0.5rem 0.25rem 0);
|
|
22
|
-
gap: var(--tab-list-gap, 0.25rem);
|
|
23
|
-
justify-content: var(--tab-list-justify, flex-start);
|
|
24
|
-
background-color: var(--tab-list-background, transparent);
|
|
25
|
-
background-image: var(--tab-list-background-image, none);
|
|
26
|
-
border-radius: var(--tab-list-radius, 0);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Tab Button styles
|
|
30
|
-
tab-button {
|
|
31
|
-
display: block;
|
|
32
|
-
padding: var(--spacing-sm) var(--spacing-md);
|
|
33
|
-
margin: var(--spacing-xs);
|
|
34
|
-
border: 1px solid transparent;
|
|
35
|
-
border-bottom: none;
|
|
36
|
-
background-color: var(--color-background);
|
|
37
|
-
color: var(--color-text);
|
|
38
|
-
cursor: pointer;
|
|
39
|
-
border-radius: var(
|
|
40
|
-
--tab-button-radius,
|
|
41
|
-
var(--border-radius) var(--border-radius) 0 0
|
|
42
|
-
);
|
|
43
|
-
white-space: nowrap;
|
|
44
|
-
user-select: none;
|
|
45
|
-
transition: all var(--transition-duration);
|
|
46
|
-
margin-bottom: -1px;
|
|
47
|
-
position: relative;
|
|
48
|
-
font-size: var(--font-size-base);
|
|
49
|
-
font-weight: var(--tab-button-font-weight, normal);
|
|
50
|
-
text-align: center;
|
|
51
|
-
min-width: 100px;
|
|
52
|
-
|
|
53
|
-
&:hover {
|
|
54
|
-
background-color: var(--color-hover);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
&:focus {
|
|
58
|
-
outline: none;
|
|
59
|
-
box-shadow: var(
|
|
60
|
-
--tab-button-focus-shadow,
|
|
61
|
-
0 0 0 2px var(--color-primary)
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
&[aria-selected='true'] {
|
|
66
|
-
background-color: var(
|
|
67
|
-
--tab-active-background,
|
|
68
|
-
var(--color-background)
|
|
69
|
-
);
|
|
70
|
-
border-color: var(--color-border);
|
|
71
|
-
border-bottom: 1px solid var(--color-background);
|
|
72
|
-
font-weight: var(--tab-active-font-weight, 600);
|
|
73
|
-
color: var(--tab-active-color, var(--color-primary));
|
|
74
|
-
box-shadow: var(--tab-active-shadow, none);
|
|
75
|
-
transform: var(--tab-active-transform, none);
|
|
76
|
-
|
|
77
|
-
&::after {
|
|
78
|
-
content: '';
|
|
79
|
-
position: absolute;
|
|
80
|
-
bottom: 0;
|
|
81
|
-
left: var(--tab-indicator-left, 0);
|
|
82
|
-
right: var(--tab-indicator-right, 0);
|
|
83
|
-
height: var(--tab-indicator-height, 2px);
|
|
84
|
-
background-color: var(
|
|
85
|
-
--tab-indicator-color,
|
|
86
|
-
var(--color-primary)
|
|
87
|
-
);
|
|
88
|
-
border-radius: var(--tab-indicator-radius, 0);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Tab Panel styles
|
|
94
|
-
tab-panel {
|
|
95
|
-
display: block;
|
|
96
|
-
padding: var(--panel-padding, var(--spacing-md));
|
|
97
|
-
background-color: var(--panel-background, var(--color-background));
|
|
98
|
-
border-radius: var(
|
|
99
|
-
--panel-radius,
|
|
100
|
-
0 0 var(--border-radius) var(--border-radius)
|
|
101
|
-
);
|
|
102
|
-
border: var(--panel-border, none);
|
|
103
|
-
border-top: var(--panel-border-top, none);
|
|
104
|
-
box-shadow: var(--panel-shadow, none);
|
|
105
|
-
transition: var(--panel-transition, all 0.3s ease);
|
|
106
|
-
margin: var(--panel-margin, 0);
|
|
107
|
-
min-height: var(--panel-min-height, 150px);
|
|
108
|
-
|
|
109
|
-
// When hidden (using the hidden attribute)
|
|
110
|
-
&[hidden] {
|
|
111
|
-
display: none;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Responsive adjustments
|
|
116
|
-
@media (max-width: 768px) {
|
|
117
|
-
tab-list {
|
|
118
|
-
flex-wrap: wrap;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
tab-button {
|
|
122
|
-
flex: 1 0 auto;
|
|
123
|
-
text-align: center;
|
|
124
|
-
}
|
|
125
|
-
}
|
package/src/scss/variables.scss
DELETED
|
File without changes
|
package/src/tab-group.js
DELETED
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
import './index.scss';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @module TabGroup
|
|
5
|
-
* A fully accessible tab group web component
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @class TabGroup
|
|
10
|
-
* the parent container that coordinates tabs and panels
|
|
11
|
-
*/
|
|
12
|
-
export default class TabGroup extends HTMLElement {
|
|
13
|
-
// static counter to ensure global unique ids for tabs and panels
|
|
14
|
-
static tabCount = 0;
|
|
15
|
-
static panelCount = 0;
|
|
16
|
-
|
|
17
|
-
constructor() {
|
|
18
|
-
super();
|
|
19
|
-
// ensure that the number of <tab-button> and <tab-panel> elements match
|
|
20
|
-
// note: in some scenarios the child elements might not be available in the constructor,
|
|
21
|
-
// so adjust as necessary or consider running this check in connectedCallback()
|
|
22
|
-
this.ensureConsistentTabsAndPanels();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* @function ensureConsistentTabsAndPanels
|
|
27
|
-
* makes sure there is an equal number of <tab-button> and <tab-panel> elements.
|
|
28
|
-
* if there are more panels than tabs, inject extra tab buttons.
|
|
29
|
-
* if there are more tabs than panels, inject extra panels.
|
|
30
|
-
*/
|
|
31
|
-
ensureConsistentTabsAndPanels() {
|
|
32
|
-
// get current tabs and panels within the tab group
|
|
33
|
-
let tabs = this.querySelectorAll("tab-button");
|
|
34
|
-
let panels = this.querySelectorAll("tab-panel");
|
|
35
|
-
|
|
36
|
-
// if there are more panels than tabs
|
|
37
|
-
if (panels.length > tabs.length) {
|
|
38
|
-
const difference = panels.length - tabs.length;
|
|
39
|
-
// try to find a <tab-list> to insert new tabs
|
|
40
|
-
let tabList = this.querySelector("tab-list");
|
|
41
|
-
if (!tabList) {
|
|
42
|
-
// if not present, create one and insert it at the beginning
|
|
43
|
-
tabList = document.createElement("tab-list");
|
|
44
|
-
this.insertBefore(tabList, this.firstChild);
|
|
45
|
-
}
|
|
46
|
-
// inject extra <tab-button> elements into the tab list
|
|
47
|
-
for (let i = 0; i < difference; i++) {
|
|
48
|
-
const newTab = document.createElement("tab-button");
|
|
49
|
-
newTab.textContent = "default tab";
|
|
50
|
-
tabList.appendChild(newTab);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// if there are more tabs than panels
|
|
54
|
-
else if (tabs.length > panels.length) {
|
|
55
|
-
const difference = tabs.length - panels.length;
|
|
56
|
-
// inject extra <tab-panel> elements at the end of the tab group
|
|
57
|
-
for (let i = 0; i < difference; i++) {
|
|
58
|
-
const newPanel = document.createElement("tab-panel");
|
|
59
|
-
newPanel.innerHTML = "<p>default panel content</p>";
|
|
60
|
-
this.appendChild(newPanel);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* called when the element is connected to the dom
|
|
67
|
-
*/
|
|
68
|
-
connectedCallback() {
|
|
69
|
-
const _ = this;
|
|
70
|
-
|
|
71
|
-
// find the <tab-list> element (should be exactly one)
|
|
72
|
-
_.tabList = _.querySelector("tab-list");
|
|
73
|
-
if (!_.tabList) return;
|
|
74
|
-
|
|
75
|
-
// find all <tab-button> elements inside the <tab-list>
|
|
76
|
-
_.tabButtons = Array.from(_.tabList.querySelectorAll("tab-button"));
|
|
77
|
-
|
|
78
|
-
// find all <tab-panel> elements inside the <tab-group>
|
|
79
|
-
_.tabPanels = Array.from(_.querySelectorAll("tab-panel"));
|
|
80
|
-
|
|
81
|
-
// initialize each tab-button with roles, ids and aria attributes
|
|
82
|
-
_.tabButtons.forEach((tab, index) => {
|
|
83
|
-
const tabIndex = TabGroup.tabCount++;
|
|
84
|
-
|
|
85
|
-
// generate a unique id for each tab, e.g. "tab-0", "tab-1", ...
|
|
86
|
-
const tabId = `tab-${tabIndex}`;
|
|
87
|
-
tab.id = tabId;
|
|
88
|
-
|
|
89
|
-
// generate a corresponding panel id, e.g. "panel-0"
|
|
90
|
-
const panelId = `panel-${tabIndex}`;
|
|
91
|
-
tab.setAttribute("role", "tab");
|
|
92
|
-
tab.setAttribute("aria-controls", panelId);
|
|
93
|
-
|
|
94
|
-
// first tab is active by default
|
|
95
|
-
if (index === 0) {
|
|
96
|
-
tab.setAttribute("aria-selected", "true");
|
|
97
|
-
tab.setAttribute("tabindex", "0");
|
|
98
|
-
} else {
|
|
99
|
-
tab.setAttribute("aria-selected", "false");
|
|
100
|
-
tab.setAttribute("tabindex", "-1");
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// initialize each tab-panel with roles, ids and aria attributes
|
|
105
|
-
_.tabPanels.forEach((panel, index) => {
|
|
106
|
-
const panelIndex = TabGroup.panelCount++;
|
|
107
|
-
const panelId = `panel-${panelIndex}`;
|
|
108
|
-
panel.id = panelId;
|
|
109
|
-
|
|
110
|
-
panel.setAttribute("role", "tabpanel");
|
|
111
|
-
panel.setAttribute("aria-labelledby", `tab-${panelIndex}`);
|
|
112
|
-
|
|
113
|
-
// hide panels except for the first one
|
|
114
|
-
panel.hidden = index !== 0;
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// set up keyboard navigation and click delegation on the <tab-list>
|
|
118
|
-
_.tabList.setAttribute("role", "tablist");
|
|
119
|
-
_.tabList.addEventListener("keydown", (e) => _.onKeyDown(e));
|
|
120
|
-
_.tabList.addEventListener("click", (e) => _.onClick(e));
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* @function setActiveTab
|
|
125
|
-
* activates a tab and updates aria attributes
|
|
126
|
-
* @param {number} index - index of the tab to activate
|
|
127
|
-
*/
|
|
128
|
-
setActiveTab(index) {
|
|
129
|
-
const _ = this;
|
|
130
|
-
const previousIndex = _.tabButtons.findIndex(tab => tab.getAttribute("aria-selected") === "true");
|
|
131
|
-
|
|
132
|
-
// update each tab-button
|
|
133
|
-
_.tabButtons.forEach((tab, i) => {
|
|
134
|
-
const isActive = i === index;
|
|
135
|
-
tab.setAttribute("aria-selected", isActive ? "true" : "false");
|
|
136
|
-
tab.setAttribute("tabindex", isActive ? "0" : "-1");
|
|
137
|
-
if (isActive) {
|
|
138
|
-
tab.focus();
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// update each tab-panel
|
|
143
|
-
_.tabPanels.forEach((panel, i) => {
|
|
144
|
-
panel.hidden = i !== index;
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// dispatch event only if the tab actually changed
|
|
148
|
-
if (previousIndex !== index) {
|
|
149
|
-
const detail = {
|
|
150
|
-
previousIndex,
|
|
151
|
-
currentIndex: index,
|
|
152
|
-
previousTab: _.tabButtons[previousIndex],
|
|
153
|
-
currentTab: _.tabButtons[index],
|
|
154
|
-
previousPanel: _.tabPanels[previousIndex],
|
|
155
|
-
currentPanel: _.tabPanels[index]
|
|
156
|
-
};
|
|
157
|
-
_.dispatchEvent(new CustomEvent('tabchange', { detail, bubbles: true }));
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* @function onClick
|
|
163
|
-
* handles click events on the <tab-list> via event delegation
|
|
164
|
-
* @param {MouseEvent} e - the click event
|
|
165
|
-
*/
|
|
166
|
-
onClick(e) {
|
|
167
|
-
const _ = this;
|
|
168
|
-
// check if the click occurred on or within a <tab-button>
|
|
169
|
-
const tabButton = e.target.closest("tab-button");
|
|
170
|
-
if (!tabButton) return;
|
|
171
|
-
|
|
172
|
-
// determine the index of the clicked tab-button
|
|
173
|
-
const index = _.tabButtons.indexOf(tabButton);
|
|
174
|
-
if (index === -1) return;
|
|
175
|
-
|
|
176
|
-
// activate the tab with the corresponding index
|
|
177
|
-
_.setActiveTab(index);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* @function onKeyDown
|
|
182
|
-
* handles keyboard navigation for the tabs
|
|
183
|
-
* @param {KeyboardEvent} e - the keydown event
|
|
184
|
-
*/
|
|
185
|
-
onKeyDown(e) {
|
|
186
|
-
const _ = this;
|
|
187
|
-
// only process keys if focus is on a <tab-button>
|
|
188
|
-
const targetIndex = _.tabButtons.indexOf(e.target);
|
|
189
|
-
if (targetIndex === -1) return;
|
|
190
|
-
|
|
191
|
-
let newIndex = targetIndex;
|
|
192
|
-
switch (e.key) {
|
|
193
|
-
case "ArrowLeft":
|
|
194
|
-
case "ArrowUp":
|
|
195
|
-
// move to the previous tab (wrap around if necessary)
|
|
196
|
-
newIndex = targetIndex > 0 ? targetIndex - 1 : _.tabButtons.length - 1;
|
|
197
|
-
e.preventDefault();
|
|
198
|
-
break;
|
|
199
|
-
case "ArrowRight":
|
|
200
|
-
case "ArrowDown":
|
|
201
|
-
// move to the next tab (wrap around if necessary)
|
|
202
|
-
newIndex = (targetIndex + 1) % _.tabButtons.length;
|
|
203
|
-
e.preventDefault();
|
|
204
|
-
break;
|
|
205
|
-
case "Home":
|
|
206
|
-
// jump to the first tab
|
|
207
|
-
newIndex = 0;
|
|
208
|
-
e.preventDefault();
|
|
209
|
-
break;
|
|
210
|
-
case "End":
|
|
211
|
-
// jump to the last tab
|
|
212
|
-
newIndex = _.tabButtons.length - 1;
|
|
213
|
-
e.preventDefault();
|
|
214
|
-
break;
|
|
215
|
-
default:
|
|
216
|
-
return; // ignore other keys
|
|
217
|
-
}
|
|
218
|
-
_.setActiveTab(newIndex);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* @class TabList
|
|
224
|
-
* a container for the <tab-button> elements
|
|
225
|
-
*/
|
|
226
|
-
class TabList extends HTMLElement {
|
|
227
|
-
constructor() {
|
|
228
|
-
super();
|
|
229
|
-
const _ = this;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
connectedCallback() {
|
|
233
|
-
const _ = this;
|
|
234
|
-
// additional logic or styling can be added here if desired
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* @class TabButton
|
|
240
|
-
* a single tab button element
|
|
241
|
-
*/
|
|
242
|
-
class TabButton extends HTMLElement {
|
|
243
|
-
constructor() {
|
|
244
|
-
super();
|
|
245
|
-
const _ = this;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
connectedCallback() {
|
|
249
|
-
const _ = this;
|
|
250
|
-
// note: role and other attributes are handled by the parent
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* @class TabPanel
|
|
256
|
-
* a single tab panel element
|
|
257
|
-
*/
|
|
258
|
-
class TabPanel extends HTMLElement {
|
|
259
|
-
constructor() {
|
|
260
|
-
super();
|
|
261
|
-
const _ = this;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
connectedCallback() {
|
|
265
|
-
const _ = this;
|
|
266
|
-
// note: role and other attributes are handled by the parent
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// define the custom elements
|
|
271
|
-
customElements.define("tab-group", TabGroup);
|
|
272
|
-
customElements.define("tab-list", TabList);
|
|
273
|
-
customElements.define("tab-button", TabButton);
|
|
274
|
-
customElements.define("tab-panel", TabPanel);
|
|
275
|
-
|
|
276
|
-
// export the main component
|
|
277
|
-
export { TabGroup };
|