UNPKG

monaco-editor

Version:
509 lines (508 loc) • 23.4 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); } return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); import './menu.css'; import * as nls from '../../../../nls.js'; import * as strings from '../../../common/strings.js'; import { Action } from '../../../common/actions.js'; import { ActionBar, Separator, ActionItem, BaseActionItem } from '../actionbar/actionbar.js'; import { KeyCodeUtils } from '../../../common/keyCodes.js'; import { addClass, EventType, EventHelper, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, getClientArea, removeClasses } from '../../dom.js'; import { StandardKeyboardEvent } from '../../keyboardEvent.js'; import { RunOnceScheduler } from '../../../common/async.js'; import { dispose } from '../../../common/lifecycle.js'; export var MENU_MNEMONIC_REGEX = /\(&{1,2}(.)\)|&{1,2}(.)/; export var MENU_ESCAPED_MNEMONIC_REGEX = /(?:&amp;){1,2}(.)/; var SubmenuAction = /** @class */ (function (_super) { __extends(SubmenuAction, _super); function SubmenuAction(label, entries, cssClass) { var _this = _super.call(this, !!cssClass ? cssClass : 'submenu', label, '', true) || this; _this.entries = entries; return _this; } return SubmenuAction; }(Action)); export { SubmenuAction }; var Menu = /** @class */ (function (_super) { __extends(Menu, _super); function Menu(container, actions, options) { if (options === void 0) { options = {}; } var _this = this; addClass(container, 'monaco-menu-container'); container.setAttribute('role', 'presentation'); var menuContainer = document.createElement('div'); addClass(menuContainer, 'monaco-menu'); menuContainer.setAttribute('role', 'presentation'); container.appendChild(menuContainer); _this = _super.call(this, menuContainer, { orientation: 2 /* VERTICAL */, actionItemProvider: function (action) { return _this.doGetActionItem(action, options, parentData); }, context: options.context, actionRunner: options.actionRunner, ariaLabel: options.ariaLabel, triggerKeys: { keys: [3 /* Enter */], keyDown: true } }) || this; _this.actionsList.setAttribute('role', 'menu'); _this.actionsList.tabIndex = 0; _this.menuDisposables = []; if (options.enableMnemonics) { _this.menuDisposables.push(addDisposableListener(menuContainer, EventType.KEY_DOWN, function (e) { var key = KeyCodeUtils.fromString(e.key); if (_this.mnemonics.has(key)) { EventHelper.stop(e, true); var actions_1 = _this.mnemonics.get(key); if (actions_1.length === 1) { if (actions_1[0] instanceof SubmenuActionItem) { _this.focusItemByElement(actions_1[0].container); } actions_1[0].onClick(event); } if (actions_1.length > 1) { var action = actions_1.shift(); _this.focusItemByElement(action.container); actions_1.push(action); _this.mnemonics.set(key, actions_1); } } })); } _this._register(addDisposableListener(_this.domNode, EventType.MOUSE_OUT, function (e) { var relatedTarget = e.relatedTarget; if (!isAncestor(relatedTarget, _this.domNode)) { _this.focusedItem = undefined; _this.updateFocus(); e.stopPropagation(); } })); _this._register(addDisposableListener(_this.actionsList, EventType.MOUSE_OVER, function (e) { var target = e.target; if (!target || !isAncestor(target, _this.actionsList) || target === _this.actionsList) { return; } while (target.parentElement !== _this.actionsList) { target = target.parentElement; } if (hasClass(target, 'action-item')) { var lastFocusedItem = _this.focusedItem; _this.setFocusedItem(target); if (lastFocusedItem !== _this.focusedItem) { _this.updateFocus(); } } })); var parentData = { parent: _this }; _this.mnemonics = new Map(); _this.push(actions, { icon: true, label: true, isMenu: true }); return _this; } Menu.prototype.style = function (style) { var container = this.getContainer(); var fgColor = style.foregroundColor ? "" + style.foregroundColor : null; var bgColor = style.backgroundColor ? "" + style.backgroundColor : null; var border = style.borderColor ? "2px solid " + style.borderColor : null; var shadow = style.shadowColor ? "0 2px 4px " + style.shadowColor : null; container.style.border = border; this.domNode.style.color = fgColor; this.domNode.style.backgroundColor = bgColor; container.style.boxShadow = shadow; if (this.items) { this.items.forEach(function (item) { if (item instanceof MenuActionItem || item instanceof MenuSeparatorActionItem) { item.style(style); } }); } }; Menu.prototype.focusItemByElement = function (element) { var lastFocusedItem = this.focusedItem; this.setFocusedItem(element); if (lastFocusedItem !== this.focusedItem) { this.updateFocus(); } }; Menu.prototype.setFocusedItem = function (element) { for (var i = 0; i < this.actionsList.children.length; i++) { var elem = this.actionsList.children[i]; if (element === elem) { this.focusedItem = i; break; } } }; Menu.prototype.doGetActionItem = function (action, options, parentData) { if (action instanceof Separator) { return new MenuSeparatorActionItem(options.context, action, { icon: true }); } else if (action instanceof SubmenuAction) { var menuActionItem = new SubmenuActionItem(action, action.entries, parentData, options); if (options.enableMnemonics) { var mnemonic = menuActionItem.getMnemonic(); if (mnemonic && menuActionItem.isEnabled()) { var actionItems = []; if (this.mnemonics.has(mnemonic)) { actionItems = this.mnemonics.get(mnemonic); } actionItems.push(menuActionItem); this.mnemonics.set(mnemonic, actionItems); } } return menuActionItem; } else { var menuItemOptions = { enableMnemonics: options.enableMnemonics }; if (options.getKeyBinding) { var keybinding = options.getKeyBinding(action); if (keybinding) { menuItemOptions.keybinding = keybinding.getLabel(); } } var menuActionItem = new MenuActionItem(options.context, action, menuItemOptions); if (options.enableMnemonics) { var mnemonic = menuActionItem.getMnemonic(); if (mnemonic && menuActionItem.isEnabled()) { var actionItems = []; if (this.mnemonics.has(mnemonic)) { actionItems = this.mnemonics.get(mnemonic); } actionItems.push(menuActionItem); this.mnemonics.set(mnemonic, actionItems); } } return menuActionItem; } }; Menu.prototype.focus = function (selectFirst) { if (selectFirst === void 0) { selectFirst = true; } _super.prototype.focus.call(this, selectFirst); }; return Menu; }(ActionBar)); export { Menu }; var MenuActionItem = /** @class */ (function (_super) { __extends(MenuActionItem, _super); function MenuActionItem(ctx, action, options) { if (options === void 0) { options = {}; } var _this = this; options.isMenu = true; _this = _super.call(this, action, action, options) || this; _this.options = options; _this.options.icon = options.icon !== undefined ? options.icon : false; _this.options.label = options.label !== undefined ? options.label : true; _this.cssClass = ''; // Set mnemonic if (_this.options.label && options.enableMnemonics) { var label = _this.getAction().label; if (label) { var matches = MENU_MNEMONIC_REGEX.exec(label); if (matches) { _this.mnemonic = KeyCodeUtils.fromString((!!matches[1] ? matches[1] : matches[2]).toLocaleLowerCase()); } } } return _this; } MenuActionItem.prototype.render = function (container) { var _this = this; _super.prototype.render.call(this, container); this.container = container; this.item = append(this.element, $('a.action-menu-item')); if (this._action.id === Separator.ID) { // A separator is a presentation item this.item.setAttribute('role', 'presentation'); } else { this.item.setAttribute('role', 'menuitem'); if (this.mnemonic) { this.item.setAttribute('aria-keyshortcuts', "" + this.mnemonic); } } this.check = append(this.item, $('span.menu-item-check')); this.check.setAttribute('role', 'none'); this.label = append(this.item, $('span.action-label')); if (this.options.label && this.options.keybinding) { append(this.item, $('span.keybinding')).textContent = this.options.keybinding; } this._register(addDisposableListener(this.element, EventType.MOUSE_UP, function (e) { EventHelper.stop(e, true); _this.onClick(e); })); this.updateClass(); this.updateLabel(); this.updateTooltip(); this.updateEnabled(); this.updateChecked(); }; MenuActionItem.prototype.blur = function () { _super.prototype.blur.call(this); this.applyStyle(); }; MenuActionItem.prototype.focus = function () { _super.prototype.focus.call(this); this.item.focus(); this.applyStyle(); }; MenuActionItem.prototype.updateLabel = function () { if (this.options.label) { var label = this.getAction().label; if (label) { var cleanLabel = cleanMnemonic(label); if (!this.options.enableMnemonics) { label = cleanLabel; } this.label.setAttribute('aria-label', cleanLabel); var matches = MENU_MNEMONIC_REGEX.exec(label); if (matches) { label = strings.escape(label).replace(MENU_ESCAPED_MNEMONIC_REGEX, '<u aria-hidden="true">$1</u>'); this.item.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[2]).toLocaleLowerCase()); } } this.label.innerHTML = label.trim(); } }; MenuActionItem.prototype.updateTooltip = function () { var title = null; if (this.getAction().tooltip) { title = this.getAction().tooltip; } else if (!this.options.label && this.getAction().label && this.options.icon) { title = this.getAction().label; if (this.options.keybinding) { title = nls.localize({ key: 'titleLabel', comment: ['action title', 'action keybinding'] }, "{0} ({1})", title, this.options.keybinding); } } if (title) { this.item.title = title; } }; MenuActionItem.prototype.updateClass = function () { if (this.cssClass) { removeClasses(this.item, this.cssClass); } if (this.options.icon) { this.cssClass = this.getAction().class; addClass(this.label, 'icon'); if (this.cssClass) { addClasses(this.label, this.cssClass); } this.updateEnabled(); } else { removeClass(this.label, 'icon'); } }; MenuActionItem.prototype.updateEnabled = function () { if (this.getAction().enabled) { removeClass(this.element, 'disabled'); removeClass(this.item, 'disabled'); this.item.tabIndex = 0; } else { addClass(this.element, 'disabled'); addClass(this.item, 'disabled'); removeTabIndexAndUpdateFocus(this.item); } }; MenuActionItem.prototype.updateChecked = function () { if (this.getAction().checked) { addClass(this.item, 'checked'); this.item.setAttribute('role', 'menuitemcheckbox'); this.item.setAttribute('aria-checked', 'true'); } else { removeClass(this.item, 'checked'); this.item.setAttribute('role', 'menuitem'); this.item.setAttribute('aria-checked', 'false'); } }; MenuActionItem.prototype.getMnemonic = function () { return this.mnemonic; }; MenuActionItem.prototype.applyStyle = function () { var isSelected = hasClass(this.element, 'focused'); var fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; var bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : this.menuStyle.backgroundColor; var border = isSelected && this.menuStyle.selectionBorderColor ? "1px solid " + this.menuStyle.selectionBorderColor : null; this.item.style.color = fgColor ? "" + fgColor : null; this.check.style.backgroundColor = fgColor ? "" + fgColor : null; this.item.style.backgroundColor = bgColor ? "" + bgColor : null; this.container.style.border = border; }; MenuActionItem.prototype.style = function (style) { this.menuStyle = style; this.applyStyle(); }; return MenuActionItem; }(BaseActionItem)); var SubmenuActionItem = /** @class */ (function (_super) { __extends(SubmenuActionItem, _super); function SubmenuActionItem(action, submenuActions, parentData, submenuOptions) { var _this = _super.call(this, action, action, submenuOptions) || this; _this.submenuActions = submenuActions; _this.parentData = parentData; _this.submenuOptions = submenuOptions; _this.submenuDisposables = []; _this.showScheduler = new RunOnceScheduler(function () { if (_this.mouseOver) { _this.cleanupExistingSubmenu(false); _this.createSubmenu(false); } }, 250); _this.hideScheduler = new RunOnceScheduler(function () { if ((!isAncestor(document.activeElement, _this.element) && _this.parentData.submenu === _this.mysubmenu)) { _this.parentData.parent.focus(false); _this.cleanupExistingSubmenu(true); } }, 750); return _this; } SubmenuActionItem.prototype.render = function (container) { var _this = this; _super.prototype.render.call(this, container); addClass(this.item, 'monaco-submenu-item'); this.item.setAttribute('aria-haspopup', 'true'); this.submenuIndicator = append(this.item, $('span.submenu-indicator')); this.submenuIndicator.setAttribute('aria-hidden', 'true'); this._register(addDisposableListener(this.element, EventType.KEY_UP, function (e) { var event = new StandardKeyboardEvent(e); if (event.equals(17 /* RightArrow */) || event.equals(3 /* Enter */)) { EventHelper.stop(e, true); _this.createSubmenu(true); } })); this._register(addDisposableListener(this.element, EventType.KEY_DOWN, function (e) { var event = new StandardKeyboardEvent(e); if (event.equals(17 /* RightArrow */) || event.equals(3 /* Enter */)) { EventHelper.stop(e, true); } })); this._register(addDisposableListener(this.element, EventType.MOUSE_OVER, function (e) { if (!_this.mouseOver) { _this.mouseOver = true; _this.showScheduler.schedule(); } })); this._register(addDisposableListener(this.element, EventType.MOUSE_LEAVE, function (e) { _this.mouseOver = false; })); this._register(addDisposableListener(this.element, EventType.FOCUS_OUT, function (e) { if (!isAncestor(document.activeElement, _this.element)) { _this.hideScheduler.schedule(); } })); }; SubmenuActionItem.prototype.onClick = function (e) { // stop clicking from trying to run an action EventHelper.stop(e, true); this.cleanupExistingSubmenu(false); this.createSubmenu(false); }; SubmenuActionItem.prototype.cleanupExistingSubmenu = function (force) { if (this.parentData.submenu && (force || (this.parentData.submenu !== this.mysubmenu))) { this.parentData.submenu.dispose(); this.parentData.submenu = null; if (this.submenuContainer) { this.submenuDisposables = dispose(this.submenuDisposables); this.submenuContainer = null; } } }; SubmenuActionItem.prototype.createSubmenu = function (selectFirstItem) { var _this = this; if (selectFirstItem === void 0) { selectFirstItem = true; } if (!this.parentData.submenu) { this.submenuContainer = append(this.element, $('div.monaco-submenu')); addClasses(this.submenuContainer, 'menubar-menu-items-holder', 'context-view'); this.submenuContainer.style.left = getClientArea(this.element).width + "px"; this.submenuDisposables.push(addDisposableListener(this.submenuContainer, EventType.KEY_UP, function (e) { var event = new StandardKeyboardEvent(e); if (event.equals(15 /* LeftArrow */)) { EventHelper.stop(e, true); _this.parentData.parent.focus(); _this.parentData.submenu.dispose(); _this.parentData.submenu = null; _this.submenuDisposables = dispose(_this.submenuDisposables); _this.submenuContainer = null; } })); this.submenuDisposables.push(addDisposableListener(this.submenuContainer, EventType.KEY_DOWN, function (e) { var event = new StandardKeyboardEvent(e); if (event.equals(15 /* LeftArrow */)) { EventHelper.stop(e, true); } })); this.parentData.submenu = new Menu(this.submenuContainer, this.submenuActions, this.submenuOptions); if (this.menuStyle) { this.parentData.submenu.style(this.menuStyle); } this.submenuDisposables.push(this.parentData.submenu.onDidCancel(function () { _this.parentData.parent.focus(); _this.parentData.submenu.dispose(); _this.parentData.submenu = null; _this.submenuDisposables = dispose(_this.submenuDisposables); _this.submenuContainer = null; })); this.parentData.submenu.focus(selectFirstItem); this.mysubmenu = this.parentData.submenu; } else { this.parentData.submenu.focus(false); } }; SubmenuActionItem.prototype.applyStyle = function () { _super.prototype.applyStyle.call(this); var isSelected = hasClass(this.element, 'focused'); var fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; this.submenuIndicator.style.backgroundColor = fgColor ? "" + fgColor : null; if (this.parentData.submenu) { this.parentData.submenu.style(this.menuStyle); } }; SubmenuActionItem.prototype.dispose = function () { _super.prototype.dispose.call(this); this.hideScheduler.dispose(); if (this.mysubmenu) { this.mysubmenu.dispose(); this.mysubmenu = null; } if (this.submenuContainer) { this.submenuDisposables = dispose(this.submenuDisposables); this.submenuContainer = null; } }; return SubmenuActionItem; }(MenuActionItem)); var MenuSeparatorActionItem = /** @class */ (function (_super) { __extends(MenuSeparatorActionItem, _super); function MenuSeparatorActionItem() { return _super !== null && _super.apply(this, arguments) || this; } MenuSeparatorActionItem.prototype.style = function (style) { this.label.style.borderBottomColor = style.separatorColor ? "" + style.separatorColor : null; }; return MenuSeparatorActionItem; }(ActionItem)); export function cleanMnemonic(label) { var regex = MENU_MNEMONIC_REGEX; var matches = regex.exec(label); if (!matches) { return label; } var mnemonicInText = matches[0].charAt(0) === '&'; return label.replace(regex, mnemonicInText ? '$2' : '').trim(); }