move modal from standalone to static
This commit is contained in:
parent
55c1385e3e
commit
b65b44bf8a
@ -1018,6 +1018,7 @@ siteLayout' headingOverride widget = do
|
|||||||
addScript $ StaticR js_utils_asyncTable_js
|
addScript $ StaticR js_utils_asyncTable_js
|
||||||
addScript $ StaticR js_utils_form_js
|
addScript $ StaticR js_utils_form_js
|
||||||
addScript $ StaticR js_utils_inputs_js
|
addScript $ StaticR js_utils_inputs_js
|
||||||
|
addScript $ StaticR js_utils_modal_js
|
||||||
addScript $ StaticR js_utils_setup_js
|
addScript $ StaticR js_utils_setup_js
|
||||||
addScript $ StaticR js_utils_showHide_js
|
addScript $ StaticR js_utils_showHide_js
|
||||||
addScript $ StaticR js_utils_tabber_js
|
addScript $ StaticR js_utils_tabber_js
|
||||||
@ -1025,12 +1026,12 @@ siteLayout' headingOverride widget = do
|
|||||||
addStylesheet $ StaticR css_utils_asidenav_scss
|
addStylesheet $ StaticR css_utils_asidenav_scss
|
||||||
addStylesheet $ StaticR css_utils_form_scss
|
addStylesheet $ StaticR css_utils_form_scss
|
||||||
addStylesheet $ StaticR css_utils_inputs_scss
|
addStylesheet $ StaticR css_utils_inputs_scss
|
||||||
|
addStylesheet $ StaticR css_utils_modal_scss
|
||||||
addStylesheet $ StaticR css_utils_showHide_scss
|
addStylesheet $ StaticR css_utils_showHide_scss
|
||||||
addStylesheet $ StaticR css_utils_tabber_scss
|
addStylesheet $ StaticR css_utils_tabber_scss
|
||||||
addStylesheet $ StaticR css_utils_tooltip_scss
|
addStylesheet $ StaticR css_utils_tooltip_scss
|
||||||
-- widgets
|
-- widgets
|
||||||
$(widgetFile "default-layout")
|
$(widgetFile "default-layout")
|
||||||
$(widgetFile "standalone/modal")
|
|
||||||
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
|
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
85
static/js/utils/modal.js
Normal file
85
static/js/utils/modal.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.utils = window.utils || {};
|
||||||
|
|
||||||
|
var JS_INITIALIZED_CLASS = 'js-initialized';
|
||||||
|
var MODAL_OPEN_CLASS = 'modal--open';
|
||||||
|
var MODAL_TRIGGER_CLASS = 'modal__trigger';
|
||||||
|
var MODAL_CLOSABLE_FLAG = 'closeable';
|
||||||
|
var OVERLAY_CLASS = 'modal__overlay';
|
||||||
|
var OVERLAY_OPEN_CLASS = 'modal__overlay--open';
|
||||||
|
var CLOSER_CLASS = 'modal__closer';
|
||||||
|
|
||||||
|
window.utils.modal = function(modalElement, options) {
|
||||||
|
|
||||||
|
if (!modalElement || modalElement.classList.contains(JS_INITIALIZED_CLASS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var overlayElement = document.createElement('div');
|
||||||
|
var closerElement = document.createElement('div');
|
||||||
|
var triggerElement = document.querySelector('#' + modalElement.dataset.trigger);
|
||||||
|
|
||||||
|
function open(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
modalElement.classList.add(MODAL_OPEN_CLASS);
|
||||||
|
overlayElement.classList.add(OVERLAY_CLASS);
|
||||||
|
document.body.insertBefore(overlayElement, modalElement);
|
||||||
|
overlayElement.classList.add(OVERLAY_OPEN_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
overlayElement.classList.remove(OVERLAY_OPEN_CLASS);
|
||||||
|
modalElement.classList.remove(MODAL_OPEN_CLASS);
|
||||||
|
};
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
document.body.insertBefore(modalElement, null);
|
||||||
|
|
||||||
|
var form = modalElement.querySelector('form');
|
||||||
|
if (form) {
|
||||||
|
setupForm(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MODAL_CLOSABLE_FLAG in modalElement.dataset) {
|
||||||
|
modalElement.insertBefore(closerElement, null);
|
||||||
|
closerElement.classList.add(CLOSER_CLASS);
|
||||||
|
closerElement.addEventListener('click', close, false);
|
||||||
|
overlayElement.addEventListener('click', close, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerElement.classList.add(MODAL_TRIGGER_CLASS);
|
||||||
|
triggerElement.addEventListener('click', open, false);
|
||||||
|
modalElement.classList.add(JS_INITIALIZED_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupForm(form) {
|
||||||
|
form.addEventListener('submit', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
var url = form.getAttribute('action');
|
||||||
|
var httpRequestOptions = {
|
||||||
|
method: form.method,
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: {
|
||||||
|
'Is-Modal': 'True'
|
||||||
|
},
|
||||||
|
body: new FormData(form),
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetch(url, httpRequestOptions).then(function(response) {
|
||||||
|
return response.json();
|
||||||
|
}).then(function(response) {
|
||||||
|
// TODO: process json response once backend returns json
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.error('could not fetch or process response from ' + url, error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setup();
|
||||||
|
};
|
||||||
|
})();
|
||||||
@ -1 +0,0 @@
|
|||||||
<!-- only here to be able to include modal using `toWidget` -->
|
|
||||||
@ -1,300 +0,0 @@
|
|||||||
(function() {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
window.utils = window.utils || {};
|
|
||||||
|
|
||||||
window.utils.modal = function(modal) {
|
|
||||||
var overlay = document.createElement('div');
|
|
||||||
var closer = document.createElement('div');
|
|
||||||
var trigger = document.querySelector('#' + modal.dataset.trigger);
|
|
||||||
// var origParent = modal.parentNode;
|
|
||||||
|
|
||||||
function open(event) {
|
|
||||||
if (!modal.classList.contains('js-modal-initialized')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable modals for narrow screens
|
|
||||||
if (event) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
modal.classList.add('modal--open');
|
|
||||||
overlay.classList.add('modal__overlay');
|
|
||||||
// document.body.insertBefore(modal, null);
|
|
||||||
document.body.insertBefore(overlay, modal);
|
|
||||||
overlay.classList.add('modal__overlay--open');
|
|
||||||
|
|
||||||
if ('closeable' in modal.dataset) {
|
|
||||||
closer.classList.add('modal__closer');
|
|
||||||
modal.insertBefore(closer, null);
|
|
||||||
closer.addEventListener('click', close, false);
|
|
||||||
overlay.addEventListener('click', close, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// you can open this modal via event
|
|
||||||
// example: document.dispatchEvent(new CustomEvent('modal-open', { details: { for: 'modal-[id]' }}))
|
|
||||||
function openOnEvent(event) {
|
|
||||||
if (event.detail.for === modal.getAttribute('id')) {
|
|
||||||
open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function close(event) {
|
|
||||||
overlay.remove();
|
|
||||||
// origParent.insertBefore(modal, null);
|
|
||||||
modal.classList.remove('modal--open');
|
|
||||||
closer.removeEventListener('click', close, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
function setup() {
|
|
||||||
document.body.insertBefore(modal, null);
|
|
||||||
|
|
||||||
// every modal can be openend via document-wide event, see openOnEvent
|
|
||||||
document.addEventListener('modal-open', openOnEvent, false);
|
|
||||||
|
|
||||||
if ('dynamic' in modal.dataset) {
|
|
||||||
function fetchModal(url, init) {
|
|
||||||
function responseHtml(body) {
|
|
||||||
var modalContent = document.createElement('div');
|
|
||||||
modalContent.innerHTML = body;
|
|
||||||
|
|
||||||
var contentBody = modalContent.querySelector('.main__content-body');
|
|
||||||
var scriptTags = [];
|
|
||||||
if (contentBody) {
|
|
||||||
modalContent.querySelectorAll('script').forEach(function(scriptTag) {
|
|
||||||
var existsAlready = Array.from(document.body.querySelectorAll('script')).some(function(haystack) {
|
|
||||||
if (haystack.text === scriptTag.text && haystack.getAttribute('src') === scriptTag.getAttribute('src')) {
|
|
||||||
scriptTags.push(haystack);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (existsAlready)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var scriptClone = document.createElement('script');
|
|
||||||
if (scriptTag.text)
|
|
||||||
scriptClone.text = scriptTag.text;
|
|
||||||
if (scriptTag.hasAttributes()) {
|
|
||||||
var attrs = scriptTag.attributes;
|
|
||||||
for (var i = attrs.length - 1; i >= 0; i--) {
|
|
||||||
scriptClone.setAttribute(attrs[i].name, attrs[i].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.insertBefore(scriptClone, null);
|
|
||||||
scriptTags.push(scriptClone);
|
|
||||||
});
|
|
||||||
|
|
||||||
modalContent.querySelectorAll('style').forEach(function(styleTag) {
|
|
||||||
if (Array.from(document.head.querySelectorAll('style')).some(function(haystack) {
|
|
||||||
return haystack.innerText === styleTag.innerText;
|
|
||||||
})) { return }
|
|
||||||
|
|
||||||
document.head.insertBefore(styleTag, null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modalContent.querySelectorAll('link').forEach(function(linkTag) {
|
|
||||||
if (linkTag.getAttribute('rel') !== 'stylesheet')
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Array.from(document.head.querySelectorAll('link')).some(function(haystack) {
|
|
||||||
return haystack.getAttribute('href') === linkTag.getAttribute('href');
|
|
||||||
})) { return }
|
|
||||||
|
|
||||||
|
|
||||||
document.head.insertBefore(linkTag, null);
|
|
||||||
});
|
|
||||||
|
|
||||||
var modalAlertsEl = modalContent.querySelector('#alerts');
|
|
||||||
var alertsEl = document.body.querySelector('#alerts');
|
|
||||||
if (alertsEl && modalAlertsEl) {
|
|
||||||
var modalAlerts = Array.from(modalAlertsEl.childNodes);
|
|
||||||
|
|
||||||
modalAlerts.forEach(function(alertEl) {
|
|
||||||
alertsEl.insertBefore(alertEl, alertsEl.querySelector('.alerts__toggler'));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (modalAlerts.length !== 0)
|
|
||||||
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: alertsEl } }));
|
|
||||||
|
|
||||||
contentBody.removeChild(modalAlertsEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
modalContent = contentBody;
|
|
||||||
}
|
|
||||||
modalContent.classList.add('modal__content');
|
|
||||||
|
|
||||||
var nudgeAttr = function(attr, x) {
|
|
||||||
var oldVal = x.getAttribute(attr);
|
|
||||||
var newVal = modal.getAttribute('id') + '__' + oldVal;
|
|
||||||
|
|
||||||
// console.log(oldVal, newVal);
|
|
||||||
x.setAttribute(attr, newVal);
|
|
||||||
};
|
|
||||||
|
|
||||||
var idAttrs = ['id', 'for', 'data-conditional-id'];
|
|
||||||
idAttrs.map(function(attr) {
|
|
||||||
modalContent.querySelectorAll('[' + attr + ']').forEach(function(x) { nudgeAttr(attr, x); });
|
|
||||||
});
|
|
||||||
|
|
||||||
modal.querySelectorAll('.modal__content').forEach(function(prev) { modal.removeChild(prev); });
|
|
||||||
modal.insertBefore(modalContent, null);
|
|
||||||
|
|
||||||
var triggerContentLoad = function() {
|
|
||||||
console.log('contentReady', modal);
|
|
||||||
|
|
||||||
document.dispatchEvent(new CustomEvent('setup', {
|
|
||||||
detail: { scope: modal },
|
|
||||||
bubbles: true,
|
|
||||||
cancelable: true
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
scriptTags.forEach(function(t) { t.addEventListener('load', triggerContentLoad); });
|
|
||||||
triggerContentLoad();
|
|
||||||
|
|
||||||
return 'html';
|
|
||||||
}
|
|
||||||
|
|
||||||
function responseJson(data) {
|
|
||||||
var alertsEl = document.querySelector('#alerts');
|
|
||||||
if (!alertsEl)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
|
||||||
var alert = document.createElement('div');
|
|
||||||
alert.classList.add('alert', 'alert-' + data[i].class);
|
|
||||||
var alertContent = document.createElement('div');
|
|
||||||
alertContent.classList.add('alert__content');
|
|
||||||
alertContent.innerHTML = data[i].content;
|
|
||||||
alert.appendChild(alertContent);
|
|
||||||
|
|
||||||
alertsEl.insertBefore(alert, alertsEl.querySelector('.alerts__toggler'));
|
|
||||||
}
|
|
||||||
|
|
||||||
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: alertsEl }, bubbles: true, cancelable: true }));
|
|
||||||
|
|
||||||
return 'json';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return fetch(url, init).then(function(response) {
|
|
||||||
var contentType = response.headers.get('Content-Type')
|
|
||||||
if (contentType && contentType.includes('text/html')) {
|
|
||||||
return response.text().then(responseHtml);
|
|
||||||
} else if (contentType && contentType.includes('application/json')) {
|
|
||||||
return response.json().then(responseJson);
|
|
||||||
} else {
|
|
||||||
console.log(response);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
modal.addEventListener('modal-fetch', function(event) {
|
|
||||||
var dynamicContentURL = (event.detail && event.detail.url) || trigger.getAttribute('href');
|
|
||||||
|
|
||||||
var fetchInit = (event.detail && event.detail.init) || {
|
|
||||||
credentials: 'same-origin',
|
|
||||||
headers: {
|
|
||||||
#{String (toPathPiece HeaderIsModal)}: 'True'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dynamicContentURL.length > 0) {
|
|
||||||
fetchModal(dynamicContentURL, fetchInit).then((event.detail && event.detail.then) || (function(){}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
modal.dispatchEvent(new CustomEvent('modal-fetch', {
|
|
||||||
detail: {
|
|
||||||
then: (function() {
|
|
||||||
if (!trigger)
|
|
||||||
return;
|
|
||||||
|
|
||||||
trigger.classList.add('modal__trigger');
|
|
||||||
trigger.addEventListener('click', open, false);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
} else if (trigger) { // if modal has trigger assigned to it open modal on click
|
|
||||||
trigger.classList.add('modal__trigger');
|
|
||||||
trigger.addEventListener('click', open, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// tell further modals, that this one already got initialized
|
|
||||||
modal.classList.add('js-modal-initialized');
|
|
||||||
|
|
||||||
modal.addEventListener('modal-close', close);
|
|
||||||
}
|
|
||||||
|
|
||||||
setup();
|
|
||||||
};
|
|
||||||
|
|
||||||
window.utils.ajaxSubmit = function(modal, form) {
|
|
||||||
function doSubmit(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
var modalContent = modal.querySelector('.modal__content');
|
|
||||||
modalContent.style.pointerEvents = 'none';
|
|
||||||
modalContent.style.opacity = 0.5;
|
|
||||||
|
|
||||||
modal.dispatchEvent(new CustomEvent('modal-fetch', {
|
|
||||||
detail: {
|
|
||||||
url: form.target,
|
|
||||||
init: {
|
|
||||||
credentials: 'same-origin',
|
|
||||||
headers: {
|
|
||||||
#{String (toPathPiece HeaderIsModal)}: 'True'
|
|
||||||
},
|
|
||||||
method: form.method,
|
|
||||||
body: new FormData(form)
|
|
||||||
},
|
|
||||||
then: (function(typ) {
|
|
||||||
modal.dispatchEvent(new CustomEvent('modal-close'));
|
|
||||||
|
|
||||||
modalContent.style.pointerEvents = 'auto';
|
|
||||||
modalContent.style.opacity = 1;
|
|
||||||
|
|
||||||
if (typ === 'json') {
|
|
||||||
modal.dispatchEvent(new CustomEvent('modal-fetch'));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
bubbles: true,
|
|
||||||
cancelable: true
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
form.addEventListener('submit', doSubmit);
|
|
||||||
form.classList.add('js-ajax-initialized');
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
document.addEventListener('setup', function(e) {
|
|
||||||
if (e.detail.module && e.detail.module !== 'modal')
|
|
||||||
return;
|
|
||||||
|
|
||||||
Array.from(e.detail.scope.querySelectorAll('.js-modal:not(.js-modal-initialized)')).forEach(function(modal) {
|
|
||||||
window.utils.modal(modal);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (e.detail.scope.classList.contains('js-modal')) {
|
|
||||||
Array.from(e.detail.scope.querySelectorAll('form[data-ajax-submit]:not(.js-ajax-initialized)')).forEach(function(form) {
|
|
||||||
window.utils.ajaxSubmit(e.detail.scope, form);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Array.from(e.detail.scope.querySelectorAll('.js-modal')).map(function(modal) {
|
|
||||||
Array.from(modal.querySelectorAll('form[data-ajax-submit]:not(.js-ajax-initialized)')).forEach(function(form) {
|
|
||||||
window.utils.ajaxSubmit(modal, form);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'modal' }, bubbles: true, cancelable: true }))
|
|
||||||
});
|
|
||||||
6
templates/widgets/modal/modal.julius
Normal file
6
templates/widgets/modal/modal.julius
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var modalElements = Array.from(document.querySelectorAll('.modal'));
|
||||||
|
modalElements.forEach(function(modal) {
|
||||||
|
window.utils.setup('modal', modal);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user