From 3145f3dcf596c85cb383365e3b4a8d7d7cda7432 Mon Sep 17 00:00:00 2001 From: jaywcjlove <398188662@qq.com> Date: Thu, 17 Jun 2021 19:59:26 +0800 Subject: [PATCH] refactor(Overlay): refactor Overlay component. --- packages/react-overlay/src/index.tsx | 309 ++++++++++++--------------- 1 file changed, 133 insertions(+), 176 deletions(-) diff --git a/packages/react-overlay/src/index.tsx b/packages/react-overlay/src/index.tsx index e64a1b309b..5d4a33fd82 100644 --- a/packages/react-overlay/src/index.tsx +++ b/packages/react-overlay/src/index.tsx @@ -9,9 +9,7 @@ * 动画效果 * https://daneden.github.io/animate.css/ */ - -import React, { cloneElement } from 'react'; -import ReactDOM from 'react-dom'; +import React, { cloneElement, useEffect, useRef, useState } from 'react'; import { CSSTransition } from 'react-transition-group'; import { TransitionProps } from 'react-transition-group/Transition'; import Portal, { PortalProps } from '@uiw/react-portal'; @@ -38,65 +36,116 @@ export interface OverlayProps extends IProps, Omit { node: HTMLElement | React.MouseEvent, ) => void; onClose?: (evn: React.MouseEvent) => void; - children?: any; } -export interface OverlayState { - isMount: boolean; - isOpen: boolean; -} +export default function Overlay(props: OverlayProps) { + const { + className, + style, + isOpen: _ = false, + prefixCls = 'w-overlay', + usePortal = true, + maskClosable = true, + backdropProps = {}, + portalProps = {}, + hasBackdrop = true, + unmountOnExit = true, // 设置 true 销毁根节点 + timeout = 300, + transitionName = 'w-overlay', + // onEnter = noop, + onOpening = noop, + onOpened = noop, + onClosing = noop, + onClosed = noop, + onClose = noop, + children, + dialogProps = {}, + ...otherProps + } = props; + const [isOpen, setIsOpen] = useState(false); + const [visible, setVisible] = useState(false); + const container = useRef(null); + const overlay = useRef(null); + + useEffect(() => { + if (props.isOpen) { + setVisible(true); + } else { + setIsOpen(false); + } + }, [props.isOpen]); + + useEffect(() => { + if (visible) { + setIsOpen(true); + } + }, [visible]); + + useEffect(() => { + if (!isOpen) { + overlayWillClose(); + } else { + overlayWillOpen(); + } + }, [isOpen]); + + const decoratedChild = + typeof children === 'object' ? ( + cloneElement(children, { + ...dialogProps, + style: { ...children.props.style, ...dialogProps.style }, + className: [children.props.className, `${prefixCls}-content`] + .filter(Boolean) + .join(' ') + .trim(), + tabIndex: 0, + }) + ) : ( + + {children} + + ); -export default class Overlay extends React.Component< - OverlayProps, - OverlayState -> { - public static defaultProps: OverlayProps = { - isOpen: false, - prefixCls: 'w-overlay', - usePortal: true, - maskClosable: true, - backdropProps: {}, - portalProps: {}, - hasBackdrop: true, - unmountOnExit: true, // 设置 true 销毁根节点 - timeout: 300, - transitionName: 'w-overlay', - onEnter: noop, - onOpening: noop, - onOpened: noop, - onClosing: noop, - onClosed: noop, - onClose: noop, - }; - public container!: HTMLDivElement | null; - private visible?: boolean; - constructor(props: OverlayProps) { - super(props); - this.state = { - isMount: false, - isOpen: false, - }; + function handleClosed( + node: HTMLElement | React.MouseEvent, + ) { + setVisible(false); + onClosed && onClosed(node); + overlayWillClose(); } - componentDidMount() { - if (this.props.isOpen) { - this.overlayWillOpen(); + + function handleBackdropMouseDown( + e: React.MouseEvent, + ) { + if (e.target !== container.current && usePortal) { + return; + } + if (maskClosable && hasBackdrop) { + setIsOpen(false); + onClose && onClose(e); } + backdropProps && backdropProps.onMouseDown && backdropProps.onMouseDown(e); } - componentWillUnmount() { - document.removeEventListener('mousedown', this.handleDocumentClick, false); + + function overlayWillOpen() { + if (hasBackdrop && usePortal) { + // add a class to the body to prevent scrolling of content below the overlay + document.body.classList.add(`${prefixCls}-open`); + } + if (maskClosable && !hasBackdrop) { + document.addEventListener('mousedown', handleDocumentClick, false); + } } - overlayWillClose() { - const { prefixCls } = this.props; - document.removeEventListener('mousedown', this.handleDocumentClick, false); + + function overlayWillClose() { + document.removeEventListener('mousedown', handleDocumentClick, false); document.body.classList.remove(`${prefixCls}-open`); - this.setState({ isOpen: false }); } - handleDocumentClick = ( + + function handleDocumentClick( e: MouseEvent | React.MouseEvent, - ) => { - const { maskClosable, onClose } = this.props; - const { isOpen } = this.state; - const domNode = ReactDOM.findDOMNode(this); + ) { + const domNode = overlay.current; if ( isOpen && maskClosable && @@ -104,114 +153,32 @@ export default class Overlay extends React.Component< (domNode.nextSibling === e.target || domNode.nextElementSibling === e.target) ) { - this.setState({ isMount: false }, () => { - onClose && - onClose(e as React.MouseEvent); - }); - } - }; - overlayWillOpen() { - const { prefixCls, maskClosable, hasBackdrop, usePortal } = this.props; - this.setState({ isMount: true, isOpen: true }); - if (this.props.hasBackdrop && usePortal) { - // add a class to the body to prevent scrolling of content below the overlay - document.body.classList.add(`${prefixCls}-open`); - } - if (maskClosable && !hasBackdrop) { - document.addEventListener('mousedown', this.handleDocumentClick, false); - } - } - componentDidUpdate(prevProps: OverlayProps) { - if ( - this.props.isOpen !== this.state.isOpen && - prevProps.isOpen !== this.props.isOpen - ) { - this.props.isOpen ? this.overlayWillOpen() : this.overlayWillClose(); + onClose && onClose(e as React.MouseEvent); } } - handleBackdropMouseDown = ( - e: React.MouseEvent, - ) => { - const { backdropProps, maskClosable, hasBackdrop, usePortal, onClose } = - this.props; - if (e.target !== this.container && usePortal) { - return; - } - if (maskClosable && hasBackdrop) { - this.setState({ isOpen: false }, onClose!.bind(this, e)); - } - backdropProps && backdropProps.onMouseDown && backdropProps.onMouseDown(e); - }; - public onClosed = ( - node: HTMLElement | React.MouseEvent, - ) => { - const { onClosed } = this.props; - if (this.state.isMount) { - this.setState({ isMount: false }, () => { - onClosed && onClosed(node); - }); - } else { - onClosed && onClosed(node); - } - this.overlayWillClose(); - }; - public render() { - const { - prefixCls, - className, - style, - isOpen, - maskClosable, - usePortal, - children, - unmountOnExit, - timeout, - transitionName, - hasBackdrop, - portalProps = {}, - backdropProps = {}, - dialogProps = {}, - onClose, - ...otherProps - } = this.props; - const { onOpening, onOpened, onClosing } = this.props; - const decoratedChild = - typeof children === 'object' ? ( - cloneElement(children, { - ...dialogProps, - style: { ...children.props.style, ...dialogProps.style }, - className: [children.props.className, `${prefixCls}-content`] - .filter(Boolean) - .join(' ') - .trim(), - tabIndex: 0, - }) - ) : ( - - {children} - - ); - const TransitionGroupComp = ( - - {(status) => ( + const TransitionGroupComp = ( + + {(status) => { + return (
, { ...backdropProps, - onMouseDown: this.handleBackdropMouseDown.bind(this), + onMouseDown: handleBackdropMouseDown, className: [`${prefixCls}-backdrop`, backdropProps.className] .filter(Boolean) .join(' ') .trim(), - tabIndex: this.props.maskClosable ? 0 : null, + tabIndex: maskClosable ? 0 : null, })} {usePortal ? (
(this.container = node)} - onMouseDown={this.handleBackdropMouseDown.bind(this)} + ref={container} + onMouseDown={handleBackdropMouseDown} className={`${prefixCls}-container`} > {cloneElement(decoratedChild, { 'data-status': status })} @@ -239,27 +206,17 @@ export default class Overlay extends React.Component< cloneElement(decoratedChild, { 'data-status': status }) )}
- )} - + ); + }} + + ); + if (usePortal) { + return ( + + {TransitionGroupComp} + ); - if (usePortal) { - if (!this.visible) { - this.visible = this.props.isOpen; - } else if ( - this.visible === true && - this.state.isOpen === false && - this.state.isMount === false - ) { - this.visible = false; - } - return ( - - {' '} - {TransitionGroupComp}{' '} - - ); - } else { - return TransitionGroupComp; - } + } else { + return TransitionGroupComp; } }