;(() => { // ---- Config -------------------------------------------------------------- const MAX_PER_POSITION = 3; // wie viele Toasts pro Ecke sichtbar const ROOT_PREFIX = 'toastra-root-'; // pro Position eigener Container const POS_STYLES = { 'top-left' : ['top:1.5rem','left:1.5rem'], 'top-center' : ['top:1.5rem','left:50%','transform:translateX(-50%)'], 'top-right' : ['top:1.5rem','right:1.5rem'], 'bottom-left' : ['bottom:1.5rem','left:1.5rem'], 'bottom-center': ['bottom:1.5rem','left:50%','transform:translateX(-50%)'], 'bottom-right' : ['bottom:1.5rem','right:1.5rem'], }; // ---- One-time style injection ------------------------------------------- let styleInjected = false; function injectStyleOnce() { if (styleInjected) return; styleInjected = true; const css = ` /* Mount/Unmount Animations */ .tg-card { opacity: 0; transform: translateY(6px); will-change: transform, opacity; } .tg-in { animation: tgIn .28s ease-out forwards; } .tg-out { animation: tgOut .28s ease-in forwards; } @keyframes tgIn { from{opacity:0; transform:translateY(6px)} to{opacity:1; transform:none} } @keyframes tgOut { from{opacity:1; transform:none} to{opacity:0; transform:translateY(6px)} } /* kleine Progress-Move-Anim */ @keyframes tgProgress { 0% { transform: translateX(-60%) } 50%{ transform: translateX(10%) } 100%{ transform: translateX(120%) } } /* Badge im Title (Fallback, falls du kein Tailwind-Badge willst) */ .tg-badge { display:inline-flex; align-items:center; gap:.25rem; padding:.25rem .5rem; border-radius:.5rem; font-size:.7rem; font-weight:700; letter-spacing:.5px; background: rgba(255,255,255,.08); color:#cbd5e1; border:1px solid rgba(255,255,255,.12); } /* Close-Hitbox */ .tg-close { display:inline-flex; align-items:center; justify-content:center; width:32px; height:32px; border-radius:.6rem; background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.12); box-shadow: 0 0 10px rgba(0,0,0,.25); transition: background .15s ease; } .tg-close:hover { background: rgba(255,255,255,.12) } .tg-close svg path { stroke: rgba(229,231,235,.9) } /* Stack-Abstand */ [data-toastra-stack] > div { margin-bottom: .75rem } `; const el = document.createElement('style'); el.textContent = css; document.head.appendChild(el); } // ---- Helpers ------------------------------------------------------------- function ensureRoot(position) { injectStyleOnce(); const pos = POS_STYLES[position] ? position : 'bottom-right'; const id = ROOT_PREFIX + pos; let root = document.getElementById(id); if (!root) { root = document.createElement('div'); root.id = id; root.setAttribute('data-toastra-stack', pos); root.style.position = 'fixed'; root.style.zIndex = '99999'; root.style.pointerEvents = 'none'; root.style.width = 'clamp(260px, 92vw, 480px)'; document.body.appendChild(root); } root.style.cssText = [ 'position:fixed','z-index:99999','pointer-events:none','width:clamp(260px,92vw,480px)' ].concat(POS_STYLES[pos]).join(';'); return root; } function statusMap(state) { switch (state) { case 'done': return { text: 'Erledigt', pill: 'bg-emerald-500/15 text-emerald-300 ring-1 ring-emerald-500/30', icon: 'ph ph-check-circle' }; case 'failed': return { text: 'Fehlgeschlagen', pill: 'bg-rose-500/15 text-rose-300 ring-1 ring-rose-500/30', icon: 'ph ph-x-circle' }; case 'running': return { text: 'Läuft…', pill: 'bg-cyan-500/15 text-cyan-300 ring-1 ring-cyan-500/30', icon: 'ph ph-arrow-clockwise animate-spin' }; default: return { text: 'Wartet…', pill: 'bg-amber-500/15 text-amber-300 ring-1 ring-amber-500/30', icon: 'ph ph-pause-circle' }; } } function buildHTML(o){ const st = statusMap(o.state); const progress = (o.state === 'running' || o.state === 'queued') ? `
${o.message}
` : ''} ${progress} ${o.finalNote ? `${o.finalNote}
` : ''}