modal弹出框

390 阅读2分钟

本人已参与「新人创作礼」活动,一起开启掘金创作之路

弹出框,是我们前端在开发后台管理系统的常用组件,常见的功能如下:

参数作用
visible是否显示当前modal
onOk点击确定的回调
onCancel点击取消的回调
widthmodal的宽度
titlemodal标题
childrenmodal的内容
mask是否展示遮罩
maskClosable点击蒙层是否允许关闭

采用的技术栈:react,styled-components,TypeScript,可直接复制运行

import * as React from 'react'
import ReactDOM from 'react-dom'
import { useState,useEffect } from 'react';
import { MaskCss, ModalBody, ModalTitle, ModalContext, ModalBotton, Span, Img, ModalButtonOk, ModalButtonCancel } from './styled'
import { createRoot } from 'react-dom/client';
interface Props {
    visible: boolean//是否显示当前modal
    onOk?: () => void//点击确定的回调
    onCancel?: () => void//点击取消的回调
    width?: number//modal的宽度
    title?: string//modal标题
    children?: any//modal的内容
    mask?: boolean//是否展示遮罩
    maskClosable?: boolean//点击蒙层是否允许关闭
}
export const Modal = (props: Props) => {
    const { visible, onOk, onCancel, width, title, children, mask, maskClosable } = props
    const [time, setTime] = useState('0s')
    const maskBoole = () => {
        if (!maskClosable) return
        onCancel!()
    }
    useEffect(() => {
        setTime('0.3s')
    }, [visible])
    const modal = () => {
        return ReactDOM.createPortal(<>
            <MaskCss id='modal' style={{ 'backgroundColor': `rgba(0,0,0,${mask ? 0.1 : 0})` }} primary={visible} time={time} onClick={maskBoole}>
            </MaskCss>
            <ModalBody style={{ 'width': `${width ?? 600}px` }} primary={visible} time={time}>
                <ModalTitle>
                    <Span>{visible ? title : ''}</Span>
                    <Img src={require('./sdao.png')} onClick={onCancel} />
                </ModalTitle>
                <ModalContext>
                    {visible ? children : ''}
                </ModalContext>
                <ModalBotton>
                    <ModalButtonOk onClick={onOk}>确定</ModalButtonOk>
                    <ModalButtonCancel onClick={onCancel}>取消</ModalButtonCancel>
                </ModalBotton>
            </ModalBody>
        </>
            , document.body)
    }
    return (
        <>
            {modal()}
        </>
    )
}
interface PropsShop {
    onOk: () => void//点击确定的回调
    onCancel: () => void//点击取消的回调
    title: string//modal标题
    children: any//modal的内容
}
let data: any = null
Modal.shop = (props: PropsShop) => {//静态调用
    if (data) return
    const modalbox = data = document.createElement('div')
    document.body.appendChild(modalbox)
    const ModalApp=()=>{
    const [visible,setVisible]=useState<boolean>(true)
    const onOk=()=>{
        props.onOk()
        setVisible(false)
    }
    const onCancel=()=>{
        props.onCancel()
        setVisible(false)
    }
    useEffect(()=>{
        if(visible)return
            document.body.removeChild(modalbox)
            data = null
    },[visible])
    return <Modal {...props} visible={visible} onOk={onOk} onCancel={onCancel} />
}
    const modalDom = createRoot(modalbox)
    modalDom.render(<ModalApp/>)
}

css

import styled, { keyframes } from 'styled-components'
const dh=keyframes`
from{
    opacity:0
}
to{
    opacity:1
}
`
const hd = keyframes`
from{
    opacity:1
}
to{
    opacity:0
}
`
export const MaskCss = styled.div<{ primary?: boolean, time?: string }>`
    position: fixed;
    top:0;
    height:100vh;
    width:100vw;
    z-index:${props => props.primary ? 1 : -2};
    opacity:${props => props.primary ? 1 : 0};
    animation:${props => props.primary ? dh : hd} ${props => props.time}
`
export const ModalBody = styled.div<{ primary?: Boolean, time?: string }>`
    background-color: white;
    border-radius: 5px;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    border:1px solid rgb(200,200,200);
    z-index:${props => props.primary ? 3 : -3};
    opacity:${props => props.primary ? 1 : 0};
    animation:${props => props.primary ? dh : hd} ${props => props.time}
`
export const ModalTitle=styled.div`
    padding:8px;
    border-bottom:1px solid rgb(220,220,220);
    display:flex;
    justify-content: space-between;
    align-items: center;
`
export const ModalContext=styled.div`
    padding:10px;
    min-height:300px;
    border-bottom:1px solid rgb(220,220,220);
`
export const ModalBotton=styled.div`
    padding:8px;
    display:flex;
    justify-content:end
`
export const Img=styled.img`
    width:20px
`
export const ModalButtonOk=styled.button`
    border:1px solid white;
    border-radius:5px;
    padding:6px 10px;
    color:white;
    background-color:#1296db;
    margin-left:20px
`
export const ModalButtonCancel=styled(ModalButtonOk)`
    background-color:white;
    color:black;
    border:1px solid rgb(200,200,200)
`
export const Span=styled.span`
    font-size:18px
`
export const LabelBotton=styled(ModalButtonCancel)`
    color:20px
    border:1px solid black
`