Greasy Fork

Toast

Toast或Toast[type] 调用

目前为 2020-12-05 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.greasyfork.icu/scripts/411093/876846/Toast.js

/* Toast */

/* global Vue Toast */

;(function() {
  'use strict'

  window.Toast = function(options, duration) {
    if (typeof options === 'string') {
      options = { content: options }
    }
    // 参数
    options = Object.assign({
      content: '',
      type: 'info',
    }, options)
    // 0时不会自动关闭 默认3s
    options.duration = duration ?? options.duration ?? 3000
    // 0时 closable默认打开
    if (options.duration === 0 && options.closable == null) {
      options.closable = true
    }

    const { createApp, h, ref, onMounted, Transition } = Vue
    const rootContainer = document.createElement('div')
    const app = createApp({
      setup() {
        const content = ref(options.content)
        const type = ref(options.type)
        const closable = ref(options.closable)
        const visible = ref(false)

        onMounted(() => {
          visible.value = true
          if (options.duration > 0) {
            setTimeout(close, options.duration)
          }
        })
        // export-api
        const close = () => {
          visible.value = false
        }
        // private-api
        const onAfterLeave = () => {
          app.unmount(rootContainer)
          rootContainer.remove()
        }

        return {
          content,
          type,
          closable,
          visible,
          close,
          onAfterLeave,
        }
      },
      render() {
        const { visible, type, content, closable, close, onAfterLeave } = this
        return h(Transition, { name: 'inject-toast-slide-fade', appear: true, onAfterLeave }, {
          default: () => (
            visible &&
            h('div', { class: 'inject-toast' },
              h('div', { class: ['inject-toast-content', `inject-toast-content--${type}`] }, {
                default: () => [
                  h('div', { class: 'inject-toast-content-text', innerHTML: content }),
                  closable && h('button', { class: 'inject-toast-content-close', onClick: close }, '×'),
                ],
              }),
            )
          ),
        })
      },
    })

    const toast = app.mount(rootContainer)
    insertElementInContainer(rootContainer)

    return {
      // 关闭
      close: toast.close,
    }
  }

  ;['info', 'success', 'warning', 'error'].forEach(type => {
    Toast[type] = function(options, duration) {
      if (typeof options === 'string') {
        options = { content: options }
      }
      options = {
        ...options,
        type,
      }
      return Toast(options, duration)
    }
  })

  function safeAppendElement(cb) {
    document.body ? cb() : window.addEventListener('DOMContentLoaded', cb)
  }

  function insertElementInContainer(elememnt) {
    function getContainer() {
      const classname = 'inject-toast-container'
      let containerEl = document.querySelector('.' + classname)
      if (containerEl == null) {
        containerEl = document.createElement('div')
        containerEl.classList.add(classname)
        document.body.appendChild(containerEl)
      }
      return containerEl
    }
    safeAppendElement(() => {
      getContainer().appendChild(elememnt)
    })
  }

  ;(function addStyle() {
    const styleEl = document.createElement('style')
    styleEl.appendChild(document.createTextNode(`
      .inject-toast-container {
        position: fixed;
        z-index: 99999;
        top: 80px;
        right: 0;
        left: 0;
        pointer-events: none;
        text-align: center;
      }
      .inject-toast {
        contain: content;
        max-height: 100vh;
        transition: all .3s ease-in-out;
      }
      |> {
        pointer-events: auto;
        display: inline-flex;
        justify-content: center;
        margin-bottom: 10px;
        padding: 8px 16px;
        max-width: 90vw;
        font-size: 14px;
        line-height: 1.5em;
        border: 1px solid;
        box-shadow: 0 2px 3px rgba(0,0,0,.1);
      }
      |>--info {
        color: #2e8bf0;
        background: #f0faff;
        border-color: #d4eeff;
      }
      |>--success {
        color: #19bf6c;
        background: #edfff3;
        border-color: #bbf2cf;
      }
      |>--warning {
        color: #f90;
        background: #fff9e6;
        border-color: #ffe7a3;
      }
      |>--error {
        color: #ed3f13;
        background: #ffefe6;
        border-color: #ffcfb8;
      }
      |>-text {
        flex: auto;
      }
      |>-close {
        flex: none;
        width: 20px;
        margin: 0 -8px 0 10px;
        padding: 0;
        font-size: 16px;
        color: #ababab;
        border: none;
        background: transparent;
        cursor: pointer;
      }

      /* 动画 */
      .inject-toast-slide-fade-enter-active,
      .inject-toast-slide-fade-leave-active {
        transition: all .3s;
       }
      .inject-toast-slide-fade-enter-from {
        transform: translateY(-50%);
        opacity: 0;
      }
      .inject-toast-slide-fade-leave-to {
        transform: translateY(50%);
        max-height: 0;
        padding: 0;
        opacity: 0;
      }
    `.replace(/\|>/g, '.inject-toast-content')))
    document.head.appendChild(styleEl)
  })()
// eslint-disable-next-line semi
})();