Skip to content

Commit 4bbe590

Browse files
committed
Finalize tabs with automatic activation
1 parent 8185fe4 commit 4bbe590

File tree

2 files changed

+64
-9
lines changed

2 files changed

+64
-9
lines changed

docs/.vuepress/theme/components/CodeToggle.vue

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,25 @@
22
<div class="code-toggle">
33
<div class="code-lang-switcher theme-default-content-override" role="tablist" v-if="!usePageToggle">
44
<button
5-
v-for="(language, index) in languages" :key="index"
6-
:class="{ active: isSelectedLanguage(language) }"
7-
:aria-selected="isSelectedLanguage(language)"
5+
v-for="(language, index) in languages"
6+
:key="language"
7+
:class="{ active: isSelectedTab(language) }"
8+
:aria-selected="isSelectedTab(language)"
89
:id="getTabId(language)"
10+
:data-language="language"
911
role="tab"
1012
:aria-controls="getTabPanelId(language)"
1113
@click="setLanguage(language)"
12-
:tabindex="isSelectedLanguage(language) ? null : '-1'"
14+
:tabindex="isSelectedTab(language) ? null : '-1'"
15+
v-on:keyup="handleKeyup"
1316
>{{ getLanguageLabel(language) }}</button>
1417
</div>
1518
<div
1619
v-for="(language, index) in languages"
20+
tabindex="0"
1721
:key="index"
1822
:id="getTabPanelId(language)"
19-
:hidden="!isSelectedLanguage(language)"
23+
:hidden="!isSelectedTab(language)"
2024
:aria-labelledby="getTabId(language)"
2125
role="tabpanel">
2226
<slot :name="language" />
@@ -48,15 +52,22 @@
4852
@apply flex flex-row rounded-t box-border m-0 p-2;
4953
background: var(--border-color);
5054
z-index: 2;
55+
gap: .3rem;
5156
5257
button {
5358
@apply block py-3 px-4 font-medium text-xs tracking-wider uppercase leading-none cursor-pointer rounded;
54-
59+
border: 1px solid transparent;
5560
&:hover {
5661
background-color: var(--sidebar-bg-color);
5762
}
5863
64+
&:focus-visible {
65+
outline: var(--custom-focus-outline);
66+
outline-offset: 2px;
67+
}
68+
5969
&.active {
70+
border-color: var(--active-tab-border-color);
6071
color: var(--text-color);
6172
background-color: var(--bg-color);
6273
}
@@ -80,13 +91,20 @@ export default {
8091
return {
8192
selectedLanguage: this.languages[0],
8293
uniqueId: null,
94+
focusedTabIndex: null,
8395
};
8496
},
8597
8698
mounted() {
8799
this.uniqueId = uuidv4();
88100
},
89101
102+
watch: {
103+
selectedLanguage(newLanguage) {
104+
this.$el.querySelector(`button[data-language="${newLanguage}"]`).focus();
105+
},
106+
},
107+
90108
computed: {
91109
usePageToggle() {
92110
if (this.$page === undefined) {
@@ -101,6 +119,12 @@ export default {
101119
setLanguage(language) {
102120
this.selectedLanguage = language;
103121
},
122+
getLanguageFromIndex(index) {
123+
return this.languages[index];
124+
},
125+
getIndexFromLanguage(language) {
126+
return this.languages.indexOf(language);
127+
},
104128
getLanguageLabel(language) {
105129
if (this.labels && this.labels[language]) {
106130
return this.labels[language];
@@ -114,10 +138,8 @@ export default {
114138
(themeLanguages && themeLanguages[language]) ||
115139
language
116140
);
117-
118-
return language;
119141
},
120-
isSelectedLanguage(language) {
142+
isSelectedTab(language) {
121143
return (
122144
language ==
123145
(this.usePageToggle
@@ -131,6 +153,38 @@ export default {
131153
getTabPanelId(language) {
132154
return `tabpanel-${this.uniqueId}-${language}`;
133155
},
156+
getNextIndex(index) {
157+
return index + 1 <= this.languages.length - 1 ? index + 1 : 0;
158+
},
159+
getPrevIndex(index) {
160+
return index - 1 >= 0 ? index - 1 : this.languages.length - 1;
161+
},
162+
handleKeyup(event) {
163+
const {keyCode, target} = event;
164+
let indexToFocus;
165+
const currentIndex = this.getIndexFromLanguage(target.getAttribute('data-language'));
166+
167+
switch (keyCode) {
168+
case 37:
169+
indexToFocus = this.getPrevIndex(currentIndex);
170+
break;
171+
case 39:
172+
indexToFocus = this.getNextIndex(currentIndex);
173+
break;
174+
case 36:
175+
indexToFocus = 0;
176+
break;
177+
case 35:
178+
indexToFocus = this.languages.length - 1;
179+
break;
180+
default:
181+
return;
182+
}
183+
184+
if (indexToFocus !== undefined) {
185+
this.setLanguage(this.getLanguageFromIndex(indexToFocus));
186+
}
187+
},
134188
}
135189
};
136190
</script>

docs/.vuepress/theme/styles/base.pcss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
--white: theme("colors.white");
2828
--black: theme("colors.black");
2929
--custom-focus-outline: 2px solid var(--link-color-default);
30+
--active-tab-border-color: (theme("colors.slate"));
3031

3132
/* Custom button */
3233
--button-background-color: theme("colors.blue.default");

0 commit comments

Comments
 (0)