Show opening method on hover. Allow items to be opened in an iframe

This commit is contained in:
Alicia Sykes 2021-04-06 15:47:34 +01:00
parent 8ddc2506ac
commit 7fd36d9ec6
14 changed files with 9255 additions and 37157 deletions

27851
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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": {

View 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

View 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

View 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

View File

@ -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; }
}
}

View File

@ -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>

View 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>

View File

@ -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>

View File

@ -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')"
/>

View File

@ -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

View File

@ -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({

View File

@ -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);

18271
yarn.lock

File diff suppressed because it is too large Load Diff