/** * Toast service global. Sin dependencias. * Inyecta un container en y empuja toasts apilados. * * Uso: * import { toast } from "./toast.js"; * toast({ kind: "error", text: "No se pudo guardar" }); * toast({ kind: "ok", text: "Listo", ms: 2000 }); */ // Lee var del :root con fallback. Permite que la paleta de toasts se adapte // al tema sin que el archivo conozca los hex. function v(name, fallback) { try { const c = getComputedStyle(document.documentElement).getPropertyValue(name).trim(); return c || fallback; } catch { return fallback; } } function kindColors() { return { error: { bg: v("--err-soft", "#fee2e2"), border: v("--err", "#ef4444"), text: v("--err-text", "#7f1d1d") }, ok: { bg: v("--ok-soft", "#d1fae5"), border: v("--ok", "#10b981"), text: v("--user-text", "#064e3b") }, warn: { bg: v("--warn-soft", "#fef3c7"), border: v("--warn", "#f59e0b"), text: v("--text", "#0f172a") }, info: { bg: v("--accent-soft","#e0f2fe"),border: v("--accent","#0ea5e9"),text: v("--bot-text", "#1e3a8a") }, }; } let _container = null; function ensureContainer() { if (_container) return _container; _container = document.createElement("div"); _container.id = "toast-stack"; Object.assign(_container.style, { position: "fixed", right: "16px", bottom: "16px", display: "flex", flexDirection: "column", gap: "8px", zIndex: "9999", pointerEvents: "none", maxWidth: "420px", }); document.body.appendChild(_container); return _container; } export function toast({ kind = "error", text = "", ms = 4000 } = {}) { if (!text) return; const COLORS = kindColors(); const colors = COLORS[kind] || COLORS.info; const el = document.createElement("div"); Object.assign(el.style, { background: colors.bg, border: `1px solid ${colors.border}`, color: colors.text, padding: "12px 16px", borderRadius: "12px", fontSize: "13px", fontWeight: "500", fontFamily: "var(--font-sans, system-ui)", boxShadow: "var(--shadow-md, 0 4px 12px rgba(15,23,42,.06))", pointerEvents: "auto", cursor: "pointer", transform: "translateX(120%)", transition: "transform .25s ease, opacity .25s ease", opacity: "0", wordBreak: "break-word", overflowWrap: "anywhere", }); el.textContent = String(text); const c = ensureContainer(); c.appendChild(el); // Animar entrada requestAnimationFrame(() => { el.style.transform = "translateX(0)"; el.style.opacity = "1"; }); const dismiss = () => { el.style.transform = "translateX(120%)"; el.style.opacity = "0"; setTimeout(() => el.remove(), 280); }; el.addEventListener("click", dismiss); setTimeout(dismiss, Math.max(800, ms)); } export function toastError(text) { toast({ kind: "error", text }); } export function toastOk(text) { toast({ kind: "ok", text, ms: 2500 }); } export function toastWarn(text) { toast({ kind: "warn", text }); }