mirror of
https://github.com/Lissy93/dashy.git
synced 2025-07-29 08:34:59 +02:00
🚧 WIP: Implementing right-click context menu
This commit is contained in:
parent
ea2b371f8e
commit
8890703598
85
src/components/LinkItems/ContextMenu.vue
Normal file
85
src/components/LinkItems/ContextMenu.vue
Normal 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>
|
@ -1,5 +1,8 @@
|
|||||||
<template ref="container">
|
<template ref="container">
|
||||||
|
<div class="item-wrapper">
|
||||||
<a @click="itemOpened"
|
<a @click="itemOpened"
|
||||||
|
@mouseup.right="openContextMenu"
|
||||||
|
@contextmenu.prevent
|
||||||
:href="target !== 'iframe' ? url : '#'"
|
:href="target !== 'iframe' ? url : '#'"
|
||||||
:target="target === 'newtab' ? '_blank' : ''"
|
:target="target === 'newtab' ? '_blank' : ''"
|
||||||
:class="`item ${!icon? 'short': ''} size-${itemSize}`"
|
:class="`item ${!icon? 'short': ''} size-${itemSize}`"
|
||||||
@ -19,6 +22,7 @@
|
|||||||
<!-- Small icon, showing opening method on hover -->
|
<!-- Small icon, showing opening method on hover -->
|
||||||
<ItemOpenMethodIcon class="opening-method-icon" :isSmall="!icon" :openingMethod="target"
|
<ItemOpenMethodIcon class="opening-method-icon" :isSmall="!icon" :openingMethod="target"
|
||||||
:position="itemSize === 'medium'? 'bottom right' : 'top right'"/>
|
:position="itemSize === 'medium'? 'bottom right' : 'top right'"/>
|
||||||
|
<!-- Status indicator dot (if enabled) showing weather srevice is availible -->
|
||||||
<StatusIndicator
|
<StatusIndicator
|
||||||
class="status-indicator"
|
class="status-indicator"
|
||||||
v-if="enableStatusCheck"
|
v-if="enableStatusCheck"
|
||||||
@ -26,6 +30,14 @@
|
|||||||
:statusText="statusResponse ? statusResponse.message : undefined"
|
:statusText="statusResponse ? statusResponse.message : undefined"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
<ContextMenu
|
||||||
|
:show="contextMenuOpen"
|
||||||
|
v-click-outside="closeContextMenu"
|
||||||
|
:posX="contextPos.posX"
|
||||||
|
:posY="contextPos.posY"
|
||||||
|
:id="`context-menu-${id}`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -33,6 +45,7 @@ import axios from 'axios';
|
|||||||
import Icon from '@/components/LinkItems/ItemIcon.vue';
|
import Icon from '@/components/LinkItems/ItemIcon.vue';
|
||||||
import ItemOpenMethodIcon from '@/components/LinkItems/ItemOpenMethodIcon';
|
import ItemOpenMethodIcon from '@/components/LinkItems/ItemOpenMethodIcon';
|
||||||
import StatusIndicator from '@/components/LinkItems/StatusIndicator';
|
import StatusIndicator from '@/components/LinkItems/StatusIndicator';
|
||||||
|
import ContextMenu from '@/components/LinkItems/ContextMenu';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Item',
|
name: 'Item',
|
||||||
@ -56,18 +69,24 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
contextMenuOpen: false,
|
||||||
getId: this.id,
|
getId: this.id,
|
||||||
customStyles: {
|
customStyles: {
|
||||||
color: this.color,
|
color: this.color,
|
||||||
background: this.backgroundColor,
|
background: this.backgroundColor,
|
||||||
},
|
},
|
||||||
statusResponse: undefined,
|
statusResponse: undefined,
|
||||||
|
contextPos: {
|
||||||
|
posX: undefined,
|
||||||
|
posY: undefined,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Icon,
|
Icon,
|
||||||
ItemOpenMethodIcon,
|
ItemOpenMethodIcon,
|
||||||
StatusIndicator,
|
StatusIndicator,
|
||||||
|
ContextMenu,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/* Called when an item is clicked, manages the opening of iframe & resets the search field */
|
/* Called when an item is clicked, manages the opening of iframe & resets the search field */
|
||||||
@ -79,6 +98,15 @@ export default {
|
|||||||
this.$emit('itemClicked');
|
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 */
|
/* Returns configuration object for the tooltip */
|
||||||
getTooltipOptions() {
|
getTooltipOptions() {
|
||||||
return {
|
return {
|
||||||
@ -101,6 +129,7 @@ export default {
|
|||||||
default: return '"\\f054"';
|
default: return '"\\f054"';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/* Checks if a given service is currently online */
|
||||||
checkWebsiteStatus() {
|
checkWebsiteStatus() {
|
||||||
this.statusResponse = undefined;
|
this.statusResponse = undefined;
|
||||||
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
|
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
|
||||||
@ -118,7 +147,9 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
// If ststus checking is enabled, then check service status
|
||||||
if (this.enableStatusCheck) this.checkWebsiteStatus();
|
if (this.enableStatusCheck) this.checkWebsiteStatus();
|
||||||
|
// If continious status checking is enabled, then start ever-lasting loop
|
||||||
if (this.statusCheckInterval > 0) {
|
if (this.statusCheckInterval > 0) {
|
||||||
setInterval(this.checkWebsiteStatus, this.statusCheckInterval * 1000);
|
setInterval(this.checkWebsiteStatus, this.statusCheckInterval * 1000);
|
||||||
}
|
}
|
||||||
@ -128,6 +159,10 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
||||||
|
.item-wrapper {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
color: var(--item-text-color);
|
color: var(--item-text-color);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user