mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Refactor chart viewer (#2381)
* working on adding charts * working on chart options * adding image and table insight * add chart viewing and handle a bunch of small bugs * formatting * remove unused code
This commit is contained in:
@@ -33,6 +33,7 @@
|
|||||||
"@angular/platform-browser-dynamic": "~4.1.3",
|
"@angular/platform-browser-dynamic": "~4.1.3",
|
||||||
"@angular/router": "~4.1.3",
|
"@angular/router": "~4.1.3",
|
||||||
"@angular/upgrade": "~4.1.3",
|
"@angular/upgrade": "~4.1.3",
|
||||||
|
"@types/chart.js": "^2.7.31",
|
||||||
"angular2-grid": "2.0.6",
|
"angular2-grid": "2.0.6",
|
||||||
"angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.4.4",
|
"angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.4.4",
|
||||||
"applicationinsights": "0.18.0",
|
"applicationinsights": "0.18.0",
|
||||||
|
|||||||
@@ -142,3 +142,8 @@ panel {
|
|||||||
.visibility.hidden {
|
.visibility.hidden {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tabBody {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|||||||
@@ -78,12 +78,16 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
} else {
|
} else {
|
||||||
this._headerVisible = false;
|
this._headerVisible = false;
|
||||||
}
|
}
|
||||||
this.$body = $('tabBody');
|
this.$body = $('.tabBody');
|
||||||
this.$body.attr('role', 'tabpanel');
|
this.$body.attr('role', 'tabpanel');
|
||||||
this.$body.attr('tabindex', '0');
|
this.$body.attr('tabindex', '0');
|
||||||
this.$parent.append(this.$body);
|
this.$parent.append(this.$body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public contains(tab: IPanelTab): boolean {
|
||||||
|
return this._tabMap.has(tab.identifier);
|
||||||
|
}
|
||||||
|
|
||||||
public pushTab(tab: IPanelTab): PanelTabIdentifier {
|
public pushTab(tab: IPanelTab): PanelTabIdentifier {
|
||||||
let internalTab = tab as IInternalPanelTab;
|
let internalTab = tab as IInternalPanelTab;
|
||||||
this._tabMap.set(tab.identifier, internalTab);
|
this._tabMap.set(tab.identifier, internalTab);
|
||||||
@@ -94,6 +98,7 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
if (this._tabMap.size > 1 && !this._headerVisible) {
|
if (this._tabMap.size > 1 && !this._headerVisible) {
|
||||||
this.$parent.append(this.$header, 0);
|
this.$parent.append(this.$header, 0);
|
||||||
this._headerVisible = true;
|
this._headerVisible = true;
|
||||||
|
this.layout(this._currentDimensions);
|
||||||
}
|
}
|
||||||
return tab.identifier as PanelTabIdentifier;
|
return tab.identifier as PanelTabIdentifier;
|
||||||
}
|
}
|
||||||
@@ -170,6 +175,8 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
|
|
||||||
public layout(dimension: Dimension): void {
|
public layout(dimension: Dimension): void {
|
||||||
this._currentDimensions = 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.$header.style('width', dimension.width + 'px');
|
||||||
this.$body.style('width', dimension.width + 'px');
|
this.$body.style('width', dimension.width + 'px');
|
||||||
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
|
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
|
||||||
|
|||||||
@@ -103,8 +103,8 @@ export const defaultChartConfig: IChartConfig = {
|
|||||||
})
|
})
|
||||||
export abstract class ChartInsight extends Disposable implements IInsightsView {
|
export abstract class ChartInsight extends Disposable implements IInsightsView {
|
||||||
private _isDataAvailable: boolean = false;
|
private _isDataAvailable: boolean = false;
|
||||||
private _hasInit: boolean = false;
|
protected _hasInit: boolean = false;
|
||||||
private _hasError: boolean = false;
|
protected _hasError: boolean = false;
|
||||||
private _options: any = {};
|
private _options: any = {};
|
||||||
|
|
||||||
@ViewChild(BaseChartDirective) private _chart: BaseChartDirective;
|
@ViewChild(BaseChartDirective) private _chart: BaseChartDirective;
|
||||||
@@ -113,7 +113,7 @@ export abstract class ChartInsight extends Disposable implements IInsightsView {
|
|||||||
protected _config: IChartConfig;
|
protected _config: IChartConfig;
|
||||||
protected _data: IInsightData;
|
protected _data: IInsightData;
|
||||||
|
|
||||||
private readonly CHART_ERROR_MESSAGE = nls.localize('chartErrorMessage', 'Chart cannot be displayed with the given data');
|
protected readonly CHART_ERROR_MESSAGE = nls.localize('chartErrorMessage', 'Chart cannot be displayed with the given data');
|
||||||
|
|
||||||
protected abstract get chartType(): ChartType;
|
protected abstract get chartType(): ChartType;
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
import { defaultChartConfig, IPointDataSet, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
import { defaultChartConfig, IPointDataSet, ChartType } 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 { mixin } from 'vs/base/common/objects';
|
import { mixin } from 'vs/base/common/objects';
|
||||||
import { clone } from 'sql/base/common/objects';
|
|
||||||
import { Color } from 'vs/base/common/color';
|
import { Color } from 'vs/base/common/color';
|
||||||
|
|
||||||
const defaultTimeSeriesConfig = mixin(clone(defaultChartConfig), { dataType: 'point', dataDirection: 'horizontal' }) as ILineConfig;
|
const defaultTimeSeriesConfig = mixin(clone(defaultChartConfig), { dataType: 'point', dataDirection: 'horizontal' }) as ILineConfig;
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insight
|
|||||||
`
|
`
|
||||||
})
|
})
|
||||||
export default class CountInsight implements IInsightsView {
|
export default class CountInsight implements IInsightsView {
|
||||||
private _labels: Array<string>;
|
protected _labels: Array<string>;
|
||||||
private _values: Array<string>;
|
protected _values: Array<string>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef) { }
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef) { }
|
||||||
|
|||||||
@@ -250,8 +250,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.grid-panel .action-label.icon {
|
.grid-panel .action-label.icon {
|
||||||
height: 35px;
|
height: 16px;
|
||||||
line-height: 35px;
|
|
||||||
min-width: 28px;
|
min-width: 28px;
|
||||||
background-size: 16px;
|
background-size: 16px;
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
|
|||||||
@@ -73,9 +73,6 @@
|
|||||||
-->
|
-->
|
||||||
<ng-template #lineInput>
|
<ng-template #lineInput>
|
||||||
<ng-container *ngTemplateOutlet="dataTypeInput"></ng-container>
|
<ng-container *ngTemplateOutlet="dataTypeInput"></ng-container>
|
||||||
<ng-template [ngIf]="showDataDirection">
|
|
||||||
<ng-container *ngTemplateOutlet="dataDirectionInput"></ng-container>
|
|
||||||
</ng-template>
|
|
||||||
<ng-template [ngIf]="showColumnsAsLabels">
|
<ng-template [ngIf]="showColumnsAsLabels">
|
||||||
<ng-container *ngTemplateOutlet="columnsAsLabelsInput"></ng-container>
|
<ng-container *ngTemplateOutlet="columnsAsLabelsInput"></ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
|||||||
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
|
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
|
||||||
import { Table } from 'sql/base/browser/ui/table/table';
|
import { Table } from 'sql/base/browser/ui/table/table';
|
||||||
import { GridTableState } from 'sql/parts/query/editor/gridPanel';
|
import { GridTableState } from 'sql/parts/query/editor/gridPanel';
|
||||||
|
import { IEditorService } from 'vs/platform/editor/common/editor';
|
||||||
|
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||||
|
import { QueryEditor } from './queryEditor';
|
||||||
|
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||||
|
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||||
|
|
||||||
export interface IGridActionContext {
|
export interface IGridActionContext {
|
||||||
cell: { row: number; cell: number; };
|
cell: { row: number; cell: number; };
|
||||||
@@ -158,3 +163,23 @@ export class MinimizeTableAction extends Action {
|
|||||||
return TPromise.as(true);
|
return TPromise.as(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ChartDataAction extends Action {
|
||||||
|
public static ID = 'grid.chart';
|
||||||
|
public static LABEL = localize('chart', 'Chart');
|
||||||
|
public static ICON = 'viewChart';
|
||||||
|
|
||||||
|
constructor(@IWorkbenchEditorService private editorService: IWorkbenchEditorService) {
|
||||||
|
super(ChartDataAction.ID, ChartDataAction.LABEL, ChartDataAction.ICON);
|
||||||
|
}
|
||||||
|
|
||||||
|
public run(context: IGridActionContext): TPromise<boolean> {
|
||||||
|
let activeEditor = this.editorService.getActiveEditor();
|
||||||
|
if (activeEditor instanceof QueryEditor) {
|
||||||
|
activeEditor.resultsEditor.chart({ batchId: context.batchId, resultId: context.resultId });
|
||||||
|
return TPromise.as(true);
|
||||||
|
} else {
|
||||||
|
return TPromise.as(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
204
src/sql/parts/query/editor/charting/chartOptions.ts
Normal file
204
src/sql/parts/query/editor/charting/chartOptions.ts
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
|
||||||
|
|
||||||
|
export enum ControlType {
|
||||||
|
combo,
|
||||||
|
numberInput,
|
||||||
|
input,
|
||||||
|
checkbox
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IChartOption {
|
||||||
|
label: string;
|
||||||
|
type: ControlType;
|
||||||
|
configEntry: string;
|
||||||
|
default: any;
|
||||||
|
options?: any[];
|
||||||
|
displayableOptions?: string[];
|
||||||
|
if?: (options: IInsightOptions) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IChartOptions {
|
||||||
|
general: Array<IChartOption>;
|
||||||
|
[x: string]: Array<IChartOption>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataDirectionOption: IChartOption = {
|
||||||
|
label: localize('dataDirectionLabel', 'Data Direction'),
|
||||||
|
type: ControlType.combo,
|
||||||
|
displayableOptions: [localize('verticalLabel', 'Vertical'), localize('horizontalLabel', 'Horizontal')],
|
||||||
|
options: [DataDirection.Vertical, DataDirection.Horizontal],
|
||||||
|
configEntry: 'dataDirection',
|
||||||
|
default: DataDirection.Horizontal
|
||||||
|
};
|
||||||
|
|
||||||
|
const columnsAsLabelsInput: IChartOption = {
|
||||||
|
label: localize('columnsAsLabelsLabel', 'Use column names as labels'),
|
||||||
|
type: ControlType.checkbox,
|
||||||
|
configEntry: 'columnsAsLabels',
|
||||||
|
default: false,
|
||||||
|
if: (options: IInsightOptions) => {
|
||||||
|
return options.dataDirection === DataDirection.Vertical && options.dataType !== DataType.Point;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const labelFirstColumnInput: IChartOption = {
|
||||||
|
label: localize('labelFirstColumnLabel', 'Use first column as row label'),
|
||||||
|
type: ControlType.checkbox,
|
||||||
|
configEntry: 'labelFirstColumn',
|
||||||
|
default: false,
|
||||||
|
if: (options: IInsightOptions) => {
|
||||||
|
return options.dataDirection === DataDirection.Horizontal && options.dataType !== DataType.Point;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const legendInput: IChartOption = {
|
||||||
|
label: localize('legendLabel', 'Legend Position'),
|
||||||
|
type: ControlType.combo,
|
||||||
|
options: Object.values(LegendPosition),
|
||||||
|
configEntry: 'legendPosition',
|
||||||
|
default: LegendPosition.Top
|
||||||
|
};
|
||||||
|
|
||||||
|
const yAxisLabelInput: IChartOption = {
|
||||||
|
label: localize('yAxisLabel', 'Y Axis Label'),
|
||||||
|
type: ControlType.input,
|
||||||
|
configEntry: 'yAxisLabel',
|
||||||
|
default: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const yAxisMinInput: IChartOption = {
|
||||||
|
label: localize('yAxisMinVal', 'Y Axis Minimum Value'),
|
||||||
|
type: ControlType.numberInput,
|
||||||
|
configEntry: 'yAxisMin',
|
||||||
|
default: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const yAxisMaxInput: IChartOption = {
|
||||||
|
label: localize('yAxisMaxVal', 'Y Axis Maximum Value'),
|
||||||
|
type: ControlType.numberInput,
|
||||||
|
configEntry: 'yAxisMax',
|
||||||
|
default: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const xAxisLabelInput: IChartOption = {
|
||||||
|
label: localize('xAxisLabel', 'X Axis Label'),
|
||||||
|
type: ControlType.input,
|
||||||
|
configEntry: 'xAxisLabel',
|
||||||
|
default: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const xAxisMinInput: IChartOption = {
|
||||||
|
label: localize('xAxisMinVal', 'X Axis Minimum Value'),
|
||||||
|
type: ControlType.numberInput,
|
||||||
|
configEntry: 'xAxisMin',
|
||||||
|
default: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const xAxisMaxInput: IChartOption = {
|
||||||
|
label: localize('xAxisMaxVal', 'X Axis Maximum Value'),
|
||||||
|
type: ControlType.numberInput,
|
||||||
|
configEntry: 'xAxisMax',
|
||||||
|
default: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataTypeInput: IChartOption = {
|
||||||
|
label: localize('dataTypeLabel', 'Data Type'),
|
||||||
|
type: ControlType.combo,
|
||||||
|
options: [DataType.Number, DataType.Point],
|
||||||
|
displayableOptions: [localize('numberLabel', 'Number'), localize('pointLabel', 'Point')],
|
||||||
|
configEntry: 'dataType',
|
||||||
|
default: DataType.Number
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ChartOptions: IChartOptions = {
|
||||||
|
general: [
|
||||||
|
{
|
||||||
|
label: localize('chartTypeLabel', 'Chart Type'),
|
||||||
|
type: ControlType.combo,
|
||||||
|
options: insightRegistry.getAllIds(),
|
||||||
|
configEntry: 'type',
|
||||||
|
default: ChartType.Bar
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[ChartType.Line]: [
|
||||||
|
dataTypeInput,
|
||||||
|
columnsAsLabelsInput,
|
||||||
|
labelFirstColumnInput,
|
||||||
|
yAxisLabelInput,
|
||||||
|
xAxisLabelInput,
|
||||||
|
legendInput
|
||||||
|
],
|
||||||
|
[ChartType.Scatter]: [
|
||||||
|
legendInput,
|
||||||
|
yAxisLabelInput,
|
||||||
|
xAxisLabelInput
|
||||||
|
],
|
||||||
|
[ChartType.TimeSeries]: [
|
||||||
|
legendInput,
|
||||||
|
yAxisLabelInput,
|
||||||
|
xAxisLabelInput
|
||||||
|
],
|
||||||
|
[ChartType.Bar]: [
|
||||||
|
dataDirectionOption,
|
||||||
|
columnsAsLabelsInput,
|
||||||
|
labelFirstColumnInput,
|
||||||
|
legendInput,
|
||||||
|
yAxisLabelInput,
|
||||||
|
yAxisMinInput,
|
||||||
|
yAxisMaxInput,
|
||||||
|
xAxisLabelInput
|
||||||
|
],
|
||||||
|
[ChartType.HorizontalBar]: [
|
||||||
|
dataDirectionOption,
|
||||||
|
columnsAsLabelsInput,
|
||||||
|
labelFirstColumnInput,
|
||||||
|
legendInput,
|
||||||
|
xAxisLabelInput,
|
||||||
|
xAxisMinInput,
|
||||||
|
xAxisMaxInput,
|
||||||
|
yAxisLabelInput
|
||||||
|
],
|
||||||
|
[ChartType.Pie]: [
|
||||||
|
dataDirectionOption,
|
||||||
|
columnsAsLabelsInput,
|
||||||
|
labelFirstColumnInput,
|
||||||
|
legendInput
|
||||||
|
],
|
||||||
|
[ChartType.Doughnut]: [
|
||||||
|
dataDirectionOption,
|
||||||
|
columnsAsLabelsInput,
|
||||||
|
labelFirstColumnInput,
|
||||||
|
legendInput
|
||||||
|
],
|
||||||
|
[InsightType.Table]: [],
|
||||||
|
[InsightType.Count]: [],
|
||||||
|
[InsightType.Image]: [
|
||||||
|
{
|
||||||
|
configEntry: 'encoding',
|
||||||
|
label: localize('encodingOption', 'Encoding'),
|
||||||
|
type: ControlType.input,
|
||||||
|
default: 'hex'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
configEntry: 'imageFormat',
|
||||||
|
label: localize('imageFormatOption', 'Image Format'),
|
||||||
|
type: ControlType.input,
|
||||||
|
default: 'jpeg'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
32
src/sql/parts/query/editor/charting/chartTab.ts
Normal file
32
src/sql/parts/query/editor/charting/chartTab.ts
Normal file
@@ -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.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { IPanelTab } from 'sql/base/browser/ui/panel/panel';
|
||||||
|
import { ChartView } from './chartView';
|
||||||
|
import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
||||||
|
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
|
||||||
|
export class ChartTab implements IPanelTab {
|
||||||
|
public readonly title = localize('chartTabTitle', 'Chart');
|
||||||
|
public readonly identifier = generateUuid();
|
||||||
|
public readonly view: ChartView;
|
||||||
|
|
||||||
|
constructor(@IInstantiationService instantiationService: IInstantiationService) {
|
||||||
|
this.view = instantiationService.createInstance(ChartView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set queryRunner(runner: QueryRunner) {
|
||||||
|
this.view.queryRunner = runner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public chart(dataId: { batchId: number, resultId: number}): void {
|
||||||
|
this.view.chart(dataId);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/sql/parts/query/editor/charting/chartView.css
Normal file
19
src/sql/parts/query/editor/charting/chartView.css
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.chart-view-container {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graph-container {
|
||||||
|
flex: 1 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options-container {
|
||||||
|
width: 250px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
240
src/sql/parts/query/editor/charting/chartView.ts
Normal file
240
src/sql/parts/query/editor/charting/chartView.ts
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import 'vs/css!./chartView';
|
||||||
|
|
||||||
|
import { IPanelView } from 'sql/base/browser/ui/panel/panel';
|
||||||
|
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 { Dimension, $, getContentHeight, getContentWidth } from 'vs/base/browser/dom';
|
||||||
|
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||||
|
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
|
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||||
|
import { Builder } from 'vs/base/browser/builder';
|
||||||
|
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';
|
||||||
|
|
||||||
|
declare class Proxy {
|
||||||
|
constructor(object, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChartView implements IPanelView {
|
||||||
|
private insight: Insight;
|
||||||
|
private _queryRunner: QueryRunner;
|
||||||
|
private _data: IInsightData;
|
||||||
|
private _currentData: { batchId: number, resultId: number };
|
||||||
|
|
||||||
|
private optionsControl: HTMLElement;
|
||||||
|
|
||||||
|
private options: IInsightOptions = {
|
||||||
|
type: ChartType.Bar
|
||||||
|
};
|
||||||
|
|
||||||
|
private container: HTMLElement;
|
||||||
|
private typeControls: HTMLElement;
|
||||||
|
private graphContainer: HTMLElement;
|
||||||
|
|
||||||
|
private optionDisposables: IDisposable[] = [];
|
||||||
|
|
||||||
|
private optionMap: { [x: string]: HTMLElement } = {};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@IContextViewService private _contextViewService: IContextViewService,
|
||||||
|
@IThemeService private _themeService: IThemeService,
|
||||||
|
@IInstantiationService private _instantiationService: IInstantiationService
|
||||||
|
) {
|
||||||
|
this.optionsControl = $('div.options-container');
|
||||||
|
let generalControls = $('div.general-controls');
|
||||||
|
this.optionsControl.appendChild(generalControls);
|
||||||
|
this.typeControls = $('div.type-controls');
|
||||||
|
this.optionsControl.appendChild(this.typeControls);
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
this.options = new Proxy(this.options, {
|
||||||
|
get: function (target, key, receiver) {
|
||||||
|
return Reflect.get(target, key, receiver);
|
||||||
|
},
|
||||||
|
set: function (target, key, value, receiver) {
|
||||||
|
let change = false;
|
||||||
|
if (target[key] !== value) {
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = Reflect.set(target, key, value, receiver);
|
||||||
|
|
||||||
|
if (change) {
|
||||||
|
if (key === 'type') {
|
||||||
|
self.buildOptions();
|
||||||
|
} else {
|
||||||
|
self.verifyOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}) as IInsightOptions;
|
||||||
|
|
||||||
|
ChartOptions.general.map(o => {
|
||||||
|
this.createOption(o, generalControls);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render(container: HTMLElement): void {
|
||||||
|
if (!this.container) {
|
||||||
|
this.container = $('div.chart-view-container');
|
||||||
|
this.graphContainer = $('div.graph-container');
|
||||||
|
this.container.appendChild(this.graphContainer);
|
||||||
|
this.container.appendChild(this.optionsControl);
|
||||||
|
this.insight = new Insight(this.graphContainer, this.options, this._instantiationService);
|
||||||
|
}
|
||||||
|
|
||||||
|
container.appendChild(this.container);
|
||||||
|
|
||||||
|
if (this._data) {
|
||||||
|
this.insight.data = this._data;
|
||||||
|
} else {
|
||||||
|
this.queryRunner = this._queryRunner;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public chart(dataId: { batchId: number, resultId: number }) {
|
||||||
|
this._currentData = dataId;
|
||||||
|
this.shouldGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
layout(dimension: Dimension): void {
|
||||||
|
if (this.insight) {
|
||||||
|
this.insight.layout(new Dimension(getContentWidth(this.graphContainer), getContentHeight(this.graphContainer)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public set queryRunner(runner: QueryRunner) {
|
||||||
|
this._queryRunner = runner;
|
||||||
|
this.shouldGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
private shouldGraph() {
|
||||||
|
// Check if we have the necessary information
|
||||||
|
if (this._currentData && this._queryRunner) {
|
||||||
|
// check if we are being asked to graph something that is available
|
||||||
|
let batch = this._queryRunner.batchSets[this._currentData.batchId];
|
||||||
|
if (batch) {
|
||||||
|
let summary = batch.resultSetSummaries[this._currentData.resultId];
|
||||||
|
if (summary) {
|
||||||
|
this._queryRunner.getQueryRows(0, summary.rowCount, 0, 0).then(d => {
|
||||||
|
this._data = {
|
||||||
|
columns: summary.columnInfo.map(c => c.columnName),
|
||||||
|
rows: d.resultSubset.rows.map(r => r.map(c => c.displayValue))
|
||||||
|
};
|
||||||
|
if (this.insight) {
|
||||||
|
this.insight.data = this._data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we have the necessary information but the information isn't avaiable yet,
|
||||||
|
// we should be smart and retrying when the information might be available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildOptions() {
|
||||||
|
dispose(this.optionDisposables);
|
||||||
|
this.optionDisposables = [];
|
||||||
|
this.optionMap = {};
|
||||||
|
new Builder(this.typeControls).clearChildren();
|
||||||
|
ChartOptions[this.options.type].map(o => {
|
||||||
|
this.createOption(o, this.typeControls);
|
||||||
|
});
|
||||||
|
if (this.insight) {
|
||||||
|
this.insight.options = this.options;
|
||||||
|
}
|
||||||
|
this.verifyOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private verifyOptions() {
|
||||||
|
for (let key in this.optionMap) {
|
||||||
|
if (this.optionMap.hasOwnProperty(key)) {
|
||||||
|
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();
|
||||||
|
} else {
|
||||||
|
new Builder(this.optionMap[key]).hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createOption(option: IChartOption, container: HTMLElement) {
|
||||||
|
let label = $('div');
|
||||||
|
label.innerText = option.label;
|
||||||
|
let optionContainer = $('div.option-container');
|
||||||
|
optionContainer.appendChild(label);
|
||||||
|
switch (option.type) {
|
||||||
|
case ControlType.checkbox:
|
||||||
|
let checkbox = new Checkbox(optionContainer, {
|
||||||
|
label: '',
|
||||||
|
ariaLabel: option.label,
|
||||||
|
checked: option.default,
|
||||||
|
onChange: () => {
|
||||||
|
if (this.options[option.configEntry] !== checkbox.checked) {
|
||||||
|
this.options[option.configEntry] = checkbox.checked;
|
||||||
|
this.insight.options = this.options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case ControlType.combo:
|
||||||
|
let dropdown = new SelectBox(option.displayableOptions || option.options, 0, this._contextViewService);
|
||||||
|
dropdown.select(option.options.indexOf(option.default));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.optionDisposables.push(attachSelectBoxStyler(dropdown, this._themeService));
|
||||||
|
break;
|
||||||
|
case ControlType.input:
|
||||||
|
let input = new InputBox(optionContainer, this._contextViewService);
|
||||||
|
input.value = option.default || '';
|
||||||
|
input.onDidChange(e => {
|
||||||
|
if (this.options[option.configEntry] !== e) {
|
||||||
|
this.options[option.configEntry] = e;
|
||||||
|
this.insight.options = this.options;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
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.onDidChange(e => {
|
||||||
|
if (this.options[option.configEntry] !== Number(e)) {
|
||||||
|
this.options[option.configEntry] = Number(e);
|
||||||
|
this.insight.options = this.options;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.optionDisposables.push(attachInputBoxStyler(numberInput, this._themeService));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.optionMap[option.configEntry] = optionContainer;
|
||||||
|
container.appendChild(optionContainer);
|
||||||
|
this.options[option.configEntry] = option.default;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.count-label-container {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-container {
|
||||||
|
font-size: 20px
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-container {
|
||||||
|
|
||||||
|
}
|
||||||
45
src/sql/parts/query/editor/charting/insights/countInsight.ts
Normal file
45
src/sql/parts/query/editor/charting/insights/countInsight.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import 'vs/css!./countInsight';
|
||||||
|
|
||||||
|
import { IInsight, InsightType } from './interfaces';
|
||||||
|
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||||
|
|
||||||
|
import { $ } from 'vs/base/browser/dom';
|
||||||
|
import { Builder } from 'vs/base/browser/builder';
|
||||||
|
|
||||||
|
export class CountInsight implements IInsight {
|
||||||
|
public options;
|
||||||
|
public static readonly types = [InsightType.Count];
|
||||||
|
public readonly types = CountInsight.types;
|
||||||
|
|
||||||
|
private countImage: HTMLElement;
|
||||||
|
|
||||||
|
constructor(container: HTMLElement, options: any) {
|
||||||
|
this.countImage = $('div');
|
||||||
|
container.appendChild(this.countImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public layout() { }
|
||||||
|
|
||||||
|
set data(data: IInsightData) {
|
||||||
|
new Builder(this.countImage).empty();
|
||||||
|
|
||||||
|
for (let i = 0; i < data.columns.length; i++) {
|
||||||
|
let container = $('div.count-label-container');
|
||||||
|
let label = $('span.label-container');
|
||||||
|
label.innerText = data.columns[i];
|
||||||
|
let value = $('span.value-container');
|
||||||
|
value.innerText = data.rows[0][i];
|
||||||
|
container.appendChild(label);
|
||||||
|
container.appendChild(value);
|
||||||
|
this.countImage.appendChild(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
321
src/sql/parts/query/editor/charting/insights/graphInsight.ts
Normal file
321
src/sql/parts/query/editor/charting/insights/graphInsight.ts
Normal file
@@ -0,0 +1,321 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { Chart as ChartJs } from 'chart.js';
|
||||||
|
|
||||||
|
import { mixin } from 'vs/base/common/objects';
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
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';
|
||||||
|
|
||||||
|
const noneLineGraphs = [ChartType.Doughnut, ChartType.Pie];
|
||||||
|
|
||||||
|
const defaultOptions: IInsightOptions = {
|
||||||
|
type: ChartType.Bar,
|
||||||
|
dataDirection: DataDirection.Horizontal
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Graph implements IInsight {
|
||||||
|
private _options: IInsightOptions;
|
||||||
|
private canvas: HTMLCanvasElement;
|
||||||
|
private chartjs: ChartJs;
|
||||||
|
private _data: IInsightData;
|
||||||
|
|
||||||
|
public static readonly types = [ChartType.Bar, ChartType.Doughnut, ChartType.HorizontalBar, ChartType.Line, ChartType.Pie, ChartType.Scatter, ChartType.TimeSeries];
|
||||||
|
public readonly types = Graph.types;
|
||||||
|
|
||||||
|
private _theme: ITheme;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
container: HTMLElement, options: IInsightOptions = defaultOptions,
|
||||||
|
@IThemeService themeService: IThemeService
|
||||||
|
) {
|
||||||
|
this._theme = themeService.getTheme();
|
||||||
|
themeService.onThemeChange(e => {
|
||||||
|
this._theme = e;
|
||||||
|
this.data = this._data;
|
||||||
|
});
|
||||||
|
this._options = mixin(options, defaultOptions, false);
|
||||||
|
|
||||||
|
let canvasContainer = document.createElement('div');
|
||||||
|
canvasContainer.style.width = '100%';
|
||||||
|
canvasContainer.style.height = '100%';
|
||||||
|
|
||||||
|
this.canvas = document.createElement('canvas');
|
||||||
|
canvasContainer.appendChild(this.canvas);
|
||||||
|
|
||||||
|
container.appendChild(canvasContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public layout() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public set data(data: IInsightData) {
|
||||||
|
this._data = data;
|
||||||
|
let chartData: Array<ChartJs.ChartDataSets>;
|
||||||
|
let labels: Array<string>;
|
||||||
|
|
||||||
|
if (this.options.dataDirection === DataDirection.Horizontal) {
|
||||||
|
if (this.options.labelFirstColumn) {
|
||||||
|
labels = data.columns.slice(1);
|
||||||
|
} else {
|
||||||
|
labels = data.columns;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
labels = data.rows.map(row => row[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.dataDirection === DataDirection.Horizontal) {
|
||||||
|
if (this.options.labelFirstColumn) {
|
||||||
|
chartData = data.rows.map((row) => {
|
||||||
|
return {
|
||||||
|
data: row.map(item => Number(item)).slice(1),
|
||||||
|
label: row[0]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
chartData = data.rows.map((row, i) => {
|
||||||
|
return {
|
||||||
|
data: row.map(item => Number(item)),
|
||||||
|
label: localize('series', 'Series {0}', i)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.options.columnsAsLabels) {
|
||||||
|
chartData = data.rows[0].slice(1).map((row, i) => {
|
||||||
|
return {
|
||||||
|
data: data.rows.map(row => Number(row[i + 1])),
|
||||||
|
label: data.columns[i + 1]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
chartData = data.rows[0].slice(1).map((row, i) => {
|
||||||
|
return {
|
||||||
|
data: data.rows.map(row => Number(row[i + 1])),
|
||||||
|
label: localize('series', 'Series {0}', i + 1)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chartData = chartData.map((c, i) => {
|
||||||
|
return mixin(c, getColors(this.options.type, i, c.data.length), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.chartjs) {
|
||||||
|
this.chartjs.data.datasets = chartData;
|
||||||
|
this.chartjs.config.type = this.options.type;
|
||||||
|
this.chartjs.data.labels = labels;
|
||||||
|
this.chartjs.options = this.transformOptions(this.options);
|
||||||
|
this.chartjs.update(0);
|
||||||
|
} else {
|
||||||
|
this.chartjs = new ChartJs(this.canvas.getContext('2d'), {
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: chartData
|
||||||
|
},
|
||||||
|
type: this.options.type,
|
||||||
|
options: {
|
||||||
|
maintainAspectRatio: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private transformOptions(options: IInsightOptions): ChartJs.ChartOptions {
|
||||||
|
let retval: ChartJs.ChartOptions = {};
|
||||||
|
retval.maintainAspectRatio = false;
|
||||||
|
|
||||||
|
let foregroundColor = this._theme.getColor(colors.editorForeground);
|
||||||
|
let foreground = foregroundColor ? foregroundColor.toString() : null;
|
||||||
|
let gridLinesColor = this._theme.getColor(editorLineNumbers);
|
||||||
|
let gridLines = gridLinesColor ? gridLinesColor.toString() : null;
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
retval.scales = {};
|
||||||
|
// we only want to include axis if it is a axis based graph type
|
||||||
|
if (!noneLineGraphs.includes(options.type as ChartType)) {
|
||||||
|
retval.scales.xAxes = [{
|
||||||
|
scaleLabel: {
|
||||||
|
fontColor: foreground,
|
||||||
|
labelString: options.xAxisLabel,
|
||||||
|
display: options.xAxisLabel ? true : false
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
fontColor: foreground,
|
||||||
|
max: options.xAxisMax,
|
||||||
|
min: options.xAxisMin
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
color: gridLines
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
retval.scales.yAxes = [{
|
||||||
|
scaleLabel: {
|
||||||
|
fontColor: foreground,
|
||||||
|
labelString: options.yAxisLabel,
|
||||||
|
display: options.yAxisLabel ? true : false
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
fontColor: foreground,
|
||||||
|
max: options.yAxisMax,
|
||||||
|
min: options.yAxisMin
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
color: gridLines
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
retval.legend = {
|
||||||
|
position: options.legendPosition as ChartJs.PositionType,
|
||||||
|
display: options.legendPosition !== LegendPosition.None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set options(options: IInsightOptions) {
|
||||||
|
this._options = options;
|
||||||
|
this.data = this._data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get options(): IInsightOptions {
|
||||||
|
return this._options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Following code is pulled from ng2-charting in order to keep the same
|
||||||
|
* color functionality
|
||||||
|
*/
|
||||||
|
|
||||||
|
const defaultColors = [
|
||||||
|
[255, 99, 132],
|
||||||
|
[54, 162, 235],
|
||||||
|
[255, 206, 86],
|
||||||
|
[231, 233, 237],
|
||||||
|
[75, 192, 192],
|
||||||
|
[151, 187, 205],
|
||||||
|
[220, 220, 220],
|
||||||
|
[247, 70, 74],
|
||||||
|
[70, 191, 189],
|
||||||
|
[253, 180, 92],
|
||||||
|
[148, 159, 177],
|
||||||
|
[77, 83, 96]
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
function rgba(colour, alpha) {
|
||||||
|
return 'rgba(' + colour.concat(alpha).join(',') + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomInt(min, max) {
|
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomColor() {
|
||||||
|
return [getRandomInt(0, 255), getRandomInt(0, 255), getRandomInt(0, 255)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate colors for line|bar charts
|
||||||
|
* @param index
|
||||||
|
* @returns {number[]|Color}
|
||||||
|
*/
|
||||||
|
function generateColor(index) {
|
||||||
|
return defaultColors[index] || getRandomColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate colors for pie|doughnut charts
|
||||||
|
* @param count
|
||||||
|
* @returns {Colors}
|
||||||
|
*/
|
||||||
|
function generateColors(count) {
|
||||||
|
var colorsArr = new Array(count);
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
colorsArr[i] = defaultColors[i] || getRandomColor();
|
||||||
|
}
|
||||||
|
return colorsArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLineColor(colors) {
|
||||||
|
return {
|
||||||
|
backgroundColor: rgba(colors, 0.4),
|
||||||
|
borderColor: rgba(colors, 1),
|
||||||
|
pointBackgroundColor: rgba(colors, 1),
|
||||||
|
pointBorderColor: '#fff',
|
||||||
|
pointHoverBackgroundColor: '#fff',
|
||||||
|
pointHoverBorderColor: rgba(colors, 0.8)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatBarColor(colors) {
|
||||||
|
return {
|
||||||
|
backgroundColor: rgba(colors, 0.6),
|
||||||
|
borderColor: rgba(colors, 1),
|
||||||
|
hoverBackgroundColor: rgba(colors, 0.8),
|
||||||
|
hoverBorderColor: rgba(colors, 1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPieColors(colors) {
|
||||||
|
return {
|
||||||
|
backgroundColor: colors.map(function (color) { return rgba(color, 0.6); }),
|
||||||
|
borderColor: colors.map(function () { return '#fff'; }),
|
||||||
|
pointBackgroundColor: colors.map(function (color) { return rgba(color, 1); }),
|
||||||
|
pointBorderColor: colors.map(function () { return '#fff'; }),
|
||||||
|
pointHoverBackgroundColor: colors.map(function (color) { return rgba(color, 1); }),
|
||||||
|
pointHoverBorderColor: colors.map(function (color) { return rgba(color, 1); })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPolarAreaColors(colors) {
|
||||||
|
return {
|
||||||
|
backgroundColor: colors.map(function (color) { return rgba(color, 0.6); }),
|
||||||
|
borderColor: colors.map(function (color) { return rgba(color, 1); }),
|
||||||
|
hoverBackgroundColor: colors.map(function (color) { return rgba(color, 0.8); }),
|
||||||
|
hoverBorderColor: colors.map(function (color) { return rgba(color, 1); })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate colors by chart type
|
||||||
|
* @param chartType
|
||||||
|
* @param index
|
||||||
|
* @param count
|
||||||
|
* @returns {Color}
|
||||||
|
*/
|
||||||
|
function getColors(chartType, index, count) {
|
||||||
|
if (chartType === 'pie' || chartType === 'doughnut') {
|
||||||
|
return formatPieColors(generateColors(count));
|
||||||
|
}
|
||||||
|
if (chartType === 'polarArea') {
|
||||||
|
return formatPolarAreaColors(generateColors(count));
|
||||||
|
}
|
||||||
|
if (chartType === 'line' || chartType === 'radar') {
|
||||||
|
return formatLineColor(generateColor(index));
|
||||||
|
}
|
||||||
|
if (chartType === 'bar' || chartType === 'horizontalBar') {
|
||||||
|
return formatBarColor(generateColor(index));
|
||||||
|
}
|
||||||
|
return generateColor(index);
|
||||||
|
}
|
||||||
71
src/sql/parts/query/editor/charting/insights/imageInsight.ts
Normal file
71
src/sql/parts/query/editor/charting/insights/imageInsight.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { IInsight, IInsightOptions, InsightType } from './interfaces';
|
||||||
|
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||||
|
|
||||||
|
import { $ } from 'vs/base/browser/dom';
|
||||||
|
import { mixin } from 'vs/base/common/objects';
|
||||||
|
|
||||||
|
export interface IConfig extends IInsightOptions {
|
||||||
|
encoding?: string;
|
||||||
|
imageFormat?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultConfig: IConfig = {
|
||||||
|
type: InsightType.Image,
|
||||||
|
encoding: 'hex',
|
||||||
|
imageFormat: 'jpeg'
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ImageInsight implements IInsight {
|
||||||
|
|
||||||
|
public static readonly types = [InsightType.Image];
|
||||||
|
public readonly types = ImageInsight.types;
|
||||||
|
|
||||||
|
private _options: IConfig;
|
||||||
|
|
||||||
|
private imageEle: HTMLImageElement;
|
||||||
|
|
||||||
|
constructor(container: HTMLElement, options: IConfig) {
|
||||||
|
this.imageEle = $('img');
|
||||||
|
container.appendChild(this.imageEle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public layout() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
set options(config: IConfig) {
|
||||||
|
this._options = mixin(config, defaultConfig, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
get options(): IConfig {
|
||||||
|
return this._options;
|
||||||
|
}
|
||||||
|
|
||||||
|
set data(data: IInsightData) {
|
||||||
|
if (data.rows && data.rows.length > 0 && data.rows[0].length > 0) {
|
||||||
|
let img = data.rows[0][0];
|
||||||
|
if (this._options.encoding === 'hex') {
|
||||||
|
img = ImageInsight._hexToBase64(img);
|
||||||
|
}
|
||||||
|
this.imageEle.src = `data:image/${this._options.imageFormat};base64,${img}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _hexToBase64(hexVal: string) {
|
||||||
|
|
||||||
|
if (hexVal.startsWith('0x')) {
|
||||||
|
hexVal = hexVal.slice(2);
|
||||||
|
}
|
||||||
|
// should be able to be replaced with new Buffer(hexVal, 'hex').toString('base64')
|
||||||
|
return btoa(String.fromCharCode.apply(null, hexVal.replace(/\r|\n/g, '').replace(/([\da-fA-F]{2}) ?/g, '0x$1 ').replace(/ +$/, '').split(' ')));
|
||||||
|
}
|
||||||
|
}
|
||||||
98
src/sql/parts/query/editor/charting/insights/insight.ts
Normal file
98
src/sql/parts/query/editor/charting/insights/insight.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'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 { ImageInsight } from './imageInsight';
|
||||||
|
import { TableInsight } from './tableInsight';
|
||||||
|
import { IInsightOptions, IInsight, InsightType, IInsightCtor } from './interfaces';
|
||||||
|
import { CountInsight } from './countInsight';
|
||||||
|
import { Dimension } from 'vs/base/browser/dom';
|
||||||
|
|
||||||
|
const defaultOptions: IInsightOptions = {
|
||||||
|
type: ChartType.Bar,
|
||||||
|
dataDirection: DataDirection.Horizontal
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Insight {
|
||||||
|
private insight: IInsight;
|
||||||
|
|
||||||
|
private _options: IInsightOptions;
|
||||||
|
private _data: IInsightData;
|
||||||
|
private dim: Dimension
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private container: HTMLElement, options: IInsightOptions = defaultOptions,
|
||||||
|
@IInstantiationService private _instantiationService: IInstantiationService
|
||||||
|
) {
|
||||||
|
this.options = options;
|
||||||
|
this.buildInsight();
|
||||||
|
}
|
||||||
|
|
||||||
|
public layout(dim: Dimension) {
|
||||||
|
this.dim = dim;
|
||||||
|
this.insight.layout(dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set options(val: IInsightOptions) {
|
||||||
|
this._options = val;
|
||||||
|
if (this.insight) {
|
||||||
|
// check to see if we need to change the insight type
|
||||||
|
if (!this.insight.types.includes(val.type)) {
|
||||||
|
this.buildInsight();
|
||||||
|
} else {
|
||||||
|
this.insight.options = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get options(): IInsightOptions {
|
||||||
|
return this._options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set data(val: IInsightData) {
|
||||||
|
this._data = val;
|
||||||
|
if (this.insight) {
|
||||||
|
this.insight.data = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildInsight() {
|
||||||
|
if (this.insight) {
|
||||||
|
this.insight.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
new Builder(this.container).empty();
|
||||||
|
|
||||||
|
let ctor = this.findctor(this.options.type);
|
||||||
|
|
||||||
|
if (ctor) {
|
||||||
|
this.insight = this._instantiationService.createInstance(ctor, this.container, this.options);
|
||||||
|
this.insight.layout(this.dim);
|
||||||
|
if (this._data) {
|
||||||
|
this.insight.data = this._data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private findctor(type: ChartType | InsightType): IInsightCtor {
|
||||||
|
if (Graph.types.includes(type as ChartType)) {
|
||||||
|
return Graph;
|
||||||
|
} else if (ImageInsight.types.includes(type as InsightType)) {
|
||||||
|
return ImageInsight;
|
||||||
|
} else if (TableInsight.types.includes(type as InsightType)) {
|
||||||
|
return TableInsight;
|
||||||
|
} else if (CountInsight.types.includes(type as InsightType)) {
|
||||||
|
return CountInsight;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/sql/parts/query/editor/charting/insights/interfaces.ts
Normal file
47
src/sql/parts/query/editor/charting/insights/interfaces.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
export interface IInsightOptions {
|
||||||
|
type: InsightType | ChartType;
|
||||||
|
dataDirection?: DataDirection;
|
||||||
|
dataType?: DataType;
|
||||||
|
labelFirstColumn?: boolean;
|
||||||
|
columnsAsLabels?: boolean;
|
||||||
|
legendPosition?: LegendPosition;
|
||||||
|
yAxisLabel?: string;
|
||||||
|
yAxisMin?: number;
|
||||||
|
yAxisMax?: number;
|
||||||
|
xAxisLabel?: string;
|
||||||
|
xAxisMin?: number;
|
||||||
|
xAxisMax?: number;
|
||||||
|
encoding?: string;
|
||||||
|
imageFormat?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IInsight {
|
||||||
|
options: IInsightOptions;
|
||||||
|
data: IInsightData;
|
||||||
|
readonly types: Array<InsightType | ChartType>;
|
||||||
|
layout(dim: Dimension);
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IInsightCtor {
|
||||||
|
new (container: HTMLElement, options: IInsightOptions, ...services: { _serviceBrand: any; }[]): IInsight;
|
||||||
|
readonly types: Array<InsightType | ChartType>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum InsightType {
|
||||||
|
Image = 'image',
|
||||||
|
Table = 'table',
|
||||||
|
Count = 'count'
|
||||||
|
}
|
||||||
74
src/sql/parts/query/editor/charting/insights/tableInsight.ts
Normal file
74
src/sql/parts/query/editor/charting/insights/tableInsight.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { IInsight, InsightType } from './interfaces';
|
||||||
|
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||||
|
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
|
||||||
|
import { Table } from 'sql/base/browser/ui/table/table';
|
||||||
|
import { attachTableStyler } from 'sql/common/theme/styler';
|
||||||
|
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
||||||
|
|
||||||
|
import { $, Dimension } from 'vs/base/browser/dom';
|
||||||
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
|
|
||||||
|
export class TableInsight extends Disposable implements IInsight {
|
||||||
|
public static readonly types = [InsightType.Table];
|
||||||
|
public readonly types = TableInsight.types;
|
||||||
|
|
||||||
|
private table: Table<any>;
|
||||||
|
private dataView: TableDataView<any>;
|
||||||
|
private columns: Slick.Column<any>[];
|
||||||
|
|
||||||
|
constructor(container: HTMLElement, options: any,
|
||||||
|
@IThemeService themeService: IThemeService
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
let tableContainer = $('div');
|
||||||
|
tableContainer.style.width = '100%';
|
||||||
|
tableContainer.style.height = '100%';
|
||||||
|
container.appendChild(tableContainer);
|
||||||
|
this.dataView = new TableDataView();
|
||||||
|
this.table = new Table(tableContainer, { dataProvider: this.dataView }, { showRowNumber: true });
|
||||||
|
this.table.setSelectionModel(new CellSelectionModel());
|
||||||
|
this._register(attachTableStyler(this.table, themeService));
|
||||||
|
}
|
||||||
|
|
||||||
|
set data(data: IInsightData) {
|
||||||
|
this.dataView.clear();
|
||||||
|
this.dataView.push(transformData(data.rows, data.columns));
|
||||||
|
this.columns = transformColumns(data.columns);
|
||||||
|
this.table.columns = this.columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
layout(dim: Dimension) {
|
||||||
|
this.table.layout(dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
public options;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformData(rows: string[][], columns: string[]): { [key: string]: string }[] {
|
||||||
|
return rows.map(row => {
|
||||||
|
let object: { [key: string]: string } = {};
|
||||||
|
row.forEach((val, index) => {
|
||||||
|
object[columns[index]] = val;
|
||||||
|
});
|
||||||
|
return object;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformColumns(columns: string[]): Slick.Column<any>[] {
|
||||||
|
return columns.map(col => {
|
||||||
|
return <Slick.Column<any>>{
|
||||||
|
name: col,
|
||||||
|
id: col,
|
||||||
|
field: col
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ import { ScrollableSplitView } from 'sql/base/browser/ui/scrollableSplitview/scr
|
|||||||
import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin';
|
import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin';
|
||||||
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
|
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
|
||||||
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
|
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
|
||||||
import { IGridActionContext, SaveResultAction, CopyResultAction, SelectAllGridAction, MaximizeTableAction, MinimizeTableAction } from 'sql/parts/query/editor/actions';
|
import { IGridActionContext, SaveResultAction, CopyResultAction, SelectAllGridAction, MaximizeTableAction, MinimizeTableAction, ChartDataAction } from 'sql/parts/query/editor/actions';
|
||||||
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
||||||
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
|
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
|
||||||
|
|
||||||
@@ -33,6 +33,7 @@ import { generateUuid } from 'vs/base/common/uuid';
|
|||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
import { Separator, ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
import { Separator, ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||||
import { Dimension, getContentWidth } from 'vs/base/browser/dom';
|
import { Dimension, getContentWidth } from 'vs/base/browser/dom';
|
||||||
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
|
||||||
const rowHeight = 29;
|
const rowHeight = 29;
|
||||||
const columnHeight = 26;
|
const columnHeight = 26;
|
||||||
@@ -93,7 +94,8 @@ export class GridPanel extends ViewletPanel {
|
|||||||
@IKeybindingService keybindingService: IKeybindingService,
|
@IKeybindingService keybindingService: IKeybindingService,
|
||||||
@IContextMenuService contextMenuService: IContextMenuService,
|
@IContextMenuService contextMenuService: IContextMenuService,
|
||||||
@IConfigurationService configurationService: IConfigurationService,
|
@IConfigurationService configurationService: IConfigurationService,
|
||||||
@IThemeService private themeService: IThemeService
|
@IThemeService private themeService: IThemeService,
|
||||||
|
@IInstantiationService private instantiationService: IInstantiationService
|
||||||
) {
|
) {
|
||||||
super(title, options, keybindingService, contextMenuService, configurationService);
|
super(title, options, keybindingService, contextMenuService, configurationService);
|
||||||
this.splitView = new ScrollableSplitView(this.container, { enableResizing: false });
|
this.splitView = new ScrollableSplitView(this.container, { enableResizing: false });
|
||||||
@@ -143,7 +145,7 @@ export class GridPanel extends ViewletPanel {
|
|||||||
|
|
||||||
for (let set of resultsToAdd) {
|
for (let set of resultsToAdd) {
|
||||||
let tableState = new GridTableState();
|
let tableState = new GridTableState();
|
||||||
let table = new GridTable(this.runner, tableState, set, this.contextMenuService);
|
let table = new GridTable(this.runner, tableState, set, this.contextMenuService, this.instantiationService);
|
||||||
tableState.onMaximizedChange(e => {
|
tableState.onMaximizedChange(e => {
|
||||||
if (e) {
|
if (e) {
|
||||||
this.maximizeTable(table.id);
|
this.maximizeTable(table.id);
|
||||||
@@ -222,7 +224,8 @@ class GridTable<T> extends Disposable implements IView {
|
|||||||
private runner: QueryRunner,
|
private runner: QueryRunner,
|
||||||
public state: GridTableState,
|
public state: GridTableState,
|
||||||
private resultSet: sqlops.ResultSetSummary,
|
private resultSet: sqlops.ResultSetSummary,
|
||||||
private contextMenuService: IContextMenuService
|
private contextMenuService: IContextMenuService,
|
||||||
|
private instantiationService: IInstantiationService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.container.style.width = '100%';
|
this.container.style.width = '100%';
|
||||||
@@ -283,7 +286,8 @@ class GridTable<T> extends Disposable implements IView {
|
|||||||
actions.push(
|
actions.push(
|
||||||
new SaveResultAction(SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveResultAction.SAVECSV_ICON, SaveFormat.CSV),
|
new SaveResultAction(SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveResultAction.SAVECSV_ICON, SaveFormat.CSV),
|
||||||
new SaveResultAction(SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveResultAction.SAVEEXCEL_ICON, SaveFormat.EXCEL),
|
new SaveResultAction(SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveResultAction.SAVEEXCEL_ICON, SaveFormat.EXCEL),
|
||||||
new SaveResultAction(SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveResultAction.SAVEJSON_ICON, SaveFormat.JSON)
|
new SaveResultAction(SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveResultAction.SAVEJSON_ICON, SaveFormat.JSON),
|
||||||
|
this.instantiationService.createInstance(ChartDataAction)
|
||||||
);
|
);
|
||||||
|
|
||||||
let actionBarContainer = document.createElement('div');
|
let actionBarContainer = document.createElement('div');
|
||||||
|
|||||||
@@ -151,6 +151,10 @@ export class QueryResultsEditor extends BaseEditor {
|
|||||||
return TPromise.wrap<void>(null);
|
return TPromise.wrap<void>(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public chart(dataId: { batchId: number, resultId: number }) {
|
||||||
|
this.resultsView.chartData(dataId);
|
||||||
|
}
|
||||||
|
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ 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 { Emitter } from 'vs/base/common/event';
|
import { ChartTab } from './charting/chartTab';
|
||||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
|
||||||
|
|
||||||
class ResultsView implements IPanelView {
|
class ResultsView implements IPanelView {
|
||||||
private panelViewlet: PanelViewlet;
|
private panelViewlet: PanelViewlet;
|
||||||
@@ -25,14 +24,6 @@ class ResultsView implements IPanelView {
|
|||||||
private messagePanel: MessagePanel;
|
private messagePanel: MessagePanel;
|
||||||
private container = document.createElement('div');
|
private container = document.createElement('div');
|
||||||
|
|
||||||
private _onRemove = new Emitter<void>();
|
|
||||||
public readonly onRemove = this._onRemove.event;
|
|
||||||
|
|
||||||
private _onLayout = new Emitter<void>();
|
|
||||||
public readonly onLayout = this._onLayout.event;
|
|
||||||
|
|
||||||
private queryRunnerDisposable: IDisposable[] = [];
|
|
||||||
|
|
||||||
constructor(instantiationService: IInstantiationService) {
|
constructor(instantiationService: IInstantiationService) {
|
||||||
this.panelViewlet = instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
|
this.panelViewlet = instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
|
||||||
this.gridPanel = instantiationService.createInstance(GridPanel, nls.localize('gridPanel', 'Results'), {});
|
this.gridPanel = instantiationService.createInstance(GridPanel, nls.localize('gridPanel', 'Results'), {});
|
||||||
@@ -61,6 +52,10 @@ class ResultsView implements IPanelView {
|
|||||||
this.gridPanel.queryRunner = runner;
|
this.gridPanel.queryRunner = runner;
|
||||||
this.messagePanel.queryRunner = runner;
|
this.messagePanel.queryRunner = runner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public hideResultHeader() {
|
||||||
|
this.gridPanel.headerVisible = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResultsTab implements IPanelTab {
|
class ResultsTab implements IPanelTab {
|
||||||
@@ -68,17 +63,8 @@ class ResultsTab implements IPanelTab {
|
|||||||
public readonly identifier = UUID.generateUuid();
|
public readonly identifier = UUID.generateUuid();
|
||||||
public readonly view: ResultsView;
|
public readonly view: ResultsView;
|
||||||
|
|
||||||
private _isAttached = false;
|
|
||||||
|
|
||||||
constructor(instantiationService: IInstantiationService) {
|
constructor(instantiationService: IInstantiationService) {
|
||||||
this.view = new ResultsView(instantiationService);
|
this.view = new ResultsView(instantiationService);
|
||||||
|
|
||||||
this.view.onLayout(() => this._isAttached = true, this);
|
|
||||||
this.view.onRemove(() => this._isAttached = false, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public isAttached(): boolean {
|
|
||||||
return this._isAttached;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public set queryRunner(runner: QueryRunner) {
|
public set queryRunner(runner: QueryRunner) {
|
||||||
@@ -90,6 +76,7 @@ export class QueryResultsView {
|
|||||||
private _panelView: TabbedPanel;
|
private _panelView: TabbedPanel;
|
||||||
private _input: QueryResultsInput;
|
private _input: QueryResultsInput;
|
||||||
private resultsTab: ResultsTab;
|
private resultsTab: ResultsTab;
|
||||||
|
private chartTab: ChartTab;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
@@ -97,19 +84,20 @@ export class QueryResultsView {
|
|||||||
@IQueryModelService private queryModelService: IQueryModelService
|
@IQueryModelService private queryModelService: IQueryModelService
|
||||||
) {
|
) {
|
||||||
this.resultsTab = new ResultsTab(instantiationService);
|
this.resultsTab = new ResultsTab(instantiationService);
|
||||||
|
this.chartTab = new ChartTab(instantiationService);
|
||||||
this._panelView = new TabbedPanel(container, { showHeaderWhenSingleView: false });
|
this._panelView = new TabbedPanel(container, { showHeaderWhenSingleView: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
public style() {
|
public style() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public set input(input: QueryResultsInput) {
|
public set input(input: QueryResultsInput) {
|
||||||
this._input = input;
|
this._input = input;
|
||||||
this.resultsTab.queryRunner = this.queryModelService._getQueryInfo(input.uri).queryRunner;
|
let queryRunner = this.queryModelService._getQueryInfo(input.uri).queryRunner;
|
||||||
// if (!this.resultsTab.isAttached) {
|
this.resultsTab.queryRunner = queryRunner;
|
||||||
|
this.chartTab.queryRunner = queryRunner;
|
||||||
|
|
||||||
this._panelView.pushTab(this.resultsTab);
|
this._panelView.pushTab(this.resultsTab);
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get input(): QueryResultsInput {
|
public get input(): QueryResultsInput {
|
||||||
@@ -119,4 +107,14 @@ export class QueryResultsView {
|
|||||||
public layout(dimension: DOM.Dimension) {
|
public layout(dimension: DOM.Dimension) {
|
||||||
this._panelView.layout(dimension);
|
this._panelView.layout(dimension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public chartData(dataId: { resultId: number, batchId: number }): void {
|
||||||
|
if (!this._panelView.contains(this.chartTab)) {
|
||||||
|
this._panelView.pushTab(this.chartTab);
|
||||||
|
this.resultsTab.view.hideResultHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._panelView.showTab(this.chartTab.identifier);
|
||||||
|
this.chartTab.chart(dataId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
668
src/typings/modules/chartjs/index.d.ts
vendored
Normal file
668
src/typings/modules/chartjs/index.d.ts
vendored
Normal file
@@ -0,0 +1,668 @@
|
|||||||
|
// Type definitions for Chart.js 2.7
|
||||||
|
// Project: https://github.com/nnnick/Chart.js
|
||||||
|
// Definitions by: Alberto Nuti <https://github.com/anuti>
|
||||||
|
// Fabien Lavocat <https://github.com/FabienLavocat>
|
||||||
|
// KentarouTakeda <https://github.com/KentarouTakeda>
|
||||||
|
// Larry Bahr <https://github.com/larrybahr>
|
||||||
|
// Daniel Luz <https://github.com/mernen>
|
||||||
|
// Joseph Page <https://github.com/josefpaij>
|
||||||
|
// Dan Manastireanu <https://github.com/danmana>
|
||||||
|
// Guillaume Rodriguez <https://github.com/guillaume-ro-fr>
|
||||||
|
// Sergey Rubanov <https://github.com/chicoxyzzy>
|
||||||
|
// Simon Archer <https://github.com/archy-bold>
|
||||||
|
// Ken Elkabany <https://github.com/braincore>
|
||||||
|
// Slavik Nychkalo <https://github.com/gebeto>
|
||||||
|
// Francesco Benedetto <https://github.com/frabnt>
|
||||||
|
// Alexandros Dorodoulis <https://github.com/alexdor>
|
||||||
|
// Manuel Heidrich <https://github.com/mahnuh>
|
||||||
|
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||||
|
// TypeScript Version: 2.3
|
||||||
|
|
||||||
|
declare module 'chart.js' {
|
||||||
|
|
||||||
|
export class Chart {
|
||||||
|
static readonly Chart: typeof Chart;
|
||||||
|
constructor(
|
||||||
|
context: string | CanvasRenderingContext2D | HTMLCanvasElement | ArrayLike<CanvasRenderingContext2D | HTMLCanvasElement>,
|
||||||
|
options: Chart.ChartConfiguration
|
||||||
|
);
|
||||||
|
config: Chart.ChartConfiguration;
|
||||||
|
data: Chart.ChartData;
|
||||||
|
destroy: () => {};
|
||||||
|
update: (duration?: any, lazy?: any) => {};
|
||||||
|
render: (duration?: any, lazy?: any) => {};
|
||||||
|
stop: () => {};
|
||||||
|
resize: () => {};
|
||||||
|
clear: () => {};
|
||||||
|
options: Chart.ChartOptions;
|
||||||
|
toBase64Image: () => string;
|
||||||
|
generateLegend: () => {};
|
||||||
|
getElementAtEvent: (e: any) => {};
|
||||||
|
getElementsAtEvent: (e: any) => Array<{}>;
|
||||||
|
getDatasetAtEvent: (e: any) => Array<{}>;
|
||||||
|
getDatasetMeta: (index: number) => Meta;
|
||||||
|
ctx: CanvasRenderingContext2D | null;
|
||||||
|
canvas: HTMLCanvasElement | null;
|
||||||
|
chartArea: Chart.ChartArea;
|
||||||
|
static pluginService: PluginServiceStatic;
|
||||||
|
static plugins: PluginServiceStatic;
|
||||||
|
|
||||||
|
static defaults: {
|
||||||
|
global: Chart.ChartOptions & Chart.ChartFontOptions;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
static controllers: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
static helpers: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tooltip Static Options
|
||||||
|
static Tooltip: Chart.ChartTooltipsStaticConfiguration;
|
||||||
|
}
|
||||||
|
export class PluginServiceStatic {
|
||||||
|
register(plugin: PluginServiceGlobalRegistration & PluginServiceRegistrationOptions): void;
|
||||||
|
unregister(plugin: PluginServiceGlobalRegistration & PluginServiceRegistrationOptions): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PluginServiceGlobalRegistration {
|
||||||
|
id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PluginServiceRegistrationOptions {
|
||||||
|
beforeInit?(chartInstance: Chart, options?: any): void;
|
||||||
|
afterInit?(chartInstance: Chart, options?: any): void;
|
||||||
|
|
||||||
|
beforeUpdate?(chartInstance: Chart, options?: any): void;
|
||||||
|
afterUpdate?(chartInstance: Chart, options?: any): void;
|
||||||
|
|
||||||
|
beforeLayout?(chartInstance: Chart, options?: any): void;
|
||||||
|
afterLayout?(chartInstance: Chart, options?: any): void;
|
||||||
|
|
||||||
|
beforeDatasetsUpdate?(chartInstance: Chart, options?: any): void;
|
||||||
|
afterDatasetsUpdate?(chartInstance: Chart, options?: any): void;
|
||||||
|
|
||||||
|
beforeDatasetUpdate?(chartInstance: Chart, options?: any): void;
|
||||||
|
afterDatasetUpdate?(chartInstance: Chart, options?: any): void;
|
||||||
|
|
||||||
|
// This is called at the start of a render. It is only called once, even if the animation will run for a number of frames. Use beforeDraw or afterDraw
|
||||||
|
// to do something on each animation frame
|
||||||
|
beforeRender?(chartInstance: Chart, options?: any): void;
|
||||||
|
afterRender?(chartInstance: Chart, options?: any): void;
|
||||||
|
|
||||||
|
// Easing is for animation
|
||||||
|
beforeDraw?(chartInstance: Chart, easing: string, options?: any): void;
|
||||||
|
afterDraw?(chartInstance: Chart, easing: string, options?: any): void;
|
||||||
|
|
||||||
|
// Before the datasets are drawn but after scales are drawn
|
||||||
|
beforeDatasetsDraw?(chartInstance: Chart, easing: string, options?: any): void;
|
||||||
|
afterDatasetsDraw?(chartInstance: Chart, easing: string, options?: any): void;
|
||||||
|
|
||||||
|
beforeDatasetDraw?(chartInstance: Chart, easing: string, options?: any): void;
|
||||||
|
afterDatasetDraw?(chartInstance: Chart, easing: string, options?: any): void;
|
||||||
|
|
||||||
|
// Called before drawing the `tooltip`. If any plugin returns `false`,
|
||||||
|
// the tooltip drawing is cancelled until another `render` is triggered.
|
||||||
|
beforeTooltipDraw?(chartInstance: Chart, tooltipData?: any, options?: any): void;
|
||||||
|
// Called after drawing the `tooltip`. Note that this hook will not,
|
||||||
|
// be called if the tooltip drawing has been previously cancelled.
|
||||||
|
afterTooltipDraw?(chartInstance: Chart, tooltipData?: any, options?: any): void;
|
||||||
|
|
||||||
|
// Called when an event occurs on the chart
|
||||||
|
beforeEvent?(chartInstance: Chart, event: Event, options?: any): void;
|
||||||
|
afterEvent?(chartInstance: Chart, event: Event, options?: any): void;
|
||||||
|
|
||||||
|
resize?(chartInstance: Chart, newChartSize: Chart.ChartSize, options?: any): void;
|
||||||
|
destroy?(chartInstance: Chart): void;
|
||||||
|
|
||||||
|
/** @deprecated since version 2.5.0. Use `afterLayout` instead. */
|
||||||
|
afterScaleUpdate?(chartInstance: Chart, options?: any): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Meta {
|
||||||
|
type: Chart.ChartType;
|
||||||
|
data: MetaData[];
|
||||||
|
dataset?: Chart.ChartDataSets;
|
||||||
|
controller: { [key: string]: any; };
|
||||||
|
hidden?: boolean;
|
||||||
|
total?: string;
|
||||||
|
xAxisID?: string;
|
||||||
|
yAxisID?: string;
|
||||||
|
"$filler"?: { [key: string]: any; };
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MetaData {
|
||||||
|
_chart: Chart;
|
||||||
|
_datasetIndex: number;
|
||||||
|
_index: number;
|
||||||
|
_model: Model;
|
||||||
|
_start?: any;
|
||||||
|
_view: Model;
|
||||||
|
_xScale: Chart.ChartScales;
|
||||||
|
_yScale: Chart.ChartScales;
|
||||||
|
hidden?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Model {
|
||||||
|
backgroundColor: string;
|
||||||
|
borderColor: string;
|
||||||
|
borderWidth?: number;
|
||||||
|
controlPointNextX: number;
|
||||||
|
controlPointNextY: number;
|
||||||
|
controlPointPreviousX: number;
|
||||||
|
controlPointPreviousY: number;
|
||||||
|
hitRadius: number;
|
||||||
|
pointStyle: string;
|
||||||
|
radius: string;
|
||||||
|
skip?: boolean;
|
||||||
|
steppedLine?: undefined;
|
||||||
|
tension: number;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Chart {
|
||||||
|
type ChartType = 'line' | 'bar' | 'horizontalBar' | 'radar' | 'doughnut' | 'polarArea' | 'bubble' | 'pie';
|
||||||
|
|
||||||
|
type TimeUnit = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year';
|
||||||
|
|
||||||
|
type ScaleType = 'category' | 'linear' | 'logarithmic' | 'time' | 'radialLinear';
|
||||||
|
|
||||||
|
type PointStyle = 'circle' | 'cross' | 'crossRot' | 'dash' | 'line' | 'rect' | 'rectRounded' | 'rectRot' | 'star' | 'triangle';
|
||||||
|
|
||||||
|
type PositionType = 'left' | 'right' | 'top' | 'bottom';
|
||||||
|
|
||||||
|
interface ChartArea {
|
||||||
|
top: number;
|
||||||
|
right: number;
|
||||||
|
bottom: number;
|
||||||
|
left: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartLegendItem {
|
||||||
|
text?: string;
|
||||||
|
fillStyle?: string;
|
||||||
|
hidden?: boolean;
|
||||||
|
lineCap?: string;
|
||||||
|
lineDash?: number[];
|
||||||
|
lineDashOffset?: number;
|
||||||
|
lineJoin?: string;
|
||||||
|
lineWidth?: number;
|
||||||
|
strokeStyle?: string;
|
||||||
|
pointStyle?: PointStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartLegendLabelItem extends ChartLegendItem {
|
||||||
|
datasetIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartTooltipItem {
|
||||||
|
xLabel?: string;
|
||||||
|
yLabel?: string;
|
||||||
|
datasetIndex?: number;
|
||||||
|
index?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartTooltipLabelColor {
|
||||||
|
borderColor: ChartColor;
|
||||||
|
backgroundColor: ChartColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartTooltipCallback {
|
||||||
|
beforeTitle?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||||
|
title?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||||
|
afterTitle?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||||
|
beforeBody?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||||
|
beforeLabel?(tooltipItem: ChartTooltipItem, data: ChartData): string | string[];
|
||||||
|
label?(tooltipItem: ChartTooltipItem, data: ChartData): string | string[];
|
||||||
|
labelColor?(tooltipItem: ChartTooltipItem, chart: Chart): ChartTooltipLabelColor;
|
||||||
|
labelTextColor?(tooltipItem: ChartTooltipItem, chart: Chart): string;
|
||||||
|
afterLabel?(tooltipItem: ChartTooltipItem, data: ChartData): string | string[];
|
||||||
|
afterBody?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||||
|
beforeFooter?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||||
|
footer?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||||
|
afterFooter?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartAnimationParameter {
|
||||||
|
chartInstance?: any;
|
||||||
|
animationObject?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartPoint {
|
||||||
|
x?: number | string | Date;
|
||||||
|
y?: number | string | Date;
|
||||||
|
r?: number;
|
||||||
|
t?: number | string | Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartConfiguration {
|
||||||
|
type?: ChartType | string;
|
||||||
|
data?: ChartData;
|
||||||
|
options?: ChartOptions;
|
||||||
|
plugins?: PluginServiceRegistrationOptions[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartData {
|
||||||
|
labels?: Array<string | string[]>;
|
||||||
|
datasets?: ChartDataSets[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RadialChartOptions extends ChartOptions {
|
||||||
|
scale?: RadialLinearScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartSize {
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartOptions {
|
||||||
|
responsive?: boolean;
|
||||||
|
responsiveAnimationDuration?: number;
|
||||||
|
aspectRatio?: number;
|
||||||
|
maintainAspectRatio?: boolean;
|
||||||
|
events?: string[];
|
||||||
|
onHover?(this: Chart, event: MouseEvent, activeElements: Array<{}>): any;
|
||||||
|
onClick?(event?: MouseEvent, activeElements?: Array<{}>): any;
|
||||||
|
onResize?(this: Chart, newSize: ChartSize): void;
|
||||||
|
title?: ChartTitleOptions;
|
||||||
|
legend?: ChartLegendOptions;
|
||||||
|
tooltips?: ChartTooltipOptions;
|
||||||
|
hover?: ChartHoverOptions;
|
||||||
|
animation?: ChartAnimationOptions;
|
||||||
|
elements?: ChartElementsOptions;
|
||||||
|
layout?: ChartLayoutOptions;
|
||||||
|
scales?: ChartScales;
|
||||||
|
showLines?: boolean;
|
||||||
|
spanGaps?: boolean;
|
||||||
|
cutoutPercentage?: number;
|
||||||
|
circumference?: number;
|
||||||
|
rotation?: number;
|
||||||
|
devicePixelRatio?: number;
|
||||||
|
// Plugins can require any options
|
||||||
|
plugins?: { [pluginId: string]: any };
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartFontOptions {
|
||||||
|
defaultFontColor?: ChartColor;
|
||||||
|
defaultFontFamily?: string;
|
||||||
|
defaultFontSize?: number;
|
||||||
|
defaultFontStyle?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartTitleOptions {
|
||||||
|
display?: boolean;
|
||||||
|
position?: PositionType;
|
||||||
|
fullWidth?: boolean;
|
||||||
|
fontSize?: number;
|
||||||
|
fontFamily?: string;
|
||||||
|
fontColor?: ChartColor;
|
||||||
|
fontStyle?: string;
|
||||||
|
padding?: number;
|
||||||
|
text?: string | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartLegendOptions {
|
||||||
|
display?: boolean;
|
||||||
|
position?: PositionType;
|
||||||
|
fullWidth?: boolean;
|
||||||
|
onClick?(event: MouseEvent, legendItem: ChartLegendLabelItem): void;
|
||||||
|
onHover?(event: MouseEvent, legendItem: ChartLegendLabelItem): void;
|
||||||
|
labels?: ChartLegendLabelOptions;
|
||||||
|
reverse?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartLegendLabelOptions {
|
||||||
|
boxWidth?: number;
|
||||||
|
fontSize?: number;
|
||||||
|
fontStyle?: string;
|
||||||
|
fontColor?: ChartColor;
|
||||||
|
fontFamily?: string;
|
||||||
|
padding?: number;
|
||||||
|
generateLabels?(chart: any): any;
|
||||||
|
filter?(legendItem: ChartLegendLabelItem, data: ChartData): any;
|
||||||
|
usePointStyle?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartTooltipOptions {
|
||||||
|
enabled?: boolean;
|
||||||
|
custom?(a: any): void;
|
||||||
|
mode?: string;
|
||||||
|
intersect?: boolean;
|
||||||
|
backgroundColor?: ChartColor;
|
||||||
|
titleFontFamily?: string;
|
||||||
|
titleFontSize?: number;
|
||||||
|
titleFontStyle?: string;
|
||||||
|
titleFontColor?: ChartColor;
|
||||||
|
titleSpacing?: number;
|
||||||
|
titleMarginBottom?: number;
|
||||||
|
bodyFontFamily?: string;
|
||||||
|
bodyFontSize?: number;
|
||||||
|
bodyFontStyle?: string;
|
||||||
|
bodyFontColor?: ChartColor;
|
||||||
|
bodySpacing?: number;
|
||||||
|
footerFontFamily?: string;
|
||||||
|
footerFontSize?: number;
|
||||||
|
footerFontStyle?: string;
|
||||||
|
footerFontColor?: ChartColor;
|
||||||
|
footerSpacing?: number;
|
||||||
|
footerMarginTop?: number;
|
||||||
|
xPadding?: number;
|
||||||
|
yPadding?: number;
|
||||||
|
caretSize?: number;
|
||||||
|
cornerRadius?: number;
|
||||||
|
multiKeyBackground?: string;
|
||||||
|
callbacks?: ChartTooltipCallback;
|
||||||
|
filter?(item: ChartTooltipItem): boolean;
|
||||||
|
itemSort?(itemA: ChartTooltipItem, itemB: ChartTooltipItem): number;
|
||||||
|
position?: string;
|
||||||
|
caretPadding?: number;
|
||||||
|
displayColors?: boolean;
|
||||||
|
borderColor?: ChartColor;
|
||||||
|
borderWidth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartTooltipsStaticConfiguration {
|
||||||
|
positioners: { [mode: string]: ChartTooltipPositioner };
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChartTooltipPositioner = (elements: any[], eventPosition: Point) => Point;
|
||||||
|
|
||||||
|
interface ChartHoverOptions {
|
||||||
|
mode?: string;
|
||||||
|
animationDuration?: number;
|
||||||
|
intersect?: boolean;
|
||||||
|
onHover?(this: Chart, event: MouseEvent, activeElements: Array<{}>): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartAnimationObject {
|
||||||
|
currentStep?: number;
|
||||||
|
numSteps?: number;
|
||||||
|
easing?: string;
|
||||||
|
render?(arg: any): void;
|
||||||
|
onAnimationProgress?(arg: any): void;
|
||||||
|
onAnimationComplete?(arg: any): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartAnimationOptions {
|
||||||
|
duration?: number;
|
||||||
|
easing?: string;
|
||||||
|
onProgress?(chart: any): void;
|
||||||
|
onComplete?(chart: any): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartElementsOptions {
|
||||||
|
point?: ChartPointOptions;
|
||||||
|
line?: ChartLineOptions;
|
||||||
|
arc?: ChartArcOptions;
|
||||||
|
rectangle?: ChartRectangleOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartArcOptions {
|
||||||
|
backgroundColor?: ChartColor;
|
||||||
|
borderColor?: ChartColor;
|
||||||
|
borderWidth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartLineOptions {
|
||||||
|
tension?: number;
|
||||||
|
backgroundColor?: ChartColor;
|
||||||
|
borderWidth?: number;
|
||||||
|
borderColor?: ChartColor;
|
||||||
|
borderCapStyle?: string;
|
||||||
|
borderDash?: any[];
|
||||||
|
borderDashOffset?: number;
|
||||||
|
borderJoinStyle?: string;
|
||||||
|
capBezierPoints?: boolean;
|
||||||
|
fill?: 'zero' | 'top' | 'bottom' | boolean;
|
||||||
|
stepped?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartPointOptions {
|
||||||
|
radius?: number;
|
||||||
|
pointStyle?: PointStyle;
|
||||||
|
backgroundColor?: ChartColor;
|
||||||
|
borderWidth?: number;
|
||||||
|
borderColor?: ChartColor;
|
||||||
|
hitRadius?: number;
|
||||||
|
hoverRadius?: number;
|
||||||
|
hoverBorderWidth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartRectangleOptions {
|
||||||
|
backgroundColor?: ChartColor;
|
||||||
|
borderWidth?: number;
|
||||||
|
borderColor?: ChartColor;
|
||||||
|
borderSkipped?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartLayoutOptions {
|
||||||
|
padding?: ChartLayoutPaddingObject | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartLayoutPaddingObject {
|
||||||
|
top?: number;
|
||||||
|
right?: number;
|
||||||
|
bottom?: number;
|
||||||
|
left?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GridLineOptions {
|
||||||
|
display?: boolean;
|
||||||
|
color?: ChartColor;
|
||||||
|
borderDash?: number[];
|
||||||
|
borderDashOffset?: number;
|
||||||
|
lineWidth?: number;
|
||||||
|
drawBorder?: boolean;
|
||||||
|
drawOnChartArea?: boolean;
|
||||||
|
drawTicks?: boolean;
|
||||||
|
tickMarkLength?: number;
|
||||||
|
zeroLineWidth?: number;
|
||||||
|
zeroLineColor?: ChartColor;
|
||||||
|
zeroLineBorderDash?: number[];
|
||||||
|
zeroLineBorderDashOffset?: number;
|
||||||
|
offsetGridLines?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ScaleTitleOptions {
|
||||||
|
display?: boolean;
|
||||||
|
labelString?: string;
|
||||||
|
fontColor?: ChartColor;
|
||||||
|
fontFamily?: string;
|
||||||
|
fontSize?: number;
|
||||||
|
fontStyle?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TickOptions {
|
||||||
|
autoSkip?: boolean;
|
||||||
|
autoSkipPadding?: number;
|
||||||
|
backdropColor?: ChartColor;
|
||||||
|
backdropPaddingX?: number;
|
||||||
|
backdropPaddingY?: number;
|
||||||
|
beginAtZero?: boolean;
|
||||||
|
callback?(value: any, index: any, values: any): string | number;
|
||||||
|
display?: boolean;
|
||||||
|
fontColor?: ChartColor;
|
||||||
|
fontFamily?: string;
|
||||||
|
fontSize?: number;
|
||||||
|
fontStyle?: string;
|
||||||
|
labelOffset?: number;
|
||||||
|
max?: any;
|
||||||
|
maxRotation?: number;
|
||||||
|
maxTicksLimit?: number;
|
||||||
|
min?: any;
|
||||||
|
minRotation?: number;
|
||||||
|
mirror?: boolean;
|
||||||
|
padding?: number;
|
||||||
|
reverse?: boolean;
|
||||||
|
showLabelBackdrop?: boolean;
|
||||||
|
source?: 'auto' | 'data' | 'labels';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AngleLineOptions {
|
||||||
|
display?: boolean;
|
||||||
|
color?: ChartColor;
|
||||||
|
lineWidth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PointLabelOptions {
|
||||||
|
callback?(arg: any): any;
|
||||||
|
fontColor?: ChartColor;
|
||||||
|
fontFamily?: string;
|
||||||
|
fontSize?: number;
|
||||||
|
fontStyle?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LinearTickOptions extends TickOptions {
|
||||||
|
maxTicksLimit?: number;
|
||||||
|
stepSize?: number;
|
||||||
|
suggestedMin?: number;
|
||||||
|
suggestedMax?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line no-empty-interface
|
||||||
|
interface LogarithmicTickOptions extends TickOptions {
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChartColor = string | CanvasGradient | CanvasPattern | string[];
|
||||||
|
|
||||||
|
interface ChartDataSets {
|
||||||
|
cubicInterpolationMode?: 'default' | 'monotone';
|
||||||
|
backgroundColor?: ChartColor | ChartColor[];
|
||||||
|
borderWidth?: number | number[];
|
||||||
|
borderColor?: ChartColor | ChartColor[];
|
||||||
|
borderCapStyle?: string;
|
||||||
|
borderDash?: number[];
|
||||||
|
borderDashOffset?: number;
|
||||||
|
borderJoinStyle?: string;
|
||||||
|
borderSkipped?: PositionType;
|
||||||
|
data?: number[] | ChartPoint[];
|
||||||
|
fill?: boolean | number | string;
|
||||||
|
hoverBackgroundColor?: string | string[];
|
||||||
|
hoverBorderColor?: string | string[];
|
||||||
|
hoverBorderWidth?: number | number[];
|
||||||
|
label?: string;
|
||||||
|
lineTension?: number;
|
||||||
|
steppedLine?: 'before' | 'after' | boolean;
|
||||||
|
pointBorderColor?: ChartColor | ChartColor[];
|
||||||
|
pointBackgroundColor?: ChartColor | ChartColor[];
|
||||||
|
pointBorderWidth?: number | number[];
|
||||||
|
pointRadius?: number | number[];
|
||||||
|
pointHoverRadius?: number | number[];
|
||||||
|
pointHitRadius?: number | number[];
|
||||||
|
pointHoverBackgroundColor?: ChartColor | ChartColor[];
|
||||||
|
pointHoverBorderColor?: ChartColor | ChartColor[];
|
||||||
|
pointHoverBorderWidth?: number | number[];
|
||||||
|
pointStyle?: PointStyle | HTMLImageElement | HTMLCanvasElement | Array<PointStyle | HTMLImageElement | HTMLCanvasElement>;
|
||||||
|
xAxisID?: string;
|
||||||
|
yAxisID?: string;
|
||||||
|
type?: string;
|
||||||
|
hidden?: boolean;
|
||||||
|
hideInLegendAndTooltip?: boolean;
|
||||||
|
showLine?: boolean;
|
||||||
|
stack?: string;
|
||||||
|
spanGaps?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartScales {
|
||||||
|
type?: ScaleType | string;
|
||||||
|
display?: boolean;
|
||||||
|
position?: PositionType | string;
|
||||||
|
gridLines?: GridLineOptions;
|
||||||
|
scaleLabel?: ScaleTitleOptions;
|
||||||
|
ticks?: TickOptions;
|
||||||
|
xAxes?: ChartXAxe[];
|
||||||
|
yAxes?: ChartYAxe[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CommonAxe {
|
||||||
|
bounds?: string;
|
||||||
|
type?: ScaleType | string;
|
||||||
|
display?: boolean;
|
||||||
|
id?: string;
|
||||||
|
stacked?: boolean;
|
||||||
|
position?: string;
|
||||||
|
ticks?: TickOptions;
|
||||||
|
gridLines?: GridLineOptions;
|
||||||
|
barThickness?: number | "flex";
|
||||||
|
maxBarThickness?: number;
|
||||||
|
scaleLabel?: ScaleTitleOptions;
|
||||||
|
time?: TimeScale;
|
||||||
|
offset?: boolean;
|
||||||
|
beforeUpdate?(scale?: any): void;
|
||||||
|
beforeSetDimension?(scale?: any): void;
|
||||||
|
beforeDataLimits?(scale?: any): void;
|
||||||
|
beforeBuildTicks?(scale?: any): void;
|
||||||
|
beforeTickToLabelConversion?(scale?: any): void;
|
||||||
|
beforeCalculateTickRotation?(scale?: any): void;
|
||||||
|
beforeFit?(scale?: any): void;
|
||||||
|
afterUpdate?(scale?: any): void;
|
||||||
|
afterSetDimension?(scale?: any): void;
|
||||||
|
afterDataLimits?(scale?: any): void;
|
||||||
|
afterBuildTicks?(scale?: any): void;
|
||||||
|
afterTickToLabelConversion?(scale?: any): void;
|
||||||
|
afterCalculateTickRotation?(scale?: any): void;
|
||||||
|
afterFit?(scale?: any): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartXAxe extends CommonAxe {
|
||||||
|
categoryPercentage?: number;
|
||||||
|
barPercentage?: number;
|
||||||
|
distribution?: 'linear' | 'series';
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line no-empty-interface
|
||||||
|
interface ChartYAxe extends CommonAxe {
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LinearScale extends ChartScales {
|
||||||
|
ticks?: LinearTickOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LogarithmicScale extends ChartScales {
|
||||||
|
ticks?: LogarithmicTickOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TimeDisplayFormat {
|
||||||
|
millisecond?: string;
|
||||||
|
second?: string;
|
||||||
|
minute?: string;
|
||||||
|
hour?: string;
|
||||||
|
day?: string;
|
||||||
|
week?: string;
|
||||||
|
month?: string;
|
||||||
|
quarter?: string;
|
||||||
|
year?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TimeScale extends ChartScales {
|
||||||
|
displayFormats?: TimeDisplayFormat;
|
||||||
|
isoWeekday?: boolean;
|
||||||
|
max?: string;
|
||||||
|
min?: string;
|
||||||
|
parser?: string | ((arg: any) => any);
|
||||||
|
round?: TimeUnit;
|
||||||
|
tooltipFormat?: string;
|
||||||
|
unit?: TimeUnit;
|
||||||
|
unitStepSize?: number;
|
||||||
|
stepSize?: number;
|
||||||
|
minUnit?: TimeUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RadialLinearScale extends LinearScale {
|
||||||
|
lineArc?: boolean;
|
||||||
|
angleLines?: AngleLineOptions;
|
||||||
|
pointLabels?: PointLabelOptions;
|
||||||
|
ticks?: TickOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,6 +53,10 @@
|
|||||||
normalize-path "^2.0.1"
|
normalize-path "^2.0.1"
|
||||||
through2 "^2.0.3"
|
through2 "^2.0.3"
|
||||||
|
|
||||||
|
"@types/chart.js@^2.7.31":
|
||||||
|
version "2.7.31"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.7.31.tgz#fe2c28d3defa461f5d5cd01f1fac635df649472b"
|
||||||
|
|
||||||
"@types/commander@^2.11.0":
|
"@types/commander@^2.11.0":
|
||||||
version "2.12.2"
|
version "2.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/commander/-/commander-2.12.2.tgz#183041a23842d4281478fa5d23c5ca78e6fd08ae"
|
resolved "https://registry.yarnpkg.com/@types/commander/-/commander-2.12.2.tgz#183041a23842d4281478fa5d23c5ca78e6fd08ae"
|
||||||
|
|||||||
Reference in New Issue
Block a user