Replace single toast with stacked toast queue

Multiple toasts can appear simultaneously, capped at 3. Oldest
toast auto-evicts when cap is reached. Each toast auto-dismisses
with a fade-up exit animation.

- frontend/app.js: toast() now creates DOM elements in toastContainer
- frontend/index.html: single #toast replaced with #toastContainer
- frontend/styles.css: .toast-container flexbox row, .toast-exit animation
This commit is contained in:
Jonathan Sykes
2026-06-14 14:36:07 +08:00
parent ddfa1494c6
commit a4d6b825d5
7 changed files with 51 additions and 11 deletions

View File

@@ -109,12 +109,29 @@ function fmtTime(sec) {
const mm = h ? String(m).padStart(2, '0') : String(m);
return (h ? h + ':' : '') + mm + ':' + String(s).padStart(2, '0');
}
function toast(msg) {
const t = $('toast');
function toast(msg, { duration = 2200 } = {}) {
const t = document.createElement('div');
t.className = 'toast';
t.textContent = msg;
t.classList.remove('hidden');
clearTimeout(toast._t);
toast._t = setTimeout(() => t.classList.add('hidden'), 2200);
const container = $('toastContainer');
container.appendChild(t);
// Trigger layout for animation
t.style.animation = 'none';
t.offsetHeight; // force reflow
t.style.animation = '';
// Auto-remove after duration
setTimeout(() => {
t.classList.add('toast-exit');
setTimeout(() => t.remove(), 280);
}, duration);
// Cap at 3 toasts — remove oldest
while (container.children.length > 3) {
const first = container.firstChild;
first.classList.add('toast-exit');
setTimeout(() => first.remove(), 280);
}
}
function uid() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 7); }
function fmtBytes(n) {