fradrive/static/js/utils/modal.js
2019-05-15 23:22:21 +02:00

212 lines
6.1 KiB
JavaScript

(function() {
'use strict';
/**
*
* Modal Utility
*
* Attribute: uw-modal
*
* Params:
* data-modal-trigger: string
* Selector for the element that toggles the modal.
* If trigger element has "href" attribute the modal will be dynamically loaded from the referenced page
* data-modal-closeable: boolean property
* If the param is present the modal will have a close-icon and can also be closed by clicking anywhere on the overlay
*
* Example usage:
* <div uw-modal data-modal-trigger='#trigger' data-modal-closeable>This is the modal content
* <div id='trigger'>Click me to open the modal
*/
var MODAL_UTIL_NAME = 'modal';
var MODAL_UTIL_SELECTOR = '[uw-modal]';
var MODAL_HEADERS = {
'Is-Modal': 'True',
};
var MODAL_INITIALIZED_CLASS = 'modal--initialized';
var MODAL_CLASS = 'modal';
var MODAL_OPEN_CLASS = 'modal--open';
var MODAL_TRIGGER_CLASS = 'modal__trigger';
var MODAL_CONTENT_CLASS = 'modal__content';
var MODAL_OVERLAY_CLASS = 'modal__overlay';
var MODAL_OVERLAY_OPEN_CLASS = 'modal__overlay--open';
var MODAL_CLOSER_CLASS = 'modal__closer';
var 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
var MODALS_WRAPPER_CLASS = 'modals-wrapper';
var MODALS_WRAPPER_SELECTOR = '.' + MODALS_WRAPPER_CLASS;
var MODALS_WRAPPER_OPEN_CLASS = 'modals-wrapper--open';
var modalUtil = function(element) {
var modalsWrapper;
var modalOverlay;
var modalUrl;
function _init() {
console.log('modalUtil.init', { element });
if (!element) {
throw new Error('Modal utility cannot be setup without an element!');
}
if (element.classList.contains(MODAL_INITIALIZED_CLASS)) {
return false;
}
ensureModalWrapper();
// param modalTrigger
if (!element.dataset.modalTrigger) {
throw new Error('Modal utility cannot be setup without a trigger element!');
} else {
setupTrigger();
}
// param modalCloseable
if (element.dataset.modalCloseable !== undefined) {
setupCloser();
}
// mark as initialized and add modal class for styling
element.classList.add(MODAL_INITIALIZED_CLASS, MODAL_CLASS);
return {
name: MODAL_UTIL_NAME,
element: element,
destroy: function() {}
};
}
function ensureModalWrapper() {
modalsWrapper = document.querySelector(MODALS_WRAPPER_SELECTOR);
if (!modalsWrapper) {
// create modal wrapper
modalsWrapper = document.createElement('div');
modalsWrapper.classList.add(MODALS_WRAPPER_CLASS);
document.body.appendChild(modalsWrapper);
}
modalOverlay = modalsWrapper.querySelector('.' + MODAL_OVERLAY_CLASS);
if (!modalOverlay) {
// create modal overlay
modalOverlay = document.createElement('div');
modalOverlay.classList.add(MODAL_OVERLAY_CLASS);
modalsWrapper.appendChild(modalOverlay);
}
}
function setupTrigger() {
var triggerSelector = element.dataset.modalTrigger;
if (!triggerSelector.startsWith('#')) {
triggerSelector = '#' + triggerSelector;
}
var triggerElement = document.querySelector(triggerSelector);
if (!triggerElement) {
throw new Error('Trigger element for Modal not found: "' + triggerSelector + '"');
}
triggerElement.classList.add(MODAL_TRIGGER_CLASS);
triggerElement.addEventListener('click', onTriggerClicked, false);
modalUrl = triggerElement.getAttribute('href');
}
function setupCloser() {
var closerElement = document.createElement('div');
element.insertBefore(closerElement, null);
closerElement.classList.add(MODAL_CLOSER_CLASS);
closerElement.addEventListener('click', onCloseClicked, false);
modalOverlay.addEventListener('click', onCloseClicked, false);
}
function onTriggerClicked(event) {
event.preventDefault();
open();
}
function onCloseClicked(event) {
event.preventDefault();
close();
}
function onKeyUp(event) {
if (event.key === 'Escape') {
close();
}
}
function open() {
element.classList.add(MODAL_OPEN_CLASS);
modalOverlay.classList.add(MODAL_OVERLAY_OPEN_CLASS);
modalsWrapper.classList.add(MODALS_WRAPPER_OPEN_CLASS);
modalsWrapper.appendChild(element);
if (modalUrl) {
fillModal(modalUrl);
}
document.addEventListener('keyup', onKeyUp);
}
function close() {
modalOverlay.classList.remove(MODAL_OVERLAY_OPEN_CLASS);
element.classList.remove(MODAL_OPEN_CLASS);
modalsWrapper.classList.remove(MODALS_WRAPPER_OPEN_CLASS);
document.removeEventListener('keyup', onKeyUp);
};
function fillModal(url) {
if (!HttpClient) {
throw new Error('HttpClient not found! Can\'t fetch modal content from ' + url);
}
HttpClient.get({
url: url,
headers: MODAL_HEADERS,
accept: HttpClient.ACCEPT.TEXT_HTML,
}).then(function(response) {
return HtmlHelpers.parseResponse(response, element.id);
}).then(processResponse);
}
function processResponse(responseElement) {
var modalContent = document.createElement('div');
modalContent.classList.add(MODAL_CONTENT_CLASS);
var contentBody = responseElement.querySelector('.' + MAIN_CONTENT_CLASS);
if (contentBody) {
modalContent.innerHTML = contentBody.innerHTML;
}
var previousModalContent = element.querySelector('.' + MODAL_CONTENT_CLASS);
if (previousModalContent) {
previousModalContent.remove();
}
element.insertBefore(modalContent, null);
// setup any newly arrived utils
UtilRegistry.setupAll(element);
}
return _init();
};
if (UtilRegistry) {
UtilRegistry.register({
name: MODAL_UTIL_NAME,
selector: MODAL_UTIL_SELECTOR,
setup: modalUtil
});
}
})();