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