window.WarpBoxUI = (() => { let toastTimer = null; function toast(message, type = "info", options = {}) { const target = options.target || document.querySelector("#toast"); if (!target) return; target.textContent = message; target.classList.remove("toast-info", "toast-warning", "toast-error", "is-visible"); target.classList.add(`toast-${type}`, "is-visible"); clearTimeout(toastTimer); toastTimer = setTimeout(() => target.classList.remove("is-visible"), options.duration || 2600); } function popupElements(options = {}) { return { popup: options.popup || document.querySelector("#doc-popup"), title: options.title || document.querySelector("#doc-popup-title"), body: options.body || document.querySelector("#doc-popup-body"), backdrop: options.backdrop || document.querySelector("#modal-backdrop"), }; } function openPopup(titleText, html, options = {}) { const parts = popupElements(options); if (!parts.popup || !parts.title || !parts.body) return; parts.title.textContent = titleText; parts.body.innerHTML = html; parts.popup.classList.toggle("is-about-popup", Boolean(options.about)); parts.popup.classList.toggle("is-properties-popup", Boolean(options.properties)); parts.popup.classList.toggle("is-preview-popup", Boolean(options.preview)); parts.popup.classList.add("is-visible"); parts.backdrop?.classList.add("is-visible"); } function closePopup(options = {}) { const parts = popupElements(options); parts.popup?.classList.remove("is-visible", "is-about-popup", "is-properties-popup", "is-preview-popup"); parts.backdrop?.classList.remove("is-visible"); } function htmlEscape(value) { return String(value || "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function renderTemplate(template, data = {}) { return String(template).replace(/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g, (_, key) => { return Object.prototype.hasOwnProperty.call(data, key) ? String(data[key]) : ""; }); } function bindMenuBar(options = {}) { const root = options.root || document; const itemSelector = options.itemSelector || ".menu-item"; const buttonSelector = options.buttonSelector || ".menu-button"; const items = Array.from(root.querySelectorAll(itemSelector)); function close() { items.forEach((item) => { item.classList.remove("is-open"); item.querySelector(buttonSelector)?.setAttribute("aria-expanded", "false"); }); } function open(item) { close(); item.classList.add("is-open"); item.querySelector(buttonSelector)?.setAttribute("aria-expanded", "true"); } items.forEach((item) => { const button = item.querySelector(buttonSelector); button?.addEventListener("click", (event) => { event.stopPropagation(); const wasOpen = item.classList.contains("is-open"); close(); if (!wasOpen) open(item); }); item.addEventListener("mouseenter", () => { if (!root.querySelector(`${itemSelector}.is-open`)) return; open(item); }); }); document.addEventListener("click", (event) => { if (!event.target.closest(itemSelector)) close(); }); return { close, open }; } return { toast, openPopup, closePopup, htmlEscape, renderTemplate, bindMenuBar }; })();