Making Notebook to scroll to output area only when Notebook command is executed (#5893)

This commit is contained in:
Gene Lee
2019-06-05 12:07:31 -07:00
committed by GitHub
parent da164cec0a
commit a518c4a529
5 changed files with 36 additions and 24 deletions

View File

@@ -43,7 +43,7 @@ export class OutputComponent extends AngularDisposable implements OnInit {
} }
ngOnInit() { ngOnInit() {
this.renderOutput(true); this.renderOutput();
this._initialized = true; this._initialized = true;
this._register(Event.debounce(this.cellModel.notebookModel.layoutChanged, (l, e) => e, 50, /*leading=*/false) this._register(Event.debounce(this.cellModel.notebookModel.layoutChanged, (l, e) => e, 50, /*leading=*/false)
(() => this.renderOutput())); (() => this.renderOutput()));
@@ -70,21 +70,11 @@ export class OutputComponent extends AngularDisposable implements OnInit {
} }
} }
private renderOutput(focusAndScroll: boolean = false): void { private renderOutput(): void {
let options = outputProcessor.getBundleOptions({ value: this.cellOutput, trusted: this.trustedMode }); let options = outputProcessor.getBundleOptions({ value: this.cellOutput, trusted: this.trustedMode });
options.themeService = this._themeService; options.themeService = this._themeService;
// TODO handle safe/unsafe mapping // TODO handle safe/unsafe mapping
this.createRenderedMimetype(options, this.outputElement.nativeElement); this.createRenderedMimetype(options, this.outputElement.nativeElement);
if (focusAndScroll) {
this.setFocusAndScroll(this.outputElement.nativeElement);
}
}
private setFocusAndScroll(node: HTMLElement): void {
if (node) {
node.focus();
node.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
} }
public layout(): void { public layout(): void {

View File

@@ -33,14 +33,24 @@ export class OutputAreaComponent extends AngularDisposable implements OnInit {
this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this)); this._register(this.themeService.onDidColorThemeChange(this.updateTheme, this));
this.updateTheme(this.themeService.getColorTheme()); this.updateTheme(this.themeService.getColorTheme());
if (this.cellModel) { if (this.cellModel) {
this._register(this.cellModel.onOutputsChanged(() => { this._register(this.cellModel.onOutputsChanged(e => {
if (!(this._changeRef['destroyed'])) { if (!(this._changeRef['destroyed'])) {
this._changeRef.detectChanges(); this._changeRef.detectChanges();
if (e && e.shouldScroll) {
this.setFocusAndScroll(this.outputArea.nativeElement);
}
} }
})); }));
} }
} }
private setFocusAndScroll(node: HTMLElement): void {
if (node) {
node.focus();
node.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
}
@Input() set activeCellId(value: string) { @Input() set activeCellId(value: string) {
this._activeCellId = value; this._activeCellId = value;
} }

View File

@@ -12,7 +12,7 @@ import { localize } from 'vs/nls';
import * as notebookUtils from '../notebookUtils'; import * as notebookUtils from '../notebookUtils';
import { CellTypes, CellType, NotebookChangeType } from 'sql/workbench/parts/notebook/models/contracts'; import { CellTypes, CellType, NotebookChangeType } from 'sql/workbench/parts/notebook/models/contracts';
import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel'; import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel';
import { ICellModel, notebookConstants } from 'sql/workbench/parts/notebook/models/modelInterfaces'; import { ICellModel, notebookConstants, IOutputChangedEvent } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { ICellModelOptions, IModelFactory, FutureInternal, CellExecutionState } from './modelInterfaces'; import { ICellModelOptions, IModelFactory, FutureInternal, CellExecutionState } from './modelInterfaces';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
@@ -20,7 +20,6 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/
import { Schemas } from 'vs/base/common/network'; import { Schemas } from 'vs/base/common/network';
let modelId = 0; let modelId = 0;
export class CellModel implements ICellModel { export class CellModel implements ICellModel {
private _cellType: nb.CellType; private _cellType: nb.CellType;
private _source: string; private _source: string;
@@ -28,7 +27,7 @@ export class CellModel implements ICellModel {
private _future: FutureInternal; private _future: FutureInternal;
private _outputs: nb.ICellOutput[] = []; private _outputs: nb.ICellOutput[] = [];
private _isEditMode: boolean; private _isEditMode: boolean;
private _onOutputsChanged = new Emitter<ReadonlyArray<nb.ICellOutput>>(); private _onOutputsChanged = new Emitter<IOutputChangedEvent>();
private _onCellModeChanged = new Emitter<boolean>(); private _onCellModeChanged = new Emitter<boolean>();
private _onExecutionStateChanged = new Emitter<CellExecutionState>(); private _onExecutionStateChanged = new Emitter<CellExecutionState>();
private _isTrusted: boolean; private _isTrusted: boolean;
@@ -62,7 +61,7 @@ export class CellModel implements ICellModel {
return other && other.id === this.id; return other && other.id === this.id;
} }
public get onOutputsChanged(): Event<ReadonlyArray<nb.ICellOutput>> { public get onOutputsChanged(): Event<IOutputChangedEvent> {
return this._onOutputsChanged.event; return this._onOutputsChanged.event;
} }
@@ -91,7 +90,11 @@ export class CellModel implements ICellModel {
public set trustedMode(isTrusted: boolean) { public set trustedMode(isTrusted: boolean) {
if (this._isTrusted !== isTrusted) { if (this._isTrusted !== isTrusted) {
this._isTrusted = isTrusted; this._isTrusted = isTrusted;
this._onOutputsChanged.fire(this._outputs); let outputEvent: IOutputChangedEvent = {
outputs: this._outputs,
shouldScroll: false
};
this._onOutputsChanged.fire(outputEvent);
} }
} }
@@ -317,8 +320,12 @@ export class CellModel implements ICellModel {
this.fireOutputsChanged(); this.fireOutputsChanged();
} }
private fireOutputsChanged(): void { private fireOutputsChanged(shouldScroll: boolean = false): void {
this._onOutputsChanged.fire(this.outputs); let outputEvent: IOutputChangedEvent = {
outputs: this.outputs,
shouldScroll: !!shouldScroll
};
this._onOutputsChanged.fire(outputEvent);
this.sendChangeToNotebook(NotebookChangeType.CellOutputUpdated); this.sendChangeToNotebook(NotebookChangeType.CellOutputUpdated);
} }
@@ -383,7 +390,7 @@ export class CellModel implements ICellModel {
// deletes transient node in the serialized JSON // deletes transient node in the serialized JSON
delete output['transient']; delete output['transient'];
this._outputs.push(this.rewriteOutputUrls(output)); this._outputs.push(this.rewriteOutputUrls(output));
this.fireOutputsChanged(); this.fireOutputsChanged(true);
} }
} }

