171 lines
4.8 KiB
JavaScript
171 lines
4.8 KiB
JavaScript
import { Utility } from '../../core/utility';
|
|
import './alerts.sass';
|
|
|
|
const ALERTS_INITIALIZED_CLASS = 'alerts--initialized';
|
|
const ALERTS_ELEVATED_CLASS = 'alerts--elevated';
|
|
const ALERTS_TOGGLER_CLASS = 'alerts__toggler';
|
|
const ALERTS_TOGGLER_VISIBLE_CLASS = 'alerts__toggler--visible';
|
|
const ALERTS_TOGGLER_APPEAR_DELAY = 120;
|
|
|
|
const ALERT_CLASS = 'alert';
|
|
const ALERT_INITIALIZED_CLASS = 'alert--initialized';
|
|
const ALERT_CLOSER_CLASS = 'alert__closer';
|
|
const ALERT_ICON_CLASS = 'alert__icon';
|
|
const ALERT_CONTENT_CLASS = 'alert__content';
|
|
const ALERT_INVISIBLE_CLASS = 'alert--invisible';
|
|
const ALERT_AUTO_HIDE_DELAY = 10;
|
|
const ALERT_AUTOCLOSING_MATCHER = '.alert-info, .alert-success';
|
|
|
|
/*
|
|
* Dataset-Inputs:
|
|
* - decay (data-decay): Custom time (in seconds) for this alert to stay visible
|
|
*/
|
|
|
|
@Utility({
|
|
selector: '[uw-alerts]',
|
|
})
|
|
export class Alerts {
|
|
_togglerCheckRequested = false;
|
|
_togglerElement;
|
|
_alertElements;
|
|
|
|
_element;
|
|
_app;
|
|
|
|
constructor(element, app) {
|
|
if (!element) {
|
|
throw new Error('Alerts util has to be called with an element!');
|
|
}
|
|
|
|
this._element = element;
|
|
this._app = app;
|
|
|
|
if (this._element.classList.contains(ALERTS_INITIALIZED_CLASS)) {
|
|
return false;
|
|
}
|
|
|
|
this._togglerElement = this._element.querySelector('.' + ALERTS_TOGGLER_CLASS);
|
|
this._alertElements = this._gatherAlertElements();
|
|
|
|
if (this._togglerElement) {
|
|
this._initToggler();
|
|
}
|
|
|
|
this._initAlerts();
|
|
|
|
// register http client interceptor to filter out Alerts Header
|
|
this._setupHttpInterceptor();
|
|
|
|
// mark initialized
|
|
this._element.classList.add(ALERTS_INITIALIZED_CLASS);
|
|
}
|
|
|
|
destroy() {
|
|
console.log('TBD: Destroy Alert');
|
|
}
|
|
|
|
_gatherAlertElements() {
|
|
return Array.from(this._element.querySelectorAll('.' + ALERT_CLASS)).filter(function(alert) {
|
|
return !alert.classList.contains(ALERT_INITIALIZED_CLASS);
|
|
});
|
|
}
|
|
|
|
_initToggler() {
|
|
this._togglerElement.addEventListener('click', () => {
|
|
this._alertElements.forEach((alertEl) => this._toggleAlert(alertEl, true));
|
|
this._togglerElement.classList.remove(ALERTS_TOGGLER_VISIBLE_CLASS);
|
|
});
|
|
}
|
|
|
|
_initAlerts() {
|
|
this._alertElements.forEach((alert) => this._initAlert(alert));
|
|
}
|
|
|
|
_initAlert(alertElement) {
|
|
let autoHideDelay = ALERT_AUTO_HIDE_DELAY;
|
|
if (alertElement.dataset.decay) {
|
|
autoHideDelay = parseInt(alertElement.dataset.decay, 10);
|
|
}
|
|
|
|
const closeEl = alertElement.querySelector('.' + ALERT_CLOSER_CLASS);
|
|
closeEl.addEventListener('click', () => {
|
|
this._toggleAlert(alertElement);
|
|
});
|
|
|
|
if (autoHideDelay > 0 && alertElement.matches(ALERT_AUTOCLOSING_MATCHER)) {
|
|
window.setTimeout(() => this._toggleAlert(alertElement), autoHideDelay * 1000);
|
|
}
|
|
}
|
|
|
|
_toggleAlert(alertEl, visible) {
|
|
alertEl.classList.toggle(ALERT_INVISIBLE_CLASS, !visible);
|
|
this._checkToggler();
|
|
}
|
|
|
|
_checkToggler() {
|
|
if (this._togglerCheckRequested) {
|
|
return;
|
|
}
|
|
|
|
const alertsHidden = this._alertElements.reduce(function(acc, alert) {
|
|
return acc && alert.classList.contains(ALERT_INVISIBLE_CLASS);
|
|
}, true);
|
|
|
|
window.setTimeout(() => {
|
|
this._togglerElement.classList.toggle(ALERTS_TOGGLER_VISIBLE_CLASS, alertsHidden);
|
|
this._togglerCheckRequested = false;
|
|
}, ALERTS_TOGGLER_APPEAR_DELAY);
|
|
}
|
|
|
|
_setupHttpInterceptor() {
|
|
this._app.httpClient.addResponseInterceptor(this._responseInterceptor.bind(this));
|
|
}
|
|
|
|
_elevateAlerts() {
|
|
this._element.classList.add(ALERTS_ELEVATED_CLASS);
|
|
}
|
|
|
|
_responseInterceptor = (response) => {
|
|
let alerts;
|
|
for (const header of response.headers) {
|
|
if (header[0] === 'alerts') {
|
|
const decodedHeader = decodeURIComponent(header[1]);
|
|
alerts = JSON.parse(decodedHeader);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (alerts) {
|
|
alerts.forEach((alert) => {
|
|
const alertElement = this._createAlertElement(alert.status, alert.content, alert.icon === null ? undefined : alert.icon);
|
|
this._element.appendChild(alertElement);
|
|
this._alertElements.push(alertElement);
|
|
this._initAlert(alertElement);
|
|
});
|
|
|
|
this._elevateAlerts();
|
|
}
|
|
}
|
|
|
|
_createAlertElement(type, content, icon = 'info-circle') {
|
|
const alertElement = document.createElement('div');
|
|
alertElement.classList.add(ALERT_CLASS, 'alert-' + type);
|
|
|
|
const alertCloser = document.createElement('div');
|
|
alertCloser.classList.add(ALERT_CLOSER_CLASS);
|
|
|
|
const alertIcon = document.createElement('div');
|
|
alertIcon.classList.add(ALERT_ICON_CLASS, 'fas', 'fa-' + icon);
|
|
|
|
const alertContent = document.createElement('div');
|
|
alertContent.classList.add(ALERT_CONTENT_CLASS);
|
|
alertContent.innerHTML = content;
|
|
|
|
alertElement.appendChild(alertCloser);
|
|
alertElement.appendChild(alertIcon);
|
|
alertElement.appendChild(alertContent);
|
|
|
|
return alertElement;
|
|
}
|
|
}
|