From e607f68b3e84265ce76afb11080ca3572cdac7d7 Mon Sep 17 00:00:00 2001
From: Yurong He <43652751+YurongHe@users.noreply.github.com>
Date: Wed, 14 Nov 2018 17:51:59 -0800
Subject: [PATCH] Add more actions to cell (#3217)
* Added toggle more actions to cell
* Resolve PR comments
-- Added INotificationService for notification msg
* Reduced ToggleMoreAction to smaller size. So the dropdown could be displayed closer to it instead of at the buttom of the cell.
---
.../notebook/cellViews/code.component.html | 2 +
.../notebook/cellViews/code.component.ts | 41 +++++++++-
src/sql/parts/notebook/cellViews/code.css | 5 ++
.../parts/notebook/cellViews/codeActions.ts | 79 ++++++++++++++++++-
.../cellViews/codeCell.component.html | 2 +-
.../notebook/cellViews/codeCell.component.ts | 12 +++
.../parts/notebook/models/modelInterfaces.ts | 4 +-
.../parts/notebook/models/notebookModel.ts | 41 +++++++---
.../parts/notebook/notebook.component.html | 2 +-
src/sql/parts/notebook/notebook.component.ts | 5 ++
10 files changed, 174 insertions(+), 19 deletions(-)
diff --git a/src/sql/parts/notebook/cellViews/code.component.html b/src/sql/parts/notebook/cellViews/code.component.html
index f7b12f94ce..cff7e38d03 100644
--- a/src/sql/parts/notebook/cellViews/code.component.html
+++ b/src/sql/parts/notebook/cellViews/code.component.html
@@ -9,4 +9,6 @@
+
+
diff --git a/src/sql/parts/notebook/cellViews/code.component.ts b/src/sql/parts/notebook/cellViews/code.component.ts
index 25ecc4869d..1fbff0f35d 100644
--- a/src/sql/parts/notebook/cellViews/code.component.ts
+++ b/src/sql/parts/notebook/cellViews/code.component.ts
@@ -19,6 +19,9 @@ 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';
@@ -26,7 +29,11 @@ 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 } from 'sql/parts/notebook/cellViews/codeActions';
+import { RunCellAction, DeleteCellAction, AddCellAction } 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';
@@ -36,16 +43,24 @@ export const CODE_SELECTOR: string = 'code-component';
})
export class CodeComponent extends AngularDisposable implements OnInit {
@ViewChild('toolbar', { read: ElementRef }) private toolbarElement: ElementRef;
+ @ViewChild('moreactions', { read: ElementRef }) private moreactionsElement: ElementRef;
@ViewChild('editor', { read: ElementRef }) private codeElement: ElementRef;
@Input() cellModel: ICellModel;
+
@Output() public onContentChanged = new EventEmitter();
+ @Input() set model(value: NotebookModel) {
+ this._model = value;
+ }
+
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;
constructor(
@Inject(forwardRef(() => CommonServiceInterface)) private _bootstrapService: CommonServiceInterface,
@@ -55,7 +70,8 @@ export class CodeComponent extends AngularDisposable implements OnInit {
@Inject(IModelService) private _modelService: IModelService,
@Inject(IModeService) private _modeService: IModeService,
@Inject(IContextMenuService) private contextMenuService: IContextMenuService,
- @Inject(IContextViewService) private contextViewService: IContextViewService
+ @Inject(IContextViewService) private contextViewService: IContextViewService,
+ @Inject(INotificationService) private notificationService: INotificationService,
) {
super();
}
@@ -78,6 +94,10 @@ export class CodeComponent extends AngularDisposable implements OnInit {
}));
}
+ get model(): NotebookModel {
+ return this._model;
+ }
+
private createEditor(): void {
let instantiationService = this._instantiationService.createChild(new ServiceCollection([IProgressService, new SimpleProgressService()]));
this._editor = instantiationService.createInstance(QueryTextEditor);
@@ -119,6 +139,19 @@ export class CodeComponent extends AngularDisposable implements OnInit {
this._actionBar.setContent([
{ action: runCellAction }
]);
+
+ let moreActionsElement = this.moreactionsElement.nativeElement;
+ this._moreActions = new ActionBar(moreActionsElement, { orientation: ActionsOrientation.VERTICAL });
+ this._moreActions.context = { target: moreActionsElement };
+
+ let actions: Action[] = [];
+ actions.push(this._instantiationService.createInstance(AddCellAction, 'codeBefore', localize('codeBefore', 'Insert Code before'), CellTypes.Code, false, this.notificationService));
+ actions.push(this._instantiationService.createInstance(AddCellAction, 'codeAfter', localize('codeAfter', 'Insert Code after'), CellTypes.Code, true, this.notificationService));
+ actions.push(this._instantiationService.createInstance(AddCellAction, 'markdownBefore', localize('markdownBefore', 'Insert Markdown before'), CellTypes.Markdown, false, this.notificationService));
+ actions.push(this._instantiationService.createInstance(AddCellAction, 'markdownAfter', localize('markdownAfter', 'Insert Markdown after'), CellTypes.Markdown, true, this.notificationService));
+ actions.push(this._instantiationService.createInstance(DeleteCellAction, 'delete', localize('delete', 'Delete'), CellTypes.Code, false, this.notificationService));
+
+ this._moreActions.push(this._instantiationService.createInstance(ToggleMoreWidgetAction, actions, this.model, this.contextMenuService), { icon: true, label: false });
}
private createUri(): URI {
@@ -146,5 +179,9 @@ export class CodeComponent extends AngularDisposable implements OnInit {
private updateTheme(theme: IColorTheme): void {
let toolbarEl = this.toolbarElement.nativeElement;
toolbarEl.style.borderRightColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
+
+ let moreactionsEl = this.moreactionsElement.nativeElement;
+ moreactionsEl.style.borderRightColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
}
+
}
diff --git a/src/sql/parts/notebook/cellViews/code.css b/src/sql/parts/notebook/cellViews/code.css
index 8785f88552..577d98b999 100644
--- a/src/sql/parts/notebook/cellViews/code.css
+++ b/src/sql/parts/notebook/cellViews/code.css
@@ -30,6 +30,11 @@ 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
{
padding-left: 10px
diff --git a/src/sql/parts/notebook/cellViews/codeActions.ts b/src/sql/parts/notebook/cellViews/codeActions.ts
index 292bd506e8..364ad449ad 100644
--- a/src/sql/parts/notebook/cellViews/codeActions.ts
+++ b/src/sql/parts/notebook/cellViews/codeActions.ts
@@ -6,7 +6,14 @@
import { Action } from 'vs/base/common/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import { localize } from 'vs/nls';
+import { CellType } from 'sql/parts/notebook/models/contracts';
+import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
+import { getErrorMessage } from 'sql/parts/notebook/notebookUtils';
+import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
+import { NOTFOUND } from 'dns';
+import { NsfwWatcherService } from 'vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService';
+let notebookMoreActionMsg = localize('notebook.failed', "Please select active cell and try again");
export class RunCellAction extends Action {
public static ID = 'jobaction.notebookRunCell';
public static LABEL = 'Run cell';
@@ -14,7 +21,7 @@ export class RunCellAction extends Action {
constructor(
) {
super(RunCellAction.ID, '', 'toolbarIconRun');
- this.tooltip = localize('runCell','Run cell');
+ this.tooltip = localize('runCell', 'Run cell');
}
public run(context: any): TPromise {
@@ -26,4 +33,74 @@ export class RunCellAction extends Action {
}
});
}
+}
+
+export class AddCellAction extends Action {
+ constructor(
+ id: string, label: string, private cellType: CellType, private isAfter: boolean, private notificationService: INotificationService
+ ) {
+ super(id, label);
+ }
+ public run(model: NotebookModel): TPromise {
+ return new TPromise((resolve, reject) => {
+ try {
+ if (!model) {
+ return;
+ }
+ if (model.activeCell === undefined) {
+ this.notificationService.notify({
+ severity: Severity.Error,
+ message: notebookMoreActionMsg
+ });
+ }
+ else {
+ let index = model.cells.findIndex((cell) => cell.id === model.activeCell.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
+ });
+ }
+ });
+ }
+}
+
+export class DeleteCellAction extends Action {
+ constructor(
+ id: string, label: string, private cellType: CellType, private isAfter: boolean, private notificationService: INotificationService
+ ) {
+ super(id, label);
+ }
+ public run(model: NotebookModel): TPromise {
+ return new TPromise((resolve, reject) => {
+ try {
+ if (!model) {
+ return;
+ }
+ if (model.activeCell === undefined) {
+ this.notificationService.notify({
+ severity: Severity.Error,
+ message: notebookMoreActionMsg
+ });
+ }
+ else {
+ model.deleteCell(model.activeCell);
+ }
+ } catch (error) {
+ let message = getErrorMessage(error);
+
+ this.notificationService.notify({
+ severity: Severity.Error,
+ message: message
+ });
+ }
+ });
+ }
}
\ No newline at end of file
diff --git a/src/sql/parts/notebook/cellViews/codeCell.component.html b/src/sql/parts/notebook/cellViews/codeCell.component.html
index 1351e5ef2f..6c3fef99ca 100644
--- a/src/sql/parts/notebook/cellViews/codeCell.component.html
+++ b/src/sql/parts/notebook/cellViews/codeCell.component.html
@@ -6,7 +6,7 @@
-->
-
+
0" [cellModel]="cellModel">
diff --git a/src/sql/parts/notebook/cellViews/codeCell.component.ts b/src/sql/parts/notebook/cellViews/codeCell.component.ts
index c15f3cc42d..700f6f273f 100644
--- a/src/sql/parts/notebook/cellViews/codeCell.component.ts
+++ b/src/sql/parts/notebook/cellViews/codeCell.component.ts
@@ -12,6 +12,7 @@ import { CellView } from 'sql/parts/notebook/cellViews/interfaces';
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import * as themeColors from 'vs/workbench/common/theme';
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
+import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
export const CODE_SELECTOR: string = 'code-cell-component';
@@ -21,7 +22,13 @@ export const CODE_SELECTOR: string = 'code-cell-component';
templateUrl: decodeURI(require.toUrl('./codeCell.component.html'))
})
export class CodeCellComponent extends CellView implements OnInit {
+ private _model: NotebookModel;
+
@Input() cellModel: ICellModel;
+ @Input() set model(value: NotebookModel) {
+ this._model = value;
+ }
+
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
@@ -41,4 +48,9 @@ export class CodeCellComponent extends CellView implements OnInit {
private updateTheme(theme: IColorTheme): void {
}
+
+ get model(): NotebookModel {
+ return this._model;
+ }
+
}
diff --git a/src/sql/parts/notebook/models/modelInterfaces.ts b/src/sql/parts/notebook/models/modelInterfaces.ts
index 93d68fee70..17baf6f61d 100644
--- a/src/sql/parts/notebook/models/modelInterfaces.ts
+++ b/src/sql/parts/notebook/models/modelInterfaces.ts
@@ -304,9 +304,9 @@ export interface INotebookModel {
changeContext(host: string): void;
/**
- * Adds a cell to the end of the model
+ * Adds a cell to the index of the model
*/
- addCell(cellType: CellType): void;
+ addCell(cellType: CellType, index?: number): void;
/**
* Deletes a cell
diff --git a/src/sql/parts/notebook/models/notebookModel.ts b/src/sql/parts/notebook/models/notebookModel.ts
index 17520a2899..cd2d08f745 100644
--- a/src/sql/parts/notebook/models/notebookModel.ts
+++ b/src/sql/parts/notebook/models/notebookModel.ts
@@ -79,6 +79,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
private readonly _nbformatMinor: number = nbversion.MINOR_VERSION;
private _hadoopConnection: NotebookConnection;
private _defaultKernel: nb.IKernelSpec;
+ private _activeCell: ICellModel;
constructor(private notebookOptions: INotebookModelOptions, startSessionImmediately?: boolean, private connectionProfile?: IConnectionProfile) {
super();
@@ -207,17 +208,25 @@ export class NotebookModel extends Disposable implements INotebookModel {
}
}
- public addCell(cellType: CellType): void {
- if (this.inErrorState || !this._cells) {
- return;
- }
- let cell = this.createCell(cellType);
- this._cells.push(cell);
- this._contentChangedEmitter.fire({
- changeType: NotebookChangeType.CellsAdded,
- cells: [cell]
- });
- }
+ public addCell(cellType: CellType, index?: number): void {
+ if (this.inErrorState || !this._cells) {
+ return;
+ }
+ let cell = this.createCell(cellType);
+
+ if (index !== undefined && index !== null && index >= 0 && index < this._cells.length) {
+ this._cells.splice(index, 0, cell);
+ } else {
+ this._cells.push(cell);
+ index = undefined;
+ }
+
+ this._contentChangedEmitter.fire({
+ changeType: NotebookChangeType.CellsAdded,
+ cells: [cell],
+ cellIndex: index
+ });
+ }
private createCell(cellType: CellType): ICellModel {
let singleCell: nb.ICell = {
@@ -229,7 +238,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
return this.notebookOptions.factory.createCell(singleCell, { notebook: this, isTrusted: true });
}
- deleteCell(cellModel: CellModel): void {
+ deleteCell(cellModel: ICellModel): void {
if (this.inErrorState || !this._cells) {
return;
}
@@ -246,6 +255,14 @@ export class NotebookModel extends Disposable implements INotebookModel {
}
}
+ public get activeCell(): ICellModel {
+ return this._activeCell;
+ }
+
+ public set activeCell(value: ICellModel) {
+ this._activeCell = value;
+ }
+
private notifyError(error: string): void {
this.onErrorEmitter.fire({ message: error, severity: Severity.Error });
}
diff --git a/src/sql/parts/notebook/notebook.component.html b/src/sql/parts/notebook/notebook.component.html
index af172b0de0..274c3ed078 100644
--- a/src/sql/parts/notebook/notebook.component.html
+++ b/src/sql/parts/notebook/notebook.component.html
@@ -10,7 +10,7 @@