Use radio buttons for server list
This commit is contained in:
parent
72772881b6
commit
e1c56c201e
@ -18,9 +18,9 @@
|
|||||||
<CollapsibleSection v-if="servers.size > 1" name="servers">
|
<CollapsibleSection v-if="servers.size > 1" name="servers">
|
||||||
<template v-slot:heading>{{ heading }}</template>
|
<template v-slot:heading>{{ heading }}</template>
|
||||||
<template v-slot:default>
|
<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>
|
<ServerListItem :server="server" v-for="[name, server] in servers" :key="name"></ServerListItem>
|
||||||
</ul>
|
</RadioList>
|
||||||
</template>
|
</template>
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
</template>
|
</template>
|
||||||
@ -30,10 +30,12 @@ import ServerListItem from './ServerListItem.vue';
|
|||||||
import {defineComponent} from 'vue';
|
import {defineComponent} from 'vue';
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import CollapsibleSection from "@/components/sidebar/CollapsibleSection.vue";
|
import CollapsibleSection from "@/components/sidebar/CollapsibleSection.vue";
|
||||||
|
import RadioList from "@/components/util/RadioList.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ServerList',
|
name: 'ServerList',
|
||||||
components: {
|
components: {
|
||||||
|
RadioList,
|
||||||
CollapsibleSection,
|
CollapsibleSection,
|
||||||
ServerListItem
|
ServerListItem
|
||||||
},
|
},
|
||||||
|
@ -15,12 +15,9 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<li :class="{'server': true, 'server--selected': selected}" role="none">
|
<input :id="`server-${server.id}`" type="radio" name="server" :value="server.id" v-model="currentServer"
|
||||||
<button type="button" :class="{'active': selected}"
|
@click="setCurrentServer(server.id)">
|
||||||
role="radio" :aria-checked="selected" :title="server.label || server.id"
|
<label :for="`server-${server.id}`">{{ server.label || server.id }}</label>
|
||||||
@click="setCurrentServer(server.id)">{{ server.label || server.id }}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@ -39,11 +36,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
currentServer(): LiveAtlasServerDefinition | undefined {
|
currentServer(): string | undefined {
|
||||||
return useStore().state.currentServer;
|
return useStore().state.currentServer ? useStore().state.currentServer.id : undefined;
|
||||||
},
|
|
||||||
selected(): boolean {
|
|
||||||
return !!this.currentServer && this.server.id === this.currentServer.id;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
71
src/components/util/RadioList.vue
Normal file
71
src/components/util/RadioList.vue
Normal 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>
|
||||||
|
|
@ -26,6 +26,7 @@
|
|||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
font-family: Raleway, sans-serif;
|
font-family: Raleway, sans-serif;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
text-shadow: none;
|
||||||
|
|
||||||
.svg-icon {
|
.svg-icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -172,20 +172,47 @@ input {
|
|||||||
display: flex;
|
display: flex;
|
||||||
padding: 0.8rem 0.8rem 0.7rem;
|
padding: 0.8rem 0.8rem 0.7rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-bottom: 0.1rem solid transparent;
|
|
||||||
|
|
||||||
> button {
|
> button {
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin: -0.8rem -0.8rem -0.7rem;
|
margin: -0.8rem -0.8rem -0.7rem;
|
||||||
padding: 0.8rem 0.8rem 0.7rem;
|
|
||||||
text-align: left;
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
min-height: 3.2rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> 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;
|
||||||
|
padding: 0.8rem 0.8rem 0.7rem;
|
||||||
|
text-align: left;
|
||||||
|
min-height: 3.2rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border-bottom: 0.1rem solid transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#app .map .tile img, img {
|
#app .map .tile img, img {
|
||||||
|
Loading…
Reference in New Issue
Block a user