New support for notifications in webpanel in vue.js

This commit is contained in:
Fabio Manganiello 2019-05-28 19:15:39 +02:00
parent c751a24790
commit 324ce7eaed
11 changed files with 260 additions and 70 deletions

View file

@ -0,0 +1,37 @@
.fade-in {
--duration: $fade-in-transition-duration;
opacity: 1;
animation-name: fadeInOpacity;
animation-iteration-count: 1;
animation-timing-function: ease-in;
animation-duration: var(--duration);
}
@keyframes fadeInOpacity {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.fade-out {
--duration: $fade-out-transition-duration;
opacity: 1;
animation-name: fadeOutOpacity;
animation-iteration-count: 1;
animation-timing-function: ease-out;
animation-duration: var(--duration);
}
@keyframes fadeOutOpacity {
0% {
opacity: 1;
}
100% {
opacity: 0;
display: none;
}
}

View file

@ -86,3 +86,10 @@ $widths: (
align-items: center; align-items: center;
} }
.horizontal-center {
display: flex;
justify-content: center;
margin-left: auto;
margin-right: auto;
}

View file

@ -1,65 +0,0 @@
#notification-container {
position: fixed;
bottom: 0;
right: 0;
width: 25em;
.notification {
background: rgba(180,245,188,0.85);
border: 1px solid #bbb;
border-radius: 5px;
margin-bottom: 1rem;
margin-right: 1rem;
cursor: pointer;
display: none;
}
.notification:hover {
background: rgba(160,235,168,0.9);
}
.notification-title {
padding: 4px 10px;
font-weight: bold;
text-transform: capitalize;
line-height: 30px;
letter-spacing: .1rem;
}
.notification-body {
height: 6em;
overflow: hidden;
padding-bottom: 1rem;
letter-spacing: .05rem;
}
.notification-text {
margin-left: 0 !important;
}
.notification-image {
height: 100%;
text-align: center;
padding-top: .6em;
padding-bottom: .6em;
.notification-image-item {
width: 100%;
height: 100%;
margin: auto;
}
.fa.notification-image-item {
margin-top: .8em;
margin-left: .2em;
font-size: 25px;
}
img {
width: 80%;
height: 80%;
}
}
}

View file

@ -0,0 +1,73 @@
#notifications {
position: fixed;
bottom: 0;
right: 0;
width: 25em;
.notification {
background: $notification-bg;
border: $notification-border;
border-radius: .5rem;
margin-bottom: 1rem;
margin-right: 1rem;
cursor: pointer;
&:hover {
background: $notification-hover-bg;
&.warning { background: $notification-warning-hover-bg; }
&.error { background: $notification-error-hover-bg; }
}
&.warning {
background: $notification-warning-bg;
border: $notification-warning-border;
.image { --color: $notification-warning-icon-color; }
}
&.error {
background: $notification-error-bg;
border: $notification-error-border;
.image { --color: $notification-error-icon-color; }
}
.title {
border-bottom: $notification-title-border;
padding: .4rem;
text-transform: uppercase;
line-height: 3rem;
letter-spacing: .1rem;
}
.body {
@extend .vertical-center;
height: 6em;
overflow: hidden;
padding-bottom: 1rem;
letter-spacing: .05rem;
}
.image {
height: 100%;
text-align: center;
--color: $notification-icon-color;
.row {
@extend .vertical-center;
@extend .horizontal-center;
width: 100%;
height: 100%;
.fa {
font-size: 2.5rem;
color: var(--color);
}
img {
width: 80%;
height: 80%;
}
}
}
}
}

View file

@ -19,6 +19,27 @@ $nav-date-time-shadow: 2px 2px 2px #ccc !default;
$modal-bg: #f0f0f0 !default; $modal-bg: #f0f0f0 !default;
//// Animations defaults
$transition-duration: .5s !default;
$fade-transition-duration: .5s !default;
$fade-in-transition-duration: $fade-transition-duration !default;
$fade-out-transition-duration: $fade-transition-duration !default;
//// Notifications
$notification-bg: rgba(185, 255, 193, 0.85) !default;
$notification-hover-bg: rgba(160,245,178,0.9) !default;
$notification-warning-bg: rgba(228, 255, 78, 0.85) !default;
$notification-warning-hover-bg: rgba(218, 245, 68, 0.9) !default;
$notification-error-bg: rgba(255, 100, 100, 0.85) !default;
$notification-error-hover-bg: rgba(245, 90, 90, 0.9) !default;
$notification-border: 1px solid rgba(109, 205, 134, 0.62) !default;
$notification-warning-border: 1px solid rgba(205, 205, 109, 0.62) !default;
$notification-error-border: 1px solid rgba(205, 109, 109, 0.62) !default;
$notification-title-border: 1px solid rgba(83, 158, 102, 0.43) !default;
$notification-icon-color: black !default;
$notification-warning-icon-color: #662 !default;
$notification-error-icon-color: #8b0000 !default;
//// Switch element //// Switch element
$switch-bg-1: #f9f8f6 !default; $switch-bg-1: #f9f8f6 !default;
$switch-bg-2: #38ffa0 !default; $switch-bg-2: #38ffa0 !default;

