确定api
我们的dialog是否隐藏是用户可以直接在外面设置的,所以这个visible需要作为一个props传入,而不是组件内部的状态
- dialog/dialog.tsx
import React from 'react'
interface Props {
visible: boolean
}
const Dialog: React.FunctionComponent<Props> = (props) => {
return (
props.visible ?
<div>dialog</div> :
null
)
}
export default Dialog
- dialog/dialog.example.tsx
import React, {useState} from 'react';
import Dialog from './dialog';
export default function () {
const [x, setX] = useState(false);
return (
<div>
<button onClick={() => setX(!x)}>click</button>
<Dialog visible={x}/>
</div>
);
}
用户自定义传入button
这里我们把buttons参数作为一个数组,数组里面的每一个元素都是一个ReactElement元素
- dialog.tsx
interface Props {
+ buttons: ReactElement[]
}
<footer className={sc('footer')}>
{props.buttons}
</footer>
如果用户想要关闭弹窗需要再自己传入的按钮里单独调用关闭方法
- dialog.example.tsx
<Dialog visible={x} title="你好啊" buttons={
[
<Button type="primary" size="mini" onClick={() => setX(false)>ok</Button>,
<Button size="mini" onClick={() => setX(false)}>ok</Button>
]
}>
小改改!
</Dialog>
接受closeOnClickMask参数,来设置可否点击遮罩层关闭弹窗,默认为false
interface Props {
closeOnClickMask?: boolean;
}
<div className={sc('mask')} onClick={(e) => {
if (props.closeOnClickMask) {
props.onClose(e)
}
}}/>
Dialog.defaultProps = {
closeOnClickMask: false
}
传送门(createPortal)
如果我们的组件里用户在外面手动设置了z-index那么我们的弹窗就有可能覆盖不了它
解决方法:把我们的组件元素放到body里,然后把它的z-index尽量设置小一点让用户可以修改
import ReactDOM from 'react-dom'
const Dialog: React.FunctionComponent<Props> = (props) => {
const x = props.visible ?
<Fragment>
<div className={sc('mask')} onClick={(e) => {
if (props.closeOnClickMask) {
props.onClose(e)
}
}}/>
<div className={sc()}>
<div className={sc('close')}>
<Icon name="close" size="mini" onClick={props.onClose}/>
</div>
<header className={sc('header')}>
{props.title ? props.title : '提示'}
</header>
<main className={sc('main')}>
{props.children}
</main>
<footer className={sc('footer')}>
{props.buttons.map((button, index) => {
return React.cloneElement(button, {key: index})
})}
</footer>
</div>
</Fragment>
:
null
return (
// 将x添加到body里
ReactDOM.createPortal(x, document.body)
)
}
实现一个alert
实现:我们可以直接通过alert的方法显示我们的dialog组件
思路:声明一个alert方法,导出它,需要我们动态创建组件(本来组件不存在,用到的时候才存在)
const alert = (content: string) => {
const component = <Dialog visible={true} onClose={() => {
// 关闭的时候重新克隆一个componet组件,并且把克隆的这个visble设置为false,挂载到div上
ReactDOM.render(React.cloneElement(component, {visible: false}), div)
// 把div从页面上卸载
ReactDOM.unmountComponentAtNode(div)
// 删除div
div.remove()
}} title="提示">{content}</Dialog>
const div = document.createElement('div')
document.body.append(div)
ReactDOM.render(component, div)
}
export {alert};
使用
import {alert} from './dialog'
<Button onClick={() => alert('1') }>提示</Button>
实现confirm和modal
- confirm
const confirm = (content: string, yes?: () => void, no?: () => void) => {
const onYes = () => {
yes && yes()
ReactDOM.render(React.cloneElement(component, {visible: false}), div)
ReactDOM.unmountComponentAtNode(div)
div.remove()
}
const onNo = () => {
no && no()
ReactDOM.render(React.cloneElement(component, {visible: false}), div)
ReactDOM.unmountComponentAtNode(div)
div.remove()
}
const component = (
<Dialog onClose={() => {}} visible={true}
buttons={
[
<Button onClick={onYes}>ok</Button>,
<Button onClick={onNo}>cancel</Button>
]
}
>
{content}
</Dialog>
)
const div = document.createElement('div')
document.body.append(div)
ReactDOM.render(component, div)
}
使用
<Button onClick={() => confirm('1',() => {console.log('success')}, () => {console.log('fail')}) }>提示</Button>
- modal
const modal = (content: ReactNode | ReactFragment) => {
const onClose = () => {
ReactDOM.render(React.cloneElement(component, {visible: false}), div)
ReactDOM.unmountComponentAtNode(div)
div.remove()
}
const component = <Dialog onClose={onClose} visible={true}>
{content}
</Dialog>
const div = document.createElement('div')
ReactDOM.render(component, div)
}
使用
<Button onClick={() => modal(<h1>小改改</h1>) }>modal</Button>
问题:我们的modal是无法提供按钮的,而且dialog是被封装在modal里面的,显示是通过visible的render来实现的,如果用户自己写关闭按钮的话,我们无法在外部关闭dialog,也就相当于我们要在一个函数外部改函数内部的变量
解决方法:modal里返回一个可以关闭dialog的方法
const modal = (content: ReactNode | ReactFragment) => {
+ return onClose
}
使用
const openModal = () => {
// close返回的就是关闭dialog的方法,所以button里直接调用就可
const close = modal(<h1>小改改<Button onClick={() => close()}>关闭</Button></h1>)
}
<Button onClick={() => openModal() }>modal</Button>
代码:https://github.com/wanglifa/IReact-UI/tree/94c35f30f67f48d115bd3c7770f90dbf18b61083
网友评论