React,TypeScript,Chakra-UIで右クリックでメニューを表示する

とってもニッチな感じ！  

## 本記事で実現できる事

ReactでTypeScriptでChakra-UIを使っている状態で、  
<b><font color="Red">右クリックした場所でChakra-UIのメニューを表示</font></b>します。  

![20230711_qiita_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954879/26b283fd-6ccd-c591-32f1-f9d2b8cee4e2.png)

今回はChakra-UIのテンプレートを弄って↑な感じで表示させます。  

## 前提

2023/07/11時点の内容です。  

## 準備

プロジェクトの準備です。不要な方はスキップで...  

```:コマンドライン
npx create-react-app react_chakra1 --template @chakra-ui/typescript
```

※react_chakra1は、お好きな名前にして下さい。  

↑で出来た src/App.tsx を編集していきます。  

## コード

先ず App = () => が直接タグを返すようになっているのを{ return (～);}の形に直します。  
ついでに ChakraProvider 直下の Box に onContextMenu の行、  
Grid の後にメニュー(ContextMenu)を追加します。  

```TS:Appのexport部分を修正

//建設予定地１

export const App = () => {

  //建設予定地２

  return (
  <ChakraProvider theme={theme}>
    <Box textAlign="center" fontSize="xl"
+       onContextMenu={onContextMenu}
    >
    　～～～Grid省略～～～
+     <ContextMenu
+         menuItems={["メニュー１", "メニュー２", "メニュー３"]}
+         onMenuItemClick={onMenuItemClick}
+         ref={(node) => menu = node}
+     />
    </Box>
  </ChakraProvider>
  );
}

```

↑の建設予定地１に、メニュー本体を埋めます。

```TS:Appの建設予定地１

//メニューに渡す引数
type MenuProps = {
    menuItems: string[],
    onMenuItemClick: (message: string) => void,
}

//メニュー
class ContextMenu extends React.Component<PropsWithChildren<MenuProps>> {

    menuElm: HTMLButtonElement | null = null;

    constructor(props: MenuProps) {
        super(props);

        this.onCloseButtonClick = this.onCloseButtonClick.bind(this);
        this.thisOnMenuItemClick = this.thisOnMenuItemClick.bind(this);
    }

    show(clientX: number, clientY: number) {

        if (this.menuElm == undefined) {
            return;
        }

        this.menuElm.style.left = clientX + "px";
        this.menuElm.style.top = clientY + "px";

        this.menuElm.style.visibility = "visible";

        this.menuElm.click(); //ここでメニュー内容を展開

        this.menuElm.focus();
    }

    close() {
        if (this.menuElm == undefined) {
            return;
        }

        this.menuElm.style.visibility = "hidden";
    }

    thisOnMenuItemClick(event: React.MouseEvent<HTMLButtonElement>) {
        this.close();

        this.props.onMenuItemClick(event.currentTarget.innerHTML);
    };

    onCloseButtonClick() {
        this.close();
    };

    render() {
        return (
            <Menu>
                <MenuButton
                    visibility="hidden"
                    ref={(node) => this.menuElm = node}
                    position="absolute"
                />
                <MenuList>
                    {this.props.menuItems.map((item) => 
                        <MenuItem
                            key={item}
                            onClick={this.thisOnMenuItemClick}
                        >
                            {item}
                        </MenuItem>)}
                    <MenuItem
                        onClick={this.onCloseButtonClick}
                    >閉じる</MenuItem>
                </MenuList>
            </Menu>
        );
    }
}

```

建設予定地２に、右クリック時のイベントとメニューのイベントを埋めます。  
```TS:Appの建設予定地２

    //建設予定地２
    let menu: ContextMenu | null = null;

    const onContextMenu = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {

        event.preventDefault();

        if (menu == undefined) {
            return;
        }

        menu.show(event.clientX, event.clientY);
    };

    //メニューのイベントを記述
    //　この例ではメニュー１～３がmessageに入る
    const onMenuItemClick = (message: string) => {
        alert(message);
    };
```

これで、右クリックした辺りにメニューが開きます。  
「閉じる」以外ではAlertを表示します。  

後は **menuItems={["メニュー１", "メニュー２", "メニュー３"]} 配列と onMenuItemClick イベント**で各自お好みの処理を実装しましょう♪  

以上、<b><font color="Red">ほぼ自分備忘用ニッチ記事</font></b>でした！  

## 参考情報

- @hanaitama-kaz さん - Reactで右クリックした場所にメニューを表示してみたい。

https://qiita.com/hanaitama-kaz/items/1f7feb55cb2f7ef1b035

- Chakra UI

https://chakra-ui.com/

## 蛇足

ニッチ繋がりで、こんな記事も書いたなぁ...  

https://qiita.com/arimoo/items/cfe9ad5f07480ed92087