Created
April 24, 2018 08:15
-
-
Save SigurdMW/fdb74b33b81f741cab12fabedd056669 to your computer and use it in GitHub Desktop.
accessible vue tab list
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div class="tabs"> | |
<ul role="tablist" class="tabs__list"> | |
<li role="presentation" v-for="(tab, key) in children" :key="key" class="tabs__list-item"> | |
<a | |
:href="'#' + tab.id" | |
role="tab" | |
:aria-controls="tab.id" | |
:id="tab.trigger" | |
class="tabs__link" | |
:class="{ 'tabs__link--active': visibleTabIndex === key }" | |
@click.prevent="showTab(key)" | |
:aria-selected="visibleTabIndex === key" | |
:tabindex="visibleTabIndex !== key ? '-1' : '0'" | |
@keydown.left.right.down.prevent="handleKey" | |
> | |
{{ tab.name }} | |
</a> | |
</li> | |
</ul> | |
<slot></slot> | |
</div> | |
</template> | |
<script> | |
// Thanks to https://codepen.io/BeyondHyper/pen/xZXXzj | |
export default { | |
name: "TabListComponent", | |
data () { | |
return { | |
children: [], | |
visibleTabIndex: 0 | |
} | |
}, | |
mounted () { | |
this.children = this.$children.map(child => ({ | |
id: child.id, | |
trigger: child.trigger, | |
name: child.name, | |
el: child | |
})); | |
// shot first tab on load. | |
this.children[0].el.showMe(); | |
}, | |
methods: { | |
showTab (key) { | |
this.children.map(child => child.el.hideMe()); | |
this.visibleTabIndex = key; | |
this.children[key].el.showMe(); | |
}, | |
updateActiveIndex (newIndex) { | |
if (newIndex < 0) newIndex = this.children.length - 1; | |
if (newIndex > this.children.length - 1) newIndex = 0; | |
this.visibleTabIndex = newIndex; | |
this.showTab(newIndex); | |
this.$el.querySelector("#" + this.children[newIndex].trigger).focus(); | |
}, | |
focusTabContent () { | |
this.children[this.visibleTabIndex].el.$el.focus(); | |
}, | |
handleKey (e) { | |
if (e.keyCode === 37) { // left | |
this.updateActiveIndex(this.visibleTabIndex - 1); | |
} else if (e.keyCode === 39) { // right | |
this.updateActiveIndex(this.visibleTabIndex + 1) | |
} else if (e.keyCode === 40) { // down | |
this.focusTabContent(); | |
} | |
} | |
} | |
} | |
</script> | |
<template><!-- new file --></template> | |
<template> | |
<section | |
:id="id" | |
role="tabpanel" | |
:aria-labelledby="trigger" | |
tabindex="-1" | |
hidden | |
class="tabs__content" | |
> | |
<slot></slot> | |
</section> | |
</template> | |
<script> | |
export default { | |
name: "TabComponent", | |
props: { | |
name: { | |
type: String, | |
required: true | |
} | |
}, | |
data () { | |
return { | |
id: "tab-component-" + this._uid, | |
trigger: "tab-trigger-" + this._uid | |
} | |
}, | |
methods: { | |
showMe () { | |
this.$el.classList.add("tabs__content--open"); | |
this.$el.removeAttribute("hidden"); | |
}, | |
hideMe () { | |
this.$el.classList.remove("tabs__content--open"); | |
this.$el.setAttribute("hidden", "hidden"); | |
} | |
} | |
} | |
</script> | |
<template><!-- usage --></template> | |
<tab-list-component> | |
<tab-component name="some tab"> | |
<p>some content in tab group 1</p> | |
</tab-component> | |
<tab-component name="some other tab"> | |
<p>some content in tab group 2</p> | |
</tab-component> | |
</tab-list-component> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment