<div class="toast-modal {shown ? 'shown' : ''}" role="alert" aria-hidden={shown}> <div class="toast-container"> {text} </div> </div> <style> .toast-modal { position: fixed; bottom: 40px; left: 0; right: 0; opacity: 0; transition: opacity 333ms linear; display: flex; flex-direction: column; align-items: center; pointer-events: none; z-index: 100000; } .toast-container { max-width: 600px; max-height: 20vh; overflow: hidden; display: flex; flex-direction: column; align-items: center; border: 2px solid var(--toast-border); background: var(--toast-bg); border-radius: 5px; margin: 0 40px; padding: 20px; font-size: 1.3em; color: var(--toast-text); } .toast-modal.shown { opacity: 0.9; } @media (max-width: 767px) { .toast-container { max-width: 80vw; } } </style> <script> import { splice, push, observe } from 'svelte-extras' const TIME_TO_SHOW_TOAST = 5000 const DELAY_BETWEEN_TOASTS = 1000 export default { oncreate () { this._queue = Promise.resolve() this.observe('messages', (messages) => { if (messages.length) { this.onNewToast(messages[0]) this.splice('messages', 0, 1) } }) }, data: () => ({ text: '', shown: false, messages: [] }), methods: { observe, push, splice, say (text) { this.push('messages', text) }, onNewToast (text) { this._queue = this._queue.then(() => { this.set({ 'text': text, shown: true }) return new Promise(resolve => { setTimeout(resolve, TIME_TO_SHOW_TOAST) }) }).then(() => { this.set({ shown: false }) return new Promise(resolve => { setTimeout(resolve, DELAY_BETWEEN_TOASTS) }) }) } } } </script>