97 lines
2.5 KiB
JavaScript
97 lines
2.5 KiB
JavaScript
/**
|
|
* Auto-dismisses flash notices after their data-flash-timeout expires.
|
|
* Supports slide-out animation and hover-pause.
|
|
*/
|
|
import { resolveHost } from '../core/app-dom.js';
|
|
import { postForm as postFormRequest } from '../core/app-http.js';
|
|
|
|
export function initFlashAutoDismiss(root = document, options = {}) {
|
|
const {
|
|
selector = '.notice[data-flash-timeout]',
|
|
defaultTimeout = 0,
|
|
} = options;
|
|
const host = resolveHost(root);
|
|
const notices = Array.from(host.querySelectorAll(selector));
|
|
if (!notices.length) {
|
|
return { destroy: () => {} };
|
|
}
|
|
|
|
const timers = [];
|
|
let destroyed = false;
|
|
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
|
|
const dismissNotice = (notice) => {
|
|
if (prefersReducedMotion) {
|
|
notice.remove();
|
|
return;
|
|
}
|
|
notice.classList.add('toast-dismissing');
|
|
notice.addEventListener('animationend', () => notice.remove(), { once: true });
|
|
setTimeout(() => { if (notice.parentNode) notice.remove(); }, 300);
|
|
};
|
|
|
|
const postForm = async (form) => {
|
|
const action = form.getAttribute('action');
|
|
if (!action) {
|
|
return false;
|
|
}
|
|
try {
|
|
await postFormRequest(action, new FormData(form));
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
notices.forEach((notice) => {
|
|
const timeout = Number.parseInt(
|
|
notice.dataset.flashTimeout || `${defaultTimeout}`,
|
|
10
|
|
);
|
|
if (!timeout || timeout <= 0) {
|
|
return;
|
|
}
|
|
notice.style.setProperty('--flash-timeout', `${timeout}ms`);
|
|
notice.classList.add('flash-timed');
|
|
|
|
let timer = window.setTimeout(async () => {
|
|
if (destroyed) {
|
|
return;
|
|
}
|
|
const form = notice.querySelector('form');
|
|
if (form) {
|
|
const ok = await postForm(form);
|
|
if (!ok) {
|
|
return;
|
|
}
|
|
}
|
|
dismissNotice(notice);
|
|
}, timeout);
|
|
timers.push(timer);
|
|
|
|
// Pause auto-dismiss on hover
|
|
notice.addEventListener('mouseenter', () => {
|
|
window.clearTimeout(timer);
|
|
});
|
|
notice.addEventListener('mouseleave', () => {
|
|
timer = window.setTimeout(async () => {
|
|
if (destroyed) return;
|
|
const form = notice.querySelector('form');
|
|
if (form) {
|
|
const ok = await postForm(form);
|
|
if (!ok) return;
|
|
}
|
|
dismissNotice(notice);
|
|
}, 1000);
|
|
timers.push(timer);
|
|
});
|
|
});
|
|
|
|
const destroy = () => {
|
|
destroyed = true;
|
|
timers.forEach((timer) => window.clearTimeout(timer));
|
|
};
|
|
|
|
return { destroy };
|
|
}
|