From ded073edd9dc0e7b2f787691ec5361fd7aacb4fc Mon Sep 17 00:00:00 2001 From: Yurong He <43652751+YurongHe@users.noreply.github.com> Date: Mon, 10 Dec 2018 11:17:35 -0800 Subject: [PATCH] Added clear output to ToggleMoreAction and added it to markdown preview (#3535) * Added toggleMoreActions to Markdown Preview only. When it is in editor mode, only editor display ToggleMoreActions. * Added clear output back --- .../parts/notebook/cellToggleMoreActions.ts | 127 ++++++++++++++++++ .../notebook/cellViews/code.component.html | 2 +- .../notebook/cellViews/code.component.ts | 46 ++----- src/sql/parts/notebook/cellViews/code.css | 4 - .../parts/notebook/cellViews/codeActions.ts | 52 +------ .../cellViews/textCell.component.html | 6 +- .../notebook/cellViews/textCell.component.ts | 30 +++-- src/sql/parts/notebook/notebook.css | 5 + src/sql/parts/notebook/notebookActions.ts | 1 - 9 files changed, 171 insertions(+), 102 deletions(-) create mode 100644 src/sql/parts/notebook/cellToggleMoreActions.ts diff --git a/src/sql/parts/notebook/cellToggleMoreActions.ts b/src/sql/parts/notebook/cellToggleMoreActions.ts new file mode 100644 index 0000000000..c7c293af2a --- /dev/null +++ b/src/sql/parts/notebook/cellToggleMoreActions.ts @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { ElementRef } from '@angular/core'; + +import { nb } from 'sqlops'; + +import { localize } from 'vs/nls'; +import { Action } from 'vs/base/common/actions'; +import { ActionBar, ActionsOrientation } 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 { ICellModel } from 'sql/parts/notebook/models/modelInterfaces'; +import { CellContext, CellActionBase } from 'sql/parts/notebook/cellViews/codeActions'; +import { NotebookModel } from 'sql/parts/notebook/models/notebookModel'; +import { ToggleMoreWidgetAction } from 'sql/parts/dashboard/common/actions'; +import { CellTypes, CellType } from 'sql/parts/notebook/models/contracts'; +import { CellModel } from 'sql/parts/notebook/models/cell'; + +export class CellToggleMoreActions { + private _actions: Action[] = []; + private _moreActions: ActionBar; + constructor( + @IInstantiationService private instantiationService: IInstantiationService) { + this._actions.push( + instantiationService.createInstance(DeleteCellAction, 'delete', localize('delete', 'Delete')), + instantiationService.createInstance(AddCellFromContextAction,'codeBefore', localize('codeBefore', 'Insert Code before'), CellTypes.Code, false), + instantiationService.createInstance(AddCellFromContextAction, 'codeAfter', localize('codeAfter', 'Insert Code after'), CellTypes.Code, true), + instantiationService.createInstance(AddCellFromContextAction, 'markdownBefore', localize('markdownBefore', 'Insert Markdown before'), CellTypes.Markdown, false), + instantiationService.createInstance(AddCellFromContextAction, 'markdownAfter', localize('markdownAfter', 'Insert Markdown after'), CellTypes.Markdown, true), + instantiationService.createInstance(ClearCellOutputAction, 'clear', localize('clear', 'Clear output')) + ); + } + + public toggle(showIcon: boolean, elementRef: ElementRef, model: NotebookModel, cellModel: ICellModel) { + let context = new CellContext(model,cellModel); + let moreActionsElement = elementRef.nativeElement; + if (showIcon) { + if (moreActionsElement.childNodes.length > 0) { + moreActionsElement.removeChild(moreActionsElement.childNodes[0]); + } + this._moreActions = new ActionBar(moreActionsElement, { orientation: ActionsOrientation.VERTICAL }); + this._moreActions.context = { target: moreActionsElement }; + this._moreActions.push(this.instantiationService.createInstance(ToggleMoreWidgetAction, this._actions, context), { icon: showIcon, label: false }); + } + else if (moreActionsElement.childNodes.length > 0) { + moreActionsElement.removeChild(moreActionsElement.childNodes[0]); + } + } +} + +export class AddCellFromContextAction extends CellActionBase { + constructor( + id: string, label: string, private cellType: CellType, private isAfter: boolean, + @INotificationService notificationService: INotificationService + ) { + super(id, label, undefined, notificationService); + } + + runCellAction(context: CellContext): Promise { + try { + let model = context.model; + let index = model.cells.findIndex((cell) => cell.id === context.cell.id); + if (index !== undefined && this.isAfter) { + index += 1; + } + model.addCell(this.cellType, index); + } catch (error) { + let message = getErrorMessage(error); + + this.notificationService.notify({ + severity: Severity.Error, + message: message + }); + } + return Promise.resolve(); + } +} + +export class DeleteCellAction extends CellActionBase { + constructor(id: string, label: string, + @INotificationService notificationService: INotificationService + ) { + super(id, label, undefined, notificationService); + } + + runCellAction(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 + ) { + super(id, label, undefined, notificationService); + } + + runCellAction(context: CellContext): Promise { + try { + (context.model.activeCell as CellModel).clearOutputs(); + } catch (error) { + let message = getErrorMessage(error); + + this.notificationService.notify({ + severity: Severity.Error, + message: message + }); + } + return Promise.resolve(); + } + +} diff --git a/src/sql/parts/notebook/cellViews/code.component.html b/src/sql/parts/notebook/cellViews/code.component.html index baa174ed5e..1bfff42f17 100644 --- a/src/sql/parts/notebook/cellViews/code.component.html +++ b/src/sql/parts/notebook/cellViews/code.component.html @@ -9,6 +9,6 @@
-
+
diff --git a/src/sql/parts/notebook/cellViews/code.component.ts b/src/sql/parts/notebook/cellViews/code.component.ts index b9329038ba..33f9d0937f 100644 --- a/src/sql/parts/notebook/cellViews/code.component.ts +++ b/src/sql/parts/notebook/cellViews/code.component.ts @@ -4,11 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./code'; -import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, Output, EventEmitter, OnChanges, SimpleChange } from '@angular/core'; +import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Output, EventEmitter, OnChanges, SimpleChange } from '@angular/core'; import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service'; import { AngularDisposable } from 'sql/base/common/lifecycle'; import { QueryTextEditor } from 'sql/parts/modelComponents/queryTextEditor'; +import { CellToggleMoreActions } from 'sql/parts/notebook/cellToggleMoreActions'; +import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces'; +import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; +import { RunCellAction, CellContext } from 'sql/parts/notebook/cellViews/codeActions'; +import { NotebookModel } from 'sql/parts/notebook/models/notebookModel'; import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import * as themeColors from 'vs/workbench/common/theme'; @@ -19,20 +24,11 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ITextModel } from 'vs/editor/common/model'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import URI from 'vs/base/common/uri'; -import { localize } from 'vs/nls'; -import { Action } from 'vs/base/common/actions'; -import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { Schemas } from 'vs/base/common/network'; import * as DOM from 'vs/base/browser/dom'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces'; -import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; -import { RunCellAction, DeleteCellAction, AddCellAction, CellContext } from 'sql/parts/notebook/cellViews/codeActions'; -import { NotebookModel } from 'sql/parts/notebook/models/notebookModel'; -import { ToggleMoreWidgetAction } from 'sql/parts/dashboard/common/actions'; -import { CellTypes } from 'sql/parts/notebook/models/contracts'; import { INotificationService } from 'vs/platform/notification/common/notification'; export const CODE_SELECTOR: string = 'code-component'; @@ -59,15 +55,14 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange } protected _actionBar: Taskbar; - protected _moreActions: ActionBar; private readonly _minimumHeight = 30; private _editor: QueryTextEditor; private _editorInput: UntitledEditorInput; private _editorModel: ITextModel; private _uri: string; private _model: NotebookModel; - private _actions: Action[] = []; private _activeCellId: string; + private _cellToggleMoreActions: CellToggleMoreActions; constructor( @Inject(forwardRef(() => CommonServiceInterface)) private _bootstrapService: CommonServiceInterface, @@ -81,13 +76,7 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange @Inject(INotificationService) private notificationService: INotificationService, ) { super(); - this._actions.push( - this._instantiationService.createInstance(AddCellAction, 'codeBefore', localize('codeBefore', 'Insert Code before'), CellTypes.Code, false), - this._instantiationService.createInstance(AddCellAction, 'codeAfter', localize('codeAfter', 'Insert Code after'), CellTypes.Code, true), - this._instantiationService.createInstance(AddCellAction, 'markdownBefore', localize('markdownBefore', 'Insert Markdown before'), CellTypes.Markdown, false), - this._instantiationService.createInstance(AddCellAction, 'markdownAfter', localize('markdownAfter', 'Insert Markdown after'), CellTypes.Markdown, true), - this._instantiationService.createInstance(DeleteCellAction, 'delete', localize('delete', 'Delete')) - ); + this._cellToggleMoreActions = this._instantiationService.createInstance(CellToggleMoreActions); } ngOnInit() { @@ -105,10 +94,10 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange if (propName === 'activeCellId') { let changedProp = changes[propName]; if (this.cellModel.id === changedProp.currentValue) { - this.toggleMoreActions(true); + this._cellToggleMoreActions.toggle(true, this.moreActionsElementRef, this.model, this.cellModel); } else { - this.toggleMoreActions(false); + this._cellToggleMoreActions.toggle(false, this.moreActionsElementRef, this.model, this.cellModel); } break; } @@ -173,21 +162,6 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange ]); } - private toggleMoreActions(showIcon: boolean) { - let context = new CellContext(this.model, this.cellModel); - let moreActionsElement = this.moreActionsElementRef.nativeElement; - if (showIcon) { - if (moreActionsElement.childNodes.length > 0) { - moreActionsElement.removeChild(moreActionsElement.childNodes[0]); - } - this._moreActions = new ActionBar(moreActionsElement, { orientation: ActionsOrientation.VERTICAL }); - this._moreActions.context = { target: moreActionsElement }; - this._moreActions.push(this._instantiationService.createInstance(ToggleMoreWidgetAction, this._actions, context), { icon: showIcon, label: false }); - } - else if (moreActionsElement.childNodes.length > 0) { - moreActionsElement.removeChild(moreActionsElement.childNodes[0]); - } - } private createUri(): URI { let uri = URI.from({ scheme: Schemas.untitled, path: `notebook-editor-${this.cellModel.id}` }); diff --git a/src/sql/parts/notebook/cellViews/code.css b/src/sql/parts/notebook/cellViews/code.css index 0666d3176e..1fa4302fc1 100644 --- a/src/sql/parts/notebook/cellViews/code.css +++ b/src/sql/parts/notebook/cellViews/code.css @@ -45,10 +45,6 @@ code-component .carbon-taskbar .icon { width: 40px; } -code-component .action-label.icon.toggle-more { - height: 20px; - width: 20px; -} code-component .carbon-taskbar.monaco-toolbar .monaco-action-bar.animated .actions-container { diff --git a/src/sql/parts/notebook/cellViews/codeActions.ts b/src/sql/parts/notebook/cellViews/codeActions.ts index c82a6ded5c..52f319b98a 100644 --- a/src/sql/parts/notebook/cellViews/codeActions.ts +++ b/src/sql/parts/notebook/cellViews/codeActions.ts @@ -40,7 +40,7 @@ export class CellContext { } } -abstract class CellActionBase extends Action { +export abstract class CellActionBase extends Action { constructor(id: string, label: string, icon: string, protected notificationService: INotificationService) { super(id, label, icon); @@ -135,53 +135,3 @@ export class RunCellAction extends ToggleableAction { return clientSession.kernel; } } - -export class AddCellAction extends CellActionBase { - constructor( - id: string, label: string, private cellType: CellType, private isAfter: boolean, - @INotificationService notificationService: INotificationService - ) { - super(id, label, undefined, notificationService); - } - - runCellAction(context: CellContext): Promise { - try { - let model = context.model; - let index = model.cells.findIndex((cell) => cell.id === context.cell.id); - if (index !== undefined && this.isAfter) { - index += 1; - } - model.addCell(this.cellType, index); - } catch (error) { - let message = getErrorMessage(error); - - this.notificationService.notify({ - severity: Severity.Error, - message: message - }); - } - return Promise.resolve(); - } -} - -export class DeleteCellAction extends CellActionBase { - constructor(id: string, label: string, - @INotificationService notificationService: INotificationService - ) { - super(id, label, undefined, notificationService); - } - - runCellAction(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(); - } -} \ No newline at end of file diff --git a/src/sql/parts/notebook/cellViews/textCell.component.html b/src/sql/parts/notebook/cellViews/textCell.component.html index bbe3e67206..f19ee0c609 100644 --- a/src/sql/parts/notebook/cellViews/textCell.component.html +++ b/src/sql/parts/notebook/cellViews/textCell.component.html @@ -10,6 +10,10 @@ -
+
+
+
+
+
diff --git a/src/sql/parts/notebook/cellViews/textCell.component.ts b/src/sql/parts/notebook/cellViews/textCell.component.ts index 79caeda2c1..18719a25f4 100644 --- a/src/sql/parts/notebook/cellViews/textCell.component.ts +++ b/src/sql/parts/notebook/cellViews/textCell.component.ts @@ -6,20 +6,22 @@ import 'vs/css!./textCell'; import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, OnChanges, SimpleChange } from '@angular/core'; -import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service'; -import { CellView } from 'sql/parts/notebook/cellViews/interfaces'; - +import { localize } from 'vs/nls'; import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import * as themeColors from 'vs/workbench/common/theme'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces'; -import { ISanitizer, defaultSanitizer } from 'sql/parts/notebook/outputs/sanitizer'; -import { localize } from 'vs/nls'; -import { NotebookModel } from 'sql/parts/notebook/models/notebookModel'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Emitter } from 'vs/base/common/event'; import URI from 'vs/base/common/uri'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service'; +import { CellView } from 'sql/parts/notebook/cellViews/interfaces'; +import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces'; +import { ISanitizer, defaultSanitizer } from 'sql/parts/notebook/outputs/sanitizer'; +import { NotebookModel } from 'sql/parts/notebook/models/notebookModel'; +import { CellToggleMoreActions } from 'sql/parts/notebook/cellToggleMoreActions'; + export const TEXT_SELECTOR: string = 'text-cell-component'; @Component({ @@ -28,6 +30,7 @@ export const TEXT_SELECTOR: string = 'text-cell-component'; }) export class TextCellComponent extends CellView implements OnInit, OnChanges { @ViewChild('preview', { read: ElementRef }) private output: ElementRef; + @ViewChild('moreactions', { read: ElementRef }) private moreActionsElementRef: ElementRef; @Input() cellModel: ICellModel; @Input() set model(value: NotebookModel) { @@ -46,10 +49,12 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges { private readonly _onDidClickLink = this._register(new Emitter()); public readonly onDidClickLink = this._onDidClickLink.event; protected isLoading: boolean; + private _cellToggleMoreActions: CellToggleMoreActions; constructor( @Inject(forwardRef(() => CommonServiceInterface)) private _bootstrapService: CommonServiceInterface, @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef, + @Inject(IInstantiationService) private _instantiationService: IInstantiationService, @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService, @Inject(ICommandService) private _commandService: ICommandService, @Inject(IOpenerService) private readonly openerService: IOpenerService, @@ -57,6 +62,7 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges { super(); this.isEditMode = false; this.isLoading = true; + this._cellToggleMoreActions = this._instantiationService.createInstance(CellToggleMoreActions); } //Gets sanitizer from ISanitizer interface @@ -137,6 +143,9 @@ 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 { @@ -145,7 +154,12 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges { public toggleEditMode(editMode?: boolean): void { this.isEditMode = editMode !== undefined? editMode : !this.isEditMode; - + if (!this.isEditMode && this.cellModel.id === this._activeCellId) { + this._cellToggleMoreActions.toggle(true, this.moreActionsElementRef, this.model, this.cellModel); + } + else { + this._cellToggleMoreActions.toggle(false, this.moreActionsElementRef, this.model, this.cellModel); + } this.updatePreview(); this._changeRef.detectChanges(); } diff --git a/src/sql/parts/notebook/notebook.css b/src/sql/parts/notebook/notebook.css index d683d9d631..c8afd50d70 100644 --- a/src/sql/parts/notebook/notebook.css +++ b/src/sql/parts/notebook/notebook.css @@ -74,4 +74,9 @@ .vs-dark .notebookEditor .notebook-button.icon-save, .hc-black .notebookEditor .notebook-button.icon-save{ background-image: url("./media/dark/save_inverse.svg"); +} + +.moreActions .action-label.icon.toggle-more { + height: 20px; + width: 20px; } \ No newline at end of file diff --git a/src/sql/parts/notebook/notebookActions.ts b/src/sql/parts/notebook/notebookActions.ts index aac3cfdae7..cb1dfc66c0 100644 --- a/src/sql/parts/notebook/notebookActions.ts +++ b/src/sql/parts/notebook/notebookActions.ts @@ -26,7 +26,6 @@ const attachToLabel: string = localize('AttachTo', 'Attach to: '); const msgLoadingContexts = localize('loadingContexts', 'Loading contexts...'); const msgAddNewConnection = localize('addNewConnection', 'Add new connection'); const msgSelectConnection = localize('selectConnection', 'Select connection'); -const msgConnectionNotApplicable = localize('connectionNotSupported', 'n/a'); const msgLocalHost = localize('localhost', 'Localhost'); // Action to add a cell to notebook based on cell type(code/markdown).