move code from parts to contrib (#8319)
@@ -0,0 +1,16 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div style="width: 100%; height: 100%; display: flex; flex-flow: row" (mouseover)="hover=true" (mouseleave)="hover=false">
|
||||
<div #toolbar class="toolbar">
|
||||
</div>
|
||||
<div style="flex: 1 1 auto; flex-flow: column; overflow: hidden;">
|
||||
<div #editor class="editor"></div>
|
||||
<collapse-component *ngIf="cellModel.cellType === 'code' && cellModel.source && cellModel.source.length > 1" [cellModel]="cellModel" [activeCellId]="activeCellId"></collapse-component>
|
||||
</div>
|
||||
<div #moreactions class="moreActions" style="flex: 0 0 auto; display: flex; flex-flow:column;width: 20px; min-height: 20px; max-height: 20px; padding-top: 0px; orientation: portrait">
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,366 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./code';
|
||||
|
||||
import { OnInit, Component, Input, Inject, ElementRef, ViewChild, Output, EventEmitter, OnChanges, SimpleChange, forwardRef, ChangeDetectorRef } from '@angular/core';
|
||||
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { QueryTextEditor } from 'sql/workbench/browser/modelComponents/queryTextEditor';
|
||||
import { CellToggleMoreActions } from 'sql/workbench/contrib/notebook/browser/cellToggleMoreActions';
|
||||
import { ICellModel, notebookConstants, CellExecutionState } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { RunCellAction, CellContext } from 'sql/workbench/contrib/notebook/browser/cellViews/codeActions';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
|
||||
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { SimpleEditorProgressService } from 'vs/editor/standalone/browser/simpleServices';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { CellTypes } from 'sql/workbench/contrib/notebook/common/models/contracts';
|
||||
import { OVERRIDE_EDITOR_THEMING_SETTING } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import * as notebookUtils from 'sql/workbench/contrib/notebook/browser/models/notebookUtils';
|
||||
import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CollapseComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/collapse.component';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
||||
export const CODE_SELECTOR: string = 'code-component';
|
||||
const MARKDOWN_CLASS = 'markdown';
|
||||
|
||||
@Component({
|
||||
selector: CODE_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./code.component.html'))
|
||||
})
|
||||
export class CodeComponent extends AngularDisposable implements OnInit, OnChanges {
|
||||
@ViewChild('toolbar', { read: ElementRef }) private toolbarElement: ElementRef;
|
||||
@ViewChild('moreactions', { read: ElementRef }) private moreActionsElementRef: ElementRef;
|
||||
@ViewChild('editor', { read: ElementRef }) private codeElement: ElementRef;
|
||||
@ViewChild(CollapseComponent) private collapseComponent: CollapseComponent;
|
||||
|
||||
public get cellModel(): ICellModel {
|
||||
return this._cellModel;
|
||||
}
|
||||
|
||||
@Input() public set cellModel(value: ICellModel) {
|
||||
this._cellModel = value;
|
||||
if (this.toolbarElement && value && value.cellType === CellTypes.Markdown) {
|
||||
let nativeToolbar = <HTMLElement>this.toolbarElement.nativeElement;
|
||||
DOM.addClass(nativeToolbar, MARKDOWN_CLASS);
|
||||
}
|
||||
}
|
||||
|
||||
@Output() public onContentChanged = new EventEmitter<void>();
|
||||
|
||||
@Input() set model(value: NotebookModel) {
|
||||
this._model = value;
|
||||
this._register(value.kernelChanged(() => {
|
||||
// On kernel change, need to reevaluate the language for each cell
|
||||
// Refresh based on the cell magic (since this is kernel-dependent) and then update using notebook language
|
||||
this.checkForLanguageMagics();
|
||||
this.updateLanguageMode();
|
||||
}));
|
||||
this._register(value.onValidConnectionSelected(() => {
|
||||
this.updateConnectionState(this.isActive());
|
||||
}));
|
||||
}
|
||||
|
||||
@Input() set activeCellId(value: string) {
|
||||
this._activeCellId = value;
|
||||
}
|
||||
|
||||
@Input() set hover(value: boolean) {
|
||||
this.cellModel.hover = value;
|
||||
if (!this.isActive()) {
|
||||
// Only make a change if we're not active, since this has priority
|
||||
this.toggleActionsVisibility(this.cellModel.hover);
|
||||
}
|
||||
}
|
||||
|
||||
protected _actionBar: Taskbar;
|
||||
private readonly _minimumHeight = 30;
|
||||
private readonly _maximumHeight = 4000;
|
||||
private _cellModel: ICellModel;
|
||||
private _editor: QueryTextEditor;
|
||||
private _editorInput: UntitledEditorInput;
|
||||
private _editorModel: ITextModel;
|
||||
private _model: NotebookModel;
|
||||
private _activeCellId: string;
|
||||
private _cellToggleMoreActions: CellToggleMoreActions;
|
||||
private _layoutEmitter = new Emitter<void>();
|
||||
|
||||
constructor(
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||
@Inject(IInstantiationService) private _instantiationService: IInstantiationService,
|
||||
@Inject(IModelService) private _modelService: IModelService,
|
||||
@Inject(IModeService) private _modeService: IModeService,
|
||||
@Inject(IConfigurationService) private _configurationService: IConfigurationService,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(ILogService) private readonly logService: ILogService
|
||||
) {
|
||||
super();
|
||||
this._cellToggleMoreActions = this._instantiationService.createInstance(CellToggleMoreActions);
|
||||
this._register(Event.debounce(this._layoutEmitter.event, (l, e) => e, 250, /*leading=*/false)
|
||||
(() => this.layout()));
|
||||
// Handle disconnect on removal of the cell, if it was the active cell
|
||||
this._register({ dispose: () => this.updateConnectionState(false) });
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
|
||||
this.updateTheme(this.themeService.getColorTheme());
|
||||
this.initActionBar();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
||||
this.updateLanguageMode();
|
||||
this.updateModel();
|
||||
for (let propName in changes) {
|
||||
if (propName === 'activeCellId') {
|
||||
let changedProp = changes[propName];
|
||||
let isActive = this.cellModel.id === changedProp.currentValue;
|
||||
this.updateConnectionState(isActive);
|
||||
this.toggleActionsVisibility(isActive);
|
||||
if (this._editor) {
|
||||
this._editor.toggleEditorSelected(isActive);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private updateConnectionState(shouldConnect: boolean) {
|
||||
if (this.isSqlCodeCell()) {
|
||||
let cellUri = this.cellModel.cellUri.toString();
|
||||
let connectionService = this.connectionService;
|
||||
if (!shouldConnect && connectionService && connectionService.isConnected(cellUri)) {
|
||||
connectionService.disconnect(cellUri).catch(e => this.logService.error(e));
|
||||
} else if (shouldConnect && this._model.activeConnection && this._model.activeConnection.id !== '-1') {
|
||||
connectionService.connect(this._model.activeConnection, cellUri).catch(e => this.logService.error(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private get connectionService(): IConnectionManagementService {
|
||||
return this._model && this._model.notebookOptions && this._model.notebookOptions.connectionService;
|
||||
}
|
||||
|
||||
private isSqlCodeCell() {
|
||||
return this._model
|
||||
&& this._model.defaultKernel
|
||||
&& this._model.defaultKernel.display_name === notebookConstants.SQL
|
||||
&& this.cellModel.cellType === CellTypes.Code
|
||||
&& this.cellModel.cellUri;
|
||||
}
|
||||
|
||||
private get destroyed(): boolean {
|
||||
return !!(this._changeRef['destroyed']);
|
||||
}
|
||||
|
||||
ngAfterContentInit(): void {
|
||||
if (this.destroyed) {
|
||||
return;
|
||||
}
|
||||
this.createEditor();
|
||||
this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, e => {
|
||||
this._layoutEmitter.fire();
|
||||
}));
|
||||
}
|
||||
|
||||
get model(): NotebookModel {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
get activeCellId(): string {
|
||||
return this._activeCellId;
|
||||
}
|
||||
|
||||
private async createEditor(): Promise<void> {
|
||||
let instantiationService = this._instantiationService.createChild(new ServiceCollection([IProgressService, new SimpleEditorProgressService()]));
|
||||
this._editor = instantiationService.createInstance(QueryTextEditor);
|
||||
this._editor.create(this.codeElement.nativeElement);
|
||||
this._editor.setVisible(true);
|
||||
this._editor.setMinimumHeight(this._minimumHeight);
|
||||
this._editor.setMaximumHeight(this._maximumHeight);
|
||||
|
||||
let uri = this.cellModel.cellUri;
|
||||
let cellModelSource: string;
|
||||
cellModelSource = Array.isArray(this.cellModel.source) ? this.cellModel.source.join('') : this.cellModel.source;
|
||||
this._editorInput = instantiationService.createInstance(UntitledEditorInput, uri, false, this.cellModel.language, cellModelSource, '');
|
||||
await this._editor.setInput(this._editorInput, undefined);
|
||||
this.setFocusAndScroll();
|
||||
|
||||
let untitledEditorModel: UntitledEditorModel = await this._editorInput.resolve();
|
||||
this._editorModel = untitledEditorModel.textEditorModel;
|
||||
|
||||
let isActive = this.cellModel.id === this._activeCellId;
|
||||
this._editor.toggleEditorSelected(isActive);
|
||||
|
||||
// For markdown cells, don't show line numbers unless we're using editor defaults
|
||||
let overrideEditorSetting = this._configurationService.getValue<boolean>(OVERRIDE_EDITOR_THEMING_SETTING);
|
||||
this._editor.hideLineNumbers = (overrideEditorSetting && this.cellModel.cellType === CellTypes.Markdown);
|
||||
|
||||
if (this.destroyed) {
|
||||
// At this point, we may have been disposed (scenario: restoring markdown cell in preview mode).
|
||||
// Exiting early to avoid warnings on registering already disposed items, which causes some churning
|
||||
// due to re-disposing things.
|
||||
// There's no negative impact as at this point the component isn't visible (it was removed from the DOM)
|
||||
return;
|
||||
}
|
||||
this._register(this._editor);
|
||||
this._register(this._editorInput);
|
||||
this._register(this._editorModel.onDidChangeContent(e => {
|
||||
this.cellModel.modelContentChangedEvent = e;
|
||||
|
||||
let originalSourceLength = this.cellModel.source.length;
|
||||
this.cellModel.source = this._editorModel.getValue();
|
||||
if (this._cellModel.isCollapsed && originalSourceLength !== this.cellModel.source.length) {
|
||||
this._cellModel.isCollapsed = false;
|
||||
}
|
||||
this._editor.setHeightToScrollHeight(false, this._cellModel.isCollapsed);
|
||||
|
||||
this.onContentChanged.emit();
|
||||
this.checkForLanguageMagics();
|
||||
}));
|
||||
this._register(this._configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('editor.wordWrap') || e.affectsConfiguration('editor.fontSize')) {
|
||||
this._editor.setHeightToScrollHeight(true, this._cellModel.isCollapsed);
|
||||
}
|
||||
}));
|
||||
this._register(this.model.layoutChanged(() => this._layoutEmitter.fire(), this));
|
||||
this._register(this.cellModel.onExecutionStateChange(event => {
|
||||
if (event === CellExecutionState.Running && !this.cellModel.stdInVisible) {
|
||||
this.setFocusAndScroll();
|
||||
}
|
||||
}));
|
||||
this._register(this.cellModel.onCollapseStateChanged(isCollapsed => {
|
||||
this.onCellCollapse(isCollapsed);
|
||||
}));
|
||||
|
||||
this.layout();
|
||||
|
||||
if (this._cellModel.isCollapsed) {
|
||||
this.onCellCollapse(true);
|
||||
}
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
this._editor.layout(new DOM.Dimension(
|
||||
DOM.getContentWidth(this.codeElement.nativeElement),
|
||||
DOM.getContentHeight(this.codeElement.nativeElement)));
|
||||
this._editor.setHeightToScrollHeight(false, this._cellModel.isCollapsed);
|
||||
}
|
||||
|
||||
protected initActionBar() {
|
||||
let context = new CellContext(this.model, this.cellModel);
|
||||
let runCellAction = this._instantiationService.createInstance(RunCellAction, context);
|
||||
|
||||
let taskbar = <HTMLElement>this.toolbarElement.nativeElement;
|
||||
this._actionBar = new Taskbar(taskbar);
|
||||
this._actionBar.context = context;
|
||||
this._actionBar.setContent([
|
||||
{ action: runCellAction }
|
||||
]);
|
||||
this._cellToggleMoreActions.onInit(this.moreActionsElementRef, this.model, this.cellModel);
|
||||
}
|
||||
|
||||
/// Editor Functions
|
||||
private updateModel() {
|
||||
if (this._editorModel) {
|
||||
let cellModelSource: string;
|
||||
cellModelSource = Array.isArray(this.cellModel.source) ? this.cellModel.source.join('') : this.cellModel.source;
|
||||
this._modelService.updateModel(this._editorModel, cellModelSource);
|
||||
}
|
||||
}
|
||||
|
||||
private checkForLanguageMagics(): void {
|
||||
try {
|
||||
if (!this.cellModel || this.cellModel.cellType !== CellTypes.Code) {
|
||||
return;
|
||||
}
|
||||
if (this._editorModel && this._editor && this._editorModel.getLineCount() > 1) {
|
||||
// Only try to match once we've typed past the first line
|
||||
let magicName = notebookUtils.tryMatchCellMagic(this._editorModel.getLineContent(1));
|
||||
if (magicName) {
|
||||
let kernelName = this._model.clientSession && this._model.clientSession.kernel ? this._model.clientSession.kernel.name : undefined;
|
||||
let magic = this._model.notebookOptions.cellMagicMapper.toLanguageMagic(magicName, kernelName);
|
||||
if (magic && this.cellModel.language !== magic.language) {
|
||||
this.cellModel.setOverrideLanguage(magic.language);
|
||||
this.updateLanguageMode();
|
||||
}
|
||||
} else {
|
||||
this.cellModel.setOverrideLanguage(undefined);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// No-op for now. Should we log?
|
||||
}
|
||||
}
|
||||
|
||||
private updateLanguageMode(): void {
|
||||
if (this._editorModel && this._editor) {
|
||||
let modeValue = this._modeService.create(this.cellModel.language);
|
||||
this._modelService.setMode(this._editorModel, modeValue);
|
||||
}
|
||||
}
|
||||
|
||||
private updateTheme(theme: IColorTheme): void {
|
||||
let toolbarEl = <HTMLElement>this.toolbarElement.nativeElement;
|
||||
toolbarEl.style.borderRightColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
|
||||
|
||||
let moreActionsEl = <HTMLElement>this.moreActionsElementRef.nativeElement;
|
||||
moreActionsEl.style.borderRightColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
|
||||
}
|
||||
|
||||
private setFocusAndScroll(): void {
|
||||
// If offsetParent is null, the element isn't visible
|
||||
// In this case, we don't want a cell to grab focus for an editor that isn't in the foreground.
|
||||
// In addition, ensure that the ownerDocument itself has focus for scenarios where ADS isn't in the foreground
|
||||
let ownerDocument = this._editor.getContainer().ownerDocument;
|
||||
if (this.cellModel.id === this._activeCellId && this._editor.getContainer().offsetParent && ownerDocument && ownerDocument.hasFocus()) {
|
||||
this._editor.focus();
|
||||
this._editor.getContainer().scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
}
|
||||
|
||||
protected isActive() {
|
||||
return this.cellModel && this.cellModel.id === this.activeCellId;
|
||||
}
|
||||
|
||||
protected toggleActionsVisibility(isActiveOrHovered: boolean) {
|
||||
this._cellToggleMoreActions.toggleVisible(!isActiveOrHovered);
|
||||
|
||||
if (this.collapseComponent) {
|
||||
this.collapseComponent.toggleIconVisibility(isActiveOrHovered);
|
||||
}
|
||||
}
|
||||
|
||||
private onCellCollapse(isCollapsed: boolean): void {
|
||||
let editorWidget = this._editor.getControl() as ICodeEditor;
|
||||
if (isCollapsed) {
|
||||
let model = editorWidget.getModel();
|
||||
let totalLines = model.getLineCount();
|
||||
let endColumn = model.getLineMaxColumn(totalLines);
|
||||
editorWidget.setHiddenAreas([{
|
||||
startLineNumber: 2,
|
||||
startColumn: 1,
|
||||
endLineNumber: totalLines,
|
||||
endColumn: endColumn
|
||||
}]);
|
||||
} else {
|
||||
editorWidget.setHiddenAreas([]);
|
||||
}
|
||||
this._editor.setHeightToScrollHeight(false, isCollapsed);
|
||||
}
|
||||
}
|
||||
120
src/sql/workbench/contrib/notebook/browser/cellViews/code.css
Normal file
@@ -0,0 +1,120 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
code-component {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
code-component .toolbar {
|
||||
border-right-width: 1px;
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-flow:column;
|
||||
width: 40px;
|
||||
min-height: 40px;
|
||||
orientation: portrait
|
||||
}
|
||||
|
||||
code-component .toolbar.markdown {
|
||||
display: none;
|
||||
}
|
||||
|
||||
code-component .toolbar .carbon-taskbar {
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
code-component .toolbarIconRun {
|
||||
height: 20px;
|
||||
background-image: url('./media/light/execute_cell.svg');
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.vs-dark code-component .toolbarIconRun,
|
||||
.hc-black code-component .toolbarIconRun {
|
||||
background-image: url('./media/dark/execute_cell_inverse.svg');
|
||||
}
|
||||
|
||||
code-component .toolbarIconRunError {
|
||||
height: 20px;
|
||||
background-image: url('./media/light/execute_cell_error.svg');
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
code-component .toolbarIconStop {
|
||||
height: 20px;
|
||||
background-image: url('./media/light/stop_cell_solidanimation.svg');
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.vs-dark code-component .toolbarIconStop,
|
||||
.hc-black code-component .toolbarIconStop {
|
||||
background-image: url('./media/dark/stop_cell_solidanimation_inverse.svg');
|
||||
}
|
||||
|
||||
code-component .editor {
|
||||
padding: 5px 0px 5px 0px
|
||||
}
|
||||
|
||||
/* overview ruler */
|
||||
code-component .monaco-editor .decorationsOverviewRuler {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
code-component .carbon-taskbar .codicon {
|
||||
background-size: 20px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
code-component .carbon-taskbar .codicon.hideIcon {
|
||||
width: 0px;
|
||||
padding-left: 0px;
|
||||
padding-top: 6px;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
code-component .carbon-taskbar .codicon.hideIcon.execCountTen {
|
||||
margin-left: -2px;
|
||||
}
|
||||
|
||||
code-component .carbon-taskbar .codicon.hideIcon.execCountHundred {
|
||||
margin-left: -6px;
|
||||
}
|
||||
|
||||
code-component .carbon-taskbar.monaco-toolbar .monaco-action-bar.animated .actions-container {
|
||||
padding-left: 10px
|
||||
}
|
||||
|
||||
code-component .hide-component-button {
|
||||
height: 16px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-width: 0px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
code-component .hide-component-button.icon-hide-cell {
|
||||
background-image: url("./media/light/chevron_up.svg");
|
||||
}
|
||||
|
||||
code-component .hide-component-button.icon-show-cell {
|
||||
background-image: url("./media/light/chevron_down.svg");
|
||||
}
|
||||
|
||||
.vs-dark code-component .hide-component-button.icon-hide-cell,
|
||||
.hc-black code-component .hide-component-button.icon-hide-cell {
|
||||
background-image: url("./media/dark/chevron_up_inverse.svg");
|
||||
}
|
||||
|
||||
.vs-dark code-component .hide-component-button.icon-show-cell,
|
||||
.hc-black code-component .hide-component-button.icon-show-cell {
|
||||
background-image: url("./media/dark/chevron_down_inverse.svg");
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { ICellModel, CellExecutionState } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { MultiStateAction, IMultiStateData } from 'sql/workbench/contrib/notebook/browser/notebookActions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { getErrorMessage } from 'vs/base/common/errors';
|
||||
|
||||
let notebookMoreActionMsg = localize('notebook.failed', "Please select active cell and try again");
|
||||
const emptyExecutionCountLabel = '[ ]';
|
||||
|
||||
function hasModelAndCell(context: CellContext, notificationService: INotificationService): boolean {
|
||||
if (!context || !context.model) {
|
||||
return false;
|
||||
}
|
||||
if (context.cell === undefined) {
|
||||
notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: notebookMoreActionMsg
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export class CellContext {
|
||||
constructor(public model: NotebookModel, private _cell?: ICellModel) {
|
||||
}
|
||||
|
||||
public get cell(): ICellModel {
|
||||
return this._cell ? this._cell : this.model.activeCell;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class CellActionBase extends Action {
|
||||
|
||||
constructor(id: string, label: string, icon: string, protected notificationService: INotificationService) {
|
||||
super(id, label, icon);
|
||||
}
|
||||
|
||||
public canRun(context: CellContext): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public run(context: CellContext): Promise<boolean> {
|
||||
if (hasModelAndCell(context, this.notificationService)) {
|
||||
return this.doRun(context).then(() => true);
|
||||
}
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
abstract doRun(context: CellContext): Promise<void>;
|
||||
}
|
||||
|
||||
export class RunCellAction extends MultiStateAction<CellExecutionState> {
|
||||
public static ID = 'notebook.runCell';
|
||||
public static LABEL = 'Run cell';
|
||||
private _executionChangedDisposable: IDisposable;
|
||||
private _context: CellContext;
|
||||
constructor(context: CellContext, @INotificationService private notificationService: INotificationService,
|
||||
@IConnectionManagementService private connectionManagementService: IConnectionManagementService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@ILogService logService: ILogService
|
||||
) {
|
||||
super(RunCellAction.ID, new IMultiStateData<CellExecutionState>([
|
||||
{ key: CellExecutionState.Hidden, value: { label: emptyExecutionCountLabel, className: '', tooltip: '', hideIcon: true } },
|
||||
{ key: CellExecutionState.Stopped, value: { label: '', className: 'toolbarIconRun', tooltip: localize('runCell', "Run cell"), commandId: 'notebook.command.runactivecell' } },
|
||||
{ key: CellExecutionState.Running, value: { label: '', className: 'toolbarIconStop', tooltip: localize('stopCell', "Cancel execution") } },
|
||||
{ key: CellExecutionState.Error, value: { label: '', className: 'toolbarIconRunError', tooltip: localize('errorRunCell', "Error on last run. Click to run again") } },
|
||||
], CellExecutionState.Hidden), keybindingService, logService);
|
||||
this.ensureContextIsUpdated(context);
|
||||
}
|
||||
|
||||
public run(context?: CellContext): Promise<boolean> {
|
||||
return this.doRun(context).then(() => true);
|
||||
}
|
||||
|
||||
public async doRun(context: CellContext): Promise<void> {
|
||||
this.ensureContextIsUpdated(context);
|
||||
if (!this._context) {
|
||||
// TODO should we error?
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this._context.cell.runCell(this.notificationService, this.connectionManagementService);
|
||||
} catch (error) {
|
||||
let message = getErrorMessage(error);
|
||||
this.notificationService.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
private ensureContextIsUpdated(context: CellContext) {
|
||||
if (context && context !== this._context) {
|
||||
if (this._executionChangedDisposable) {
|
||||
this._executionChangedDisposable.dispose();
|
||||
}
|
||||
this._context = context;
|
||||
this.updateStateAndExecutionCount(context.cell.executionState);
|
||||
this._executionChangedDisposable = this._context.cell.onExecutionStateChange((state) => {
|
||||
this.updateStateAndExecutionCount(state);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private updateStateAndExecutionCount(state: CellExecutionState) {
|
||||
let label = emptyExecutionCountLabel;
|
||||
let className = '';
|
||||
if (!types.isUndefinedOrNull(this._context.cell.executionCount)) {
|
||||
label = `[${this._context.cell.executionCount}]`;
|
||||
// Heuristic to try and align correctly independent of execution count length. Moving left margin
|
||||
// back by a few px seems to make things "work" OK, but isn't a super clean solution
|
||||
if (label.length === 4) {
|
||||
className = 'execCountTen';
|
||||
} else if (label.length > 4) {
|
||||
className = 'execCountHundred';
|
||||
}
|
||||
}
|
||||
this.states.updateStateData(CellExecutionState.Hidden, (data) => {
|
||||
data.label = label;
|
||||
data.className = className;
|
||||
});
|
||||
this.updateState(state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div style="width: 100%; height: 100%; display: flex; flex-flow: column">
|
||||
<div style="flex: 0 0 auto;">
|
||||
<code-component [cellModel]="cellModel" [model]="model" [activeCellId]="activeCellId"></code-component>
|
||||
</div>
|
||||
<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>
|
||||
<stdin-component *ngIf="isStdInVisible" [onSendInput]="inputDeferred" [stdIn]="stdIn" [cellModel]="cellModel"></stdin-component>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,113 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { nb } from 'azdata';
|
||||
import { OnInit, Component, Input, Inject, forwardRef, ChangeDetectorRef, SimpleChange, OnChanges, HostListener } from '@angular/core';
|
||||
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
|
||||
|
||||
export const CODE_SELECTOR: string = 'code-cell-component';
|
||||
|
||||
@Component({
|
||||
selector: CODE_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./codeCell.component.html'))
|
||||
})
|
||||
|
||||
export class CodeCellComponent extends CellView implements OnInit, OnChanges {
|
||||
@Input() cellModel: ICellModel;
|
||||
@Input() set model(value: NotebookModel) {
|
||||
this._model = value;
|
||||
}
|
||||
@Input() set activeCellId(value: string) {
|
||||
this._activeCellId = value;
|
||||
}
|
||||
|
||||
@HostListener('document:keydown.escape', ['$event'])
|
||||
handleKeyboardEvent() {
|
||||
this.cellModel.active = false;
|
||||
this._model.updateActiveCell(undefined);
|
||||
}
|
||||
|
||||
private _model: NotebookModel;
|
||||
private _activeCellId: string;
|
||||
|
||||
public inputDeferred: Deferred<string>;
|
||||
public stdIn: nb.IStdinMessage;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.cellModel) {
|
||||
this._register(this.cellModel.onCollapseStateChanged((state) => {
|
||||
this._changeRef.detectChanges();
|
||||
}));
|
||||
this._register(this.cellModel.onOutputsChanged(() => {
|
||||
this._changeRef.detectChanges();
|
||||
}));
|
||||
// Register request handler, cleanup on dispose of this component
|
||||
this.cellModel.setStdInHandler({ handle: (msg) => this.handleStdIn(msg) });
|
||||
this._register({ dispose: () => this.cellModel.setStdInHandler(undefined) });
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
||||
for (let propName in changes) {
|
||||
if (propName === 'activeCellId') {
|
||||
let changedProp = changes[propName];
|
||||
this._activeCellId = changedProp.currentValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get model(): NotebookModel {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
get activeCellId(): string {
|
||||
return this._activeCellId;
|
||||
}
|
||||
|
||||
public layout() {
|
||||
|
||||
}
|
||||
|
||||
handleStdIn(msg: nb.IStdinMessage): void | Thenable<void> {
|
||||
if (msg) {
|
||||
this.stdIn = msg;
|
||||
this.inputDeferred = new Deferred();
|
||||
this.cellModel.stdInVisible = true;
|
||||
this._changeRef.detectChanges();
|
||||
return this.awaitStdIn();
|
||||
}
|
||||
}
|
||||
|
||||
private async awaitStdIn(): Promise<void> {
|
||||
try {
|
||||
let value = await this.inputDeferred.promise;
|
||||
this.cellModel.future.sendInputReply({ value: value });
|
||||
} catch (err) {
|
||||
// Note: don't have a better way to handle completing input request. For now just canceling by sending empty string?
|
||||
this.cellModel.future.sendInputReply({ value: '' });
|
||||
} finally {
|
||||
// Clean up so no matter what, the stdIn request goes away
|
||||
this.stdIn = undefined;
|
||||
this.inputDeferred = undefined;
|
||||
this.cellModel.stdInVisible = false;
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
get isStdInVisible(): boolean {
|
||||
return this.cellModel.stdInVisible;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div style="width: 100%; height: fit-content; display: flex; flex-flow: column">
|
||||
<button #collapseCellButton (click)="toggleCollapsed($event)" class="hide-component-button"></button>
|
||||
<button #expandCellButton (click)="toggleCollapsed($event)" style="display:none" class="hide-component-button icon-show-cell"></button>
|
||||
</div>
|
||||
@@ -0,0 +1,77 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./code';
|
||||
|
||||
import { OnInit, Component, Input, ElementRef, ViewChild, SimpleChange, OnChanges } from '@angular/core';
|
||||
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
|
||||
export const COLLAPSE_SELECTOR: string = 'collapse-component';
|
||||
|
||||
@Component({
|
||||
selector: COLLAPSE_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./collapse.component.html'))
|
||||
})
|
||||
|
||||
export class CollapseComponent extends CellView implements OnInit, OnChanges {
|
||||
@ViewChild('collapseCellButton', { read: ElementRef }) private collapseCellButtonElement: ElementRef;
|
||||
@ViewChild('expandCellButton', { read: ElementRef }) private expandCellButtonElement: ElementRef;
|
||||
|
||||
@Input() cellModel: ICellModel;
|
||||
@Input() activeCellId: string;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
this._register(this.cellModel.onCollapseStateChanged(isCollapsed => {
|
||||
this.handleCellCollapse(isCollapsed);
|
||||
}));
|
||||
this.handleCellCollapse(this.cellModel.isCollapsed);
|
||||
if (this.activeCellId === this.cellModel.id) {
|
||||
this.toggleIconVisibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
private handleCellCollapse(isCollapsed: boolean): void {
|
||||
let collapseButton = <HTMLElement>this.collapseCellButtonElement.nativeElement;
|
||||
let expandButton = <HTMLElement>this.expandCellButtonElement.nativeElement;
|
||||
if (isCollapsed) {
|
||||
collapseButton.style.display = 'none';
|
||||
expandButton.style.display = 'block';
|
||||
} else {
|
||||
collapseButton.style.display = 'block';
|
||||
expandButton.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
public toggleCollapsed(event?: Event): void {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
this.cellModel.isCollapsed = !this.cellModel.isCollapsed;
|
||||
}
|
||||
|
||||
public layout() {
|
||||
|
||||
}
|
||||
|
||||
public toggleIconVisibility(isActiveOrHovered: boolean) {
|
||||
let collapseButton = <HTMLElement>this.collapseCellButtonElement.nativeElement;
|
||||
let buttonClass = 'icon-hide-cell';
|
||||
if (isActiveOrHovered) {
|
||||
collapseButton.classList.add(buttonClass);
|
||||
} else {
|
||||
collapseButton.classList.remove(buttonClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { OnDestroy } from '@angular/core';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
|
||||
export abstract class CellView extends AngularDisposable implements OnDestroy {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
public abstract layout(): void;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Directive, Inject, HostListener, Input } from '@angular/core';
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
|
||||
const knownSchemes = new Set(['http', 'https', 'file', 'mailto', 'data', 'azuredatastudio', 'azuredatastudio-insiders', 'vscode', 'vscode-insiders', 'vscode-resource']);
|
||||
@Directive({
|
||||
selector: '[link-handler]',
|
||||
})
|
||||
export class LinkHandlerDirective {
|
||||
private workbenchFilePath: URI;
|
||||
@Input() isTrusted: boolean;
|
||||
@Input() notebookUri: URI;
|
||||
|
||||
constructor(
|
||||
@Inject(IOpenerService) private readonly openerService: IOpenerService,
|
||||
@Inject(INotebookService) private readonly notebookService: INotebookService
|
||||
) {
|
||||
this.workbenchFilePath = URI.parse(require.toUrl('vs/code/electron-browser/workbench/workbench.html'));
|
||||
}
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
onclick(event: MouseEvent): void {
|
||||
// Note: this logic is taken from the VSCode handling of links in markdown
|
||||
// Untrusted cells will not support commands or raw HTML tags
|
||||
// Finally, we should consider supporting relative paths - created #5238 to track
|
||||
let target: HTMLElement = event.target as HTMLElement;
|
||||
if (target.tagName !== 'A') {
|
||||
target = target.parentElement;
|
||||
if (!target || target.tagName !== 'A') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
const href = target['href'];
|
||||
if (href) {
|
||||
this.handleLink(href);
|
||||
}
|
||||
} catch (err) {
|
||||
onUnexpectedError(err);
|
||||
} finally {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
private handleLink(content: string): void {
|
||||
let uri: URI | undefined;
|
||||
try {
|
||||
uri = URI.parse(content);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
if (uri && this.openerService && this.isSupportedLink(uri)) {
|
||||
if (uri.fragment && uri.fragment.length > 0 && uri.path === this.workbenchFilePath.path) {
|
||||
this.notebookService.navigateTo(this.notebookUri, uri.fragment);
|
||||
} else {
|
||||
this.openerService.open(uri).catch(onUnexpectedError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private isSupportedLink(link: URI): boolean {
|
||||
if (knownSchemes.has(link.scheme)) {
|
||||
return true;
|
||||
}
|
||||
return !!this.isTrusted && link.scheme === 'command';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.1484 3.64844L15.8516 4.35156L8 12.2031L0.148438 4.35156L0.851562 3.64844L8 10.7969L15.1484 3.64844Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 232 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.2734 11.9766L8 4.71094L0.726562 11.9766L0.0234375 11.2734L8 3.28906L15.9766 11.2734L15.2734 11.9766Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 233 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>execute_cell_inverse </title><circle class="cls-1" cx="8" cy="7.92" r="7.76"/><polygon points="10.7 8 6.67 11 6.67 5 10.7 8 10.7 8"/></svg>
|
||||
|
After Width: | Height: | Size: 285 B |
@@ -0,0 +1,16 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs><style>.cls-1{fill:#c1d6e6;}.cls-2{fill:#0078d4;}.cls-3{fill:#fff;}</style></defs>
|
||||
<title>stop_cell_solidanimation_inverse</title>
|
||||
<path class="cls-1" d="M8,16a8,8,0,1,1,8-8A8,8,0,0,1,8,16ZM8,1a7,7,0,1,0,7,7A7,7,0,0,0,8,1Z"/>
|
||||
<path class="cls-2" d="M8.51,0v1A7,7,0,0,1,15,8a6.87,6.87,0,0,1-1.07,3.7l.81.64A7.92,7.92,0,0,0,16,8,8,8,0,0,0,8.51,0Z">
|
||||
<animateTransform attributeName="transform"
|
||||
type="rotate"
|
||||
from="0 8 8"
|
||||
to="360 8 8"
|
||||
begin="0s"
|
||||
dur="1.5s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</path>
|
||||
<circle class="cls-3" cx="8" cy="8" r="6.32"/>
|
||||
<rect x="4.91" y="4.91" width="6.18" height="6.18"/></svg>
|
||||
|
After Width: | Height: | Size: 774 B |
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs2015.css
|
||||
*/
|
||||
/*
|
||||
* Visual Studio 2015 dark style
|
||||
* Author: Nicolas LLOBERA <nllobera@gmail.com>
|
||||
*/
|
||||
|
||||
|
||||
.notebook-preview .hljs-keyword,
|
||||
.notebook-preview .hljs-literal,
|
||||
.notebook-preview .hljs-symbol,
|
||||
.notebook-preview .hljs-name {
|
||||
color: #569CD6;
|
||||
}
|
||||
.notebook-preview .hljs-link {
|
||||
color: #569CD6;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-built_in,
|
||||
.notebook-preview .hljs-type {
|
||||
color: #4EC9B0;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-number,
|
||||
.notebook-preview .hljs-class {
|
||||
color: #B8D7A3;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-string,
|
||||
.notebook-preview .hljs-meta-string {
|
||||
color: #D69D85;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-regexp,
|
||||
.notebook-preview .hljs-template-tag {
|
||||
color: #9A5334;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-subst,
|
||||
.notebook-preview .hljs-function,
|
||||
.notebook-preview .hljs-title,
|
||||
.notebook-preview .hljs-params,
|
||||
.notebook-preview .hljs-formula {
|
||||
color: #DCDCDC;
|
||||
}
|
||||
|
||||
.notebook-preview pre code .hljs-subst,
|
||||
.notebook-preview pre code .hljs-function,
|
||||
.notebook-preview pre code .hljs-title,
|
||||
.notebook-preview pre code .hljs-params,
|
||||
.notebook-preview pre code .hljs-formula {
|
||||
color: var(--vscode-editor-foreground);
|
||||
}
|
||||
|
||||
|
||||
.notebook-preview .hljs-comment,
|
||||
.notebook-preview .hljs-quote {
|
||||
color: #57A64A;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-doctag {
|
||||
color: #608B4E;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-meta,
|
||||
.notebook-preview .hljs-meta-keyword,
|
||||
.notebook-preview .hljs-tag {
|
||||
color: #9B9B9B;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-variable,
|
||||
.notebook-preview .hljs-template-variable {
|
||||
color: #BD63C5;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-attr,
|
||||
.notebook-preview .hljs-attribute,
|
||||
.notebook-preview .hljs-builtin-name {
|
||||
color: #9CDCFE;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-section {
|
||||
color: gold;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*.hljs-code {
|
||||
font-family:'Monospace';
|
||||
}*/
|
||||
|
||||
.notebook-preview .hljs-bullet,
|
||||
.notebook-preview .hljs-selector-tag,
|
||||
.notebook-preview .hljs-selector-id,
|
||||
.notebook-preview .hljs-selector-class,
|
||||
.notebook-preview .hljs-selector-attr,
|
||||
.notebook-preview .hljs-selector-pseudo {
|
||||
color: #D7BA7D;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-addition {
|
||||
background-color: var(--vscode-diffEditor-insertedTextBackground, rgba(155, 185, 85, 0.2));
|
||||
color: rgb(155, 185, 85);
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-deletion {
|
||||
background: var(--vscode-diffEditor-removedTextBackground, rgba(255, 0, 0, 0.2));
|
||||
color: rgb(255, 0, 0);
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
From https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs.css
|
||||
*/
|
||||
/*
|
||||
|
||||
Visual Studio-like style based on original C# coloring by Jason Diamond <jason@diamond.name>
|
||||
|
||||
*/
|
||||
|
||||
.notebook-preview .hljs-function,
|
||||
.notebook-preview .hljs-params {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-comment,
|
||||
.notebook-preview .hljs-quote,
|
||||
.notebook-preview .hljs-variable {
|
||||
color: #008000;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-keyword,
|
||||
.notebook-preview .hljs-selector-tag,
|
||||
.notebook-preview .hljs-built_in,
|
||||
.notebook-preview .hljs-name,
|
||||
.notebook-preview .hljs-tag {
|
||||
color: #00f;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-string,
|
||||
.notebook-preview .hljs-title,
|
||||
.notebook-preview .hljs-section,
|
||||
.notebook-preview .hljs-attribute,
|
||||
.notebook-preview .hljs-literal,
|
||||
.notebook-preview .hljs-template-tag,
|
||||
.notebook-preview .hljs-template-variable,
|
||||
.notebook-preview .hljs-type {
|
||||
color: #a31515;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-selector-attr,
|
||||
.notebook-preview .hljs-selector-pseudo,
|
||||
.notebook-preview .hljs-meta {
|
||||
color: #2b91af;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-doctag {
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-attr {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-symbol,
|
||||
.notebook-preview .hljs-bullet,
|
||||
.notebook-preview .hljs-link {
|
||||
color: #00b0e8;
|
||||
}
|
||||
|
||||
|
||||
.notebook-preview .hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.1484 3.64844L15.8516 4.35156L8 12.2031L0.148438 4.35156L0.851562 3.64844L8 10.7969L15.1484 3.64844Z" fill="#4F4F4F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 234 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.2734 11.9766L8 4.71094L0.726562 11.9766L0.0234375 11.2734L8 3.28906L15.9766 11.2734L15.2734 11.9766Z" fill="#4F4F4F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 235 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>execute_cell</title><circle cx="8" cy="7.92" r="7.76"/><polygon class="cls-1" points="10.7 8 6.67 11 6.67 5 10.7 8 10.7 8"/></svg>
|
||||
|
After Width: | Height: | Size: 276 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#d02e00;}.cls-2{fill:#fff;}</style></defs><title>execute_cell_error</title><circle class="cls-1" cx="8" cy="7.92" r="7.76"/><polygon class="cls-2" points="10.7 8 6.67 11 6.67 5 10.7 8 10.7 8"/></svg>
|
||||
|
After Width: | Height: | Size: 317 B |
@@ -0,0 +1,16 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs><style>.cls-1{fill:#c1d6e6;}.cls-2{fill:#0078d4;}.cls-3{fill:#fff;}</style></defs>
|
||||
<title>stop_cell_solidanimation</title>
|
||||
<path class="cls-1" d="M8,16a8,8,0,1,1,8-8A8,8,0,0,1,8,16ZM8,1a7,7,0,1,0,7,7A7,7,0,0,0,8,1Z"/>
|
||||
<path class="cls-2" d="M8.51,0v1A7,7,0,0,1,15,8a6.87,6.87,0,0,1-1.07,3.7l.81.64A7.92,7.92,0,0,0,16,8,8,8,0,0,0,8.51,0Z">
|
||||
<animateTransform attributeName="transform"
|
||||
type="rotate"
|
||||
from="0 8 8"
|
||||
to="360 8 8"
|
||||
begin="0s"
|
||||
dur="1.5s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</path>
|
||||
<circle cx="8" cy="8" r="6.32"/>
|
||||
<rect class="cls-3" x="4.91" y="4.91" width="6.18" height="6.18"/></svg>
|
||||
|
After Width: | Height: | Size: 766 B |
@@ -0,0 +1,231 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.notebook-preview {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.notebook-preview #code-csp-warning {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
color: white;
|
||||
margin: 16px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-family: sans-serif;
|
||||
background-color:#444444;
|
||||
cursor: pointer;
|
||||
padding: 6px;
|
||||
box-shadow: 1px 1px 1px rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
.notebook-preview #code-csp-warning:hover {
|
||||
text-decoration: none;
|
||||
background-color:#007acc;
|
||||
box-shadow: 2px 2px 2px rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
|
||||
.notebook-preview .scrollBeyondLastLine {
|
||||
margin-bottom: calc(100vh - 22px);
|
||||
}
|
||||
|
||||
.notebook-preview .showEditorSelection .code-line {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.notebook-preview .showEditorSelection .code-active-line:before,
|
||||
.notebook-preview .showEditorSelection .code-line:hover:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -12px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.notebook-preview .showEditorSelection li.code-active-line:before,
|
||||
.notebook-preview .showEditorSelection li.code-line:hover:before {
|
||||
left: -30px;
|
||||
}
|
||||
|
||||
.notebook-preview .showEditorSelection .code-active-line:before {
|
||||
border-left: 3px solid rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.notebook-preview .showEditorSelection .code-line:hover:before {
|
||||
border-left: 3px solid rgba(0, 0, 0, 0.40);
|
||||
}
|
||||
|
||||
.notebook-preview .showEditorSelection .code-line .code-line:hover:before {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.vs-dark .notebook-preview .showEditorSelection .code-active-line:before {
|
||||
border-left: 3px solid rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.vs-dark .notebook-preview .showEditorSelection .code-line:hover:before {
|
||||
border-left: 3px solid rgba(255, 255, 255, 0.60);
|
||||
}
|
||||
|
||||
.vs-dark .notebook-preview .showEditorSelection .code-line .code-line:hover:before {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.hc-black .notebook-preview .showEditorSelection .code-active-line:before {
|
||||
border-left: 3px solid rgba(255, 160, 0, 0.7);
|
||||
}
|
||||
|
||||
.hc-black .notebook-preview .showEditorSelection .code-line:hover:before {
|
||||
border-left: 3px solid rgba(255, 160, 0, 1);
|
||||
}
|
||||
|
||||
.hc-black .notebook-preview .showEditorSelection .code-line .code-line:hover:before {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.notebook-preview img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.notebookEditor a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.notebookEditor a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notebook-preview a:focus,
|
||||
.notebook-preview input:focus,
|
||||
.notebook-preview select:focus,
|
||||
.notebook-preview textarea:focus {
|
||||
outline: 1px solid -webkit-focus-ring-color;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
.notebook-preview hr {
|
||||
border: 0;
|
||||
height: 2px;
|
||||
border-bottom: 2px solid;
|
||||
}
|
||||
|
||||
.notebook-preview h1 {
|
||||
padding-bottom: 0.3em;
|
||||
line-height: 1.2;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
.notebook-preview h1, .notebook-preview h2, .notebook-preview h3 {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.notebook-preview h1 code,
|
||||
.notebook-preview h2 code,
|
||||
.notebook-preview h3 code,
|
||||
.notebook-preview h4 code,
|
||||
.notebook-preview h5 code,
|
||||
.notebook-preview h6 code {
|
||||
font-size: inherit;
|
||||
line-height: auto;
|
||||
}
|
||||
|
||||
.notebook-preview table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.notebook-preview table > thead > tr > th {
|
||||
text-align: left;
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
.notebook-preview table > thead > tr > th,
|
||||
.notebook-preview table > thead > tr > td,
|
||||
.notebook-preview table > tbody > tr > th,
|
||||
.notebook-preview .notebook-preview table > tbody > tr > td {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.notebook-preview table > tbody > tr + tr > td {
|
||||
border-top: 1px solid;
|
||||
}
|
||||
|
||||
.notebook-preview blockquote {
|
||||
margin: 0 7px 0 5px;
|
||||
padding: 0 16px 0 10px;
|
||||
border-left-width: 5px;
|
||||
border-left-style: solid;
|
||||
}
|
||||
|
||||
.notebook-preview code {
|
||||
font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
|
||||
font-size: 12px;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
.notebook-preview pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.notebook-preview .mac code {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.notebook-preview pre:not(.hljs),
|
||||
.notebook-preview pre.hljs code > div {
|
||||
padding: 16px;
|
||||
border-radius: 3px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/** Theming */
|
||||
|
||||
.notebook-preview pre code {
|
||||
color: var(--vscode-editor-foreground);
|
||||
}
|
||||
|
||||
|
||||
.notebook-preview pre {
|
||||
background-color: rgba(220, 220, 220, 0.4);
|
||||
}
|
||||
|
||||
.vs-dark .notebook-preview pre {
|
||||
background-color: rgba(10, 10, 10, 0.4);
|
||||
}
|
||||
|
||||
.hc-black .notebook-preview pre {
|
||||
background-color: rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
.hc-black .notebook-preview h1 {
|
||||
border-color: rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
.notebook-preview table > thead > tr > th {
|
||||
border-color: rgba(0, 0, 0, 0.69);
|
||||
}
|
||||
|
||||
.vs-dark .notebook-preview table > thead > tr > th {
|
||||
border-color: rgba(255, 255, 255, 0.69);
|
||||
}
|
||||
|
||||
.notebook-preview h1,
|
||||
.notebook-preview hr,
|
||||
.notebook-preview table > tbody > tr + tr > td {
|
||||
border-color: rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.vs-dark .notebook-preview h1,
|
||||
.vs-dark .notebook-preview hr,
|
||||
.vs-dark .notebook-preview table > tbody > tr + tr > td {
|
||||
border-color: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
@@ -0,0 +1,476 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
| Copyright (c) Jupyter Development Team.
|
||||
| Distributed under the terms of the Modified BSD License.
|
||||
|----------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
| RenderedText
|
||||
|----------------------------------------------------------------------------*/
|
||||
|
||||
output-component .jp-RenderedText {
|
||||
text-align: left;
|
||||
padding-left: var(--jp-code-padding);
|
||||
font-size: var(--jp-code-font-size);
|
||||
line-height: var(--jp-code-line-height);
|
||||
font-family: var(--jp-code-font-family);
|
||||
}
|
||||
|
||||
output-component .jp-RenderedText pre,
|
||||
.jp-RenderedJavaScript pre,
|
||||
output-component .jp-RenderedHTMLCommon pre {
|
||||
color: var(--jp-content-font-color1);
|
||||
border: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
/* ansi_up creates classed spans for console foregrounds and backgrounds. */
|
||||
output-component .jp-RenderedText pre .ansi-black-fg {
|
||||
color: #3e424d;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-red-fg {
|
||||
color: #e75c58;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-green-fg {
|
||||
color: #00a250;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-yellow-fg {
|
||||
color: #ddb62b;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-blue-fg {
|
||||
color: #208ffb;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-magenta-fg {
|
||||
color: #d160c4;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-cyan-fg {
|
||||
color: #60c6c8;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-white-fg {
|
||||
color: #c5c1b4;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedText pre .ansi-black-bg {
|
||||
background-color: #3e424d;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-red-bg {
|
||||
background-color: #e75c58;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-green-bg {
|
||||
background-color: #00a250;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-yellow-bg {
|
||||
background-color: #ddb62b;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-blue-bg {
|
||||
background-color: #208ffb;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-magenta-bg {
|
||||
background-color: #d160c4;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-cyan-bg {
|
||||
background-color: #60c6c8;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-white-bg {
|
||||
background-color: #c5c1b4;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedText pre .ansi-bright-black-fg {
|
||||
color: #282c36;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-red-fg {
|
||||
color: #b22b31;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-green-fg {
|
||||
color: #007427;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-yellow-fg {
|
||||
color: #b27d12;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-blue-fg {
|
||||
color: #0065ca;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-magenta-fg {
|
||||
color: #a03196;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-cyan-fg {
|
||||
color: #258f8f;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-white-fg {
|
||||
color: #a1a6b2;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedText pre .ansi-bright-black-bg {
|
||||
background-color: #282c36;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-red-bg {
|
||||
background-color: #b22b31;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-green-bg {
|
||||
background-color: #007427;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-yellow-bg {
|
||||
background-color: #b27d12;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-blue-bg {
|
||||
background-color: #0065ca;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-magenta-bg {
|
||||
background-color: #a03196;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-cyan-bg {
|
||||
background-color: #258f8f;
|
||||
}
|
||||
output-component .jp-RenderedText pre .ansi-bright-white-bg {
|
||||
background-color: #a1a6b2;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedText[data-mime-type='application/vnd.jupyter.stderr'] {
|
||||
background: var(--jp-rendermime-error-background);
|
||||
padding-top: var(--jp-code-padding);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
| RenderedLatex
|
||||
|----------------------------------------------------------------------------*/
|
||||
|
||||
.jp-RenderedLatex {
|
||||
color: var(--jp-content-font-color1);
|
||||
font-size: var(--jp-content-font-size1);
|
||||
line-height: var(--jp-content-line-height);
|
||||
}
|
||||
|
||||
/* Left-justify outputs.*/
|
||||
.jp-OutputArea-output.jp-RenderedLatex {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
| RenderedHTML
|
||||
|----------------------------------------------------------------------------*/
|
||||
|
||||
output-component .jp-RenderedHTMLCommon {
|
||||
color: var(--jp-content-font-color1);
|
||||
font-family: var(--jp-content-font-family);
|
||||
font-size: var(--jp-content-font-size1);
|
||||
line-height: var(--jp-content-line-height);
|
||||
/* Give a bit more R padding on Markdown text to keep line lengths reasonable */
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon u {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon a:link {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon a:visited {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Headings */
|
||||
|
||||
output-component .jp-RenderedHTMLCommon h1,
|
||||
output-component .jp-RenderedHTMLCommon h2,
|
||||
output-component .jp-RenderedHTMLCommon h3,
|
||||
output-component .jp-RenderedHTMLCommon h4,
|
||||
output-component .jp-RenderedHTMLCommon h5,
|
||||
output-component .jp-RenderedHTMLCommon h6 {
|
||||
line-height: var(--jp-content-heading-line-height);
|
||||
font-weight: var(--jp-content-heading-font-weight);
|
||||
font-style: normal;
|
||||
margin: var(--jp-content-heading-margin-top) 0
|
||||
var(--jp-content-heading-margin-bottom) 0;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon h1:first-child,
|
||||
output-component .jp-RenderedHTMLCommon h2:first-child,
|
||||
output-component .jp-RenderedHTMLCommon h3:first-child,
|
||||
output-component .jp-RenderedHTMLCommon h4:first-child,
|
||||
output-component .jp-RenderedHTMLCommon h5:first-child,
|
||||
output-component .jp-RenderedHTMLCommon h6:first-child {
|
||||
margin-top: calc(0.5 * var(--jp-content-heading-margin-top));
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon h1:last-child,
|
||||
output-component .jp-RenderedHTMLCommon h2:last-child,
|
||||
output-component .jp-RenderedHTMLCommon h3:last-child,
|
||||
output-component .jp-RenderedHTMLCommon h4:last-child,
|
||||
output-component .jp-RenderedHTMLCommon h5:last-child,
|
||||
output-component .jp-RenderedHTMLCommon h6:last-child {
|
||||
margin-bottom: calc(0.5 * var(--jp-content-heading-margin-bottom));
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon h1 {
|
||||
font-size: var(--jp-content-font-size5);
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon h2 {
|
||||
font-size: var(--jp-content-font-size4);
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon h3 {
|
||||
font-size: var(--jp-content-font-size3);
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon h4 {
|
||||
font-size: var(--jp-content-font-size2);
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon h5 {
|
||||
font-size: var(--jp-content-font-size1);
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon h6 {
|
||||
font-size: var(--jp-content-font-size0);
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
|
||||
output-component .jp-RenderedHTMLCommon ul:not(.list-inline),
|
||||
output-component .jp-RenderedHTMLCommon ol:not(.list-inline) {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon ul {
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon ul ul {
|
||||
list-style: square;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon ul ul ul {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon ol {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon ol ol {
|
||||
list-style: upper-alpha;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon ol ol ol {
|
||||
list-style: lower-alpha;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon ol ol ol ol {
|
||||
list-style: lower-roman;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon ol ol ol ol ol {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon ol,
|
||||
output-component .jp-RenderedHTMLCommon ul {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon ul ul,
|
||||
output-component .jp-RenderedHTMLCommon ul ol,
|
||||
output-component .jp-RenderedHTMLCommon ol ul,
|
||||
output-component .jp-RenderedHTMLCommon ol ol {
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon hr {
|
||||
color: var(--jp-border-color2);
|
||||
background-color: var(--jp-border-color1);
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon > pre {
|
||||
margin: 1.5em 2em;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon pre,
|
||||
output-component .jp-RenderedHTMLCommon code {
|
||||
border: 0;
|
||||
background-color: var(--jp-layout-color0);
|
||||
color: var(--jp-content-font-color1);
|
||||
font-family: var(--jp-code-font-family);
|
||||
font-size: inherit;
|
||||
line-height: var(--jp-code-line-height);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon p > code {
|
||||
background-color: var(--jp-layout-color2);
|
||||
padding: 1px 5px;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
|
||||
output-component .jp-RenderedHTMLCommon table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border: none;
|
||||
color: var(--jp-ui-font-color1);
|
||||
font-size: 12px;
|
||||
table-layout: auto;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon thead {
|
||||
border-bottom: var(--jp-border-width) solid var(--jp-border-color1);
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon td,
|
||||
output-component .jp-RenderedHTMLCommon th,
|
||||
output-component .jp-RenderedHTMLCommon tr {
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
padding: 0.5em 0.5em;
|
||||
line-height: normal;
|
||||
white-space: normal;
|
||||
max-width: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.jp-RenderedMarkdown.jp-RenderedHTMLCommon td,
|
||||
.jp-RenderedMarkdown.jp-RenderedHTMLCommon th {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
output-component th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon tbody tr:nth-child(odd) {
|
||||
background: var(--jp-layout-color0);
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon tbody tr:nth-child(even) {
|
||||
background: var(--jp-rendermime-table-row-background);
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon tbody tr:hover {
|
||||
background: var(--jp-rendermime-table-row-hover-background);
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon table {
|
||||
margin-bottom: 1em;
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon p {
|
||||
text-align: left;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon p {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon img {
|
||||
-moz-force-broken-image-icon: 1;
|
||||
}
|
||||
|
||||
/* Restrict to direct children as other images could be nested in other content. */
|
||||
output-component .jp-RenderedHTMLCommon > img {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
/* Change color behind transparent images if they need it... */
|
||||
[data-theme-light='false'] .jp-RenderedImage img.jp-needs-light-background {
|
||||
background-color: var(--jp-inverse-layout-color1);
|
||||
}
|
||||
[data-theme-light='true'] .jp-RenderedImage img.jp-needs-dark-background {
|
||||
background-color: var(--jp-inverse-layout-color1);
|
||||
}
|
||||
/* ...or leave it untouched if they don't */
|
||||
[data-theme-light='false'] .jp-RenderedImage img.jp-needs-dark-background {
|
||||
}
|
||||
[data-theme-light='true'] .jp-RenderedImage img.jp-needs-light-background {
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon img,
|
||||
.jp-RenderedImage img,
|
||||
output-component .jp-RenderedHTMLCommon svg,
|
||||
.jp-RenderedSVG svg {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon img.jp-mod-unconfined,
|
||||
.jp-RenderedImage img.jp-mod-unconfined,
|
||||
output-component .jp-RenderedHTMLCommon svg.jp-mod-unconfined,
|
||||
.jp-RenderedSVG svg.jp-mod-unconfined {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon .alert {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon blockquote {
|
||||
margin: 1em 2em;
|
||||
padding: 0 1em;
|
||||
border-left: 5px solid var(--jp-border-color2);
|
||||
}
|
||||
|
||||
a.jp-InternalAnchorLink {
|
||||
visibility: hidden;
|
||||
margin-left: 8px;
|
||||
color: var(--md-blue-800);
|
||||
}
|
||||
|
||||
h1:hover .jp-InternalAnchorLink,
|
||||
h2:hover .jp-InternalAnchorLink,
|
||||
h3:hover .jp-InternalAnchorLink,
|
||||
h4:hover .jp-InternalAnchorLink,
|
||||
h5:hover .jp-InternalAnchorLink,
|
||||
h6:hover .jp-InternalAnchorLink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Most direct children of .jp-RenderedHTMLCommon have a margin-bottom of 1.0.
|
||||
* At the bottom of cells this is a bit too much as there is also spacing
|
||||
* between cells. Going all the way to 0 gets too tight between markdown and
|
||||
* code cells.
|
||||
*/
|
||||
output-component .jp-RenderedHTMLCommon > *:last-child {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
| RenderedPDF
|
||||
|----------------------------------------------------------------------------*/
|
||||
|
||||
.jp-RenderedPDF {
|
||||
font-size: var(--jp-ui-font-size1);
|
||||
}
|
||||
|
||||
plotly-output .plotly-wrapper {
|
||||
display: block;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
output-component .grid-panel .action-label.codicon {
|
||||
min-width: 16px;
|
||||
margin-right: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
|
||||
<div style="flex: 0 0 auto; user-select: none;">
|
||||
<div #output class="output-userselect">
|
||||
<ng-template component-host>
|
||||
</ng-template>
|
||||
<pre *ngIf="hasError" class="p-Widget jp-RenderedText">{{errorText}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,187 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./code';
|
||||
import 'vs/css!./media/output';
|
||||
|
||||
import { OnInit, Component, Input, Inject, ElementRef, ViewChild, SimpleChange, AfterViewInit, forwardRef, ChangeDetectorRef, ComponentRef, ComponentFactoryResolver } from '@angular/core';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { nb } from 'azdata';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import * as outputProcessor from 'sql/workbench/contrib/notebook/browser/models/outputProcessor';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { ComponentHostDirective } from 'sql/workbench/contrib/dashboard/browser/core/componentHost.directive';
|
||||
import { Extensions, IMimeComponent, IMimeComponentRegistry } from 'sql/workbench/contrib/notebook/browser/outputs/mimeRegistry';
|
||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { localize } from 'vs/nls';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { getErrorMessage } from 'vs/base/common/errors';
|
||||
|
||||
export const OUTPUT_SELECTOR: string = 'output-component';
|
||||
const USER_SELECT_CLASS = 'actionselect';
|
||||
|
||||
const componentRegistry = <IMimeComponentRegistry>Registry.as(Extensions.MimeComponentContribution);
|
||||
|
||||
@Component({
|
||||
selector: OUTPUT_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./output.component.html'))
|
||||
})
|
||||
export class OutputComponent extends AngularDisposable implements OnInit, AfterViewInit {
|
||||
@ViewChild('output', { read: ElementRef }) private outputElement: ElementRef;
|
||||
@ViewChild(ComponentHostDirective) componentHost: ComponentHostDirective;
|
||||
@Input() cellOutput: nb.ICellOutput;
|
||||
@Input() cellModel: ICellModel;
|
||||
|
||||
private _trusted: boolean;
|
||||
private _initialized: boolean = false;
|
||||
private _activeCellId: string;
|
||||
private _componentInstance: IMimeComponent;
|
||||
public errorText: string;
|
||||
|
||||
constructor(
|
||||
@Inject(IThemeService) private _themeService: IThemeService,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeref: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) private _ref: ElementRef,
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._register(this._themeService.onThemeChange(event => this.updateTheme(event)));
|
||||
this.loadComponent();
|
||||
this.layout();
|
||||
this._initialized = true;
|
||||
this._register(Event.debounce(this.cellModel.notebookModel.layoutChanged, (l, e) => e, 50, /*leading=*/false)
|
||||
(() => this.layout()));
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.updateTheme(this._themeService.getTheme());
|
||||
}
|
||||
|
||||
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
||||
for (let propName in changes) {
|
||||
if (propName === 'activeCellId') {
|
||||
this.toggleUserSelect(this.isActive());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private toggleUserSelect(userSelect: boolean): void {
|
||||
if (!this.nativeOutputElement) {
|
||||
return;
|
||||
}
|
||||
if (userSelect) {
|
||||
DOM.addClass(this.nativeOutputElement, USER_SELECT_CLASS);
|
||||
} else {
|
||||
DOM.removeClass(this.nativeOutputElement, USER_SELECT_CLASS);
|
||||
}
|
||||
}
|
||||
|
||||
private get nativeOutputElement() {
|
||||
return this.outputElement ? this.outputElement.nativeElement : undefined;
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
if (this.componentInstance && this.componentInstance.layout) {
|
||||
this.componentInstance.layout();
|
||||
}
|
||||
}
|
||||
|
||||
private get componentInstance(): IMimeComponent {
|
||||
if (!this._componentInstance) {
|
||||
this.loadComponent();
|
||||
}
|
||||
return this._componentInstance;
|
||||
}
|
||||
|
||||
get trustedMode(): boolean {
|
||||
return this._trusted;
|
||||
}
|
||||
|
||||
@Input() set trustedMode(value: boolean) {
|
||||
this._trusted = value;
|
||||
if (this._initialized) {
|
||||
this.layout();
|
||||
}
|
||||
}
|
||||
|
||||
@Input() set activeCellId(value: string) {
|
||||
this._activeCellId = value;
|
||||
}
|
||||
|
||||
get activeCellId(): string {
|
||||
return this._activeCellId;
|
||||
}
|
||||
|
||||
protected isActive() {
|
||||
return this.cellModel && this.cellModel.id === this.activeCellId;
|
||||
}
|
||||
|
||||
public hasError(): boolean {
|
||||
return !types.isUndefinedOrNull(this.errorText);
|
||||
}
|
||||
private updateTheme(theme: ITheme): void {
|
||||
let el = <HTMLElement>this._ref.nativeElement;
|
||||
let backgroundColor = theme.getColor(colors.editorBackground, true);
|
||||
let foregroundColor = theme.getColor(themeColors.SIDE_BAR_FOREGROUND, true);
|
||||
|
||||
if (backgroundColor) {
|
||||
el.style.backgroundColor = backgroundColor.toString();
|
||||
}
|
||||
if (foregroundColor) {
|
||||
el.style.color = foregroundColor.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private loadComponent(): void {
|
||||
let options = outputProcessor.getBundleOptions({ value: this.cellOutput, trusted: this.trustedMode });
|
||||
options.themeService = this._themeService;
|
||||
let mimeType = componentRegistry.getPreferredMimeType(
|
||||
options.data,
|
||||
options.trusted ? 'any' : 'ensure'
|
||||
);
|
||||
this.errorText = undefined;
|
||||
if (!mimeType) {
|
||||
this.errorText = localize('noMimeTypeFound', "No {0}renderer could be found for output. It has the following MIME types: {1}",
|
||||
options.trusted ? '' : localize('safe', "safe "),
|
||||
Object.keys(options.data).join(', '));
|
||||
return;
|
||||
}
|
||||
let selector = componentRegistry.getCtorFromMimeType(mimeType);
|
||||
if (!selector) {
|
||||
this.errorText = localize('noSelectorFound', "No component could be found for selector {0}", mimeType);
|
||||
return;
|
||||
}
|
||||
|
||||
let componentFactory = this._componentFactoryResolver.resolveComponentFactory(selector);
|
||||
|
||||
let viewContainerRef = this.componentHost.viewContainerRef;
|
||||
viewContainerRef.clear();
|
||||
|
||||
let componentRef: ComponentRef<IMimeComponent>;
|
||||
try {
|
||||
componentRef = viewContainerRef.createComponent(componentFactory, 0);
|
||||
this._componentInstance = componentRef.instance;
|
||||
this._componentInstance.mimeType = mimeType;
|
||||
this._componentInstance.cellModel = this.cellModel;
|
||||
this._componentInstance.bundleOptions = options;
|
||||
this._changeref.detectChanges();
|
||||
let el = <HTMLElement>componentRef.location.nativeElement;
|
||||
|
||||
// set widget styles to conform to its box
|
||||
el.style.overflow = 'hidden';
|
||||
el.style.position = 'relative';
|
||||
} catch (e) {
|
||||
this.errorText = localize('componentRenderError', "Error rendering component: {0}", getErrorMessage(e));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 #outputarea link-handler [isTrusted]="isTrusted" [notebookUri]="notebookUri" class="notebook-output" style="flex: 0 0 auto;">
|
||||
<output-component *ngFor="let output of cellModel.outputs" [cellOutput]="output" [trustedMode] = "cellModel.trustedMode" [cellModel]="cellModel" [activeCellId]="activeCellId">
|
||||
</output-component>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,77 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./code';
|
||||
import 'vs/css!./outputArea';
|
||||
import { OnInit, Component, Input, Inject, ElementRef, ViewChild, forwardRef, ChangeDetectorRef } from '@angular/core';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export const OUTPUT_AREA_SELECTOR: string = 'output-area-component';
|
||||
|
||||
@Component({
|
||||
selector: OUTPUT_AREA_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./outputArea.component.html'))
|
||||
})
|
||||
export class OutputAreaComponent extends AngularDisposable implements OnInit {
|
||||
@ViewChild('outputarea', { read: ElementRef }) private outputArea: ElementRef;
|
||||
@Input() cellModel: ICellModel;
|
||||
|
||||
private _activeCellId: string;
|
||||
|
||||
constructor(
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
|
||||
this.updateTheme(this.themeService.getColorTheme());
|
||||
if (this.cellModel) {
|
||||
this._register(this.cellModel.onOutputsChanged(e => {
|
||||
if (!(this._changeRef['destroyed'])) {
|
||||
this._changeRef.detectChanges();
|
||||
if (e && e.shouldScroll) {
|
||||
this.setFocusAndScroll(this.outputArea.nativeElement);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
public get isTrusted(): boolean {
|
||||
return this.cellModel.trustedMode;
|
||||
}
|
||||
|
||||
public get notebookUri(): URI {
|
||||
return this.cellModel.notebookModel.notebookUri;
|
||||
}
|
||||
|
||||
private setFocusAndScroll(node: HTMLElement): void {
|
||||
// If offsetParent is null, the element isn't visible
|
||||
// In this case, we don't want a cell to grab focus for an editor that isn't in the foreground
|
||||
if (node && node.offsetParent) {
|
||||
node.focus();
|
||||
node.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
}
|
||||
|
||||
@Input() set activeCellId(value: string) {
|
||||
this._activeCellId = value;
|
||||
}
|
||||
|
||||
get activeCellId(): string {
|
||||
return this._activeCellId;
|
||||
}
|
||||
|
||||
private updateTheme(theme: IColorTheme): void {
|
||||
let outputElement = <HTMLElement>this.outputArea.nativeElement;
|
||||
outputElement.style.borderTopColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
output-area-component {
|
||||
display: block;
|
||||
}
|
||||
|
||||
output-area-component .notebook-output {
|
||||
border-top-width: 0px;
|
||||
user-select: text;
|
||||
padding: 5px 20px 0px;
|
||||
}
|
||||
|
||||
.output-userselect.actionselect {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.output-userselect pre{
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
@@ -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', $event)">{{plusCode}}</a> {{or}} <a href="#" (click)="addCell('markdown', $event)">{{plusText}}</a> {{toAddCell}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,85 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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, ChangeDetectorRef, SimpleChange, OnChanges } from '@angular/core';
|
||||
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { localize } from 'vs/nls';
|
||||
import { CellType } from 'sql/workbench/contrib/notebook/common/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, event?: Event): void {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
let type: CellType = <CellType>cellType;
|
||||
if (!type) {
|
||||
type = 'code';
|
||||
}
|
||||
this._model.addCell(type);
|
||||
}
|
||||
|
||||
public layout() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./stdin';
|
||||
|
||||
import {
|
||||
Component, Input, Inject,
|
||||
ViewChild, ElementRef, AfterViewInit, HostListener
|
||||
} from '@angular/core';
|
||||
import { nb } from 'azdata';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
import { IInputOptions } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { inputBackground, inputBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { attachInputBoxStyler } from 'sql/platform/theme/common/styler';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { ICellModel, CellExecutionState } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
|
||||
export const STDIN_SELECTOR: string = 'stdin-component';
|
||||
@Component({
|
||||
selector: STDIN_SELECTOR,
|
||||
template: `
|
||||
<div class="prompt">{{prompt}}</div>
|
||||
<div #input class="input"></div>
|
||||
`
|
||||
})
|
||||
export class StdInComponent extends AngularDisposable implements AfterViewInit {
|
||||
private _input: InputBox;
|
||||
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
|
||||
|
||||
@Input() stdIn: nb.IStdinMessage;
|
||||
@Input() onSendInput: Deferred<string>;
|
||||
@Input() cellModel: ICellModel;
|
||||
|
||||
|
||||
constructor(
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||
@Inject(IContextViewService) private contextViewService: IContextViewService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
let inputOptions: IInputOptions = {
|
||||
placeholder: '',
|
||||
ariaLabel: this.prompt
|
||||
};
|
||||
this._input = new InputBox(this._inputContainer.nativeElement, this.contextViewService, inputOptions);
|
||||
if (this.password) {
|
||||
this._input.inputElement.type = 'password';
|
||||
}
|
||||
this._register(this._input);
|
||||
this._register(attachInputBoxStyler(this._input, this.themeService, {
|
||||
inputValidationInfoBackground: inputBackground,
|
||||
inputValidationInfoBorder: inputBorder,
|
||||
}));
|
||||
if (this.cellModel) {
|
||||
this._register(this.cellModel.onExecutionStateChange((status) => this.handleExecutionChange(status)));
|
||||
}
|
||||
this._input.focus();
|
||||
}
|
||||
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
public handleKeyboardInput(event: KeyboardEvent): void {
|
||||
let e = new StandardKeyboardEvent(event);
|
||||
switch (e.keyCode) {
|
||||
case KeyCode.Enter:
|
||||
// Indi
|
||||
if (this.onSendInput) {
|
||||
this.onSendInput.resolve(this._input.value);
|
||||
}
|
||||
e.stopPropagation();
|
||||
break;
|
||||
case KeyCode.Escape:
|
||||
if (this.onSendInput) {
|
||||
this.onSendInput.reject('');
|
||||
}
|
||||
e.stopPropagation();
|
||||
break;
|
||||
default:
|
||||
// No-op
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleExecutionChange(status: CellExecutionState): void {
|
||||
if (status !== CellExecutionState.Running && this.onSendInput) {
|
||||
this.onSendInput.reject('');
|
||||
}
|
||||
}
|
||||
|
||||
private get prompt(): string {
|
||||
if (this.stdIn && this.stdIn.content && this.stdIn.content.prompt) {
|
||||
return this.stdIn.content.prompt;
|
||||
}
|
||||
return localize('stdInLabel', "StdIn:");
|
||||
}
|
||||
|
||||
private get password(): boolean {
|
||||
return this.stdIn && this.stdIn.content && this.stdIn.content.password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
stdin-component {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
padding: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
stdin-component .prompt {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
stdin-component .input {
|
||||
flex: 1 1 auto;
|
||||
padding-left: 10px;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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" (mouseover)="hover=true" (mouseleave)="hover=false">
|
||||
<div class="notebook-text" style="flex: 0 0 auto;">
|
||||
<code-component *ngIf="isEditMode" [cellModel]="cellModel" (onContentChanged)="handleContentChanged()" [model]="model" [activeCellId]="activeCellId">
|
||||
</code-component>
|
||||
</div>
|
||||
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: row">
|
||||
<div #preview link-handler [isTrusted]="isTrusted" [notebookUri]="notebookUri" class="notebook-preview" style="flex: 1 1 auto" (dblclick)="toggleEditMode()">
|
||||
</div>
|
||||
<div #moreactions class="moreActions" style="flex: 0 0 auto; display: flex; flex-flow:column;width: 20px; min-height: 20px; max-height: 20px; padding-top: 0px; orientation: portrait">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,262 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./textCell';
|
||||
import 'vs/css!./media/markdown';
|
||||
import 'vs/css!./media/highlight';
|
||||
|
||||
import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, OnChanges, SimpleChange, HostListener } from '@angular/core';
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IMarkdownRenderResult } from 'vs/editor/contrib/markdown/markdownRenderer';
|
||||
import { NotebookMarkdownRenderer } from 'sql/workbench/contrib/notebook/browser/outputs/notebookMarkdown';
|
||||
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { ISanitizer, defaultSanitizer } from 'sql/workbench/contrib/notebook/browser/outputs/sanitizer';
|
||||
import { CellToggleMoreActions } from 'sql/workbench/contrib/notebook/browser/cellToggleMoreActions';
|
||||
import { useInProcMarkdown, convertVscodeResourceToFileInSubDirectories } from 'sql/workbench/contrib/notebook/browser/models/notebookUtils';
|
||||
|
||||
export const TEXT_SELECTOR: string = 'text-cell-component';
|
||||
const USER_SELECT_CLASS = 'actionselect';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: TEXT_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./textCell.component.html'))
|
||||
})
|
||||
export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
@ViewChild('preview', { read: ElementRef }) private output: ElementRef;
|
||||
@ViewChild('moreactions', { read: ElementRef }) private moreActionsElementRef: ElementRef;
|
||||
@Input() cellModel: ICellModel;
|
||||
|
||||
@Input() set model(value: NotebookModel) {
|
||||
this._model = value;
|
||||
}
|
||||
|
||||
@Input() set activeCellId(value: string) {
|
||||
this._activeCellId = value;
|
||||
}
|
||||
|
||||
@Input() set hover(value: boolean) {
|
||||
this._hover = value;
|
||||
if (!this.isActive()) {
|
||||
// Only make a change if we're not active, since this has priority
|
||||
this.updateMoreActions();
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('document:keydown.escape', ['$event'])
|
||||
handleKeyboardEvent() {
|
||||
if (this.isEditMode) {
|
||||
this.toggleEditMode(false);
|
||||
}
|
||||
this.cellModel.active = false;
|
||||
this._model.updateActiveCell(undefined);
|
||||
}
|
||||
|
||||
private _content: string | string[];
|
||||
private _lastTrustedMode: boolean;
|
||||
private isEditMode: boolean;
|
||||
private _sanitizer: ISanitizer;
|
||||
private _model: NotebookModel;
|
||||
private _activeCellId: string;
|
||||
private readonly _onDidClickLink = this._register(new Emitter<URI>());
|
||||
public readonly onDidClickLink = this._onDidClickLink.event;
|
||||
private _cellToggleMoreActions: CellToggleMoreActions;
|
||||
private _hover: boolean;
|
||||
private markdownRenderer: NotebookMarkdownRenderer;
|
||||
private markdownResult: IMarkdownRenderResult;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(IInstantiationService) private _instantiationService: IInstantiationService,
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||
@Inject(ICommandService) private _commandService: ICommandService,
|
||||
@Inject(IConfigurationService) private configurationService: IConfigurationService,
|
||||
|
||||
) {
|
||||
super();
|
||||
this.isEditMode = true;
|
||||
this._cellToggleMoreActions = this._instantiationService.createInstance(CellToggleMoreActions);
|
||||
this.markdownRenderer = this._instantiationService.createInstance(NotebookMarkdownRenderer);
|
||||
this._register(toDisposable(() => {
|
||||
if (this.markdownResult) {
|
||||
this.markdownResult.dispose();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
//Gets sanitizer from ISanitizer interface
|
||||
private get sanitizer(): ISanitizer {
|
||||
if (this._sanitizer) {
|
||||
return this._sanitizer;
|
||||
}
|
||||
return this._sanitizer = defaultSanitizer;
|
||||
}
|
||||
|
||||
get model(): NotebookModel {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
get activeCellId(): string {
|
||||
return this._activeCellId;
|
||||
}
|
||||
|
||||
private setLoading(isLoading: boolean): void {
|
||||
this.cellModel.loaded = !isLoading;
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
|
||||
this.updateTheme(this.themeService.getColorTheme());
|
||||
this._cellToggleMoreActions.onInit(this.moreActionsElementRef, this.model, this.cellModel);
|
||||
this.setFocusAndScroll();
|
||||
this._register(this.cellModel.onOutputsChanged(e => {
|
||||
this.updatePreview();
|
||||
}));
|
||||
}
|
||||
|
||||
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
||||
for (let propName in changes) {
|
||||
if (propName === 'activeCellId') {
|
||||
let changedProp = changes[propName];
|
||||
this._activeCellId = changedProp.currentValue;
|
||||
this.toggleUserSelect(this.isActive());
|
||||
// If the activeCellId is undefined (i.e. in an active cell update), don't unnecessarily set editMode to false;
|
||||
// it will be set to true in a subsequent call to toggleEditMode()
|
||||
if (changedProp.previousValue !== undefined) {
|
||||
this.toggleEditMode(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get isTrusted(): boolean {
|
||||
return this.model.trustedMode;
|
||||
}
|
||||
|
||||
public get notebookUri(): URI {
|
||||
return this.model.notebookUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the preview of markdown component with latest changes
|
||||
* If content is empty and in non-edit mode, default it to 'Double-click to edit'
|
||||
* Sanitizes the data to be shown in markdown cell
|
||||
*/
|
||||
private updatePreview(): void {
|
||||
let trustedChanged = this.cellModel && this._lastTrustedMode !== this.cellModel.trustedMode;
|
||||
let cellModelSourceJoined = Array.isArray(this.cellModel.source) ? this.cellModel.source.join('') : this.cellModel.source;
|
||||
let contentJoined = Array.isArray(this._content) ? this._content.join('') : this._content;
|
||||
let contentChanged = contentJoined !== cellModelSourceJoined || cellModelSourceJoined.length === 0;
|
||||
if (trustedChanged || contentChanged) {
|
||||
this._lastTrustedMode = this.cellModel.trustedMode;
|
||||
if ((!cellModelSourceJoined) && !this.isEditMode) {
|
||||
this._content = localize('doubleClickEdit', "Double-click to edit");
|
||||
} else {
|
||||
this._content = this.cellModel.source;
|
||||
}
|
||||
|
||||
if (useInProcMarkdown(this.configurationService)) {
|
||||
this.markdownRenderer.setNotebookURI(this.cellModel.notebookModel.notebookUri);
|
||||
this.markdownResult = this.markdownRenderer.render({
|
||||
isTrusted: true,
|
||||
value: Array.isArray(this._content) ? this._content.join('') : this._content
|
||||
});
|
||||
this.markdownResult.element.innerHTML = this.sanitizeContent(this.markdownResult.element.innerHTML);
|
||||
this.setLoading(false);
|
||||
let outputElement = <HTMLElement>this.output.nativeElement;
|
||||
outputElement.innerHTML = this.markdownResult.element.innerHTML;
|
||||
} else {
|
||||
this._commandService.executeCommand<string>('notebook.showPreview', this.cellModel.notebookModel.notebookUri, this._content).then((htmlcontent) => {
|
||||
htmlcontent = convertVscodeResourceToFileInSubDirectories(htmlcontent, this.cellModel);
|
||||
htmlcontent = this.sanitizeContent(htmlcontent);
|
||||
let outputElement = <HTMLElement>this.output.nativeElement;
|
||||
outputElement.innerHTML = htmlcontent;
|
||||
this.setLoading(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Sanitizes the content based on trusted mode of Cell Model
|
||||
private sanitizeContent(content: string): string {
|
||||
if (this.cellModel && !this.cellModel.trustedMode) {
|
||||
content = this.sanitizer.sanitize(content);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
// Todo: implement layout
|
||||
public layout() {
|
||||
}
|
||||
|
||||
private updateTheme(theme: IColorTheme): void {
|
||||
let outputElement = <HTMLElement>this.output.nativeElement;
|
||||
outputElement.style.borderTopColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
|
||||
|
||||
let moreActionsEl = <HTMLElement>this.moreActionsElementRef.nativeElement;
|
||||
moreActionsEl.style.borderRightColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
|
||||
}
|
||||
|
||||
public handleContentChanged(): void {
|
||||
this.updatePreview();
|
||||
}
|
||||
|
||||
public toggleEditMode(editMode?: boolean): void {
|
||||
this.isEditMode = editMode !== undefined ? editMode : !this.isEditMode;
|
||||
this.updateMoreActions();
|
||||
this.updatePreview();
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
private updateMoreActions(): void {
|
||||
if (!this.isEditMode && (this.isActive() || this._hover)) {
|
||||
this.toggleMoreActionsButton(true);
|
||||
}
|
||||
else {
|
||||
this.toggleMoreActionsButton(false);
|
||||
}
|
||||
}
|
||||
|
||||
private toggleUserSelect(userSelect: boolean): void {
|
||||
if (!this.output) {
|
||||
return;
|
||||
}
|
||||
if (userSelect) {
|
||||
DOM.addClass(this.output.nativeElement, USER_SELECT_CLASS);
|
||||
} else {
|
||||
DOM.removeClass(this.output.nativeElement, USER_SELECT_CLASS);
|
||||
}
|
||||
}
|
||||
|
||||
private setFocusAndScroll(): void {
|
||||
this.toggleEditMode(this.isActive());
|
||||
|
||||
if (this.output && this.output.nativeElement) {
|
||||
(<HTMLElement>this.output.nativeElement).scrollTo({ behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
|
||||
protected isActive() {
|
||||
return this.cellModel && this.cellModel.id === this.activeCellId;
|
||||
}
|
||||
|
||||
protected toggleMoreActionsButton(isActiveOrHovered: boolean) {
|
||||
this._cellToggleMoreActions.toggleVisible(!isActiveOrHovered);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
text-cell-component {
|
||||
display: block;
|
||||
}
|
||||
|
||||
text-cell-component .notebook-preview {
|
||||
user-select: none;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.notebook-preview.actionselect {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
text-cell-component table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border: none;
|
||||
font-size: 12px;
|
||||
table-layout: auto;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 1em;
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
text-cell-component thead {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
text-cell-component td,
|
||||
text-cell-component th,
|
||||
text-cell-component tr {
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
padding: 0.5em 0.5em;
|
||||
line-height: normal;
|
||||
white-space: normal;
|
||||
max-width: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
text-cell-component th {
|
||||
font-weight: bold;
|
||||
}
|
||||