Archived
1
0

Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'

This commit is contained in:
Joe Previte
2020-12-15 15:52:33 -07:00
4649 changed files with 1311795 additions and 0 deletions

View File

@ -0,0 +1,14 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-dropdown {
height: 100%;
padding: 0;
}
.monaco-dropdown > .dropdown-label {
cursor: pointer;
height: 100%;
}

View File

@ -0,0 +1,280 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./dropdown';
import { Gesture, EventType as GestureEventType } from 'vs/base/browser/touch';
import { ActionRunner, IAction } from 'vs/base/common/actions';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IContextViewProvider, IAnchor, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { IMenuOptions } from 'vs/base/browser/ui/menu/menu';
import { KeyCode } from 'vs/base/common/keyCodes';
import { EventHelper, EventType, append, $, addDisposableListener, DOMEvent } from 'vs/base/browser/dom';
import { IContextMenuProvider } from 'vs/base/browser/contextmenu';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Emitter } from 'vs/base/common/event';
export interface ILabelRenderer {
(container: HTMLElement): IDisposable | null;
}
export interface IBaseDropdownOptions {
label?: string;
labelRenderer?: ILabelRenderer;
}
export class BaseDropdown extends ActionRunner {
private _element: HTMLElement;
private boxContainer?: HTMLElement;
private _label?: HTMLElement;
private contents?: HTMLElement;
private visible: boolean | undefined;
private _onDidChangeVisibility = new Emitter<boolean>();
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
constructor(container: HTMLElement, options: IBaseDropdownOptions) {
super();
this._element = append(container, $('.monaco-dropdown'));
this._label = append(this._element, $('.dropdown-label'));
let labelRenderer = options.labelRenderer;
if (!labelRenderer) {
labelRenderer = (container: HTMLElement): IDisposable | null => {
container.textContent = options.label || '';
return null;
};
}
for (const event of [EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap]) {
this._register(addDisposableListener(this.element, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger
}
for (const event of [EventType.MOUSE_DOWN, GestureEventType.Tap]) {
this._register(addDisposableListener(this._label, event, e => {
if (e instanceof MouseEvent && e.detail > 1) {
return; // prevent multiple clicks to open multiple context menus (https://github.com/microsoft/vscode/issues/41363)
}
if (this.visible) {
this.hide();
} else {
this.show();
}
}));
}
this._register(addDisposableListener(this._label, EventType.KEY_UP, e => {
const event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
EventHelper.stop(e, true); // https://github.com/microsoft/vscode/issues/57997
if (this.visible) {
this.hide();
} else {
this.show();
}
}
}));
const cleanupFn = labelRenderer(this._label);
if (cleanupFn) {
this._register(cleanupFn);
}
this._register(Gesture.addTarget(this._label));
}
get element(): HTMLElement {
return this._element;
}
get label() {
return this._label;
}
set tooltip(tooltip: string) {
if (this._label) {
this._label.title = tooltip;
}
}
show(): void {
if (!this.visible) {
this.visible = true;
this._onDidChangeVisibility.fire(true);
}
}
hide(): void {
if (this.visible) {
this.visible = false;
this._onDidChangeVisibility.fire(false);
}
}
isVisible(): boolean {
return !!this.visible;
}
protected onEvent(e: DOMEvent, activeElement: HTMLElement): void {
this.hide();
}
dispose(): void {
super.dispose();
this.hide();
if (this.boxContainer) {
this.boxContainer.remove();
this.boxContainer = undefined;
}
if (this.contents) {
this.contents.remove();
this.contents = undefined;
}
if (this._label) {
this._label.remove();
this._label = undefined;
}
}
}
export interface IDropdownOptions extends IBaseDropdownOptions {
contextViewProvider: IContextViewProvider;
}
export class Dropdown extends BaseDropdown {
private contextViewProvider: IContextViewProvider;
constructor(container: HTMLElement, options: IDropdownOptions) {
super(container, options);
this.contextViewProvider = options.contextViewProvider;
}
show(): void {
super.show();
this.element.classList.add('active');
this.contextViewProvider.showContextView({
getAnchor: () => this.getAnchor(),
render: (container) => {
return this.renderContents(container);
},
onDOMEvent: (e, activeElement) => {
this.onEvent(e, activeElement);
},
onHide: () => this.onHide()
});
}
protected getAnchor(): HTMLElement | IAnchor {
return this.element;
}
protected onHide(): void {
this.element.classList.remove('active');
}
hide(): void {
super.hide();
if (this.contextViewProvider) {
this.contextViewProvider.hideContextView();
}
}
protected renderContents(container: HTMLElement): IDisposable | null {
return null;
}
}
export interface IActionProvider {
getActions(): IAction[];
}
export interface IDropdownMenuOptions extends IBaseDropdownOptions {
contextMenuProvider: IContextMenuProvider;
readonly actions?: IAction[];
readonly actionProvider?: IActionProvider;
menuClassName?: string;
menuAsChild?: boolean; // scope down for #99448
}
export class DropdownMenu extends BaseDropdown {
private _contextMenuProvider: IContextMenuProvider;
private _menuOptions: IMenuOptions | undefined;
private _actions: IAction[] = [];
private actionProvider?: IActionProvider;
private menuClassName: string;
private menuAsChild?: boolean;
constructor(container: HTMLElement, options: IDropdownMenuOptions) {
super(container, options);
this._contextMenuProvider = options.contextMenuProvider;
this.actions = options.actions || [];
this.actionProvider = options.actionProvider;
this.menuClassName = options.menuClassName || '';
this.menuAsChild = !!options.menuAsChild;
}
set menuOptions(options: IMenuOptions | undefined) {
this._menuOptions = options;
}
get menuOptions(): IMenuOptions | undefined {
return this._menuOptions;
}
private get actions(): IAction[] {
if (this.actionProvider) {
return this.actionProvider.getActions();
}
return this._actions;
}
private set actions(actions: IAction[]) {
this._actions = actions;
}
show(): void {
super.show();
this.element.classList.add('active');
this._contextMenuProvider.showContextMenu({
getAnchor: () => this.element,
getActions: () => this.actions,
getActionsContext: () => this.menuOptions ? this.menuOptions.context : null,
getActionViewItem: action => this.menuOptions && this.menuOptions.actionViewItemProvider ? this.menuOptions.actionViewItemProvider(action) : undefined,
getKeyBinding: action => this.menuOptions && this.menuOptions.getKeyBinding ? this.menuOptions.getKeyBinding(action) : undefined,
getMenuClassName: () => this.menuClassName,
onHide: () => this.onHide(),
actionRunner: this.menuOptions ? this.menuOptions.actionRunner : undefined,
anchorAlignment: this.menuOptions ? this.menuOptions.anchorAlignment : AnchorAlignment.LEFT,
domForShadowRoot: this.menuAsChild ? this.element : undefined
});
}
hide(): void {
super.hide();
}
private onHide(): void {
this.hide();
this.element.classList.remove('active');
}
}

View File

@ -0,0 +1,166 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./dropdown';
import { Action, IAction, IActionRunner, IActionViewItemProvider } from 'vs/base/common/actions';
import { IDisposable } from 'vs/base/common/lifecycle';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { append, $ } from 'vs/base/browser/dom';
import { Emitter } from 'vs/base/common/event';
import { ActionViewItem, BaseActionViewItem, IActionViewItemOptions, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { IActionProvider, DropdownMenu, IDropdownMenuOptions, ILabelRenderer } from 'vs/base/browser/ui/dropdown/dropdown';
import { IContextMenuProvider } from 'vs/base/browser/contextmenu';
export interface IKeybindingProvider {
(action: IAction): ResolvedKeybinding | undefined;
}
export interface IAnchorAlignmentProvider {
(): AnchorAlignment;
}
export interface IDropdownMenuActionViewItemOptions extends IBaseActionViewItemOptions {
readonly actionViewItemProvider?: IActionViewItemProvider;
readonly keybindingProvider?: IKeybindingProvider;
readonly actionRunner?: IActionRunner;
readonly classNames?: string[] | string;
readonly anchorAlignmentProvider?: IAnchorAlignmentProvider;
readonly menuAsChild?: boolean;
}
export class DropdownMenuActionViewItem extends BaseActionViewItem {
private menuActionsOrProvider: readonly IAction[] | IActionProvider;
private dropdownMenu: DropdownMenu | undefined;
private contextMenuProvider: IContextMenuProvider;
private _onDidChangeVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
constructor(
action: IAction,
menuActionsOrProvider: readonly IAction[] | IActionProvider,
contextMenuProvider: IContextMenuProvider,
protected options: IDropdownMenuActionViewItemOptions = {}
) {
super(null, action, options);
this.menuActionsOrProvider = menuActionsOrProvider;
this.contextMenuProvider = contextMenuProvider;
if (this.options.actionRunner) {
this.actionRunner = this.options.actionRunner;
}
}
render(container: HTMLElement): void {
const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable | null => {
this.element = append(el, $('a.action-label'));
let classNames: string[] = [];
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.tabIndex = 0;
this.element.setAttribute('role', 'button');
this.element.setAttribute('aria-haspopup', 'true');
this.element.setAttribute('aria-expanded', 'false');
this.element.title = this._action.label || '';
return null;
};
const isActionsArray = Array.isArray(this.menuActionsOrProvider);
const options: IDropdownMenuOptions = {
contextMenuProvider: this.contextMenuProvider,
labelRenderer: labelRenderer,
menuAsChild: this.options.menuAsChild,
actions: isActionsArray ? this.menuActionsOrProvider as IAction[] : undefined,
actionProvider: isActionsArray ? undefined : this.menuActionsOrProvider as IActionProvider
};
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(): AnchorAlignment {
return that.options.anchorAlignmentProvider!();
}
};
}
}
setActionContext(newContext: unknown): void {
super.setActionContext(newContext);
if (this.dropdownMenu) {
if (this.dropdownMenu.menuOptions) {
this.dropdownMenu.menuOptions.context = newContext;
} else {
this.dropdownMenu.menuOptions = { context: newContext };
}
}
}
show(): void {
if (this.dropdownMenu) {
this.dropdownMenu.show();
}
}
}
export interface IActionWithDropdownActionViewItemOptions extends IActionViewItemOptions {
readonly menuActionsOrProvider: readonly IAction[] | IActionProvider;
readonly menuActionClassNames?: string[];
}
export class ActionWithDropdownActionViewItem extends ActionViewItem {
protected dropdownMenuActionViewItem: DropdownMenuActionViewItem | undefined;
constructor(
context: unknown,
action: IAction,
options: IActionWithDropdownActionViewItemOptions,
private readonly contextMenuProvider: IContextMenuProvider
) {
super(context, action, options);
}
render(container: HTMLElement): void {
super.render(container);
if (this.element) {
this.element.classList.add('action-dropdown-item');
this.dropdownMenuActionViewItem = new DropdownMenuActionViewItem(new Action('dropdownAction', undefined), (<IActionWithDropdownActionViewItemOptions>this.options).menuActionsOrProvider, this.contextMenuProvider, { classNames: ['dropdown', 'codicon-chevron-down', ...(<IActionWithDropdownActionViewItemOptions>this.options).menuActionClassNames || []] });
this.dropdownMenuActionViewItem.render(this.element);
}
}
}