模仿antdmessage组件封装的简约版Toast组件.

组件文件(.jsx)

Toast/index.jsx

/\*\*

 \* @nameToast

 \* @authordarcrand

 \* @date 2020-01-20

 \* @desc 提示信息

 \*/

  

importReact, { Component } from'react'

importReactDOMfrom'react-dom'

import { CSSTransition, TransitionGroup } from'react-transition-group'

import'./styles.less'

  

importicoInfofrom'@/assets/component-toast/toast-info.svg'

importicoSuccessfrom'@/assets/component-toast/toast-success.svg'

importicoWarningfrom'@/assets/component-toast/toast-warning.svg'

importicoErrorfrom'@/assets/component-toast/toast-error.svg'

  

// 切换动画持续时间(ms), 需要和'less'中的'@time'保持一致

constANIMATION\_DURATION\_TIME\=250

// api(提示)类型

constTOAST\_TYPES\= \['info', 'success', 'warning', 'error'\]

// 持续时间(ms)

constDEFAULT\_DURATION\=3000

  

classToastextendsComponent {

state\= {

zIndex:100, // 防止被遮盖

top:0, // 距离顶部的位置(px)

defaultDuration:DEFAULT\_DURATION, // 提示持续的时间(ms)

list: \[\], // 提示内容列表

  }

  

setConfig\= (config\= {}) \=> {

const { zIndex\=100, top\=0, defaultDuration\=DEFAULT\_DURATION } \=config

this.setState({ zIndex, top, defaultDuration })

  }

  

/\*\*

   \* @description 添加提示

   \*

   \* @param{Object}options 选项

   \* @param{String}options.type 类型 enum: TOAST\_TYPES

   \* @param{String}options.content 内容

   \* @param{Number}options.duration 持续时间

   \*/

add\= (options\= {}) \=> {

const { list, defaultDuration } \=this.state

constid\=Date.now()

constitem\= { id, ...options }

this.setState({ list:list.concat(item) }, () \=> {

consttimer\=setTimeout(() \=> {

clearTimeout(timer)

this.setState(prev\=> ({ list:prev.list.filter(v\=>v.id!==id) }))

      }, options.duration||defaultDuration)

    })

  }

  

render() {

const { zIndex, top, list } \=this.state

if (!Array.isArray(list)) {

returnfalse

    }

  

return (

<divclassName\="top-toast--container"style\={{ zIndex, top }}\>

<TransitionGroupclassName\="top-toast--wrapper"\>

{list.map(v\=> (

<CSSTransitionkey\={v.id}timeout\={ANIMATION\_DURATION\_TIME}classNames\="fade"\>

<div\>

<divclassName\="top-toast--item-content"\>

<iclassName\="item-ico"style\={{ backgroundImage:getIco(v.type) }}/>

<span\>{v.content}</span\>

</div\>

</div\>

</CSSTransition\>

          ))}

</TransitionGroup\>

</div\>

    )

  }

}

  

//自动获取组件挂载节点

functiongetContainer() {

constid\='toast-container-element'

letelContainer\=document.getElementById(id)

if (!elContainer) {

elContainer\=document.createElement('div')

elContainer.setAttribute('id', id)

  }

returnelContainer

}

  

//获取挂载节点的父容器

functiongetRenderNode() {

returndocument.getElementsByTagName('body')\[0\]

}

  

// 获取对应图标

functiongetIco(type) {

switch (type) {

case'success':

return\`url("${icoSuccess}")\`

case'warning':

return\`url("${icoWarning}")\`

case'error':

return\`url("${icoError}")\`

default:

return\`url("${icoInfo}")\`

  }

}

  

functionmount() {

constcontainer\=getContainer()

getRenderNode().appendChild(container)

ReactDOM.render(<Toastref\={onComponentInit}/>, container)

}

  

// 暴露出去的api方法集合

constapi\= {}

  

functiononComponentInit(ins\=null) {

TOAST\_TYPES.forEach(type\=> {

api\[type\] \= (arg\=null) \=> {

// 可传入'string'或'object'类型

if (!arg) {

return

      }

if (typeofarg\==='object') {

const { content\='', duration\=DEFAULT\_DURATION } \=arg

ins.add({ type, content, duration })

      } elseif (typeofarg\==='string') {

ins.add({ type, content:arg })

      }

    }

  })

  

// 配置config

api.config\=config\=>ins.setConfig(config)

}

  

// 执行挂载函数(仅一次)

mount()

  

exportdefaultapi

样式文件(.less)

Toast/styles.less

@item-size: 30px;

@spacing: 5px;

@ico-size: 16px;

@time: 250ms;

  

.top-toast--container {

position: fixed;

//   top: 0;

left: 0;

right: 0;

padding: 10px;

pointer-events: none;

}

  

.top-toast--wrapper {

display: flex;

flex-direction: column;

align-items: center;

list-style: none;

margin: 0;

padding-left: 0;

}

  

.top-toast--item-content {

position: relative;

display: flex;

align-items: center;

height: @item-size;

padding: 010px020+@ico-size;

margin-bottom: @spacing;

border-radius: 4px;

background-color: #fff;

box-shadow: 04px12pxrgba(0, 0, 0, 0.15);

font-size: 14px;

line-height: @item-size;

color: rgba(0, 0, 0, 0.65);

  

\>.item-ico {

position: absolute;

top: 50%;

left: 10px;

display: block;

width: @ico-size;

height: @ico-size;

transform: translateY(\-50%);

background: center/coverno-repeat;

  }

}

  

.fade-enter {

opacity: 0;

height: 0;

transform: translateY(-@item-size);

}

  

.fade-enter-active {

position: relative;

opacity: 1;

height: @item-size+@spacing;

transform: translateY(0);

transition: all@timeease-in-out;

}

  

.fade-exit {

opacity: 1;

height: @item-size+@spacing;

transition: all@timeease-in-out;

}

  

.fade-exit-active {

opacity: 0;

height: 0;

transition: all@timeease-in-out;

}

使用

import Toast from'@/components/Toast'
// 父组件

Toast.info("提示内容")
Toast.success({content:"对象形式参数", duration: 5000})

darcrand
637 声望20 粉丝