mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 09:35:40 -05:00
Notebook views UI 2 (#15865)
Provide access to the Notebook Views UI. Feature is hidden behind a feature flag. Co-authored-by: rkselfhost <rkselfhost@outlook.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.nb-grid-stack .grid-stack-item.notebook-cell .actionbar {
|
||||
.nb-grid-stack .grid-stack-item .notebook-cell .actionbar {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
position: absolute;
|
||||
@@ -11,48 +11,48 @@
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
.nb-grid-stack .grid-stack-item.notebook-cell .actionbar .carbon-taskbar .monaco-action-bar.animated {
|
||||
.nb-grid-stack .grid-stack-item .notebook-cell .actionbar .carbon-taskbar .monaco-action-bar.animated {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nb-grid-stack .grid-stack-item.notebook-cell .actionbar .carbon-taskbar.monaco-toolbar .monaco-action-bar.animated ul.actions-container {
|
||||
.nb-grid-stack .grid-stack-item .notebook-cell .actionbar .carbon-taskbar.monaco-toolbar .monaco-action-bar.animated ul.actions-container {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nb-grid-stack .grid-stack-item.notebook-cell .actionbar ul.actions-container li.action-item {
|
||||
.nb-grid-stack .grid-stack-item .notebook-cell .actionbar ul.actions-container li.action-item {
|
||||
display: inline-flex;
|
||||
margin-right: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nb-grid-stack .grid-stack-item.notebook-cell .actionbar ul.actions-container li:last-child {
|
||||
.nb-grid-stack .grid-stack-item .notebook-cell .actionbar ul.actions-container li:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
.nb-grid-stack .grid-stack-item.notebook-cell .actionbar .carbon-taskbar .action-label {
|
||||
.nb-grid-stack .grid-stack-item .notebook-cell .actionbar .carbon-taskbar .action-label {
|
||||
padding: 0;
|
||||
}
|
||||
.nb-grid-stack .grid-stack-item.notebook-cell .actionbar .monaco-action-bar .action-label {
|
||||
.nb-grid-stack .grid-stack-item .notebook-cell .actionbar .monaco-action-bar .action-label {
|
||||
margin-right: 0;
|
||||
}
|
||||
.nb-grid-stack .grid-stack-item.notebook-cell .actionbar ul.actions-container li a.masked-icon {
|
||||
.nb-grid-stack .grid-stack-item .notebook-cell .actionbar ul.actions-container li a.masked-icon {
|
||||
display: flex;
|
||||
}
|
||||
.nb-grid-stack .grid-stack-item.notebook-cell .actionbar ul.actions-container li a.masked-icon,
|
||||
.nb-grid-stack .grid-stack-item.notebook-cell .actionbar ul.actions-container li a.masked-icon:before {
|
||||
.nb-grid-stack .grid-stack-item .notebook-cell .actionbar ul.actions-container li a.masked-icon,
|
||||
.nb-grid-stack .grid-stack-item .notebook-cell .actionbar ul.actions-container li a.masked-icon:before {
|
||||
height: 24px;
|
||||
width: 29px;
|
||||
}
|
||||
.nb-grid-stack .grid-stack-item.notebook-cell .actionbar .codicon.masked-icon,
|
||||
.vs .nb-grid-stack .grid-stack-item.notebook-cell .actionbar .codicon.masked-icon,
|
||||
.vs-dark .nb-grid-stack .grid-stack-item.notebook-cell .actionbar .codicon.masked-icon,
|
||||
.hc-black .nb-grid-stack .grid-stack-item.notebook-cell .actionbar .codicon.masked-icon {
|
||||
.nb-grid-stack .grid-stack-item .notebook-cell .actionbar .codicon.masked-icon,
|
||||
.vs .nb-grid-stack .grid-stack-item .notebook-cell .actionbar .codicon.masked-icon,
|
||||
.vs-dark .nb-grid-stack .grid-stack-item .notebook-cell .actionbar .codicon.masked-icon,
|
||||
.hc-black .nb-grid-stack .grid-stack-item .notebook-cell .actionbar .codicon.masked-icon {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.nb-grid-stack .grid-stack-item.notebook-cell .actionbar .notebook-button.toolbarIconStop {
|
||||
.nb-grid-stack .grid-stack-item .notebook-cell .actionbar .notebook-button.toolbarIconStop {
|
||||
margin: 0 2px;
|
||||
background-size: 20px 25px;
|
||||
height: 24px;
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
|
||||
<div #viewsToolbar class="editor-toolbar actionbar-container" style="flex: 0 0 auto; display: flex; flex-flow: row; width: 100%; align-items: center;"></div>
|
||||
<div #container class="scrollable" style="flex: 1 1 auto; position: relative; outline: none" (click)="unselectActiveCell()" (scroll)="scrollHandler($event)">
|
||||
<loading-spinner [loading]="isLoading"></loading-spinner>
|
||||
<notebook-views-grid-component #gridstack [views]="views" [cells]="cells" [model]="model"></notebook-views-grid-component>
|
||||
<div class="book-nav" #bookNav [style.visibility]="navigationVisibility">
|
||||
</div>
|
||||
<notebook-views-grid-component #gridstack [views]="views" [activeView]="activeView" [cells]="cells" [model]="model"></notebook-views-grid-component>
|
||||
|
||||
<ng-container *ngFor="let cell of cellsAwaitingInput">
|
||||
<notebook-views-modal-component class="modal" [title]="cellsAwaitingInputModalTitle">
|
||||
<views-code-cell-component *ngIf="cell.cellType === 'code'" [cellModel]="cell" [model]="model" [activeCellId]="activeCellId"></views-code-cell-component>
|
||||
</notebook-views-modal-component>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!./notebookViewsGrid';
|
||||
import { Component, Input, ViewChildren, QueryList, ChangeDetectorRef, forwardRef, Inject, ViewChild, ElementRef } from '@angular/core';
|
||||
import { ICellModel, INotebookModel, ISingleNotebookEditOperation } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { ICellModel, INotebookModel, ISingleNotebookEditOperation, NotebookContentChange, } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { CodeCellComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/codeCell.component';
|
||||
import { TextCellComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/textCell.component';
|
||||
import { ICellEditorProvider, INotebookParams, INotebookService, INotebookEditor, NotebookRange, INotebookSection, DEFAULT_NOTEBOOK_PROVIDER, SQL_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
@@ -11,18 +12,18 @@ import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/no
|
||||
import * as notebookUtils from 'sql/workbench/services/notebook/browser/models/notebookUtils';
|
||||
import { IBootstrapParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { LabeledMenuItemActionItem } from 'sql/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { CellType, CellTypes } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { CellType, CellTypes, NotebookChangeType } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension';
|
||||
@@ -30,7 +31,11 @@ import { INotebookView, INotebookViewMetadata } from 'sql/workbench/services/not
|
||||
import { NotebookViewsGridComponent } from 'sql/workbench/contrib/notebook/browser/notebookViews/notebookViewsGrid.component';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { DeleteViewAction, InsertCellAction, ViewSettingsAction } from 'sql/workbench/contrib/notebook/browser/notebookViews/notebookViewsActions';
|
||||
import { RunAllCellsAction } from 'sql/workbench/contrib/notebook/browser/notebookActions';
|
||||
import { DropdownMenuActionViewItem } from 'sql/base/browser/ui/buttonMenu/buttonMenu';
|
||||
import { NotebookViewsActionProvider, AddCellAction, RunAllCellsAction } from 'sql/workbench/contrib/notebook/browser/notebookActions';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { LabeledMenuItemActionItem } from 'sql/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
|
||||
export const NOTEBOOKVIEWS_SELECTOR: string = 'notebook-view-component';
|
||||
@@ -44,7 +49,7 @@ export class NotebookViewComponent extends AngularDisposable implements INoteboo
|
||||
@Input() model: NotebookModel;
|
||||
@Input() activeView: INotebookView;
|
||||
@Input() views: NotebookViewsExtension;
|
||||
@Input() notebookMeta: INotebookViewMetadata;
|
||||
@Input() notebookMetadata: INotebookViewMetadata;
|
||||
|
||||
@ViewChild('container', { read: ElementRef }) private _container: ElementRef;
|
||||
@ViewChild('viewsToolbar', { read: ElementRef }) private _viewsToolbar: ElementRef;
|
||||
@@ -57,12 +62,16 @@ export class NotebookViewComponent extends AngularDisposable implements INoteboo
|
||||
private _modelReadyDeferred = new Deferred<NotebookModel>();
|
||||
private _runAllCellsAction: RunAllCellsAction;
|
||||
private _scrollTop: number;
|
||||
public _cellsAwaitingInput: ICellModel[] = [];
|
||||
public readonly cellsAwaitingInputModalTitle: string = localize('cellAwaitingInputTitle', "Cell Awaiting Input");
|
||||
public readonly loadingMessage = localize('loading', "Loading");
|
||||
|
||||
constructor(
|
||||
@Inject(IBootstrapParams) private _notebookParams: INotebookParams,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(IInstantiationService) private _instantiationService: IInstantiationService,
|
||||
@Inject(IKeybindingService) private _keybindingService: IKeybindingService,
|
||||
@Inject(IContextMenuService) private _contextMenuService: IContextMenuService,
|
||||
@Inject(INotificationService) private _notificationService: INotificationService,
|
||||
@Inject(INotebookService) private _notebookService: INotebookService,
|
||||
@Inject(IConnectionManagementService) private _connectionManagementService: IConnectionManagementService,
|
||||
@@ -120,12 +129,19 @@ export class NotebookViewComponent extends AngularDisposable implements INoteboo
|
||||
if (!isUndefinedOrNull(endCell)) {
|
||||
endIndex = codeCells.findIndex(c => c.id === endCell.id);
|
||||
}
|
||||
let statusNotification = this._notificationService.notify({ severity: Severity.Info, message: localize('startingExecution', "Starting execution"), progress: { total: endIndex + 1, worked: 0 } });
|
||||
for (let i = startIndex; i < endIndex; i++) {
|
||||
let cellStatus = await this.runCell(codeCells[i]);
|
||||
if (!cellStatus) {
|
||||
throw new Error(localize('cellRunFailed', "Run Cells failed - See error in output of the currently selected cell for more information."));
|
||||
statusNotification.updateMessage(localize('runningCell', "Running cell {0} of {1}", (i + 1), (endIndex)));
|
||||
statusNotification.progress.worked(i);
|
||||
try {
|
||||
await this.runCell(codeCells[i]);
|
||||
} catch (error) {
|
||||
statusNotification.updateSeverity(Severity.Error);
|
||||
statusNotification.updateMessage(localize('cellRunFailed', "Run Cells failed - See error in output of the currently selected cell for more information."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
statusNotification.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -153,10 +169,11 @@ export class NotebookViewComponent extends AngularDisposable implements INoteboo
|
||||
|
||||
ngOnInit() {
|
||||
this.initViewsToolbar();
|
||||
this._notebookService.addNotebookEditor(this);
|
||||
this._modelReadyDeferred.resolve(this.model);
|
||||
this._notebookService.addNotebookEditor(this);
|
||||
this.setScrollPosition();
|
||||
|
||||
this._register(this.model.contentChanged((e) => this.handleContentChanged(e)));
|
||||
this.doLoad().catch(e => onUnexpectedError(e));
|
||||
}
|
||||
|
||||
@@ -249,6 +266,25 @@ export class NotebookViewComponent extends AngularDisposable implements INoteboo
|
||||
|
||||
let viewOptions = this._instantiationService.createInstance(ViewSettingsAction, this.views);
|
||||
|
||||
let viewsContainer = document.createElement('li');
|
||||
let viewsActionsProvider = new NotebookViewsActionProvider(viewsContainer, this.views, this.modelReady, this._notebookService, this._notificationService, this._instantiationService);
|
||||
let viewsButton = this._instantiationService.createInstance(AddCellAction, 'notebook.OpenViews', undefined, 'notebook-button masked-pseudo code');
|
||||
let viewsDropdownContainer = DOM.$('li.action-item');
|
||||
viewsDropdownContainer.setAttribute('role', 'presentation');
|
||||
let viewsDropdownMenuActionViewItem = new DropdownMenuActionViewItem(
|
||||
viewsButton,
|
||||
viewsActionsProvider,
|
||||
this._contextMenuService,
|
||||
undefined,
|
||||
this._actionBar.actionRunner,
|
||||
undefined,
|
||||
'codicon notebook-button masked-pseudo masked-pseudo-after icon-dashboard-view dropdown-arrow',
|
||||
this.activeView?.name,
|
||||
() => AnchorAlignment.RIGHT
|
||||
);
|
||||
viewsDropdownMenuActionViewItem.render(viewsDropdownContainer);
|
||||
viewsDropdownMenuActionViewItem.setActionContext(this._notebookParams.notebookUri);
|
||||
|
||||
let deleteView = this._instantiationService.createInstance(DeleteViewAction, this.views);
|
||||
|
||||
this._actionBar.setContent([
|
||||
@@ -257,6 +293,7 @@ export class NotebookViewComponent extends AngularDisposable implements INoteboo
|
||||
{ action: insertCellsAction },
|
||||
{ action: this._runAllCellsAction },
|
||||
{ element: spacerElement },
|
||||
{ element: viewsDropdownContainer },
|
||||
{ action: viewOptions },
|
||||
{ action: deleteView }
|
||||
]);
|
||||
@@ -276,6 +313,27 @@ export class NotebookViewComponent extends AngularDisposable implements INoteboo
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private handleContentChanged(change: NotebookContentChange) {
|
||||
switch (change.changeType) {
|
||||
case NotebookChangeType.CellAwaitingInput: {
|
||||
const cell: ICellModel = change?.cells[0];
|
||||
this._cellsAwaitingInput.push(cell);
|
||||
this.detectChanges();
|
||||
break;
|
||||
}
|
||||
case NotebookChangeType.CellExecuted: {
|
||||
const cell: ICellModel = change?.cells[0];
|
||||
const indexToRemove = this._cellsAwaitingInput.findIndex(c => c.id === cell.id);
|
||||
if (indexToRemove >= 0) {
|
||||
this._cellsAwaitingInput.splice(indexToRemove, 1);
|
||||
this.model.serializationStateChanged(NotebookChangeType.CellsModified, cell);
|
||||
this.detectChanges();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private detectChanges(): void {
|
||||
if (!(this._changeRef['destroyed'])) {
|
||||
this._changeRef.detectChanges();
|
||||
@@ -297,4 +355,8 @@ export class NotebookViewComponent extends AngularDisposable implements INoteboo
|
||||
}
|
||||
return editors;
|
||||
}
|
||||
|
||||
public get cellsAwaitingInput(): ICellModel[] {
|
||||
return this._gridstack.hiddenItems.filter(i => this._cellsAwaitingInput.includes(i.cell)).map(i => i.cell);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
-->
|
||||
<ng-template #templateRef>
|
||||
<div
|
||||
*ngIf="display"
|
||||
*ngIf="visible"
|
||||
class="grid-stack-item"
|
||||
attr.data-cell-id="{{cell.cellGuid}}"
|
||||
attr.data-gs-width="{{width}}"
|
||||
attr.data-gs-height="{{height}}"
|
||||
attr.data-gs-x="{{x}}"
|
||||
attr.data-gs-y="{{y}}"
|
||||
attr.gs-w="{{width}}"
|
||||
attr.gs-h="{{height}}"
|
||||
attr.gs-x="{{x}}"
|
||||
attr.gs-y="{{y}}"
|
||||
(click)="selectCell(cell, $event)"
|
||||
>
|
||||
<div
|
||||
@@ -23,7 +23,7 @@
|
||||
<div #actionbar [style.display]="showActionBar ? 'block' : 'none'" class="actionbar"></div>
|
||||
<div class="grid-stack-content-wrapper">
|
||||
<div class="grid-stack-content-wrapper-inner">
|
||||
<views-code-cell-component *ngIf="cell.cellType === 'code'" [cellModel]="cell" [model]="model" [activeCellId]="activeCellId">
|
||||
<views-code-cell-component *ngIf="cell.cellType === 'code'" [cellModel]="cell" [model]="model" [activeCellId]="activeCellId" [visible]="visible">
|
||||
</views-code-cell-component>
|
||||
<text-cell-component *ngIf="cell.cellType === 'markdown'" [cellModel]="cell" [model]="model" [activeCellId]="activeCellId">
|
||||
</text-cell-component>
|
||||
|
||||
@@ -4,32 +4,36 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!./cellToolbar';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { Component, OnInit, Input, ViewChild, TemplateRef, ElementRef, Inject, Output, EventEmitter, ChangeDetectorRef, forwardRef } from '@angular/core';
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { Component, OnInit, Input, ViewChild, TemplateRef, ElementRef, Inject, Output, EventEmitter, ChangeDetectorRef, forwardRef, SimpleChanges } from '@angular/core';
|
||||
import { CellExecutionState, ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
import { DEFAULT_VIEW_CARD_HEIGHT, DEFAULT_VIEW_CARD_WIDTH } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewModel';
|
||||
import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension';
|
||||
import { CellChangeEventType, INotebookView, INotebookViewCellMetadata } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews';
|
||||
import { CellChangeEventType, INotebookView, INotebookViewCell } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews';
|
||||
import { ITaskbarContent, Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { CellContext } from 'sql/workbench/contrib/notebook/browser/cellViews/codeActions';
|
||||
import { RunCellAction, HideCellAction, ViewCellToggleMoreActions } from 'sql/workbench/contrib/notebook/browser/notebookViews/notebookViewsActions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { CellTypes } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { IColorTheme, ICssStyleCollector, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { cellBorder, notebookToolbarSelectBackground } from 'sql/platform/theme/common/colorRegistry';
|
||||
|
||||
@Component({
|
||||
selector: 'view-card-component',
|
||||
templateUrl: decodeURI(require.toUrl('./notebookViewsCard.component.html'))
|
||||
})
|
||||
export class NotebookViewsCardComponent implements OnInit {
|
||||
public _cellToggleMoreActions: ViewCellToggleMoreActions;
|
||||
|
||||
export class NotebookViewsCardComponent extends AngularDisposable implements OnInit {
|
||||
private _actionbar: Taskbar;
|
||||
private _metadata: INotebookViewCellMetadata;
|
||||
private _activeView: INotebookView;
|
||||
private _metadata: INotebookViewCell;
|
||||
private _executionState: CellExecutionState;
|
||||
private _pendingReinitialize: boolean = false;
|
||||
|
||||
public _cellToggleMoreActions: ViewCellToggleMoreActions;
|
||||
|
||||
@Input() cell: ICellModel;
|
||||
@Input() model: NotebookModel;
|
||||
@Input() views: NotebookViewsExtension;
|
||||
@Input() activeView: INotebookView;
|
||||
@Input() meta: boolean;
|
||||
@Input() ready: boolean;
|
||||
@Output() onChange: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
@@ -39,28 +43,50 @@ export class NotebookViewsCardComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(IInstantiationService) private _instantiationService: IInstantiationService
|
||||
) { }
|
||||
@Inject(IInstantiationService) private _instantiationService: IInstantiationService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.initActionBar();
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
if (this.views) {
|
||||
this._activeView = this.views.getActiveView();
|
||||
this._metadata = this.views.getCellMetadata(this.cell);
|
||||
ngAfterViewInit() {
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (this.activeView && changes['activeView'] && changes['activeView'].currentValue?.guid !== changes['activeView'].previousValue?.guid) {
|
||||
this._metadata = this.activeView.getCellMetadata(this.cell);
|
||||
this._pendingReinitialize = true;
|
||||
}
|
||||
this.detectChanges();
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
if (this.views) {
|
||||
this._activeView = this.views.getActiveView();
|
||||
this._metadata = this.views.getCellMetadata(this.cell);
|
||||
if (this.activeView) {
|
||||
this._metadata = this.activeView.getCellMetadata(this.cell);
|
||||
this._pendingReinitialize = true;
|
||||
}
|
||||
this.detectChanges();
|
||||
}
|
||||
|
||||
ngAfterViewChecked() {
|
||||
if (this._pendingReinitialize) {
|
||||
this._pendingReinitialize = false;
|
||||
this.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
override ngOnDestroy() {
|
||||
if (this._actionbar) {
|
||||
this._actionbar.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public initialize(): void {
|
||||
this.initActionBar();
|
||||
this.detectChanges();
|
||||
}
|
||||
|
||||
@@ -69,6 +95,10 @@ export class NotebookViewsCardComponent implements OnInit {
|
||||
let taskbarContent: ITaskbarContent[] = [];
|
||||
let context = new CellContext(this.model, this.cell);
|
||||
|
||||
if (this._actionbar) {
|
||||
this._actionbar.dispose();
|
||||
}
|
||||
|
||||
this._actionbar = new Taskbar(this._actionbarRef.nativeElement);
|
||||
this._actionbar.context = { target: this._actionbarRef.nativeElement };
|
||||
|
||||
@@ -97,6 +127,10 @@ export class NotebookViewsCardComponent implements OnInit {
|
||||
this.onChange.emit({ cell: this.cell, event: event });
|
||||
}
|
||||
|
||||
get displayInputModal(): boolean {
|
||||
return this.awaitingInput;
|
||||
}
|
||||
|
||||
detectChanges() {
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
@@ -110,39 +144,77 @@ export class NotebookViewsCardComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public set executionState(state: CellExecutionState) {
|
||||
if (this._executionState !== state) {
|
||||
this._executionState = state;
|
||||
}
|
||||
}
|
||||
|
||||
public get executionState(): CellExecutionState {
|
||||
return this._executionState;
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
this.changed('hide');
|
||||
}
|
||||
|
||||
public get data(): any {
|
||||
return this._metadata?.views?.find(v => v.guid === this._activeView.guid);
|
||||
public get metadata(): INotebookViewCell {
|
||||
return this._metadata;
|
||||
}
|
||||
|
||||
public get width(): number {
|
||||
return this.data?.width ? this.data.width : DEFAULT_VIEW_CARD_WIDTH;
|
||||
return this.metadata?.width ? this.metadata.width : DEFAULT_VIEW_CARD_WIDTH;
|
||||
}
|
||||
|
||||
public get height(): number {
|
||||
return this.data.height ? this.data.height : DEFAULT_VIEW_CARD_HEIGHT;
|
||||
return this.metadata?.height ? this.metadata.height : DEFAULT_VIEW_CARD_HEIGHT;
|
||||
}
|
||||
|
||||
public get x(): number {
|
||||
return this.data?.x;
|
||||
return this.metadata?.x;
|
||||
}
|
||||
|
||||
public get y(): number {
|
||||
return this.data?.y;
|
||||
return this.metadata?.y;
|
||||
}
|
||||
|
||||
public get display(): boolean {
|
||||
if (!this._metadata || !this._activeView) {
|
||||
/**
|
||||
* Whether to display the card
|
||||
*/
|
||||
public get visible(): boolean {
|
||||
if (!this.activeView) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !this.data?.hidden;
|
||||
if (!this._metadata) { //Means not initialized
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!this.activeView.displayedCells.find(c => c.cellGuid === this.cell.cellGuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the cell expecting input
|
||||
*/
|
||||
public get awaitingInput(): boolean {
|
||||
return this.cell.future && this.cell.future.inProgress;
|
||||
}
|
||||
|
||||
public get showActionBar(): boolean {
|
||||
return this.cell.active;
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
|
||||
const cellBorderColor = theme.getColor(cellBorder);
|
||||
if (cellBorderColor) {
|
||||
collector.addRule(`.notebookEditor .nb-grid-stack .notebook-cell.active .actionbar { border-color: ${cellBorderColor};}`);
|
||||
collector.addRule(`.notebookEditor .nb-grid-stack .notebook-cell.active .actionbar .codicon:before { background-color: ${cellBorderColor};}`);
|
||||
}
|
||||
|
||||
// Cell toolbar background
|
||||
const notebookToolbarSelectBackgroundColor = theme.getColor(notebookToolbarSelectBackground);
|
||||
if (notebookToolbarSelectBackgroundColor) {
|
||||
collector.addRule(`.notebookEditor .nb-grid-stack .notebook-cell.active .actionbar { background-color: ${notebookToolbarSelectBackgroundColor};}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
-->
|
||||
<div style="width: 100%; height: 100%; display: flex; flex-flow: column">
|
||||
<div style="flex: 0 0 auto; width: 100%; height: 100%; display: block">
|
||||
<output-area-component *ngIf="cellModel.outputs && cellModel.outputs.length > 0" [cellModel]="cellModel" [activeCellId]="activeCellId">
|
||||
<output-area-component *ngIf="viewCellModel.outputs && viewCellModel.outputs.length > 0" [cellModel]="viewCellModel" [activeCellId]="activeCellId">
|
||||
</output-area-component>
|
||||
<div *ngIf="!cellModel.outputs || !cellModel.outputs.length">
|
||||
<div *ngIf="(!viewCellModel.outputs || !viewCellModel.outputs.length) && !isStdInVisible">
|
||||
{{emptyCellText}}
|
||||
</div>
|
||||
<stdin-component *ngIf="isStdInVisible" [onSendInput]="inputDeferred" [stdIn]="stdIn" [cellModel]="cellModel"></stdin-component>
|
||||
<stdin-component *ngIf="isStdInVisible" [onSendInput]="inputDeferred" [stdIn]="stdIn" [cellModel]="viewCellModel"></stdin-component>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,26 +4,89 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { nb } from 'azdata';
|
||||
import { ChangeDetectorRef, Component, forwardRef, Inject } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, Inject, Input, OnChanges, SimpleChange } from '@angular/core';
|
||||
import { CodeCellComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/codeCell.component';
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { CellModel } from 'sql/workbench/services/notebook/browser/models/cell';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
|
||||
export const CODE_SELECTOR: string = 'views-code-cell-component';
|
||||
|
||||
@Component({
|
||||
selector: CODE_SELECTOR,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: decodeURI(require.toUrl('./notebookViewsCodeCell.component.html'))
|
||||
})
|
||||
|
||||
export class NotebookViewsCodeCellComponent extends CodeCellComponent {
|
||||
public readonly emptyCellText: string = localize('viewsCodeCell.emptyCellText', "Please run this cell to view outputs.");
|
||||
export class NotebookViewsCodeCellComponent extends CodeCellComponent implements OnChanges {
|
||||
@Input() visible: boolean;
|
||||
@Input() modal: boolean;
|
||||
@Input() override cellModel: ICellModel;
|
||||
|
||||
public override stdIn: nb.IStdinMessage;
|
||||
|
||||
constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
|
||||
super(changeRef);
|
||||
}
|
||||
|
||||
get outputs(): nb.ICellOutput[] {
|
||||
return this.cellModel.outputs.filter((output: nb.IDisplayResult) => output.data && output.data['text/plain'] !== '<IPython.core.display.HTML object>');
|
||||
override ngOnInit() {
|
||||
if (this.cellModel) {
|
||||
this._register(this.cellModel.onCollapseStateChanged((state) => {
|
||||
this._changeRef.detectChanges();
|
||||
}));
|
||||
this._register(this.cellModel.onParameterStateChanged((state) => {
|
||||
this._changeRef.detectChanges();
|
||||
}));
|
||||
this._register(this.cellModel.onOutputsChanged(() => {
|
||||
this._changeRef.detectChanges();
|
||||
}));
|
||||
|
||||
// If we have a pre-existing message, listen to that
|
||||
if (this.cellModel?.future?.msg) {
|
||||
this.handleStdIn(this.cellModel.future.msg as nb.IStdinMessage);
|
||||
}
|
||||
|
||||
// Register request handler, cleanup on dispose of this component
|
||||
this.cellModel.setStdInHandler({ handle: (msg) => super.handleStdIn(msg) });
|
||||
this._register({ dispose: () => this.cellModel.setStdInHandler(undefined) });
|
||||
}
|
||||
}
|
||||
|
||||
override ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
||||
for (let propName in changes) {
|
||||
if (propName === 'activeCellId') {
|
||||
let changedProp = changes[propName];
|
||||
super.activeCellId = changedProp.currentValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get outputs(): readonly nb.ICellOutput[] {
|
||||
return this.cellModel.outputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the cell model for Views.
|
||||
*/
|
||||
get viewCellModel(): ICellModel {
|
||||
return new NotebookViewsCellModel(this.cellModel.toJSON(), { notebook: this.cellModel.notebookModel, isTrusted: this.cellModel.trustedMode });
|
||||
}
|
||||
|
||||
get emptyCellText(): string {
|
||||
return localize('viewsCodeCell.emptyCellText', "Please run this cell to view outputs.");
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookViewsCellModel extends CellModel {
|
||||
/**
|
||||
* Override the cell output.
|
||||
* - Hide plain text messages
|
||||
*/
|
||||
public override get outputs(): Array<nb.ICellOutput> {
|
||||
return super.outputs
|
||||
.filter((output: nb.IDisplayResult) => output.data === undefined || output?.data['text/plain'] !== '<IPython.core.display.HTML object>')
|
||||
.map((output: nb.ICellOutput) => ({ ...output }))
|
||||
.map((output: nb.ICellOutput) => { output.metadata = { ...output.metadata }; return output; });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,15 @@
|
||||
-->
|
||||
<div class="nb-grid-stack grid-stack">
|
||||
<ng-container *ngFor="let cell of cells">
|
||||
<view-card-component #wrapper [ready]="loaded" [views]="views" [cell]="cell" [model]="model" (onChange)="onCellChanged($event)"></view-card-component>
|
||||
<view-card-component #wrapper [ready]="loaded" [activeView]="activeView" [meta]="cell.metadata" [cell]="cell" [model]="model" (onChange)="onCellChanged($event)"></view-card-component>
|
||||
<ng-content *ngTemplateOutlet='wrapper.templateRef'></ng-content>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="empty-message" *ngIf="empty && loaded">
|
||||
{{emptyText}}
|
||||
</div>
|
||||
|
||||
<div *ngIf="!loaded" style="display: block; position: absolute; height: 100%; width: 100%; z-index: 9999; background: #fff;">
|
||||
<loading-spinner [loading]="!loaded" [loadingMessage]="loadingMessage"></loading-spinner>
|
||||
</div>
|
||||
|
||||
@@ -36,6 +36,8 @@ export class NotebookViewsGridComponent extends AngularDisposable implements OnI
|
||||
protected _grid: GridStack;
|
||||
protected _gridEnabled: boolean;
|
||||
protected _loaded: boolean;
|
||||
protected _gridView: INotebookView;
|
||||
protected _activeCell: ICellModel;
|
||||
|
||||
protected _options: INotebookViewsGridOptions = {
|
||||
cellHeight: 60
|
||||
@@ -49,11 +51,11 @@ export class NotebookViewsGridComponent extends AngularDisposable implements OnI
|
||||
}
|
||||
|
||||
public get empty(): boolean {
|
||||
return !this._items || !this._items.find(item => item.display);
|
||||
return !this._items || !this._items.find(item => item.visible);
|
||||
}
|
||||
|
||||
public get hiddenItems(): NotebookViewsCardComponent[] {
|
||||
return this._items?.filter(item => !item.display) ?? [];
|
||||
return this._items?.filter(item => !item.visible) ?? [];
|
||||
}
|
||||
|
||||
public get emptyText(): String {
|
||||
@@ -77,17 +79,26 @@ export class NotebookViewsGridComponent extends AngularDisposable implements OnI
|
||||
|
||||
ngAfterContentChecked() {
|
||||
//If activeView has changed or not present, we will destroy the grid in order to rebuild it later.
|
||||
if (!this.activeView || this.activeView.guid !== this.activeView.guid) {
|
||||
if (!this.activeView || this.activeView.guid !== this._gridView?.guid) {
|
||||
if (this._grid) {
|
||||
this.destroyGrid();
|
||||
this._grid = undefined;
|
||||
}
|
||||
}
|
||||
if (this.activeView && this.activeView.guid !== this._gridView?.guid) {
|
||||
this.activeView.initialize();
|
||||
}
|
||||
|
||||
if (this.model?.activeCell?.id !== this._activeCell?.id) {
|
||||
this._activeCell = this.model.activeCell;
|
||||
this.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewChecked() {
|
||||
// If activeView has changed, rebuild the grid
|
||||
if (!this.activeView || this.activeView.guid !== this.activeView.guid) {
|
||||
if (this.activeView && this.activeView.guid !== this._gridView?.guid) {
|
||||
this._gridView = this.activeView;
|
||||
|
||||
if (!this._grid) {
|
||||
this.createGrid();
|
||||
@@ -103,12 +114,15 @@ export class NotebookViewsGridComponent extends AngularDisposable implements OnI
|
||||
}
|
||||
|
||||
private destroyGrid() {
|
||||
this._gridEnabled = false;
|
||||
this._grid.destroy(false);
|
||||
if (this._grid) {
|
||||
this._gridEnabled = false;
|
||||
this._grid.destroy(false);
|
||||
}
|
||||
}
|
||||
|
||||
private createGrid() {
|
||||
const isNew = this.activeView.isNew;
|
||||
|
||||
if (this._grid) {
|
||||
this.destroyGrid();
|
||||
}
|
||||
@@ -191,6 +205,8 @@ export class NotebookViewsGridComponent extends AngularDisposable implements OnI
|
||||
}
|
||||
|
||||
if (e.cell && e.event === 'insert') {
|
||||
const component = this._items.find(x => x.cell.cellGuid === e.cell.cellGuid);
|
||||
|
||||
this.activeView.insertCell(e.cell);
|
||||
|
||||
this.detectChanges();
|
||||
@@ -200,6 +216,18 @@ export class NotebookViewsGridComponent extends AngularDisposable implements OnI
|
||||
this._grid.update(el, { x: 0, y: 0 });
|
||||
this._grid.resizable(el, true);
|
||||
this._grid.movable(el, true);
|
||||
|
||||
component.initialize();
|
||||
}
|
||||
|
||||
if (e.cell && e.event === 'update') {
|
||||
const el = this._grid.getGridItems().find(x => x.getAttribute('data-cell-id') === e.cell.cellGuid);
|
||||
const cellData = this.activeView.getCellMetadata(e.cell);
|
||||
this._grid.update(el, { x: cellData.x, y: cellData.y, w: cellData.width, h: cellData.height });
|
||||
}
|
||||
|
||||
if (e.event === 'active') {
|
||||
this._activeCell = e.cell;
|
||||
}
|
||||
|
||||
this.detectChanges();
|
||||
|
||||
@@ -2,9 +2,305 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
:root .nb-grid-stack .grid-stack-item > .ui-resizable-handle {
|
||||
filter: none;
|
||||
}
|
||||
.nb-grid-stack {
|
||||
position: relative;
|
||||
}
|
||||
.nb-grid-stack.grid-stack-rtl {
|
||||
direction: ltr;
|
||||
}
|
||||
.nb-grid-stack.grid-stack-rtl > .grid-stack-item {
|
||||
direction: rtl;
|
||||
}
|
||||
.nb-grid-stack .grid-stack-placeholder > .placeholder-content {
|
||||
border: 1px dashed lightgray;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
width: auto;
|
||||
z-index: 0 !important;
|
||||
text-align: center;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item {
|
||||
min-width: 8.3333333333%;
|
||||
position: absolute;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nb-grid-stack > .grid-stack-item > .grid-stack-item-content {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
width: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-handle {
|
||||
position: absolute;
|
||||
font-size: 0.1px;
|
||||
display: block;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item.ui-resizable-disabled > .ui-resizable-handle, .nb-grid-stack > .grid-stack-item.ui-resizable-autohide > .ui-resizable-handle {
|
||||
display: none;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item.ui-draggable-dragging, .nb-grid-stack > .grid-stack-item.ui-resizable-resizing {
|
||||
z-index: 100;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item.ui-draggable-dragging > .grid-stack-item-content, .nb-grid-stack > .grid-stack-item.ui-resizable-resizing > .grid-stack-item-content {
|
||||
box-shadow: 1px 4px 6px rgba(0, 0, 0, 0.2);
|
||||
opacity: 0.8;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-se, .nb-grid-stack > .grid-stack-item > .ui-resizable-sw {
|
||||
background-image: url("../media/light/notebook_views_card_handle.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
-o-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-se {
|
||||
-webkit-transform: rotate(-45deg);
|
||||
-moz-transform: rotate(-45deg);
|
||||
-ms-transform: rotate(-45deg);
|
||||
-o-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-nw {
|
||||
cursor: nw-resize;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: 0;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-n {
|
||||
cursor: n-resize;
|
||||
height: 10px;
|
||||
top: 0;
|
||||
left: 25px;
|
||||
right: 25px;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-ne {
|
||||
cursor: ne-resize;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: 0;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-e {
|
||||
cursor: e-resize;
|
||||
width: 10px;
|
||||
top: 15px;
|
||||
bottom: 15px;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-se {
|
||||
cursor: se-resize;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-s {
|
||||
cursor: s-resize;
|
||||
height: 10px;
|
||||
left: 25px;
|
||||
bottom: 0;
|
||||
right: 25px;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-sw {
|
||||
cursor: sw-resize;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
bottom: 0;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-w {
|
||||
cursor: w-resize;
|
||||
width: 10px;
|
||||
top: 15px;
|
||||
bottom: 15px;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item.ui-draggable-dragging > .ui-resizable-handle {
|
||||
display: none !important;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-w='1'] {
|
||||
width: 8.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-x='1'] {
|
||||
left: 8.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-min-w='1'] {
|
||||
min-width: 8.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-max-w='1'] {
|
||||
max-width: 8.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-w='2'] {
|
||||
width: 16.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-x='2'] {
|
||||
left: 16.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-min-w='2'] {
|
||||
min-width: 16.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-max-w='2'] {
|
||||
max-width: 16.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-w='3'] {
|
||||
width: 25%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-x='3'] {
|
||||
left: 25%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-min-w='3'] {
|
||||
min-width: 25%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-max-w='3'] {
|
||||
max-width: 25%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-w='4'] {
|
||||
width: 33.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-x='4'] {
|
||||
left: 33.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-min-w='4'] {
|
||||
min-width: 33.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-max-w='4'] {
|
||||
max-width: 33.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-w='5'] {
|
||||
width: 41.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-x='5'] {
|
||||
left: 41.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-min-w='5'] {
|
||||
min-width: 41.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-max-w='5'] {
|
||||
max-width: 41.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-w='6'] {
|
||||
width: 50%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-x='6'] {
|
||||
left: 50%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-min-w='6'] {
|
||||
min-width: 50%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-max-w='6'] {
|
||||
max-width: 50%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-w='7'] {
|
||||
width: 58.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-x='7'] {
|
||||
left: 58.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-min-w='7'] {
|
||||
min-width: 58.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-max-w='7'] {
|
||||
max-width: 58.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-w='8'] {
|
||||
width: 66.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-x='8'] {
|
||||
left: 66.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-min-w='8'] {
|
||||
min-width: 66.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-max-w='8'] {
|
||||
max-width: 66.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-w='9'] {
|
||||
width: 75%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-x='9'] {
|
||||
left: 75%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-min-w='9'] {
|
||||
min-width: 75%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-max-w='9'] {
|
||||
max-width: 75%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-w='10'] {
|
||||
width: 83.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-x='10'] {
|
||||
left: 83.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-min-w='10'] {
|
||||
min-width: 83.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-max-w='10'] {
|
||||
max-width: 83.3333333333%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-w='11'] {
|
||||
width: 91.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-x='11'] {
|
||||
left: 91.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-min-w='11'] {
|
||||
min-width: 91.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-max-w='11'] {
|
||||
max-width: 91.6666666667%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-w='12'] {
|
||||
width: 100%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-x='12'] {
|
||||
left: 100%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-min-w='12'] {
|
||||
min-width: 100%;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item[gs-max-w='12'] {
|
||||
max-width: 100%;
|
||||
}
|
||||
.nb-grid-stack.grid-stack-1 > .grid-stack-item {
|
||||
min-width: 100%;
|
||||
}
|
||||
.nb-grid-stack.grid-stack-1 > .grid-stack-item[gs-w='1'] {
|
||||
width: 100%;
|
||||
}
|
||||
.nb-grid-stack.grid-stack-1 > .grid-stack-item[gs-x='1'] {
|
||||
left: 100%;
|
||||
}
|
||||
.nb-grid-stack.grid-stack-1 > .grid-stack-item[gs-min-w='1'] {
|
||||
min-width: 100%;
|
||||
}
|
||||
.nb-grid-stack.grid-stack-1 > .grid-stack-item[gs-max-w='1'] {
|
||||
max-width: 100%;
|
||||
}
|
||||
.nb-grid-stack.grid-stack-animate, .nb-grid-stack.grid-stack-animate .grid-stack-item {
|
||||
-webkit-transition: left 0.3s, top 0.3s, height 0.3s, width 0.3s;
|
||||
-moz-transition: left 0.3s, top 0.3s, height 0.3s, width 0.3s;
|
||||
-ms-transition: left 0.3s, top 0.3s, height 0.3s, width 0.3s;
|
||||
-o-transition: left 0.3s, top 0.3s, height 0.3s, width 0.3s;
|
||||
transition: left 0.3s, top 0.3s, height 0.3s, width 0.3s;
|
||||
}
|
||||
.nb-grid-stack.grid-stack-animate .grid-stack-item.ui-draggable-dragging, .nb-grid-stack.grid-stack-animate .grid-stack-item.ui-resizable-resizing, .nb-grid-stack.grid-stack-animate .grid-stack-item.grid-stack-placeholder {
|
||||
-webkit-transition: left 0s, top 0s, height 0s, width 0s;
|
||||
-moz-transition: left 0s, top 0s, height 0s, width 0s;
|
||||
-ms-transition: left 0s, top 0s, height 0s, width 0s;
|
||||
-o-transition: left 0s, top 0s, height 0s, width 0s;
|
||||
transition: left 0s, top 0s, height 0s, width 0s;
|
||||
}
|
||||
.nb-grid-stack.ui-droppable.ui-droppable-over > *:not(.ui-droppable) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
.nb-grid-stack {
|
||||
margin: 0 8px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.nb-grid-stack + .empty-message{
|
||||
@@ -26,7 +322,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item .grid-stack-item-content .grid-stack-content-wrapper-inner {
|
||||
overflow-y: auto;
|
||||
@@ -53,18 +349,14 @@
|
||||
font-weight: 400;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item .grid-stack-item-content .actionbar {
|
||||
top: -14px;
|
||||
z-index: 999;
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item.notebook-cell.active .actionbar {
|
||||
z-index: 1;
|
||||
transform: translate(0px, 7px);
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item.ui-draggable-dragging > .grid-stack-item-content,.nb-grid-stack > .grid-stack-item.ui-resizable-resizing > .grid-stack-item-content{
|
||||
box-shadow:1px 4px 6px rgba(0,0,0,.2);
|
||||
opacity:.8
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-se,.nb-grid-stack > .grid-stack-item > .ui-resizable-sw{
|
||||
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-se,.nb-grid-stack > .grid-stack-item > .ui-resizable-sw {
|
||||
background-image:url("../media/light/notebook_views_card_handle.svg");
|
||||
background-repeat:no-repeat;
|
||||
background-position:center;
|
||||
@@ -102,8 +394,6 @@
|
||||
cursor:se-resize;
|
||||
width:20px;
|
||||
height:20px;
|
||||
right:5px;
|
||||
bottom:0
|
||||
}
|
||||
.nb-grid-stack > .grid-stack-item > .ui-resizable-s{
|
||||
cursor:s-resize;
|
||||
@@ -126,3 +416,11 @@
|
||||
top:15px;
|
||||
bottom:15px
|
||||
}
|
||||
|
||||
.nb-grid-stack text-cell-component .notebook-preview {
|
||||
padding: 0px 0px 0px;
|
||||
}
|
||||
|
||||
.nb-grid-stack output-area-component .notebook-output {
|
||||
padding: 0px 0px 0px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./notebookViewsModal';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { calloutDialogBodyBackground, calloutDialogForeground, calloutDialogInteriorBorder } from 'sql/platform/theme/common/colorRegistry';
|
||||
import { IColorTheme, ICssStyleCollector, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
@Component({
|
||||
selector: 'notebook-views-modal-component',
|
||||
template: `
|
||||
<div [class.modal]="displayInputModal">
|
||||
<div class="content">
|
||||
<div class="title">{{title}}</div>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class NotebookViewsModalComponent {
|
||||
@Input() title: boolean;
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
|
||||
const foreground = theme.getColor(calloutDialogForeground);
|
||||
if (foreground) {
|
||||
collector.addRule(`notebook-views-grid-component .modal { position: absolute; background: ${foreground};}`);
|
||||
}
|
||||
|
||||
const internalBorder = theme.getColor(calloutDialogInteriorBorder);
|
||||
if (internalBorder) {
|
||||
collector.addRule(`notebook-views-grid-component .modal .content { border-color: ${internalBorder}; }`);
|
||||
collector.addRule(`notebook-views-grid-component .modal .content .title { border-color: ${internalBorder}; }`);
|
||||
}
|
||||
|
||||
const bodyBackground = theme.getColor(calloutDialogBodyBackground);
|
||||
if (bodyBackground) {
|
||||
collector.addRule(`notebook-views-grid-component .modal .content { background: ${bodyBackground}; }`);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
notebook-view-component .modal{
|
||||
position: absolute;
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
notebook-view-component .modal .content {
|
||||
height: calc(100% - 60px);
|
||||
width: calc(100% - 60px);
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 30px;
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
notebook-view-component .modal .content .title {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { Modal } from 'sql/workbench/browser/modal/modal';
|
||||
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { attachInputBoxStyler } from 'sql/platform/theme/common/styler';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IInputOptions, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { INotebookView } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews';
|
||||
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
|
||||
|
||||
export class ViewOptionsModal extends Modal {
|
||||
private _submitButton: Button;
|
||||
private _cancelButton: Button;
|
||||
private _viewNameInput: InputBox;
|
||||
|
||||
constructor(
|
||||
private _view: INotebookView,
|
||||
@ILogService logService: ILogService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@ILayoutService layoutService: ILayoutService,
|
||||
@IClipboardService clipboardService: IClipboardService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
||||
@IContextViewService private _contextViewService: IContextViewService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService,
|
||||
) {
|
||||
super(
|
||||
localize("viewOptionsModal.title", "Configure View"),
|
||||
'ViewOptionsModal',
|
||||
telemetryService,
|
||||
layoutService,
|
||||
clipboardService,
|
||||
themeService,
|
||||
logService,
|
||||
textResourcePropertiesService,
|
||||
contextKeyService,
|
||||
{ hasErrors: true, hasSpinner: true }
|
||||
);
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement): void {
|
||||
const formWrapper = DOM.$<HTMLDivElement>('div#notebookviews-options-form');
|
||||
formWrapper.style.padding = '10px';
|
||||
|
||||
DOM.append(container, formWrapper);
|
||||
|
||||
this._viewNameInput = this.createNameInput(formWrapper);
|
||||
|
||||
}
|
||||
|
||||
protected layout(height: number): void {
|
||||
|
||||
}
|
||||
|
||||
protected createNameInput(container: HTMLElement): InputBox {
|
||||
return this.createInputBoxHelper(container, localize('viewOptionsModal.name', "View Name"), this._view.name, {
|
||||
validationOptions: {
|
||||
validation: (value: string) => {
|
||||
if (!value) {
|
||||
return ({ type: MessageType.ERROR, content: localize('viewOptionsModal.missingRequireField', "This field is required.") });
|
||||
}
|
||||
if (this._view.name !== value && !this._view.nameAvailable(value)) {
|
||||
return ({ type: MessageType.ERROR, content: localize('viewOptionsModal.nameTaken', "This view name has already been taken.") });
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
ariaLabel: localize('viewOptionsModal.name', "View Name")
|
||||
});
|
||||
}
|
||||
|
||||
private createInputBoxHelper(container: HTMLElement, label: string, defaultValue: string = '', options?: IInputOptions): InputBox {
|
||||
const inputContainer = DOM.append(container, DOM.$('.dialog-input-section'));
|
||||
DOM.append(inputContainer, DOM.$('.dialog-label')).innerText = label;
|
||||
const input = new InputBox(DOM.append(inputContainer, DOM.$('.dialog-input')), this._contextViewService, options);
|
||||
input.value = defaultValue;
|
||||
return input;
|
||||
}
|
||||
|
||||
public override render() {
|
||||
super.render();
|
||||
|
||||
this._submitButton = this.addFooterButton(localize('save', "Save"), () => this.onSubmitHandler());
|
||||
this._cancelButton = this.addFooterButton(localize('cancel', "Cancel"), () => this.onCancelHandler());
|
||||
|
||||
this._register(attachInputBoxStyler(this._viewNameInput!, this._themeService));
|
||||
this._register(attachButtonStyler(this._submitButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._cancelButton, this._themeService));
|
||||
|
||||
this._register(this._viewNameInput.onDidChange(v => this.validate()));
|
||||
|
||||
attachModalDialogStyler(this, this._themeService);
|
||||
this.validate();
|
||||
}
|
||||
|
||||
private validate() {
|
||||
let valid = true;
|
||||
|
||||
if (!this._viewNameInput.validate()) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
this._submitButton.enabled = valid;
|
||||
}
|
||||
|
||||
private onSubmitHandler() {
|
||||
this._view.name = this._viewNameInput.value;
|
||||
this._view.save();
|
||||
|
||||
this.close();
|
||||
}
|
||||
|
||||
private onCancelHandler() {
|
||||
this.close();
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
return this.hide();
|
||||
}
|
||||
|
||||
public open(): void {
|
||||
this.show();
|
||||
}
|
||||
|
||||
public override dispose(): void {
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user