View file

@ -3,8 +3,9 @@
@import 'common/mixins'; @import 'common/mixins';
@import 'common/layout'; @import 'common/layout';
@import 'common/elements'; @import 'common/elements';
@import 'common/animations';
@import 'common/modal'; @import 'common/modal';
@import 'common/notification'; @import 'common/notifications';
@import 'header'; @import 'header';
@import 'nav'; @import 'nav';

View file

@ -22,12 +22,21 @@ function execute(request) {
if (!response.errors.length) { if (!response.errors.length) {
resolve(response.output); resolve(response.output);
} else { } else {
// TODO Handle error const error = response.errors[0];
reject(response.errors[0]); createNotification({
text: error,
error: true,
});
reject(error);
} }
}) })
.catch((error) => { .catch((error) => {
// TODO Handle error createNotification({
text: error,
error: true,
});
reject(error); reject(error);
}); });
}); });

View file

@ -26,7 +26,7 @@ Vue.component('plugin', {
}); });
// Declaration of the main vue app // Declaration of the main vue app
var app = new Vue({ window.vm = new Vue({
el: '#app', el: '#app',
// Override {{ }} delimiters to prevent clash with Flask templates // Override {{ }} delimiters to prevent clash with Flask templates
delimiters: ['[[',']]'], delimiters: ['[[',']]'],

View file

@ -0,0 +1,63 @@
Vue.component('notifications', {
template: '#tmpl-notifications',
props: {
duration: {
// Default notification duration in milliseconds
type: Number,
default: 10000,
}
},
data: function() {
return {
index: 0,
notifications: {},
timeouts: {},
};
},
methods: {
create: function(args) {
var id = this.index++;
Vue.set(this.notifications, id, args);
if (args.duration == null) {
args.duration = this.duration;
}
if (args.duration != 0) {
this.timeouts[id] = setTimeout(this.destroy.bind(null, id), args.duration);
}
},
destroy: function(id) {
Vue.delete(this.notifications, id);
delete this.timeouts[id];
},
},
});
Vue.component('notification', {
template: '#tmpl-notification',
props: ['id','text','html','title','image','link','error','warning'],
methods: {
mousein: function(event) {
},
mouseout: function(event) {
},
clicked: function(event) {
if (this.link) {
window.open(this.link, '_blank');
}
this.$emit('clicked', this.id);
},
},
});
function createNotification(args) {
window.vm.$refs.notifications.create(args);
}

View file

@ -14,6 +14,7 @@
<script type="text/javascript" src="{{ url_for('static', filename='js/api.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='js/api.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/events.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='js/events.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/notifications.js') }}"></script>
{% for style in styles.values() %} {% for style in styles.values() %}
<link rel="stylesheet" href="{{ url_for('static', filename=style['_style_file']) }}"> <link rel="stylesheet" href="{{ url_for('static', filename=style['_style_file']) }}">
@ -60,6 +61,8 @@
{% endfor %} {% endfor %}
</div> </div>
</main> </main>
{% include 'notifications.html' %}
</div> </div>
{% include 'plugins/template.html' %} {% include 'plugins/template.html' %}

View file

@ -0,0 +1,41 @@
<script type="text/x-template" id="tmpl-notifications">
<div id="notifications">
<notification v-for="(notification, id, index) in notifications"
:key="index"
:id="id"
:text="notification.text"
:html="notification.html"
:title="notification.title"
:link="notification.link"
:image="notification.image"
:warning="notification.warning"
:error="notification.error"
@clicked="destroy">
</notification>
</div>
</script>
<script type="text/x-template" id="tmpl-notification">
<div class="notification fade-in" :class="{warning: warning, error: error}"
@mouseover="mousein" @mouseleave="mouseout" @click="clicked">
<div class="title" v-if="title" v-text="title"></div>
<div class="body">
<div class="image col-3" v-if="image || warning || error">
<div class="row">
<img :src="image.src" v-if="image && image.src">
<i :class="['fa', 'fa-' + image.icon]" :style="image.color ? '--color: ' + image.color : ''"
v-else-if="image && image.icon"></i>
<i class="fa fa-exclamation" v-else-if="warning"></i>
<i class="fa fa-times" v-else-if="error"></i>
</div>
</div>
<div class="text col-9" v-if="text && !!image" v-text="text"></div>
<div class="text col-9" v-if="html && !!image" v-html="html"></div>
<div class="text row horizontal-center" v-if="text && !image" v-text="text"></div>
<div class="text row horizontal-center" v-if="html && !image" v-html="html"></div>
</div>
</div>
</script>
<notifications ref="notifications"></notifications>