175 lines
5.2 KiB
JavaScript
175 lines
5.2 KiB
JavaScript
|
|
(function () {
|
||
|
|
const DEFAULT_DURATION = 6200;
|
||
|
|
const VARIANTS = ["info", "warning", "error"];
|
||
|
|
const GENERIC_ERROR_MESSAGE = "Something went wrong on this page. Please try again in a moment.";
|
||
|
|
|
||
|
|
window.Warpbox = window.Warpbox || {};
|
||
|
|
let lastGlobalErrorAt = 0;
|
||
|
|
|
||
|
|
function ensureRegion() {
|
||
|
|
let region = document.querySelector("[data-warpbox-popups]");
|
||
|
|
if (region) {
|
||
|
|
return region;
|
||
|
|
}
|
||
|
|
region = document.createElement("div");
|
||
|
|
region.className = "warpbox-popups";
|
||
|
|
region.setAttribute("data-warpbox-popups", "");
|
||
|
|
region.setAttribute("aria-live", "polite");
|
||
|
|
region.setAttribute("aria-atomic", "false");
|
||
|
|
document.body.append(region);
|
||
|
|
return region;
|
||
|
|
}
|
||
|
|
|
||
|
|
function normalizeOptions(options, message) {
|
||
|
|
if (typeof options === "string") {
|
||
|
|
options = { message: options };
|
||
|
|
} else {
|
||
|
|
options = options || {};
|
||
|
|
}
|
||
|
|
if (message) {
|
||
|
|
options.message = message;
|
||
|
|
}
|
||
|
|
const variant = VARIANTS.includes(options.variant) ? options.variant : "info";
|
||
|
|
return {
|
||
|
|
variant,
|
||
|
|
title: options.title || defaultTitle(variant),
|
||
|
|
message: options.message || "",
|
||
|
|
duration: Number.isFinite(options.duration) ? options.duration : DEFAULT_DURATION,
|
||
|
|
actions: Array.isArray(options.actions) ? options.actions : [],
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function defaultTitle(variant) {
|
||
|
|
if (variant === "error") {
|
||
|
|
return "Error";
|
||
|
|
}
|
||
|
|
if (variant === "warning") {
|
||
|
|
return "Warning";
|
||
|
|
}
|
||
|
|
return "Info";
|
||
|
|
}
|
||
|
|
|
||
|
|
function notify(options, message) {
|
||
|
|
const config = normalizeOptions(options, message);
|
||
|
|
const region = ensureRegion();
|
||
|
|
const popup = document.createElement("section");
|
||
|
|
popup.className = "warpbox-popup warpbox-popup-" + config.variant;
|
||
|
|
popup.setAttribute("role", config.variant === "error" ? "alert" : "status");
|
||
|
|
|
||
|
|
const chrome = document.createElement("div");
|
||
|
|
chrome.className = "warpbox-popup-chrome";
|
||
|
|
|
||
|
|
const icon = document.createElement("span");
|
||
|
|
icon.className = "warpbox-popup-icon";
|
||
|
|
icon.setAttribute("aria-hidden", "true");
|
||
|
|
icon.textContent = config.variant === "error" ? "!" : config.variant === "warning" ? "?" : "i";
|
||
|
|
|
||
|
|
const body = document.createElement("div");
|
||
|
|
body.className = "warpbox-popup-body";
|
||
|
|
|
||
|
|
const title = document.createElement("strong");
|
||
|
|
title.className = "warpbox-popup-title";
|
||
|
|
title.textContent = config.title;
|
||
|
|
|
||
|
|
const text = document.createElement("p");
|
||
|
|
text.className = "warpbox-popup-message";
|
||
|
|
text.textContent = config.message;
|
||
|
|
|
||
|
|
body.append(title, text);
|
||
|
|
|
||
|
|
const close = document.createElement("button");
|
||
|
|
close.type = "button";
|
||
|
|
close.className = "warpbox-popup-close";
|
||
|
|
close.setAttribute("aria-label", "Dismiss notification");
|
||
|
|
close.textContent = "x";
|
||
|
|
close.addEventListener("click", () => dismiss(popup));
|
||
|
|
|
||
|
|
chrome.append(icon, body, close);
|
||
|
|
popup.append(chrome);
|
||
|
|
|
||
|
|
if (config.actions.length > 0) {
|
||
|
|
const actions = document.createElement("div");
|
||
|
|
actions.className = "warpbox-popup-actions";
|
||
|
|
config.actions.forEach((action) => {
|
||
|
|
const button = document.createElement("button");
|
||
|
|
button.type = "button";
|
||
|
|
button.className = "button " + (action.kind === "primary" ? "button-primary" : "button-outline");
|
||
|
|
button.textContent = action.label || "Action";
|
||
|
|
button.addEventListener("click", () => {
|
||
|
|
if (typeof action.onClick === "function") {
|
||
|
|
action.onClick();
|
||
|
|
}
|
||
|
|
if (action.dismiss !== false) {
|
||
|
|
dismiss(popup);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
actions.append(button);
|
||
|
|
});
|
||
|
|
popup.append(actions);
|
||
|
|
}
|
||
|
|
|
||
|
|
region.append(popup);
|
||
|
|
window.requestAnimationFrame(() => popup.classList.add("is-visible"));
|
||
|
|
|
||
|
|
let timer = null;
|
||
|
|
if (config.duration > 0) {
|
||
|
|
timer = window.setTimeout(() => dismiss(popup), config.duration);
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
element: popup,
|
||
|
|
close: function closePopup() {
|
||
|
|
if (timer) {
|
||
|
|
window.clearTimeout(timer);
|
||
|
|
}
|
||
|
|
dismiss(popup);
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function dismiss(popup) {
|
||
|
|
if (!popup || popup.dataset.closing === "true") {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
popup.dataset.closing = "true";
|
||
|
|
popup.classList.remove("is-visible");
|
||
|
|
window.setTimeout(() => popup.remove(), 180);
|
||
|
|
}
|
||
|
|
|
||
|
|
window.Warpbox.notify = notify;
|
||
|
|
window.Warpbox.info = function info(message, options) {
|
||
|
|
return notify({ ...(options || {}), variant: "info", message });
|
||
|
|
};
|
||
|
|
window.Warpbox.warning = function warning(message, options) {
|
||
|
|
return notify({ ...(options || {}), variant: "warning", message });
|
||
|
|
};
|
||
|
|
window.Warpbox.error = function error(message, options) {
|
||
|
|
return notify({ ...(options || {}), variant: "error", message });
|
||
|
|
};
|
||
|
|
|
||
|
|
function showGlobalError() {
|
||
|
|
const now = Date.now();
|
||
|
|
if (now - lastGlobalErrorAt < 2500) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
lastGlobalErrorAt = now;
|
||
|
|
notify({
|
||
|
|
variant: "error",
|
||
|
|
title: "Page error",
|
||
|
|
message: GENERIC_ERROR_MESSAGE,
|
||
|
|
duration: 9000,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
window.addEventListener("error", function (event) {
|
||
|
|
if (event && event.target && event.target !== window) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
showGlobalError();
|
||
|
|
});
|
||
|
|
|
||
|
|
window.addEventListener("unhandledrejection", function () {
|
||
|
|
showGlobalError();
|
||
|
|
});
|
||
|
|
})();
|