import { UiElement } from "./UiElement.js";
import "./ContextMenu.css";
export { ContextMenu };
/**
* Right click Context Menu
*
* @author rhess <robin.hess@awi.de>
* @memberof vef.ui
*/
class ContextMenu extends UiElement {
classes = "vef-context-menu";
/**
* @param {HTMLElement | string} target
*/
constructor(target) {
super(null, {
"open": [],
"close": []
});
this.menuCallback_ = (e) => {
e.preventDefault();
const node = e.currentTarget;
const x = e.pageX;
const y = e.pageY;
this.open(node, x, y);
}
this.closeCallback_ = () => this.close();
this.nodes_ = [];
this.target = target || document.body;
this.setClass(this.classes);
}
/**
* clear content of the menu
* @private
*/
clear_() {
this.getElement().innerHTML = "";
}
/**
* close the context-menu
*/
close() {
document.body.removeEventListener("click", this.closeCallback_);
this.clear_();
const element = this.getElement();
element.remove();
element.style.top = "unset";
element.style.right = "unset";
element.style.bottom = "unset";
element.style.left = "unset";
this.fire("close", this);
}
/**
* open the context menu for a registered elemebt
* at a given location.
*
* @param {HTMLElement} node
* @param {number} x
* @param {number} y
*/
open(node, x, y) {
const element = this.getElement();
this.close();
for (let i = 0; i < this.nodes_.length; ++i) {
if (node == this.nodes_[i].node) {
const menuContent = this.nodes_[i].menuContent;
for (let j = 0; j < menuContent.length; ++j) {
// check if the condition for showing the element is ment
if (menuContent[j].condition && !menuContent[j].condition()) continue;
const text = (typeof menuContent[j].text == "function") ? menuContent[j].text(node, x, y) : menuContent[j].text;
const item = document.createElement("div");
item.classList.add("context-menu-item");
item.innerHTML = ((menuContent[j].icon) ? ('<i class="' + menuContent[j].icon + '"></i>') : "") + text;
item.addEventListener("click", (e) => {
this.close();
menuContent[j].callback(e);
});
item.addEventListener("contextmenu", (e) => e.preventDefault());
element.appendChild(item);
}
element.style.visibility = "hidden";
this.appendTo(this.target);
const width = element.clientWidth;
const height = element.clientHeight;
if (window.innerWidth <= (x + width)) {
element.style.right = window.innerWidth - x + "px";
} else {
element.style.left = x + "px";
}
if (window.innerHeight <= (y + height)) {
element.style.bottom = window.innerHeight - y + "px";
} else {
element.style.top = y + "px";
}
element.style.visibility = "visible";
// push to the end of the event loop, so the event is not triggered right after opening
setTimeout(() => document.body.addEventListener("click", this.closeCallback_), 0);
return;
}
}
}
/**
* Register a new element that should react to
* contextmenu events
*
* @param {HTMLElemebt} node
* @param {object[]} menuContent
*/
registerElement(node, menuContent) {
this.nodes_.push({
node: node,
menuContent: menuContent
});
node.addEventListener("contextmenu", this.menuCallback_);
}
/**
* unregister an Element to disable
* contextmenu events
*
* @param {HTMLElement} node
*/
unregisterElement(node) {
for (let i = 0; i < this.nodes_.length; ++i) {
if (node == this.nodes_[i].node) {
node.removeEventListener("contextmenu", this.menuCallback_);
this.nodes_.splice(i, 1);
return;
}
}
}
/**
* Dispose the Contextmenu instance and it's content
*/
dispose() {
this.close();
super.dispose();
for (let i = 0; i < this.nodes_.length; ++i) {
this.nodes_[i].node.removeEventListener("contextmenu", this.menuCallback_);
}
delete this.nodes_;
}
}