New support for notifications in webpanel in vue.js
This commit is contained in:
parent
e1c3951456
commit
444fe6331d
11 changed files with 260 additions and 70 deletions
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -86,3 +86,10 @@ $widths: (
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.horizontal-center {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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: ['[[',']]'],
|
||||||
|
|
63
platypush/backend/http/static/js/notifications.js
Normal file
63
platypush/backend/http/static/js/notifications.js
Normal 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);
|
||||||
|
}
|
||||||
|
|
|
@ -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' %}
|
||||||
|
|
41
platypush/backend/http/templates/notifications.html
Normal file
41
platypush/backend/http/templates/notifications.html
Normal 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>
|
||||||
|
|
Loading…
Reference in a new issue