Really-amin's picture
Upload 303 files
b068b76 verified
(function () {
const stack = document.createElement('div');
stack.className = 'toast-stack';
const mountStack = () => document.body.appendChild(stack);
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', mountStack, { once: true });
} else {
mountStack();
}
const createToast = (type, title, message) => {
const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.innerHTML = `<div><strong>${title}</strong>${message ? `<small>${message}</small>` : ''}</div>`;
stack.appendChild(toast);
setTimeout(() => toast.remove(), 4500);
};
const setBadge = (element, text, tone = 'info') => {
if (!element) return;
element.textContent = text;
element.className = `badge ${tone}`;
};
const showLoading = (container, message = 'Loading data...') => {
if (!container) return;
container.innerHTML = `<div class="loading-indicator">${message}</div>`;
};
const fadeReplace = (container, html) => {
if (!container) return;
container.innerHTML = html;
container.classList.add('fade-in');
setTimeout(() => container.classList.remove('fade-in'), 200);
};
const fetchJSON = async (url, options = {}, context = '') => {
try {
const response = await fetch(url, options);
if (!response.ok) {
const text = await response.text();
createToast('error', context || 'Request failed', text || response.statusText);
throw new Error(text || response.statusText);
}
return await response.json();
} catch (err) {
createToast('error', context || 'Network error', err.message || String(err));
throw err;
}
};
window.UIFeedback = {
toast: createToast,
setBadge,
showLoading,
fadeReplace,
fetchJSON,
};
})();