【JavaScript】【React】右クリックメニューの作り方

 右クリックで開くメニューを作りたい時がしばしばあります。この記事ではその方法の起点を大雑把に紹介し、React での実例を紹介します。
 ブラウザ上の戻るなどのメニューを開くイベントには contextmenu イベントが割り当てられています。素の JavaScript で右クリックメニューに関するなんやかんやをしたい時は contextmenu イベントから順に色々と組み上げていくのが良いです。そうすれば後述のライブラリに縛られない自由なモノが作れます。
ContextMenus – Archive of obsolete content | MDN
Element: contextmenu event – Web APIs | MDN
 `contextmenu ${JavaScript 関連の何か}`でググるとその JavaScript 関連の何かと右クリックメニューを組み合わせたモノが見つかります。React ならば次のリンク先の react-contextmenu ライブラリが右クリックメニュー制御に便利です。
vkbansal/react-contextmenu: Project is no longer maintainedreact-contextmenu – npm
 使い方は次の引用コードの通り、ContextMenuTrigger と ContextMenu の二つのコンポーネントを定義し、それぞれに同じ id を割り当てるだけです。そうすると ContextMenuTrigger 内で contextmenu イベントが走った時、ContextMenu 内の JSX が画面上に表示されます。例では MenuItem という react-contextmenu 内のコンポーネントを使っていますが別に MenuItem を使う必要はありません。

// @see https://github.com/vkbansal/react-contextmenu/blob/master/examples/SimpleMenu.js
import React, { Component } from 'react';

import ContextMenuTrigger from 'src/ContextMenuTrigger';
import ContextMenu from 'src/ContextMenu';
import MenuItem from 'src/MenuItem';

const MENU_TYPE = 'SIMPLE';

export default class SimpleMenu extends Component {
    constructor(props) {
        super(props);

        this.state = { logs: [] };
    }

    handleClick = (e, data) => {
        this.setState(({ logs }) => ({
            logs: [`Clicked on menu ${data.item}`, ...logs]
        }));
    }

    render() {
        return (
            <div>
                <h3>Simple Menu</h3>
                <p>This demo simple usage of a context menu.</p>
                <ContextMenuTrigger id={MENU_TYPE} holdToDisplay={1000}>
                    {/* この ContextMenuTrigger 内を右クリックでメニューを開く */}
                    <div className='well'>right click to see the menu</div>
                </ContextMenuTrigger>
                <div>
                    {this.state.logs.map((log, i) => <p key={i}>{log}</p>)}
                </div>
                <ContextMenu id={MENU_TYPE}>
                    {/* この ContextMenu 内がメニュー実体 */}
                    <MenuItem onClick={this.handleClick} data={{ item: 'item 1' }}>Menu Item 1</MenuItem>
                    <MenuItem onClick={this.handleClick} data={{ item: 'item 2' }}>Menu Item 2</MenuItem>
                    <MenuItem divider />
                    <MenuItem onClick={this.handleClick} data={{ item: 'item 3' }}>Menu Item 3</MenuItem>
                </ContextMenu>
            </div>
        );
    }
}

 実行したデモは次です。

 一々 id を定義するのが面倒ならば次の様に必要な情報を入れたコンポーネントを返す関数を作るのも手です。

import {
  ContextMenu as ContextMenuOrigin,
  ContextMenuProps,
  ContextMenuTrigger as ContextMenuTriggerOrigin,
  ContextMenuTriggerProps,
} from 'react-contextmenu';
import React, { PropsWithChildren } from 'react';

/**
 * 右クリックメニュー用コンポーネント生成器
 */
export function contextMenuProvider(
  id: string | null = null
): {
  ContextMenuTrigger: React.FC<PropsWithChildren<Partial<ContextMenuTriggerProps>>>;
  ContextMenuBody: React.FC<PropsWithChildren<Partial<ContextMenuProps>>>;
} {
  const contextId: string = id || `${Math.random()}`;
  const ContextMenuTrigger = (props: PropsWithChildren<Partial<ContextMenuTriggerProps>>): JSX.Element => (
    <ContextMenuTriggerOrigin id={contextId} {...props}>
      {props.children}
    </ContextMenuTriggerOrigin>
  );
  const ContextMenuBody = (props: PropsWithChildren<Partial<ContextMenuProps>>): JSX.Element => {
    return (
      <ContextMenuOrigin id={contextId} {...props} style={{ zIndex: 1e10 }}>
        {props.children}
      </ContextMenuOrigin>
    );
  };

  return { ContextMenuTrigger: React.memo(ContextMenuTrigger), ContextMenuBody: React.memo(ContextMenuBody) };
}

 このようなどこでも開けるメニューを作ることで複雑で細かい操作項目をメニュー内に押し込めることができます。

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG