pvbemu/app/toolkit/Menu.js

226 lines
6.2 KiB
JavaScript

"use strict";
// Selection within a MenuBar
Toolkit.Menu = class Menu extends Toolkit.MenuItem {
// Object constructor
constructor(application, options) {
super(application, options);
// Configure menu element
this.menu = this.add(this.application.newPanel({
layout : "flex",
alignCross: "stretch",
direction : "column",
visible : false
}));
this.menu.element.style.position = "absolute";
this.menu.element.setAttribute("role", "menu");
this.menu.element.setAttribute("aria-labelledby", this.id);
this.menu.element.addEventListener("pointerdown",e=>this.stopEvent(e));
this.menu.element.addEventListener("pointermove",e=>this.stopEvent(e));
this.menu.element.addEventListener("pointerup" ,e=>this.stopEvent(e));
this.containers.push(this.menu);
this.children = this.menu.children;
// Configure element
this.element.setAttribute("aria-expanded", "false");
this.element.setAttribute("aria-haspopup", "menu");
}
///////////////////////////// Public Methods //////////////////////////////
// Create a MenuItem and associate it with the application and component
newMenu(options, index) {
let menu = this.menu.add(new Toolkit.Menu(
this.application, options), index);
menu.child();
menu.element.insertAdjacentElement("afterend", menu.menu.element);
return menu;
}
// Create a MenuItem and associate it with the application and component
newMenuItem(options, index) {
let item = this.menu.add(new Toolkit.MenuItem(
this.application, options), index);
item.child();
return item;
}
// Specify whether the menu is enabled
setEnabled(enabled) {
super.setEnabled(enabled);
if (!this.enabled && this.parent.expanded == this)
this.setExpanded(false);
}
///////////////////////////// Package Methods /////////////////////////////
// The menu item was activated
activate(deeper) {
if (!this.enabled)
return;
this.setExpanded(true);
if (deeper && this.children.length > 0)
this.children[0].focus();
}
// Show or hide the pop-up menu
setExpanded(expanded) {
// Setting expanded to false
if (!expanded) {
// Hide the pop-up menu
this.element.setAttribute("aria-expanded", "false");
this.menu.setVisible(false);
this.parent.expanded = null;
// Close any expanded submenus
if (this.expanded != null)
this.expanded.setExpanded(false);
return;
}
// Hide the existing submenu of the parent
if (this.parent.expanded != null && this.parent.expanded != this)
this.parent.expanded.setExpanded(false);
this.parent.expanded = this;
// Configure element
this.element.setAttribute("aria-expanded", "true");
// Configure pop-up menu
let barBounds = this.menuBar.element.getBoundingClientRect();
let bounds = this.element.getBoundingClientRect();
this.menu.setVisible(true);
this.menu.setLocation(
(bounds.x - barBounds.x) + "px",
(bounds.y + bounds.height - barBounds.y) + "px"
);
}
///////////////////////////// Private Methods /////////////////////////////
// Key press event handler
onkeydown(e) {
let index;
// Processing by key
switch (e.key) {
// Delegate to the MenuItem handler for these keys
case " " :
case "ArrowLeft":
case "End" :
case "Enter" :
case "Escape" :
case "Home" :
return super.onkeydown(e);
// Conditional
case "ArrowDown":
// Open the menu and select the first item (if any)
if (this.parent == this.menuBar)
this.activate(true);
// Delegate to the MenuItem handler
else return super.onkeydown(e);
break;
// Conditional
case "ArrowRight":
// Open the menu and select the first item (if any)
if (this.parent != this.menuBar)
this.activate(true);
// Delegate to the MenuItem handler
else return super.onkeydown(e);
break;
// Conditional
case "ArrowUp":
// Open the menu and select the last item (if any)
if (this.parent == this.menuBar) {
this.activate(false);
index = this.previousChild(0);
if (index != -1)
this.children[index].focus();
}
// Delegate to the MenuItem handler
else return super.onkeydown(e);
break;
default: return;
}
// Configure event
e.preventDefault();
e.stopPropagation();
}
// Pointer down event handler
onpointerdown(e) {
// Configure event
e.preventDefault();
e.stopPropagation();
// Error checking
if (!this.enabled || e.button != 0)
return;
// Activate the menu
this.focus();
this.activate(false);
}
// Pointer move event handler
onpointermove(e) {
// Configure event
e.preventDefault();
e.stopPropagation();
// Error checking
if (
this.parent != this.menuBar ||
this.parent.expanded == null ||
this.parent.expanded == this
) return;
// Activate the menu
this.parent.expanded.setExpanded(false);
this.parent.expanded = this;
this.focus();
this.setExpanded(true);
}
// Pointer up event handler (prevent superclass behavior)
onpointerup(e) {
e.preventDefault();
e.stopPropagation();
}
// Prevent an event from bubbling
stopEvent(e) {
e.preventDefault();
e.stopPropagation();
}
};