View File

@@ -435,6 +435,11 @@ export enum CellExecutionState {
Error = 3 Error = 3
} }
export interface IOutputChangedEvent {
outputs: ReadonlyArray<nb.ICellOutput>;
shouldScroll: boolean;
}
export interface ICellModel { export interface ICellModel {
cellUri: URI; cellUri: URI;
id: string; id: string;
@@ -447,7 +452,7 @@ export interface ICellModel {
executionCount: number | undefined; executionCount: number | undefined;
readonly future: FutureInternal; readonly future: FutureInternal;
readonly outputs: ReadonlyArray<nb.ICellOutput>; readonly outputs: ReadonlyArray<nb.ICellOutput>;
readonly onOutputsChanged: Event<ReadonlyArray<nb.ICellOutput>>; readonly onOutputsChanged: Event<IOutputChangedEvent>;
readonly onExecutionStateChange: Event<CellExecutionState>; readonly onExecutionStateChange: Event<CellExecutionState>;
readonly executionState: CellExecutionState; readonly executionState: CellExecutionState;
readonly notebookModel: NotebookModel; readonly notebookModel: NotebookModel;

View File

@@ -162,7 +162,7 @@ suite('Cell Model', function (): void {
future.setup(f => f.setReplyHandler(TypeMoq.It.isAny())).callback((handler) => onReply = handler); future.setup(f => f.setReplyHandler(TypeMoq.It.isAny())).callback((handler) => onReply = handler);
future.setup(f => f.setIOPubHandler(TypeMoq.It.isAny())).callback((handler) => onIopub = handler); future.setup(f => f.setIOPubHandler(TypeMoq.It.isAny())).callback((handler) => onIopub = handler);
let outputs: ReadonlyArray<nb.ICellOutput> = undefined; let outputs: ReadonlyArray<nb.ICellOutput> = undefined;
cell.onOutputsChanged((o => outputs = o)); cell.onOutputsChanged((o => outputs = o.outputs));
// When I set it on the cell // When I set it on the cell
cell.setFuture(future.object); cell.setFuture(future.object);
@@ -265,7 +265,7 @@ suite('Cell Model', function (): void {
let onIopub: nb.MessageHandler<nb.IIOPubMessage>; let onIopub: nb.MessageHandler<nb.IIOPubMessage>;
future.setup(f => f.setIOPubHandler(TypeMoq.It.isAny())).callback((handler) => onIopub = handler); future.setup(f => f.setIOPubHandler(TypeMoq.It.isAny())).callback((handler) => onIopub = handler);
let outputs: ReadonlyArray<nb.ICellOutput> = undefined; let outputs: ReadonlyArray<nb.ICellOutput> = undefined;
cell.onOutputsChanged((o => outputs = o)); cell.onOutputsChanged((o => outputs = o.outputs));
//Set the future //Set the future
cell.setFuture(future.object); cell.setFuture(future.object);