mirror of
https://github.com/Lissy93/dashy.git
synced 2025-07-22 13:14:58 +02:00
Show opening method on hover. Allow items to be opened in an iframe
This commit is contained in:
parent
8ddc2506ac
commit
7fd36d9ec6
27851
package-lock.json
generated
27851
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@
|
|||||||
"v-tooltip": "^2.1.3",
|
"v-tooltip": "^2.1.3",
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.6.10",
|
||||||
"vue-cli-plugin-yaml": "^1.0.2",
|
"vue-cli-plugin-yaml": "^1.0.2",
|
||||||
|
"vue-js-modal": "^2.0.0-rc.6",
|
||||||
"vue-router": "^3.0.3"
|
"vue-router": "^3.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
1
src/assets/icons/open-current-tab.svg
Normal file
1
src/assets/icons/open-current-tab.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="window-maximize" class="svg-inline--fa fa-window-maximize fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M464 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h416c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm0 394c0 3.3-2.7 6-6 6H54c-3.3 0-6-2.7-6-6V192h416v234z"></path></svg>
|
After Width: | Height: | Size: 410 B |
1
src/assets/icons/open-iframe.svg
Normal file
1
src/assets/icons/open-iframe.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="clone" class="svg-inline--fa fa-clone fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M464 0c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48H176c-26.51 0-48-21.49-48-48V48c0-26.51 21.49-48 48-48h288M176 416c-44.112 0-80-35.888-80-80V128H48c-26.51 0-48 21.49-48 48v288c0 26.51 21.49 48 48 48h288c26.51 0 48-21.49 48-48v-48H176z"></path></svg>
|
After Width: | Height: | Size: 472 B |
1
src/assets/icons/open-new-tab.svg
Normal file
1
src/assets/icons/open-new-tab.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="external-link-alt" class="svg-inline--fa fa-external-link-alt fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M432,320H400a16,16,0,0,0-16,16V448H64V128H208a16,16,0,0,0,16-16V80a16,16,0,0,0-16-16H48A48,48,0,0,0,0,112V464a48,48,0,0,0,48,48H400a48,48,0,0,0,48-48V336A16,16,0,0,0,432,320ZM488,0h-128c-21.37,0-32.05,25.91-17,41l35.73,35.73L135,320.37a24,24,0,0,0,0,34L157.67,377a24,24,0,0,0,34,0L435.28,133.32,471,169c15,15,41,4.5,41-17V24A24,24,0,0,0,488,0Z"></path></svg>
|
After Width: | Height: | Size: 597 B |
@ -143,16 +143,16 @@ export default {
|
|||||||
|
|
||||||
span.options-label {
|
span.options-label {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
color: #5cabca;
|
color: $ascent-with-opacity;
|
||||||
width: 5.5rem;
|
width: 5.5rem;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.display-options {
|
.display-options {
|
||||||
color: $ascent;
|
color: $ascent-with-opacity;
|
||||||
svg {
|
svg {
|
||||||
path {
|
path {
|
||||||
fill: $ascent;
|
fill: $ascent-with-opacity;
|
||||||
}
|
}
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
@ -165,7 +165,7 @@ export default {
|
|||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover, &.selected {
|
&:hover, &.selected {
|
||||||
background: $ascent;
|
background: $ascent-with-opacity;
|
||||||
path { fill: $background; }
|
path { fill: $background; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="hello">
|
|
||||||
<h1>{{ msg }}</h1>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'HelloWorld',
|
|
||||||
props: {
|
|
||||||
msg: String,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped lang="scss">
|
|
||||||
h3 {
|
|
||||||
margin: 40px 0 0;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #42b983;
|
|
||||||
}
|
|
||||||
</style>
|
|
30
src/components/IframeModal.vue
Normal file
30
src/components/IframeModal.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<modal name="iframe-modal" :resizable="true"
|
||||||
|
:adaptive="true" width="80%" height="80%">
|
||||||
|
<iframe :src="url" class="frame" />
|
||||||
|
</modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'IframeModal',
|
||||||
|
props: {
|
||||||
|
url: String,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
show: function show() {
|
||||||
|
this.$modal.show('iframe-modal');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
.frame {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -1,6 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<a :href="url" :class="`item ${!icon? 'short': ''}`" v-on:click="$emit('itemClicked')"
|
<a
|
||||||
tabindex="0" target="_blank" rel="noopener noreferrer" v-tooltip="getTooltipOptions()">
|
:href="target !== 'iframe' ? url : '#'"
|
||||||
|
v-on:click="itemOpened()"
|
||||||
|
:class="`item ${!icon? 'short': ''}`"
|
||||||
|
v-tooltip="getTooltipOptions()"
|
||||||
|
:target="target === 'newtab' ? '_blank' : ''"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
<!-- Item Text -->
|
<!-- Item Text -->
|
||||||
<div class="tile-title" :id="`tile-${id}`">
|
<div class="tile-title" :id="`tile-${id}`">
|
||||||
<span class="text">{{ title }}</span>
|
<span class="text">{{ title }}</span>
|
||||||
@ -8,11 +15,22 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Item Icon -->
|
<!-- Item Icon -->
|
||||||
<Icon :icon="icon" :url="url" />
|
<Icon :icon="icon" :url="url" />
|
||||||
|
<div :class="`opening-method-icon ${!icon? 'short': ''}`">
|
||||||
|
<NewTabOpenIcon v-if="target === 'newtab'" />
|
||||||
|
<SameTabOpenIcon v-else-if="target === 'sametab'" />
|
||||||
|
<IframeOpenIcon v-else-if="target === 'iframe'" />
|
||||||
|
</div>
|
||||||
|
<IframeModal v-if="target === 'iframe'" :url="url" ref="iframeModal"/>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Icon from '@/components/ItemIcon.vue';
|
import Icon from '@/components/ItemIcon.vue';
|
||||||
|
import IframeModal from '@/components/IframeModal.vue';
|
||||||
|
|
||||||
|
import NewTabOpenIcon from '@/assets/icons/open-new-tab.svg';
|
||||||
|
import SameTabOpenIcon from '@/assets/icons/open-current-tab.svg';
|
||||||
|
import IframeOpenIcon from '@/assets/icons/open-iframe.svg';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Item',
|
name: 'Item',
|
||||||
@ -25,7 +43,7 @@ export default {
|
|||||||
svg: String, // Optional vector graphic, that is then dynamically filled
|
svg: String, // Optional vector graphic, that is then dynamically filled
|
||||||
color: String, // Optional background color, specified in hex code
|
color: String, // Optional background color, specified in hex code
|
||||||
url: String, // URL to the resource, optional but recommended
|
url: String, // URL to the resource, optional but recommended
|
||||||
openingMethod: { // Where resource will open, either 'newtab', 'sametab' or 'iframe'
|
target: { // Where resource will open, either 'newtab', 'sametab' or 'iframe'
|
||||||
type: String,
|
type: String,
|
||||||
default: 'newtab',
|
default: 'newtab',
|
||||||
validator: (value) => ['newtab', 'sametab', 'iframe'].indexOf(value) !== -1,
|
validator: (value) => ['newtab', 'sametab', 'iframe'].indexOf(value) !== -1,
|
||||||
@ -38,11 +56,18 @@ export default {
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Icon,
|
Icon,
|
||||||
|
NewTabOpenIcon,
|
||||||
|
SameTabOpenIcon,
|
||||||
|
IframeOpenIcon,
|
||||||
|
IframeModal,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/* Called when an item is opened, so that search field can be reset */
|
/* Called when an item is opened, so that search field can be reset */
|
||||||
itemOpened() {
|
itemOpened() {
|
||||||
this.$emit('itemClicked');
|
this.$emit('itemClicked');
|
||||||
|
if (this.target === 'iframe') {
|
||||||
|
this.$refs.iframeModal.show();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Detects overflowing text, shows ellipse, and allows is to marguee on hover
|
* Detects overflowing text, shows ellipse, and allows is to marguee on hover
|
||||||
@ -78,9 +103,11 @@ export default {
|
|||||||
@import '../../src/styles/color-pallet.scss';
|
@import '../../src/styles/color-pallet.scss';
|
||||||
@import '../../src/styles/constants.scss';
|
@import '../../src/styles/constants.scss';
|
||||||
|
|
||||||
|
/* Item wrapper */
|
||||||
.item {
|
.item {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
|
position: relative;
|
||||||
color: $ascent;
|
color: $ascent;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin: 0.5rem;
|
margin: 0.5rem;
|
||||||
@ -103,6 +130,8 @@ export default {
|
|||||||
height: 18px;
|
height: 18px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Text in tile */
|
||||||
.tile-title {
|
.tile-title {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -112,7 +141,6 @@ export default {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
span.text {
|
span.text {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -126,9 +154,7 @@ export default {
|
|||||||
.overflow-dots {
|
.overflow-dots {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
&.is-overflowing {
|
||||||
|
|
||||||
.tile-title.is-overflowing {
|
|
||||||
span.text {
|
span.text {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@ -141,23 +167,27 @@ export default {
|
|||||||
right: 0;
|
right: 0;
|
||||||
transition: opacity 0.1s ease-in;
|
transition: opacity 0.1s ease-in;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item:hover .tile-title.is-overflowing{
|
/* Manage hover and focus actions */
|
||||||
|
.item:hover, .item:focus {
|
||||||
|
/* Show opening-method icon */
|
||||||
|
.opening-method-icon svg {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trigger text-marquee for text that doesn't fit */
|
||||||
|
.tile-title.is-overflowing{
|
||||||
.overflow-dots {
|
.overflow-dots {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
span.text {
|
span.text {
|
||||||
transform: translateX(calc(120px - 100%));
|
transform: translateX(calc(120px - 100%));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tile-icon {
|
/* Colourize icons on hover */
|
||||||
width: 60px;
|
|
||||||
filter: drop-shadow(2px 4px 6px $transparent-black) saturate(0.65);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item:hover {
|
|
||||||
.tile-svg {
|
.tile-svg {
|
||||||
filter: drop-shadow(4px 8px 3px $transparent-black);
|
filter: drop-shadow(4px 8px 3px $transparent-black);
|
||||||
}
|
}
|
||||||
@ -168,17 +198,33 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tile-svg {
|
.tile-icon {
|
||||||
width: 56px;
|
width: 60px;
|
||||||
filter:
|
filter: drop-shadow(2px 4px 6px $transparent-black) saturate(0.65);
|
||||||
invert(69%)
|
|
||||||
sepia(40%)
|
|
||||||
saturate(4686%)
|
|
||||||
hue-rotate(142deg)
|
|
||||||
brightness(96%)
|
|
||||||
contrast(102%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tile-svg {
|
||||||
|
width: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Opening-method icon */
|
||||||
|
.opening-method-icon {
|
||||||
|
svg {
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
width: 1rem;
|
||||||
|
margin: 2px;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
path {
|
||||||
|
fill: $ascent-with-opacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.short svg {
|
||||||
|
width: 0.5rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
:title="item.title"
|
:title="item.title"
|
||||||
:description="item.description"
|
:description="item.description"
|
||||||
:icon="item.icon"
|
:icon="item.icon"
|
||||||
:iconType="item.iconType"
|
:target="item.target"
|
||||||
:svg="item.svg"
|
:svg="item.svg"
|
||||||
@itemClicked="$emit('itemClicked')"
|
@itemClicked="$emit('itemClicked')"
|
||||||
/>
|
/>
|
||||||
|
@ -9,6 +9,7 @@ sections:
|
|||||||
description: Firewall Central Management
|
description: Firewall Central Management
|
||||||
icon: networking/opnsense.png
|
icon: networking/opnsense.png
|
||||||
iconType: img
|
iconType: img
|
||||||
|
target: iframe
|
||||||
url: https://192.168.1.1
|
url: https://192.168.1.1
|
||||||
- title: NetData
|
- title: NetData
|
||||||
description: System resource usage on firewall
|
description: System resource usage on firewall
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import VTooltip from 'v-tooltip'; // A Vue directive for Popper.js
|
import VTooltip from 'v-tooltip'; // A Vue directive for Popper.js
|
||||||
|
import VModal from 'vue-js-modal'; // Modal component
|
||||||
|
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import './registerServiceWorker';
|
import './registerServiceWorker';
|
||||||
|
|
||||||
Vue.use(VTooltip);
|
Vue.use(VTooltip);
|
||||||
|
Vue.use(VModal);
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
|
@ -12,4 +12,4 @@ $overflow-ellipse: #283e51;
|
|||||||
$bg-with-opacity: rgba($background, 0.8);
|
$bg-with-opacity: rgba($background, 0.8);
|
||||||
$header-color: darken($background, 5%);
|
$header-color: darken($background, 5%);
|
||||||
$dark-ascent: darken($ascent, 50%);
|
$dark-ascent: darken($ascent, 50%);
|
||||||
$ascent-with-opacity: rgba($ascent, 0.8);
|
$ascent-with-opacity: rgba($ascent, 0.7);
|
Loading…
x
Reference in New Issue
Block a user