Context menu keyboard event handling and general improvements
This commit is contained in:
parent
2e1426e945
commit
dd1a1b3c97
@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav id="map-context-menu" v-show="menuVisible" ref="menuElement" :style="style">
|
<nav role="none" id="map-context-menu" ref="menuElement" :style="style" @keydown="handleKeydown">
|
||||||
<div tabindex="0" ref="focusMover" class="focus-mover" aria-label="Context menu"></div>
|
|
||||||
<ul class="menu" role="menu">
|
<ul class="menu" role="menu">
|
||||||
<li role="none">
|
<li role="none">
|
||||||
<!--suppress HtmlUnknownAttribute -->
|
<!--suppress HtmlUnknownAttribute -->
|
||||||
@ -31,10 +30,11 @@ import {computed, defineComponent, onMounted, onUnmounted, watch} from "@vue/run
|
|||||||
import {LeafletMouseEvent} from "leaflet";
|
import {LeafletMouseEvent} from "leaflet";
|
||||||
import {useStore} from "@/store";
|
import {useStore} from "@/store";
|
||||||
import WorldListItem from "@/components/sidebar/WorldListItem.vue";
|
import WorldListItem from "@/components/sidebar/WorldListItem.vue";
|
||||||
import {ref} from "vue";
|
import {CSSProperties, ref} from "vue";
|
||||||
import {getUrlForLocation} from "@/util";
|
import {getUrlForLocation} from "@/util";
|
||||||
import {notify} from "@kyvg/vue3-notification";
|
import {notify} from "@kyvg/vue3-notification";
|
||||||
import {nextTick} from 'vue';
|
import {nextTick} from 'vue';
|
||||||
|
import {handleKeyboardEvent} from "@/util/events";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "MapContextMenu",
|
name: "MapContextMenu",
|
||||||
@ -55,7 +55,6 @@ export default defineComponent({
|
|||||||
messageCenterHere = computed(() => store.state.messages.contextMenuCenterHere),
|
messageCenterHere = computed(() => store.state.messages.contextMenuCenterHere),
|
||||||
|
|
||||||
menuElement = ref<HTMLInputElement | null>(null),
|
menuElement = ref<HTMLInputElement | null>(null),
|
||||||
focusMover = ref<HTMLInputElement | null>(null),
|
|
||||||
menuVisible = computed(() => !!event.value),
|
menuVisible = computed(() => !!event.value),
|
||||||
|
|
||||||
currentProjection = computed(() => store.state.currentProjection),
|
currentProjection = computed(() => store.state.currentProjection),
|
||||||
@ -94,17 +93,20 @@ export default defineComponent({
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
style = computed(() => {
|
style = computed(() => {
|
||||||
if (!menuElement.value || !event.value) {
|
if (!event.value) {
|
||||||
return {};
|
return {
|
||||||
|
'visibility': 'hidden',
|
||||||
|
'left': '-1000px',
|
||||||
|
} as CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Don't position offscreen
|
//Don't position offscreen
|
||||||
const x = Math.min(
|
const x = Math.min(
|
||||||
window.innerWidth - menuElement.value.offsetWidth - 10,
|
window.innerWidth - menuElement.value!.offsetWidth - 10,
|
||||||
event.value.originalEvent.clientX
|
event.value.originalEvent.clientX
|
||||||
),
|
),
|
||||||
y = Math.min(
|
y = Math.min(
|
||||||
window.innerHeight - menuElement.value.offsetHeight - 10,
|
window.innerHeight - menuElement.value!.offsetHeight - 10,
|
||||||
event.value.originalEvent.clientY
|
event.value.originalEvent.clientY
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -114,30 +116,47 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleEsc = (e: KeyboardEvent) => {
|
const handleEsc = (e: KeyboardEvent) => {
|
||||||
if (e.key === "Escape" && menuVisible.value) {
|
if (e.key === "Escape" && menuVisible.value) {
|
||||||
closeContextMenu();
|
closeContextMenu();
|
||||||
}
|
|
||||||
},
|
|
||||||
closeContextMenu = () => {
|
|
||||||
event.value = null;
|
|
||||||
},
|
|
||||||
pan = () => {
|
|
||||||
if (event.value) {
|
|
||||||
props.leaflet.panTo(event.value.latlng);
|
|
||||||
props.leaflet.getContainer().focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
copySuccess = () => notify('Copied to clipboard'),
|
|
||||||
copyError = (e: Error) => {
|
|
||||||
notify({ type: 'error', text:'Unable to copy to clipboard'});
|
|
||||||
console.error('Error copying to clipboard', e);
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(menuVisible, value => {
|
|
||||||
if(value) {
|
|
||||||
nextTick(() => focusMover.value && focusMover.value.focus());
|
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
|
|
||||||
|
const handleKeydown = (e: KeyboardEvent) => {
|
||||||
|
handleKeyboardEvent(e, Array.from(menuElement.value!.querySelectorAll('button, input')));
|
||||||
|
}
|
||||||
|
|
||||||
|
const focusFirstItem = () => {
|
||||||
|
if(menuElement.value) {
|
||||||
|
const firstItem = menuElement.value.querySelector('button');
|
||||||
|
|
||||||
|
if(firstItem) {
|
||||||
|
firstItem.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeContextMenu = () => event.value = null;
|
||||||
|
|
||||||
|
const pan = () => {
|
||||||
|
if (event.value) {
|
||||||
|
props.leaflet.panTo(event.value.latlng);
|
||||||
|
props.leaflet.getContainer().focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const copySuccess = () => notify('Copied to clipboard');
|
||||||
|
const copyError = (e: Error) => {
|
||||||
|
notify({ type: 'error', text:'Unable to copy to clipboard'});
|
||||||
|
console.error('Error copying to clipboard', e);
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(event, value => {
|
||||||
|
if(value) {
|
||||||
|
props.leaflet.closePopup();
|
||||||
|
props.leaflet.closeTooltip();
|
||||||
|
nextTick(() => menuElement.value && focusFirstItem());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('click', closeContextMenu);
|
window.addEventListener('click', closeContextMenu);
|
||||||
@ -180,13 +199,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(event, value => {
|
|
||||||
if(value) {
|
|
||||||
props.leaflet.closePopup();
|
|
||||||
props.leaflet.closeTooltip();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
messageCopyLink,
|
messageCopyLink,
|
||||||
messageCenterHere,
|
messageCenterHere,
|
||||||
@ -196,7 +208,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
menuVisible,
|
menuVisible,
|
||||||
menuElement,
|
menuElement,
|
||||||
focusMover,
|
|
||||||
url,
|
url,
|
||||||
|
|
||||||
locationLabel,
|
locationLabel,
|
||||||
@ -205,6 +216,7 @@ export default defineComponent({
|
|||||||
style,
|
style,
|
||||||
|
|
||||||
pan,
|
pan,
|
||||||
|
handleKeydown,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -89,18 +89,6 @@ input {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.focus-mover {
|
|
||||||
clip: rect(1px, 1px, 1px, 1px);
|
|
||||||
position: absolute;
|
|
||||||
height: 1px;
|
|
||||||
width: 1px;
|
|
||||||
|
|
||||||
@include focus {
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox {
|
.checkbox {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
Loading…
Reference in New Issue
Block a user