// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
/*-----------------------------------------------------------------------------
| Copyright (c) 2014-2017, PhosphorJS Contributors
|
| Distributed under the terms of the BSD 3-Clause License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
import { ArrayExt } from '@lumino/algorithm';
import { ElementExt } from '@lumino/domutils';
import { getKeyboardLayout } from '@lumino/keyboard';
import { Message, MessageLoop } from '@lumino/messaging';
import { CommandRegistry } from '@lumino/commands';
import {
ElementARIAAttrs,
ElementDataset,
h,
VirtualDOM,
VirtualElement
} from '@lumino/virtualdom';
import { Menu } from './menu';
import { Title } from './title';
import { Widget } from './widget';
/**
* A widget which displays menus as a canonical menu bar.
*
* #### Notes
* See also the related [example](../../examples/menubar/index.html) and
* its [source](https://github.com/jupyterlab/lumino/tree/main/examples/example-menubar).
*/
export class MenuBar extends Widget {
/**
* Construct a new menu bar.
*
* @param options - The options for initializing the menu bar.
*/
constructor(options: MenuBar.IOptions = {}) {
super({ node: Private.createNode() });
this.addClass('lm-MenuBar');
this.setFlag(Widget.Flag.DisallowLayout);
this.renderer = options.renderer || MenuBar.defaultRenderer;
this._forceItemsPosition = options.forceItemsPosition || {
forceX: true,
forceY: true
};
this._overflowMenuOptions = options.overflowMenuOptions || {
isVisible: true
};
}
/**
* Dispose of the resources held by the widget.
*/
dispose(): void {
this._closeChildMenu();
this._menus.length = 0;
super.dispose();
}
/**
* The renderer used by the menu bar.
*/
readonly renderer: MenuBar.IRenderer;
/**
* The child menu of the menu bar.
*
* #### Notes
* This will be `null` if the menu bar does not have an open menu.
*/
get childMenu(): Menu | null {
return this._childMenu;
}
/**
* The overflow index of the menu bar.
*/
get overflowIndex(): number {
return this._overflowIndex;
}
/**
* The overflow menu of the menu bar.
*/
get overflowMenu(): Menu | null {
return this._overflowMenu;
}
/**
* Get the menu bar content node.
*
* #### Notes
* This is the node which holds the menu title nodes.
*
* Modifying this node directly can lead to undefined behavior.
*/
get contentNode(): HTMLUListElement {
return this.node.getElementsByClassName(
'lm-MenuBar-content'
)[0] as HTMLUListElement;
}
/**
* Get the currently active menu.
*/
get activeMenu(): Menu | null {
return this._menus[this._activeIndex] || null;
}
/**
* Set the currently active menu.
*
* #### Notes
* If the menu does not exist, the menu will be set to `null`.
*/
set activeMenu(value: Menu | null) {
this.activeIndex = value ? this._menus.indexOf(value) : -1;
}
/**
* Get the index of the currently active menu.
*
* #### Notes
* This will be `-1` if no menu is active.
*/
get activeIndex(): number {
return this._activeIndex;
}
/**
* Set the index of the currently active menu.
*
* #### Notes
* If the menu cannot be activated, the index will be set to `-1`.
*/
set activeIndex(value: number) {
// Adjust the value for an out of range index.
if (value < 0 || value >= this._menus.length) {
value = -1;
}
// An empty menu cannot be active
if (value > -1 && this._menus[value].items.length === 0) {
value = -1;
}
// Bail early if the index will not change.
if (this._activeIndex === value) {
return;
}
// Update the active index.
this._activeIndex = value;
// Schedule an update of the items.
this.update();
}
/**
* A read-only array of the menus in the menu bar.
*/
get menus(): ReadonlyArray