Sidebar cleanup and fixes

- Merge functions handling sidebar section events, focusing and toggling
- Use hidden attribute instead of v-show for sidebar sections
- Ignore hidden sidebar sections when navigating with the keyboard
- Allow focusing an already visible section via down arrow on its button
This commit is contained in:
James Lyne 2022-01-14 13:16:04 +00:00
parent ef28afaceb
commit 4e8780b3a4
3 changed files with 41 additions and 31 deletions

View File

@ -17,24 +17,24 @@
<template>
<section class="sidebar" role="none" ref="sidebar">
<header class="sidebar__buttons">
<button v-if="mapCount > 1 || serverCount > 1" :class="{'button--maps': true}" @click="toggleMaps"
<button v-if="mapCount > 1 || serverCount > 1" class="button--maps" data-section="maps"
:title="mapCount > 1 ? messageWorlds : messageServers"
:aria-label="mapCount > 1 ? messageWorlds : messageServers"
:aria-expanded="mapsVisible" @keydown="handleMapsKeydown">
:aria-expanded="mapsVisible"
@click="handleSectionClick" @keydown="handleSectionKeydown">
<SvgIcon :name="mapCount > 1 ? 'maps' : 'servers'"></SvgIcon>
</button>
<button v-if="playerMakersEnabled" :class="{'button--players': true}" @click="togglePlayers"
<button v-if="playerMakersEnabled" class="button--players" data-section="players"
:title="messagePlayers" :aria-label="messagePlayers" :aria-expanded="playersVisible"
@keydown="handlePlayersKeydown">
@click="handleSectionClick" @keydown="handleSectionKeydown">
<SvgIcon name="players"></SvgIcon>
</button>
</header>
<div class="sidebar__content" @keydown="handleSidebarKeydown">
<ServersSection v-if="serverCount > 1" v-show="mapsVisible"></ServersSection>
<WorldsSection v-if="mapCount > 1" v-show="mapsVisible"></WorldsSection>
<PlayersSection id="players" v-if="playerMakersEnabled && previouslyVisible.has('players')"
v-show="playersVisible"></PlayersSection>
<FollowTargetSection v-if="following" v-show="followVisible" :target="following"></FollowTargetSection>
<ServersSection v-if="serverCount > 1" :hidden="!mapsVisible"></ServersSection>
<WorldsSection v-if="mapCount > 1" :hidden="!mapsVisible"></WorldsSection>
<PlayersSection v-if="playerMakersEnabled && previouslyVisible.has('players')" :hidden="!playersVisible"></PlayersSection>
<FollowTargetSection v-if="following" :hidden="!followVisible" :target="following"></FollowTargetSection>
</div>
</section>
</template>
@ -54,6 +54,7 @@ import "@/assets/icons/servers.svg";
import {nextTick, ref, watch} from "vue";
import {handleKeyboardEvent} from "@/util/events";
import {focus} from "@/util";
import {LiveAtlasSidebarSection} from "@/index";
export default defineComponent({
components: {
@ -96,33 +97,41 @@ export default defineComponent({
return;
}
const sectionHeadings: HTMLElement[] = Array.from(sidebar.value!.querySelectorAll('.section__heading button'));
const sectionHeadings: HTMLElement[] = Array.from(sidebar.value!
.querySelectorAll('.sidebar__section:not([hidden]) .section__heading button'));
handleKeyboardEvent(e, sectionHeadings);
};
//Show sectinos on ArrowDown from button
const handleMapsKeydown = (e: KeyboardEvent) => {
if(e.key === 'ArrowDown') {
store.commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'maps', state: true});
//Show sections on ArrowDown from button
const handleSectionKeydown = (e: KeyboardEvent) => {
const section = (e.target as HTMLElement).dataset.section as LiveAtlasSidebarSection;
if(e.key === 'ArrowDown' && section) {
if(currentlyVisible.value.has(section)) {
focusSection(section);
} else {
store.commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {
element: section,
state: true
});
}
}
};
const handlePlayersKeydown = (e: KeyboardEvent) => {
if(e.key === 'ArrowDown') {
store.commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'players', state: true});
}
};
const handleSectionClick = (e: MouseEvent) => {
const section = (e.target as HTMLElement).dataset.section as LiveAtlasSidebarSection;
const togglePlayers = () => store.commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, 'players');
const toggleMaps = () => store.commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, 'maps');
if(section) {
store.commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, section);
}
}
//Move focus when sidebar sections become visible
const focusMaps = () => focus('.section__heading button');
const focusPlayers = () => focus('#players-heading');
const focusSection = (section: LiveAtlasSidebarSection) => focus(`[data-section=${section}] .section__heading button`);
//Focus sidebar sections when they become visible, except on initial load
watch(playersVisible, newValue => newValue && !firstLoad.value && nextTick(() => focusPlayers()));
watch(mapsVisible, newValue => newValue && !firstLoad.value && nextTick(() => focusMaps()));
watch(playersVisible, newValue => newValue && !firstLoad.value && nextTick(() => focusSection('players')));
watch(mapsVisible, newValue => newValue && !firstLoad.value && nextTick(() => focusSection('maps')));
return {
sidebar,
@ -142,11 +151,8 @@ export default defineComponent({
playerMakersEnabled,
handleSidebarKeydown,
handleMapsKeydown,
handlePlayersKeydown,
togglePlayers,
toggleMaps
handleSectionKeydown,
handleSectionClick,
}
},
});

View File

@ -95,6 +95,10 @@ export default defineComponent({
max-width: 26rem;
flex: 0 0 auto;
&[hidden] {
display: none;
}
.section__heading {
cursor: pointer;
user-select: none;

2
src/index.d.ts vendored
View File

@ -98,7 +98,7 @@ interface LiveAtlasUIConfig {
compactPlayerMarkers: boolean;
}
export type LiveAtlasUIElement = 'layers' | 'chat' | 'players' | 'maps';
export type LiveAtlasUIElement = 'layers' | 'chat' | LiveAtlasSidebarSection;
export type LiveAtlasUIModal = 'login' | 'settings';
export type LiveAtlasSidebarSection = 'servers' | 'players' | 'maps';
export type LiveAtlasDimension = 'overworld' | 'nether' | 'end';