"use strict"; // Selection within a Menu Toolkit.MenuItem = class MenuItem extends Toolkit.Panel { // Object constructor constructor(application, options) { super(application, options); // Configure instance fields this.clickListeners = []; this.enabled = "enabled" in options ? !!options.enabled : true; this.icon = options.icon || null; this.text = options.text || ""; this.shortcut = options.shortcut || null; // Configure base element this.setLayout("flex", {}); this.element.setAttribute("role", "menuitem"); this.element.setAttribute("tabindex", "-1"); this.element.addEventListener("keydown", e=>this.onkeydown(e)); this.element.addEventListener("pointerdown", e=>this.onpointerdown(e)); this.element.addEventListener("pointermove", e=>this.onpointermove(e)); this.element.addEventListener("pointerup" , e=>this.onpointerup (e)); // Configure display text element this.textElement = document.createElement("div"); this.textElement.id = Toolkit.id(); this.textElement.style.cursor = "default"; this.textElement.style.flexGrow = "1"; this.textElement.style.userSelect = "none"; this.textElement.setAttribute("name", "text"); this.element.appendChild(this.textElement); this.element.setAttribute("aria-labelledby", this.textElement.id); // Configure properties this.setEnabled(this.enabled); this.setText (this.text); this.application.addComponent(this); } ///////////////////////////// Public Methods ////////////////////////////// // Add a callback for click events addClickListener(listener) { if (this.clickListeners.indexOf(listener) == -1) this.clickListeners.push(listener); } // Request focus on the appropriate element focus() { this.element.focus(); } // Retrieve the item's display text getText() { return this.text; } // Determine whether the item is enabled isEnabled() { return this.enabled; } // Specify whether the item is enabled setEnabled(enabled) { this.enabled = enabled = !!enabled; if (enabled) this.element.removeAttribute("disabled"); else this.element.setAttribute("disabled", ""); } // Specify the item's display text setText(text) { this.text = text || ""; this.localize(); } ///////////////////////////// Package Methods ///////////////////////////// // Configure this component to be a child of its parent child() { this.menuBar = this.parent; while (!(this.menuBar instanceof Toolkit.MenuBar)) this.menuBar = this.menuBar.parent; this.menuItem = this instanceof Toolkit.Menu ? this : this.parent; while (!(this.menuItem instanceof Toolkit.Menu)) this.menuItem = this.menuItem.parent; this.menuTop = this instanceof Toolkit.Menu ? this : this.parent; while (this.menuTop.parent != this.menuBar) this.menuTop = this.menuTop.parent; } // Update display text with localized strings localize() { let text = this.text; if (this.application) text = this.application.translate(text, this); this.textElement.innerText = text; } ///////////////////////////// Private Methods ///////////////////////////// // The menu item was activated activate(e) { if (!this.enabled) return; this.menuBar.restoreFocus(); for (let listener of this.clickListeners) listener(e, this); } // Key press event handler onkeydown(e) { let index; // Processing by key switch (e.key) { // Activate the item case " ": case "Enter": this.activate(e); break; // Select the next item case "ArrowDown": index = this.parent.nextChild( this.parent.children.indexOf(this)); if (index != -1) this.parent.children[index].focus(); break; // Conditional case "ArrowLeft": // Move to the previous menu in the menu bar if (this.menuItem.parent == this.menuBar) { index = this.menuBar.previousChild( this.menuBar.children.indexOf(this.menuItem)); if (index != -1) { let menu = this.menuBar.children[index]; if (menu != this.menuTop) { if (this.menuBar.expanded != null) menu.activate(true); else menu.focus(); } } } // Close the containing submenu else { this.menuItem.setExpanded(false); this.menuItem.focus(); } break; // Move to the next menu in the menu bar case "ArrowRight": index = this.menuBar.nextChild( this.menuBar.children.indexOf(this.menuTop)); if (index != -1) { let menu = this.menuBar.children[index]; if (menu != this.menuTop) { if (this.menuBar.expanded != null) menu.activate(true); else menu.focus(); } } break; // Select the previous item case "ArrowUp": index = this.parent.previousChild( this.parent.children.indexOf(this)); if (index != -1) this.parent.children[index].focus(); break; // Conditional case "End": // Select the last menu in the menu bar if (this.parent == this.menuBar) { index = this.menuBar.previousChild( this.menuBar.children.length); if (index != -1) { let menu = this.menuBar.children[index]; if (menu != this.menuTop) { if (this.menuBar.expanded != null) menu.activate(true); else menu.focus(); } } } // Select the last item in the menu else { index = this.menuItem.previousChild( this.menuItem.children.length); if (index != -1) this.menuItem.children[index].focus(); } break; // Return focus to the original element case "Escape": if (this.menuBar.expanded != null) this.menuBar.expanded.setExpanded(false); this.menuBar.restoreFocus(); break; // Conditional case "Home": // Select the first menu in the menu bar if (this.parent == this.menuBar) { index = this.menuBar.nextChild(-1); if (index != -1) { let menu = this.menuBar.children[index]; if (menu != this.menuTop) { if (this.menuBar.expanded != null) menu.activate(true); else menu.focus(); } } } // Select the last item in the menu else { index = this.menuItem.nextChild(-1); if (index != -1) this.menuItem.children[index].focus(); } break; default: return; } // Configure event e.preventDefault(); e.stopPropagation(); } // Pointer down event handler onpointerdown(e) { // Configure event e.preventDefault(); e.stopPropagation(); // Configure focus if (this.enabled) this.focus(); else return; // Error checking if (e.button != 0 || this.element.hasPointerCapture(e.pointerId)) return; // Configure element this.element.setPointerCapture(e.pointerId); this.element.setAttribute("active", ""); } // Pointer move event handler onpointermove(e) { // Configure event e.preventDefault(); e.stopPropagation(); // Error checking if (!this.element.hasPointerCapture(e.pointerid)) return; // Working variables let bounds = this.getBounds(); let active = e.x >= bounds.x && e.x < bounds.x + bounds.width && e.y >= bounds.y && e.y < bounds.y + bounds.height ; // Configure element if (active) this.element.setAttribute("active", ""); else this.element.removeAttribute("active"); } // Pointer up event handler onpointerup(e) { // Configure event e.preventDefault(); e.stopPropagation(); // Error checking if (!this.element.hasPointerCapture(e.pointerId)) return; // Configure element this.element.releasePointerCapture(e.pointerId); // Activate the menu item if it is active if (!this.element.hasAttribute("active")) return; this.element.removeAttribute("active"); this.activate(e); } };