Improve context menus

This commit is contained in:
Pavel Djundik 2019-11-18 22:05:47 +02:00
parent 9147772cb2
commit 85907f54ba
4 changed files with 85 additions and 56 deletions

View file

@ -1,19 +1,34 @@
<template>
<div v-if="isOpen" id="context-menu-container" @click="close" @contextmenu.prevent="close">
<ul id="context-menu" ref="contextMenu" role="menu" :style="style">
<li
v-for="item of items"
:key="item.name"
:class="[
'context-menu-' + item.type,
item.class ? 'context-menu-' + item.class : null,
]"
tabindex="0"
role="menuitem"
@click="clickItem(item)"
>
{{ item.label }}
</li>
<div
v-if="isOpen"
id="context-menu-container"
@click="containerClick"
@contextmenu.prevent="containerClick"
>
<ul
id="context-menu"
ref="contextMenu"
role="menu"
:style="style"
tabindex="-1"
@mouseleave="activeItem = -1"
@keydown.enter.prevent="clickActiveItem"
>
<template v-for="(item, id) of items">
<li
:key="item.name"
:class="[
'context-menu-' + item.type,
item.class ? 'context-menu-' + item.class : null,
{active: id === activeItem},
]"
role="menuitem"
@mouseover="hoverItem(id)"
@click="clickItem(item)"
>
{{ item.label }}
</li>
</template>
</ul>
</div>
</template>
@ -31,6 +46,7 @@ export default {
isOpen: false,
previousActiveElement: null,
items: [],
activeItem: -1,
style: {
left: 0,
top: 0,
@ -39,57 +55,27 @@ export default {
},
mounted() {
Mousetrap.bind("esc", this.close);
const trap = Mousetrap(this.$refs.contextMenu);
trap.bind(["up", "down"], (e, key) => {
if (!this.isOpen) {
return;
}
const items = this.$refs.contextMenu.querySelectorAll(".context-menu-item");
let index = Array.from(items).findIndex((item) => item === document.activeElement);
if (key === "down") {
index = (index + 1) % items.length;
} else {
index = Math.max(index, 0) - 1;
if (index < 0) {
index = items.length + index;
}
}
items[index].focus();
});
trap.bind("enter", () => {
if (!this.isOpen) {
return;
}
const item = this.$refs.contextMenu.querySelector(":focus");
item.click();
return false;
});
Mousetrap.bind(["up", "down", "tab", "shift+tab"], this.navigateMenu);
},
destroyed() {
Mousetrap.unbind("esc", this.close);
Mousetrap.unbind(["up", "down", "tab", "shift+tab"], this.navigateMenu);
},
methods: {
open(event, items) {
this.items = items;
this.isOpen = true;
event.preventDefault();
this.previousActiveElement = document.activeElement;
this.items = items;
this.activeItem = 0;
this.isOpen = true;
// Position the menu and set the focus on the first item after it's size has updated
this.$nextTick(() => {
const pos = this.positionContextMenu(event);
this.style.left = pos.left + "px";
this.style.top = pos.top + "px";
this.$refs.contextMenu.querySelector(".context-menu-item:first-child").focus();
this.$refs.contextMenu.focus();
});
},
close() {
@ -98,17 +84,59 @@ export default {
}
this.isOpen = false;
this.items = [];
if (this.previousActiveElement) {
this.previousActiveElement.focus();
this.previousActiveElement = null;
}
},
hoverItem(id) {
this.activeItem = id;
},
clickItem(item) {
if (item.action) {
item.action();
this.close();
} else if (item.link) {
this.$router.push(item.link);
this.close();
}
},
clickActiveItem() {
if (this.items[this.activeItem]) {
this.clickItem(this.items[this.activeItem]);
}
},
navigateMenu(event, key) {
event.preventDefault();
const direction = key === "down" || key === "tab" ? 1 : -1;
let currentIndex = this.activeItem;
currentIndex += direction;
const nextItem = this.items[currentIndex];
// If the next item we would select is a divider, skip over it
if (nextItem && !nextItem.action && !nextItem.link) {
currentIndex += direction;
}
if (currentIndex < 0) {
currentIndex += this.items.length;
}
if (currentIndex > this.items.length - 1) {
currentIndex -= this.items.length;
}
this.activeItem = currentIndex;
},
containerClick(event) {
if (event.currentTarget === event.target) {
this.close();
}
},
positionContextMenu(event) {

View file

@ -4,6 +4,7 @@
:data-name="user.nick"
role="button"
v-on="onHover ? {mouseover: hover} : {}"
@click.prevent="rightClick($event)"
@contextmenu.prevent="rightClick($event)"
>{{ user.mode }}{{ user.nick }}</span
>

View file

@ -4,6 +4,7 @@
:data-name="user.original.nick"
role="button"
@mouseover="hover"
@click.prevent="rightClick($event)"
@contextmenu.prevent="rightClick($event)"
v-html="user.original.mode + user.string"
/>

View file

@ -2088,6 +2088,7 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.15);
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 5px;
outline: 0;
}
.context-menu-divider {
@ -2109,15 +2110,13 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
border-radius: 3px;
}
.context-menu-item:focus,
.context-menu-item.active,
.textcomplete-item:focus,
.context-menu-item:hover,
.textcomplete-item:hover,
.textcomplete-menu .active,
#chat .userlist .user.active {
background-color: rgba(0, 0, 0, 0.1);
transition: none;
outline: 0;
}
.context-menu-item::before,