mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Maintain Query State (#2571)
* add results view stating * working through the bugs * handle various resizing bugs * gnale resizing better * fix tests by adding missing node module * formatting * refactor interfaces out to get around testing restrictions * more refactoring of importants to avoid loading errors
This commit is contained in:
committed by
Karl Burtram
parent
b03c0a3e2d
commit
9fe4237033
@@ -180,14 +180,16 @@ export class TabbedPanel extends Disposable implements IThemable {
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
this._currentDimensions = dimension;
|
||||
this.$parent.style('height', dimension.height + 'px');
|
||||
this.$parent.style('width', dimension.width + 'px');
|
||||
this.$header.style('width', dimension.width + 'px');
|
||||
this.$body.style('width', dimension.width + 'px');
|
||||
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
|
||||
this.$body.style('height', bodyHeight + 'px');
|
||||
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
|
||||
if (dimension) {
|
||||
this._currentDimensions = dimension;
|
||||
this.$parent.style('height', dimension.height + 'px');
|
||||
this.$parent.style('width', dimension.width + 'px');
|
||||
this.$header.style('width', dimension.width + 'px');
|
||||
this.$body.style('width', dimension.width + 'px');
|
||||
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
|
||||
this.$body.style('height', bodyHeight + 'px');
|
||||
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
|
||||
}
|
||||
}
|
||||
|
||||
private _layoutCurrentTab(dimension: Dimension): void {
|
||||
|
||||
@@ -109,6 +109,9 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
|
||||
private _onDidSashReset = new Emitter<void>();
|
||||
readonly onDidSashReset = this._onDidSashReset.event;
|
||||
|
||||
private _onScroll = new Emitter<number>();
|
||||
readonly onScroll = this._onScroll.event;
|
||||
|
||||
get length(): number {
|
||||
return this.viewItems.length;
|
||||
}
|
||||
@@ -124,6 +127,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
|
||||
debounceEvent(this.scrollable.onScroll, (l, e) => e, 25)(e => {
|
||||
this.render(e.scrollTop, e.height);
|
||||
this.relayout();
|
||||
this._onScroll.fire(e.scrollTop);
|
||||
});
|
||||
let domNode = this.scrollable.getDomNode();
|
||||
dom.addClass(this.el, 'monaco-scroll-split-view');
|
||||
@@ -330,6 +334,10 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
|
||||
this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex);
|
||||
}
|
||||
|
||||
public setScrollPosition(position: number) {
|
||||
this.scrollable.setScrollPosition({ scrollTop: position });
|
||||
}
|
||||
|
||||
layout(size: number): void {
|
||||
const previousSize = this.size;
|
||||
this.size = size;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
|
||||
|
||||
const defaultOptions: ICellRangeSelectorOptions = {
|
||||
selectionCss: {
|
||||
'border': '2px dashed blue'
|
||||
@@ -44,6 +42,8 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
|
||||
public onCellRangeSelected = new Slick.Event<{ range: Slick.Range }>();
|
||||
|
||||
constructor(private options: ICellRangeSelectorOptions) {
|
||||
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
|
||||
|
||||
this.options = mixin(this.options, defaultOptions, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
|
||||
import { CellRangeSelector, ICellRangeSelector } from 'sql/base/browser/ui/table/plugins/cellRangeSelector';
|
||||
|
||||
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
|
||||
|
||||
export interface ICellSelectionModelOptions {
|
||||
cellRangeSelector?: any;
|
||||
selectActiveCell?: boolean;
|
||||
|
||||
@@ -10,6 +10,7 @@ import * as TelemetryUtils from 'sql/common/telemetryUtilities';
|
||||
import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
import { memoize, unmemoize } from 'sql/base/common/decorators';
|
||||
import { mixin } from 'sql/base/common/objects';
|
||||
import { LegendPosition, DataDirection, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
@@ -21,29 +22,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
declare var Chart: any;
|
||||
|
||||
export enum ChartType {
|
||||
Bar = 'bar',
|
||||
Doughnut = 'doughnut',
|
||||
HorizontalBar = 'horizontalBar',
|
||||
Line = 'line',
|
||||
Pie = 'pie',
|
||||
TimeSeries = 'timeSeries',
|
||||
Scatter = 'scatter'
|
||||
}
|
||||
|
||||
export enum DataDirection {
|
||||
Vertical = 'vertical',
|
||||
Horizontal = 'horizontal'
|
||||
}
|
||||
|
||||
export enum LegendPosition {
|
||||
Top = 'top',
|
||||
Bottom = 'bottom',
|
||||
Left = 'left',
|
||||
Right = 'right',
|
||||
None = 'none'
|
||||
}
|
||||
|
||||
export function customMixin(destination: any, source: any, overwrite?: boolean): any {
|
||||
if (types.isObject(source)) {
|
||||
mixin(destination, source, overwrite, customMixin);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export enum ChartType {
|
||||
Bar = 'bar',
|
||||
Doughnut = 'doughnut',
|
||||
HorizontalBar = 'horizontalBar',
|
||||
Line = 'line',
|
||||
Pie = 'pie',
|
||||
TimeSeries = 'timeSeries',
|
||||
Scatter = 'scatter'
|
||||
}
|
||||
|
||||
export enum DataDirection {
|
||||
Vertical = 'vertical',
|
||||
Horizontal = 'horizontal'
|
||||
}
|
||||
|
||||
export enum LegendPosition {
|
||||
Top = 'top',
|
||||
Bottom = 'bottom',
|
||||
Left = 'left',
|
||||
Right = 'right',
|
||||
None = 'none'
|
||||
}
|
||||
|
||||
export enum DataType {
|
||||
Number = 'number',
|
||||
Point = 'point'
|
||||
}
|
||||
@@ -3,8 +3,9 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChartInsight, ChartType, customMixin, IChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { ChartInsight, customMixin, IChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { mixin } from 'sql/base/common/objects';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import PieChart from './pieChart.component';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
export default class DoughnutChart extends PieChart {
|
||||
protected readonly chartType: ChartType = ChartType.Doughnut;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import BarChart from './barChart.component';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
export default class HorizontalBarChart extends BarChart {
|
||||
protected readonly chartType: ChartType = ChartType.HorizontalBar;
|
||||
|
||||
@@ -3,16 +3,13 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChartType, customMixin, defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
import { defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import BarChart, { IBarChartConfig } from './barChart.component';
|
||||
import { memoize, unmemoize } from 'sql/base/common/decorators';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { clone } from 'sql/base/common/objects';
|
||||
|
||||
export enum DataType {
|
||||
Number = 'number',
|
||||
Point = 'point'
|
||||
}
|
||||
import { ChartType, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
export interface ILineConfig extends IBarChartConfig {
|
||||
dataType?: DataType;
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChartInsight, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { ChartInsight } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
export default class PieChart extends ChartInsight {
|
||||
protected readonly chartType: ChartType = ChartType.Pie;
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChartType, defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import LineChart, { ILineConfig } from './lineChart.component';
|
||||
import { clone } from 'sql/base/common/objects';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { clone } from 'sql/base/common/objects';
|
||||
|
||||
const defaultScatterConfig = mixin(clone(defaultChartConfig), { dataType: 'point', dataDirection: 'horizontal' }) as ILineConfig;
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { defaultChartConfig, IPointDataSet, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { defaultChartConfig, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import LineChart, { ILineConfig } from './lineChart.component';
|
||||
import { clone } from 'sql/base/common/objects';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
@@ -16,7 +16,7 @@ import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
|
||||
import { IInsightData, IInsightsView, IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
|
||||
import { DataType, ILineConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
|
||||
import { ILineConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
|
||||
import * as PathUtilities from 'sql/common/pathUtilities';
|
||||
import { IChartViewActionContext, CopyAction, CreateInsightAction, SaveImageAction } from 'sql/parts/grid/views/query/chartViewerActions';
|
||||
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
|
||||
@@ -24,10 +24,11 @@ import * as Constants from 'sql/parts/query/common/constants';
|
||||
import { SelectBox as AngularSelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
||||
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
|
||||
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||
import { LegendPosition, DataDirection, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
/* Insights */
|
||||
import {
|
||||
ChartInsight, DataDirection, LegendPosition
|
||||
ChartInsight
|
||||
} from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
@@ -10,6 +10,22 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EditorInput } from 'vs/workbench/common/editor';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
import { GridPanelState } from 'sql/parts/query/editor/gridPanel';
|
||||
import { MessagePanelState } from 'sql/parts/query/editor/messagePanel';
|
||||
import { QueryPlanState } from 'sql/parts/queryPlan/queryPlan';
|
||||
import { ChartState } from 'sql/parts/query/editor/charting/chartView';
|
||||
|
||||
export class ResultsViewState {
|
||||
public gridPanelState: GridPanelState = new GridPanelState();
|
||||
public messagePanelState: MessagePanelState = new MessagePanelState();
|
||||
public chartState: ChartState = new ChartState();
|
||||
public queryPlanState: QueryPlanState = new QueryPlanState();
|
||||
public gridPanelSize: number;
|
||||
public messagePanelSize: number;
|
||||
public activeTab: string;
|
||||
public visibleTabs: Set<string> = new Set<string>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Input for the QueryResultsEditor. This input helps with logic for the viewing and editing of
|
||||
* data in the results grid.
|
||||
@@ -29,6 +45,8 @@ export class QueryResultsInput extends EditorInput {
|
||||
public readonly onRestoreViewStateEmitter = new Emitter<void>();
|
||||
public readonly onSaveViewStateEmitter = new Emitter<void>();
|
||||
|
||||
public readonly state = new ResultsViewState();
|
||||
|
||||
constructor(private _uri: string) {
|
||||
super();
|
||||
this._visible = false;
|
||||
|
||||
@@ -8,10 +8,9 @@
|
||||
import { localize } from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
|
||||
import { InsightType, IInsightOptions } from './insights/interfaces';
|
||||
import { DataDirection, ChartType, LegendPosition, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
|
||||
export class ChartTab implements IPanelTab {
|
||||
public readonly title = localize('chartTabTitle', 'Chart');
|
||||
public readonly identifier = generateUuid();
|
||||
public readonly identifier = 'ChartTab';
|
||||
public readonly view: ChartView;
|
||||
|
||||
constructor(@IInstantiationService instantiationService: IInstantiationService) {
|
||||
|
||||
@@ -12,11 +12,11 @@ import { Insight } from './insights/insight';
|
||||
import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
||||
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
import { ChartOptions, IChartOption, ControlType } from './chartOptions';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
|
||||
import { IInsightOptions } from './insights/interfaces';
|
||||
import { CopyAction, SaveImageAction, CreateInsightAction, IChartActionContext } from './actions';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
import { Dimension, $, getContentHeight, getContentWidth } from 'vs/base/browser/dom';
|
||||
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||
@@ -27,6 +27,14 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { attachSelectBoxStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
|
||||
export class ChartState {
|
||||
dataId: { batchId: number, resultId: number };
|
||||
options: IInsightOptions = {
|
||||
type: ChartType.Bar
|
||||
};
|
||||
}
|
||||
|
||||
declare class Proxy {
|
||||
constructor(object, handler);
|
||||
@@ -43,6 +51,8 @@ export class ChartView implements IPanelView {
|
||||
private _copyAction: CopyAction;
|
||||
private _saveAction: SaveImageAction;
|
||||
|
||||
private _state: ChartState;
|
||||
|
||||
private options: IInsightOptions = {
|
||||
type: ChartType.Bar
|
||||
};
|
||||
@@ -61,7 +71,7 @@ export class ChartView implements IPanelView {
|
||||
private chartingContainer: HTMLElement;
|
||||
|
||||
private optionDisposables: IDisposable[] = [];
|
||||
private optionMap: { [x: string]: HTMLElement } = {};
|
||||
private optionMap: { [x: string]: { element: HTMLElement; set: (val) => void } } = {};
|
||||
|
||||
constructor(
|
||||
@IContextViewService private _contextViewService: IContextViewService,
|
||||
@@ -95,6 +105,10 @@ export class ChartView implements IPanelView {
|
||||
}
|
||||
|
||||
let result = Reflect.set(target, key, value, receiver);
|
||||
// mirror the change in our state
|
||||
if (self.state) {
|
||||
Reflect.set(self.state.options, key, value);
|
||||
}
|
||||
|
||||
if (change) {
|
||||
self.taskbar.context = <IChartActionContext>{ options: self.options, insight: self.insight ? self.insight.insight : undefined };
|
||||
@@ -138,6 +152,7 @@ export class ChartView implements IPanelView {
|
||||
}
|
||||
|
||||
public chart(dataId: { batchId: number, resultId: number }) {
|
||||
this.state.dataId = dataId;
|
||||
this._currentData = dataId;
|
||||
this.shouldGraph();
|
||||
}
|
||||
@@ -180,7 +195,9 @@ export class ChartView implements IPanelView {
|
||||
private buildOptions() {
|
||||
dispose(this.optionDisposables);
|
||||
this.optionDisposables = [];
|
||||
this.optionMap = {};
|
||||
this.optionMap = {
|
||||
'type': this.optionMap['type']
|
||||
};
|
||||
new Builder(this.typeControls).clearChildren();
|
||||
|
||||
this.updateActionbar();
|
||||
@@ -200,9 +217,9 @@ export class ChartView implements IPanelView {
|
||||
let option = ChartOptions[this.options.type].find(e => e.configEntry === key);
|
||||
if (option && option.if) {
|
||||
if (option.if(this.options)) {
|
||||
new Builder(this.optionMap[key]).show();
|
||||
new Builder(this.optionMap[key].element).show();
|
||||
} else {
|
||||
new Builder(this.optionMap[key]).hide();
|
||||
new Builder(this.optionMap[key].element).hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,57 +244,104 @@ export class ChartView implements IPanelView {
|
||||
label.innerText = option.label;
|
||||
let optionContainer = $('div.option-container');
|
||||
optionContainer.appendChild(label);
|
||||
let setFunc: (val) => void;
|
||||
let value = this.state ? this.state.options[option.configEntry] || option.default : option.default;
|
||||
switch (option.type) {
|
||||
case ControlType.checkbox:
|
||||
let checkbox = new Checkbox(optionContainer, {
|
||||
label: '',
|
||||
ariaLabel: option.label,
|
||||
checked: option.default,
|
||||
checked: value,
|
||||
onChange: () => {
|
||||
if (this.options[option.configEntry] !== checkbox.checked) {
|
||||
this.options[option.configEntry] = checkbox.checked;
|
||||
this.insight.options = this.options;
|
||||
if (this.insight) {
|
||||
this.insight.options = this.options;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
setFunc = (val: boolean) => {
|
||||
checkbox.checked = val;
|
||||
};
|
||||
break;
|
||||
case ControlType.combo:
|
||||
let dropdown = new SelectBox(option.displayableOptions || option.options, 0, this._contextViewService);
|
||||
dropdown.select(option.options.indexOf(option.default));
|
||||
dropdown.select(option.options.indexOf(value));
|
||||
dropdown.render(optionContainer);
|
||||
dropdown.onDidSelect(e => {
|
||||
if (this.options[option.configEntry] !== option.options[e.index]) {
|
||||
this.options[option.configEntry] = option.options[e.index];
|
||||
this.insight.options = this.options;
|
||||
if (this.insight) {
|
||||
this.insight.options = this.options;
|
||||
}
|
||||
}
|
||||
});
|
||||
setFunc = (val: string) => {
|
||||
if (!isUndefinedOrNull(val)) {
|
||||
dropdown.select(option.options.indexOf(val));
|
||||
}
|
||||
};
|
||||
this.optionDisposables.push(attachSelectBoxStyler(dropdown, this._themeService));
|
||||
break;
|
||||
case ControlType.input:
|
||||
let input = new InputBox(optionContainer, this._contextViewService);
|
||||
input.value = option.default || '';
|
||||
input.value = value || '';
|
||||
input.onDidChange(e => {
|
||||
if (this.options[option.configEntry] !== e) {
|
||||
this.options[option.configEntry] = e;
|
||||
this.insight.options = this.options;
|
||||
if (this.insight) {
|
||||
this.insight.options = this.options;
|
||||
}
|
||||
}
|
||||
});
|
||||
setFunc = (val: string) => {
|
||||
if (!isUndefinedOrNull(val)) {
|
||||
input.value = val;
|
||||
}
|
||||
};
|
||||
this.optionDisposables.push(attachInputBoxStyler(input, this._themeService));
|
||||
break;
|
||||
case ControlType.numberInput:
|
||||
let numberInput = new InputBox(optionContainer, this._contextViewService, { type: 'number' });
|
||||
numberInput.value = option.default || '';
|
||||
numberInput.value = value || '';
|
||||
numberInput.onDidChange(e => {
|
||||
if (this.options[option.configEntry] !== Number(e)) {
|
||||
this.options[option.configEntry] = Number(e);
|
||||
this.insight.options = this.options;
|
||||
if (this.insight) {
|
||||
this.insight.options = this.options;
|
||||
}
|
||||
}
|
||||
});
|
||||
setFunc = (val: string) => {
|
||||
if (!isUndefinedOrNull(val)) {
|
||||
input.value = val;
|
||||
}
|
||||
};
|
||||
this.optionDisposables.push(attachInputBoxStyler(numberInput, this._themeService));
|
||||
break;
|
||||
}
|
||||
this.optionMap[option.configEntry] = optionContainer;
|
||||
this.optionMap[option.configEntry] = { element: optionContainer, set: setFunc };
|
||||
container.appendChild(optionContainer);
|
||||
this.options[option.configEntry] = option.default;
|
||||
this.options[option.configEntry] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public set state(val: ChartState) {
|
||||
this._state = val;
|
||||
if (this.state.options) {
|
||||
for (let key in this.state.options) {
|
||||
if (this.state.options.hasOwnProperty(key)) {
|
||||
this.options[key] = this.state.options[key];
|
||||
this.optionMap[key].set(this.state.options[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.state.dataId) {
|
||||
this.chart(this.state.dataId);
|
||||
}
|
||||
}
|
||||
|
||||
public get state(): ChartState {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
import { IInsightOptions, IInsight } from './interfaces';
|
||||
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
const noneLineGraphs = [ChartType.Doughnut, ChartType.Pie];
|
||||
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
'use strict';
|
||||
|
||||
import { Graph } from './graphInsight';
|
||||
import { ChartType, DataDirection } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
|
||||
import { Builder } from 'vs/base/browser/builder';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { DataDirection, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
import { ImageInsight } from './imageInsight';
|
||||
import { TableInsight } from './tableInsight';
|
||||
import { IInsightOptions, IInsight, InsightType, IInsightCtor } from './interfaces';
|
||||
import { CountInsight } from './countInsight';
|
||||
|
||||
import { Builder } from 'vs/base/browser/builder';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
|
||||
const defaultOptions: IInsightOptions = {
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
|
||||
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
import { ChartType, LegendPosition, DataDirection } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
import { DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
|
||||
import { DataDirection, ChartType, LegendPosition, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
export interface IInsightOptions {
|
||||
type: InsightType | ChartType;
|
||||
|
||||
@@ -55,6 +55,11 @@ const ACTIONBAR_HEIGHT = 100;
|
||||
// this handles min size if rows is greater than the min grid visible rows
|
||||
const MIN_GRID_HEIGHT = (MIN_GRID_HEIGHT_ROWS * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
|
||||
|
||||
export class GridPanelState {
|
||||
public tableStates: GridTableState[] = [];
|
||||
public scrollPosition: number;
|
||||
public collapsed = false;
|
||||
}
|
||||
|
||||
export interface IGridTableState {
|
||||
canBeMaximized: boolean;
|
||||
@@ -73,11 +78,12 @@ export class GridTableState {
|
||||
|
||||
private _canBeMaximized: boolean;
|
||||
|
||||
constructor(state?: IGridTableState) {
|
||||
if (state) {
|
||||
this._maximized = state.maximized;
|
||||
this._canBeMaximized = state.canBeMaximized;
|
||||
}
|
||||
/* The top row of the current scroll */
|
||||
public scrollPosition = 0;
|
||||
public selection: Slick.Range[];
|
||||
public activeCell: Slick.Cell;
|
||||
|
||||
constructor(public readonly resultId: number, public readonly batchId: number) {
|
||||
}
|
||||
|
||||
public get canBeMaximized(): boolean {
|
||||
@@ -103,10 +109,6 @@ export class GridTableState {
|
||||
this._maximized = val;
|
||||
this._onMaximizedChange.fire(val);
|
||||
}
|
||||
|
||||
public clone(): GridTableState {
|
||||
return new GridTableState({ canBeMaximized: this.canBeMaximized, maximized: this.maximized });
|
||||
}
|
||||
}
|
||||
|
||||
export class GridPanel extends ViewletPanel {
|
||||
@@ -120,6 +122,7 @@ export class GridPanel extends ViewletPanel {
|
||||
private runner: QueryRunner;
|
||||
|
||||
private maximizedGrid: GridTable<any>;
|
||||
private _state: GridPanelState;
|
||||
|
||||
constructor(
|
||||
options: IViewletPanelOptions,
|
||||
@@ -131,6 +134,16 @@ export class GridPanel extends ViewletPanel {
|
||||
) {
|
||||
super(options, keybindingService, contextMenuService, configurationService);
|
||||
this.splitView = new ScrollableSplitView(this.container, { enableResizing: false });
|
||||
this.splitView.onScroll(e => {
|
||||
if (this.state) {
|
||||
this.state.scrollPosition = e;
|
||||
}
|
||||
});
|
||||
this.onDidChange(e => {
|
||||
if (this.state) {
|
||||
this.state.collapsed = !this.isExpanded();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement): void {
|
||||
@@ -168,6 +181,10 @@ export class GridPanel extends ViewletPanel {
|
||||
this.maximumBodySize = this.tables.reduce((p, c) => {
|
||||
return p + c.maximumSize;
|
||||
}, 0);
|
||||
|
||||
if (this.state && this.state.scrollPosition) {
|
||||
this.splitView.setScrollPosition(this.state.scrollPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private addResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) {
|
||||
@@ -181,8 +198,18 @@ export class GridPanel extends ViewletPanel {
|
||||
let tables: GridTable<any>[] = [];
|
||||
|
||||
for (let set of resultsToAdd) {
|
||||
let tableState = new GridTableState();
|
||||
let table = this.instantiationService.createInstance(GridTable, this.runner, tableState, set);
|
||||
let tableState: GridTableState;
|
||||
if (this._state) {
|
||||
tableState = this.state.tableStates.find(e => e.batchId === set.batchId && e.resultId === set.id);
|
||||
}
|
||||
if (!tableState) {
|
||||
tableState = new GridTableState(set.id, set.batchId);
|
||||
if (this._state) {
|
||||
this._state.tableStates.push(tableState);
|
||||
}
|
||||
}
|
||||
let table = this.instantiationService.createInstance(GridTable, this.runner, set);
|
||||
table.state = tableState;
|
||||
tableState.onMaximizedChange(e => {
|
||||
if (e) {
|
||||
this.maximizeTable(table.id);
|
||||
@@ -241,6 +268,24 @@ export class GridPanel extends ViewletPanel {
|
||||
this.splitView.addViews(this.tables, this.tables.map(i => i.minimumSize));
|
||||
}
|
||||
}
|
||||
|
||||
public set state(val: GridPanelState) {
|
||||
this._state = val;
|
||||
this.tables.map(t => {
|
||||
let state = this.state.tableStates.find(s => s.batchId === t.resultSet.batchId && s.resultId === t.resultSet.id);
|
||||
if (!state) {
|
||||
this.state.tableStates.push(t.state);
|
||||
}
|
||||
if (state) {
|
||||
t.state = state;
|
||||
}
|
||||
});
|
||||
this.setExpanded(!this.state.collapsed);
|
||||
}
|
||||
|
||||
public get state(): GridPanelState {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
|
||||
class GridTable<T> extends Disposable implements IView {
|
||||
@@ -259,13 +304,14 @@ class GridTable<T> extends Disposable implements IView {
|
||||
public id = generateUuid();
|
||||
readonly element: HTMLElement = this.container;
|
||||
|
||||
private _state: GridTableState;
|
||||
|
||||
// this handles if the row count is small, like 4-5 rows
|
||||
private readonly maxSize = ((this.resultSet.rowCount) * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
|
||||
|
||||
constructor(
|
||||
private runner: QueryRunner,
|
||||
public state: GridTableState,
|
||||
private resultSet: sqlops.ResultSetSummary,
|
||||
public readonly resultSet: sqlops.ResultSetSummary,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@@ -357,10 +403,52 @@ class GridTable<T> extends Disposable implements IView {
|
||||
});
|
||||
this.actionBar.push(actions, { icon: true, label: false });
|
||||
|
||||
this.selectionModel.onSelectedRangesChanged.subscribe(e => {
|
||||
if (this.state) {
|
||||
this.state.selection = this.selectionModel.getSelectedRanges();
|
||||
}
|
||||
});
|
||||
|
||||
this.table.grid.onScroll.subscribe(e => {
|
||||
if (this.state) {
|
||||
this.state.scrollPosition = this.table.grid.getViewport().top;
|
||||
}
|
||||
});
|
||||
|
||||
this.table.grid.onActiveCellChanged.subscribe(e => {
|
||||
if (this.state) {
|
||||
this.state.activeCell = this.table.grid.getActiveCell();
|
||||
}
|
||||
});
|
||||
|
||||
this.setupState();
|
||||
}
|
||||
|
||||
private setupState() {
|
||||
// change actionbar on maximize change
|
||||
this.state.onMaximizedChange(this.rebuildActionBar, this);
|
||||
|
||||
this.state.onCanBeMaximizedChange(this.rebuildActionBar, this);
|
||||
|
||||
if (this.state.scrollPosition) {
|
||||
this.table.grid.scrollRowToTop(this.state.scrollPosition);
|
||||
}
|
||||
|
||||
if (this.state.selection) {
|
||||
this.selectionModel.setSelectedRanges(this.state.selection);
|
||||
}
|
||||
|
||||
if (this.state.activeCell) {
|
||||
this.table.setActiveCell(this.state.activeCell.row, this.state.activeCell.cell);
|
||||
}
|
||||
}
|
||||
|
||||
public get state(): GridTableState {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public set state(val: GridTableState) {
|
||||
this._state = val;
|
||||
}
|
||||
|
||||
private onTableClick(event: ITableMouseEvent) {
|
||||
|
||||
@@ -59,6 +59,11 @@ const TemplateIds = {
|
||||
ERROR: 'error'
|
||||
};
|
||||
|
||||
export class MessagePanelState {
|
||||
public scrollPosition: number;
|
||||
public collapsed = false;
|
||||
}
|
||||
|
||||
export class MessagePanel extends ViewletPanel {
|
||||
private ds = new MessageDataSource();
|
||||
private renderer = new MessageRenderer();
|
||||
@@ -67,6 +72,7 @@ export class MessagePanel extends ViewletPanel {
|
||||
private container = $('div message-tree').getHTMLElement();
|
||||
|
||||
private queryRunnerDisposables: IDisposable[] = [];
|
||||
private _state: MessagePanelState;
|
||||
|
||||
private tree: ITree;
|
||||
|
||||
@@ -86,6 +92,16 @@ export class MessagePanel extends ViewletPanel {
|
||||
renderer: this.renderer,
|
||||
controller: this.controller
|
||||
}, { keyboardSupport: false });
|
||||
this.tree.onDidScroll(e => {
|
||||
if (this.state) {
|
||||
this.state.scrollPosition = this.tree.getScrollPosition();
|
||||
}
|
||||
});
|
||||
this.onDidChange(e => {
|
||||
if (this.state) {
|
||||
this.state.collapsed = !this.isExpanded();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement): void {
|
||||
@@ -99,8 +115,12 @@ export class MessagePanel extends ViewletPanel {
|
||||
protected layoutBody(size: number): void {
|
||||
const previousScrollPosition = this.tree.getScrollPosition();
|
||||
this.tree.layout(size);
|
||||
if (previousScrollPosition === 1) {
|
||||
this.tree.setScrollPosition(1);
|
||||
if (this.state && this.state.scrollPosition) {
|
||||
this.tree.setScrollPosition(this.state.scrollPosition);
|
||||
} else {
|
||||
if (previousScrollPosition === 1) {
|
||||
this.tree.setScrollPosition(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,12 +144,19 @@ export class MessagePanel extends ViewletPanel {
|
||||
if (hasError) {
|
||||
this.setExpanded(true);
|
||||
}
|
||||
const previousScrollPosition = this.tree.getScrollPosition();
|
||||
this.tree.refresh(this.model).then(() => {
|
||||
if (previousScrollPosition === 1) {
|
||||
if (this.state.scrollPosition) {
|
||||
this.tree.refresh(this.model).then(() => {
|
||||
this.tree.setScrollPosition(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const previousScrollPosition = this.tree.getScrollPosition();
|
||||
this.tree.refresh(this.model).then(() => {
|
||||
if (previousScrollPosition === 1) {
|
||||
this.tree.setScrollPosition(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.maximumBodySize = this.model.messages.length * 22;
|
||||
}
|
||||
|
||||
private reset() {
|
||||
@@ -137,6 +164,17 @@ export class MessagePanel extends ViewletPanel {
|
||||
this.model.totalExecuteMessage = undefined;
|
||||
this.tree.refresh(this.model);
|
||||
}
|
||||
|
||||
public set state(val: MessagePanelState) {
|
||||
this._state = val;
|
||||
if (this.state.scrollPosition) {
|
||||
this.tree.setScrollPosition(this.state.scrollPosition);
|
||||
}
|
||||
this.setExpanded(!this.state.collapsed);
|
||||
}
|
||||
public get state(): MessagePanelState {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
|
||||
class MessageDataSource implements IDataSource {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
|
||||
import { QueryResultsInput, ResultsViewState } from 'sql/parts/query/common/queryResultsInput';
|
||||
import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/panel';
|
||||
import { IQueryModelService } from '../execution/queryModel';
|
||||
import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
||||
@@ -14,11 +14,10 @@ import { ChartTab } from './charting/chartTab';
|
||||
import { QueryPlanTab } from 'sql/parts/queryPlan/queryPlan';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
import { PanelViewlet } from 'vs/workbench/browser/parts/views/panelViewlet';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { once, anyEvent } from 'vs/base/common/event';
|
||||
|
||||
class ResultsView implements IPanelView {
|
||||
private panelViewlet: PanelViewlet;
|
||||
@@ -26,61 +25,72 @@ class ResultsView implements IPanelView {
|
||||
private messagePanel: MessagePanel;
|
||||
private container = document.createElement('div');
|
||||
private currentDimension: DOM.Dimension;
|
||||
private isGridRendered = false;
|
||||
private needsGridResize = false;
|
||||
private lastGridHeight: number;
|
||||
private _state: ResultsViewState;
|
||||
|
||||
constructor(instantiationService: IInstantiationService) {
|
||||
this.panelViewlet = instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
|
||||
this.gridPanel = instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results'), id: 'gridPanel' });
|
||||
this.messagePanel = instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages'), minimumBodySize: 0, id: 'messagePanel' });
|
||||
constructor(private instantiationService: IInstantiationService) {
|
||||
|
||||
this.panelViewlet = this.instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
|
||||
this.gridPanel = this.instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results'), id: 'gridPanel' });
|
||||
this.messagePanel = this.instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages'), minimumBodySize: 0, id: 'messagePanel' });
|
||||
this.gridPanel.render();
|
||||
this.messagePanel.render();
|
||||
this.panelViewlet.create(this.container).then(() => {
|
||||
this.gridPanel.setVisible(false);
|
||||
this.panelViewlet.addPanels([
|
||||
{ panel: this.messagePanel, size: this.messagePanel.minimumSize, index: 1 }
|
||||
]);
|
||||
});
|
||||
this.gridPanel.onDidChange(e => {
|
||||
anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(e => {
|
||||
let size = this.gridPanel.maximumBodySize;
|
||||
if (this.isGridRendered) {
|
||||
if (size < 1) {
|
||||
this.lastGridHeight = this.panelViewlet.getPanelSize(this.gridPanel);
|
||||
this.panelViewlet.removePanels([this.gridPanel]);
|
||||
// tell the panel is has been removed.
|
||||
this.gridPanel.layout(0);
|
||||
this.isGridRendered = false;
|
||||
}
|
||||
} else {
|
||||
if (this.currentDimension) {
|
||||
this.needsGridResize = false;
|
||||
if (size > 0) {
|
||||
this.panelViewlet.addPanels([
|
||||
{ panel: this.gridPanel, index: 0, size: this.lastGridHeight || Math.round(this.currentDimension.height * .7) }
|
||||
]);
|
||||
this.isGridRendered = true;
|
||||
}
|
||||
if (size < 1 && this.gridPanel.isVisible()) {
|
||||
this.gridPanel.setVisible(false);
|
||||
this.panelViewlet.removePanels([this.gridPanel]);
|
||||
this.gridPanel.layout(0);
|
||||
} else if (size > 0 && !this.gridPanel.isVisible()) {
|
||||
this.gridPanel.setVisible(true);
|
||||
let panelSize: number;
|
||||
if (this.state && this.state.gridPanelSize) {
|
||||
panelSize = this.state.gridPanelSize;
|
||||
} else if (this.currentDimension) {
|
||||
panelSize = Math.round(this.currentDimension.height * .7);
|
||||
} else {
|
||||
this.panelViewlet.addPanels([
|
||||
{ panel: this.gridPanel, index: 0, size: this.lastGridHeight || 200 }
|
||||
]);
|
||||
this.isGridRendered = true;
|
||||
panelSize = 200;
|
||||
this.needsGridResize = true;
|
||||
}
|
||||
this.panelViewlet.addPanels([{ panel: this.gridPanel, index: 0, size: panelSize }]);
|
||||
}
|
||||
});
|
||||
let gridResizeList = this.gridPanel.onDidChange(e => {
|
||||
if (this.currentDimension) {
|
||||
this.needsGridResize = false;
|
||||
this.panelViewlet.resizePanel(this.gridPanel, Math.round(this.currentDimension.height * .7));
|
||||
let resizeList = anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(() => {
|
||||
let panelSize: number;
|
||||
if (this.state && this.state.gridPanelSize) {
|
||||
panelSize = this.state.gridPanelSize;
|
||||
} else if (this.currentDimension) {
|
||||
panelSize = Math.round(this.currentDimension.height * .7);
|
||||
} else {
|
||||
panelSize = 200;
|
||||
this.needsGridResize = true;
|
||||
}
|
||||
});
|
||||
if (this.state.messagePanelSize) {
|
||||
this.panelViewlet.resizePanel(this.gridPanel, this.state.messagePanelSize);
|
||||
}
|
||||
this.panelViewlet.resizePanel(this.gridPanel, panelSize);
|
||||
})
|
||||
// once the user changes the sash we should stop trying to resize the grid
|
||||
once(this.panelViewlet.onDidSashChange)(e => {
|
||||
this.needsGridResize = false;
|
||||
gridResizeList.dispose();
|
||||
resizeList.dispose();
|
||||
});
|
||||
|
||||
this.panelViewlet.onDidSashChange(e => {
|
||||
if (this.state) {
|
||||
if (this.gridPanel.isExpanded()) {
|
||||
this.state.gridPanelSize = this.panelViewlet.getPanelSize(this.gridPanel);
|
||||
}
|
||||
if (this.messagePanel.isExpanded()) {
|
||||
this.state.messagePanelSize = this.panelViewlet.getPanelSize(this.messagePanel);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -96,7 +106,7 @@ class ResultsView implements IPanelView {
|
||||
}
|
||||
this.currentDimension = dimension;
|
||||
if (this.needsGridResize) {
|
||||
this.panelViewlet.resizePanel(this.gridPanel, Math.round(this.currentDimension.height * .7));
|
||||
this.panelViewlet.resizePanel(this.gridPanel, this.state.gridPanelSize || Math.round(this.currentDimension.height * .7));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,11 +122,21 @@ class ResultsView implements IPanelView {
|
||||
public hideResultHeader() {
|
||||
this.gridPanel.headerVisible = false;
|
||||
}
|
||||
|
||||
public set state(val: ResultsViewState) {
|
||||
this._state = val;
|
||||
this.gridPanel.state = val.gridPanelState;
|
||||
this.messagePanel.state = val.messagePanelState;
|
||||
}
|
||||
|
||||
public get state(): ResultsViewState {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
|
||||
class ResultsTab implements IPanelTab {
|
||||
public readonly title = nls.localize('resultsTabTitle', 'Results');
|
||||
public readonly identifier = UUID.generateUuid();
|
||||
public readonly identifier = 'resultsTab';
|
||||
public readonly view: ResultsView;
|
||||
|
||||
constructor(instantiationService: IInstantiationService) {
|
||||
@@ -144,6 +164,12 @@ export class QueryResultsView {
|
||||
this.chartTab = new ChartTab(instantiationService);
|
||||
this._panelView = new TabbedPanel(container, { showHeaderWhenSingleView: false });
|
||||
this.qpTab = new QueryPlanTab();
|
||||
this._panelView.pushTab(this.resultsTab);
|
||||
this._panelView.onTabChange(e => {
|
||||
if (this.input) {
|
||||
this.input.state.activeTab = e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public style() {
|
||||
@@ -151,11 +177,24 @@ export class QueryResultsView {
|
||||
|
||||
public set input(input: QueryResultsInput) {
|
||||
this._input = input;
|
||||
this.resultsTab.view.state = this.input.state;
|
||||
this.qpTab.view.state = this.input.state.queryPlanState;
|
||||
this.chartTab.view.state = this.input.state.chartState;
|
||||
let queryRunner = this.queryModelService._getQueryInfo(input.uri).queryRunner;
|
||||
this.resultsTab.queryRunner = queryRunner;
|
||||
this.chartTab.queryRunner = queryRunner;
|
||||
if (!this._panelView.contains(this.resultsTab)) {
|
||||
this._panelView.pushTab(this.resultsTab);
|
||||
if (this.input.state.visibleTabs.has(this.chartTab.identifier)) {
|
||||
if (!this._panelView.contains(this.chartTab)) {
|
||||
this._panelView.pushTab(this.chartTab);
|
||||
}
|
||||
}
|
||||
if (this.input.state.visibleTabs.has(this.qpTab.identifier)) {
|
||||
if (!this._panelView.contains(this.qpTab)) {
|
||||
this._panelView.pushTab(this.qpTab);
|
||||
}
|
||||
}
|
||||
if (this.input.state.activeTab) {
|
||||
this._panelView.showTab(this.input.state.activeTab);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +211,7 @@ export class QueryResultsView {
|
||||
}
|
||||
|
||||
public chartData(dataId: { resultId: number, batchId: number }): void {
|
||||
this.input.state.visibleTabs.add(this.chartTab.identifier);
|
||||
if (!this._panelView.contains(this.chartTab)) {
|
||||
this._panelView.pushTab(this.chartTab);
|
||||
}
|
||||
@@ -181,6 +221,7 @@ export class QueryResultsView {
|
||||
}
|
||||
|
||||
public showPlan(xml: string) {
|
||||
this.input.state.visibleTabs.add(this.qpTab.identifier);
|
||||
if (!this._panelView.contains(this.qpTab)) {
|
||||
this._panelView.pushTab(this.qpTab);
|
||||
}
|
||||
|
||||
@@ -13,9 +13,13 @@ import { localize } from 'vs/nls';
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
import { Builder } from 'vs/base/browser/builder';
|
||||
|
||||
export class QueryPlanState {
|
||||
xml: string;
|
||||
}
|
||||
|
||||
export class QueryPlanTab implements IPanelTab {
|
||||
public readonly title = localize('queryPlanTitle', 'Query Plan');
|
||||
public readonly identifier = UUID.generateUuid();
|
||||
public readonly identifier = 'QueryPlanTab';
|
||||
public readonly view: QueryPlanView;
|
||||
|
||||
constructor() {
|
||||
@@ -27,6 +31,7 @@ export class QueryPlanView implements IPanelView {
|
||||
private qp: QueryPlan;
|
||||
private xml: string;
|
||||
private container = document.createElement('div');
|
||||
private _state: QueryPlanState;
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
if (!this.qp) {
|
||||
@@ -47,6 +52,20 @@ export class QueryPlanView implements IPanelView {
|
||||
} else {
|
||||
this.xml = xml;
|
||||
}
|
||||
if (this.state) {
|
||||
this.state.xml = xml;
|
||||
}
|
||||
}
|
||||
|
||||
public set state(val: QueryPlanState) {
|
||||
this._state = val;
|
||||
if (this.state.xml) {
|
||||
this.showPlan(this.state.xml);
|
||||
}
|
||||
}
|
||||
|
||||
public get state(): QueryPlanState {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ function main() {
|
||||
'bootstrap': `../${ out }/bootstrap`
|
||||
},
|
||||
catchError: true,
|
||||
// {{SQL CARBON EDIT}}
|
||||
// {{SQL CARBON EDIT}}
|
||||
nodeModules: [
|
||||
'@angular/common',
|
||||
'@angular/core',
|
||||
@@ -70,6 +70,7 @@ function main() {
|
||||
'@angular/platform-browser-dynamic',
|
||||
'@angular/router',
|
||||
'angular2-grid',
|
||||
'ng2-charts/ng2-charts',
|
||||
'rxjs/add/observable/of',
|
||||
'rxjs/Observable',
|
||||
'rxjs/Subject',
|
||||
|
||||
Reference in New Issue
Block a user