自定义右键菜单(react)

3,243 阅读2分钟

由于我们项目只需要兼容chrome即可,所以我增加的是chrome的版本判断

参考资料

实现代码:

import * as React from 'react';

export interface ContextMenuInputProps {
    className?: string;
    value?: string;
    type?: string;
    spellCheck?: boolean;
    placeholder?: string;
    onChange?: (value: string) => void;
    onClick?: () => void;
    onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
    onFocus?: () => void;
    onBlur?: () => void;
}

export class ContextMenuInput extends React.Component<ContextMenuInputProps, any> {
    private inputElement: React.RefObject<HTMLInputElement>;
    private menuElement: any;
    private selectionStart: number;
    private clipTextLen: number;

    constructor(props: ContextMenuInputProps) {
        super(props);
        this.state = {
            displayValue: props.value || '',
        };
        this.inputElement = React.createRef();
        this.selectionStart = 0;
        this.clipTextLen = 0;
    }

    public componentWillReceiveProps(nextProps: ContextMenuInputProps) {
        if (this.props.value !== nextProps.value) {
            this.setState({
                displayValue: nextProps.value,
            });
        }
    }

    public onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        this.setState({
            displayValue: e.target.value,
        }, () => {
            if (this.props.onChange) {
                this.props.onChange(this.state.displayValue);
            }
        });
    }

    public onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
        if (this.props.onKeyDown) {
            this.props.onKeyDown(e);
        }
    }

    public onClick = () => {
        // 光标位置
        if (this.inputElement.current) {
            this.selectionStart = this.inputElement.current.selectionStart || 0;
        }

        // 剪切板上字符长度
        if (this.hasRightMenu) {
            const clipboard = (navigator as any).clipboard;
            clipboard.readText().then((clipText: any) => {
                this.clipTextLen = clipText.trim().length;
                // console.log('clipText.trim().length', clipText.trim().length);
                if (this.clipTextLen === 0) {
                    this.menuElement.style.display = 'none';
                }
            });
        }

        if (this.props.onClick) {
            this.props.onClick();
        }
    }

<!--获取当前鼠标点击位置,并将位置赋给自定义右键菜单-->
    public customContextMenu = (event: any) => {
        event.preventDefault ? event.preventDefault() : (event.returnValue = false);

        if (this.hasRightMenu) {
            const cstCM = this.menuElement;
            cstCM.style.left = event.clientX + 'px';
            cstCM.style.top = event.clientY + 'px';
            cstCM.style.display = 'block';
            <!--点击document 是右键菜单消失-->
            document.body.onclick = this.clearCustomCM;
            // document.onmousedown = this.clearCustomCM;
        }
    }

    public clearCustomCM = () => {
        this.menuElement.style.display = 'none';
        document.onmousedown = null;
    }
    
<!--复制-->
    public copy = (text: any) => {
        (navigator as any).clipboard.writeText(text).then(function () {
            // console.log('复制成功');
        }, () => {
            this.clearCustomCM();
        });
    }
    
<!--粘贴-->
    public paste = () => {
        const clipboard = (navigator as any).clipboard;
        clipboard.readText().then((clipText: any) => {
            // console.log('clipText' + clipText);
            let pasteClipText = clipText.trim();
            if (this.selectionStart > 0) {
                pasteClipText = this.state.displayValue.slice(0, this.selectionStart) + clipText.trim() + this.state.displayValue.slice(this.selectionStart);
            }
            this.setState({
                displayValue: pasteClipText,
            }, () => {
                if (this.inputElement.current) {
                    this.inputElement.current.focus();
                }
                if (this.props.onChange) {
                    this.props.onChange(this.state.displayValue);
                }
            });
        }, () => {
            this.clearCustomCM();
        });
    }
    <!--判断chrome 浏览器版本号-->
    public getChromeVersion = () => {
        const arr = navigator.userAgent.split(' ');
        let chromeVersion = '';
        for (let i = 0; i < arr.length; i++) {
            if (/chrome/i.test(arr[i])) {
                chromeVersion = arr[i];
            }
        }
        if (chromeVersion) {
            return Number(chromeVersion.split('/')[1].split('.')[0]);
        } else {
            return false;
        }
    }
    
    <!--浏览器的版本控制自定义右键菜单的显示与否-->
    public hasRightMenu = () => {
        if (this.getChromeVersion()) {
            const version = this.getChromeVersion();
            if (version < 66) {
                this.menuElement.style.display = 'none';
                return false;
            }
            return true;
        } else {
            return false;
        }
    }

    public render() {
        const { displayValue } = this.state;
        const { type, placeholder, className, spellCheck, onFocus, onBlur } = this.props;

        return <>
            <input
                ref={this.inputElement}
                type={type}
                spellCheck={spellCheck}
                className={`${className ? className : ''}`}
                value={displayValue}
                placeholder={placeholder}
                onChange={this.onChange}
                onKeyDown={this.onKeyDown}
                onClick={this.onClick}
                onFocus={onFocus}
                onBlur={onBlur}
                onContextMenu={this.customContextMenu}
            />

            <div ref={node => this.menuElement = node} style={{ position: 'fixed', zIndex: 11111, display: 'none' }}>
                 <ul>
                    <li onClick={() => this.copy(displayValue)}>复制<span>ctrl+C</span></li>
                    <li onClick={() => this.paste()}>粘贴<span>ctrl+V</span></li>
                </ul>
            </div>
        </ >;
    }
}