gallery_server_client_s190js/public/js/operationBanner.js
2026-10-26 11:00:14 +01:00

126 lines
3.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function () {
"use strict";
const banners = new Map();
const GAP = 12;
const DEFAULT_ID = "default";
function normalizeId(id) {
return String(id || DEFAULT_ID);
}
function getBanner(id) {
id = normalizeId(id);
if (banners.has(id)) return banners.get(id).element;
const banner = document.createElement("div");
banner.className = "operation-banner";
banner.dataset.operationBannerId = id;
banner.setAttribute("role", "status");
banner.setAttribute("aria-live", "polite");
banner.innerHTML = `
<div class="ob-row">
<div class="ob-icon" aria-hidden="true">ℹ</div>
<div class="ob-content"></div>
<button class="ob-close" type="button" aria-label="Chiudi">&times;</button>
</div>
<div class="ob-progress-label" aria-live="polite"></div>
<div class="ob-progress">
<div class="ob-progress-fill"></div>
</div>
`;
banner.querySelector(".ob-close").addEventListener("click", () => hide(id));
document.body.appendChild(banner);
banners.set(id, { element: banner, timer: null });
return banner;
}
function clearTimer(entry) {
if (entry && entry.timer !== null) {
clearTimeout(entry.timer);
entry.timer = null;
}
}
function reposition() {
let bottom = 20;
for (const entry of [...banners.values()]) {
const banner = entry.element;
if (!banner.classList.contains("is-visible")) continue;
banner.style.bottom = `${bottom}px`;
bottom += banner.getBoundingClientRect().height + GAP;
}
}
function hide(id = DEFAULT_ID) {
id = normalizeId(id);
const entry = banners.get(id);
if (!entry) return;
clearTimer(entry);
entry.element.classList.remove("is-visible", "is-busy");
reposition();
}
function show(message, options = {}) {
const id = normalizeId(options.id);
const banner = getBanner(id);
const entry = banners.get(id);
clearTimer(entry);
const type = options.type || "info";
const busy = options.busy === true;
const sticky = options.sticky === true;
const icons = {
info: "ℹ",
upload: "↑",
download: "↓",
success: "✓",
error: "!"
};
banner.classList.remove(
"is-info", "is-upload", "is-download", "is-success", "is-error"
);
banner.classList.add("is-" + type);
banner.classList.toggle("is-busy", busy);
banner.querySelector(".ob-icon").textContent = icons[type] || icons.info;
banner.querySelector(".ob-content").textContent = String(message || "");
banner.classList.add("is-visible");
reposition();
if (!busy && !sticky) {
const duration = Number.isFinite(options.duration)
? options.duration
: (type === "error" ? 6000 : 3500);
entry.timer = setTimeout(() => hide(id), duration);
}
}
function setProgress(id, value) {
id = normalizeId(id);
const banner = getBanner(id);
const percent = Math.max(0, Math.min(100, Number(value) || 0));
banner.classList.add("is-determinate");
banner.classList.remove("is-busy");
banner.querySelector(".ob-progress-fill").style.width = `${percent}%`;
banner.querySelector(".ob-progress-label").textContent = `${Math.round(percent)}%`;
reposition();
}
window.operationBanner = { show, hide, setProgress };
})();