Markdown toolbar > Preview toggle feature (#10963)

* Added toggle preview button to Markdown toolbar. Revised components, theme and styles to present the preview as a second column beside the markdown.

* Added showPreview to model and began working on togglePreview.

* Uncommented use of cellModel.showPreview

* add cell model event for onPreviewChange

* Renamed my showPreview boolean to prevent confusion with local boolean used in toogglePreview.

* Added CSS class when preview is enabled. Adjusted styles accordingly.

* Swapped icon show/hide references for correct sequence. Modified updatePreview to include state of doShowPreview.

* Added check for isEditMode so we can run togglePreview and show it once editor closes.

* Added listener to code.component that triggers layoutEmitter on changes to peview.

* Renamed local boolean doShowPreview. Removed unneeded code. Fixed ambiguity in my use of booleans, adding a getter and setter to textCell.

* Cleaned up implementation of new get/set for toggling preview.

Co-authored-by: chlafreniere <hichise@gmail.com>
This commit is contained in:
Hale Rankin
2020-06-26 13:47:17 -07:00
committed by GitHub
parent 00fe535add
commit 7ef8acf04e
11 changed files with 130 additions and 35 deletions

View File

@@ -253,6 +253,10 @@ export class CodeComponent extends CellView implements OnInit, OnChanges {
this.onCellCollapse(isCollapsed);
}));
this._register(this.cellModel.onCellPreviewChanged(() => {
this._layoutEmitter.fire();
}));
this.layout();
if (this._cellModel.isCollapsed) {

View File

@@ -7,7 +7,7 @@ import { Component, Input, Inject, ViewChild, ElementRef } from '@angular/core';
import { localize } from 'vs/nls';
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { TransformMarkdownAction, MarkdownButtonType } from 'sql/workbench/contrib/notebook/browser/markdownToolbarActions';
import { TransformMarkdownAction, MarkdownButtonType, TogglePreviewAction } from 'sql/workbench/contrib/notebook/browser/markdownToolbarActions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
export const MARKDOWN_TOOLBAR_SELECTOR: string = 'markdown-toolbar-component';
@@ -28,7 +28,6 @@ export class MarkdownToolbarComponent {
public buttonList = localize('buttonList', "List");
public buttonOrderedList = localize('buttonOrderedList', "Ordered list");
public buttonImage = localize('buttonImage', "Image");
public buttonPreview = localize('buttonPreview', "Markdown preview toggle - off");
@Input() public cellModel: ICellModel;
private _actionBar: Taskbar;
@@ -51,6 +50,7 @@ export class MarkdownToolbarComponent {
let listButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.listText', '', 'list', this.buttonList, this.cellModel, MarkdownButtonType.UNORDERED_LIST);
let orderedListButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.orderedText', '', 'ordered-list', this.buttonOrderedList, this.cellModel, MarkdownButtonType.ORDERED_LIST);
let imageButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.imageText', '', 'insert-image', this.buttonImage, this.cellModel, MarkdownButtonType.IMAGE);
let togglePreviewAction = this._instantiationService.createInstance(TogglePreviewAction, 'notebook.togglePreview', true, this.cellModel.showPreview);
let taskbar = <HTMLElement>this.mdtoolbar.nativeElement;
this._actionBar = new Taskbar(taskbar);
@@ -64,7 +64,8 @@ export class MarkdownToolbarComponent {
{ action: linkButton },
{ action: listButton },
{ action: orderedListButton },
{ action: imageButton }
{ action: imageButton },
{ action: togglePreviewAction }
]);
}
}

View File

