Use radio buttons for server list

This commit is contained in:
James Lyne 2021-05-27 18:18:26 +01:00
parent 72772881b6
commit e1c56c201e
5 changed files with 116 additions and 21 deletions

View File

@ -18,9 +18,9 @@
<CollapsibleSection v-if="servers.size > 1" name="servers">
<template v-slot:heading>{{ heading }}</template>
<template v-slot:default>
<ul class="section__content menu" role="listbox" aria-labelledby="servers-heading">
<RadioList class="section__content" aria-labelledby="servers-heading">
<ServerListItem :server="server" v-for="[name, server] in servers" :key="name"></ServerListItem>
</ul>
</RadioList>
</template>
</CollapsibleSection>
</template>
@ -30,10 +30,12 @@ import ServerListItem from './ServerListItem.vue';
import {defineComponent} from 'vue';
import {useStore} from "@/store";
import CollapsibleSection from "@/components/sidebar/CollapsibleSection.vue";
import RadioList from "@/components/util/RadioList.vue";
export default defineComponent({
name: 'ServerList',
components: {
RadioList,
CollapsibleSection,
ServerListItem
},

View File

@ -15,12 +15,9 @@
-->
<template>
<li :class="{'server': true, 'server--selected': selected}" role="none">
<button type="button" :class="{'active': selected}"
role="radio" :aria-checked="selected" :title="server.label || server.id"
@click="setCurrentServer(server.id)">{{ server.label || server.id }}
</button>
</li>
<input :id="`server-${server.id}`" type="radio" name="server" :value="server.id" v-model="currentServer"
@click="setCurrentServer(server.id)">
<label :for="`server-${server.id}`">{{ server.label || server.id }}</label>
</template>
<script lang="ts">
@ -39,11 +36,8 @@ export default defineComponent({
},
computed: {
currentServer(): LiveAtlasServerDefinition | undefined {
return useStore().state.currentServer;
},
selected(): boolean {
return !!this.currentServer && this.server.id === this.currentServer.id;
currentServer(): string | undefined {
return useStore().state.currentServer ? useStore().state.currentServer.id : undefined;
}
},

View File

@ -0,0 +1,71 @@
<!--
- Copyright 2020 James Lyne
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-->
<template>
<fieldset class="menu" role="radiogroup" @keydown="onKeydown">
<slot></slot>
</fieldset>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
export default defineComponent({
name: 'RadioList',
setup() {
},
methods: {
onKeydown(e: KeyboardEvent) {
if(e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
const fieldset = e.currentTarget as HTMLFieldSetElement,
position = Array.from(fieldset.elements).indexOf(e.target as HTMLElement);
if(position < 0) {
return;
}
let newPosition = (e.key === 'ArrowUp' || e.key === 'ArrowLeft') ? position - 1 : position + 1;
if(newPosition < 0) {
newPosition = fieldset.elements.length - 1;
} else if (newPosition >= fieldset.elements.length) {
newPosition = 0;
}
(fieldset.elements[newPosition] as HTMLElement).focus();
e.preventDefault();
} else if(e.key === 'Enter') {
if(e.target instanceof HTMLInputElement && e.target.type === 'radio') {
e.target.click();
}
}
}
}
});
</script>
<style lang="scss">
@import '../../scss/mixins';
fieldset {
appearance: none;
border: none;
margin: 0;
padding: 0;
}
</style>

View File

@ -26,6 +26,7 @@
font-size: 1.6rem;
font-family: Raleway, sans-serif;
box-sizing: border-box;
text-shadow: none;
.svg-icon {
display: inline-block;

View File

@ -172,19 +172,46 @@ input {
display: flex;
padding: 0.8rem 0.8rem 0.7rem;
position: relative;
border-bottom: 0.1rem solid transparent;
> button {
margin: -0.8rem -0.8rem -0.7rem;
flex-grow: 1;
}
}
> input[type=radio] {
clip: rect(1px, 1px, 1px, 1px);
height: 1px;
width: 1px;
position: absolute;
& + label {
@include button;
}
&:hover + label, &:checked + label {
@include button-hovered;
position: relative;
}
&:focus + label {
@include button-focused;
}
&:active + label {
@include button-active;
}
}
& > li > button, & > input[type=radio] + label {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
margin: -0.8rem -0.8rem -0.7rem;
padding: 0.8rem 0.8rem 0.7rem;
text-align: left;
flex-grow: 1;
min-height: 3.2rem;
border-radius: 0.5rem;
}
border-bottom: 0.1rem solid transparent;
}
}