138 lines
4.3 KiB
JavaScript
138 lines
4.3 KiB
JavaScript
/**
|
|
* Alert and Confirm modals.
|
|
*
|
|
* Usage:
|
|
*
|
|
* modalAlert({message: "Hello world!"}).then(callback);
|
|
* modalConfirm({message: "Are you sure?"}).then(callback);
|
|
*
|
|
* Available options for modalAlert:
|
|
* - message
|
|
* - title: Alert
|
|
*
|
|
* Available options for modalConfirm:
|
|
* - message
|
|
* - title: Confirm
|
|
* - buttons: ["Ok", "Cancel"]
|
|
* - event (pass `event` for easy inline onclick handlers)
|
|
* - element (pass `this` for easy inline onclick handlers)
|
|
*
|
|
* Example onclick for modalConfirm:
|
|
*
|
|
* <button onclick="modalConfirm({message: 'Are you sure?', event, element}">Delete</button>
|
|
*
|
|
* The `element` is used to find the nearest <form> and submit it on OK.
|
|
* The `event` is used to cancel the submit button's default.
|
|
*/
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const $modal = document.querySelector("#nonshy-alert-modal"),
|
|
$ok = $modal.querySelector("button.nonshy-alert-ok-button"),
|
|
$cancel = $modal.querySelector("button.nonshy-alert-cancel-button"),
|
|
$title = $modal.querySelector("#nonshy-alert-modal-title"),
|
|
$body = $modal.querySelector("#nonshy-alert-modal-body"),
|
|
alertIcon = `<i class="fa fa-exclamation-triangle mr-2"></i>`,
|
|
confirmIcon = `<i class="fa fa-question-circle mr-2"></i>`,
|
|
cls = 'is-active';
|
|
|
|
// Current caller's promise.
|
|
var currentPromise = null;
|
|
|
|
const hideModal = () => {
|
|
currentPromise = null;
|
|
$modal.classList.remove(cls);
|
|
};
|
|
|
|
const showModal = ({
|
|
message,
|
|
title="Alert",
|
|
isConfirm=false,
|
|
buttons=["Ok", "Cancel"],
|
|
}) => {
|
|
$ok.innerHTML = buttons[0];
|
|
$cancel.innerHTML = buttons[1];
|
|
$cancel.style.display = isConfirm ? "" : "none";
|
|
|
|
// Strip HTML from message but allow line breaks.
|
|
message = message.replace(/</g, "<");
|
|
message = message.replace(/>/g, ">");
|
|
message = message.replace(/\n/g, "<br>");
|
|
|
|
$title.innerHTML = (isConfirm ? confirmIcon : alertIcon) + title;
|
|
$body.innerHTML = message;
|
|
|
|
// Show the modal.
|
|
$modal.classList.add(cls);
|
|
|
|
// Focus the OK button, e.g. so hitting Enter doesn't accidentally (re)click the same
|
|
// link/button which prompted the alert box in the first place.
|
|
window.requestAnimationFrame(() => {
|
|
$ok.focus();
|
|
});
|
|
|
|
// Return as a promise.
|
|
return new Promise((resolve, reject) => {
|
|
currentPromise = resolve;
|
|
});
|
|
};
|
|
|
|
// Click events for the modal buttons.
|
|
$ok.addEventListener('click', (e) => {
|
|
if (currentPromise !== null) {
|
|
currentPromise();
|
|
}
|
|
hideModal();
|
|
});
|
|
$cancel.addEventListener('click', (e) => {
|
|
hideModal();
|
|
});
|
|
|
|
// Key bindings to dismiss the modal.
|
|
window.addEventListener('keydown', (e) => {
|
|
if ($modal.classList.contains(cls)) {
|
|
if (e.key == 'Enter') {
|
|
$ok.click();
|
|
} else if (e.key == 'Escape') {
|
|
$cancel.click();
|
|
}
|
|
}
|
|
});
|
|
|
|
// Inline submit button confirmation prompts, e.g.: many submit buttons have name="intent"
|
|
// and want the user to confirm before submitting, and had inline onclick handlers.
|
|
(document.querySelectorAll('.nonshy-confirm-submit') || []).forEach(button => {
|
|
const message = button.dataset.confirm;
|
|
if (!message) return;
|
|
|
|
const onclick = (e) => {
|
|
e.preventDefault();
|
|
modalConfirm({
|
|
message: message.replace(/\\n/g, '\n'),
|
|
}).then(() => {
|
|
button.removeEventListener('click', onclick);
|
|
window.requestAnimationFrame(() => {
|
|
button.click();
|
|
});
|
|
});
|
|
}
|
|
|
|
button.addEventListener('click', onclick);
|
|
});
|
|
|
|
// Exported global functions to invoke the modal.
|
|
window.modalAlert = async ({ message, title="Alert" }) => {
|
|
return showModal({
|
|
message,
|
|
title,
|
|
isConfirm: false,
|
|
});
|
|
};
|
|
window.modalConfirm = async ({ message, title="Confirm", buttons=["Ok", "Cancel"] }) => {
|
|
return showModal({
|
|
message,
|
|
title,
|
|
isConfirm: true,
|
|
buttons,
|
|
});
|
|
};
|
|
});
|