1.目的
今回 LWC で ContextMenu コンポーネントを作成しようと思います、
基本的な考え方は右クリックをすると、カーソルの座標を取得し、
その座標を使って、ContextMenu の位置を設定します。
2.ソース構成
lwc
├─contextMenu
└─contextMenuContainer
div {
display: block;
}
.contextmenu {
background-color: #fff;
background-clip: padding-box;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.25rem;
color: #373a3c;
margin: 2px 0 0;
min-width: 10em;
outline: none;
padding: 2px 0;
/_ pointer-events: none; _/
text-align: left;
transition: opacity 250ms ease !important;
z-index: 9999;
position: fixed;
opacity: 1;
}
.contextmenu-item {
background: 0 0;
border: 0;
color: #373a3c;
cursor: pointer;
font-weight: 400;
line-height: 1.5;
padding: 3px 20px;
text-align: inherit;
white-space: nowrap;
}
.contextmenu-item:active {
outline: none;
}
.contextmenu-item:hover {
background-color: rgb(78, 107, 235);
color: #fff;
}
<template>
<template if:true="{_showMenu}">
<div
role="menu"
tabindex="-1"
class="contextmenu"
style="{position}"
oncontextmenu="{contextMenuHadler}"
>
<template for:each="{_menuItem}" for:item="item">
<div
key="{item.id}"
data-id="{item.id}"
data-name="{item.name}"
data-value="{item.value}"
onclick="{clickHandler}"
class="contextmenu-item"
role="menuitem"
tabindex="-1"
aria-disabled="false"
>
{item.name}
</div>
</template>
</div>
</template>
</template>
import { LightningElement, api, track } from "lwc";
export default class MenuComponent extends LightningElement {
@track _positionX;
@track _positionY;
@track _showMenu;
@track _menuItem;
@api name;
/**
* スタイル取得
*/
get position() {
const { _positionX, _positionY } = this;
if (_positionX && _positionY) {
// return `position:absolute;z-index: 999; top: ${_positionY}px; left:${_positionX}px`;
return `top: ${_positionY}px; left:${_positionX}px`;
} else {
this.closeMenu();
return "";
}
}
/**
* 座標Xを取得
*/
@api
get positionX() {
return this._positionX;
}
/**
* 座標Xを設定
*/
set positionX(val) {
this._positionX = val;
}
/**
* 座標Yを取得
*/
@api
get positionY() {
return this._positionY;
}
/**
* 座標Yを設定
*/
set positionY(val) {
this._positionY = val;
}
/**
* メニューアイテムを取得
*/
@api
get menuItem() {
return this._menuItem;
}
/**
* メニューアイテムを設定
*/
set menuItem(val) {
this._menuItem = val || [];
}
/**
* メニューアイテム押下
* @param {*} event
*/
clickHandler(event) {
event.preventDefault();
event.stopPropagation();
let target = event.target;
let id = target.dataset.id;
let name = target.dataset.name;
let value = target.dataset.value;
this.closeMenu();
const clickEvent = new CustomEvent("rightclick", {
detail: {
id,
name,
value,
},
});
this.dispatchEvent(clickEvent);
}
@api
closeMenu() {
this._showMenu = false;
}
@api
openMenu() {
this._showMenu = true;
}
/**
*
* @param {*} event
* @returns
*/
contextMenuHadler(event) {
event.preventDefault();
return false;
}
}
<template>
<c-context-menu
position-x="{positionX}"
position-y="{positionY}"
menu-item="{menuItem}"
onrightclick="{contextMenuClickHandler}"
>
</c-context-menu>
<div
class="slds-card"
style="width:100%;height:300px;"
onmousedown="{rightClick}"
oncontextmenu="{contextMenuHadler}"
></div>
</template>
import { LightningElement, track } from "lwc";
export default class ContextMenuContainer extends LightningElement {
@track positionX;
@track positionY;
//メニューアイテム設定
@track menuItem = [
{
id: 0,
name: "メニュー1",
value: "menu1",
},
{
id: 1,
name: "メニュー2",
value: "menu2",
},
];
/**
* 右クリック
* @param {*} event
*/
rightClick(event) {
if (event.which == 3) {
this.positionX = event.clientX;
this.positionY = event.clientY;
//右クリックメニュー表示
this.template.querySelector("c-context-menu").openMenu();
} else {
//右クリックメニュー閉じる
this.template.querySelector("c-context-menu").closeMenu();
}
}
/**
*
* @param {*} event
* @returns
*/
contextMenuHadler(event) {
event.preventDefault();
return false;
}
/**
* 右クリックメニューハンドラー
* @param {*} event
*/
contextMenuClickHandler(event) {
let result = event.detail;
const { id, name, value } = result;
console.log(id, name, value);
}
}