"use strict"; // Selection within a Menu Toolkit.MenuItem = class MenuItem extends Toolkit.Component { // Object constructor constructor(parent, options) { super(parent && parent.application, "div"); // Configure instance fields this.clickListeners = []; this.enabled = "enabled" in options ? !!options.enabled : true; this.icon = options.icon || null; this.menuBar = parent.menuBar; this.parent = parent; this.text = options.text || ""; this.shortcut = options.shortcut || null; // Configure base element this.element.style.display = "flex"; this.element.setAttribute("role", "menuitem"); this.element.setAttribute("tabindex", "-1"); this.element.addEventListener("blur" , e=>this.onblur (e)); this.element.addEventListener("focus" , e=>this.onfocus (e)); this.element.addEventListener("keydown", e=>this.onkeydown(e)); if (this.parent != this.menuBar) this.element.addEventListener("pointerup", e=>this.onpointerup(e)); // Configure display text element this.textElement = document.createElement("div"); this.textElement.style.cursor = "default"; this.textElement.style.flexGrow = "1"; this.textElement.style.userSelect = "none"; this.element.appendChild(this.textElement); // 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); } // 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 ///////////////////////////// // Update display text with localized strings localize() { let text = this.text; if (this.application) text = this.application.translate(text, this); this.element.setAttribute("aria-label", text); 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); this.menuBar.expanded.setExpanded(false); } // Focus lost event handler onblur(e) { this.focusChanged( this, e.relatedTarget ? e.relatedTarget.component : null); } // Focus gained event handler onfocus(e) { this.focusChanged( e.relatedTarget ? e.relatedTarget.component : null, this); } // Key press event handler onkeydown(e) { // Processing by key switch (e.key) { // Activate the item case " ": case "Enter": this.activate(e); break; // Select the next item case "ArrowDown": this.parent.items[ (this.parent.items.indexOf(this) + 1) % this.parent.items.length ].element.focus(); break; // Conditional case "ArrowLeft": // Move to the previous menu in the menu bar if (this.parent.parent == this.menuBar) { let menu = this.menuBar.menus[ (this.menuBar.menus.indexOf(this.parent) + this.menuBar.menus.length - 1) % this.menuBar.menus.length ]; if (menu != this.parent) menu.activate(true); } // Close the containing submenu else { this.parent.setExpanded(false); this.parent.parent.element.focus(); } break; // Move to the next menu in the menu bar case "ArrowRight": let menu = this.menuBar.menus[ (this.menuBar.menus.indexOf(this.menuBar.expanded) + 1) % this.menuBar.menus.length ]; if (this.menuBar.expanded != menu) menu.activate(true); break; // Select the previous item case "ArrowUp": this.parent.items[ (this.parent.items.indexOf(this) + this.parent.items.length - 1) % this.parent.items.length ].element.focus(); break; // Select the last item in the menu case "End": this.parent.items[this.parent.items.length-1].element.focus(); break; // Return focus to the original element case "Escape": this.menuBar.expanded.setExpanded(false); break; // Select the first item in the menu case "Home": this.parent.items[0].element.focus(); break; default: return; } // Configure element e.preventDefault(); e.stopPropagation(); } // Pointer up event handler onpointerup(e) { // Error checking if (e.button != 0 || document.activeElement != this.element) return; // Configure event e.preventDefault(); e.stopPropagation(); // Activate the menu item this.activate(e); } };