sussudio
Version:
An unofficial VS Code Internal API
167 lines (166 loc) • 7.49 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from "../../../../nls.mjs";
import { $, addDisposableListener, append, EventType, h } from "../../dom.mjs";
import { StandardKeyboardEvent } from "../../keyboardEvent.mjs";
import { ActionViewItem, BaseActionViewItem } from "../actionbar/actionViewItems.mjs";
import { DropdownMenu } from "./dropdown.mjs";
import { Action } from "../../../common/actions.mjs";
import { Codicon } from "../../../common/codicons.mjs";
import { Emitter } from "../../../common/event.mjs";
import "../../../../css!./dropdown.mjs";
export class DropdownMenuActionViewItem extends BaseActionViewItem {
menuActionsOrProvider;
dropdownMenu;
contextMenuProvider;
actionItem = null;
_onDidChangeVisibility = this._register(new Emitter());
onDidChangeVisibility = this._onDidChangeVisibility.event;
options;
constructor(action, menuActionsOrProvider, contextMenuProvider, options = Object.create(null)) {
super(null, action, options);
this.menuActionsOrProvider = menuActionsOrProvider;
this.contextMenuProvider = contextMenuProvider;
this.options = options;
if (this.options.actionRunner) {
this.actionRunner = this.options.actionRunner;
}
}
render(container) {
this.actionItem = container;
const labelRenderer = (el) => {
this.element = append(el, $('a.action-label'));
let classNames = [];
if (typeof this.options.classNames === 'string') {
classNames = this.options.classNames.split(/\s+/g).filter(s => !!s);
}
else if (this.options.classNames) {
classNames = this.options.classNames;
}
// todo@aeschli: remove codicon, should come through `this.options.classNames`
if (!classNames.find(c => c === 'icon')) {
classNames.push('codicon');
}
this.element.classList.add(...classNames);
this.element.setAttribute('role', 'button');
this.element.setAttribute('aria-haspopup', 'true');
this.element.setAttribute('aria-expanded', 'false');
this.element.title = this._action.label || '';
this.element.ariaLabel = this._action.label || '';
return null;
};
const isActionsArray = Array.isArray(this.menuActionsOrProvider);
const options = {
contextMenuProvider: this.contextMenuProvider,
labelRenderer: labelRenderer,
menuAsChild: this.options.menuAsChild,
actions: isActionsArray ? this.menuActionsOrProvider : undefined,
actionProvider: isActionsArray ? undefined : this.menuActionsOrProvider
};
this.dropdownMenu = this._register(new DropdownMenu(container, options));
this._register(this.dropdownMenu.onDidChangeVisibility(visible => {
this.element?.setAttribute('aria-expanded', `${visible}`);
this._onDidChangeVisibility.fire(visible);
}));
this.dropdownMenu.menuOptions = {
actionViewItemProvider: this.options.actionViewItemProvider,
actionRunner: this.actionRunner,
getKeyBinding: this.options.keybindingProvider,
context: this._context
};
if (this.options.anchorAlignmentProvider) {
const that = this;
this.dropdownMenu.menuOptions = {
...this.dropdownMenu.menuOptions,
get anchorAlignment() {
return that.options.anchorAlignmentProvider();
}
};
}
this.updateTooltip();
this.updateEnabled();
}
getTooltip() {
let title = null;
if (this.action.tooltip) {
title = this.action.tooltip;
}
else if (this.action.label) {
title = this.action.label;
}
return title ?? undefined;
}
setActionContext(newContext) {
super.setActionContext(newContext);
if (this.dropdownMenu) {
if (this.dropdownMenu.menuOptions) {
this.dropdownMenu.menuOptions.context = newContext;
}
else {
this.dropdownMenu.menuOptions = { context: newContext };
}
}
}
show() {
this.dropdownMenu?.show();
}
updateEnabled() {
const disabled = !this.action.enabled;
this.actionItem?.classList.toggle('disabled', disabled);
this.element?.classList.toggle('disabled', disabled);
}
}
export class ActionWithDropdownActionViewItem extends ActionViewItem {
contextMenuProvider;
dropdownMenuActionViewItem;
constructor(context, action, options, contextMenuProvider) {
super(context, action, options);
this.contextMenuProvider = contextMenuProvider;
}
render(container) {
super.render(container);
if (this.element) {
this.element.classList.add('action-dropdown-item');
const menuActionsProvider = {
getActions: () => {
const actionsProvider = this.options.menuActionsOrProvider;
return Array.isArray(actionsProvider) ? actionsProvider : actionsProvider.getActions(); // TODO: microsoft/TypeScript#42768
}
};
const menuActionClassNames = this.options.menuActionClassNames || [];
const separator = h('div.action-dropdown-item-separator', [h('div', {})]).root;
separator.classList.toggle('prominent', menuActionClassNames.includes('prominent'));
append(this.element, separator);
this.dropdownMenuActionViewItem = new DropdownMenuActionViewItem(this._register(new Action('dropdownAction', nls.localize('moreActions', "More Actions..."))), menuActionsProvider, this.contextMenuProvider, { classNames: ['dropdown', ...Codicon.dropDownButton.classNamesArray, ...menuActionClassNames] });
this.dropdownMenuActionViewItem.render(this.element);
this._register(addDisposableListener(this.element, EventType.KEY_DOWN, e => {
const event = new StandardKeyboardEvent(e);
let handled = false;
if (this.dropdownMenuActionViewItem?.isFocused() && event.equals(15 /* KeyCode.LeftArrow */)) {
handled = true;
this.dropdownMenuActionViewItem?.blur();
this.focus();
}
else if (this.isFocused() && event.equals(17 /* KeyCode.RightArrow */)) {
handled = true;
this.blur();
this.dropdownMenuActionViewItem?.focus();
}
if (handled) {
event.preventDefault();
event.stopPropagation();
}
}));
}
}
blur() {
super.blur();
this.dropdownMenuActionViewItem?.blur();
}
setFocusable(focusable) {
super.setFocusable(focusable);
this.dropdownMenuActionViewItem?.setFocusable(focusable);
}
}