Select Git revision
SystemNotification.vue
Forked from
Stud.IP / Stud.IP
Source project has a limited visibility.
-
Thomas Hackl authored
Closes #4504 Merge request studip/studip!3673
Thomas Hackl authoredCloses #4504 Merge request studip/studip!3673
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
SystemNotification.vue 5.82 KiB
<template>
<div v-cloak
class="system-notification"
:class="cssClasses"
@mouseover="disruptTimeout"
@mouseout="initTimeout"
@focus="disruptTimeout"
@blur="initTimeout"
>
<div class="system-notification-icon">
<studip-icon :shape="icon.shape"
:size="40"
:role="icon.color"
alt=""
title=""></studip-icon>
</div>
<div class="system-notification-content">
<p v-html="notification.message"></p>
<p class="sr-only" v-if="hasTimeout">
{{ $gettext('Strg+Alt+T hält das automatische Ausblenden der Meldung an bzw. setzt es wieder fort.') }}
</p>
<details v-if="notification.details?.length > 0"
class="system-notification-details"
:open="showDetails"
>
<summary @click="toggleDetails()">
{{ $gettext('Details') }}
</summary>
<template v-if="Array.isArray(notification.details)">
<p v-for="(detail, index) in notification.details"
:key="index"
v-html="detail"></p>
</template>
<p v-else v-html="notification.details"></p>
</details>
</div>
<button v-if="allowClosing"
class="system-notification-close undecorated"
:title="$gettext('Diese Meldung schließen')"
@click.prevent="destroyMe"
@keydown.space="destroyMe"
tabindex="0">
<studip-icon shape="decline"
:role="icon.color"
:size="20"
class="close-system-notification"/>
</button>
<transition v-if="hasTimeout"
name="system-notification-timeout"
appear
>
<div v-if="!stopTimeout"
class="system-notification-timeout"
ref="timeout-counter"></div>
</transition>
</div>
</template>
<script>
export default {
name: 'SystemNotification',
props: {
allowClosing: {
type: Boolean,
default: true
},
appendTo: {
type: String,
default: null
},
notification: {
type: Object,
required: true
},
visibleFor: {
type: Number,
default: 5000
},
placement: {
type: String,
default: 'topcenter'
}
},
data() {
return {
showDetails: !this.notification.close_details,
stopTimeout: false,
timeout: null,
windowIsBlurred: false,
}
},
computed: {
cssClasses() {
const classes = [`system-notification-${this.notification.type}`];
if (this.isDisrupted) {
classes.push('system-notification-disrupted');
}
return classes;
},
hasTimeout() {
return !['exception', 'error'].includes(this.notification.type);
},
icon() {
let iconShape = 'info-circle';
let iconColor = this.notification.type === 'warning' ? 'info' : 'info_alt';
return {shape: iconShape, color: iconColor};
},
isDisrupted() {
return this.timeout !== null && this.stopTimeout;
}
},
methods: {
destroyMe() {
this.$emit('destroyMe');
},
disruptTimeout() {
this.stopTimeout = true;
clearTimeout(this.timeout);
},
initTimeout() {
if (this.hasTimeout && this.visibleFor > 0) {
this.stopTimeout = false;
this.timeout = setTimeout(
() => this.destroyMe(),
this.visibleFor
);
}
},
toggleDetails() {
this.showDetails = !this.showDetails;
}
},
mounted() {
if (this.appendTo !== null) {
const target = document.querySelector(this.appendTo);
// Create a live area for screen reader compatibility.
const div = document.createElement('div');
div.setAttribute('role', 'alert');
div.appendChild(this.$el);
if (target) {
target.prepend(div);
}
}
this.initTimeout();
this.globalOn('disrupt-system-notifications', this.disruptTimeout);
this.globalOn('resume-system-notifications', this.initTimeout);
if (!STUDIP.config?.PERSONAL_NOTIFICATIONS_AUDIO_DEACTIVATED) {
let audio = null;
switch (this.notification.type) {
case 'info':
audio = new Audio(STUDIP.ASSETS_URL + '/sounds/notify-ok.mp3');
break;
case 'success':
audio = new Audio(STUDIP.ASSETS_URL + '/sounds/notify-good.mp3');
break;
case 'warning':
case 'error':
case 'exception':
audio = new Audio(STUDIP.ASSETS_URL + '/sounds/notify-bad.mp3');
break;
}
let timing = 300;
if (this.placement === 'bottomright') {
timing = 750;
}
setTimeout(() => {
audio.play().catch(error => {});
}, timing);
}
},
destroyed() {
this.globalOff('disrupt-system-notifications', this.disruptTimeout);
this.globalOff('resume-system-notifications', this.initTimeout);
}
}
</script>
<style scoped>
[v-cloak] {
display: none;
}
</style>