import './context-menu-modal.less' import classnames from 'classnames'; import { CSSProperties, ReactNode, useEffect, useRef, useState } from 'react'; import React from 'react'; import ReactDOM from 'react-dom'; const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, } interface IProps { left?: number top?: number // 自动判断位置使菜单不超出屏幕区域 autoAdjust?: boolean width?: number visible?: boolean mask?: boolean wrapperClassname?: string children?: React.ReactNode forceRender?: boolean onCancel?: () => void onEscape?: () => void onRightClick?: (e: React.MouseEvent) => void maskStyle?: CSSProperties // 可以在modal体外包裹组件,实现自定义功能,如拖拽 modalRender?: (modal: ReactNode) => ReactNode } function ContextMenuFC(props: IProps) { let [container, setContainer] = useState(null); let [bottom, setBottom] = useState(); let [right, setRight] = useState(); const ref = useRef(); useEffect(() => { document.addEventListener('keydown', onKeyup); if (props.forceRender) { let container = document.createElement('div'); setContainer(container); document.body.appendChild(container); } return () => { container?.parentElement?.removeChild(container); document.removeEventListener('keydown', onKeyup); } }, []) const onKeyup = (event) => { let keyCode = event.keyCode; if (keyCode === 27) { //escape if (props.onEscape) { props.onEscape(); } } } useEffect(() => { // if (props.forceRender) return; if (!!props.visible) { if (container == null) { container = document.createElement('div'); setContainer(container); } document.body.appendChild(container); } if (!props.visible && !props.forceRender) { container?.parentElement.removeChild(container); setContainer(null); } }, [props.visible]) useEffect(() => { if (!ref.current) return; let clientH = document.body.clientHeight; if (props.autoAdjust && ref.current!.clientHeight + props.top > clientH) { setBottom(clientH - props.top); } else { setBottom(null); } // right let clientW = document.body.clientWidth; if (props.autoAdjust && props.left + ref.current!.clientWidth > clientW) { setRight(0) } else { setRight(null); } }, [props.visible, container]) const onClick = (e) => { if (e.button === MOUSE.LEFT) { if (checkClickInBound(e)) return; if (props.onCancel) { props.onCancel(); } } } const onRightClick = (e) => { if (props.onRightClick) { e.preventDefault(); props.onRightClick(e); } else if (props.onCancel) { e.preventDefault(); props.onCancel(); } } const checkClickInBound = (e) => { let bound = ref.current?.getBoundingClientRect(); if (!bound) return false; let x = e.clientX; let y = e.clientY; return x>bound.left && xbound.top && y onRightClick(e)} style={props.maskStyle}>
{props.children}
) return ( (props.visible || props.forceRender) && container && (ReactDOM.createPortal( !!props.modalRender ? props.modalRender(modal) : modal, container)) ) } ContextMenuFC.defaultProps = { mask: true, forceRender: false, left: 0, top: 0, autoAdjust: true, } export default ContextMenuFC;