diff --git a/src/App.vue b/src/App.vue
index 0fa8b27..7c2b637 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -31,7 +31,7 @@ import {ActionTypes} from "@/store/action-types";
import {parseUrl} from '@/util';
import {hideSplash, showSplash, showSplashError} from '@/util/splash';
import {MutationTypes} from "@/store/mutation-types";
-import {LiveAtlasServerDefinition} from "@/index";
+import {LiveAtlasServerDefinition, LiveAtlasUIElement} from "@/index";
export default defineComponent({
name: 'App',
@@ -132,6 +132,34 @@ export default defineComponent({
onResize = () => {
store.commit(MutationTypes.SET_SMALL_SCREEN, window.innerWidth < 480 || window.innerHeight < 500);
+ },
+
+ onKeydown = (e: KeyboardEvent) => {
+ if(!e.ctrlKey || !e.shiftKey) {
+ return;
+ }
+
+ let element: LiveAtlasUIElement;
+
+ switch(e.key) {
+ case 'M':
+ element = 'maps';
+ break;
+ case 'C':
+ element = 'chat';
+ break;
+ case 'P':
+ element = 'players';
+ break;
+ case 'L':
+ element = 'layers';
+ break;
+ default:
+ return;
+ }
+
+ e.preventDefault();
+ store.commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, element);
};
watch(title, (title) => document.title = title);
@@ -169,8 +197,14 @@ export default defineComponent({
handleUrl();
onResize();
- onMounted(() => window.addEventListener('resize', onResize));
- onUnmounted(() => window.addEventListener('resize', onResize));
+ onMounted(() => {
+ window.addEventListener('resize', onResize);
+ window.addEventListener('keydown', onKeydown);
+ });
+ onUnmounted(() => {
+ window.addEventListener('resize', onResize);
+ window.addEventListener('keydown', onKeydown);
+ });
return {
chatBoxEnabled,
diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue
index e6d8779..aec7595 100644
--- a/src/components/Sidebar.vue
+++ b/src/components/Sidebar.vue
@@ -15,25 +15,22 @@
-->
-
@@ -49,8 +46,9 @@ import SvgIcon from "@/components/SvgIcon.vue";
import {MutationTypes} from "@/store/mutation-types";
import "@/assets/icons/players.svg";
import "@/assets/icons/maps.svg";
-import {nextTick} from "vue";
+import {nextTick, ref, watch} from "vue";
import {handleKeyboardEvent} from "@/util/events";
+import {focus} from "@/util";
export default defineComponent({
components: {
@@ -63,6 +61,8 @@ export default defineComponent({
setup() {
const store = useStore(),
+ sidebar = ref(null),
+
currentlyVisible = computed(() => store.state.ui.visibleElements),
previouslyVisible = computed(() => store.state.ui.previouslyVisibleElements),
smallScreen = computed(() => store.state.ui.smallScreen),
@@ -73,78 +73,55 @@ export default defineComponent({
messageWorlds = computed(() => store.state.messages.worldsHeading),
messagePlayers = computed(() => store.state.messages.playersHeading),
- followActive = computed(() => {
+ playersVisible = computed(() => currentlyVisible.value.has('players')),
+ mapsVisible = computed(() => currentlyVisible.value.has('maps')),
+ followVisible = computed(() => {
//Show following alongside playerlist on small screens
return (!smallScreen.value && following.value)
- || (smallScreen.value && currentlyVisible.value.has('players'));
+ || (smallScreen.value && playersVisible.value);
});
- return {
- mapCount,
- serverCount,
- currentlyVisible,
- previouslyVisible,
- followActive,
- following,
- messageWorlds,
- messagePlayers,
- }
- },
-
- mounted() {
- window.addEventListener('keydown', this.handleButtonKeydown);
- },
-
- unmounted() {
- window.removeEventListener('keydown', this.handleButtonKeydown);
- },
-
- methods: {
- handleButtonKeydown(e: KeyboardEvent) {
- if(e.key === '!' && e.ctrlKey && e.shiftKey) {
- e.preventDefault();
- this.toggleMaps();
- } else if(e.key === '"' && e.ctrlKey && e.shiftKey) {
- e.preventDefault();
- this.togglePlayers();
- }
- },
- handleSidebarKeydown(e: KeyboardEvent) {
+ //Arrow key section navigation
+ const handleSidebarKeydown = (e: KeyboardEvent) => {
if(!e.target || !(e.target as HTMLElement).matches('.section__heading button')) {
return;
}
- const sectionHeadings: HTMLElement[] = Array.from(this.$el.querySelectorAll('.section__heading button'));
+ const sectionHeadings: HTMLElement[] = Array.from(sidebar.value!.querySelectorAll('.section__heading button'));
handleKeyboardEvent(e, sectionHeadings);
- },
- togglePlayers() {
- useStore().commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, 'players');
- nextTick(this.focusPlayers);
- },
- toggleMaps() {
- useStore().commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, 'maps');
- nextTick(this.focusMaps);
- },
- focusPlayers() {
- if (this.currentlyVisible.has('players')) {
- const heading = document.getElementById('players-heading');
+ };
- if (heading) {
- heading.focus();
- }
- }
- },
- focusMaps() {
- if(this.currentlyVisible.has('maps')) {
- const heading = document.querySelector('.section__heading button');
+ const togglePlayers = () => store.commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, 'players');
+ const toggleMaps = () => store.commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, 'maps');
- if(heading) {
- (heading as HTMLElement).focus();
- }
- }
+ //Move focus when sidebar sections become visible
+ const focusMaps = () => focus('.section__heading button');
+ const focusPlayers = () => focus('#players-heading');
+
+ watch(playersVisible, newValue => newValue && nextTick(() => focusPlayers()));
+ watch(mapsVisible, newValue => newValue && nextTick(() => focusMaps()));
+
+ return {
+ sidebar,
+
+ mapCount,
+ serverCount,
+ following,
+
+ messageWorlds,
+ messagePlayers,
+
+ previouslyVisible,
+ playersVisible,
+ mapsVisible,
+ followVisible,
+
+ handleSidebarKeydown,
+ togglePlayers,
+ toggleMaps
}
- }
-})
+ },
+});
diff --git a/src/util.ts b/src/util.ts
index 784be00..422e03b 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -229,3 +229,11 @@ export const getUrlForLocation = (world: DynmapWorld, map: DynmapWorldMap, locat
return `#${world.name};${map.name};${locationString};${zoom}`;
}
+
+export const focus = (selector: string) => {
+ const element = document.querySelector(selector);
+
+ if(element) {
+ (element as HTMLElement).focus();
+ }
+}
\ No newline at end of file