121 lines
3.8 KiB
JavaScript
121 lines
3.8 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
var el = {};
|
|
var _resolve = null;
|
|
var _busy = false;
|
|
|
|
function build() {
|
|
el.overlay = document.createElement('div');
|
|
el.overlay.id = 'app-modal';
|
|
el.overlay.style.cssText = [
|
|
'display:none', 'position:fixed', 'inset:0',
|
|
'background:rgba(0,0,0,0.65)', 'z-index:9999',
|
|
'align-items:center', 'justify-content:center'
|
|
].join(';');
|
|
|
|
el.box = document.createElement('div');
|
|
el.box.style.cssText = [
|
|
'background:#0f1520', 'border:1px solid #1e2d45',
|
|
'border-radius:8px', 'padding:24px 28px',
|
|
'max-width:440px', 'width:calc(100% - 48px)',
|
|
'box-shadow:0 16px 48px rgba(0,0,0,0.6)'
|
|
].join(';');
|
|
|
|
el.msg = document.createElement('p');
|
|
el.msg.style.cssText = 'margin:0 0 20px;color:#c9d1d9;font-size:14px;line-height:1.6;white-space:pre-wrap';
|
|
|
|
el.row = document.createElement('div');
|
|
el.row.style.cssText = 'display:flex;gap:8px;justify-content:flex-end';
|
|
|
|
el.cancelBtn = document.createElement('button');
|
|
el.cancelBtn.textContent = 'Cancel';
|
|
el.cancelBtn.className = 'btn-ghost';
|
|
|
|
el.confirmBtn = document.createElement('button');
|
|
el.confirmBtn.className = 'btn-danger';
|
|
|
|
el.row.appendChild(el.cancelBtn);
|
|
el.row.appendChild(el.confirmBtn);
|
|
el.box.appendChild(el.msg);
|
|
el.box.appendChild(el.row);
|
|
el.overlay.appendChild(el.box);
|
|
document.body.appendChild(el.overlay);
|
|
|
|
el.cancelBtn.addEventListener('click', function () { close(false); });
|
|
el.confirmBtn.addEventListener('click', function () { close(true); });
|
|
el.overlay.addEventListener('click', function (e) {
|
|
if (e.target === el.overlay) close(false);
|
|
});
|
|
document.addEventListener('keydown', function (e) {
|
|
if (el.overlay && el.overlay.style.display !== 'none' && e.key === 'Escape') close(false);
|
|
});
|
|
}
|
|
|
|
function openModal(msg, confirmLabel, isDanger, showCancel) {
|
|
if (!el.overlay) build();
|
|
el.msg.textContent = msg;
|
|
el.confirmBtn.textContent = confirmLabel || 'OK';
|
|
el.confirmBtn.className = isDanger !== false ? 'btn-danger' : 'btn-primary';
|
|
el.cancelBtn.style.display = showCancel === false ? 'none' : '';
|
|
el.overlay.style.display = 'flex';
|
|
el.confirmBtn.focus();
|
|
return new Promise(function (res) { _resolve = res; });
|
|
}
|
|
|
|
function close(result) {
|
|
if (el.overlay) el.overlay.style.display = 'none';
|
|
if (_resolve) { _resolve(result); _resolve = null; }
|
|
}
|
|
|
|
window.appModal = {
|
|
// Confirmation dialog — returns Promise<boolean>
|
|
confirm: function (msg, label) {
|
|
return openModal(msg, label || 'Confirm', true, true);
|
|
},
|
|
// Info/alert dialog — returns Promise<void>
|
|
info: function (msg) {
|
|
return openModal(msg, 'OK', false, false);
|
|
}
|
|
};
|
|
|
|
// Auto-intercept: buttons/links with data-confirm or data-confirm-fn attribute.
|
|
document.addEventListener('click', function (e) {
|
|
if (_busy) return;
|
|
|
|
var btn = e.target.closest('[data-confirm], [data-confirm-fn]');
|
|
if (!btn) return;
|
|
|
|
e.preventDefault();
|
|
e.stopImmediatePropagation();
|
|
|
|
var msg;
|
|
|
|
if (btn.dataset.confirm) {
|
|
msg = btn.dataset.confirm;
|
|
} else {
|
|
var fn = window[btn.dataset.confirmFn];
|
|
if (typeof fn !== 'function') return;
|
|
msg = fn(); // returns message string, or null/'' to abort (fn may show its own info modal)
|
|
}
|
|
|
|
if (!msg) return;
|
|
|
|
var label = btn.dataset.confirmLabel || 'Confirm';
|
|
window.appModal.confirm(msg, label).then(function (ok) {
|
|
if (!ok) return;
|
|
_busy = true;
|
|
// requestSubmit includes button name/value and respects formaction
|
|
if (btn.form && typeof btn.form.requestSubmit === 'function') {
|
|
btn.form.requestSubmit(btn);
|
|
} else if (btn.form) {
|
|
btn.form.submit();
|
|
} else {
|
|
btn.click();
|
|
}
|
|
setTimeout(function () { _busy = false; }, 100);
|
|
});
|
|
}, true);
|
|
|
|
})();
|