diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 55d7a57ba6a..f82eec031b4 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -16,12 +16,18 @@ import { CharCode } from 'vs/base/common/charCode'; import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; -export function clearNode(node: HTMLElement) { +export function clearNode(node: HTMLElement): void { while (node.firstChild) { node.removeChild(node.firstChild); } } +export function removeNode(node: HTMLElement): void { + if (node.parentNode) { + node.parentNode.removeChild(node); + } +} + export function isInDOM(node: Node): boolean { while (node) { if (node === document.body) { diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index fd0a1c2e2f1..05e61cae92a 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -10,7 +10,6 @@ import * as platform from 'vs/base/common/platform'; import * as nls from 'vs/nls'; import * as lifecycle from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; -import { Builder, $ } from 'vs/base/browser/builder'; import { SelectBox, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox'; import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner, IRunEvent } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; @@ -38,7 +37,7 @@ export interface IBaseActionItemOptions { export class BaseActionItem implements IActionItem { - public builder: Builder; + public builder: HTMLElement; public _callOnDispose: lifecycle.IDisposable[]; public _context: any; public _action: IAction; @@ -106,7 +105,7 @@ export class BaseActionItem implements IActionItem { } public render(container: HTMLElement): void { - this.builder = $(container); + this.builder = container; Gesture.addTarget(container); const enableDragging = this.options && this.options.draggable; @@ -114,20 +113,20 @@ export class BaseActionItem implements IActionItem { container.draggable = true; } - this.builder.on(EventType.Tap, e => this.onClick(e)); + this._callOnDispose.push(DOM.addDisposableListener(this.builder, EventType.Tap, e => this.onClick(e))); - this.builder.on(DOM.EventType.MOUSE_DOWN, (e) => { + this._callOnDispose.push(DOM.addDisposableListener(this.builder, DOM.EventType.MOUSE_DOWN, e => { if (!enableDragging) { DOM.EventHelper.stop(e, true); // do not run when dragging is on because that would disable it } const mouseEvent = e as MouseEvent; if (this._action.enabled && mouseEvent.button === 0) { - this.builder.addClass('active'); + DOM.addClass(this.builder, 'active'); } - }); + })); - this.builder.on(DOM.EventType.CLICK, (e) => { + this._callOnDispose.push(DOM.addDisposableListener(this.builder, DOM.EventType.CLICK, e => { DOM.EventHelper.stop(e, true); // See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard // > Writing to the clipboard @@ -142,11 +141,13 @@ export class BaseActionItem implements IActionItem { } else { platform.setImmediate(() => this.onClick(e)); } - }); + })); - this.builder.on([DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT], (e) => { - DOM.EventHelper.stop(e); - this.builder.removeClass('active'); + [DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT].forEach(event => { + this._callOnDispose.push(DOM.addDisposableListener(this.builder, event, e => { + DOM.EventHelper.stop(e); + DOM.removeClass(this.builder, 'active'); + })); }); } @@ -169,15 +170,15 @@ export class BaseActionItem implements IActionItem { public focus(): void { if (this.builder) { - this.builder.domFocus(); - this.builder.addClass('focused'); + this.builder.focus(); + DOM.addClass(this.builder, 'focused'); } } public blur(): void { if (this.builder) { - this.builder.domBlur(); - this.builder.removeClass('focused'); + this.builder.blur(); + DOM.removeClass(this.builder, 'focused'); } } @@ -203,7 +204,7 @@ export class BaseActionItem implements IActionItem { public dispose(): void { if (this.builder) { - this.builder.destroy(); + DOM.removeNode(this.builder); this.builder = null; } @@ -232,7 +233,7 @@ export interface IActionItemOptions extends IBaseActionItemOptions { export class ActionItem extends BaseActionItem { - protected $e: Builder; + protected $e: HTMLElement; protected options: IActionItemOptions; private cssClass: string; @@ -248,20 +249,20 @@ export class ActionItem extends BaseActionItem { public render(container: HTMLElement): void { super.render(container); - this.$e = $('a.action-label').appendTo(this.builder); + this.$e = DOM.append(this.builder, DOM.$('a.action-label')); if (this._action.id === Separator.ID) { // A separator is a presentation item - this.$e.attr({ role: 'presentation' }); + this.$e.setAttribute('role', 'presentation'); } else { if (this.options.isMenu) { - this.$e.attr({ role: 'menuitem' }); + this.$e.setAttribute('role', 'menuitem'); } else { - this.$e.attr({ role: 'button' }); + this.$e.setAttribute('role', 'button'); } } if (this.options.label && this.options.keybinding) { - $('span.keybinding').text(this.options.keybinding).appendTo(this.builder); + DOM.append(this.builder, DOM.$('span.keybinding')).textContent = this.options.keybinding; } this._updateClass(); @@ -273,12 +274,12 @@ export class ActionItem extends BaseActionItem { public focus(): void { super.focus(); - this.$e.domFocus(); + this.$e.focus(); } public _updateLabel(): void { if (this.options.label) { - this.$e.text(this.getAction().label); + this.$e.textContent = this.getAction().label; } } @@ -297,43 +298,43 @@ export class ActionItem extends BaseActionItem { } if (title) { - this.$e.attr({ title: title }); + this.$e.title = title; } } public _updateClass(): void { if (this.cssClass) { - this.$e.removeClass(this.cssClass); + DOM.removeClasses(this.$e, this.cssClass); } if (this.options.icon) { this.cssClass = this.getAction().class; - this.$e.addClass('icon'); + DOM.addClass(this.$e, 'icon'); if (this.cssClass) { - this.$e.addClass(this.cssClass); + DOM.addClasses(this.$e, this.cssClass); } this._updateEnabled(); } else { - this.$e.removeClass('icon'); + DOM.removeClass(this.$e, 'icon'); } } public _updateEnabled(): void { if (this.getAction().enabled) { - this.builder.removeClass('disabled'); - this.$e.removeClass('disabled'); - this.$e.attr({ tabindex: 0 }); + DOM.removeClass(this.builder, 'disabled'); + DOM.removeClass(this.$e, 'disabled'); + this.$e.tabIndex = 0; } else { - this.builder.addClass('disabled'); - this.$e.addClass('disabled'); - DOM.removeTabIndexAndUpdateFocus(this.$e.getHTMLElement()); + DOM.addClass(this.builder, 'disabled'); + DOM.addClass(this.$e, 'disabled'); + DOM.removeTabIndexAndUpdateFocus(this.$e); } } public _updateChecked(): void { if (this.getAction().checked) { - this.$e.addClass('checked'); + DOM.addClass(this.$e, 'checked'); } else { - this.$e.removeClass('checked'); + DOM.removeClass(this.$e, 'checked'); } } } @@ -439,7 +440,7 @@ export class ActionBar implements IActionRunner { break; } - $(this.domNode).on(DOM.EventType.KEY_DOWN, (e) => { + this.toDispose.push(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_DOWN, e => { let event = new StandardKeyboardEvent(e as KeyboardEvent); let eventHandled = true; @@ -459,9 +460,9 @@ export class ActionBar implements IActionRunner { event.preventDefault(); event.stopPropagation(); } - }); + })); - $(this.domNode).on(DOM.EventType.KEY_UP, (e) => { + this.toDispose.push(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_UP, e => { let event = new StandardKeyboardEvent(e as KeyboardEvent); // Run action on Enter/Space @@ -475,7 +476,7 @@ export class ActionBar implements IActionRunner { else if (event.equals(KeyCode.Tab) || event.equals(KeyMod.Shift | KeyCode.Tab)) { this.updateFocusedItem(); } - }); + })); this.focusTracker = DOM.trackFocus(this.domNode); this.toDispose.push(this.focusTracker.onDidBlur(() => { @@ -570,10 +571,10 @@ export class ActionBar implements IActionRunner { actionItemElement.setAttribute('role', 'presentation'); // Prevent native context menu on actions - $(actionItemElement).on(DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => { + this.toDispose.push(DOM.addDisposableListener(actionItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => { e.preventDefault(); e.stopPropagation(); - }); + })); let item: IActionItem = null; @@ -626,7 +627,7 @@ export class ActionBar implements IActionRunner { public clear(): void { this.items = lifecycle.dispose(this.items); - $(this.actionsList).empty(); + DOM.clearNode(this.actionsList); } public length(): number { @@ -756,7 +757,7 @@ export class ActionBar implements IActionRunner { this.toDispose = lifecycle.dispose(this.toDispose); - $(this.getContainer()).destroy(); + DOM.removeNode(this.getContainer()); } } diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index c16fc27af11..15ad50296cb 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -6,9 +6,8 @@ 'use strict'; import 'vs/css!./contextview'; -import { Builder, $ } from 'vs/base/browser/builder'; import * as DOM from 'vs/base/browser/dom'; -import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; export interface IAnchor { x: number; @@ -101,39 +100,50 @@ export class ContextView { private static readonly BUBBLE_UP_EVENTS = ['click', 'keydown', 'focus', 'blur']; private static readonly BUBBLE_DOWN_EVENTS = ['click']; - private $container: Builder; - private $view: Builder; + private $container: HTMLElement; + private $view: HTMLElement; private delegate: IDelegate; private toDispose: IDisposable[]; private toDisposeOnClean: IDisposable; + private toDisposeOnSetContainer: IDisposable; constructor(container: HTMLElement) { - this.$view = $('.context-view').hide(); + this.$view = DOM.$('.context-view'); + + DOM.hide(this.$view); + this.setContainer(container); this.toDispose = [toDisposable(() => { this.setContainer(null); })]; - - this.toDisposeOnClean = null; } public setContainer(container: HTMLElement): void { if (this.$container) { - this.$container.getHTMLElement().removeChild(this.$view.getHTMLElement()); - this.$container.off(ContextView.BUBBLE_UP_EVENTS); - this.$container.off(ContextView.BUBBLE_DOWN_EVENTS, true); + this.toDisposeOnSetContainer = dispose(this.toDisposeOnSetContainer); + this.$container.removeChild(this.$view); this.$container = null; } if (container) { - this.$container = $(container); - this.$view.appendTo(this.$container); - this.$container.on(ContextView.BUBBLE_UP_EVENTS, (e: Event) => { - this.onDOMEvent(e, document.activeElement, false); + this.$container = container; + this.$container.appendChild(this.$view); + + const toDispose: IDisposable[] = []; + + ContextView.BUBBLE_UP_EVENTS.forEach(event => { + toDispose.push(DOM.addStandardDisposableListener(this.$container, event, (e: Event) => { + this.onDOMEvent(e, document.activeElement, false); + })); }); - this.$container.on(ContextView.BUBBLE_DOWN_EVENTS, (e: Event) => { - this.onDOMEvent(e, document.activeElement, true); - }, null, true); + + ContextView.BUBBLE_DOWN_EVENTS.forEach(event => { + toDispose.push(DOM.addStandardDisposableListener(this.$container, event, (e: Event) => { + this.onDOMEvent(e, document.activeElement, true); + }, true)); + }); + + this.toDisposeOnSetContainer = combinedDisposable(toDispose); } } @@ -143,10 +153,14 @@ export class ContextView { } // Show static box - this.$view.setClass('context-view').empty().style({ top: '0px', left: '0px' }).show(); + DOM.clearNode(this.$view); + this.$view.className = 'context-view'; + this.$view.style.top = '0px'; + this.$view.style.left = '0px'; + DOM.show(this.$view); // Render content - this.toDisposeOnClean = delegate.render(this.$view.getHTMLElement()); + this.toDisposeOnClean = delegate.render(this.$view); // Set active delegate this.delegate = delegate; @@ -205,7 +219,9 @@ export class ContextView { }; } - const viewSize = this.$view.getTotalSize(); + const viewSizeWidth = DOM.getTotalWidth(this.$view); + const viewSizeHeight = DOM.getTotalHeight(this.$view); + const anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW; const anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT; @@ -219,14 +235,17 @@ export class ContextView { horizontalAnchor = { offset: around.left + around.width, size: 0, position: LayoutAnchorPosition.After }; } - const containerPosition = DOM.getDomNodePagePosition(this.$container.getHTMLElement()); - const top = layout(window.innerHeight, viewSize.height, verticalAnchor) - containerPosition.top; - const left = layout(window.innerWidth, viewSize.width, horizontalAnchor) - containerPosition.left; + const containerPosition = DOM.getDomNodePagePosition(this.$container); + const top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) - containerPosition.top; + const left = layout(window.innerWidth, viewSizeWidth, horizontalAnchor) - containerPosition.left; - this.$view.removeClass('top', 'bottom', 'left', 'right'); - this.$view.addClass(anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top'); - this.$view.addClass(anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right'); - this.$view.style({ top: `${top}px`, left: `${left}px`, width: 'initial' }); + DOM.removeClasses(this.$view, 'top', 'bottom', 'left', 'right'); + DOM.addClass(this.$view, anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top'); + DOM.addClass(this.$view, anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right'); + + this.$view.style.top = `${top}px`; + this.$view.style.left = `${left}px`; + this.$view.style.width = 'initial'; } public hide(data?: any): void { @@ -241,7 +260,7 @@ export class ContextView { this.toDisposeOnClean = null; } - this.$view.hide(); + DOM.hide(this.$view); } private isVisible(): boolean { @@ -252,7 +271,7 @@ export class ContextView { if (this.delegate) { if (this.delegate.onDOMEvent) { this.delegate.onDOMEvent(e, document.activeElement); - } else if (onCapture && !DOM.isAncestor(e.target, this.$container.getHTMLElement())) { + } else if (onCapture && !DOM.isAncestor(e.target, this.$container)) { this.hide(); } } diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index f2b9a1f842e..a2fd1d60735 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -6,7 +6,6 @@ 'use strict'; import 'vs/css!./dropdown'; -import { Builder, $ } from 'vs/base/browser/builder'; import { TPromise } from 'vs/base/common/winjs.base'; import { Gesture, EventType as GestureEventType } from 'vs/base/browser/touch'; import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions'; @@ -15,7 +14,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IContextViewProvider, IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { IMenuOptions } from 'vs/base/browser/ui/menu/menu'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { EventHelper, EventType, removeClass, addClass } from 'vs/base/browser/dom'; +import { EventHelper, EventType, removeClass, addClass, append, $, removeNode, addDisposableListener, addClasses } from 'vs/base/browser/dom'; import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; export interface ILabelRenderer { @@ -30,47 +29,52 @@ export interface IBaseDropdownOptions { export class BaseDropdown extends ActionRunner { private _toDispose: IDisposable[] = []; - private $el: Builder; - private $boxContainer: Builder; - private $label: Builder; - private $contents: Builder; + private $el: HTMLElement; + private $boxContainer: HTMLElement; + private $label: HTMLElement; + private $contents: HTMLElement; private visible: boolean; constructor(container: HTMLElement, options: IBaseDropdownOptions) { super(); - this.$el = $('.monaco-dropdown').appendTo(container); + this.$el = append(container, $('.monaco-dropdown')); - this.$label = $('.dropdown-label'); + this.$label = append(this.$el, $('.dropdown-label')); let labelRenderer = options.labelRenderer; if (!labelRenderer) { labelRenderer = (container: HTMLElement): IDisposable => { - $(container).text(options.label || ''); + container.textContent = options.label || ''; + return null; }; } - this.$label.on([EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap], (e: Event) => { - EventHelper.stop(e, true); // prevent default click behaviour to trigger - }).on([EventType.MOUSE_DOWN, GestureEventType.Tap], (e: Event) => { - if (e instanceof MouseEvent && e.detail > 1) { - return; // prevent multiple clicks to open multiple context menus (https://github.com/Microsoft/vscode/issues/41363) - } + [EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap].forEach(event => { + this._toDispose.push(addDisposableListener(this.$label, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger + }); - if (this.visible) { - this.hide(); - } else { - this.show(); - } - }).appendTo(this.$el); + [EventType.MOUSE_DOWN, GestureEventType.Tap].forEach(event => { + this._toDispose.push(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) + } - const cleanupFn = labelRenderer(this.$label.getHTMLElement()); + if (this.visible) { + this.hide(); + } else { + this.show(); + } + })); + }); + + const cleanupFn = labelRenderer(this.$label); if (cleanupFn) { this._toDispose.push(cleanupFn); } - Gesture.addTarget(this.$label.getHTMLElement()); + Gesture.addTarget(this.$label); } get toDispose(): IDisposable[] { @@ -78,15 +82,15 @@ export class BaseDropdown extends ActionRunner { } get element(): HTMLElement { - return this.$el.getHTMLElement(); + return this.$el; } get label(): HTMLElement { - return this.$label.getHTMLElement(); + return this.$label; } set tooltip(tooltip: string) { - this.$label.title(tooltip); + this.$label.title = tooltip; } show(): void { @@ -108,17 +112,17 @@ export class BaseDropdown extends ActionRunner { this._toDispose = dispose(this.toDispose); if (this.$boxContainer) { - this.$boxContainer.destroy(); + removeNode(this.$boxContainer); this.$boxContainer = null; } if (this.$contents) { - this.$contents.destroy(); + removeNode(this.$contents); this.$contents = null; } if (this.$label) { - this.$label.destroy(); + removeNode(this.$label); this.$label = null; } } @@ -279,15 +283,13 @@ export class DropdownMenuActionItem extends BaseActionItem { render(container: HTMLElement): void { const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable => { - this.builder = $('a.action-label').attr({ - tabIndex: '0', - role: 'button', - 'aria-haspopup': 'true', - title: this._action.label || '', - class: this.clazz - }); + this.builder = append(el, $('a.action-label')); + addClasses(this.builder, this.clazz); - this.builder.appendTo(el); + this.builder.tabIndex = 0; + this.builder.setAttribute('role', 'button'); + this.builder.setAttribute('aria-haspopup', 'true'); + this.builder.title = this._action.label || ''; return null; }; diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 5fdb8d19292..a2d946073e1 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -11,7 +11,7 @@ import * as strings from 'vs/base/common/strings'; import { IActionRunner, IAction, Action } from 'vs/base/common/actions'; import { ActionBar, IActionItemProvider, ActionsOrientation, Separator, ActionItem, IActionItemOptions, BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { ResolvedKeybinding, KeyCode, KeyCodeUtils } from 'vs/base/common/keyCodes'; -import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener } from 'vs/base/browser/dom'; +import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { $, Builder } from 'vs/base/browser/builder'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -335,11 +335,11 @@ class MenuActionItem extends BaseActionItem { _updateEnabled(): void { if (this.getAction().enabled) { - this.builder.removeClass('disabled'); + removeClass(this.builder, 'disabled'); this.$e.removeClass('disabled'); this.$e.attr({ tabindex: 0 }); } else { - this.builder.addClass('disabled'); + addClass(this.builder, 'disabled'); this.$e.addClass('disabled'); removeTabIndexAndUpdateFocus(this.$e.getHTMLElement()); } @@ -383,7 +383,7 @@ class SubmenuActionItem extends MenuActionItem { }, 250); this.hideScheduler = new RunOnceScheduler(() => { - if ((!isAncestor(document.activeElement, this.builder.getHTMLElement()) && this.parentData.submenu === this.mysubmenu)) { + if ((!isAncestor(document.activeElement, this.builder) && this.parentData.submenu === this.mysubmenu)) { this.parentData.parent.focus(false); this.cleanupExistingSubmenu(true); } @@ -426,7 +426,7 @@ class SubmenuActionItem extends MenuActionItem { }); $(this.builder).on(EventType.FOCUS_OUT, (e) => { - if (!isAncestor(document.activeElement, this.builder.getHTMLElement())) { + if (!isAncestor(document.activeElement, this.builder)) { this.hideScheduler.schedule(); } }); diff --git a/src/vs/platform/actions/browser/menuItemActionItem.ts b/src/vs/platform/actions/browser/menuItemActionItem.ts index a8ff208c0a6..20de6c969d2 100644 --- a/src/vs/platform/actions/browser/menuItemActionItem.ts +++ b/src/vs/platform/actions/browser/menuItemActionItem.ts @@ -9,13 +9,13 @@ import { localize } from 'vs/nls'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IMenu, MenuItemAction, IMenuActionOptions, ICommandAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IAction } from 'vs/base/common/actions'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { domEvent } from 'vs/base/browser/event'; import { Emitter } from 'vs/base/common/event'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IdGenerator } from 'vs/base/common/idGenerator'; -import { createCSSRule } from 'vs/base/browser/dom'; +import { createCSSRule, addClasses, removeClasses } from 'vs/base/browser/dom'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { isWindows, isLinux } from 'vs/base/common/platform'; @@ -217,12 +217,12 @@ export class MenuItemActionItem extends ActionItem { _updateLabel(): void { if (this.options.label) { - this.$e.text(this._commandAction.label); + this.$e.textContent = this._commandAction.label; } } _updateTooltip(): void { - const element = this.$e.getHTMLElement(); + const element = this.$e; const keybinding = this._keybindingService.lookupKeybinding(this._commandAction.id); const keybindingLabel = keybinding && keybinding.getLabel(); @@ -259,8 +259,8 @@ export class MenuItemActionItem extends ActionItem { MenuItemActionItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass); } - this.$e.getHTMLElement().classList.add('icon', iconClass); - this._itemClassDispose = { dispose: () => this.$e.getHTMLElement().classList.remove('icon', iconClass) }; + addClasses(this.$e, 'icon', iconClass); + this._itemClassDispose = toDisposable(() => removeClasses(this.$e, 'icon', iconClass)); } } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 773bbe0d763..da7c06bd2d9 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -122,28 +122,29 @@ export class GlobalActivityActionItem extends ActivityActionItem { // Context menus are triggered on mouse down so that an item can be picked // and executed with releasing the mouse over it - this.$container.on(DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { + + this.callOnDispose.push(DOM.addDisposableListener(this.$container, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { DOM.EventHelper.stop(e, true); const event = new StandardMouseEvent(e); this.showContextMenu({ x: event.posx, y: event.posy }); - }); + })); - this.$container.on(DOM.EventType.KEY_UP, (e: KeyboardEvent) => { + this.callOnDispose.push(DOM.addDisposableListener(this.$container, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { let event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { DOM.EventHelper.stop(e, true); - this.showContextMenu(this.$container.getHTMLElement()); + this.showContextMenu(this.$container); } - }); + })); - this.$container.on(TouchEventType.Tap, (e: GestureEvent) => { + this.callOnDispose.push(DOM.addDisposableListener(this.$container, TouchEventType.Tap, (e: GestureEvent) => { DOM.EventHelper.stop(e, true); const event = new StandardMouseEvent(e); this.showContextMenu({ x: event.posx, y: event.posy }); - }); + })); } private showContextMenu(location: HTMLElement | { x: number, y: number }): void { diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index fa12612a68a..f1a10123c37 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -14,13 +14,14 @@ import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ActionBar, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { CompositeActionItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionItem, ActivityAction, ICompositeBar, ICompositeBarColors } from 'vs/workbench/browser/parts/compositeBarActions'; +import { CompositeActionItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionItem, ActivityAction, ICompositeBar, ICompositeBarColors, DraggedCompositeIdentifier } from 'vs/workbench/browser/parts/compositeBarActions'; import { TPromise } from 'vs/base/common/winjs.base'; import { Dimension, $, addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { Widget } from 'vs/base/browser/ui/widget'; import { isUndefinedOrNull } from 'vs/base/common/types'; +import { LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; export interface ICompositeBarOptions { icon: boolean; @@ -50,6 +51,8 @@ export class CompositeBar extends Widget implements ICompositeBar { private visibleComposites: string[]; private compositeSizeInBar: Map; + private compositeTransfer: LocalSelectionTransfer; + constructor( private options: ICompositeBarOptions, @IInstantiationService private instantiationService: IInstantiationService, @@ -61,6 +64,7 @@ export class CompositeBar extends Widget implements ICompositeBar { this.model = new CompositeBarModel(options, storageService); this.visibleComposites = []; this.compositeSizeInBar = new Map(); + this.compositeTransfer = LocalSelectionTransfer.getInstance(); } create(parent: HTMLElement): HTMLElement { @@ -83,10 +87,11 @@ export class CompositeBar extends Widget implements ICompositeBar { // Allow to drop at the end to move composites to the end this._register(addDisposableListener(parent, EventType.DROP, (e: DragEvent) => { - const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); - if (draggedCompositeId) { + if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { EventHelper.stop(e, true); - CompositeActionItem.clearDraggedComposite(); + + const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id; + this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); const targetItem = this.model.visibleItems[this.model.visibleItems.length - 1]; if (targetItem && targetItem.id !== draggedCompositeId) { diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index eed75df278b..3b275abc9cd 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -9,7 +9,6 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { TPromise } from 'vs/base/common/winjs.base'; import * as dom from 'vs/base/browser/dom'; -import { Builder, $ } from 'vs/base/browser/builder'; import { BaseActionItem, IBaseActionItemOptions, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { dispose, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -22,6 +21,7 @@ import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { IActivity } from 'vs/workbench/common/activity'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Event, Emitter } from 'vs/base/common/event'; +import { DragAndDropObserver, LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; export interface ICompositeActivity { badge: IBadge; @@ -124,12 +124,12 @@ export interface IActivityActionItemOptions extends IBaseActionItemOptions { } export class ActivityActionItem extends BaseActionItem { - protected $container: Builder; - protected $label: Builder; - protected $badge: Builder; + protected $container: HTMLElement; + protected $label: HTMLElement; + protected $badge: HTMLElement; protected options: IActivityActionItemOptions; - private $badgeContent: Builder; + private $badgeContent: HTMLElement; private badgeDisposable: IDisposable = Disposable.None; private mouseUpTimeout: number; @@ -156,7 +156,7 @@ export class ActivityActionItem extends BaseActionItem { if (this.$label && this.options.icon) { const background = theme.getColor(this.options.colors.backgroundColor); - this.$label.style('background-color', background ? background.toString() : null); + this.$label.style.backgroundColor = background ? background.toString() : null; } // Badge @@ -165,46 +165,47 @@ export class ActivityActionItem extends BaseActionItem { const badgeBackground = theme.getColor(this.options.colors.badgeBackground); const contrastBorderColor = theme.getColor(contrastBorder); - this.$badgeContent.style('color', badgeForeground ? badgeForeground.toString() : null); - this.$badgeContent.style('background-color', badgeBackground ? badgeBackground.toString() : null); + this.$badgeContent.style.color = badgeForeground ? badgeForeground.toString() : null; + this.$badgeContent.style.backgroundColor = badgeBackground ? badgeBackground.toString() : null; - this.$badgeContent.style('border-style', contrastBorderColor ? 'solid' : null); - this.$badgeContent.style('border-width', contrastBorderColor ? '1px' : null); - this.$badgeContent.style('border-color', contrastBorderColor ? contrastBorderColor.toString() : null); + this.$badgeContent.style.borderStyle = contrastBorderColor ? 'solid' : null; + this.$badgeContent.style.borderWidth = contrastBorderColor ? '1px' : null; + this.$badgeContent.style.borderColor = contrastBorderColor ? contrastBorderColor.toString() : null; } } render(container: HTMLElement): void { super.render(container); + this.$container = container; + // Make the container tab-able for keyboard navigation - this.$container = $(container).attr({ - tabIndex: '0', - role: 'button' - }); + this.$container.tabIndex = 0; + this.$container.setAttribute('role', 'button'); // Try hard to prevent keyboard only focus feedback when using mouse - this.$container.on(dom.EventType.MOUSE_DOWN, () => { - this.$container.addClass('clicked'); - }); + this.callOnDispose.push(dom.addDisposableListener(this.$container, dom.EventType.MOUSE_DOWN, () => { + dom.addClass(this.$container, 'clicked'); + })); - this.$container.on(dom.EventType.MOUSE_UP, () => { + this.callOnDispose.push(dom.addDisposableListener(this.$container, dom.EventType.MOUSE_UP, () => { if (this.mouseUpTimeout) { clearTimeout(this.mouseUpTimeout); } this.mouseUpTimeout = setTimeout(() => { - this.$container.removeClass('clicked'); + dom.removeClass(this.$container, 'clicked'); }, 800); // delayed to prevent focus feedback from showing on mouse up - }); + })); // Label - this.$label = $('a.action-label').appendTo(this.builder); + this.$label = dom.append(this.builder, dom.$('a.action-label')); - this.$badge = this.builder.clone().div({ 'class': 'badge' }, badge => { - this.$badgeContent = badge.div({ 'class': 'badge-content' }); - }); - this.$badge.hide(); + // Badge + this.$badge = dom.append(this.builder, dom.$('.badge')); + this.$badgeContent = dom.append(this.$badge, dom.$('.badge-content')); + + dom.hide(this.$badge); this.updateActivity(); this.updateStyles(); @@ -232,8 +233,8 @@ export class ActivityActionItem extends BaseActionItem { this.badgeDisposable.dispose(); this.badgeDisposable = Disposable.None; - this.$badgeContent.empty(); - this.$badge.hide(); + dom.clearNode(this.$badgeContent); + dom.hide(this.$badge); if (badge) { @@ -246,30 +247,30 @@ export class ActivityActionItem extends BaseActionItem { } else if (badge.number > 999) { number = number.charAt(0) + 'k'; } - this.$badgeContent.text(number); - this.$badge.show(); + this.$badgeContent.textContent = number; + dom.show(this.$badge); } } // Text else if (badge instanceof TextBadge) { - this.$badgeContent.text(badge.text); - this.$badge.show(); + this.$badgeContent.textContent = badge.text; + dom.show(this.$badge); } // Text else if (badge instanceof IconBadge) { - this.$badge.show(); + dom.show(this.$badge); } // Progress else if (badge instanceof ProgressBadge) { - this.$badge.show(); + dom.show(this.$badge); } if (clazz) { - this.$badge.addClass(clazz); - this.badgeDisposable = toDisposable(() => this.$badge.removeClass(clazz)); + dom.addClasses(this.$badge, clazz); + this.badgeDisposable = toDisposable(() => dom.removeClasses(this.$badge, clazz)); } } @@ -289,18 +290,18 @@ export class ActivityActionItem extends BaseActionItem { private updateLabel(): void { if (this.activity.cssClass) { - this.$label.addClass(this.activity.cssClass); + dom.addClasses(this.$label, this.activity.cssClass); } if (!this.options.icon) { - this.$label.text(this.getAction().label); + this.$label.textContent = this.getAction().label; } } private updateTitle(title: string): void { - [this.$label, this.$badge, this.$container].forEach(b => { - if (b) { - b.attr('aria-label', title); - b.title(title); + [this.$label, this.$badge, this.$container].forEach(element => { + if (element) { + element.setAttribute('aria-label', title); + element.title = title; } }); } @@ -312,7 +313,7 @@ export class ActivityActionItem extends BaseActionItem { clearTimeout(this.mouseUpTimeout); } - this.$badge.destroy(); + dom.removeNode(this.$badge); } } @@ -359,7 +360,7 @@ export class CompositeOverflowActivityActionItem extends ActivityActionItem { this.actions = this.getActions(); this.contextMenuService.showContextMenu({ - getAnchor: () => this.builder.getHTMLElement(), + getAnchor: () => this.builder, getActions: () => TPromise.as(this.actions), onHide: () => dispose(this.actions) }); @@ -408,13 +409,21 @@ class ManageExtensionAction extends Action { } } +export class DraggedCompositeIdentifier { + constructor(private _compositeId: string) { } + + get id(): string { + return this._compositeId; + } +} + export class CompositeActionItem extends ActivityActionItem { private static manageExtensionAction: ManageExtensionAction; - private static draggedCompositeId: string; private compositeActivity: IActivity; private cssClass: string; + private compositeTransfer: LocalSelectionTransfer; constructor( private compositeActivityAction: ActivityAction, @@ -430,6 +439,7 @@ export class CompositeActionItem extends ActivityActionItem { super(compositeActivityAction, { draggable: true, colors, icon }, themeService); this.cssClass = compositeActivityAction.class; + this.compositeTransfer = LocalSelectionTransfer.getInstance(); if (!CompositeActionItem.manageExtensionAction) { CompositeActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction); @@ -473,71 +483,64 @@ export class CompositeActionItem extends ActivityActionItem { this._updateChecked(); this._updateEnabled(); - this.$container.on('contextmenu', e => { + this.callOnDispose.push(dom.addDisposableListener(this.$container, dom.EventType.CONTEXT_MENU, e => { dom.EventHelper.stop(e, true); this.showContextMenu(container); - }); + })); // Allow to drag - this.$container.on(dom.EventType.DRAG_START, (e: DragEvent) => { + this.callOnDispose.push(dom.addDisposableListener(this.$container, dom.EventType.DRAG_START, (e: DragEvent) => { e.dataTransfer.effectAllowed = 'move'; - this.setDraggedComposite(this.activity.id); + + // Registe as dragged to local transfer + this.compositeTransfer.setData([new DraggedCompositeIdentifier(this.activity.id)], DraggedCompositeIdentifier.prototype); // Trigger the action even on drag start to prevent clicks from failing that started a drag if (!this.getAction().checked) { this.getAction().run(); } - }); + })); - // Drag enter - let counter = 0; // see https://github.com/Microsoft/vscode/issues/14470 - this.$container.on(dom.EventType.DRAG_ENTER, (e: DragEvent) => { - const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); - if (draggedCompositeId && draggedCompositeId !== this.activity.id) { - counter++; - this.updateFromDragging(container, true); - } - }); + this.callOnDispose.push(new DragAndDropObserver(this.$container, { + onDragEnter: e => { + if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id !== this.activity.id) { + this.updateFromDragging(container, true); + } + }, - // Drag leave - this.$container.on(dom.EventType.DRAG_LEAVE, (e: DragEvent) => { - const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); - if (draggedCompositeId) { - counter--; - if (counter === 0) { + onDragLeave: e => { + if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { this.updateFromDragging(container, false); } + }, + + onDragEnd: e => { + if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { + this.updateFromDragging(container, false); + + this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); + } + }, + + onDrop: e => { + dom.EventHelper.stop(e, true); + + if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { + const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id; + if (draggedCompositeId !== this.activity.id) { + this.updateFromDragging(container, false); + this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); + + this.compositeBar.move(draggedCompositeId, this.activity.id); + } + } } - }); - - // Drag end - this.$container.on(dom.EventType.DRAG_END, (e: DragEvent) => { - const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); - if (draggedCompositeId) { - counter = 0; - this.updateFromDragging(container, false); - - CompositeActionItem.clearDraggedComposite(); - } - }); - - // Drop - this.$container.on(dom.EventType.DROP, (e: DragEvent) => { - dom.EventHelper.stop(e, true); - - const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); - if (draggedCompositeId && draggedCompositeId !== this.activity.id) { - this.updateFromDragging(container, false); - CompositeActionItem.clearDraggedComposite(); - - this.compositeBar.move(draggedCompositeId, this.activity.id); - } - }); + })); // Activate on drag over to reveal targets - [this.$badge, this.$label].forEach(b => new DelayedDragHandler(b.getHTMLElement(), () => { - if (!CompositeActionItem.getDraggedCompositeId() && !this.getAction().checked) { + [this.$badge, this.$label].forEach(b => new DelayedDragHandler(b, () => { + if (!this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && !this.getAction().checked) { this.getAction().run(); } })); @@ -552,18 +555,6 @@ export class CompositeActionItem extends ActivityActionItem { element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : null; } - static getDraggedCompositeId(): string { - return CompositeActionItem.draggedCompositeId; - } - - private setDraggedComposite(compositeId: string): void { - CompositeActionItem.draggedCompositeId = compositeId; - } - - static clearDraggedComposite(): void { - CompositeActionItem.draggedCompositeId = void 0; - } - private showContextMenu(container: HTMLElement): void { const actions: Action[] = [this.toggleCompositePinnedAction]; if ((this.compositeActivityAction.activity).extensionId) { @@ -587,44 +578,44 @@ export class CompositeActionItem extends ActivityActionItem { } focus(): void { - this.$container.domFocus(); + this.$container.focus(); } protected _updateClass(): void { if (this.cssClass) { - this.$label.removeClass(this.cssClass); + dom.removeClasses(this.$label, this.cssClass); } this.cssClass = this.getAction().class; if (this.cssClass) { - this.$label.addClass(this.cssClass); + dom.addClasses(this.$label, this.cssClass); } } protected _updateChecked(): void { if (this.getAction().checked) { - this.$container.addClass('checked'); - this.$container.attr('aria-label', nls.localize('compositeActive', "{0} active", this.$container.getHTMLElement().title)); + dom.addClass(this.$container, 'checked'); + this.$container.setAttribute('aria-label', nls.localize('compositeActive', "{0} active", this.$container.title)); } else { - this.$container.removeClass('checked'); - this.$container.attr('aria-label', this.$container.getHTMLElement().title); + dom.removeClass(this.$container, 'checked'); + this.$container.setAttribute('aria-label', this.$container.title); } } protected _updateEnabled(): void { if (this.getAction().enabled) { - this.builder.removeClass('disabled'); + dom.removeClass(this.builder, 'disabled'); } else { - this.builder.addClass('disabled'); + dom.addClass(this.builder, 'disabled'); } } dispose(): void { super.dispose(); - CompositeActionItem.clearDraggedComposite(); + this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); - this.$label.destroy(); + dom.removeNode(this.$label); } } diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 52578559e00..70b3371097b 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -10,7 +10,6 @@ import * as nls from 'vs/nls'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Builder, $ } from 'vs/base/browser/builder'; import * as strings from 'vs/base/common/strings'; import { Emitter } from 'vs/base/common/event'; import * as types from 'vs/base/common/types'; @@ -35,7 +34,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { Dimension } from 'vs/base/browser/dom'; +import { Dimension, append, $, addClass, hide, removeNode, show, addClasses } from 'vs/base/browser/dom'; export interface ICompositeTitleLabel { @@ -58,7 +57,7 @@ export abstract class CompositePart extends Part { protected toolBar: ToolBar; private instantiatedCompositeListeners: IDisposable[]; - private mapCompositeToCompositeContainer: { [compositeId: string]: Builder; }; + private mapCompositeToCompositeContainer: { [compositeId: string]: HTMLElement; }; private mapActionsBindingToComposite: { [compositeId: string]: () => void; }; private mapProgressServiceToComposite: { [compositeId: string]: IProgressService; }; private activeComposite: Composite; @@ -222,13 +221,12 @@ export abstract class CompositePart extends Part { if (!compositeContainer) { // Build Container off-DOM - compositeContainer = $().div({ - 'class': ['composite', this.compositeCSSClass], - id: composite.getId() - }, div => { - createCompositePromise = composite.create(div.getHTMLElement()).then(() => { - composite.updateStyles(); - }); + compositeContainer = $('.composite'); + addClasses(compositeContainer, this.compositeCSSClass); + compositeContainer.id = composite.getId(); + + createCompositePromise = composite.create(compositeContainer).then(() => { + composite.updateStyles(); }); // Remember composite container @@ -255,8 +253,8 @@ export abstract class CompositePart extends Part { } // Take Composite on-DOM and show - compositeContainer.build(this.getContentArea()); - compositeContainer.show(); + this.getContentArea().appendChild(compositeContainer); + show(compositeContainer); // Setup action runner this.toolBar.actionRunner = composite.getActionRunner(); @@ -389,8 +387,8 @@ export abstract class CompositePart extends Part { return composite.setVisible(false).then(() => { // Take Container Off-DOM and hide - compositeContainer.offDOM(); - compositeContainer.hide(); + removeNode(compositeContainer); + hide(compositeContainer); // Clear any running Progress this.progressBar.stop().hide(); @@ -406,45 +404,38 @@ export abstract class CompositePart extends Part { createTitleArea(parent: HTMLElement): HTMLElement { // Title Area Container - const titleArea = $(parent).div({ - 'class': ['composite', 'title'] - }); + const titleArea = append(parent, $('.composite')); + addClass(titleArea, 'title'); // Left Title Label - this.titleLabel = this.createTitleLabel(titleArea.getHTMLElement()); + this.titleLabel = this.createTitleLabel(titleArea); // Right Actions Container - $(titleArea).div({ - 'class': 'title-actions' - }, div => { + const titleActionsContainer = append(titleArea, $('.title-actions')); - // Toolbar - this.toolBar = this._register(new ToolBar(div.getHTMLElement(), this.contextMenuService, { - actionItemProvider: action => this.actionItemProvider(action as Action), - orientation: ActionsOrientation.HORIZONTAL, - getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id) - })); - }); + // Toolbar + this.toolBar = this._register(new ToolBar(titleActionsContainer, this.contextMenuService, { + actionItemProvider: action => this.actionItemProvider(action as Action), + orientation: ActionsOrientation.HORIZONTAL, + getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id) + })); - return titleArea.getHTMLElement(); + return titleArea; } protected createTitleLabel(parent: HTMLElement): ICompositeTitleLabel { - let titleLabel: Builder; - $(parent).div({ - 'class': 'title-label' - }, div => { - titleLabel = div.element('h2'); - }); + const titleContainer = append(parent, $('.title-label')); + const titleLabel = append(titleContainer, $('h2')); const $this = this; return { updateTitle: (id, title, keybinding) => { - titleLabel.safeInnerHtml(title); - titleLabel.title(keybinding ? nls.localize('titleTooltip', "{0} ({1})", title, keybinding) : title); + titleLabel.innerHTML = strings.escape(title); + titleLabel.title = keybinding ? nls.localize('titleTooltip', "{0} ({1})", title, keybinding) : title; }, + updateStyles: () => { - titleLabel.style('color', $this.getColor($this.titleForegroundColor)); + titleLabel.style.color = $this.getColor($this.titleForegroundColor); } }; } @@ -467,13 +458,13 @@ export abstract class CompositePart extends Part { } createContentArea(parent: HTMLElement): HTMLElement { - return $(parent).div({ - 'class': 'content' - }, div => { - this.progressBar = this._register(new ProgressBar(div.getHTMLElement())); - this._register(attachProgressBarStyler(this.progressBar, this.themeService)); - this.progressBar.hide(); - }).getHTMLElement(); + const contentContainer = append(parent, $('.content')); + + this.progressBar = this._register(new ProgressBar(contentContainer)); + this._register(attachProgressBarStyler(this.progressBar, this.themeService)); + this.progressBar.hide(); + + return contentContainer; } private onError(error: any): void { diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index d0c0302f07c..baccf0bc4f9 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -10,7 +10,6 @@ import * as nls from 'vs/nls'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { TPromise } from 'vs/base/common/winjs.base'; import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { $ } from 'vs/base/browser/builder'; import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { Registry } from 'vs/platform/registry/common/platform'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -28,15 +27,15 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { isThemeColor } from 'vs/editor/common/editorCommon'; import { Color } from 'vs/base/common/color'; -import { addClass, EventHelper, createStyleSheet } from 'vs/base/browser/dom'; +import { addClass, EventHelper, createStyleSheet, addDisposableListener, removeNode } from 'vs/base/browser/dom'; import { INotificationService } from 'vs/platform/notification/common/notification'; export class StatusbarPart extends Part implements IStatusbarService { _serviceBrand: any; - private static readonly PRIORITY_PROP = 'priority'; - private static readonly ALIGNMENT_PROP = 'alignment'; + private static readonly PRIORITY_PROP = 'statusbar-entry-priority'; + private static readonly ALIGNMENT_PROP = 'statusbar-entry-alignment'; private statusItemsContainer: HTMLElement; private statusMsgDispose: IDisposable; @@ -71,7 +70,7 @@ export class StatusbarPart extends Part implements IStatusbarService { let inserted = false; for (let i = 0; i < neighbours.length; i++) { const neighbour = neighbours[i]; - const nPriority = $(neighbour).getProperty(StatusbarPart.PRIORITY_PROP); + const nPriority = Number(neighbour.getAttribute(StatusbarPart.PRIORITY_PROP)); if ( alignment === StatusbarAlignment.LEFT && nPriority < priority || alignment === StatusbarAlignment.RIGHT && nPriority > priority @@ -87,7 +86,7 @@ export class StatusbarPart extends Part implements IStatusbarService { } return toDisposable(() => { - $(el).destroy(); + removeNode(el); if (toDispose) { toDispose.dispose(); @@ -102,7 +101,7 @@ export class StatusbarPart extends Part implements IStatusbarService { const children = container.children; for (let i = 0; i < children.length; i++) { const childElement = children.item(i); - if ($(childElement).getProperty(StatusbarPart.ALIGNMENT_PROP) === alignment) { + if (Number(childElement.getAttribute(StatusbarPart.ALIGNMENT_PROP)) === alignment) { entries.push(childElement); } } @@ -168,8 +167,8 @@ export class StatusbarPart extends Part implements IStatusbarService { addClass(el, 'left'); } - $(el).setProperty(StatusbarPart.PRIORITY_PROP, priority); - $(el).setProperty(StatusbarPart.ALIGNMENT_PROP, alignment); + el.setAttribute(StatusbarPart.PRIORITY_PROP, String(priority)); + el.setAttribute(StatusbarPart.ALIGNMENT_PROP, String(alignment)); return el; } @@ -242,7 +241,7 @@ class StatusBarEntryItem implements IStatusbarItem { if (this.entry.command) { textContainer = document.createElement('a'); - $(textContainer).on('click', () => this.executeCommand(this.entry.command, this.entry.arguments), toDispose); + toDispose.push(addDisposableListener(textContainer, 'click', () => this.executeCommand(this.entry.command, this.entry.arguments))); } else { textContainer = document.createElement('span'); } @@ -252,7 +251,7 @@ class StatusBarEntryItem implements IStatusbarItem { // Tooltip if (this.entry.tooltip) { - $(textContainer).title(this.entry.tooltip); + textContainer.title = this.entry.tooltip; } // Color @@ -263,15 +262,15 @@ class StatusBarEntryItem implements IStatusbarItem { color = (this.themeService.getTheme().getColor(colorId) || Color.transparent).toString(); toDispose.push(this.themeService.onThemeChange(theme => { let colorValue = (this.themeService.getTheme().getColor(colorId) || Color.transparent).toString(); - $(textContainer).color(colorValue); + textContainer.style.color = colorValue; })); } - $(textContainer).color(color); + textContainer.style.color = color; } // Context Menu if (this.entry.extensionId) { - $(textContainer).on('contextmenu', e => { + toDispose.push(addDisposableListener(textContainer, 'contextmenu', e => { EventHelper.stop(e, true); this.contextMenuService.showContextMenu({ @@ -279,7 +278,7 @@ class StatusBarEntryItem implements IStatusbarItem { getActionsContext: () => this.entry.extensionId, getActions: () => TPromise.as([manageExtensionAction]) }); - }, toDispose); + })); } el.appendChild(textContainer); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 753719af952..1bf667f3452 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -377,7 +377,7 @@ export class DropDownMenuActionItem extends ActionItem { public showMenu(): void { const actions = this.getActions(); - let elementPosition = DOM.getDomNodePagePosition(this.builder.getHTMLElement()); + let elementPosition = DOM.getDomNodePagePosition(this.builder); const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts index d20ed821f30..f351fb2a143 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts @@ -354,7 +354,7 @@ export class QuickFixActionItem extends ActionItem { public onClick(event: DOM.EventLike): void { DOM.EventHelper.stop(event, true); - const elementPosition = DOM.getDomNodePagePosition(this.builder.getHTMLElement()); + const elementPosition = DOM.getDomNodePagePosition(this.builder); this.contextMenuService.showContextMenu({ getAnchor: () => ({ x: elementPosition.left + 10, y: elementPosition.top + elementPosition.height }), getActions: () => TPromise.wrap((this.getAction()).getQuickFixActions()), diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index 3c57b2f5fcb..725a95e7de2 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -5,7 +5,6 @@ import { localize } from 'vs/nls'; import URI from 'vs/base/common/uri'; -import { $ } from 'vs/base/browser/builder'; import * as DOM from 'vs/base/browser/dom'; import { TPromise } from 'vs/base/common/winjs.base'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -318,7 +317,7 @@ export class FolderSettingsActionItem extends BaseActionItem { } public render(container: HTMLElement): void { - this.builder = $(container); + this.builder = container; this.container = container; this.labelElement = DOM.$('.action-title'); diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 4a9da7a25f7..d4ffc1ea54e 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -109,7 +109,7 @@ class StatusBarActionItem extends ActionItem { _updateLabel(): void { if (this.options.label) { - this.$e.innerHtml(renderOcticons(this.getAction().label)); + this.$e.innerHTML = renderOcticons(this.getAction().label); } } } diff --git a/src/vs/workbench/test/browser/part.test.ts b/src/vs/workbench/test/browser/part.test.ts index 22c8a969e55..c4a78a473c6 100644 --- a/src/vs/workbench/test/browser/part.test.ts +++ b/src/vs/workbench/test/browser/part.test.ts @@ -6,13 +6,13 @@ 'use strict'; import * as assert from 'assert'; -import { Builder, $ } from 'vs/base/browser/builder'; import { Part } from 'vs/workbench/browser/part'; import * as Types from 'vs/base/common/types'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; +import { append, $, hide } from 'vs/base/browser/dom'; class MyPart extends Part { @@ -42,21 +42,21 @@ class MyPart2 extends Part { } public createTitleArea(parent: HTMLElement): HTMLElement { - return $(parent).div(function (div) { - div.span({ - id: 'myPart.title', - innerHtml: 'Title' - }); - }).getHTMLElement(); + const titleContainer = append(parent, $('div')); + const titleLabel = append(titleContainer, $('span')); + titleLabel.id = 'myPart.title'; + titleLabel.innerHTML = 'Title'; + + return titleContainer; } public createContentArea(parent: HTMLElement): HTMLElement { - return $(parent).div(function (div) { - div.span({ - id: 'myPart.content', - innerHtml: 'Content' - }); - }).getHTMLElement(); + const contentContainer = append(parent, $('div')); + const contentSpan = append(contentContainer, $('span')); + contentSpan.id = 'myPart.content'; + contentSpan.innerHTML = 'Content'; + + return contentContainer; } } @@ -71,12 +71,12 @@ class MyPart3 extends Part { } public createContentArea(parent: HTMLElement): HTMLElement { - return $(parent).div(function (div) { - div.span({ - id: 'myPart.content', - innerHtml: 'Content' - }); - }).getHTMLElement(); + const contentContainer = append(parent, $('div')); + const contentSpan = append(contentContainer, $('span')); + contentSpan.id = 'myPart.content'; + contentSpan.innerHTML = 'Content'; + + return contentContainer; } } @@ -97,11 +97,12 @@ suite('Workbench parts', () => { }); test('Creation', function () { - let b = new Builder(document.getElementById(fixtureId)); - b.div().hide(); + let b = document.createElement('div'); + document.getElementById(fixtureId).appendChild(b); + hide(b); - let part = new MyPart(b.getHTMLElement()); - part.create(b.getHTMLElement()); + let part = new MyPart(b); + part.create(b); assert.strictEqual(part.getId(), 'myPart'); @@ -114,7 +115,7 @@ suite('Workbench parts', () => { part.shutdown(); // Re-Create to assert memento contents - part = new MyPart(b.getHTMLElement()); + part = new MyPart(b); memento = part.getMemento(storage); assert(memento); @@ -126,29 +127,31 @@ suite('Workbench parts', () => { delete memento.bar; part.shutdown(); - part = new MyPart(b.getHTMLElement()); + part = new MyPart(b); memento = part.getMemento(storage); assert(memento); assert.strictEqual(Types.isEmptyObject(memento), true); }); test('Part Layout with Title and Content', function () { - let b = new Builder(document.getElementById(fixtureId)); - b.div().hide(); + let b = document.createElement('div'); + document.getElementById(fixtureId).appendChild(b); + hide(b); let part = new MyPart2(); - part.create(b.getHTMLElement()); + part.create(b); assert(document.getElementById('myPart.title')); assert(document.getElementById('myPart.content')); }); test('Part Layout with Content only', function () { - let b = new Builder(document.getElementById(fixtureId)); - b.div().hide(); + let b = document.createElement('div'); + document.getElementById(fixtureId).appendChild(b); + hide(b); let part = new MyPart3(); - part.create(b.getHTMLElement()); + part.create(b); assert(!document.getElementById('myPart.title')); assert(document.getElementById('myPart.content'));