模仿antd
的message
組件封裝的簡約版Toast
組件.
Toast/index.jsx
react
/\*\* \* @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
Toast/styles.less
api
@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})