<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>