diff --git a/src/sql/media/icons/common-icons.css b/src/sql/media/icons/common-icons.css index f85aae7cc2..11023dda22 100644 --- a/src/sql/media/icons/common-icons.css +++ b/src/sql/media/icons/common-icons.css @@ -128,13 +128,13 @@ background-position: 2px center; } -.vs .codicon.close, -.vs .codicon.remove { - background: url("close.svg") center center no-repeat !important; +.vs .codicon:not(.masked-icon).close, +.vs .codicon:not(.masked-icon).remove { + background: url('close.svg') center center no-repeat !important; } -.vs-dark .codicon.close, -.hc-black .codicon.close, +.vs-dark .codicon:not(.masked-icon).close, +.hc-black .codicon:not(.masked-icon).close, .vs-dark .codicon.remove, .hc-black .codicon.remove { background: url("close_inverse.svg") center center no-repeat !important; @@ -188,12 +188,12 @@ background: url("ellipsis.svg") center center no-repeat; } -.hc-black .codicon.new, -.vs-dark .codicon.new { +.hc-black .codicon:not(.masked-icon).new, +.vs-dark .codicon:not(.masked-icon).new { background: url("new_inverse.svg") center center no-repeat; } -.vs .codicon.new { +.vs .codicon:not(.masked-icon).new { background: url("new.svg") center center no-repeat; } @@ -446,11 +446,11 @@ Includes non-masked style declarations. */ mask-image: url("chevron_down.svg"); } -.vs .codicon.new-blue { - background-image: url("new-blue.svg"); +.vs .codicon:not(.masked-icon).new-blue { + background-image: url('new-blue.svg'); } -.vs .codicon.start-outline { - background-image: url("start-outline.svg"); +.vs .codicon:not(.masked-icon).start-outline { + background-image: url('start-outline.svg'); } /* Masked element inside pseudo element */ @@ -504,24 +504,41 @@ Includes non-masked style declarations. */ mask-image: url("markdown.svg"); } -.cell-tool-close { - background-image: url("close-blue.svg"); +.codicon.masked-icon.new { + background-image: none; + -webkit-mask-image: url('new.svg'); + mask-image: url('new.svg'); } -.cell-tool-edit { - background-image: url("edit.svg"); +.codicon.masked-icon.close { + background-image: none; + -webkit-mask-image: url('close-blue.svg'); + mask-image: url('close-blue.svg'); } -.cell-tool-move-up { - background-image: url("down-arrow-blue.svg"); +.codicon.masked-icon.edit { + background-image: none; + -webkit-mask-image: url('edit.svg'); + mask-image: url('edit.svg'); +} +.codicon.masked-icon.move-up { transform: scale(-1); + background-image: none; + -webkit-mask-image: url('down-arrow-blue.svg'); + mask-image: url('down-arrow-blue.svg'); } -.cell-tool-move-down { - background-image: url("down-arrow-blue.svg"); +.codicon.masked-icon.move-down { + background-image: none; + -webkit-mask-image: url('down-arrow-blue.svg'); + mask-image: url('down-arrow-blue.svg'); } -.cell-tool-delete { - background-image: url("garbage-can-blue.svg"); +.codicon.masked-icon.delete { + background-image: none; + -webkit-mask-image: url('garbage-can-blue.svg'); + mask-image: url('garbage-can-blue.svg'); } -.cell-tool-more { - background-image: url("ellipsis-blue.svg"); +.codicon.masked-icon.more { + background-image: none; + -webkit-mask-image: url('ellipsis-blue.svg'); + mask-image: url('ellipsis-blue.svg'); } .database-colored.codicon { diff --git a/src/sql/workbench/contrib/notebook/browser/cellToggleMoreActions.ts b/src/sql/workbench/contrib/notebook/browser/cellToolbarActions.ts similarity index 72% rename from src/sql/workbench/contrib/notebook/browser/cellToggleMoreActions.ts rename to src/sql/workbench/contrib/notebook/browser/cellToolbarActions.ts index 33f8c7402b..8cd53a6d70 100644 --- a/src/sql/workbench/contrib/notebook/browser/cellToggleMoreActions.ts +++ b/src/sql/workbench/contrib/notebook/browser/cellToolbarActions.ts @@ -3,33 +3,103 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ElementRef } from '@angular/core'; - import { localize } from 'vs/nls'; -import { ActionBar, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { getErrorMessage } from 'vs/base/common/errors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import * as DOM from 'vs/base/browser/dom'; - -import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { Action, IAction } from 'vs/base/common/actions'; +import { ActionBar, Separator, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { CellActionBase, CellContext } from 'sql/workbench/contrib/notebook/browser/cellViews/codeActions'; -import { CellTypes, CellType } from 'sql/workbench/services/notebook/common/contracts'; -import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel'; -import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; -import { ToggleMoreWidgetAction } from 'sql/workbench/contrib/dashboard/browser/core/actions'; import { CellModel } from 'sql/workbench/services/notebook/browser/models/cell'; -import { Action } from 'vs/base/common/actions'; +import { CellTypes, CellType } from 'sql/workbench/services/notebook/common/contracts'; +import { ToggleableAction } from 'sql/workbench/contrib/notebook/browser/notebookActions'; import { firstIndex } from 'vs/base/common/arrays'; +import { getErrorMessage } from 'vs/base/common/errors'; +import Severity from 'vs/base/common/severity'; +import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -export const HIDDEN_CLASS = 'actionhidden'; + +export class EditCellAction extends ToggleableAction { + // Constants + private static readonly editLabel = localize('editLabel', "Edit"); + private static readonly closeLabel = localize('closeLabel', "Close"); + private static readonly baseClass = 'codicon'; + private static readonly editCssClass = 'edit'; + private static readonly closeCssClass = 'close'; + private static readonly maskedIconClass = 'masked-icon'; + + constructor( + id: string, toggleTooltip: boolean, isEditMode: boolean + ) { + super(id, { + baseClass: EditCellAction.baseClass, + toggleOnLabel: EditCellAction.closeLabel, + toggleOnClass: EditCellAction.closeCssClass, + toggleOffLabel: EditCellAction.editLabel, + toggleOffClass: EditCellAction.editCssClass, + maskedIconClass: EditCellAction.maskedIconClass, + shouldToggleTooltip: toggleTooltip, + isOn: isEditMode + }); + } + + public get editMode(): boolean { + return this.state.isOn; + } + public set editMode(value: boolean) { + this.toggle(value); + } + + public run(context: CellContext): Promise { + let self = this; + return new Promise((resolve, reject) => { + try { + self.editMode = !self.editMode; + context.cell.isEditMode = self.editMode; + resolve(true); + } catch (e) { + reject(e); + } + }); + } +} + +export class DeleteCellAction extends CellActionBase { + constructor( + id: string, + cssClass: string, + label: string, + @INotificationService notificationService: INotificationService + ) { + super(id, label, undefined, notificationService); + this._cssClass = cssClass; + this._tooltip = label; + this._label = ''; + } + + doRun(context: CellContext): Promise { + try { + context.model.deleteCell(context.cell); + } catch (error) { + let message = getErrorMessage(error); + + this.notificationService.notify({ + severity: Severity.Error, + message: message + }); + } + return Promise.resolve(); + } +} export class CellToggleMoreActions { private _actions: (Action | CellActionBase)[] = []; private _moreActions: ActionBar; private _moreActionsElement: HTMLElement; constructor( - @IInstantiationService private instantiationService: IInstantiationService) { + @IInstantiationService private instantiationService: IInstantiationService + ) { this._actions.push( instantiationService.createInstance(RunCellsAction, 'runAllBefore', localize('runAllBefore', "Run Cells Before"), false), instantiationService.createInstance(RunCellsAction, 'runAllAfter', localize('runAllAfter', "Run Cells After"), true), @@ -44,14 +114,11 @@ export class CellToggleMoreActions { instantiationService.createInstance(CollapseCellAction, 'expandCell', localize('expandCell', "Expand Cell"), false), new Separator(), instantiationService.createInstance(ClearCellOutputAction, 'clear', localize('clear', "Clear Result")), - new Separator(), - instantiationService.createInstance(DeleteCellAction, 'delete', localize('delete', "Delete")), ); } - public onInit(elementRef: ElementRef, model: NotebookModel, cellModel: ICellModel) { - let context = new CellContext(model, cellModel); - this._moreActionsElement = elementRef.nativeElement; + public onInit(elementRef: HTMLElement, context: CellContext) { + this._moreActionsElement = elementRef; if (this._moreActionsElement.childNodes.length > 0) { this._moreActionsElement.removeChild(this._moreActionsElement.childNodes[0]); } @@ -59,18 +126,7 @@ export class CellToggleMoreActions { this._moreActions.context = { target: this._moreActionsElement }; let validActions = this._actions.filter(a => a instanceof Separator || a instanceof CellActionBase && a.canRun(context)); this.removeDuplicatedAndStartingSeparators(validActions); - this._moreActions.push(this.instantiationService.createInstance(ToggleMoreWidgetAction, validActions, context), { icon: true, label: false }); - } - - public toggleVisible(visible: boolean): void { - if (!this._moreActionsElement) { - return; - } - if (visible) { - DOM.addClass(this._moreActionsElement, HIDDEN_CLASS); - } else { - DOM.removeClass(this._moreActionsElement, HIDDEN_CLASS); - } + this._moreActions.push(this.instantiationService.createInstance(ToggleMoreActions, validActions, context), { icon: true, label: false }); } private removeDuplicatedAndStartingSeparators(actions: (Action | CellActionBase)[]): void { @@ -93,6 +149,7 @@ export class CellToggleMoreActions { } } + export class AddCellFromContextAction extends CellActionBase { constructor( id: string, label: string, private cellType: CellType, private isAfter: boolean, @@ -121,28 +178,6 @@ export class AddCellFromContextAction extends CellActionBase { } } -export class DeleteCellAction extends CellActionBase { - constructor(id: string, label: string, - @INotificationService notificationService: INotificationService - ) { - super(id, label, undefined, notificationService); - } - - doRun(context: CellContext): Promise { - try { - context.model.deleteCell(context.cell); - } catch (error) { - let message = getErrorMessage(error); - - this.notificationService.notify({ - severity: Severity.Error, - message: message - }); - } - return Promise.resolve(); - } -} - export class ClearCellOutputAction extends CellActionBase { constructor(id: string, label: string, @INotificationService notificationService: INotificationService @@ -249,3 +284,27 @@ export class CollapseCellAction extends CellActionBase { return Promise.resolve(); } } + +export class ToggleMoreActions extends Action { + + private static readonly ID = 'toggleMore'; + private static readonly LABEL = localize('toggleMore', "Toggle More"); + private static readonly ICON = 'masked-icon more'; + + constructor( + private readonly _actions: Array, + private readonly _context: CellContext, + @IContextMenuService private readonly _contextMenuService: IContextMenuService + ) { + super(ToggleMoreActions.ID, ToggleMoreActions.LABEL, ToggleMoreActions.ICON); + } + + run(context: StandardKeyboardEvent): Promise { + this._contextMenuService.showContextMenu({ + getAnchor: () => context.target, + getActions: () => this._actions, + getActionsContext: () => this._context + }); + return Promise.resolve(true); + } +} diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.html b/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.html deleted file mode 100644 index 43bea07c7f..0000000000 --- a/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.html +++ /dev/null @@ -1,15 +0,0 @@ - - diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.ts b/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.ts index 28e07d8aa1..cf6a87fef1 100644 --- a/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.component.ts @@ -2,25 +2,96 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./cellToolbar'; -import { Component } from '@angular/core'; +import 'vs/css!./cellToolbar'; +import * as DOM from 'vs/base/browser/dom'; +import { Component, Inject, ViewChild, ElementRef, Input } from '@angular/core'; import { localize } from 'vs/nls'; +import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { DeleteCellAction, EditCellAction, CellToggleMoreActions } from 'sql/workbench/contrib/notebook/browser/cellToolbarActions'; +import { AddCellAction } from 'sql/workbench/contrib/notebook/browser/notebookActions'; +import { CellTypes } from 'sql/workbench/services/notebook/common/contracts'; +import { DropdownMenuActionViewItem } from 'sql/base/browser/ui/buttonMenu/buttonMenu'; +import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; +import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel'; +import { CellContext } from 'sql/workbench/contrib/notebook/browser/cellViews/codeActions'; export const CELL_TOOLBAR_SELECTOR: string = 'cell-toolbar-component'; @Component({ selector: CELL_TOOLBAR_SELECTOR, - templateUrl: decodeURI(require.toUrl('./cellToolbar.component.html')) + template: `
` }) export class CellToolbarComponent { + @ViewChild('celltoolbar', { read: ElementRef }) private celltoolbar: ElementRef; + public buttonEdit = localize('buttonEdit', "Edit"); public buttonClose = localize('buttonClose', "Close"); public buttonAdd = localize('buttonAdd', "Add new cell"); - public buttonMoveDown = localize('buttonMoveDown', "Move cell down"); - public buttonMoveUp = localize('buttonMoveUp', "Move cell up"); public buttonDelete = localize('buttonDelete', "Delete cell"); - constructor() { + @Input() cellModel: ICellModel; + @Input() model: NotebookModel; + + private _actionBar: Taskbar; + private _editCellAction: EditCellAction; + public _cellToggleMoreActions: CellToggleMoreActions; + + constructor( + @Inject(IInstantiationService) private instantiationService: IInstantiationService, + @Inject(IContextMenuService) private contextMenuService: IContextMenuService + ) { + this._cellToggleMoreActions = this.instantiationService.createInstance(CellToggleMoreActions); + } + + ngOnInit() { + this.initActionBar(); + } + + protected initActionBar(): void { + let context = new CellContext(this.model, this.cellModel); + let taskbar = this.celltoolbar.nativeElement; + this._actionBar = new Taskbar(taskbar); + this._actionBar.context = context; + + let addCodeCellButton = new AddCellAction('notebook.AddCodeCell', localize('codePreview', "Code cell"), 'notebook-button masked-pseudo code'); + addCodeCellButton.cellType = CellTypes.Code; + + let addTextCellButton = new AddCellAction('notebook.AddTextCell', localize('textPreview', "Markdown cell"), 'notebook-button masked-pseudo markdown'); + addTextCellButton.cellType = CellTypes.Markdown; + + let deleteButton = this.instantiationService.createInstance(DeleteCellAction, 'delete', 'codicon masked-icon delete', localize('delete', "Delete")); + + let moreActionsContainer = DOM.$('li.action-item'); + this._cellToggleMoreActions = this.instantiationService.createInstance(CellToggleMoreActions); + this._cellToggleMoreActions.onInit(moreActionsContainer, context); + + this._editCellAction = this.instantiationService.createInstance(EditCellAction, 'notebook.editCell', true, this.cellModel.isEditMode); + this._editCellAction.enabled = true; + + let buttonDropdownContainer = DOM.$('li.action-item'); + buttonDropdownContainer.setAttribute('role', 'presentation'); + let dropdownMenuActionViewItem = new DropdownMenuActionViewItem( + addCodeCellButton, + [addCodeCellButton, addTextCellButton], + this.contextMenuService, + undefined, + this._actionBar.actionRunner, + undefined, + 'codicon masked-icon new', + localize('addCell', "Cell"), + undefined + ); + dropdownMenuActionViewItem.render(buttonDropdownContainer); + dropdownMenuActionViewItem.setActionContext(context); + + this._actionBar.setContent([ + { action: this._editCellAction }, + { element: buttonDropdownContainer }, + { action: deleteButton }, + { element: moreActionsContainer } + ]); } } diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.css b/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.css index 68bf5e2cf9..4ad0184b5f 100644 --- a/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.css +++ b/src/sql/workbench/contrib/notebook/browser/cellViews/cellToolbar.css @@ -4,44 +4,41 @@ *--------------------------------------------------------------------------------------------*/ cell-toolbar-component { + border-width: 1px; + border-style: solid; position: absolute; - left: 25px; - top: -21px; + left: 20px; + top: -20px; } -cell-toolbar-component ul { - display: inline-block; +cell-toolbar-component .carbon-taskbar .monaco-action-bar.animated { + padding: 0; +} + +cell-toolbar-component .carbon-taskbar.monaco-toolbar .monaco-action-bar.animated ul.actions-container { + display: flex; list-style: none; margin: 0; - padding: 5px 10px 0 10px; + padding: 0; } -cell-toolbar-component li { - display: inline-block; - margin-right: 4px; +cell-toolbar-component ul.actions-container li.action-item { + display: inline-flex; + margin-right: 0; text-align: center; } -cell-toolbar-component li:last-child { +cell-toolbar-component ul.actions-container li:last-child { margin-right: 0; } - -cell-toolbar-component li a { - background: 50% 50% no-repeat; - display: block; - height: 16px; - width: 16px; +cell-toolbar-component .carbon-taskbar .action-label { + padding: 0; } - -cell-toolbar-component li div { - background: 50% 50% no-repeat; - height: 16px; - width: 16px; +cell-toolbar-component .monaco-action-bar .action-label { + margin-right: 0; } - -cell-toolbar-component .offscreen { - height: 1px; - text-indent: -999999px; - margin-top: -1px; - position: absolute; +cell-toolbar-component ul.actions-container li a.masked-icon { + display: flex; + height: 24px; + width: 29px; } diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/code.component.html b/src/sql/workbench/contrib/notebook/browser/cellViews/code.component.html index f0dc24c339..8a3d54c5e8 100644 --- a/src/sql/workbench/contrib/notebook/browser/cellViews/code.component.html +++ b/src/sql/workbench/contrib/notebook/browser/cellViews/code.component.html @@ -11,7 +11,6 @@
-
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/code.component.ts b/src/sql/workbench/contrib/notebook/browser/cellViews/code.component.ts index 503fb5e2ad..772167e072 100644 --- a/src/sql/workbench/contrib/notebook/browser/cellViews/code.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/cellViews/code.component.ts @@ -7,7 +7,6 @@ import 'vs/css!./code'; import { OnInit, Component, Input, Inject, ElementRef, ViewChild, Output, EventEmitter, OnChanges, SimpleChange, forwardRef, ChangeDetectorRef } from '@angular/core'; import { QueryTextEditor } from 'sql/workbench/browser/modelComponents/queryTextEditor'; -import { CellToggleMoreActions } from 'sql/workbench/contrib/notebook/browser/cellToggleMoreActions'; import { ICellModel, CellExecutionState } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; import { RunCellAction, CellContext } from 'sql/workbench/contrib/notebook/browser/cellViews/codeActions'; @@ -26,7 +25,6 @@ import { CellTypes } from 'sql/workbench/services/notebook/common/contracts'; import { OVERRIDE_EDITOR_THEMING_SETTING } from 'sql/workbench/services/notebook/browser/notebookService'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { ILogService } from 'vs/platform/log/common/log'; -import { CollapseComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/collapse.component'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; @@ -48,9 +46,7 @@ const DEFAULT_OR_LOCAL_CONTEXT_ID = '-1'; }) export class CodeComponent extends CellView implements OnInit, OnChanges { @ViewChild('toolbar', { read: ElementRef }) private toolbarElement: ElementRef; - @ViewChild('moreactions', { read: ElementRef }) private moreActionsElementRef: ElementRef; @ViewChild('editor', { read: ElementRef }) private codeElement: ElementRef; - @ViewChild(CollapseComponent) private collapseComponent: CollapseComponent; public get cellModel(): ICellModel { return this._cellModel; @@ -83,14 +79,6 @@ export class CodeComponent extends CellView implements OnInit, OnChanges { this._activeCellId = value; } - @Input() set hover(value: boolean) { - this.cellModel.hover = value; - if (!this.isActive()) { - // Only make a change if we're not active, since this has priority - this.toggleActionsVisibility(this.cellModel.hover); - } - } - protected _actionBar: Taskbar; private readonly _minimumHeight = 30; private readonly _maximumHeight = 4000; @@ -100,7 +88,6 @@ export class CodeComponent extends CellView implements OnInit, OnChanges { private _editorModel: ITextModel; private _model: NotebookModel; private _activeCellId: string; - private _cellToggleMoreActions: CellToggleMoreActions; private _layoutEmitter = new Emitter(); constructor( @@ -113,7 +100,6 @@ export class CodeComponent extends CellView implements OnInit, OnChanges { @Inject(ILogService) private readonly logService: ILogService ) { super(); - this._cellToggleMoreActions = this._instantiationService.createInstance(CellToggleMoreActions); this._register(Event.debounce(this._layoutEmitter.event, (l, e) => e, 250, /*leading=*/false) (() => this.layout())); // Handle disconnect on removal of the cell, if it was the active cell @@ -134,7 +120,6 @@ export class CodeComponent extends CellView implements OnInit, OnChanges { let changedProp = changes[propName]; let isActive = this.cellModel.id === changedProp.currentValue; this.updateConnectionState(isActive); - this.toggleActionsVisibility(isActive); if (this._editor) { this._editor.toggleEditorSelected(isActive); } @@ -288,7 +273,6 @@ export class CodeComponent extends CellView implements OnInit, OnChanges { this._actionBar.setContent([ { action: runCellAction } ]); - this._cellToggleMoreActions.onInit(this.moreActionsElementRef, this.model, this.cellModel); } /// Editor Functions @@ -334,9 +318,6 @@ export class CodeComponent extends CellView implements OnInit, OnChanges { private updateTheme(theme: IColorTheme): void { let toolbarEl = this.toolbarElement.nativeElement; toolbarEl.style.borderRightColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString(); - - let moreActionsEl = this.moreActionsElementRef.nativeElement; - moreActionsEl.style.borderRightColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString(); } private setFocusAndScroll(): void { @@ -354,14 +335,6 @@ export class CodeComponent extends CellView implements OnInit, OnChanges { return this.cellModel && this.cellModel.id === this.activeCellId; } - protected toggleActionsVisibility(isActiveOrHovered: boolean) { - this._cellToggleMoreActions.toggleVisible(!isActiveOrHovered); - - if (this.collapseComponent) { - this.collapseComponent.toggleIconVisibility(isActiveOrHovered); - } - } - private onCellCollapse(isCollapsed: boolean): void { let editorWidget = this._editor.getControl() as ICodeEditor; if (isCollapsed) { diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.css b/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.css index 3a37406edd..5171bedb46 100644 --- a/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.css +++ b/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.css @@ -18,7 +18,7 @@ display: block; list-style: none; margin: 0; - padding: 4px 16px; + padding: 10px 16px 4px 16px; } .markdown-toolbar .carbon-taskbar li.action-item { display: inline-block; diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.html b/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.html index 904fbf9c4c..edc7a11c30 100644 --- a/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.html +++ b/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.html @@ -11,9 +11,7 @@
-
-
-
+
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.ts b/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.ts index d1d1b9d817..b2b4f88731 100644 --- a/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/cellViews/textCell.component.ts @@ -23,7 +23,6 @@ import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/inter import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel'; import { ISanitizer, defaultSanitizer } from 'sql/workbench/services/notebook/browser/outputs/sanitizer'; -import { CellToggleMoreActions } from 'sql/workbench/contrib/notebook/browser/cellToggleMoreActions'; import { CodeComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/code.component'; import { NotebookRange, ICellEditorProvider } from 'sql/workbench/services/notebook/browser/notebookService'; import { IColorTheme } from 'vs/platform/theme/common/themeService'; @@ -38,7 +37,6 @@ const USER_SELECT_CLASS = 'actionselect'; }) export class TextCellComponent extends CellView implements OnInit, OnChanges { @ViewChild('preview', { read: ElementRef }) private output: ElementRef; - @ViewChild('moreactions', { read: ElementRef }) private moreActionsElementRef: ElementRef; @ViewChildren(CodeComponent) private markdowncodeCell: QueryList; @Input() cellModel: ICellModel; @@ -51,14 +49,6 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges { this._activeCellId = value; } - @Input() set hover(value: boolean) { - this._hover = value; - if (!this.isActive()) { - // Only make a change if we're not active, since this has priority - this.updateMoreActions(); - } - } - @HostListener('document:keydown.escape', ['$event']) handleKeyboardEvent() { if (this.isEditMode) { @@ -84,8 +74,6 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges { private _activeCellId: string; private readonly _onDidClickLink = this._register(new Emitter()); public readonly onDidClickLink = this._onDidClickLink.event; - private _cellToggleMoreActions: CellToggleMoreActions; - private _hover: boolean; private markdownRenderer: NotebookMarkdownRenderer; private markdownResult: IMarkdownRenderResult; public previewFeaturesEnabled: boolean = false; @@ -98,7 +86,6 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges { ) { super(); this.isEditMode = true; - this._cellToggleMoreActions = this._instantiationService.createInstance(CellToggleMoreActions); this.markdownRenderer = this._instantiationService.createInstance(NotebookMarkdownRenderer); this._register(toDisposable(() => { if (this.markdownResult) { @@ -143,11 +130,15 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges { this.previewFeaturesEnabled = this._configurationService.getValue('workbench.enablePreviewFeatures'); this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this)); this.updateTheme(this.themeService.getColorTheme()); - this._cellToggleMoreActions.onInit(this.moreActionsElementRef, this.model, this.cellModel); this.setFocusAndScroll(); this._register(this.cellModel.onOutputsChanged(e => { this.updatePreview(); })); + this._register(this.cellModel.onCellModeChanged(mode => { + if (mode !== this.isEditMode) { + this.toggleEditMode(mode); + } + })); } ngOnChanges(changes: { [propKey: string]: SimpleChange }) { @@ -191,7 +182,7 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges { if (trustedChanged || contentChanged) { this._lastTrustedMode = this.cellModel.trustedMode; if ((!cellModelSourceJoined) && !this.isEditMode) { - this._content = localize('doubleClickEdit', "Double-click to edit"); + this._content = localize('addContent', "Add content here..."); } else { this._content = this.cellModel.source; } @@ -224,9 +215,6 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges { private updateTheme(theme: IColorTheme): void { let outputElement = this.output.nativeElement; outputElement.style.borderTopColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString(); - - let moreActionsEl = this.moreActionsElementRef.nativeElement; - moreActionsEl.style.borderRightColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString(); } public handleContentChanged(): void { @@ -236,20 +224,10 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges { public toggleEditMode(editMode?: boolean): void { this.isEditMode = editMode !== undefined ? editMode : !this.isEditMode; this.cellModel.isEditMode = this.isEditMode; - this.updateMoreActions(); this.updatePreview(); this._changeRef.detectChanges(); } - private updateMoreActions(): void { - if (!this.isEditMode && (this.isActive() || this._hover)) { - this.toggleMoreActionsButton(true); - } - else { - this.toggleMoreActionsButton(false); - } - } - private toggleUserSelect(userSelect: boolean): void { if (!this.output) { return; @@ -273,10 +251,6 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges { return this.cellModel && this.cellModel.id === this.activeCellId; } - protected toggleMoreActionsButton(isActiveOrHovered: boolean) { - this._cellToggleMoreActions.toggleVisible(!isActiveOrHovered); - } - public deltaDecorations(newDecorationRange: NotebookRange, oldDecorationRange: NotebookRange): void { if (oldDecorationRange) { this.removeDecoration(oldDecorationRange); diff --git a/src/sql/workbench/contrib/notebook/browser/notebook.component.html b/src/sql/workbench/contrib/notebook/browser/notebook.component.html index bd3626aa07..81784109fe 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebook.component.html +++ b/src/sql/workbench/contrib/notebook/browser/notebook.component.html @@ -9,35 +9,14 @@
-
- - - -
+
-
- - - -
diff --git a/src/sql/workbench/contrib/notebook/browser/notebook.css b/src/sql/workbench/contrib/notebook/browser/notebook.css index e55d22747d..3d3c767dc2 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebook.css +++ b/src/sql/workbench/contrib/notebook/browser/notebook.css @@ -2,8 +2,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + .notebookEditor .scrollable { - margin-top: 5px; + padding-top: 6px; } .notebookEditor .taskbarSeparator { @@ -19,7 +20,7 @@ border-color: transparent; border-style: solid; border-width: 1px 1px 1px 4px; - margin: 1px 20px; + margin: 16px; position: relative; } @@ -236,54 +237,6 @@ margin-right: 10px; } -.notebookEditor .hoverButtonsContainer { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - width: 100%; - padding: 0px 20px; - margin: 1px 0px; - box-sizing: border-box; -} - -.notebookEditor .hoverButtonsContainer .containerBackground { - position: absolute; - width: auto; - left: 20px; - right: 20px; - height: 1px; - z-index: 0; - visibility: hidden; -} - -.notebookEditor .hoverButtonsContainer:hover .containerBackground { - visibility: visible; -} - -.notebookEditor .hoverButtonsContainer .hoverButton { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - margin: 3px 2px; - padding: 4px 12px; - font-size: 12px; - box-sizing: border-box; - border-width: 1px; - border-style: solid; - z-index: 1; - visibility: hidden; -} - -.notebookEditor .hoverButtonsContainer:hover .hoverButton { - visibility: visible; -} - -.notebookEditor .hoverButton:active { - transform: scale(1.05); -} - .notebookEditor .hoverButton .addCodeIcon, .notebookEditor .hoverButton .addTextIcon { display: inline-block; @@ -295,24 +248,6 @@ margin-right: 4px; } -.notebookEditor .hoverButton .addCodeIcon { - background-image: url("./media/light/add_code.svg"); -} - -.vs-dark .notebookEditor .hoverButton .addCodeIcon, -.hc-black .notebookEditor .hoverButton .addCodeIcon { - background-image: url("./media/dark/add_code_inverse.svg"); -} - -.notebookEditor .hoverButton .addTextIcon { - background-image: url("./media/light/add_text.svg"); -} - -.vs-dark .notebookEditor .hoverButton .addTextIcon, -.hc-black .notebookEditor .hoverButton .addTextIcon { - background-image: url("./media/dark/add_text_inverse.svg"); -} - .monaco-workbench.mac .notebookEditor .select-container { padding-top: 2px; } diff --git a/src/sql/workbench/contrib/notebook/browser/notebookActions.ts b/src/sql/workbench/contrib/notebook/browser/notebookActions.ts index 6c2e12fa6a..13649a4115 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookActions.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookActions.ts @@ -28,6 +28,7 @@ import { find, firstIndex } from 'vs/base/common/arrays'; import { INotebookEditor } from 'sql/workbench/services/notebook/browser/notebookService'; import { NotebookComponent } from 'sql/workbench/contrib/notebook/browser/notebook.component'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { CellContext } from 'sql/workbench/contrib/notebook/browser/cellViews/codeActions'; const msgLoading = localize('loading', "Loading kernels..."); const msgChanging = localize('changing', "Changing kernel..."); @@ -49,18 +50,29 @@ export class AddCellAction extends Action { ) { super(id, label, cssClass); } - public async run(context: INotebookEditor): Promise { - //Add Cell after current selected cell. + public async run(context: INotebookEditor | CellContext): Promise { let index = 0; - if (context && context.cells) { - let notebookcomponent = context as NotebookComponent; - let id = notebookcomponent.activeCellId; - if (id) { - index = context.cells.findIndex(cell => cell.id === id); - index = index + 1; + if (context instanceof CellContext) { + if (context?.model?.cells) { + let activeCellId = context.model.activeCell.id; + if (activeCellId) { + index = context.model.cells.findIndex(cell => cell.id === activeCellId) + 1; + } } + if (context?.model) { + context.model.addCell(this.cellType, index); + } + } else { + //Add Cell after current selected cell. + if (context?.cells) { + let notebookcomponent = context as NotebookComponent; + let id = notebookcomponent.activeCellId; + if (id) { + index = context.cells.findIndex(cell => cell.id === id) + 1; + } + } + context.addCell(this.cellType, index); } - context.addCell(this.cellType, index); } } diff --git a/src/sql/workbench/contrib/notebook/browser/notebookStyles.ts b/src/sql/workbench/contrib/notebook/browser/notebookStyles.ts index 38f32f6bf9..25a86a4064 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookStyles.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookStyles.ts @@ -6,9 +6,9 @@ import 'vs/css!./notebook'; import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { SIDE_BAR_BACKGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND } from 'vs/workbench/common/theme'; -import { activeContrastBorder, contrastBorder, buttonBackground, textLinkForeground, textLinkActiveForeground, textPreformatForeground, textBlockQuoteBackground, textBlockQuoteBorder, buttonForeground, editorBackground, lighten } from 'vs/platform/theme/common/colorRegistry'; +import { activeContrastBorder, contrastBorder, buttonBackground, textLinkForeground, textLinkActiveForeground, textPreformatForeground, textBlockQuoteBackground, textBlockQuoteBorder, buttonForeground } from 'vs/platform/theme/common/colorRegistry'; import { editorLineHighlight, editorLineHighlightBorder } from 'vs/editor/common/view/editorColorRegistry'; -import { cellBorder, notebookToolbarIcon, notebookToolbarLines, buttonMenuArrow, dropdownArrow, markdownEditorBackground, splitBorder, codeEditorBackground, codeEditorBackgroundActive, codeEditorLineNumber, codeEditorToolbarIcon, codeEditorToolbarBackground, codeEditorToolbarBorder, toolbarBackground, toolbarIcon, toolbarBottomBorder } from 'sql/platform/theme/common/colorRegistry'; +import { cellBorder, notebookToolbarIcon, notebookToolbarLines, buttonMenuArrow, dropdownArrow, markdownEditorBackground, splitBorder, codeEditorBackground, codeEditorBackgroundActive, codeEditorLineNumber, codeEditorToolbarIcon, codeEditorToolbarBackground, codeEditorToolbarBorder, toolbarBackground, toolbarIcon, toolbarBottomBorder, notebookToolbarSelectBackground } from 'sql/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { BareResultsGridInfo, getBareResultsGridInfoStyles } from 'sql/workbench/contrib/query/browser/queryResultsEditor'; @@ -34,49 +34,6 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean, conf `); } - if (buttonBackgroundColor) { - let lighterBackgroundColor = lighten(buttonBackgroundColor, 0.825)(theme); - collector.addRule(` - .notebookEditor .hoverButton { - border-color: ${buttonBackgroundColor}; - } - .notebookEditor .hoverButton:active, - .notebookEditor .hoverButton:hover { - background-color: ${buttonBackgroundColor}; - } - .notebookEditor .hoverButton { - color: ${buttonBackgroundColor}; - } - .vs-dark .notebookEditor .hoverButton { - border-color: ${lighterBackgroundColor}; - } - .vs-dark .notebookEditor .hoverButton:active, - .vs-dark .notebookEditor .hoverButton:hover { - background-color: ${lighterBackgroundColor}; - } - .vs-dark .notebookEditor .hoverButton { - color: ${lighterBackgroundColor}; - } - `); - } - - const backgroundColor = theme.getColor(editorBackground); - if (backgroundColor) { - collector.addRule(` - .notebookEditor .hoverButton { - background-color: ${backgroundColor}; - } - .notebookEditor .hoverButton:active, - .notebookEditor .hoverButton:hover { - color: ${backgroundColor}; - } - .hc-black .notebookEditor .hoverButton:active, - .hc-black .notebookEditor .hoverButton:hover { - color: ${backgroundColor}; - } - `); - } - const inactiveBorder = theme.getColor(SIDE_BAR_BACKGROUND); const notebookLineHighlight = theme.getColor(EDITOR_GROUP_HEADER_TABS_BACKGROUND); // Code editor style overrides - only applied if user chooses this as preferred option @@ -100,13 +57,6 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean, conf // Inactive border if (inactiveBorder) { - // Standard notebook cell behavior - collector.addRule(` - .notebookEditor .hoverButtonsContainer .containerBackground { - background-color: ${inactiveBorder}; - } - `); - // Ensure there's always a line between editor and output collector.addRule(` .notebookEditor .notebook-cell.active code-component { @@ -129,16 +79,6 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean, conf outline-width: 1px; outline-style: solid; } - .hc-black .notebookEditor .hoverButton { - color: ${hcOutline}; - } - .hc-black .notebookEditor .hoverButton:not(:active) { - border-color: ${hcOutline}; - } - .hc-black .notebookEditor .hoverButton:active, - .hc-black .notebookEditor .hoverButton:hover { - background-color: ${hcOutline}; - } `); } @@ -227,10 +167,17 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean, conf collector.addRule(`.notebookEditor .notebook-button.masked-pseudo-after:after { background-color: ${buttonMenuArrowColor};}`); } - // Cell border + // Active cell border, cell toolbar border, cell toolbar icons const cellBorderColor = theme.getColor(cellBorder); if (cellBorderColor) { collector.addRule(`.notebookEditor .notebook-cell.active { border-color: ${cellBorderColor};}`); + collector.addRule(`.notebookEditor .notebook-cell.active cell-toolbar-component { border-color: ${cellBorderColor};}`); + collector.addRule(`.notebookEditor .notebook-cell.active cell-toolbar-component .codicon { background-color: ${cellBorderColor};}`); + } + // Cell toolbar background + const notebookToolbarSelectBackgroundColor = theme.getColor(notebookToolbarSelectBackground); + if (notebookToolbarSelectBackgroundColor) { + collector.addRule(`.notebookEditor .notebook-cell.active cell-toolbar-component { background-color: ${notebookToolbarSelectBackgroundColor};}`); } // Markdown editor toolbar diff --git a/src/sql/workbench/services/notebook/browser/models/notebookModel.ts b/src/sql/workbench/services/notebook/browser/models/notebookModel.ts index 20ec5a7ff1..fb7ce0df83 100644 --- a/src/sql/workbench/services/notebook/browser/models/notebookModel.ts +++ b/src/sql/workbench/services/notebook/browser/models/notebookModel.ts @@ -378,7 +378,7 @@ export class NotebookModel extends Disposable implements INotebookModel { } // Set newly created cell as active cell this.updateActiveCell(cell); - + cell.isEditMode = true; this._contentChangedEmitter.fire({ changeType: NotebookChangeType.CellsModified, cells: [cell],