refactor JS modal utility to work with new util registry
This commit is contained in:
parent
4161af4742
commit
ffef0b94bc
@ -1076,7 +1076,8 @@ siteLayout' headingOverride widget = do
|
||||
-- addScript $ StaticR js_utils_httpClient_js
|
||||
-- addScript $ StaticR js_utils_form_js
|
||||
-- addScript $ StaticR js_utils_inputs_js
|
||||
-- addScript $ StaticR js_utils_modal_js
|
||||
-- JavaScript utils
|
||||
addScript $ StaticR js_utils_modal_js
|
||||
addScript $ StaticR js_utils_poc_js
|
||||
addScript $ StaticR js_utils_showHide_js
|
||||
-- addScript $ StaticR js_utils_tabber_js
|
||||
|
||||
@ -14,9 +14,6 @@
|
||||
padding: 0 65px 0 20px;
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
transition:
|
||||
opacity .2s .1s ease-in-out,
|
||||
transform .3s ease-in-out;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
|
||||
@ -25,6 +22,9 @@
|
||||
pointer-events: auto;
|
||||
z-index: 200;
|
||||
transform: translate(-50%, -50%) scale(1, 1);
|
||||
transition:
|
||||
opacity .2s .1s ease-in-out,
|
||||
transform .3s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,169 +1,192 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
window.utils = window.utils || {};
|
||||
// ########################
|
||||
// TODO: make use of selector
|
||||
// or think of a different way to dynamically initialize widgets
|
||||
// with selectors with specific ids like #modal-hident69
|
||||
//
|
||||
// Idee:
|
||||
// Alles wegschmeißen zu dynamischen IDs. Util init rein über Selector '[uw-...]'
|
||||
// bedarf Änderung in Templates.
|
||||
// Ausserdem müssen sich Utils bei Event 'util-setup-ready' registrieren als Util
|
||||
// utils.setup wird dann überflüssig, bzw. wird zu einer Registry / Controller
|
||||
// der die utils bei DomCOntentLoaded intialisiert.
|
||||
//
|
||||
// ########################
|
||||
var SELECTOR = '[uw-modal]';
|
||||
/**
|
||||
*
|
||||
* 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 JS_INITIALIZED_CLASS = 'js-modal-initialized';
|
||||
var MODAL_OPEN_CLASS = 'modal--open';
|
||||
var MODAL_TRIGGER_CLASS = 'modal__trigger';
|
||||
var MODAL_CONTENT_CLASS = 'modal__content';
|
||||
var MAIN_CONTENT_CLASS = 'main__content-body'
|
||||
var MODAL_CLOSABLE_FLAG = 'closeable';
|
||||
var MODAL_DYNAMIC_FLAG = 'dynamic';
|
||||
var MODAL_HEADERS = {
|
||||
'Is-Modal': 'True',
|
||||
};
|
||||
var OVERLAY_CLASS = 'modal__overlay';
|
||||
var OVERLAY_OPEN_CLASS = 'modal__overlay--open';
|
||||
var CLOSER_CLASS = 'modal__closer';
|
||||
|
||||
window.utils.modal = function(scope, options) {
|
||||
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';
|
||||
|
||||
if (!modalElement || modalElement.classList.contains(JS_INITIALIZED_CLASS)) {
|
||||
return;
|
||||
}
|
||||
var MAIN_CONTENT_CLASS = 'main__content-body'
|
||||
|
||||
var utilInstances = [];
|
||||
var modalUtil = function(element) {
|
||||
|
||||
var overlayElement = document.createElement('div');
|
||||
var closerElement = document.createElement('div');
|
||||
var triggerElement = document.querySelector('#' + modalElement.dataset.trigger);
|
||||
var modalUrl;
|
||||
|
||||
function setup() {
|
||||
document.body.insertBefore(modalElement, null);
|
||||
function _init() {
|
||||
if (!element) {
|
||||
throw new Error('Modal utility cannot be setup without an element!');
|
||||
}
|
||||
|
||||
setupForm();
|
||||
setupCloser();
|
||||
if (element.classList.contains(MODAL_INITIALIZED_CLASS)) {
|
||||
throw new Error('Modal utility already initialized!');
|
||||
}
|
||||
|
||||
triggerElement.classList.add(MODAL_TRIGGER_CLASS);
|
||||
triggerElement.addEventListener('click', openHandler, false);
|
||||
// param modalTrigger
|
||||
if (!element.dataset.modalTrigger) {
|
||||
throw new Error('Modal utility cannot be setup without a trigger element!');
|
||||
} else {
|
||||
setupTrigger();
|
||||
}
|
||||
|
||||
modalElement.classList.add(JS_INITIALIZED_CLASS);
|
||||
// param modalCloseable
|
||||
if (element.dataset.modalCloseable !== undefined) {
|
||||
setupCloser();
|
||||
}
|
||||
|
||||
// setupForm();
|
||||
|
||||
// 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 openHandler(event) {
|
||||
function setupTrigger() {
|
||||
var triggerElement = document.querySelector(element.dataset.modalTrigger);
|
||||
|
||||
if (!triggerElement) {
|
||||
throw new Error('Trigger element for Modal not found: "', + element.dataset.modalTrigger + '"');
|
||||
}
|
||||
|
||||
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);
|
||||
overlayElement.addEventListener('click', onCloseClicked, false);
|
||||
}
|
||||
|
||||
function onTriggerClicked(event) {
|
||||
event.preventDefault();
|
||||
open();
|
||||
}
|
||||
|
||||
function open() {
|
||||
modalElement.classList.add(MODAL_OPEN_CLASS);
|
||||
overlayElement.classList.add(OVERLAY_CLASS);
|
||||
document.body.insertBefore(overlayElement, modalElement);
|
||||
overlayElement.classList.add(OVERLAY_OPEN_CLASS);
|
||||
|
||||
var modalUrl = triggerElement.getAttribute('href');
|
||||
if (modalUrl && MODAL_DYNAMIC_FLAG in modalElement.dataset) {
|
||||
fillModal(modalUrl);
|
||||
}
|
||||
|
||||
document.addEventListener('keyup', keyupHandler);
|
||||
}
|
||||
|
||||
function closeHandler(event) {
|
||||
function onCloseClicked(event) {
|
||||
event.preventDefault();
|
||||
close();
|
||||
}
|
||||
|
||||
function close() {
|
||||
overlayElement.classList.remove(OVERLAY_OPEN_CLASS);
|
||||
modalElement.classList.remove(MODAL_OPEN_CLASS);
|
||||
function onKeyUp(event) {
|
||||
if (event.key === 'Escape') {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
document.removeEventListener('keyup', keyupHandler);
|
||||
function open() {
|
||||
document.body.insertBefore(element, null);
|
||||
element.classList.add(MODAL_OPEN_CLASS);
|
||||
overlayElement.classList.add(MODAL_OVERLAY_CLASS);
|
||||
document.body.insertBefore(overlayElement, element);
|
||||
overlayElement.classList.add(MODAL_OVERLAY_OPEN_CLASS);
|
||||
|
||||
if (modalUrl) {
|
||||
fillModal(modalUrl);
|
||||
}
|
||||
|
||||
document.addEventListener('keyup', onKeyUp);
|
||||
}
|
||||
|
||||
function close() {
|
||||
overlayElement.classList.remove(MODAL_OVERLAY_OPEN_CLASS);
|
||||
element.classList.remove(MODAL_OPEN_CLASS);
|
||||
|
||||
document.removeEventListener('keyup', onKeyUp);
|
||||
};
|
||||
|
||||
function setupForm() {
|
||||
var form = modalElement.querySelector('form');
|
||||
if (form) {
|
||||
utilInstances.push(window.utils.setup('form', form, { headers: MODAL_HEADERS, force: true }));
|
||||
}
|
||||
}
|
||||
|
||||
function setupCloser() {
|
||||
if (MODAL_CLOSABLE_FLAG in modalElement.dataset) {
|
||||
modalElement.insertBefore(closerElement, null);
|
||||
closerElement.classList.add(CLOSER_CLASS);
|
||||
closerElement.addEventListener('click', closeHandler, false);
|
||||
overlayElement.addEventListener('click', closeHandler, false);
|
||||
}
|
||||
}
|
||||
|
||||
function fillModal(url) {
|
||||
if (!window.utils.httpClient) {
|
||||
throw new Error('httpClient not found! Can\' fetch modal content from ' + url);
|
||||
if (!HttpClient) {
|
||||
throw new Error('HttpClient not found! Can\'t fetch modal content from ' + url);
|
||||
}
|
||||
|
||||
window.utils.httpClient.get(url, MODAL_HEADERS)
|
||||
HttpClient.get(url, MODAL_HEADERS)
|
||||
.then(function(response) {
|
||||
response.text().then(processResponse);
|
||||
});
|
||||
}
|
||||
|
||||
function processResponse(reponseBody) {
|
||||
function processResponse(responseBody) {
|
||||
var responseElement = document.createElement('div');
|
||||
responseElement.innerHTML = responseBody;
|
||||
|
||||
var modalContent = document.createElement('div');
|
||||
modalContent.classList.add(MODAL_CONTENT_CLASS);
|
||||
modalContent.innerHTML = reponseBody;
|
||||
|
||||
var contentBody = modalContent.querySelector('.' + MAIN_CONTENT_CLASS);
|
||||
var contentBody = responseElement.querySelector('.' + MAIN_CONTENT_CLASS);
|
||||
|
||||
if (contentBody) {
|
||||
modalContent.innerHTML = contentBody.innerHTML;
|
||||
}
|
||||
|
||||
var previousModalContent = modalElement.querySelector('.' + MODAL_CONTENT_CLASS);
|
||||
var previousModalContent = element.querySelector('.' + MODAL_CONTENT_CLASS);
|
||||
if (previousModalContent) {
|
||||
previousModalContent.remove();
|
||||
}
|
||||
|
||||
modalContent = withPrefixedInputIDs(modalContent);
|
||||
modalElement.insertBefore(modalContent, null);
|
||||
setupForm();
|
||||
element.insertBefore(modalContent, null);
|
||||
|
||||
// setup any newly arrived utils
|
||||
UtilRegistry.setupAll();
|
||||
}
|
||||
|
||||
function withPrefixedInputIDs(modalContent) {
|
||||
var idAttrs = ['id', 'for', 'data-conditional-id'];
|
||||
idAttrs.forEach(function(attr) {
|
||||
modalContent.querySelectorAll('[' + attr + ']').forEach(function(input) {
|
||||
var value = modalElement.id + '__' + input.getAttribute(attr);
|
||||
var value = element.id + '__' + input.getAttribute(attr);
|
||||
input.setAttribute(attr, value);
|
||||
});
|
||||
});
|
||||
return modalContent;
|
||||
}
|
||||
|
||||
function keyupHandler(event) {
|
||||
if (event.key === 'Escape') {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
setup();
|
||||
|
||||
function destroyUtils() {
|
||||
utilInstances.filter(function(utilInstance) {
|
||||
return !!utilInstance;
|
||||
}).forEach(function(utilInstance) {
|
||||
utilInstance.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
scope: modalElement,
|
||||
destroy: destroyUtils,
|
||||
};
|
||||
return _init();
|
||||
};
|
||||
|
||||
if (UtilRegistry) {
|
||||
UtilRegistry.register({
|
||||
name: MODAL_UTIL_NAME,
|
||||
selector: MODAL_UTIL_SELECTOR,
|
||||
setup: modalUtil
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
@ -26,11 +26,11 @@
|
||||
<li>
|
||||
Knopf-Test:
|
||||
^{btnForm}
|
||||
<li><br>
|
||||
Modals:
|
||||
^{modal "Klick mich für Ajax-Test" (Left $ SomeRoute UsersR)}
|
||||
^{modal "Klick mich für Content-Test" (Right "Test Inhalt für Modal")}
|
||||
<li>
|
||||
^{modal "Email-Test" (Right emailWidget')}
|
||||
Modals:
|
||||
<ul>
|
||||
<li>^{modal "Klick mich für Ajax-Test" (Left $ SomeRoute UsersR)}
|
||||
<li>^{modal "Klick mich für Content-Test" (Right "Test Inhalt für Modal")}
|
||||
<li>^{modal "Email-Test" (Right emailWidget')}
|
||||
<li>
|
||||
Some icons: ^{isVisible False} ^{hasComment True}
|
||||
|
||||
@ -12,7 +12,7 @@ $newline never
|
||||
|
||||
^{pageHead pc}
|
||||
|
||||
<body .no-js .theme--#{toPathPiece currentTheme} :isAuth:.logged-in :isModal:.modal>
|
||||
<body .no-js .theme--#{toPathPiece currentTheme} :isAuth:.logged-in>
|
||||
<!-- removes no-js class from body if client supports javascript -->
|
||||
<script>
|
||||
document.body.classList.remove('no-js');
|
||||
|
||||
@ -189,27 +189,21 @@ h4 {
|
||||
}
|
||||
|
||||
@media (min-width: 426px) {
|
||||
:not(.modal) {
|
||||
.main__content {
|
||||
margin-left: var(--asidenav-width-md, 50px);
|
||||
}
|
||||
.main__content {
|
||||
margin-left: var(--asidenav-width-md, 50px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 769px) {
|
||||
:not(.modal) {
|
||||
.main__content {
|
||||
margin-left: var(--asidenav-width-lg, 20%);
|
||||
margin-top: var(--header-height);
|
||||
}
|
||||
.main__content {
|
||||
margin-left: var(--asidenav-width-lg, 20%);
|
||||
margin-top: var(--header-height);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
:not(.modal) {
|
||||
.main__content {
|
||||
margin-left: var(--asidenav-width-xl, 250px);
|
||||
}
|
||||
.main__content {
|
||||
margin-left: var(--asidenav-width-xl, 250px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
$newline never
|
||||
<div .modal.js-modal #modal-#{modalId'} data-trigger=#{triggerId'} data-closeable :isDynamic:data-dynamic>
|
||||
<div uw-modal data-modal-trigger=##{triggerId'} data-modal-closeable :isDynamic:.not-used>
|
||||
$case modalContent
|
||||
$of Right content
|
||||
<div .modal__content>
|
||||
|
||||
@ -7,7 +7,7 @@ $newline never
|
||||
$of PageActionPrime
|
||||
<div .pagenav__list-item>
|
||||
$if menuItemModal
|
||||
<div .modal.js-modal #modal-#{menuIdent} data-trigger=#{menuIdent} data-closeable data-dynamic>
|
||||
<div uw-modal data-modal-trigger=#{menuIdent} data-modal-closeable>
|
||||
<a .pagenav__link-wrapper href=#{route} ##{menuIdent}>_{SomeMessage menuItemLabel}
|
||||
$of _
|
||||
$if hasSecondaryPageActions
|
||||
@ -18,6 +18,6 @@ $newline never
|
||||
$of PageActionSecondary
|
||||
<div .pagenav__list-item.pagenav__list-item--secondary>
|
||||
$if menuItemModal
|
||||
<div .modal.js-modal #modal-#{menuIdent} data-trigger=#{menuIdent} data-closeable data-dynamic>
|
||||
<div uw-modal data-modal-trigger=#{menuIdent} data-modal-closeable>
|
||||
<a .pagenav__link-wrapper.pagenav__link-wrapper--secondary href=#{route} ##{menuIdent}>_{SomeMessage menuItemLabel}
|
||||
$of _
|
||||
|
||||
Loading…
Reference in New Issue
Block a user