自己封装的一套右键菜单组件,id必须唯一且一致,不能以数字开头,否则queryselector会报错。

contextmenu.js

import React, { Fragment, useEffect, useMemo } from "react";
import "./contextMenu.less";

//<ContextMenuTrigger>和<ContextMenu>的id必须唯一且一致,id必须以字母开头,如果以数字开头,querySelector会有问题
function ContextMenu(props) {

    const { id, options } = props;

    const handleClick = (ev, onClick) => {
        if (onClick && typeof onClick == "function") {
            onClick(ev);
        };
        if (/^\d+/.test(id)) {
            console.warn("ID should be start with string!");
            return;
        }
        let dom = document.querySelector(`#${id}`);
        if (dom) {
            dom.style.display = "none";
        }
    }

    const renderLi = useMemo(() => {
        return options && options.map((item, index) => {
            const { label, onClick } = item;
            return <li key={`contextMenu-li-${index}`} onClick={(ev) => handleClick(ev, onClick)}>
                {label}
            </li>
        })
    }, [options]);

    return (
        <div id={id} className="contextMenu" style={{ display: "none" }}>
            <ul>
                {renderLi}
            </ul>
        </div>
    )
}

let clickIds = {};

function ContextMenuTrigger(props) {

    const { children, id } = props;

    useEffect(() => {
        if (!clickIds[id]) {
            document.addEventListener("mousedown", onMouseDown, false);
            clickIds[id] = 1;
        }
        return () => {
            document.removeEventListener("mousedown", onMouseDown, false);
            clickIds = {};
        }
    }, [])

    const onMouseDown = (ev) => {
        if (/^\d+/.test(id)) {
            console.warn("ID should be start with string!");
            return;
        }
        let dom = document.querySelector(`#${id}`);
        if (dom) {
            let clientRect = dom.getBoundingClientRect();
            const { clientX, clientY } = ev;
            const { x, y, width, height } = clientRect;
            if (!(clientX >= x && clientX <= x + width && clientY >= y && clientY <= y + height)) {
                dom.style.display = "none";
            }
        }
    }

    const onContextMenu = (ev) => {
        if (/^\d+/.test(id)) {
            console.warn("ID should be start with string!");
            return;
        }
        let dom = document.querySelector(`#${id}`);
        if (dom) {
            dom.style.display = "block";
            dom.style.left = ev.clientX + "px";
            dom.style.top = ev.clientY + "px";
        }
        ev.preventDefault();
    }
    const newChildren = React.cloneElement(children, { onContextMenu: onContextMenu });

    return <Fragment>
        {newChildren}
    </Fragment>
}

ContextMenu.defaultProps = {
    options: [],
}

export { ContextMenu, ContextMenuTrigger };

contextmenu.less

.contextMenu {
    position        : fixed;
    background-color: white;
    border          : 1px solid #888888;
    box-shadow      : 2px 2px 3px 0px #888888;
    z-index         : 999;
    cursor          : default;

    ul {
        li {
            padding: 5px;

            &:hover {
                background-color: #dddddd;
            }
        }
    }
}

点墨
26 声望3 粉丝

全栈前端开发工程师