🚧 WIP: Implementing right-click context menu

This commit is contained in:
Alicia Sykes 2021-06-24 20:55:54 +01:00
parent ea2b371f8e
commit 8890703598
2 changed files with 120 additions and 0 deletions

View File

@ -0,0 +1,85 @@
<template>
<transition name="slide">
<div class="context-menu" v-if="show"
:style="posX && posY ? `top:${posY}px;left:${posX}px;` : ''">
<ul>
<li><SameTabOpenIcon /> Open in Current Tab</li>
<li><NewTabOpenIcon /> Open in New Tab</li>
<li><IframeOpenIcon /> Open in Pop-Up Modal</li>
<li><WorkspaceOpenIcon /> Open in Workspace View </li>
</ul>
</div>
</transition>
</template>
<script>
// Import icons for each element
import SameTabOpenIcon from '@/assets/interface-icons/open-current-tab.svg';
import NewTabOpenIcon from '@/assets/interface-icons/open-new-tab.svg';
import IframeOpenIcon from '@/assets/interface-icons/open-iframe.svg';
import WorkspaceOpenIcon from '@/assets/interface-icons/open-workspace.svg';
export default {
name: 'ContextMenu',
inject: ['config'],
components: {
SameTabOpenIcon,
NewTabOpenIcon,
IframeOpenIcon,
WorkspaceOpenIcon,
},
props: {
posX: Number,
posY: Number,
show: Boolean,
},
};
</script>
<style lang="scss">
div.context-menu {
position: absolute;
margin: 0;
padding: 0;
z-index: 8;
background: var(--context-menu-background);
color: var(--context-menu-color);
border: 1px solid var(--context-menu-secondary-color);
border-radius: var(--curve-factor);
box-shadow: var(--context-menu-shadow);
opacity: 0.98;
ul {
list-style-type: none;
margin: 0;
padding: 0;
li {
cursor: pointer;
padding: 0.5rem 1rem;
display: flex;
flex-direction: row;
font-size: 1rem;
&:not(:last-child) {
border-bottom: 1px solid var(--context-menu-secondary-color);
}
&:hover {
background: var(--context-menu-secondary-color);
}
svg {
width: 1rem;
margin-right: 0.5rem;
path { fill: currentColor; }
}
}
}
}
// Define enter and leave transitions
.slide-enter-active { animation: slide-in .1s; }
.slide-leave-active { animation: slide-in .1s reverse; }
@keyframes slide-in {
0% { transform: scaleY(0.5) scaleX(0.8) translateY(-50px); }
100% { transform: scaleY(1) translateY(0) translateY(0); }
}
</style>

View File

@ -1,5 +1,8 @@
<template ref="container">
<div class="item-wrapper">
<a @click="itemOpened"
@mouseup.right="openContextMenu"
@contextmenu.prevent
:href="target !== 'iframe' ? url : '#'"
:target="target === 'newtab' ? '_blank' : ''"
:class="`item ${!icon? 'short': ''} size-${itemSize}`"
@ -19,6 +22,7 @@
<!-- Small icon, showing opening method on hover -->
<ItemOpenMethodIcon class="opening-method-icon" :isSmall="!icon" :openingMethod="target"
:position="itemSize === 'medium'? 'bottom right' : 'top right'"/>
<!-- Status indicator dot (if enabled) showing weather srevice is availible -->
<StatusIndicator
class="status-indicator"
v-if="enableStatusCheck"
@ -26,6 +30,14 @@
:statusText="statusResponse ? statusResponse.message : undefined"
/>
</a>
<ContextMenu
:show="contextMenuOpen"
v-click-outside="closeContextMenu"
:posX="contextPos.posX"
:posY="contextPos.posY"
:id="`context-menu-${id}`"
/>
</div>
</template>
<script>
@ -33,6 +45,7 @@ import axios from 'axios';
import Icon from '@/components/LinkItems/ItemIcon.vue';
import ItemOpenMethodIcon from '@/components/LinkItems/ItemOpenMethodIcon';
import StatusIndicator from '@/components/LinkItems/StatusIndicator';
import ContextMenu from '@/components/LinkItems/ContextMenu';
export default {
name: 'Item',
@ -56,18 +69,24 @@ export default {
},
data() {
return {
contextMenuOpen: false,
getId: this.id,
customStyles: {
color: this.color,
background: this.backgroundColor,
},
statusResponse: undefined,
contextPos: {
posX: undefined,
posY: undefined,
},
};
},
components: {
Icon,
ItemOpenMethodIcon,
StatusIndicator,
ContextMenu,
},
methods: {
/* Called when an item is clicked, manages the opening of iframe & resets the search field */
@ -79,6 +98,15 @@ export default {
this.$emit('itemClicked');
}
},
/* Open custom context menu, and set position */
openContextMenu(e) {
this.contextMenuOpen = !this.contextMenuOpen;
if (e) this.contextPos = { posX: e.clientX, posY: e.clientY };
},
/* Closes the context menu, called when user clicks literally anywhere */
closeContextMenu() {
this.contextMenuOpen = false;
},
/* Returns configuration object for the tooltip */
getTooltipOptions() {
return {
@ -101,6 +129,7 @@ export default {
default: return '"\\f054"';
}
},
/* Checks if a given service is currently online */
checkWebsiteStatus() {
this.statusResponse = undefined;
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
@ -118,7 +147,9 @@ export default {
},
},
mounted() {
// If ststus checking is enabled, then check service status
if (this.enableStatusCheck) this.checkWebsiteStatus();
// If continious status checking is enabled, then start ever-lasting loop
if (this.statusCheckInterval > 0) {
setInterval(this.checkWebsiteStatus, this.statusCheckInterval * 1000);
}
@ -128,6 +159,10 @@ export default {
<style lang="scss">
.item-wrapper {
flex-grow: 1;
}
.item {
flex-grow: 1;
color: var(--item-text-color);