58 lines
1.8 KiB
JavaScript
Executable File
58 lines
1.8 KiB
JavaScript
Executable File
// assets/js/toast.js
|
|
// Shows toast in the TOP LAYER: if a <dialog open> exists, append inside it.
|
|
// Otherwise append to <body>. Default type = success (green).
|
|
|
|
window.Toast = (function () {
|
|
// create (or reuse) a toast root inside a given container
|
|
function ensureRoot(container) {
|
|
// Find last (topmost) open dialog if not explicitly passed
|
|
const host = container || (() => {
|
|
const dialogs = Array.from(document.querySelectorAll('dialog[open]'));
|
|
return dialogs.length ? dialogs[dialogs.length - 1] : document.body;
|
|
})();
|
|
|
|
// Root per container (class-based to allow multiple roots)
|
|
let root = host.querySelector(':scope > .toast-root');
|
|
if (!root) {
|
|
root = document.createElement('div');
|
|
root.className = 'toast-root';
|
|
host.appendChild(root);
|
|
}
|
|
return root;
|
|
}
|
|
|
|
function render(msg, type, duration, container) {
|
|
const root = ensureRoot(container);
|
|
|
|
const n = document.createElement('div');
|
|
n.className = 'toast' + (type === 'error' ? ' error' : ' success');
|
|
n.innerHTML = `
|
|
<span class="icon">${type === 'error' ? '⚠️' : '✅'}</span>
|
|
<span class="content">${msg || ''}</span>
|
|
<button class="close" aria-label="Schließen">✕</button>
|
|
`;
|
|
root.appendChild(n);
|
|
|
|
const close = () => { try { n.remove(); } catch {} };
|
|
n.querySelector('.close')?.addEventListener('click', close);
|
|
|
|
const t = setTimeout(close, Number(duration) || 2200);
|
|
// clean timer if user closes early
|
|
n.addEventListener('remove', () => clearTimeout(t), { once:true });
|
|
}
|
|
|
|
// API
|
|
function show(msg, opt) {
|
|
opt = opt || {};
|
|
render(
|
|
msg,
|
|
opt.type === 'error' ? 'error' : 'success',
|
|
opt.duration,
|
|
opt.container // optional: pass a specific container
|
|
);
|
|
}
|
|
|
|
return { show };
|
|
})();
|
|
|