mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Notebooks: Add Placeholder Cell, Fix Link Styling (#3728)
* Placeholder cell to add new real cells * Fix links in notebooks to show correct color, rely on angular ngif for placeholder * Fix failing test where one cell was expected by default * Remove unnecessary TODO
This commit is contained in:
20
src/sql/parts/notebook/cellViews/placeholder.css
Normal file
20
src/sql/parts/notebook/cellViews/placeholder.css
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
placeholder-cell-component {
|
||||||
|
height: 50px;
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
box-shadow: 0px 4px 6px 0px rgba(0,0,0,0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
placeholder-cell-component .text {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 50px;
|
||||||
|
-webkit-margin-before: 0em;
|
||||||
|
-webkit-margin-after: 0em;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!--
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
-->
|
||||||
|
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
|
||||||
|
<div class="placeholder-cell-component" style="flex: 0 0 auto;">
|
||||||
|
<div class="placeholder-cell-component text">
|
||||||
|
<p>{{clickOn}} <a href="#" (click)="addCell('code')">{{plusCode}}</a> {{or}} <a href="#" (click)="addCell('markdown')">{{plusText}}</a> {{toAddCell}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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!./placeholder';
|
||||||
|
|
||||||
|
import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, SimpleChange, OnChanges } from '@angular/core';
|
||||||
|
import { CellView } from 'sql/parts/notebook/cellViews/interfaces';
|
||||||
|
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
|
||||||
|
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
import { CellType } from 'sql/parts/notebook/models/contracts';
|
||||||
|
|
||||||
|
|
||||||
|
export const PLACEHOLDER_SELECTOR: string = 'placeholder-cell-component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: PLACEHOLDER_SELECTOR,
|
||||||
|
templateUrl: decodeURI(require.toUrl('./placeholderCell.component.html'))
|
||||||
|
})
|
||||||
|
|
||||||
|
export class PlaceholderCellComponent extends CellView implements OnInit, OnChanges {
|
||||||
|
@Input() cellModel: ICellModel;
|
||||||
|
@Input() set model(value: NotebookModel) {
|
||||||
|
this._model = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _model: NotebookModel;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (this.cellModel) {
|
||||||
|
this._register(this.cellModel.onOutputsChanged(() => {
|
||||||
|
this._changeRef.detectChanges();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
||||||
|
}
|
||||||
|
|
||||||
|
get model(): NotebookModel {
|
||||||
|
return this._model;
|
||||||
|
}
|
||||||
|
|
||||||
|
get clickOn(): string {
|
||||||
|
return localize('clickOn','Click on');
|
||||||
|
}
|
||||||
|
|
||||||
|
get plusCode(): string {
|
||||||
|
return localize('plusCode', '+ Code');
|
||||||
|
}
|
||||||
|
|
||||||
|
get or(): string {
|
||||||
|
return localize('or', 'or');
|
||||||
|
}
|
||||||
|
|
||||||
|
get plusText(): string {
|
||||||
|
return localize('plusText', '+ Text');
|
||||||
|
}
|
||||||
|
|
||||||
|
get toAddCell(): string {
|
||||||
|
return localize('toAddCell', 'to add a code or text cell');
|
||||||
|
}
|
||||||
|
|
||||||
|
public addCell(cellType: string): void {
|
||||||
|
let type: CellType = <CellType>cellType;
|
||||||
|
if (!type) {
|
||||||
|
type = 'code';
|
||||||
|
}
|
||||||
|
this._model.addCell(<CellType>cellType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public layout() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -217,7 +217,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
|||||||
}
|
}
|
||||||
let factory = this.notebookOptions.factory;
|
let factory = this.notebookOptions.factory;
|
||||||
// if cells already exist, create them with language info (if it is saved)
|
// if cells already exist, create them with language info (if it is saved)
|
||||||
this._cells = undefined;
|
this._cells = [];
|
||||||
this._defaultLanguageInfo = {
|
this._defaultLanguageInfo = {
|
||||||
name: this._providerId === SQL_NOTEBOOK_PROVIDER ? 'sql' : 'python',
|
name: this._providerId === SQL_NOTEBOOK_PROVIDER ? 'sql' : 'python',
|
||||||
version: ''
|
version: ''
|
||||||
@@ -230,9 +230,6 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
|||||||
this._cells = contents.cells.map(c => factory.createCell(c, { notebook: this, isTrusted: isTrusted }));
|
this._cells = contents.cells.map(c => factory.createCell(c, { notebook: this, isTrusted: isTrusted }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this._cells) {
|
|
||||||
this._cells = [this.createCell(CellTypes.Code)];
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._inErrorState = true;
|
this._inErrorState = true;
|
||||||
throw error;
|
throw error;
|
||||||
@@ -244,7 +241,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public addCell(cellType: CellType, index?: number): ICellModel {
|
public addCell(cellType: CellType, index?: number): ICellModel {
|
||||||
if (this.inErrorState || !this._cells) {
|
if (this.inErrorState) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let cell = this.createCell(cellType);
|
let cell = this.createCell(cellType);
|
||||||
|
|||||||
@@ -15,5 +15,9 @@
|
|||||||
<text-cell-component *ngIf="cell.cellType === 'markdown'" [cellModel]="cell" [model]="model" [activeCellId]="activeCellId">
|
<text-cell-component *ngIf="cell.cellType === 'markdown'" [cellModel]="cell" [model]="model" [activeCellId]="activeCellId">
|
||||||
</text-cell-component>
|
</text-cell-component>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="notebook-cell" *ngIf="!cells.length">
|
||||||
|
<placeholder-cell-component [cellModel]="cell" [model]="model">
|
||||||
|
</placeholder-cell-component>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -250,11 +250,6 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
|||||||
this.updateToolbarComponents(this._model.trustedMode);
|
this.updateToolbarComponents(this._model.trustedMode);
|
||||||
this._modelRegisteredDeferred.resolve(this._model);
|
this._modelRegisteredDeferred.resolve(this._model);
|
||||||
model.backgroundStartSession();
|
model.backgroundStartSession();
|
||||||
// Set first cell as default active cell if user creates new notebook
|
|
||||||
// Otherwise, don't select any cells by default
|
|
||||||
if (this._model && this._model.cells && this._model.cells[0] && this._model.isNewNotebook) {
|
|
||||||
this.selectCell(model.cells[0]);
|
|
||||||
}
|
|
||||||
this._changeRef.detectChanges();
|
this._changeRef.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
margin: 10px 20px 10px;
|
margin: 10px 20px 10px;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notebookEditor .notebook-info-label {
|
.notebookEditor .notebook-info-label {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { CommonModule, APP_BASE_HREF } from '@angular/common';
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
|
||||||
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
|
||||||
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
|
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
|
||||||
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
@@ -22,12 +21,12 @@ import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
|||||||
import { NotebookComponent } from 'sql/parts/notebook/notebook.component';
|
import { NotebookComponent } from 'sql/parts/notebook/notebook.component';
|
||||||
|
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
|
||||||
import { CodeComponent } from 'sql/parts/notebook/cellViews/code.component';
|
import { CodeComponent } from 'sql/parts/notebook/cellViews/code.component';
|
||||||
import { CodeCellComponent } from 'sql/parts/notebook/cellViews/codeCell.component';
|
import { CodeCellComponent } from 'sql/parts/notebook/cellViews/codeCell.component';
|
||||||
import { TextCellComponent } from 'sql/parts/notebook/cellViews/textCell.component';
|
import { TextCellComponent } from 'sql/parts/notebook/cellViews/textCell.component';
|
||||||
import { OutputAreaComponent } from 'sql/parts/notebook/cellViews/outputArea.component';
|
import { OutputAreaComponent } from 'sql/parts/notebook/cellViews/outputArea.component';
|
||||||
import { OutputComponent } from 'sql/parts/notebook/cellViews/output.component';
|
import { OutputComponent } from 'sql/parts/notebook/cellViews/output.component';
|
||||||
|
import { PlaceholderCellComponent } from 'sql/parts/notebook/cellViews/placeholderCell.component';
|
||||||
import LoadingSpinner from 'sql/parts/modelComponents/loadingSpinner.component';
|
import LoadingSpinner from 'sql/parts/modelComponents/loadingSpinner.component';
|
||||||
|
|
||||||
export const NotebookModule = (params, selector: string, instantiationService: IInstantiationService): any => {
|
export const NotebookModule = (params, selector: string, instantiationService: IInstantiationService): any => {
|
||||||
@@ -41,6 +40,7 @@ export const NotebookModule = (params, selector: string, instantiationService: I
|
|||||||
CodeComponent,
|
CodeComponent,
|
||||||
CodeCellComponent,
|
CodeCellComponent,
|
||||||
TextCellComponent,
|
TextCellComponent,
|
||||||
|
PlaceholderCellComponent,
|
||||||
NotebookComponent,
|
NotebookComponent,
|
||||||
ComponentHostDirective,
|
ComponentHostDirective,
|
||||||
OutputAreaComponent,
|
OutputAreaComponent,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'vs/css!./notebook';
|
|||||||
|
|
||||||
import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||||
import { activeContrastBorder, buttonBackground } from 'vs/platform/theme/common/colorRegistry';
|
import { activeContrastBorder, buttonBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||||
|
|
||||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||||
|
|
||||||
@@ -48,4 +48,16 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Styling for all links in notebooks
|
||||||
|
const linkForeground = theme.getColor(textLinkForeground);
|
||||||
|
if (linkForeground) {
|
||||||
|
collector.addRule(`
|
||||||
|
.notebookEditor a:link {
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
color: ${linkForeground};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -172,17 +172,14 @@ output-component .jp-RenderedHTMLCommon u {
|
|||||||
|
|
||||||
output-component .jp-RenderedHTMLCommon a:link {
|
output-component .jp-RenderedHTMLCommon a:link {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--jp-content-link-color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
output-component .jp-RenderedHTMLCommon a:hover {
|
output-component .jp-RenderedHTMLCommon a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
color: var(--jp-content-link-color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
output-component .jp-RenderedHTMLCommon a:visited {
|
output-component .jp-RenderedHTMLCommon a:visited {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--jp-content-link-color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Headings */
|
/* Headings */
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ describe('notebook model', function(): void {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should create single cell if model has no contents', async function(): Promise<void> {
|
it('Should create no cells if model has no contents', async function(): Promise<void> {
|
||||||
// Given an empty notebook
|
// Given an empty notebook
|
||||||
let emptyNotebook: nb.INotebookContents = {
|
let emptyNotebook: nb.INotebookContents = {
|
||||||
cells: [],
|
cells: [],
|
||||||
@@ -125,9 +125,8 @@ describe('notebook model', function(): void {
|
|||||||
let model = new NotebookModel(defaultModelOptions);
|
let model = new NotebookModel(defaultModelOptions);
|
||||||
await model.requestModelLoad();
|
await model.requestModelLoad();
|
||||||
|
|
||||||
// Then I expect to have 1 code cell as the contents
|
// Then I expect to have 0 code cell as the contents
|
||||||
should(model.cells).have.length(1);
|
should(model.cells).have.length(0);
|
||||||
should(model.cells[0].source).be.empty();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should throw if model load fails', async function(): Promise<void> {
|
it('Should throw if model load fails', async function(): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user