mirror of https://github.com/Lissy93/dashy.git
Show opening method on hover. Allow items to be opened in an iframe
This commit is contained in:
parent
8ddc2506ac
commit
7fd36d9ec6
File diff suppressed because it is too large
Load Diff
|
@ -15,6 +15,7 @@
|
|||
"v-tooltip": "^2.1.3",
|
||||
"vue": "^2.6.10",
|
||||
"vue-cli-plugin-yaml": "^1.0.2",
|
||||
"vue-js-modal": "^2.0.0-rc.6",
|
||||
"vue-router": "^3.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -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 |
|
@ -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 |
|
@ -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 {
|
||||
font-size: 0.8rem;
|
||||
color: #5cabca;
|
||||
color: $ascent-with-opacity;
|
||||
width: 5.5rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.display-options {
|
||||
color: $ascent;
|
||||
color: $ascent-with-opacity;
|
||||
svg {
|
||||
path {
|
||||
fill: $ascent;
|
||||
fill: $ascent-with-opacity;
|
||||
}
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
|
@ -165,7 +165,7 @@ export default {
|
|||
opacity: 0.8;
|
||||
cursor: pointer;
|
||||
&:hover, &.selected {
|
||||
background: $ascent;
|
||||
background: $ascent-with-opacity;
|
||||
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>
|
|
@ -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>
|
||||
<a :href="url" :class="`item ${!icon? 'short': ''}`" v-on:click="$emit('itemClicked')"
|
||||
tabindex="0" target="_blank" rel="noopener noreferrer" v-tooltip="getTooltipOptions()">
|
||||
<a
|
||||
: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 -->
|
||||
<div class="tile-title" :id="`tile-${id}`">
|
||||
<span class="text">{{ title }}</span>
|
||||
|
@ -8,11 +15,22 @@
|
|||
</div>
|
||||
<!-- Item Icon -->
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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 {
|
||||
name: 'Item',
|
||||
|
@ -25,7 +43,7 @@ export default {
|
|||
svg: String, // Optional vector graphic, that is then dynamically filled
|
||||
color: String, // Optional background color, specified in hex code
|
||||
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,
|
||||
default: 'newtab',
|
||||
validator: (value) => ['newtab', 'sametab', 'iframe'].indexOf(value) !== -1,
|
||||
|
@ -38,11 +56,18 @@ export default {
|
|||
},
|
||||
components: {
|
||||
Icon,
|
||||
NewTabOpenIcon,
|
||||
SameTabOpenIcon,
|
||||
IframeOpenIcon,
|
||||
IframeModal,
|
||||
},
|
||||
methods: {
|
||||
/* Called when an item is opened, so that search field can be reset */
|
||||
itemOpened() {
|
||||
this.$emit('itemClicked');
|
||||
if (this.target === 'iframe') {
|
||||
this.$refs.iframeModal.show();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Detects overflowing text, shows ellipse, and allows is to marguee on hover
|
||||
|
@ -78,107 +103,128 @@ export default {
|
|||
@import '../../src/styles/color-pallet.scss';
|
||||
@import '../../src/styles/constants.scss';
|
||||
|
||||
/* Item wrapper */
|
||||
.item {
|
||||
flex-grow: 1;
|
||||
height: 100px;
|
||||
color: $ascent;
|
||||
vertical-align: middle;
|
||||
margin: 0.5rem;
|
||||
background: #607d8b33;
|
||||
text-align: center;
|
||||
padding: 2px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: $curve-factor;
|
||||
box-shadow: 1px 1px 2px #373737;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
box-shadow: 1px 2px 4px #373737;
|
||||
background: #607d8b4d;
|
||||
}
|
||||
&:focus {
|
||||
border: 2px solid $ascent;
|
||||
outline: none;
|
||||
}
|
||||
&.short {
|
||||
height: 18px;
|
||||
}
|
||||
flex-grow: 1;
|
||||
height: 100px;
|
||||
position: relative;
|
||||
color: $ascent;
|
||||
vertical-align: middle;
|
||||
margin: 0.5rem;
|
||||
background: #607d8b33;
|
||||
text-align: center;
|
||||
padding: 2px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: $curve-factor;
|
||||
box-shadow: 1px 1px 2px #373737;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
box-shadow: 1px 2px 4px #373737;
|
||||
background: #607d8b4d;
|
||||
}
|
||||
&:focus {
|
||||
border: 2px solid $ascent;
|
||||
outline: none;
|
||||
}
|
||||
&.short {
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Text in tile */
|
||||
.tile-title {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 120px;
|
||||
height: 30px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
span.text {
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 120px;
|
||||
height: 30px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
|
||||
transition: 1s;
|
||||
float: left;
|
||||
left: 0;
|
||||
}
|
||||
&:not(.is-overflowing) span.text{
|
||||
width: 100%;
|
||||
}
|
||||
.overflow-dots {
|
||||
opacity: 0;
|
||||
}
|
||||
&.is-overflowing {
|
||||
span.text {
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
transition: 1s;
|
||||
float: left;
|
||||
left: 0;
|
||||
}
|
||||
&:not(.is-overflowing) span.text{
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.overflow-dots {
|
||||
opacity: 0;
|
||||
display: block;
|
||||
opacity: 1;
|
||||
background: $overflow-ellipse;
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
right: 0;
|
||||
transition: opacity 0.1s ease-in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tile-title.is-overflowing {
|
||||
span.text {
|
||||
overflow: hidden;
|
||||
}
|
||||
.overflow-dots {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
background: $overflow-ellipse;
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
right: 0;
|
||||
transition: opacity 0.1s ease-in;
|
||||
}
|
||||
}
|
||||
/* Manage hover and focus actions */
|
||||
.item:hover, .item:focus {
|
||||
/* Show opening-method icon */
|
||||
.opening-method-icon svg {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.item:hover .tile-title.is-overflowing{
|
||||
/* Trigger text-marquee for text that doesn't fit */
|
||||
.tile-title.is-overflowing{
|
||||
.overflow-dots {
|
||||
opacity: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
span.text {
|
||||
transform: translateX(calc(120px - 100%));
|
||||
transform: translateX(calc(120px - 100%));
|
||||
}
|
||||
}
|
||||
|
||||
/* Colourize icons on hover */
|
||||
.tile-svg {
|
||||
filter: drop-shadow(4px 8px 3px $transparent-black);
|
||||
}
|
||||
.tile-icon {
|
||||
filter:
|
||||
drop-shadow(4px 8px 3px $transparent-black)
|
||||
saturate(2);
|
||||
}
|
||||
}
|
||||
|
||||
.tile-icon {
|
||||
width: 60px;
|
||||
filter: drop-shadow(2px 4px 6px $transparent-black) saturate(0.65);
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
.tile-svg {
|
||||
filter: drop-shadow(4px 8px 3px $transparent-black);
|
||||
}
|
||||
.tile-icon {
|
||||
filter:
|
||||
drop-shadow(4px 8px 3px $transparent-black)
|
||||
saturate(2);
|
||||
}
|
||||
width: 60px;
|
||||
filter: drop-shadow(2px 4px 6px $transparent-black) saturate(0.65);
|
||||
}
|
||||
|
||||
.tile-svg {
|
||||
width: 56px;
|
||||
filter:
|
||||
invert(69%)
|
||||
sepia(40%)
|
||||
saturate(4686%)
|
||||
hue-rotate(142deg)
|
||||
brightness(96%)
|
||||
contrast(102%);
|
||||
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>
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
:title="item.title"
|
||||
:description="item.description"
|
||||
:icon="item.icon"
|
||||
:iconType="item.iconType"
|
||||
:target="item.target"
|
||||
:svg="item.svg"
|
||||
@itemClicked="$emit('itemClicked')"
|
||||
/>
|
||||
|
|
|
@ -9,6 +9,7 @@ sections:
|
|||
description: Firewall Central Management
|
||||
icon: networking/opnsense.png
|
||||
iconType: img
|
||||
target: iframe
|
||||
url: https://192.168.1.1
|
||||
- title: NetData
|
||||
description: System resource usage on firewall
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import Vue from 'vue';
|
||||
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 router from './router';
|
||||
import './registerServiceWorker';
|
||||
|
||||
Vue.use(VTooltip);
|
||||
Vue.use(VModal);
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
new Vue({
|
||||
|
|
|
@ -12,4 +12,4 @@ $overflow-ellipse: #283e51;
|
|||
$bg-with-opacity: rgba($background, 0.8);
|
||||
$header-color: darken($background, 5%);
|
||||
$dark-ascent: darken($ascent, 50%);
|
||||
$ascent-with-opacity: rgba($ascent, 0.8);
|
||||
$ascent-with-opacity: rgba($ascent, 0.7);
|
Loading…
Reference in New Issue