mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-19 17:22:48 -05:00
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:
@@ -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) {
|
||||
|
||||
@@ -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 }
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user