Add notebook grid actions (#7181)

* Add notebook grid actions

* pr comments
This commit is contained in:
Chris LaFreniere
2019-09-17 14:16:35 -07:00
committed by GitHub
parent 7e0a5205b2
commit 141226332c
4 changed files with 77 additions and 14 deletions

View File

@@ -468,3 +468,9 @@ plotly-output .plotly-wrapper {
display: block; display: block;
overflow-y: hidden; overflow-y: hidden;
} }
output-component .grid-panel .action-label.icon {
min-width: 16px;
margin-right: 6px;
margin-bottom: 6px;
}

View File

@@ -32,10 +32,11 @@ import { getErrorMessage } from 'vs/base/common/errors';
import { ISerializationService, SerializeDataParams } from 'sql/platform/serialization/common/serializationService'; import { ISerializationService, SerializeDataParams } from 'sql/platform/serialization/common/serializationService';
import { SaveResultAction } from 'sql/workbench/parts/query/browser/actions'; import { SaveResultAction } from 'sql/workbench/parts/query/browser/actions';
import { ResultSerializer, SaveResultsResponse } from 'sql/workbench/parts/query/common/resultSerializer'; import { ResultSerializer, SaveResultsResponse } from 'sql/workbench/parts/query/common/resultSerializer';
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
@Component({ @Component({
selector: GridOutputComponent.SELECTOR, selector: GridOutputComponent.SELECTOR,
template: `<div #output class="notebook-cellTable"></div>` template: `<div #output class="notebook-cellTable" (mouseover)="hover=true" (mouseleave)="hover=false"></div>`
}) })
export class GridOutputComponent extends AngularDisposable implements IMimeComponent, OnInit { export class GridOutputComponent extends AngularDisposable implements IMimeComponent, OnInit {
public static readonly SELECTOR: string = 'grid-output'; public static readonly SELECTOR: string = 'grid-output';
@@ -46,6 +47,7 @@ export class GridOutputComponent extends AngularDisposable implements IMimeCompo
private _cellModel: ICellModel; private _cellModel: ICellModel;
private _bundleOptions: MimeModel.IOptions; private _bundleOptions: MimeModel.IOptions;
private _table: DataResourceTable; private _table: DataResourceTable;
private _hover: boolean;
constructor( constructor(
@Inject(IInstantiationService) private instantiationService: IInstantiationService, @Inject(IInstantiationService) private instantiationService: IInstantiationService,
@Inject(IThemeService) private readonly themeService: IThemeService @Inject(IThemeService) private readonly themeService: IThemeService
@@ -73,6 +75,14 @@ export class GridOutputComponent extends AngularDisposable implements IMimeCompo
} }
} }
@Input() set hover(value: boolean) {
// only reaction on hover changes
if (this._hover !== value) {
this.toggleActionbar(value);
this._hover = value;
}
}
ngOnInit() { ngOnInit() {
this.renderGrid(); this.renderGrid();
} }
@@ -89,6 +99,8 @@ export class GridOutputComponent extends AngularDisposable implements IMimeCompo
outputElement.appendChild(this._table.element); outputElement.appendChild(this._table.element);
this._register(attachTableStyler(this._table, this.themeService)); this._register(attachTableStyler(this._table, this.themeService));
this.layout(); this.layout();
// By default, do not show the actions
this.toggleActionbar(false);
this._table.onAdd(); this._table.onAdd();
this._initialized = true; this._initialized = true;
} }
@@ -97,7 +109,19 @@ export class GridOutputComponent extends AngularDisposable implements IMimeCompo
layout(): void { layout(): void {
if (this._table) { if (this._table) {
let maxSize = Math.min(this._table.maximumSize, 500); let maxSize = Math.min(this._table.maximumSize, 500);
this._table.layout(maxSize); this._table.layout(maxSize, undefined, ActionsOrientation.HORIZONTAL);
}
}
private toggleActionbar(visible: boolean) {
let outputElement = <HTMLElement>this.output.nativeElement;
let actionsContainers: HTMLElement[] = Array.prototype.slice.call(outputElement.getElementsByClassName('actions-container'));
if (actionsContainers && actionsContainers.length) {
if (visible) {
actionsContainers.forEach(container => container.style.visibility = 'visible');
} else {
actionsContainers.forEach(container => container.style.visibility = 'hidden');
}
} }
} }
} }
@@ -125,7 +149,7 @@ class DataResourceTable extends GridTableBase<any> {
} }
protected getCurrentActions(): IAction[] { protected getCurrentActions(): IAction[] {
return []; return this.getContextActions();
} }
protected getContextActions(): IAction[] { protected getContextActions(): IAction[] {

View File

@@ -3,6 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/gridPanel';
import { attachTableStyler } from 'sql/platform/theme/common/styler'; import { attachTableStyler } from 'sql/platform/theme/common/styler';
import QueryRunner, { QueryGridDataProvider } from 'sql/platform/query/common/queryRunner'; import QueryRunner, { QueryGridDataProvider } from 'sql/platform/query/common/queryRunner';
import { VirtualizedCollection, AsyncDataProvider } from 'sql/base/browser/ui/table/asyncDataView'; import { VirtualizedCollection, AsyncDataProvider } from 'sql/base/browser/ui/table/asyncDataView';
@@ -416,7 +418,22 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
this.scrolled = false; this.scrolled = false;
} }
private build(): void { // actionsOrientation controls the orientation (horizontal or vertical) of the actionBar
private build(actionsOrientation?: ActionsOrientation): void {
// Default is VERTICAL
if (isUndefinedOrNull(actionsOrientation)) {
actionsOrientation = ActionsOrientation.VERTICAL;
}
let actionBarContainer = document.createElement('div');
// Create a horizontal actionbar if orientation passed in is HORIZONTAL
if (actionsOrientation === ActionsOrientation.HORIZONTAL) {
actionBarContainer.className = 'grid-panel action-bar horizontal';
this.container.appendChild(actionBarContainer);
}
let tableContainer = document.createElement('div'); let tableContainer = document.createElement('div');
tableContainer.style.display = 'inline-block'; tableContainer.style.display = 'inline-block';
tableContainer.style.width = `calc(100% - ${ACTIONBAR_WIDTH}px)`; tableContainer.style.width = `calc(100% - ${ACTIONBAR_WIDTH}px)`;
@@ -459,13 +476,12 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
if (this.styles) { if (this.styles) {
this.table.style(this.styles); this.table.style(this.styles);
} }
// If the actionsOrientation passed in is "VERTICAL" (or no actionsOrientation is passed in at all), create a vertical actionBar
let actionBarContainer = document.createElement('div'); if (actionsOrientation === ActionsOrientation.VERTICAL) {
actionBarContainer.style.width = ACTIONBAR_WIDTH + 'px'; actionBarContainer.className = 'grid-panel action-bar vertical';
actionBarContainer.style.display = 'inline-block'; actionBarContainer.style.width = ACTIONBAR_WIDTH + 'px';
actionBarContainer.style.height = '100%'; this.container.appendChild(actionBarContainer);
actionBarContainer.style.verticalAlign = 'top'; }
this.container.appendChild(actionBarContainer);
let context: IGridActionContext = { let context: IGridActionContext = {
gridDataProvider: this.gridDataProvider, gridDataProvider: this.gridDataProvider,
table: this.table, table: this.table,
@@ -474,7 +490,7 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
resultId: this.resultSet.id resultId: this.resultSet.id
}; };
this.actionBar = new ActionBar(actionBarContainer, { this.actionBar = new ActionBar(actionBarContainer, {
orientation: ActionsOrientation.VERTICAL, context: context orientation: actionsOrientation, context: context
}); });
// update context before we run an action // update context before we run an action
this.selectionModel.onSelectedRangesChanged.subscribe(e => { this.selectionModel.onSelectedRangesChanged.subscribe(e => {
@@ -602,15 +618,17 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
protected abstract getContextActions(): IAction[]; protected abstract getContextActions(): IAction[];
public layout(size?: number): void { // The actionsOrientation passed in controls the actionBar orientation
public layout(size?: number, orientation?: Orientation, actionsOrientation?: ActionsOrientation): void {
if (!this.table) { if (!this.table) {
this.build(); this.build(actionsOrientation);
} }
if (!size) { if (!size) {
size = this.currentHeight; size = this.currentHeight;
} else { } else {
this.currentHeight = size; this.currentHeight = size;
} }
// Table is always called with Orientation as VERTICAL
this.table.layout(size, Orientation.VERTICAL); this.table.layout(size, Orientation.VERTICAL);
} }

View File

@@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.grid-panel.action-bar.vertical {
display : inline-block;
height : 100%;
vertical-align : top;
}
.grid-panel.action-bar.horizontal {
width : 100%;
display: flex;
}