@@ -11,7 +11,6 @@
padding: 0;
}
.markdown-toolbar {
border-bottom-width: 1px;
border-bottom-style: solid;
@@ -20,6 +19,11 @@
margin: 0;
padding: 10px 16px 4px 16px;
}
.markdown-toolbar ul {
position: relative;
}
.markdown-toolbar .carbon-taskbar li.action-item {
display: inline-block;
margin-right: 14px;
@@ -30,6 +34,11 @@
.markdown-toolbar .carbon-taskbar li:nth-child(2) {
margin-right: 9px;
}
.markdown-toolbar .carbon-taskbar li:last-child {
margin-right: 0;
position: absolute;
right: 0;
}
.markdown-toolbar .carbon-taskbar li a {
display: inline-block;
height: 20px;

View File

@@ -6,12 +6,10 @@
-->
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column" (mouseover)="hover=true" (mouseleave)="hover=false">
<markdown-toolbar-component #markdownToolbar *ngIf="previewFeaturesEnabled === true && isEditMode" [cellModel]="cellModel"></markdown-toolbar-component>
<div class="notebook-text" style="flex: 0 0 auto;">
<div class="notebook-text" [class.edit-mode]="isEditMode" [class.show-preview]="showPreview">
<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">
<div #preview link-handler *ngIf="showPreview" [isTrusted]="isTrusted" [notebookUri]="notebookUri" class="notebook-preview">
</div>
</div>
</div>

View File

@@ -69,6 +69,7 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
private _content: string | string[];
private _lastTrustedMode: boolean;
private isEditMode: boolean;
private showPreview: boolean;
private _sanitizer: ISanitizer;
private _model: NotebookModel;
private _activeCellId: string;
@@ -86,6 +87,7 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
) {
super();
this.isEditMode = true;
this.showPreview = true;
this.markdownRenderer = this._instantiationService.createInstance(NotebookMarkdownRenderer);
this._register(toDisposable(() => {
if (this.markdownResult) {
@@ -139,6 +141,9 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
this.toggleEditMode(mode);
}
}));
this._register(this.cellModel.onCellPreviewChanged(preview => {
this.previewMode = preview;
}));
}
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
@@ -171,14 +176,14 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
/**
* 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'
* If content is empty and in non-edit mode, default it to 'Add content here...'
* 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;
let contentChanged = contentJoined !== cellModelSourceJoined || cellModelSourceJoined.length === 0 || this.showPreview === true;
if (trustedChanged || contentChanged) {
this._lastTrustedMode = this.cellModel.trustedMode;
if ((!cellModelSourceJoined) && !this.isEditMode) {
@@ -213,8 +218,10 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
}
private updateTheme(theme: IColorTheme): void {
let outputElement = <HTMLElement>this.output.nativeElement;
outputElement.style.borderTopColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
let outputElement = <HTMLElement>this.output?.nativeElement;
if (outputElement) {
outputElement.style.borderTopColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString();
}
}
public handleContentChanged(): void {
@@ -224,10 +231,22 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
public toggleEditMode(editMode?: boolean): void {
this.isEditMode = editMode !== undefined ? editMode : !this.isEditMode;
this.cellModel.isEditMode = this.isEditMode;
if (!this.isEditMode) {
this.previewMode = true;
}
this.updatePreview();
this._changeRef.detectChanges();
}
public get previewMode(): boolean {
return this.showPreview;
}
public set previewMode(value: boolean) {
this.showPreview = value;
this._changeRef.detectChanges();
this.updatePreview();
}
private toggleUserSelect(userSelect: boolean): void {
if (!this.output) {
return;

View File

@@ -7,17 +7,34 @@ text-cell-component {
display: block;
}
.notebookEditor .notebook-cell.active text-cell-component code-component {
border-color: transparent;
border-bottom-width: 1px;
border-style: solid;
text-cell-component .notebook-text {
display: flex;
}
text-cell-component code-component {
flex-direction: column;
}
text-cell-component .notebook-preview {
flex-direction: column;
width: 100%;
user-select: none;
padding-left: 8px;
padding-right: 8px;
}
text-cell-component .edit-mode code-component {
display: block;
width: 100%;
}
text-cell-component .show-preview.edit-mode code-component {
width: 50%;
}
text-cell-component .edit-mode .notebook-preview {
border-color: transparent;
border-left-width: 1px;
border-style: solid;
border-top-width: 0;
flex-direction: column;
width: 50%;
}
.notebook-preview.actionselect {
user-select: text;

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { INotebookEditor, INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { IRange } from 'vs/editor/common/core/range';
@@ -13,7 +13,7 @@ import { TextModel } from 'vs/editor/common/model/textModel';
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
import { QueryTextEditor } from 'sql/workbench/browser/modelComponents/queryTextEditor';
import { Selection } from 'vs/editor/common/core/selection';
import { ToggleableAction } from 'sql/workbench/contrib/notebook/browser/notebookActions';
// Action to decorate markdown
@@ -390,3 +390,40 @@ export enum MarkdownLineType {
EVERY_LINE,
WRAPPED_ABOVE_AND_BELOW
}
export class TogglePreviewAction extends ToggleableAction {
private static readonly previewShowLabel = localize('previewShowLabel', "Show Preview");
private static readonly previewHideLabel = localize('previewHideLabel', "Hide Preview");
private static readonly baseClass = 'codicon';
private static readonly previewShowCssClass = 'split-toggle-on';
private static readonly previewHideCssClass = 'split-toggle-off';
private static readonly maskedIconClass = 'masked-icon';
constructor(
id: string, toggleTooltip: boolean, showPreview: boolean
) {
super(id, {
baseClass: TogglePreviewAction.baseClass,
toggleOffLabel: TogglePreviewAction.previewShowLabel,
toggleOffClass: TogglePreviewAction.previewShowCssClass,
toggleOnLabel: TogglePreviewAction.previewHideLabel,
toggleOnClass: TogglePreviewAction.previewHideCssClass,
maskedIconClass: TogglePreviewAction.maskedIconClass,
shouldToggleTooltip: toggleTooltip,
isOn: showPreview
});
}
public get previewMode(): boolean {
return this.state.isOn;
}
public set previewMode(value: boolean) {
this.toggle(value);
}
public async run(context: any): Promise<boolean> {
this.previewMode = !this.previewMode;
context.cellModel.showPreview = this.previewMode;
return true;
}
}

View File

@@ -74,7 +74,7 @@
width: 16px;
}
.notebookEditor .in-preview .actions-container .action-item:last-child {
margin-right: 8px;
margin-right: 14px;
}
.notebookEditor
.in-preview
@@ -83,20 +83,11 @@
.notebook-button {
margin-right: 0;
}
.notebookEditor
.in-preview
.actions-container
.action-item:last-child
.notebook-button.fixed-width {
.notebookEditor .in-preview .actions-container .action-item:last-child .notebook-button.fixed-width {
background-size: contain;
margin-left: 8px;
margin-right: -28px;
}
.notebookEditor
.in-preview
.actions-container
.action-item
.notebook-button.fixed-width {
width: 34px;
padding: 0 0 0 18px;
}
.notebookEditor .labelOnLeftContainer {

View File

@@ -8,7 +8,7 @@ import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/
import { SIDE_BAR_BACKGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND } from 'vs/workbench/common/theme';
import { activeContrastBorder, contrastBorder, buttonBackground, textLinkForeground, textLinkActiveForeground, textPreformatForeground, textBlockQuoteBackground, textBlockQuoteBorder, buttonForeground } from 'vs/platform/theme/common/colorRegistry';
import { editorLineHighlight, editorLineHighlightBorder } from 'vs/editor/common/view/editorColorRegistry';
import { cellBorder, notebookToolbarIcon, notebookToolbarLines, buttonMenuArrow, dropdownArrow, markdownEditorBackground, splitBorder, codeEditorBackground, codeEditorBackgroundActive, codeEditorLineNumber, codeEditorToolbarIcon, codeEditorToolbarBackground, codeEditorToolbarBorder, toolbarBackground, toolbarIcon, toolbarBottomBorder, notebookToolbarSelectBackground } from 'sql/platform/theme/common/colorRegistry';
import { cellBorder, notebookToolbarIcon, notebookToolbarLines, buttonMenuArrow, dropdownArrow, markdownEditorBackground, codeEditorBackground, codeEditorBackgroundActive, codeEditorLineNumber, codeEditorToolbarIcon, codeEditorToolbarBackground, codeEditorToolbarBorder, toolbarBackground, toolbarIcon, toolbarBottomBorder, notebookToolbarSelectBackground, splitBorder } from 'sql/platform/theme/common/colorRegistry';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BareResultsGridInfo, getBareResultsGridInfoStyles } from 'sql/workbench/contrib/query/browser/queryResultsEditor';
@@ -201,7 +201,7 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean, conf
}
const splitBorderColor = theme.getColor(splitBorder);
if (splitBorderColor) {
collector.addRule(`.notebookEditor .notebook-cell.active text-cell-component code-component { border-bottom-color: ${splitBorderColor}; }`);
collector.addRule(`.notebookEditor .notebook-cell.active text-cell-component .notebook-preview { border-left-color: ${splitBorderColor}; }`);
}
// Code editor colors

View File

@@ -56,6 +56,8 @@ export class CellModel implements ICellModel {
private _isCollapsed: boolean;
private _onCollapseStateChanged = new Emitter<boolean>();
private _modelContentChangedEvent: IModelContentChangedEvent;
private _showPreview: boolean = true;
private _onCellPreviewChanged = new Emitter<boolean>();
constructor(cellData: nb.ICellContents,
private _options: ICellModelOptions,
@@ -275,6 +277,21 @@ export class CellModel implements ICellModel {
this._stdInVisible = val;
}
public get showPreview(): boolean {
return this._showPreview;
}
public set showPreview(val: boolean) {
if (val !== this._showPreview) {
this._showPreview = val;
this._onCellPreviewChanged.fire(this._showPreview);
}
}
public get onCellPreviewChanged(): Event<boolean> {
return this._onCellPreviewChanged.event;
}
private notifyExecutionComplete(): void {
if (this._notebookService) {
this._notebookService.serializeNotebookStateChange(this.notebookModel.notebookUri, NotebookChangeType.CellExecuted, this)

View File

@@ -461,6 +461,8 @@ export interface ICellModel {
readonly onCellModeChanged: Event<boolean>;
modelContentChangedEvent: IModelContentChangedEvent;
isEditMode: boolean;
showPreview: boolean;
readonly onCellPreviewChanged: Event<boolean>;
sendChangeToNotebook(change: NotebookChangeType): void;
}