mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-04 09:35:38 -05:00
Add collapse/expand functionality to notebook code cells. (#7481)
This commit is contained in:
@@ -7,7 +7,9 @@
|
||||
<div style="width: 100%; height: 100%; display: flex; flex-flow: row" (mouseover)="hover=true" (mouseleave)="hover=false">
|
||||
<div #toolbar class="toolbar">
|
||||
</div>
|
||||
<div #editor class="editor" style="flex: 1 1 auto; overflow: hidden;">
|
||||
<div style="flex: 1 1 auto; flex-flow: column; overflow: hidden;">
|
||||
<div #editor class="editor"></div>
|
||||
<collapse-component *ngIf="cellModel.cellType === 'code'" [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>
|
||||
|
||||
@@ -33,6 +33,8 @@ import * as notebookUtils from 'sql/workbench/parts/notebook/browser/models/note
|
||||
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/parts/notebook/browser/cellViews/collapse.component';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
||||
export const CODE_SELECTOR: string = 'code-component';
|
||||
const MARKDOWN_CLASS = 'markdown';
|
||||
@@ -45,6 +47,7 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
@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;
|
||||
@@ -81,7 +84,7 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
this.cellModel.hover = value;
|
||||
if (!this.isActive()) {
|
||||
// Only make a change if we're not active, since this has priority
|
||||
this.toggleMoreActionsButton(this.cellModel.hover);
|
||||
this.toggleActionsVisibility(this.cellModel.hover);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +115,6 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
(() => this.layout()));
|
||||
// Handle disconnect on removal of the cell, if it was the active cell
|
||||
this._register({ dispose: () => this.updateConnectionState(false) });
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -129,7 +131,7 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
let changedProp = changes[propName];
|
||||
let isActive = this.cellModel.id === changedProp.currentValue;
|
||||
this.updateConnectionState(isActive);
|
||||
this.toggleMoreActionsButton(isActive);
|
||||
this.toggleActionsVisibility(isActive);
|
||||
if (this._editor) {
|
||||
this._editor.toggleEditorSelected(isActive);
|
||||
}
|
||||
@@ -191,21 +193,24 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
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
|
||||
@@ -216,15 +221,21 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
this._register(this._editor);
|
||||
this._register(this._editorInput);
|
||||
this._register(this._editorModel.onDidChangeContent(e => {
|
||||
this._editor.setHeightToScrollHeight();
|
||||
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._editor.setHeightToScrollHeight(true, this._cellModel.isCollapsed);
|
||||
}
|
||||
}));
|
||||
this._register(this.model.layoutChanged(() => this._layoutEmitter.fire(), this));
|
||||
@@ -233,14 +244,22 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
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();
|
||||
this._editor.setHeightToScrollHeight(false, this._cellModel.isCollapsed);
|
||||
}
|
||||
|
||||
protected initActionBar() {
|
||||
@@ -317,7 +336,29 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
return this.cellModel && this.cellModel.id === this.activeCellId;
|
||||
}
|
||||
|
||||
protected toggleMoreActionsButton(isActiveOrHovered: boolean) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ code-component .toolbarIconStop {
|
||||
code-component .editor {
|
||||
padding: 5px 0px 5px 0px
|
||||
}
|
||||
|
||||
/* overview ruler */
|
||||
code-component .monaco-editor .decorationsOverviewRuler {
|
||||
visibility: hidden;
|
||||
@@ -86,7 +87,34 @@ code-component .carbon-taskbar .codicon.hideIcon.execCountHundred {
|
||||
margin-left: -6px;
|
||||
}
|
||||
|
||||
code-component .carbon-taskbar.monaco-toolbar .monaco-action-bar.animated .actions-container
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<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 *ngIf="cellModel.outputs && cellModel.outputs.length > 0 && !cellModel.isCollapsed" [cellModel]="cellModel" [activeCellId]="activeCellId">
|
||||
</output-area-component>
|
||||
<stdin-component *ngIf="isStdInVisible" [onSendInput]="inputDeferred" [stdIn]="stdIn" [cellModel]="cellModel"></stdin-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -47,6 +47,9 @@ export class CodeCellComponent extends CellView implements OnInit, OnChanges {
|
||||
|
||||
ngOnInit() {
|
||||
if (this.cellModel) {
|
||||
this._register(this.cellModel.onCollapseStateChanged((state) => {
|
||||
this._changeRef.detectChanges();
|
||||
}));
|
||||
this._register(this.cellModel.onOutputsChanged(() => {
|
||||
this._changeRef.detectChanges();
|
||||
}));
|
||||
@@ -73,6 +76,7 @@ export class CodeCellComponent extends CellView implements OnInit, OnChanges {
|
||||
get activeCellId(): string {
|
||||
return this._activeCellId;
|
||||
}
|
||||
|
||||
public layout() {
|
||||
|
||||
}
|
||||
|
||||
@@ -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,79 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, SimpleChange, OnChanges } from '@angular/core';
|
||||
import { CellView } from 'sql/workbench/parts/notebook/browser/cellViews/interfaces';
|
||||
import { ICellModel } from 'sql/workbench/parts/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(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
) {
|
||||
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,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,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 |
Reference in New Issue
Block a user