192 lines
5.7 KiB
JavaScript
192 lines
5.7 KiB
JavaScript
import { Utility } from '../../core/utility';
|
|
import { EventManager, EventWrapper, EVENT_TYPE } from '../../lib/event-manager/event-manager';
|
|
import './modal.sass';
|
|
|
|
const MODAL_HEADERS = {
|
|
'Is-Modal': 'True',
|
|
};
|
|
|
|
const MODAL_INITIALIZED_CLASS = 'modal--initialized';
|
|
const MODAL_CLASS = 'modal';
|
|
const MODAL_OPEN_CLASS = 'modal--open';
|
|
const MODAL_TRIGGER_CLASS = 'modal__trigger';
|
|
const MODAL_CONTENT_CLASS = 'modal__content';
|
|
const MODAL_OVERLAY_CLASS = 'modal__overlay';
|
|
const MODAL_OVERLAY_OPEN_CLASS = 'modal__overlay--open';
|
|
const MODAL_CLOSER_CLASS = 'modal__closer';
|
|
|
|
const MAIN_CONTENT_CLASS = 'main__content-body';
|
|
|
|
// one singleton wrapper to keep all the modals to avoid CSS bug
|
|
// with blurry text due to `transform: translate(-50%, -50%)`
|
|
// will be created (and reused) for the first modal that gets initialized
|
|
const MODALS_WRAPPER_CLASS = 'modals-wrapper';
|
|
const MODALS_WRAPPER_SELECTOR = '.' + MODALS_WRAPPER_CLASS;
|
|
const MODALS_WRAPPER_OPEN_CLASS = 'modals-wrapper--open';
|
|
|
|
@Utility({
|
|
selector: '[uw-modal]',
|
|
})
|
|
export class Modal {
|
|
|
|
_eventManager
|
|
|
|
_element;
|
|
_app;
|
|
|
|
_modalsWrapper;
|
|
_modalOverlay;
|
|
_modalUrl;
|
|
_triggerElement;
|
|
_closerElement;
|
|
|
|
constructor(element, app) {
|
|
if (!element) {
|
|
throw new Error('Modal utility cannot be setup without an element!');
|
|
}
|
|
|
|
this._element = element;
|
|
this._app = app;
|
|
this._eventManager = new EventManager();
|
|
|
|
if (this._element.classList.contains(MODAL_INITIALIZED_CLASS)) {
|
|
return false;
|
|
}
|
|
|
|
this._ensureModalWrapper();
|
|
|
|
// param modalTrigger
|
|
if (!this._element.dataset.modalTrigger) {
|
|
throw new Error('Modal utility cannot be setup without a trigger element!');
|
|
} else {
|
|
this._setupTrigger();
|
|
}
|
|
|
|
// param modalCloseable
|
|
if (this._element.dataset.modalCloseable !== undefined) {
|
|
this._setupCloser();
|
|
}
|
|
|
|
// mark as initialized and add modal class for styling
|
|
this._element.classList.add(MODAL_INITIALIZED_CLASS, MODAL_CLASS);
|
|
}
|
|
|
|
destroy() {
|
|
throw new Error('Destroying modals is not possible.');
|
|
}
|
|
|
|
_ensureModalWrapper() {
|
|
this._modalsWrapper = document.querySelector(MODALS_WRAPPER_SELECTOR);
|
|
if (!this._modalsWrapper) {
|
|
// create modal wrapper
|
|
this._modalsWrapper = document.createElement('div');
|
|
this._modalsWrapper.classList.add(MODALS_WRAPPER_CLASS);
|
|
document.body.appendChild(this._modalsWrapper);
|
|
}
|
|
|
|
this._modalOverlay = this._modalsWrapper.querySelector('.' + MODAL_OVERLAY_CLASS);
|
|
if (!this._modalOverlay) {
|
|
// create modal overlay
|
|
this._modalOverlay = document.createElement('div');
|
|
this._modalOverlay.classList.add(MODAL_OVERLAY_CLASS);
|
|
this._modalsWrapper.appendChild(this._modalOverlay);
|
|
}
|
|
}
|
|
|
|
_setupTrigger() {
|
|
let triggerSelector = this._element.dataset.modalTrigger;
|
|
if (!triggerSelector.startsWith('#')) {
|
|
triggerSelector = '#' + triggerSelector;
|
|
}
|
|
this._triggerElement = document.querySelector(triggerSelector);
|
|
|
|
if (!this._triggerElement) {
|
|
throw new Error('Trigger element for Modal not found: "' + triggerSelector + '"');
|
|
}
|
|
|
|
this._triggerElement.classList.add(MODAL_TRIGGER_CLASS);
|
|
const triggerEvent = new EventWrapper(EVENT_TYPE.CLICK, this._onTriggerClicked.bind(this), this._triggerElement, false);
|
|
this._eventManager.registerNewListener(triggerEvent);
|
|
this._modalUrl = this._triggerElement.getAttribute('href');
|
|
}
|
|
|
|
_setupCloser() {
|
|
this._closerElement = document.createElement('div');
|
|
this._element.insertBefore(this._closerElement, null);
|
|
this._closerElement.classList.add(MODAL_CLOSER_CLASS);
|
|
const closerElEvent = new EventWrapper(EVENT_TYPE.CLICK, this._onCloseClicked.bind(this), this._closerElement, false);
|
|
this._eventManager.registerNewListener(closerElEvent);
|
|
const overlayClose = new EventWrapper(EVENT_TYPE.CLICK, this._onCloseClicked.bind(this), this._modalOverlay, false);
|
|
this._eventManager.registerNewListener(overlayClose);
|
|
}
|
|
|
|
_onTriggerClicked = (event) => {
|
|
event.preventDefault();
|
|
this._open();
|
|
}
|
|
|
|
_onCloseClicked = (event) => {
|
|
event.preventDefault();
|
|
this._close();
|
|
}
|
|
|
|
_onKeyUp = (event) => {
|
|
if (event.key === 'Escape') {
|
|
this._close();
|
|
}
|
|
}
|
|
|
|
_open() {
|
|
this._element.classList.add(MODAL_OPEN_CLASS);
|
|
this._modalOverlay.classList.add(MODAL_OVERLAY_OPEN_CLASS);
|
|
this._modalsWrapper.classList.add(MODALS_WRAPPER_OPEN_CLASS);
|
|
this._modalsWrapper.appendChild(this._element);
|
|
|
|
if (this._modalUrl) {
|
|
this._fillModal(this._modalUrl);
|
|
}
|
|
|
|
document.addEventListener('keyup', this._onKeyUp);
|
|
}
|
|
|
|
_close() {
|
|
this._modalOverlay.classList.remove(MODAL_OVERLAY_OPEN_CLASS);
|
|
this._element.classList.remove(MODAL_OPEN_CLASS);
|
|
this._modalsWrapper.classList.remove(MODALS_WRAPPER_OPEN_CLASS);
|
|
|
|
document.removeEventListener('keyup', this._onKeyUp);
|
|
};
|
|
|
|
_fillModal(url) {
|
|
this._app.httpClient.get({
|
|
url: url,
|
|
headers: MODAL_HEADERS,
|
|
}).then(
|
|
(response) => this._app.htmlHelpers.parseResponse(response)
|
|
).then(
|
|
(response) => this._processResponse(response.element)
|
|
);
|
|
}
|
|
|
|
_processResponse(responseElement) {
|
|
const modalContent = document.createElement('div');
|
|
modalContent.classList.add(MODAL_CONTENT_CLASS);
|
|
|
|
const contentBody = responseElement.querySelector('.' + MAIN_CONTENT_CLASS);
|
|
|
|
if (contentBody) {
|
|
modalContent.innerHTML = contentBody.innerHTML;
|
|
}
|
|
|
|
const previousModalContent = this._element.querySelector('.' + MODAL_CONTENT_CLASS);
|
|
if (previousModalContent) {
|
|
previousModalContent.remove();
|
|
}
|
|
|
|
this._element.insertBefore(modalContent, null);
|
|
|
|
// setup any newly arrived utils
|
|
this._app.utilRegistry.initAll(this._element);
|
|
}
|
|
}
|