提交 e201679f 编写于 作者: Y yewenwen

feat: toast test

上级 73f17152
......@@ -97,7 +97,18 @@
},
{
"name": "操作反馈",
"packages": []
"packages": [
{
"version": "1.0.0",
"name": "Toast",
"type": "component",
"cName": "吐司",
"desc": "轻提示",
"sort": 1,
"show": true,
"author": "VickyYe"
}
]
},
{
"name": "基础组件",
......
import * as React from 'react'
import * as ReactDOM from 'react-dom'
// import { CSSTransition } from 'react-transition-group'
// import classNames from 'classnames'
// import Mask from '../mask'
import Icon from '../icon/index'
import './toast.scss'
export interface NotificationProps {
prefixCls?: string
style?: React.CSSProperties
icon?: string
message: string | React.ReactNode
bottom?: boolean
duration?: number
onClose: () => void
className?: string
mask?: boolean
}
interface State {
show: boolean
}
export default class Notification extends React.PureComponent<NotificationProps, State> {
static defaultProps = {
prefixCls: 'nut-toast',
style: {},
//show:false,
bottom: false,
duration: 1.5,
mask: false,
}
private closeTimer: number | undefined
static newInstance: (properties: NotificationProps, callback: any) => void
constructor(props: NotificationProps) {
super(props)
this.close = this.close.bind(this)
this.startCloseTimer = this.startCloseTimer.bind(this)
this.clearCloseTimer = this.clearCloseTimer.bind(this)
this.close = this.close.bind(this)
this.state = {
show: true,
}
}
close() {
this.setState({
show: false,
})
this.clearCloseTimer()
this.props.onClose()
}
startCloseTimer() {
const { duration } = this.props
if (duration) {
this.closeTimer = window.setTimeout(() => {
this.close()
}, duration * 1000)
}
}
// hasIcon() {
// if (type !== 'text') {
// return true
// } else {
// return !!icon
// }
// }
// toastBodyClass() {
// return `
// nut-toast
// ${center ? 'nut-toast-center' : ''}
// ${hasIcon() ? 'nut-toast-has-icon' : ''}
// ${cover ? 'nut-toast-cover' : ''}
// ${type === 'loading' ? 'nut-toast-loading' : ''}
// ${customClass ? customClass : ''}
// ${size ? 'nut-toast-' + size : ''}
// `
// }
clearCloseTimer() {
if (this.closeTimer) {
clearTimeout(this.closeTimer)
this.closeTimer = -1
}
}
componentDidMount() {
this.startCloseTimer()
}
componentWillUnmount() {
this.clearCloseTimer()
}
render() {
const { className, prefixCls, style, icon, message, bottom, mask } = this.props
const { show } = this.state
// const cls = classNames(prefixCls, className, `${prefixCls}-mask`, {
// [`${prefixCls}-bottom`]: bottom,
// })
return (
<>
<div
className="nut-toast nut-toast-center"
style={{ bottom: 'auto', backgroundColor: 'rgba(0, 0, 0, 0)' }}
>
<div
className="nut-toast-inner"
style={{ bottom: 'auto', backgroundColor: 'rgba(0, 0, 0, .8)' }}
>
<span className="nut-toast-icon-wrapper">
<Icon name={icon ? icon : ''} />
</span>
<br />
<span className="nut-toast-text">{message}</span>
</div>
</div>
</>
)
}
}
Notification.newInstance = (properties, callback) => {
const element = document.createElement('div')
document.body.appendChild(element)
let called = false
function ref(instance: any) {
if (called) {
return
}
called = true
callback({
component: instance,
destroy() {
ReactDOM.unmountComponentAtNode(element)
element && element.parentNode && element.parentNode.removeChild(element)
},
})
}
ReactDOM.render(<Notification {...properties} ref={ref} />, element)
}
import React, { ReactHTML } from 'react'
import ReactDOM from 'react-dom'
import { Toast } from './toast'
// import ToastContainer from './toastContainer'
let instance: any
type type = 'text' | 'success' | 'fail' | 'hide' | 'warn' | 'loading'
const noticeType: type[] = ['text', 'success', 'fail', 'hide', 'warn', 'loading']
export interface IToastProps {
msg: string
id?: string
duration?: number
center?: boolean
type?: string
customClass?: string
bottom?: number
size?: string
icon?: string
textAlignCenter?: boolean
loadingRotate?: boolean
bgColor?: string
onClose?: () => void
unmount?: () => void
cover?: boolean
coverColor?: string
closeOnClickOverlay?: boolean
}
const defaultOptions = {
msg: '',
id: '',
duration: 2000, //显示时间(毫秒)
center: true,
type: 'text',
customClass: '',
bottom: 30,
size: 'base',
icon: '',
textAlignCenter: true,
loadingRotate: true,
bgColor: 'rgba(0, 0, 0, .8)',
onClose: () => {},
unmount: () => {},
cover: false, //透明遮罩层
coverColor: 'rgba(0, 0, 0, 0)',
closeOnClickOverlay: false,
}
export type toastProps = Partial<IToastProps>
interface ConfigOptions {
duration?: number
}
type onClose = () => void
type open = (content: string | IToastProps, duration?: number, onClose?: onClose) => void
interface toast {
id: number
config: (ConfigOptions: ConfigOptions) => void
open: (toastProps: IToastProps, duration?: number, onClose?: onClose) => void
success: open
fail: open
text: open
warn: open
loading: open
hide: open
}
const toast: toast = {
id: 0,
config: (options: ConfigOptions) => {
if (typeof options.duration === 'number') {
Object.assign(defaultOptions, { duration: options.duration })
}
},
open(config: IToastProps) {
let messageProps: IToastProps = { ...defaultOptions, ...config },
key: number | string = toast.id++
if (!config.msg) {
throw new Error('content defined error')
} else {
key = config.id || key
}
let noticeInstance: any = null
const noticePromise = new Promise((resolve, reject) => {
const { onClose, ...rest } = messageProps
console.log(messageProps)
const message = <Toast {...rest}></Toast>
const div = React.createElement('div', null, message)
let mesagecontent = { key, content: message }
console.log(mesagecontent)
})
const result = () => {
console.log(config)
if (noticeInstance) {
noticeInstance.close()
}
}
result.then = (filled: any, rejected: any) => noticePromise.then(filled, rejected)
return result
},
success: setType('success'),
text: setType('text'),
fail: setType('fail'),
warn: setType('warn'),
loading: setType('loading'),
hide: setType('hide'),
}
function setType(type: string): open {
return (
config: IToastProps | string,
duration?: number,
onClose?: (key: string | number) => void
) => {
if (typeof config === 'string') {
const option = { msg: config, type }
if (typeof duration === 'function') {
Object.assign(option, { onClose: duration })
return toast.open(option)
} else if (typeof duration === 'number') {
Object.assign(option, { duration })
}
if (onClose !== undefined) {
Object.assign(option, { onClose })
}
return toast.open(option)
} else {
Object.assign(config, { type })
return toast.open(config)
}
}
}
export { toast }
import React from 'react'
// import { Toast } from './toast'
import Toast from './index'
const ToastDemo = () => {
return (
<>
<div className="demo">
<h2>基础用法</h2>
<div
onClick={() => {
console.log(111, Toast)
console.log(111, Toast.success('这是一个特别长的提示!!!'))
}}
>
click me
</div>
{/* <Toast type={'icon'} msg={'test'} icon={'JD'}></Toast> */}
</div>
</>
)
}
export default ToastDemo
# Toast组件
### 介绍
基于 xxxxxxx
### 安装
## 代码演示
### 基础用法1
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| name | 图标名称或图片链接 | String | - |
| color | 图标颜色 | String | - |
| size | 图标大小,如 '20px' '2em' '2rem' | String | - |
| class-prefix | 类名前缀,用于使用自定义图标 | String | 'nutui-iconfont' |
| tag | HTML 标签 | String | 'i' |
### Events
| 事件名 | 说明 | 回调参数 |
|--------|----------------|--------------|
| click | 点击图标时触发 | event: Event |
import Toast from './test'
export default Toast
import * as React from 'react'
// import CheckCircleOutlined from '@jdcfe/icons-react/CheckCircleOutlined'
// import TipOutlined from '@jdcfe/icons-react/TipOutlined'
// import Icon from '../icon'
import Icon from '../icon/index'
import Notification, { NotificationProps } from './Notification'
// const { JiaZai } = Icon
let messageInstance: any = null
interface IToastOptions {
duration: number
mask: boolean
}
const SHORT = 3
const options: IToastOptions = {
duration: SHORT,
mask: false,
}
function getInstance(props: NotificationProps, callback: (notification: any) => void) {
if (messageInstance) {
messageInstance.destroy()
messageInstance = null
}
Notification.newInstance(props, (notification: any) => {
return callback && callback(notification)
})
}
function notice(
message: string | React.ReactNode,
icon: any,
duration = options.duration,
onClose: (() => void) | undefined | null,
mask = options.mask
) {
function close() {
if (messageInstance) {
messageInstance.destroy()
messageInstance = null
}
if (onClose) {
onClose()
}
}
getInstance(
{
message,
icon,
duration,
onClose: close,
mask,
},
(notification: any) => {
messageInstance = notification
}
)
}
export default {
SHORT,
LONG: 8,
text(message: string | React.ReactNode, duration?: number, onClose?: () => void, mask?: boolean) {
return notice(message, null, duration, onClose, mask)
},
success(
message: string | React.ReactNode,
duration?: number,
icon?: string,
onClose?: () => void,
mask?: boolean
) {
return notice(message, 'JD', duration, onClose, mask)
},
fail(
message: string | React.ReactNode,
icon?: string,
duration?: number,
onClose?: () => void,
mask?: boolean
) {
return notice(message, icon, duration, onClose, mask)
},
loading(
message: string | React.ReactNode,
duration?: number,
icon?: string,
onClose?: () => void,
mask?: boolean
) {
return notice(message, icon, duration, onClose, mask)
},
hide() {
if (messageInstance) {
messageInstance.destroy()
messageInstance = null
}
},
config(option: Partial<IToastOptions> = {}) {
const { duration = SHORT, mask } = option
options.duration = duration
if (mask === true) {
options.mask = true
}
},
}
@keyframes rotation {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
.nut-toast {
position: fixed;
left: 0;
bottom: 150px;
width: 100%;
text-align: center;
pointer-events: none;
z-index: 9999;
font-family: $font-family;
&-small {
.nut-toast-inner {
font-size: $font-size-small;
}
}
&-large {
.nut-toast-inner {
font-size: $font-size-large;
}
}
&-cover {
display: flex;
align-items: center;
justify-content: center;
pointer-events: auto;
height: 100%;
}
&-inner {
display: inline-block;
font-size: $font-size-base;
min-width: 40%;
max-width: 65%;
text-align: center;
padding: 24px 30px;
word-break: break-all;
background: rgba(0, 0, 0, 0);
border-radius: 12px;
color: $white;
}
&-text {
font-size: 14px;
&:empty {
margin-bottom: -8px;
}
}
&-has-icon {
.nut-toast-icon-wrapper {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
}
}
&-center {
top: 50%;
transform: translateY(-50%);
}
&-loading {
.nut-toast-inner {
display: inline-flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.nut-toast-icon-wrapper {
animation: rotation 2s linear infinite;
}
}
}
.toast-fade-enter-active {
transition: opacity 0.3s;
}
.toast-fade-leave-active {
transition: opacity 0.3s;
}
.toast-fade-enter-from,
.toast-fade-leave-to {
opacity: 0;
}
import React, { FunctionComponent, useState, useLayoutEffect } from 'react'
import bem from '@/utils/bem'
import './toast.scss'
import Icon from '../icon/index'
export interface ToastProps {
msg: string
id?: string
duration?: number
center?: boolean
type?: string
customClass?: string
bottom?: number
size?: string
icon?: string
textAlignCenter?: boolean
loadingRotate?: boolean
bgColor?: string
onClose?: (params?: string | number) => void
unmount?: () => void
cover?: boolean
coverColor?: string
closeOnClickOverlay?: boolean
noticeKey: number | string
}
const defaultProps = {
msg: '',
id: '',
duration: 2000, //显示时间(毫秒)
center: true,
type: 'text',
customClass: '',
bottom: 30,
size: 'base',
icon: '',
textAlignCenter: true,
loadingRotate: true,
bgColor: 'rgba(0, 0, 0, .8)',
onClose: (params?: string | number) => {},
unmount: () => {},
cover: false, //透明遮罩层
coverColor: 'rgba(0, 0, 0, 0)',
closeOnClickOverlay: false,
} as ToastProps
export const Toast: FunctionComponent<Partial<ToastProps>> = (props) => {
const {
msg,
id,
duration,
center,
type,
customClass,
bottom,
size,
icon,
textAlignCenter,
loadingRotate,
bgColor,
onClose,
cover,
coverColor,
closeOnClickOverlay,
noticeKey,
} = { ...defaultProps, ...props }
const [mounted, SetMAounted] = useState(false)
const [isRef, SetRef] = useState(false)
const [value, SetValue] = useState(false)
let timer: any
useLayoutEffect(() => {
if (duration) {
show()
}
}, [])
const hasIcon = () => {
if (type !== 'text') {
return true
} else {
return !!icon
}
}
const toastBodyClass = () => {
return `
nut-toast
${center ? 'nut-toast-center' : ''}
${hasIcon() ? 'nut-toast-has-icon' : ''}
${cover ? 'nut-toast-cover' : ''}
${type === 'loading' ? 'nut-toast-loading' : ''}
${customClass ? customClass : ''}
${size ? 'nut-toast-' + size : ''}
`
}
const clickCover = () => {
if (closeOnClickOverlay) {
hide()
}
}
const hide = () => {
SetMAounted(false)
}
const show = () => {
SetMAounted(true)
clearTimer()
if (duration) {
timer = setTimeout(() => {
hide()
}, duration)
}
}
const clearTimer = () => {
if (timer) {
clearTimeout(timer)
timer = null
}
}
const alignStyle = () => {
return center ? 'auto' : bottom + 'px'
}
return mounted ? (
<div className={toastBodyClass()} style={{ bottom: alignStyle(), backgroundColor: coverColor }}>
<div
className="nut-toast-inner"
style={{
textAlign: textAlignCenter ? 'center' : 'left',
backgroundColor: bgColor,
}}
>
{hasIcon() ? (
<span className="nut-toast-icon-wrapper">
<Icon name={icon} />
</span>
) : null}
<span className="nut-toast-text">{msg}</span>
</div>
</div>
) : null
}
Toast.defaultProps = defaultProps
Toast.displayName = 'NutToast'
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册