mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-12-20 19:06:06 +01:00
f1329d2847
This patch fixes some accessibility problems on the main settings page. Most notably, it makes sure that the different options have labels which are picked up by screen readers. As a more generic addition, this also makes sure that the dropdown component will always have a proper label constructed, explaining what the dropdown is for and what its current value is.
118 lines
3.9 KiB
Vue
118 lines
3.9 KiB
Vue
<template>
|
|
<div class="relative w-full" v-click-outside="clickOutsideObj">
|
|
<p class="text-sm font-semibold px-1" :class="disabled ? 'text-gray-300' : ''">{{ label }}</p>
|
|
<button type="button" :aria-label="longLabel" :disabled="disabled" class="relative w-full border rounded shadow-sm pl-3 pr-8 py-2 text-left sm:text-sm" :class="buttonClass" aria-haspopup="listbox" aria-expanded="true" @click.stop.prevent="clickShowMenu">
|
|
<span class="flex items-center">
|
|
<span class="block truncate font-sans" :class="{ 'font-semibold': selectedSubtext, 'text-sm': small }">{{ selectedText }}</span>
|
|
<span v-if="selectedSubtext">: </span>
|
|
<span v-if="selectedSubtext" class="font-normal block truncate font-sans text-sm text-gray-400">{{ selectedSubtext }}</span>
|
|
</span>
|
|
<span class="ml-3 absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
|
<span class="material-icons text-2xl">expand_more</span>
|
|
</span>
|
|
</button>
|
|
|
|
<transition name="menu">
|
|
<ul v-show="showMenu" class="absolute z-10 -mt-px w-full bg-primary border border-black-200 shadow-lg max-h-56 rounded-b-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto sm:text-sm" tabindex="-1" role="listbox">
|
|
<template v-for="item in itemsToShow">
|
|
<li :key="item.value" class="text-gray-100 relative py-2 cursor-pointer hover:bg-black-400" :id="'listbox-option-' + item.value" role="option" tabindex="0" @keyup.enter="clickedOption(item.value)" @click="clickedOption(item.value)">
|
|
<div class="flex items-center">
|
|
<span class="ml-3 block truncate font-sans text-sm" :class="{ 'font-semibold': item.subtext }">{{ item.text }}</span>
|
|
<span v-if="item.subtext">: </span>
|
|
<span v-if="item.subtext" class="font-normal block truncate font-sans text-sm text-gray-400">{{ item.subtext }}</span>
|
|
</div>
|
|
</li>
|
|
</template>
|
|
</ul>
|
|
</transition>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
props: {
|
|
value: [String, Number],
|
|
label: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
items: {
|
|
type: Array,
|
|
default: () => []
|
|
},
|
|
disabled: Boolean,
|
|
small: Boolean
|
|
},
|
|
data() {
|
|
return {
|
|
clickOutsideObj: {
|
|
handler: this.clickedOutside,
|
|
events: ['mousedown'],
|
|
isActive: true
|
|
},
|
|
showMenu: false
|
|
}
|
|
},
|
|
computed: {
|
|
selected: {
|
|
get() {
|
|
return this.value
|
|
},
|
|
set(val) {
|
|
this.$emit('input', val)
|
|
}
|
|
},
|
|
itemsToShow() {
|
|
return this.items.map((i) => {
|
|
if (typeof i === 'string') {
|
|
return {
|
|
text: i,
|
|
value: i
|
|
}
|
|
}
|
|
return i
|
|
})
|
|
},
|
|
selectedItem() {
|
|
return this.itemsToShow.find((i) => i.value === this.selected)
|
|
},
|
|
selectedText() {
|
|
return this.selectedItem ? this.selectedItem.text : ''
|
|
},
|
|
selectedSubtext() {
|
|
return this.selectedItem ? this.selectedItem.subtext : ''
|
|
},
|
|
buttonClass() {
|
|
var classes = []
|
|
if (this.small) classes.push('h-9')
|
|
else classes.push('h-10')
|
|
|
|
if (this.disabled) classes.push('cursor-not-allowed border-gray-600 bg-primary bg-opacity-70 border-opacity-70 text-gray-400')
|
|
else classes.push('cursor-pointer border-gray-600 bg-primary text-gray-100')
|
|
|
|
return classes.join(' ')
|
|
},
|
|
longLabel() {
|
|
let result = ''
|
|
if (this.label) result += this.label + ': '
|
|
if (this.selectedText) result += this.selectedText
|
|
if (this.selectedSubtext) result += ' ' + this.selectedSubtext
|
|
return result
|
|
}
|
|
},
|
|
methods: {
|
|
clickShowMenu() {
|
|
if (this.disabled) return
|
|
this.showMenu = !this.showMenu
|
|
},
|
|
clickedOutside() {
|
|
this.showMenu = false
|
|
},
|
|
clickedOption(itemValue) {
|
|
this.selected = itemValue
|
|
this.showMenu = false
|
|
}
|
|
},
|
|
mounted() {}
|
|
}
|
|
</script> |