115 lines
3.2 KiB
JavaScript
115 lines
3.2 KiB
JavaScript
// - Create your context menu by instantiating ContextMenu and adding elements to it
|
|
// - Open your menu in response to a contextmenu event by using globalContextMenuSpace.openAtCursor(event, menu)
|
|
|
|
import { listen } from "rkgk/framework.js";
|
|
|
|
export class ContextMenu extends HTMLElement {
|
|
connectedCallback() {
|
|
this.classList.add("rkgk-panel");
|
|
|
|
this.closeController = new AbortController();
|
|
|
|
this.hasMouse = false;
|
|
this.addEventListener("mouseover", () => (this.hasMouse = true));
|
|
this.addEventListener("mouseleave", () => (this.hasMouse = false));
|
|
|
|
window.addEventListener(
|
|
"mousedown",
|
|
() => {
|
|
if (!this.hasMouse) {
|
|
this.close();
|
|
}
|
|
},
|
|
{ signal: this.closeController.signal },
|
|
);
|
|
|
|
window.addEventListener(
|
|
"keydown",
|
|
(event) => {
|
|
if (event.key == "Escape") {
|
|
this.close();
|
|
}
|
|
},
|
|
{ signal: this.closeController.signal },
|
|
);
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
this.closeController.abort();
|
|
}
|
|
|
|
close() {
|
|
this.space.close(this);
|
|
}
|
|
|
|
addButton(label, options) {
|
|
let { classList, disabled, tooltip, noCloseOnClick } = options ?? {};
|
|
classList ??= [];
|
|
disabled ??= false;
|
|
tooltip ??= "";
|
|
noCloseOnClick ??= false;
|
|
|
|
let button = this.appendChild(document.createElement("button"));
|
|
button.classList.add(...classList);
|
|
button.innerText = label;
|
|
button.disabled = disabled;
|
|
button.title = tooltip;
|
|
|
|
button.addEventListener("click", () => {
|
|
if (!noCloseOnClick) this.close();
|
|
});
|
|
|
|
return button;
|
|
}
|
|
|
|
addHtmlP(text, options) {
|
|
let { classList } = options ?? {};
|
|
classList ??= [];
|
|
|
|
let p = this.appendChild(document.createElement("p"));
|
|
p.innerHTML = text;
|
|
p.classList.add(...classList);
|
|
return p;
|
|
}
|
|
|
|
addSeparator() {
|
|
this.appendChild(document.createElement("hr"));
|
|
}
|
|
}
|
|
|
|
customElements.define("rkgk-context-menu", ContextMenu);
|
|
|
|
export class ContextMenuSpace extends HTMLElement {
|
|
open(contextMenu) {
|
|
contextMenu.space = this;
|
|
this.appendChild(contextMenu);
|
|
}
|
|
|
|
close(contextMenu) {
|
|
this.removeChild(contextMenu);
|
|
}
|
|
|
|
openAtCursor(cursorEvent, contextMenu) {
|
|
this.open(contextMenu);
|
|
|
|
// NOTE: This assumes the context menu space is positioned at (0, 0) and spans the full page.
|
|
|
|
let areaWidth = this.clientWidth;
|
|
let areaHeight = this.clientHeight;
|
|
let menuWidth = contextMenu.clientWidth;
|
|
let menuHeight = contextMenu.clientHeight;
|
|
|
|
let x = cursorEvent.clientX;
|
|
let y = cursorEvent.clientY;
|
|
|
|
// If the menu would reach outside the screen bounds, snap it back in.
|
|
if (x + menuWidth > areaWidth) x -= menuWidth;
|
|
if (y + menuHeight > areaHeight) y -= menuHeight;
|
|
|
|
contextMenu.style.transform = `translate(${x}px, ${y}px)`;
|
|
}
|
|
}
|
|
|
|
customElements.define("rkgk-context-menu-space", ContextMenuSpace);
|
|
|
|
export const globalContextMenuSpace = document.querySelector("rkgk-context-menu-space");
|