blob: 38742834ff765ee472843b9c5ff0096f9bd87873 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Class to manage the ChromeVox menus.
*/
import {Command, CommandStore} from '../common/command_store.js';
import {PanelNodeMenuData, PanelNodeMenuId, PanelNodeMenuItemData} from '../common/panel_menu_data.js';
import {PanelInterface} from './panel_interface.js';
import {PanelMenu, PanelNodeMenu, PanelSearchMenu} from './panel_menu.js';
const $ = (id) => document.getElementById(id);
export class MenuManager {
constructor() {
/**
* The currently active menu, if any.
* @private {?PanelMenu}
*/
this.activeMenu_ = null;
/** @private {string} */
this.lastMenu_ = '';
/**
* The array of top-level menus.
* @private {!Array<!PanelMenu>}
*/
this.menus_ = [];
/** @private {!Object<!PanelNodeMenuId, !PanelNodeMenu>} */
this.nodeMenuDictionary_ = {};
/** @private {?PanelSearchMenu} */
this.searchMenu_ = null;
}
/**
* Activate a menu, which implies hiding the previous active menu.
* @param {?PanelMenu} menu The new menu to activate.
* @param {boolean} activateFirstItem Whether or not we should activate the
* menu's first item.
*/
activateMenu(menu, activateFirstItem) {
if (menu === this.activeMenu_) {
return;
}
if (this.activeMenu_) {
this.activeMenu_.deactivate();
this.activeMenu_ = null;
}
this.activeMenu_ = menu;
PanelInterface.instance.setPendingCallback(null);
if (this.activeMenu_) {
this.activeMenu_.activate(activateFirstItem);
}
}
/**
* Create a new menu with the given name and add it to the menu bar.
* @param {string} menuMsg The msg id of the new menu to add.
* @return {!PanelMenu} The menu just created.
*/
addMenu(menuMsg) {
const menu = new PanelMenu(menuMsg);
$('menu-bar').appendChild(menu.menuBarItemElement);
menu.menuBarItemElement.addEventListener(
'mouseover',
() => this.activateMenu(menu, true /* activateFirstItem */), false);
menu.menuBarItemElement.addEventListener(
'mouseup', event => this.onMouseUpOnMenuTitle(menu, event), false);
$('menus_background').appendChild(menu.menuContainerElement);
this.menus_.push(menu);
return menu;
}
/**
* Create a new node menu with the given name and add it to the menu bar.
* @param {!PanelNodeMenuData} menuData The title/predicate for the new menu.
*/
addNodeMenu(menuData) {
const menu = new PanelNodeMenu(menuData.titleId);
$('menu-bar').appendChild(menu.menuBarItemElement);
menu.menuBarItemElement.addEventListener(
'mouseover',
() => this.activateMenu(menu, true /* activateFirstItem */));
menu.menuBarItemElement.addEventListener(
'mouseup', event => this.onMouseUpOnMenuTitle(menu, event));
$('menus_background').appendChild(menu.menuContainerElement);
this.menus_.push(menu);
this.nodeMenuDictionary_[menuData.menuId] = menu;
}
/** @param {!PanelNodeMenuItemData} itemData */
addNodeMenuItem(itemData) {
this.nodeMenuDictionary_[itemData.menuId].addItemFromData(itemData);
}
/**
* Clear any previous menus. The menus are all regenerated each time the
* menus are opened.
*/
clearMenus() {
while (this.menus_.length) {
const menu = this.menus_.pop();
$('menu-bar').removeChild(menu.menuBarItemElement);
$('menus_background').removeChild(menu.menuContainerElement);
}
if (this.activeMenu_) {
this.lastMenu_ = this.activeMenu_.menuMsg;
}
this.activeMenu_ = null;
}
/** Disables menu items that are prohibited without a signed-in user. */
denySignedOut() {
for (const menu of this.menus_) {
for (const item of menu.items) {
if (CommandStore.denySignedOut(
/** @type {!Command} */ (item.element.id))) {
item.disable();
}
}
}
}
/**
* @param {string|undefined} opt_menuTitle
* @return {!PanelMenu}
*/
getSelectedMenu(opt_menuTitle) {
const specifiedMenu =
this.menus_.find(menu => menu.menuMsg === opt_menuTitle);
return specifiedMenu || this.searchMenu_ || this.menus_[0];
}
/**
* Activate a menu whose title has been clicked. Stop event propagation at
* this point so we don't close the ChromeVox menus and restore focus.
* @param {PanelMenu} menu The menu we would like to activate.
* @param {Event} mouseUpEvent The mouseup event.
*/
onMouseUpOnMenuTitle(menu, mouseUpEvent) {
this.activateMenu(menu, true /* activateFirstItem */);
mouseUpEvent.preventDefault();
mouseUpEvent.stopPropagation();
}
// The following getters and setters are temporary during the migration from
// panel.js.
/** @return {?PanelMenu} */
get activeMenu() {
return this.activeMenu_;
}
/** @param {?PanelMenu} menu */
set activeMenu(menu) {
this.activeMenu_ = menu;
}
/** @return {string} */
get lastMenu() {
return this.lastMenu_;
}
/** @param {string} menuMsg */
set lastMenu(menuMsg) {
this.lastMenu_ = menuMsg;
}
/** @return {!Array<!PanelMenu>} */
get menus() {
return this.menus_;
}
/** @return {!Object<!PanelNodeMenuId, !PanelNodeMenu>} */
get nodeMenuDictionary() {
return this.nodeMenuDictionary_;
}
/** @return {?PanelSearchMenu} */
get searchMenu() {
return this.searchMenu_;
}
/** @param {?PanelSearchMenu} menu */
set searchMenu(menu) {
this.searchMenu_ = menu;
}
}