/** * 担心各个组件自带的 mask 的行为和样式不一样,统一 mask 的 Mask 组件 * TODO: Mask 应该要支持 clickable,现在是点到 Modal 和 Drawer 的 wrap 触发来了关闭 */ import React, { HTMLAttributes, ReactNode, useEffect, useRef, useState } from 'react' import classnames from 'classnames' import { enableBodyScroll, disableBodyScroll } from 'body-scroll-lock' import { usePrefix } from '../../ConfigProvider' import Portal from '../Portal' import { concat } from '../../utils' import { useLayer } from './layer' // 如果Mask要单独对外开放,需要把这里的 HTMLAttributes 改成 rootHtmlProps,并对相应组件做改动 export type MaskProps = HTMLAttributes & { visible?: boolean /** * invisible 的时候是否 unmount 真实 dom * * @default false */ autoDestroy?: boolean onClick?(e: React.MouseEvent): void children?: ReactNode /** * 是否支持键盘 esc 关闭 * * @default true */ keyboard?: boolean /** * 当按下键盘的 Escape 后触发, keyboard 不传或者为 false 不触发 */ onKeyboard?(): void } export default function Mask(props: MaskProps) { const { onClick, autoDestroy = false, visible = false, children, className, keyboard = true, onKeyboard, ...rest } = props const prefix = usePrefix('mask') const classname = classnames(prefix, visible && concat(prefix, 'visible'), className) const [container, setContainer] = useState(null) const { topmostRoleLevel, roleLevel, zIndex } = useLayer(visible, 'Mask') const rootRef = useRef(null) useEffect(() => { setContainer(document.body) }, []) // 处理键盘 esc 事件 // 可能 Mask 内有类似 Dropdown、Tooltip 这样的弹框不需要立即关闭 Mask useEffect(() => { if (container == null) return if (keyboard === false) return function handleKeydown(event: KeyboardEvent) { if (event.key === 'Escape') { if (topmostRoleLevel === roleLevel) { onKeyboard?.() } } } container.addEventListener('keydown', handleKeydown) return () => container.removeEventListener('keydown', handleKeydown) }, [container, keyboard, onKeyboard, roleLevel, topmostRoleLevel]) useEffect(() => { if (rootRef.current == null) return const root = rootRef.current if (roleLevel === topmostRoleLevel) { if (roleLevel === 0) { // 此 enable 其实是还原到 disable 之前的状态 enableBodyScroll(root) } else { disableBodyScroll(root, { reserveScrollBarGap: true }) } } return () => { enableBodyScroll(root) } }, [roleLevel, topmostRoleLevel, visible]) if (autoDestroy && visible === false) { return null } return (
{children}
) }