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

View File

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

2
src/index.d.ts vendored
View File

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