Compare commits

...

28 Commits

Author SHA1 Message Date
Anthony Dresser
f7abf5a2d5 Fix stating for scrolls (#2615)
* nearly working

* add accounting for the downsides to slickgrid
2018-09-17 17:55:52 -07:00
Anthony Dresser
c8c6d072f6 Respect message settings (#2614)
* add results view stating

* working through the bugs

* handle various resizing bugs

* gnale resizing better

* add configuration to state

* address comments
2018-09-17 17:55:38 -07:00
Anthony Dresser
4d9cc604b9 add view area options to pick up chart background fix (#2613) 2018-09-17 16:37:18 -07:00
Aditya Bist
1dc76fa171 Dashboard: Fixed all insight bugs (#2612)
* fixed all insight bugs

* removed unused imports

* added comment
2018-09-17 14:47:46 -07:00
Aditya Bist
1d37b9ae9c fixes scrolling in query plan (#2609) 2018-09-17 13:02:38 -07:00
Matt Irvine
26828602a8 Use version 2.0.9 of electron (#2606) 2018-09-17 12:08:50 -07:00
Matt Irvine
e253f3ac89 Fix/bump dependency versions (#2608) 2018-09-17 11:21:41 -07:00
AlexFsmn
4d59fdea1b The "New Query" context menu is now only available from the server & db (#2598)
#1890
2018-09-15 13:44:58 -07:00
AlexFsmn
98a313eb5b Changed the "Configure" link to "Learn How To Configure The Dashboard". (#2599)
* Changed the "Configure" link to "Learn How To Configure the Dashboard".
This inlcudes the command as well as the tile label
#1227

* Capitalizing first character in each word
2018-09-15 11:45:21 -07:00
Aditya Bist
77e1cd8b32 fixed insights crash (#2596) 2018-09-14 21:20:37 -07:00
Alan Ren
dede5c5ef5 edit data issue with column index handling (#2595) 2018-09-14 21:19:56 -07:00
Matt Irvine
d156c0be3d Fix crash when reverting in edit data with no changes (#2594) 2018-09-14 21:18:54 -07:00
Karl Burtram
dc0bc6e606 Update SQL Ops to 0.33.6 2018-09-13 23:48:40 -07:00
Matt Irvine
05040425df Add OE node refresh API method (#2578)
* Initial working commit for refreshing OE node via API

* Add test and fix up code

* Run tsfmt

* Fix test
2018-09-13 18:43:47 -07:00
Alan Ren
36f7c283b8 use latest slickgrid library (#2584) 2018-09-13 18:43:06 -07:00
Anthony Dresser
9fe4237033 Maintain Query State (#2571)
* add results view stating

* working through the bugs

* handle various resizing bugs

* gnale resizing better

* fix tests by adding missing node module

* formatting

* refactor interfaces out to get around testing restrictions

* more refactoring of importants to avoid loading errors
2018-09-13 18:42:29 -07:00
Alan Ren
b03c0a3e2d accessibility setting based select database dropdown (#2579) 2018-09-13 15:07:08 -07:00
Aditya Bist
87946996ed added context to chart buttons so they work (#2575) 2018-09-13 14:22:57 -07:00
AlexFsmn
cc55023440 Disabled connection name input when connecting to a server. (#2566)
* Disabled connection name input when connecting to a server.
#2557

* Fixed reset state of connection inputs
2018-09-13 12:36:28 -07:00
Karl Burtram
1f19dfc50d Update SQL Ops to 0.33.5 2018-09-13 08:06:27 -07:00
Alan Ren
950a440350 fix the connection issue when opening new query after connection (#2561) 2018-09-13 08:05:46 -07:00
Aditya Bist
c92b88bfaf Bug/extension contribution (#2560)
* revert 4ab5d84b94

* fixed extensions
2018-09-12 23:04:59 -07:00
Abbie Petchtes
10875f26dc add divcontainer in modelview (#2559)
* add divcontainer in modelview

* address comment
2018-09-12 20:18:40 -07:00
Kevin Cunnane
d62e809c18 Support isDirty flag for model view editors and begin plumb through of save support (#2547)
* Add dirty and save support to model view

* Add issue # for a TODO
2018-09-12 14:35:19 -07:00
Alan Ren
d85bf4f6dd fix the account not found error when creating firewall rules (#2543) 2018-09-12 13:32:40 -07:00
Anthony Dresser
801e201cc3 change active cell during change to fix focus shift (#2545) 2018-09-12 13:22:01 -07:00
Anthony Dresser
e18e0da0c1 Fix sizing error when switching windows (#2544)
* add work around for when we need to resize while we don't have a dimension to resize off of

* formatting
2018-09-12 13:21:51 -07:00
Karl Burtram
d046b0a412 Update SQL Ops to 0.33.4 2018-09-12 13:09:41 -07:00
72 changed files with 6522 additions and 5863 deletions

View File

@@ -1,3 +1,3 @@
disturl "https://atom.io/download/electron"
target "2.0.8"
target "2.0.9"
runtime "electron"

View File

@@ -13,7 +13,7 @@
"azure-storage": "^2.1.0",
"decompress": "^4.2.0",
"documentdb": "1.13.0",
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
"service-downloader": "github:anthonydresser/service-downloader#0.1.5",
"fs-extra-promise": "^1.0.1",
"mime": "^1.3.4",
"minimist": "^1.2.0",

View File

@@ -1002,7 +1002,7 @@ hoek@4.x.x:
version "4.2.1"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb"
http-proxy-agent@^2.0.0:
http-proxy-agent@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
dependencies:
@@ -1025,7 +1025,7 @@ http-signature@~1.2.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
https-proxy-agent@^2.1.1:
https-proxy-agent@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
dependencies:
@@ -1843,14 +1843,14 @@ semver@^5.4.1:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
"service-downloader@github:anthonydresser/service-downloader#0.1.2":
version "0.1.2"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/2aa9b336b6442e17e24693ddc907030575539798"
"service-downloader@github:anthonydresser/service-downloader#0.1.5":
version "0.1.5"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/6ebb0465573cc140e461a22f334260f55ef45546"
dependencies:
decompress "^4.2.0"
eventemitter2 "^5.0.1"
http-proxy-agent "^2.0.0"
https-proxy-agent "^2.1.1"
http-proxy-agent "^2.1.0"
https-proxy-agent "^2.2.1"
mkdirp "^0.5.1"
tmp "^0.0.33"

View File

@@ -82,7 +82,7 @@
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.7",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
"service-downloader": "github:anthonydresser/service-downloader#0.1.5",
"vscode-extension-telemetry": "^0.0.5",
"vscode-nls": "^3.2.1"
},

View File

@@ -177,14 +177,14 @@ graceful-fs@^4.1.10:
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
http-proxy-agent@^2.0.0:
http-proxy-agent@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
dependencies:
agent-base "4"
debug "3.1.0"
https-proxy-agent@^2.1.1:
https-proxy-agent@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
dependencies:
@@ -297,14 +297,14 @@ seek-bzip@^1.0.5:
dependencies:
commander "~2.8.1"
"service-downloader@github:anthonydresser/service-downloader#0.1.4":
version "0.1.4"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/3c0abdf8603aca85d2eacfac3c547173e41bf0c7"
"service-downloader@github:anthonydresser/service-downloader#0.1.5":
version "0.1.5"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/6ebb0465573cc140e461a22f334260f55ef45546"
dependencies:
decompress "^4.2.0"
eventemitter2 "^5.0.1"
http-proxy-agent "^2.0.0"
https-proxy-agent "^2.1.1"
http-proxy-agent "^2.1.0"
https-proxy-agent "^2.2.1"
mkdirp "^0.5.1"
tmp "^0.0.33"

View File

@@ -20,7 +20,7 @@
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.8",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
"service-downloader": "github:anthonydresser/service-downloader#0.1.5",
"vscode-extension-telemetry": "^0.0.15"
},
"devDependencies": {

View File

@@ -191,14 +191,14 @@ graceful-fs@^4.1.10:
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
http-proxy-agent@^2.0.0:
http-proxy-agent@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
dependencies:
agent-base "4"
debug "3.1.0"
https-proxy-agent@^2.1.1:
https-proxy-agent@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
dependencies:
@@ -315,14 +315,14 @@ semver@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
"service-downloader@github:anthonydresser/service-downloader#0.1.4":
version "0.1.4"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/3c0abdf8603aca85d2eacfac3c547173e41bf0c7"
"service-downloader@github:anthonydresser/service-downloader#0.1.5":
version "0.1.5"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/6ebb0465573cc140e461a22f334260f55ef45546"
dependencies:
decompress "^4.2.0"
eventemitter2 "^5.0.1"
http-proxy-agent "^2.0.0"
https-proxy-agent "^2.1.1"
http-proxy-agent "^2.1.0"
https-proxy-agent "^2.2.1"
mkdirp "^0.5.1"
tmp "^0.0.33"

View File

@@ -1,6 +1,6 @@
{
"name": "sqlops",
"version": "0.33.3",
"version": "0.33.6",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": {
"name": "Microsoft Corporation"
@@ -64,7 +64,7 @@
"reflect-metadata": "^0.1.8",
"rxjs": "5.4.0",
"semver": "^5.5.0",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.25",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.27",
"spdlog": "0.7.1",
"sudo-prompt": "8.2.0",
"svg.js": "^2.2.5",
@@ -167,5 +167,8 @@
"windows-foreground-love": "0.1.0",
"windows-mutex": "^0.2.0",
"windows-process-tree": "0.2.2"
},
"resolutions": {
"rc": "1.2.8"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -518,5 +518,6 @@ export abstract class Modal extends Disposable implements IThemable {
public dispose() {
super.dispose();
this._keydownListener.dispose();
this._footerButtons = [];
}
}

View File

@@ -180,14 +180,16 @@ export class TabbedPanel extends Disposable implements IThemable {
}
public layout(dimension: Dimension): void {
this._currentDimensions = dimension;
this.$parent.style('height', dimension.height + 'px');
this.$parent.style('width', dimension.width + 'px');
this.$header.style('width', dimension.width + 'px');
this.$body.style('width', dimension.width + 'px');
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
this.$body.style('height', bodyHeight + 'px');
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
if (dimension) {
this._currentDimensions = dimension;
this.$parent.style('height', dimension.height + 'px');
this.$parent.style('width', dimension.width + 'px');
this.$header.style('width', dimension.width + 'px');
this.$body.style('width', dimension.width + 'px');
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
this.$body.style('height', bodyHeight + 'px');
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
}
}
private _layoutCurrentTab(dimension: Dimension): void {

View File

@@ -34,6 +34,8 @@ export interface IView extends HeightIView {
readonly onDidChange: Event<number | undefined>;
render(container: HTMLElement, orientation: Orientation): void;
layout(size: number, orientation: Orientation): void;
onAdd?(): void;
onRemove?(): void;
}
interface ISashEvent {
@@ -48,6 +50,8 @@ interface IViewItem extends HeightIViewItem {
container: HTMLElement;
disposable: IDisposable;
layout(): void;
onRemove: () => void;
onAdd: () => void;
}
interface ISashItem {
@@ -109,6 +113,9 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
private _onDidSashReset = new Emitter<void>();
readonly onDidSashReset = this._onDidSashReset.event;
private _onScroll = new Emitter<number>();
readonly onScroll = this._onScroll.event;
get length(): number {
return this.viewItems.length;
}
@@ -124,6 +131,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
debounceEvent(this.scrollable.onScroll, (l, e) => e, 25)(e => {
this.render(e.scrollTop, e.height);
this.relayout();
this._onScroll.fire(e.scrollTop);
});
let domNode = this.scrollable.getDomNode();
dom.addClass(this.el, 'monaco-scroll-split-view');
@@ -155,6 +163,9 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
});
const disposable = combinedDisposable([onChangeDisposable, containerDisposable]);
const onAdd = view.onAdd ? () => view.onAdd() : () => { };
const onRemove = view.onRemove ? () => view.onRemove() : () => { };
const layoutContainer = this.orientation === Orientation.VERTICAL
? size => item.container.style.height = `${item.size}px`
: size => item.container.style.width = `${item.size}px`;
@@ -165,7 +176,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
};
size = Math.round(size);
const item: IViewItem = { view, container, size, layout, disposable, height: size, top: 0, width: 0 };
const item: IViewItem = { onRemove, onAdd, view, container, size, layout, disposable, height: size, top: 0, width: 0 };
this.viewItems.splice(viewIndex, 0, item);
this.onInsertItems(new ArrayIterator([item]), viewIndex > 0 ? this.viewItems[viewIndex - 1].view.id : undefined);
@@ -220,6 +231,9 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
});
const disposable = combinedDisposable([onChangeDisposable, containerDisposable]);
const onAdd = view.onAdd ? () => view.onAdd() : () => { };
const onRemove = view.onRemove ? () => view.onRemove() : () => { };
const layoutContainer = this.orientation === Orientation.VERTICAL
? size => item.container.style.height = `${item.size}px`
: size => item.container.style.width = `${item.size}px`;
@@ -230,7 +244,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
};
size = Math.round(size);
const item: IViewItem = { view, container, size, layout, disposable, height: size, top: 0, width: 0 };
const item: IViewItem = { onAdd, onRemove, view, container, size, layout, disposable, height: size, top: 0, width: 0 };
this.viewItems.splice(index, 0, item);
this.onInsertItems(new ArrayIterator([item]), index > 0 ? this.viewItems[index - 1].view.id : undefined);
@@ -330,6 +344,10 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex);
}
public setScrollPosition(position: number) {
this.scrollable.setScrollPosition({ scrollTop: position });
}
layout(size: number): void {
const previousSize = this.size;
this.size = size;
@@ -487,6 +505,8 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
}
item.layout();
item.onAdd();
return true;
}
@@ -496,6 +516,8 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
}
this.el.removeChild(item.container);
item.onRemove();
return true;
}

View File

@@ -1,7 +1,5 @@
import { mixin } from 'vs/base/common/objects';
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
const defaultOptions: ICellRangeSelectorOptions = {
selectionCss: {
'border': '2px dashed blue'
@@ -44,6 +42,8 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
public onCellRangeSelected = new Slick.Event<{ range: Slick.Range }>();
constructor(private options: ICellRangeSelectorOptions) {
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
this.options = mixin(this.options, defaultOptions, false);
}
@@ -89,7 +89,7 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
this.canvas.classList.add(this.options.dragClass);
this.grid.focus();
this.grid.setActiveCell(cell.row, cell.cell);
let start = this.grid.getCellFromPoint(
dd.startX - $(this.canvas).offset().left,

View File

@@ -6,8 +6,6 @@ import { isUndefinedOrNull } from 'vs/base/common/types';
import { CellRangeSelector, ICellRangeSelector } from 'sql/base/browser/ui/table/plugins/cellRangeSelector';
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
export interface ICellSelectionModelOptions {
cellRangeSelector?: any;
selectActiveCell?: boolean;

View File

@@ -123,6 +123,7 @@ export class ConnectionStore {
.then(savedCred => {
if (savedCred) {
credentialsItem.password = savedCred.password;
credentialsItem.options['password'] = savedCred.password;
}
resolve({ profile: credentialsItem, savedCred: !!savedCred });
},

View File

@@ -492,6 +492,7 @@ export class ConnectionWidget {
this._databaseNameInputBox.enabled = false;
this._userNameInputBox.disable();
this._passwordInputBox.disable();
this._connectionNameInputBox.disable();
this._rememberPasswordCheckBox.enabled = false;
if (this._authTypeSelectBox) {
this._authTypeSelectBox.disable();
@@ -503,6 +504,7 @@ export class ConnectionWidget {
this._serverGroupSelectBox.enable();
this._serverNameInputBox.enable();
this._connectionNameInputBox.enable();
this._databaseNameInputBox.enabled = true;
let currentAuthType: AuthenticationType = undefined;
if (this._authTypeSelectBox) {

View File

@@ -10,6 +10,7 @@ import * as TelemetryUtils from 'sql/common/telemetryUtilities';
import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { memoize, unmemoize } from 'sql/base/common/decorators';
import { mixin } from 'sql/base/common/objects';
import { LegendPosition, DataDirection, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import * as colors from 'vs/platform/theme/common/colorRegistry';
import { Color } from 'vs/base/common/color';
@@ -21,29 +22,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
declare var Chart: any;
export enum ChartType {
Bar = 'bar',
Doughnut = 'doughnut',
HorizontalBar = 'horizontalBar',
Line = 'line',
Pie = 'pie',
TimeSeries = 'timeSeries',
Scatter = 'scatter'
}
export enum DataDirection {
Vertical = 'vertical',
Horizontal = 'horizontal'
}
export enum LegendPosition {
Top = 'top',
Bottom = 'bottom',
Left = 'left',
Right = 'right',
None = 'none'
}
export function customMixin(destination: any, source: any, overwrite?: boolean): any {
if (types.isObject(source)) {
mixin(destination, source, overwrite, customMixin);

View 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.
*--------------------------------------------------------------------------------------------*/
export enum ChartType {
Bar = 'bar',
Doughnut = 'doughnut',
HorizontalBar = 'horizontalBar',
Line = 'line',
Pie = 'pie',
TimeSeries = 'timeSeries',
Scatter = 'scatter'
}
export enum DataDirection {
Vertical = 'vertical',
Horizontal = 'horizontal'
}
export enum LegendPosition {
Top = 'top',
Bottom = 'bottom',
Left = 'left',
Right = 'right',
None = 'none'
}
export enum DataType {
Number = 'number',
Point = 'point'
}

View File

@@ -3,8 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChartInsight, ChartType, customMixin, IChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { ChartInsight, customMixin, IChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { mixin } from 'sql/base/common/objects';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import * as colors from 'vs/platform/theme/common/colorRegistry';

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import PieChart from './pieChart.component';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export default class DoughnutChart extends PieChart {
protected readonly chartType: ChartType = ChartType.Doughnut;

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import BarChart from './barChart.component';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export default class HorizontalBarChart extends BarChart {
protected readonly chartType: ChartType = ChartType.HorizontalBar;

View File

@@ -3,16 +3,13 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChartType, customMixin, defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { mixin } from 'vs/base/common/objects';
import { defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import BarChart, { IBarChartConfig } from './barChart.component';
import { memoize, unmemoize } from 'sql/base/common/decorators';
import { mixin } from 'vs/base/common/objects';
import { clone } from 'sql/base/common/objects';
export enum DataType {
Number = 'number',
Point = 'point'
}
import { ChartType, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export interface ILineConfig extends IBarChartConfig {
dataType?: DataType;

View File

@@ -3,7 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChartInsight, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { ChartInsight } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export default class PieChart extends ChartInsight {
protected readonly chartType: ChartType = ChartType.Pie;

View File

@@ -3,11 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChartType, defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import LineChart, { ILineConfig } from './lineChart.component';
import { clone } from 'sql/base/common/objects';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { mixin } from 'vs/base/common/objects';
import { clone } from 'sql/base/common/objects';
const defaultScatterConfig = mixin(clone(defaultChartConfig), { dataType: 'point', dataDirection: 'horizontal' }) as ILineConfig;

View File

@@ -3,9 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { defaultChartConfig, IPointDataSet, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { defaultChartConfig, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import LineChart, { ILineConfig } from './lineChart.component';
import { clone } from 'sql/base/common/objects';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { mixin } from 'vs/base/common/objects';
import { Color } from 'vs/base/common/color';

View File

@@ -86,7 +86,7 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
}).filter(i => !!i);
}
this._tasks = tasks.map(i => TaskRegistry.getCommandActionById(i)).filter(v => !!v);
this._tasks = tasks.map(i => MenuRegistry.getCommand(i)).filter(v => !!v);
}
ngOnInit() {

View File

@@ -166,7 +166,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.onCellEditEnd = (event: Slick.OnCellChangeEventArgs<any>): void => {
// Store the value that was set
self.currentEditCellValue = event.item[event.cell - 1];
self.currentEditCellValue = event.item[event.cell];
};
this.overrideCellFn = (rowNumber, columnId, value?, data?): string => {
@@ -270,18 +270,18 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
return self.dataService.updateCell(sessionRowId, self.currentCell.column - 1, self.currentEditCellValue)
.then(
result => {
// Cell update was successful, update the flags
self.currentEditCellValue = null;
self.setCellDirtyState(row, self.currentCell.column, result.cell.isDirty);
self.setRowDirtyState(row, result.isRowDirty);
return Promise.resolve();
},
error => {
// Cell update failed, jump back to the last cell we were on
self.focusCell(self.currentCell.row, self.currentCell.column, true);
return Promise.reject(null);
}
result => {
// Cell update was successful, update the flags
self.currentEditCellValue = null;
self.setCellDirtyState(row, self.currentCell.column, result.cell.isDirty);
self.setRowDirtyState(row, result.isRowDirty);
return Promise.resolve();
},
error => {
// Cell update failed, jump back to the last cell we were on
self.focusCell(self.currentCell.row, self.currentCell.column, true);
return Promise.reject(null);
}
);
});
}
@@ -377,10 +377,11 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
index => { return {}; }
),
columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let columnIndex = (i + 1).toString();
return {
id: i.toString(),
id: columnIndex,
name: escape(c.columnName),
field: i.toString(),
field: columnIndex,
formatter: Services.textFormatter,
isEditable: c.isUpdatable
};
@@ -464,7 +465,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
} else {
try {
// Perform a revert row operation
if (this.currentCell) {
if (this.currentCell && this.currentCell.row !== undefined && this.currentCell.row !== null) {
await this.dataService.revertRow(this.currentCell.row);
}
} finally {

View File

@@ -16,7 +16,7 @@ import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
import { IInsightData, IInsightsView, IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
import { DataType, ILineConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
import { ILineConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
import * as PathUtilities from 'sql/common/pathUtilities';
import { IChartViewActionContext, CopyAction, CreateInsightAction, SaveImageAction } from 'sql/parts/grid/views/query/chartViewerActions';
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
@@ -24,10 +24,11 @@ import * as Constants from 'sql/parts/query/common/constants';
import { SelectBox as AngularSelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
import { LegendPosition, DataDirection, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
/* Insights */
import {
ChartInsight, DataDirection, LegendPosition
ChartInsight
} from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { IDisposable } from 'vs/base/common/lifecycle';

View File

@@ -188,8 +188,16 @@ export class InsightsDialogView extends Modal {
for (let i = 0; i < this._model.columns.length; i++) {
resourceArray.push({ label: this._model.columns[i], value: element.data[i], data: element.data });
}
this._bottomTableData.clear();
this._bottomTableData.push(resourceArray);
// this table view has to be collapsed and expanded
// because the initial expand doesn't have the
// loaded data
if (bottomTableView.isExpanded()) {
bottomTableView.collapse();
bottomTableView.expand();
}
this._enableTaskButtons(true);
} else {
this._enableTaskButtons(false);
@@ -334,6 +342,7 @@ export class InsightsDialogView extends Modal {
this.hide();
dispose(this._taskButtonDisposables);
this._taskButtonDisposables = [];
this.dispose();
}
protected onClose(e: StandardKeyboardEvent) {

View File

@@ -2,7 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import DivContainer from './divContainer.component';
import FlexContainer from './flexContainer.component';
import FormContainer from './formContainer.component';
import ToolbarContainer from './toolbarContainer.component';
@@ -25,6 +25,9 @@ import EditorComponent from './editor.component';
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
export const DIV_CONTAINER = 'div-container';
registerComponentType(DIV_CONTAINER, ModelComponentTypes.DivContainer, DivContainer);
export const FLEX_CONTAINER = 'flex-container';
registerComponentType(FLEX_CONTAINER, ModelComponentTypes.FlexContainer, FlexContainer);

View File

@@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------------------------
* 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!./divContainer';
import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList,
} from '@angular/core';
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces';
import * as sqlops from 'sqlops';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
import types = require('vs/base/common/types');
class DivItem {
constructor(public descriptor: IComponentDescriptor, public config: sqlops.DivItemLayout) { }
}
@Component({
template: `
<div #divContainer *ngIf="items" class="divContainer" [style.height]="height" [style.width]="width">
<div *ngFor="let item of items" [style.order]="getItemOrder(item)" [ngStyle]="getItemStyles(item)">
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
</model-component-wrapper>
</div>
</div>
`
})
export default class DivContainer extends ContainerBase<sqlops.DivItemLayout> implements IComponent, OnDestroy {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
@ViewChild('divContainer', { read: ElementRef }) divContainer;
private _height: string;
private _width: string;
private _overflowY: string;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) el: ElementRef
) {
super(changeRef, el);
this._overflowY = ''; // default
}
ngOnInit(): void {
this.baseInit();
}
ngOnDestroy(): void {
this.baseDestroy();
}
/// IComponent implementation
public setLayout(layout: sqlops.DivLayout): void {
this._height = this.convertSize(layout.height);
this._width = this.convertSize(layout.width);
this.layout();
}
public setProperties(properties: { [key: string]: any; }): void {
super.setProperties(properties);
if (this.overflowY !== this._overflowY) {
this.updateOverflowY();
}
this.updateScroll();
}
private updateOverflowY() {
this._overflowY = this.overflowY;
if (this._overflowY) {
let element = <HTMLElement> this.divContainer.nativeElement;
element.style.overflowY = this._overflowY;
}
}
private updateScroll() {
let element = <HTMLElement> this.divContainer.nativeElement;
element.scrollTop = element.scrollTop - this.yOffsetChange;
element.dispatchEvent(new Event('scroll'));
}
// CSS-bound properties
public get height(): string {
return this._height;
}
public get width(): string {
return this._width;
}
// CSS-bound properties
public get overflowY(): string {
return this.getPropertyOrDefault<sqlops.DivContainerProperties, any>((props) => props.overflowY, '');
}
public set overflowY(newValue: string) {
this.setPropertyFromUI<sqlops.DivContainerProperties, any>((properties, newValue) => { properties.overflowY = newValue; }, newValue);
}
public get yOffsetChange(): number {
return this.getPropertyOrDefault<sqlops.DivContainerProperties, any>((props) => props.yOffsetChange, 0);
}
public set yOffsetChange(newValue: number) {
this.setPropertyFromUI<sqlops.DivContainerProperties, any>((properties, newValue) => { properties.yOffsetChange = newValue; }, newValue);
}
private getItemOrder(item: DivItem): number {
return item.config ? item.config.order : 0;
}
private getItemStyles(item: DivItem): { [key: string]: string } {
return item.config && item.config.CSSStyles ? item.config.CSSStyles : {};
}
}

View File

@@ -0,0 +1,5 @@
.divContainer {
display: block;
height: 100%;
}

View File

@@ -3,16 +3,50 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as sqlops from 'sqlops';
import { TPromise } from 'vs/base/common/winjs.base';
import { IEditorModel } from 'vs/platform/editor/common/editor';
import { EditorInput } from 'vs/workbench/common/editor';
import { EditorInput, EditorModel, ConfirmResult } from 'vs/workbench/common/editor';
import * as DOM from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import { DialogPane } from 'sql/platform/dialog/dialogPane';
import { Emitter, Event } from 'vs/base/common/event';
import * as sqlops from 'sqlops';
export type ModeViewSaveHandler = (handle: number) => Thenable<boolean>;
export class ModelViewInputModel extends EditorModel {
private dirty: boolean;
private readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
get onDidChangeDirty(): Event<void> { return this._onDidChangeDirty.event; }
constructor(public readonly modelViewId, private readonly handle: number, private saveHandler?: ModeViewSaveHandler) {
super();
this.dirty = false;
}
get isDirty(): boolean {
return this.dirty;
}
public setDirty(dirty: boolean): void {
if (this.dirty === dirty) {
return;
}
this.dirty = dirty;
this._onDidChangeDirty.fire();
}
save(): TPromise<boolean> {
if (this.saveHandler) {
return TPromise.wrap(this.saveHandler(this.handle));
}
return TPromise.wrap(true);
}
}
export class ModelViewInput extends EditorInput {
public static ID: string = 'workbench.editorinputs.ModelViewEditorInput';
@@ -20,14 +54,15 @@ export class ModelViewInput extends EditorInput {
private _dialogPaneContainer: HTMLElement;
private _dialogPane: DialogPane;
constructor(private _title: string, private _modelViewId: string,
constructor(private _title: string, private _model: ModelViewInputModel,
private _options: sqlops.ModelViewEditorOptions,
@IInstantiationService private _instantiationService: IInstantiationService,
@IPartService private readonly _partService: IPartService
) {
super();
this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire());
this._container = document.createElement('div');
this._container.id = `modelView-${_modelViewId}`;
this._container.id = `modelView-${_model.modelViewId}`;
this._partService.getContainer(Parts.EDITOR_PART).appendChild(this._container);
}
@@ -37,7 +72,7 @@ export class ModelViewInput extends EditorInput {
}
public get modelViewId(): string {
return this._modelViewId;
return this._model.modelViewId;
}
public getTypeId(): string {
@@ -85,6 +120,31 @@ export class ModelViewInput extends EditorInput {
return this._options;
}
/**
* An editor that is dirty will be asked to be saved once it closes.
*/
isDirty(): boolean {
return this._model.isDirty;
}
/**
* Subclasses should bring up a proper dialog for the user if the editor is dirty and return the result.
*/
confirmSave(): TPromise<ConfirmResult> {
// TODO #2530 support save on close / confirm save. This is significantly more work
// as we need to either integrate with textFileService (seems like this isn't viable)
// or register our own complimentary service that handles the lifecycle operations such
// as close all, auto save etc.
return TPromise.wrap(ConfirmResult.DONT_SAVE);
}
/**
* Saves the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation.
*/
save(): TPromise<boolean> {
return this._model.save();
}
public dispose(): void {
if (this._dialogPane) {
this._dialogPane.dispose();
@@ -93,6 +153,9 @@ export class ModelViewInput extends EditorInput {
this._container.remove();
this._container = undefined;
}
if (this._model) {
this._model.dispose();
}
super.dispose();
}
}

View File

@@ -72,6 +72,8 @@ export interface IObjectExplorerService {
getActiveConnectionNodes(): TreeNode[];
getTreeNode(connectionId: string, nodePath: string): Thenable<TreeNode>;
refreshNodeInView(connectionId: string, nodePath: string): Thenable<TreeNode>;
}
interface SessionStatus {
@@ -476,6 +478,20 @@ export class ObjectExplorerService implements IObjectExplorerService {
return Object.values(this._activeObjectExplorerNodes);
}
public async refreshNodeInView(connectionId: string, nodePath: string): Promise<TreeNode> {
// Get the tree node and call refresh from the provider
let treeNode = await this.getTreeNode(connectionId, nodePath);
await this.refreshTreeNode(treeNode.getSession(), treeNode);
// Get the new tree node, refresh it in the view, and expand it if needed
treeNode = await this.getTreeNode(connectionId, nodePath);
await this._serverTreeView.refreshElement(treeNode);
if (treeNode.children.length > 0) {
await treeNode.setExpandedState(TreeItemCollapsibleState.Expanded);
}
return treeNode;
}
private async setNodeExpandedState(treeNode: TreeNode, expandedState: TreeItemCollapsibleState): Promise<void> {
treeNode = await this.getUpdatedTreeNode(treeNode);
let expandNode = this.getTreeItem(treeNode);

View File

@@ -167,12 +167,12 @@ export class ServerTreeActionProvider extends ContributableActionProvider {
if (TreeUpdateUtils.isDatabaseNode(treeNode)) {
if (TreeUpdateUtils.isAvailableDatabaseNode(treeNode)) {
actions.push(this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL, context.tree));
this.addNewQueryAction(context, actions);
} else {
return actions;
}
}
this.addNewQueryAction(context, actions);
this.addScriptingActions(context, actions);
actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, context.tree, treeNode));

View File

@@ -138,7 +138,7 @@ export class ServerTreeView {
let expandGroups: boolean = self._configurationService.getValue(SERVER_GROUP_CONFIG)[SERVER_GROUP_AUTOEXPAND_CONFIG];
if (expandGroups) {
self._tree.expandAll(ConnectionProfileGroup.getSubgroups(root));
self._tree.expandAll(ConnectionProfileGroup.getSubgroups(root));
}
if (root && !root.hasValidConnections) {
@@ -244,6 +244,10 @@ export class ServerTreeView {
TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService);
}
public refreshElement(element: any): Thenable<void> {
return this._tree.refresh(element);
}
/**
* Filter connections based on view (recent/active)
*/

View File

@@ -10,6 +10,27 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { EditorInput } from 'vs/workbench/common/editor';
import { Emitter } from 'vs/base/common/event';
import { GridPanelState } from 'sql/parts/query/editor/gridPanel';
import { MessagePanelState } from 'sql/parts/query/editor/messagePanel';
import { QueryPlanState } from 'sql/parts/queryPlan/queryPlan';
import { ChartState } from 'sql/parts/query/editor/charting/chartView';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export class ResultsViewState {
public gridPanelState: GridPanelState = new GridPanelState();
public messagePanelState: MessagePanelState = new MessagePanelState(this.configurationService);
public chartState: ChartState = new ChartState();
public queryPlanState: QueryPlanState = new QueryPlanState();
public gridPanelSize: number;
public messagePanelSize: number;
public activeTab: string;
public visibleTabs: Set<string> = new Set<string>();
constructor(@IConfigurationService private configurationService: IConfigurationService) {
}
}
/**
* Input for the QueryResultsEditor. This input helps with logic for the viewing and editing of
* data in the results grid.
@@ -29,7 +50,11 @@ export class QueryResultsInput extends EditorInput {
public readonly onRestoreViewStateEmitter = new Emitter<void>();
public readonly onSaveViewStateEmitter = new Emitter<void>();
constructor(private _uri: string) {
public readonly state = new ResultsViewState(this.configurationService);
constructor(private _uri: string,
@IConfigurationService private configurationService: IConfigurationService
) {
super();
this._visible = false;
this._hasBootstrapped = false;

View File

@@ -8,10 +8,9 @@
import { localize } from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
import { InsightType, IInsightOptions } from './insights/interfaces';
import { DataDirection, ChartType, LegendPosition, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);

View File

@@ -15,7 +15,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
export class ChartTab implements IPanelTab {
public readonly title = localize('chartTabTitle', 'Chart');
public readonly identifier = generateUuid();
public readonly identifier = 'ChartTab';
public readonly view: ChartView;
constructor(@IInstantiationService instantiationService: IInstantiationService) {

View File

@@ -12,11 +12,11 @@ import { Insight } from './insights/insight';
import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { ChartOptions, IChartOption, ControlType } from './chartOptions';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
import { IInsightOptions } from './insights/interfaces';
import { CopyAction, SaveImageAction, CreateInsightAction, IChartActionContext } from './actions';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { Dimension, $, getContentHeight, getContentWidth } from 'vs/base/browser/dom';
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
@@ -27,6 +27,14 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { attachSelectBoxStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { isUndefinedOrNull } from 'vs/base/common/types';
export class ChartState {
dataId: { batchId: number, resultId: number };
options: IInsightOptions = {
type: ChartType.Bar
};
}
declare class Proxy {
constructor(object, handler);
@@ -43,6 +51,8 @@ export class ChartView implements IPanelView {
private _copyAction: CopyAction;
private _saveAction: SaveImageAction;
private _state: ChartState;
private options: IInsightOptions = {
type: ChartType.Bar
};
@@ -61,7 +71,7 @@ export class ChartView implements IPanelView {
private chartingContainer: HTMLElement;
private optionDisposables: IDisposable[] = [];
private optionMap: { [x: string]: HTMLElement } = {};
private optionMap: { [x: string]: { element: HTMLElement; set: (val) => void } } = {};
constructor(
@IContextViewService private _contextViewService: IContextViewService,
@@ -95,6 +105,10 @@ export class ChartView implements IPanelView {
}
let result = Reflect.set(target, key, value, receiver);
// mirror the change in our state
if (self.state) {
Reflect.set(self.state.options, key, value);
}
if (change) {
self.taskbar.context = <IChartActionContext>{ options: self.options, insight: self.insight ? self.insight.insight : undefined };
@@ -138,6 +152,7 @@ export class ChartView implements IPanelView {
}
public chart(dataId: { batchId: number, resultId: number }) {
this.state.dataId = dataId;
this._currentData = dataId;
this.shouldGraph();
}
@@ -172,7 +187,7 @@ export class ChartView implements IPanelView {
});
}
}
// if we have the necessary information but the information isn't avaiable yet,
// if we have the necessary information but the information isn't available yet,
// we should be smart and retrying when the information might be available
}
}
@@ -180,7 +195,9 @@ export class ChartView implements IPanelView {
private buildOptions() {
dispose(this.optionDisposables);
this.optionDisposables = [];
this.optionMap = {};
this.optionMap = {
'type': this.optionMap['type']
};
new Builder(this.typeControls).clearChildren();
this.updateActionbar();
@@ -200,9 +217,9 @@ export class ChartView implements IPanelView {
let option = ChartOptions[this.options.type].find(e => e.configEntry === key);
if (option && option.if) {
if (option.if(this.options)) {
new Builder(this.optionMap[key]).show();
new Builder(this.optionMap[key].element).show();
} else {
new Builder(this.optionMap[key]).hide();
new Builder(this.optionMap[key].element).hide();
}
}
}
@@ -211,6 +228,7 @@ export class ChartView implements IPanelView {
private updateActionbar() {
if (this.insight && this.insight.isCopyable) {
this.taskbar.context = { insight: this.insight.insight, options: this.options };
this.taskbar.setContent([
{ action: this._createInsightAction },
{ action: this._copyAction },
@@ -226,57 +244,104 @@ export class ChartView implements IPanelView {
label.innerText = option.label;
let optionContainer = $('div.option-container');
optionContainer.appendChild(label);
let setFunc: (val) => void;
let value = this.state ? this.state.options[option.configEntry] || option.default : option.default;
switch (option.type) {
case ControlType.checkbox:
let checkbox = new Checkbox(optionContainer, {
label: '',
ariaLabel: option.label,
checked: option.default,
checked: value,
onChange: () => {
if (this.options[option.configEntry] !== checkbox.checked) {
this.options[option.configEntry] = checkbox.checked;
this.insight.options = this.options;
if (this.insight) {
this.insight.options = this.options;
}
}
}
});
setFunc = (val: boolean) => {
checkbox.checked = val;
};
break;
case ControlType.combo:
let dropdown = new SelectBox(option.displayableOptions || option.options, 0, this._contextViewService);
dropdown.select(option.options.indexOf(option.default));
dropdown.select(option.options.indexOf(value));
dropdown.render(optionContainer);
dropdown.onDidSelect(e => {
if (this.options[option.configEntry] !== option.options[e.index]) {
this.options[option.configEntry] = option.options[e.index];
this.insight.options = this.options;
if (this.insight) {
this.insight.options = this.options;
}
}
});
setFunc = (val: string) => {
if (!isUndefinedOrNull(val)) {
dropdown.select(option.options.indexOf(val));
}
};
this.optionDisposables.push(attachSelectBoxStyler(dropdown, this._themeService));
break;
case ControlType.input:
let input = new InputBox(optionContainer, this._contextViewService);
input.value = option.default || '';
input.value = value || '';
input.onDidChange(e => {
if (this.options[option.configEntry] !== e) {
this.options[option.configEntry] = e;
this.insight.options = this.options;
if (this.insight) {
this.insight.options = this.options;
}
}
});
setFunc = (val: string) => {
if (!isUndefinedOrNull(val)) {
input.value = val;
}
};
this.optionDisposables.push(attachInputBoxStyler(input, this._themeService));
break;
case ControlType.numberInput:
let numberInput = new InputBox(optionContainer, this._contextViewService, { type: 'number' });
numberInput.value = option.default || '';
numberInput.value = value || '';
numberInput.onDidChange(e => {
if (this.options[option.configEntry] !== Number(e)) {
this.options[option.configEntry] = Number(e);
this.insight.options = this.options;
if (this.insight) {
this.insight.options = this.options;
}
}
});
setFunc = (val: string) => {
if (!isUndefinedOrNull(val)) {
input.value = val;
}
};
this.optionDisposables.push(attachInputBoxStyler(numberInput, this._themeService));
break;
}
this.optionMap[option.configEntry] = optionContainer;
this.optionMap[option.configEntry] = { element: optionContainer, set: setFunc };
container.appendChild(optionContainer);
this.options[option.configEntry] = option.default;
this.options[option.configEntry] = value;
}
}
public set state(val: ChartState) {
this._state = val;
if (this.state.options) {
for (let key in this.state.options) {
if (this.state.options.hasOwnProperty(key)) {
this.options[key] = this.state.options[key];
this.optionMap[key].set(this.state.options[key]);
}
}
}
if (this.state.dataId) {
this.chart(this.state.dataId);
}
}
public get state(): ChartState {
return this._state;
}
}

View File

@@ -13,9 +13,9 @@ import * as colors from 'vs/platform/theme/common/colorRegistry';
import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { IInsightOptions, IInsight } from './interfaces';
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
const noneLineGraphs = [ChartType.Doughnut, ChartType.Pie];
@@ -149,6 +149,8 @@ export class Graph implements IInsight {
let foreground = foregroundColor ? foregroundColor.toString() : null;
let gridLinesColor = this._theme.getColor(editorLineNumbers);
let gridLines = gridLinesColor ? gridLinesColor.toString() : null;
let backgroundColor = this._theme.getColor(colors.editorBackground);
let background = backgroundColor ? backgroundColor.toString() : null;
if (options) {
retval.scales = {};
@@ -187,12 +189,20 @@ export class Graph implements IInsight {
}];
}
retval.legend = {
retval.legend = <ChartJs.ChartLegendOptions>{
position: options.legendPosition as ChartJs.PositionType,
display: options.legendPosition !== LegendPosition.None
display: options.legendPosition !== LegendPosition.None,
labels: {
fontColor: foreground
}
};
}
// these are custom options that will throw compile errors
(<any>retval).viewArea = {
backgroundColor: background
};
return retval;
}

View File

@@ -6,15 +6,15 @@
'use strict';
import { Graph } from './graphInsight';
import { ChartType, DataDirection } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { Builder } from 'vs/base/browser/builder';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { DataDirection, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { ImageInsight } from './imageInsight';
import { TableInsight } from './tableInsight';
import { IInsightOptions, IInsight, InsightType, IInsightCtor } from './interfaces';
import { CountInsight } from './countInsight';
import { Builder } from 'vs/base/browser/builder';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Dimension } from 'vs/base/browser/dom';
const defaultOptions: IInsightOptions = {

View File

@@ -4,11 +4,10 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Dimension } from 'vs/base/browser/dom';
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { ChartType, LegendPosition, DataDirection } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { Dimension } from 'vs/base/browser/dom';
import { DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
import { DataDirection, ChartType, LegendPosition, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export interface IInsightOptions {
type: InsightType | ChartType;

View File

@@ -36,7 +36,7 @@ import { $ } from 'vs/base/browser/builder';
import { generateUuid } from 'vs/base/common/uuid';
import { TPromise } from 'vs/base/common/winjs.base';
import { Separator, ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { Dimension, getContentWidth } from 'vs/base/browser/dom';
import { Dimension, getContentWidth, isInDOM } from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -55,6 +55,11 @@ const ACTIONBAR_HEIGHT = 100;
// this handles min size if rows is greater than the min grid visible rows
const MIN_GRID_HEIGHT = (MIN_GRID_HEIGHT_ROWS * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
export class GridPanelState {
public tableStates: GridTableState[] = [];
public scrollPosition: number;
public collapsed = false;
}
export interface IGridTableState {
canBeMaximized: boolean;
@@ -73,11 +78,12 @@ export class GridTableState {
private _canBeMaximized: boolean;
constructor(state?: IGridTableState) {
if (state) {
this._maximized = state.maximized;
this._canBeMaximized = state.canBeMaximized;
}
/* The top row of the current scroll */
public scrollPosition = 0;
public selection: Slick.Range[];
public activeCell: Slick.Cell;
constructor(public readonly resultId: number, public readonly batchId: number) {
}
public get canBeMaximized(): boolean {
@@ -103,10 +109,6 @@ export class GridTableState {
this._maximized = val;
this._onMaximizedChange.fire(val);
}
public clone(): GridTableState {
return new GridTableState({ canBeMaximized: this.canBeMaximized, maximized: this.maximized });
}
}
export class GridPanel extends ViewletPanel {
@@ -120,6 +122,7 @@ export class GridPanel extends ViewletPanel {
private runner: QueryRunner;
private maximizedGrid: GridTable<any>;
private _state: GridPanelState;
constructor(
options: IViewletPanelOptions,
@@ -131,6 +134,16 @@ export class GridPanel extends ViewletPanel {
) {
super(options, keybindingService, contextMenuService, configurationService);
this.splitView = new ScrollableSplitView(this.container, { enableResizing: false });
this.splitView.onScroll(e => {
if (this.state) {
this.state.scrollPosition = e;
}
});
this.onDidChange(e => {
if (this.state) {
this.state.collapsed = !this.isExpanded();
}
});
}
protected renderBody(container: HTMLElement): void {
@@ -155,7 +168,12 @@ export class GridPanel extends ViewletPanel {
this.queryRunnerDisposables = [];
this.runner = runner;
this.queryRunnerDisposables.push(this.runner.onResultSet(e => this.onResultSet(e)));
this.queryRunnerDisposables.push(this.runner.onQueryStart(() => this.reset()));
this.queryRunnerDisposables.push(this.runner.onQueryStart(() => {
if (this.state) {
this.state.tableStates = [];
}
this.reset();
}));
}
private onResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) {
@@ -168,6 +186,10 @@ export class GridPanel extends ViewletPanel {
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition);
}
}
private addResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) {
@@ -181,8 +203,18 @@ export class GridPanel extends ViewletPanel {
let tables: GridTable<any>[] = [];
for (let set of resultsToAdd) {
let tableState = new GridTableState();
let table = this.instantiationService.createInstance(GridTable, this.runner, tableState, set);
let tableState: GridTableState;
if (this._state) {
tableState = this.state.tableStates.find(e => e.batchId === set.batchId && e.resultId === set.id);
}
if (!tableState) {
tableState = new GridTableState(set.id, set.batchId);
if (this._state) {
this._state.tableStates.push(tableState);
}
}
let table = this.instantiationService.createInstance(GridTable, this.runner, set);
table.state = tableState;
tableState.onMaximizedChange(e => {
if (e) {
this.maximizeTable(table.id);
@@ -208,7 +240,6 @@ export class GridPanel extends ViewletPanel {
for (let i = this.splitView.length - 1; i >= 0; i--) {
this.splitView.removeView(i);
}
dispose(this.tables);
this.tables = [];
@@ -241,6 +272,24 @@ export class GridPanel extends ViewletPanel {
this.splitView.addViews(this.tables, this.tables.map(i => i.minimumSize));
}
}
public set state(val: GridPanelState) {
this._state = val;
this.tables.map(t => {
let state = this.state.tableStates.find(s => s.batchId === t.resultSet.batchId && s.resultId === t.resultSet.id);
if (!state) {
this.state.tableStates.push(t.state);
}
if (state) {
t.state = state;
}
});
this.setExpanded(!this.state.collapsed);
}
public get state(): GridPanelState {
return this._state;
}
}
class GridTable<T> extends Disposable implements IView {
@@ -259,13 +308,16 @@ class GridTable<T> extends Disposable implements IView {
public id = generateUuid();
readonly element: HTMLElement = this.container;
private _state: GridTableState;
private scrolled = false;
// this handles if the row count is small, like 4-5 rows
private readonly maxSize = ((this.resultSet.rowCount) * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
constructor(
private runner: QueryRunner,
public state: GridTableState,
private resultSet: sqlops.ResultSetSummary,
public readonly resultSet: sqlops.ResultSetSummary,
@IContextMenuService private contextMenuService: IContextMenuService,
@IInstantiationService private instantiationService: IInstantiationService,
@IEditorService private editorService: IEditorService,
@@ -291,6 +343,11 @@ class GridTable<T> extends Disposable implements IView {
});
}
public onRemove() {
// when we are removed slickgrid acts badly so we need to account for that
this.scrolled = false;
}
public render(container: HTMLElement, orientation: Orientation): void {
container.appendChild(this.container);
}
@@ -357,10 +414,59 @@ class GridTable<T> extends Disposable implements IView {
});
this.actionBar.push(actions, { icon: true, label: false });
this.selectionModel.onSelectedRangesChanged.subscribe(e => {
if (this.state) {
this.state.selection = this.selectionModel.getSelectedRanges();
}
});
this.table.grid.onScroll.subscribe((e, data) => {
if (!this.scrolled && this.state.scrollPosition && isInDOM(this.container)) {
this.scrolled = true;
this.table.grid.scrollTo(this.state.scrollPosition);
}
if (this.state && isInDOM(this.container)) {
this.state.scrollPosition = data.scrollTop;
}
});
this.table.grid.onActiveCellChanged.subscribe(e => {
if (this.state) {
this.state.activeCell = this.table.grid.getActiveCell();
}
});
this.setupState();
}
private setupState() {
// change actionbar on maximize change
this.state.onMaximizedChange(this.rebuildActionBar, this);
this.state.onCanBeMaximizedChange(this.rebuildActionBar, this);
if (this.state.scrollPosition) {
// most of the time this won't do anything
this.table.grid.scrollTo(this.state.scrollPosition);
// the problem here is that the scrolling state slickgrid uses
// doesn't work with it offDOM.
}
if (this.state.selection) {
this.selectionModel.setSelectedRanges(this.state.selection);
}
if (this.state.activeCell) {
this.table.setActiveCell(this.state.activeCell.row, this.state.activeCell.cell);
}
}
public get state(): GridTableState {
return this._state;
}
public set state(val: GridTableState) {
this._state = val;
}
private onTableClick(event: ITableMouseEvent) {

View File

@@ -0,0 +1,5 @@
.monaco-select-box {
cursor: pointer;
min-width: 150px;
padding: 2px;
}

View File

@@ -25,7 +25,7 @@ import { OpenMode, ClickBehavior, ICancelableEvent, IControllerOptions } from 'v
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { $ } from 'vs/base/browser/builder';
import { isArray } from 'vs/base/common/types';
import { isArray, isUndefinedOrNull } from 'vs/base/common/types';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditor } from 'vs/editor/common/editorCommon';
@@ -59,6 +59,18 @@ const TemplateIds = {
ERROR: 'error'
};
export class MessagePanelState {
public scrollPosition: number;
public collapsed = false;
constructor(@IConfigurationService configurationService: IConfigurationService) {
let messagesOpenedSettings = configurationService.getValue<boolean>('sql.messagesDefaultOpen');
if (!isUndefinedOrNull(messagesOpenedSettings)) {
this.collapsed = !messagesOpenedSettings;
}
}
}
export class MessagePanel extends ViewletPanel {
private ds = new MessageDataSource();
private renderer = new MessageRenderer();
@@ -67,6 +79,7 @@ export class MessagePanel extends ViewletPanel {
private container = $('div message-tree').getHTMLElement();
private queryRunnerDisposables: IDisposable[] = [];
private _state: MessagePanelState;
private tree: ITree;
@@ -86,6 +99,16 @@ export class MessagePanel extends ViewletPanel {
renderer: this.renderer,
controller: this.controller
}, { keyboardSupport: false });
this.tree.onDidScroll(e => {
if (this.state) {
this.state.scrollPosition = this.tree.getScrollPosition();
}
});
this.onDidChange(e => {
if (this.state) {
this.state.collapsed = !this.isExpanded();
}
});
}
protected renderBody(container: HTMLElement): void {
@@ -99,8 +122,12 @@ export class MessagePanel extends ViewletPanel {
protected layoutBody(size: number): void {
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.layout(size);
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
if (this.state && this.state.scrollPosition) {
this.tree.setScrollPosition(this.state.scrollPosition);
} else {
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
}
}
@@ -124,12 +151,19 @@ export class MessagePanel extends ViewletPanel {
if (hasError) {
this.setExpanded(true);
}
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
if (previousScrollPosition === 1) {
if (this.state.scrollPosition) {
this.tree.refresh(this.model).then(() => {
this.tree.setScrollPosition(1);
}
});
});
} else {
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
});
}
this.maximumBodySize = this.model.messages.length * 22;
}
private reset() {
@@ -137,6 +171,17 @@ export class MessagePanel extends ViewletPanel {
this.model.totalExecuteMessage = undefined;
this.tree.refresh(this.model);
}
public set state(val: MessagePanelState) {
this._state = val;
if (this.state.scrollPosition) {
this.tree.setScrollPosition(this.state.scrollPosition);
}
this.setExpanded(!this.state.collapsed);
}
public get state(): MessagePanelState {
return this._state;
}
}
class MessageDataSource implements IDataSource {

View File

@@ -43,7 +43,6 @@ import {
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IEditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -500,7 +499,7 @@ export class QueryEditor extends BaseEditor {
public get listDatabasesActionItem(): ListDatabasesActionItem {
if (!this._listDatabasesActionItem) {
this._listDatabasesActionItem = this._instantiationService.createInstance(ListDatabasesActionItem, this, this._listDatabasesAction);
this._register(attachEditableDropdownStyler(this._listDatabasesActionItem, this.themeService));
this._register(this._listDatabasesActionItem.attachStyler(this.themeService));
}
return this._listDatabasesActionItem;
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
import { QueryResultsInput, ResultsViewState } from 'sql/parts/query/common/queryResultsInput';
import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/panel';
import { IQueryModelService } from '../execution/queryModel';
import QueryRunner from 'sql/parts/query/execution/queryRunner';
@@ -14,11 +14,10 @@ import { ChartTab } from './charting/chartTab';
import { QueryPlanTab } from 'sql/parts/queryPlan/queryPlan';
import * as nls from 'vs/nls';
import * as UUID from 'vs/base/common/uuid';
import { PanelViewlet } from 'vs/workbench/browser/parts/views/panelViewlet';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as DOM from 'vs/base/browser/dom';
import { once } from 'vs/base/common/event';
import { once, anyEvent } from 'vs/base/common/event';
class ResultsView implements IPanelView {
private panelViewlet: PanelViewlet;
@@ -26,45 +25,72 @@ class ResultsView implements IPanelView {
private messagePanel: MessagePanel;
private container = document.createElement('div');
private currentDimension: DOM.Dimension;
private isGridRendered = false;
private lastGridHeight: number;
private needsGridResize = false;
private _state: ResultsViewState;
constructor(instantiationService: IInstantiationService) {
this.panelViewlet = instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
this.gridPanel = instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results'), id: 'gridPanel' });
this.messagePanel = instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages'), minimumBodySize: 0, id: 'messagePanel' });
constructor(private instantiationService: IInstantiationService) {
this.panelViewlet = this.instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
this.gridPanel = this.instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results'), id: 'gridPanel' });
this.messagePanel = this.instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages'), minimumBodySize: 0, id: 'messagePanel' });
this.gridPanel.render();
this.messagePanel.render();
this.panelViewlet.create(this.container).then(() => {
this.gridPanel.setVisible(false);
this.panelViewlet.addPanels([
{ panel: this.messagePanel, size: this.messagePanel.minimumSize, index: 1 }
]);
});
this.gridPanel.onDidChange(e => {
anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(e => {
let size = this.gridPanel.maximumBodySize;
if (this.isGridRendered) {
if (size < 1) {
this.lastGridHeight = this.panelViewlet.getPanelSize(this.gridPanel);
this.panelViewlet.removePanels([this.gridPanel]);
// tell the panel is has been removed.
this.gridPanel.layout(0);
this.isGridRendered = false;
}
} else {
if (size > 0) {
this.panelViewlet.addPanels([
{ panel: this.gridPanel, index: 0, size: this.lastGridHeight || Math.round(this.currentDimension.height * .8) }
]);
this.isGridRendered = true;
if (size < 1 && this.gridPanel.isVisible()) {
this.gridPanel.setVisible(false);
this.panelViewlet.removePanels([this.gridPanel]);
this.gridPanel.layout(0);
} else if (size > 0 && !this.gridPanel.isVisible()) {
this.gridPanel.setVisible(true);
let panelSize: number;
if (this.state && this.state.gridPanelSize) {
panelSize = this.state.gridPanelSize;
} else if (this.currentDimension) {
panelSize = Math.round(this.currentDimension.height * .7);
} else {
panelSize = 200;
this.needsGridResize = true;
}
this.panelViewlet.addPanels([{ panel: this.gridPanel, index: 0, size: panelSize }]);
}
});
let gridResizeList = this.gridPanel.onDidChange(e => {
this.panelViewlet.resizePanel(this.gridPanel, Math.round(this.currentDimension.height * .8));
});
let resizeList = anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(() => {
let panelSize: number;
if (this.state && this.state.gridPanelSize) {
panelSize = this.state.gridPanelSize;
} else if (this.currentDimension) {
panelSize = Math.round(this.currentDimension.height * .7);
} else {
panelSize = 200;
this.needsGridResize = true;
}
if (this.state.messagePanelSize) {
this.panelViewlet.resizePanel(this.gridPanel, this.state.messagePanelSize);
}
this.panelViewlet.resizePanel(this.gridPanel, panelSize);
})
// once the user changes the sash we should stop trying to resize the grid
once(this.panelViewlet.onDidSashChange)(e => {
gridResizeList.dispose();
this.needsGridResize = false;
resizeList.dispose();
});
this.panelViewlet.onDidSashChange(e => {
if (this.state) {
if (this.gridPanel.isExpanded()) {
this.state.gridPanelSize = this.panelViewlet.getPanelSize(this.gridPanel);
}
if (this.messagePanel.isExpanded()) {
this.state.messagePanelSize = this.panelViewlet.getPanelSize(this.messagePanel);
}
}
});
}
@@ -79,6 +105,9 @@ class ResultsView implements IPanelView {
this.gridPanel.layout(dimension.height);
}
this.currentDimension = dimension;
if (this.needsGridResize) {
this.panelViewlet.resizePanel(this.gridPanel, this.state.gridPanelSize || Math.round(this.currentDimension.height * .7));
}
}
remove(): void {
@@ -93,11 +122,21 @@ class ResultsView implements IPanelView {
public hideResultHeader() {
this.gridPanel.headerVisible = false;
}
public set state(val: ResultsViewState) {
this._state = val;
this.gridPanel.state = val.gridPanelState;
this.messagePanel.state = val.messagePanelState;
}
public get state(): ResultsViewState {
return this._state;
}
}
class ResultsTab implements IPanelTab {
public readonly title = nls.localize('resultsTabTitle', 'Results');
public readonly identifier = UUID.generateUuid();
public readonly identifier = 'resultsTab';
public readonly view: ResultsView;
constructor(instantiationService: IInstantiationService) {
@@ -125,6 +164,12 @@ export class QueryResultsView {
this.chartTab = new ChartTab(instantiationService);
this._panelView = new TabbedPanel(container, { showHeaderWhenSingleView: false });
this.qpTab = new QueryPlanTab();
this._panelView.pushTab(this.resultsTab);
this._panelView.onTabChange(e => {
if (this.input) {
this.input.state.activeTab = e;
}
});
}
public style() {
@@ -132,11 +177,24 @@ export class QueryResultsView {
public set input(input: QueryResultsInput) {
this._input = input;
this.resultsTab.view.state = this.input.state;
this.qpTab.view.state = this.input.state.queryPlanState;
this.chartTab.view.state = this.input.state.chartState;
let queryRunner = this.queryModelService._getQueryInfo(input.uri).queryRunner;
this.resultsTab.queryRunner = queryRunner;
this.chartTab.queryRunner = queryRunner;
if (!this._panelView.contains(this.resultsTab)) {
this._panelView.pushTab(this.resultsTab);
if (this.input.state.visibleTabs.has(this.chartTab.identifier)) {
if (!this._panelView.contains(this.chartTab)) {
this._panelView.pushTab(this.chartTab);
}
}
if (this.input.state.visibleTabs.has(this.qpTab.identifier)) {
if (!this._panelView.contains(this.qpTab)) {
this._panelView.pushTab(this.qpTab);
}
}
if (this.input.state.activeTab) {
this._panelView.showTab(this.input.state.activeTab);
}
}
@@ -153,6 +211,7 @@ export class QueryResultsView {
}
public chartData(dataId: { resultId: number, batchId: number }): void {
this.input.state.visibleTabs.add(this.chartTab.identifier);
if (!this._panelView.contains(this.chartTab)) {
this._panelView.pushTab(this.chartTab);
}
@@ -162,6 +221,7 @@ export class QueryResultsView {
}
public showPlan(xml: string) {
this.input.state.visibleTabs.add(this.qpTab.identifier);
if (!this._panelView.contains(this.qpTab)) {
this._panelView.pushTab(this.qpTab);
}

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!sql/parts/query/editor/media/queryActions';
import * as nls from 'vs/nls';
import { Builder, $ } from 'vs/base/browser/builder';
import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown';
@@ -12,6 +13,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachEditableDropdownStyler, attachSelectBoxStyler } from 'sql/common/theme/styler';
import { ISelectionData } from 'sqlops';
import {
@@ -25,6 +27,8 @@ import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
/**
* Action class that query-based Actions will extend. This base class automatically handles activating and
@@ -431,6 +435,9 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
private _isConnected: boolean;
private $databaseListDropdown: Builder;
private _dropdown: Dropdown;
private _databaseSelectBox: SelectBox;
private _isInAccessibilityMode: boolean;
private readonly _selectDatabaseString: string = nls.localize("selectDatabase", "Select Database");
// CONSTRUCTOR /////////////////////////////////////////////////////////
constructor(
@@ -439,23 +446,33 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@INotificationService private _notificationService: INotificationService,
@IContextViewService contextViewProvider: IContextViewService,
@IThemeService themeService: IThemeService
@IThemeService themeService: IThemeService,
@IConfigurationService private readonly _configurationService: IConfigurationService
) {
super();
this._toDispose = [];
this.$databaseListDropdown = $('.databaseListDropdown');
let selectString = nls.localize("selectDatabase", "Select Database");
this._dropdown = new Dropdown(this.$databaseListDropdown.getHTMLElement(), contextViewProvider, themeService, {
strictSelection: true,
placeholder: selectString,
ariaLabel: selectString,
actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
});
this._dropdown.onValueChange(s => this.databaseSelected(s));
this._isInAccessibilityMode = this._configurationService.getValue('editor.accessibilitySupport') === 'on';
if (this._isInAccessibilityMode) {
this._databaseSelectBox = new SelectBox([this._selectDatabaseString], this._selectDatabaseString, contextViewProvider, undefined, { ariaLabel: this._selectDatabaseString });
this._databaseSelectBox.render(this.$databaseListDropdown.getHTMLElement());
this._databaseSelectBox.onDidSelect(e => { this.databaseSelected(e.selected); });
this._databaseSelectBox.disable();
} else {
this._dropdown = new Dropdown(this.$databaseListDropdown.getHTMLElement(), contextViewProvider, themeService, {
strictSelection: true,
placeholder: this._selectDatabaseString,
ariaLabel: this._selectDatabaseString,
actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
});
this._dropdown.onValueChange(s => this.databaseSelected(s));
this._toDispose.push(this._dropdown.onFocus(() => { self.onDropdownFocus(); }));
}
// Register event handlers
let self = this;
this._toDispose.push(this._dropdown.onFocus(() => { self.onDropdownFocus(); }));
this._toDispose.push(this._connectionManagementService.onConnectionChanged(params => { self.onConnectionChanged(params); }));
}
@@ -465,7 +482,12 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
}
public style(styles) {
this._dropdown.style(styles);
if (this._isInAccessibilityMode) {
this._databaseSelectBox.style(styles);
}
else {
this._dropdown.style(styles);
}
}
public setActionContext(context: any): void {
@@ -477,11 +499,27 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
}
public focus(): void {
this._dropdown.focus();
if (this._isInAccessibilityMode) {
this._databaseSelectBox.focus();
} else {
this._dropdown.focus();
}
}
public blur(): void {
this._dropdown.blur();
if (this._isInAccessibilityMode) {
this._databaseSelectBox.blur();
} else {
this._dropdown.blur();
}
}
public attachStyler(themeService: IThemeService): IDisposable {
if (this._isInAccessibilityMode) {
return attachSelectBoxStyler(this, themeService);
} else {
return attachEditableDropdownStyler(this, themeService);
}
}
public dispose(): void {
@@ -496,9 +534,15 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
public onDisconnect(): void {
this._isConnected = false;
this._dropdown.enabled = false;
this._currentDatabaseName = undefined;
this._dropdown.value = '';
if (this._isInAccessibilityMode) {
this._databaseSelectBox.disable();
this._databaseSelectBox.setOptions([this._selectDatabaseString]);
} else {
this._dropdown.enabled = false;
this._dropdown.value = '';
}
}
// PRIVATE HELPERS /////////////////////////////////////////////////////
@@ -515,22 +559,22 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
this._connectionManagementService.changeDatabase(this._editor.uri, dbName)
.then(
result => {
if (!result) {
result => {
if (!result) {
this.resetDatabaseName();
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('changeDatabase.failed', "Failed to change database")
});
}
},
error => {
this.resetDatabaseName();
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('changeDatabase.failed', "Failed to change database")
message: nls.localize('changeDatabase.failedWithError', "Failed to change database {0}", error)
});
}
},
error => {
this.resetDatabaseName();
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('changeDatabase.failedWithError', "Failed to change database {0}", error)
});
});
}
private getCurrentDatabaseName() {
@@ -545,7 +589,11 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
}
private resetDatabaseName() {
this._dropdown.value = this.getCurrentDatabaseName();
if (this._isInAccessibilityMode) {
this._databaseSelectBox.selectWithOptionName(this.getCurrentDatabaseName());
} else {
this._dropdown.value = this.getCurrentDatabaseName();
}
}
private onConnectionChanged(connParams: IConnectionParams): void {
@@ -579,9 +627,26 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
private updateConnection(databaseName: string) {
this._isConnected = true;
this._dropdown.enabled = true;
this._currentDatabaseName = databaseName;
this._dropdown.value = databaseName;
if (this._isInAccessibilityMode) {
this._databaseSelectBox.enable();
let self = this;
let uri = self._editor.connectedUri;
if (!uri) {
return;
}
self._connectionManagementService.listDatabases(uri)
.then(result => {
if (result && result.databaseNames) {
this._databaseSelectBox.setOptions(result.databaseNames);
}
this._databaseSelectBox.selectWithOptionName(databaseName);
});
} else {
this._dropdown.enabled = true;
this._dropdown.value = databaseName;
}
}
// TESTING PROPERTIES //////////////////////////////////////////////////

View File

@@ -13,9 +13,13 @@ import { localize } from 'vs/nls';
import * as UUID from 'vs/base/common/uuid';
import { Builder } from 'vs/base/browser/builder';
export class QueryPlanState {
xml: string;
}
export class QueryPlanTab implements IPanelTab {
public readonly title = localize('queryPlanTitle', 'Query Plan');
public readonly identifier = UUID.generateUuid();
public readonly identifier = 'QueryPlanTab';
public readonly view: QueryPlanView;
constructor() {
@@ -27,6 +31,7 @@ export class QueryPlanView implements IPanelView {
private qp: QueryPlan;
private xml: string;
private container = document.createElement('div');
private _state: QueryPlanState;
public render(container: HTMLElement): void {
if (!this.qp) {
@@ -36,6 +41,8 @@ export class QueryPlanView implements IPanelView {
}
}
container.appendChild(this.container);
container.style.overflow = 'scroll';
container.style.background = '#FFFFFF';
}
public layout(dimension: Dimension): void {
@@ -47,6 +54,20 @@ export class QueryPlanView implements IPanelView {
} else {
this.xml = xml;
}
if (this.state) {
this.state.xml = xml;
}
}
public set state(val: QueryPlanState) {
this._state = val;
if (this.state.xml) {
this.showPlan(this.state.xml);
}
}
public get state(): QueryPlanState {
return this._state;
}
}

View File

@@ -48,9 +48,7 @@ export abstract class Task {
id: this.id,
handler: (accessor, profile, args) => this.runTask(accessor, profile, args),
description: this._description,
iconClass: this._iconClass,
iconPath: this.opts.iconPath,
title: this.title
iconClass: this._iconClass
};
}
@@ -62,10 +60,8 @@ export abstract class Task {
};
}
public registerTask(showInCommandPalette: boolean = true): IDisposable {
if (showInCommandPalette) {
MenuRegistry.addCommand(this.toCommandAction());
}
public registerTask(): IDisposable {
MenuRegistry.addCommand(this.toCommandAction());
return TaskRegistry.registerTask(this.toITask());
}
@@ -100,8 +96,6 @@ export interface ITask {
precondition?: ContextKeyExpr;
description?: ITaskHandlerDescription;
iconClass?: string;
iconPath?: { dark: string; light?: string; };
title?: string;
}
export interface ITaskRegistry {
@@ -110,7 +104,6 @@ export interface ITaskRegistry {
getTasks(): string[];
getOrCreateTaskIconClassName(item: ICommandAction): string;
onTaskRegistered: Event<string>;
getCommandActionById(id: string): ICommandAction;
}
const ids = new IdGenerator('task-icon-');
@@ -121,7 +114,6 @@ export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
private _onTaskRegistered = new Emitter<string>();
public readonly onTaskRegistered: Event<string> = this._onTaskRegistered.event;
private taskIdToIconClassNameMap: Map<string /* task id */, string /* CSS rule */> = new Map<string, string>();
private taskIdToCommandActionMap: Map<string, ICommandAction> = new Map<string, ICommandAction>();
registerTask(idOrTask: string | ITask, handler?: ITaskHandler): IDisposable {
let disposable: IDisposable;
@@ -133,16 +125,6 @@ export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
if (idOrTask.iconClass) {
this.taskIdToIconClassNameMap.set(idOrTask.id, idOrTask.iconClass);
}
if (idOrTask.iconPath && idOrTask.title) {
this.taskIdToCommandActionMap.set(idOrTask.id, {
iconLocation: {
dark: URI.parse(idOrTask.iconPath.dark),
light: URI.parse(idOrTask.iconPath.light),
},
id: idOrTask.id,
title: idOrTask.title
});
}
disposable = CommandsRegistry.registerCommand(idOrTask);
id = idOrTask.id;
}
@@ -177,8 +159,4 @@ export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
getTasks(): string[] {
return this._tasks.slice(0);
}
getCommandActionById(taskId: string): ICommandAction {
return this.taskIdToCommandActionMap.get(taskId);
}
};

5
src/sql/sqlops.d.ts vendored
View File

@@ -185,6 +185,11 @@ declare module 'sqlops' {
* Get the parent node. Returns undefined if there is none.
*/
getParent(): Thenable<ObjectExplorerNode>;
/**
* Refresh the node, expanding it if it has children
*/
refresh(): Thenable<void>;
}
}

View File

@@ -17,6 +17,7 @@ declare module 'sqlops' {
*/
export interface ModelBuilder {
navContainer(): ContainerBuilder<NavContainer, any, any>;
divContainer(): DivBuilder;
flexContainer(): FlexBuilder;
card(): ComponentBuilder<CardComponent>;
inputBox(): ComponentBuilder<InputBoxComponent>;
@@ -72,6 +73,10 @@ declare module 'sqlops' {
}
export interface DivBuilder extends ContainerBuilder<DivContainer, DivLayout, DivItemLayout> {
}
export interface GroupBuilder extends ContainerBuilder<GroupContainer, GroupLayout, GroupItemLayout> {
}
@@ -346,6 +351,33 @@ declare module 'sqlops' {
export interface GroupItemLayout {
}
export interface DivLayout {
/**
* Container Height
*/
height?: number | string;
/**
* Container Width
*/
width?: number | string;
}
export interface DivItemLayout {
/**
* Matches the order CSS property and its available values.
*/
order?: number;
/**
* Matches the CSS style key and its available values.
*/
CSSStyles?: { [key: string]: string };
}
export interface DivContainer extends Container<DivLayout, DivItemLayout>, DivContainerProperties {
}
export interface FlexContainer extends Container<FlexLayout, FlexItemLayout> {
}
@@ -568,6 +600,19 @@ declare module 'sqlops' {
loading?: boolean;
}
export interface DivContainerProperties extends ComponentProperties {
/**
* Matches the overflow-y CSS property and its available values.
*/
overflowY?: string;
/**
* Setting the scroll based on the y offset
* This is used when its child component is webview
*/
yOffsetChange?: number;
}
export interface CardComponent extends Component, CardProperties {
onDidActionClick: vscode.Event<ActionDescriptor>;
onCardSelectedChanged: vscode.Event<any>;
@@ -1115,11 +1160,22 @@ declare module 'sqlops' {
export function createModelViewEditor(title: string, options?: ModelViewEditorOptions): ModelViewEditor;
export interface ModelViewEditor extends window.modelviewdialog.ModelViewPanel {
/**
* `true` if there are unpersisted changes.
* This is editable to support extensions updating the dirty status.
*/
isDirty: boolean;
/**
* Opens the editor
*/
openEditor(position?: vscode.ViewColumn): Thenable<void>;
/**
* Registers a save handler for this editor. This will be called if [supportsSave](#ModelViewEditorOptions.supportsSave)
* is set to true and the editor is marked as dirty
*/
registerSaveHandler(handler: () => Thenable<boolean>);
}
}
@@ -1128,6 +1184,11 @@ declare module 'sqlops' {
* Should the model view editor's context be kept around even when the editor is no longer visible? It is false by default
*/
readonly retainContextWhenHidden?: boolean;
/**
* Does this model view editor support save?
*/
readonly supportsSave?: boolean;
}
export enum DataProviderType {

View File

@@ -131,6 +131,7 @@ export enum FrequencyRelativeIntervals {
export enum ModelComponentTypes {
NavContainer,
DivContainer,
FlexContainer,
Card,
InputBox,

View File

@@ -90,14 +90,16 @@ export class ExtHostAccountManagement extends ExtHostAccountManagementShape {
}
public $getSecurityToken(account: sqlops.Account): Thenable<{}> {
for (const handle in this._accounts) {
const providerHandle = parseInt(handle);
if (this._accounts[handle].findIndex((acct) => acct.key.accountId === account.key.accountId) !== -1) {
return this._withProvider(providerHandle, (provider: sqlops.AccountProvider) => provider.getSecurityToken(account));
return this.$getAllAccounts().then(() => {
for (const handle in this._accounts) {
const providerHandle = parseInt(handle);
if (this._accounts[handle].findIndex((acct) => acct.key.accountId === account.key.accountId) !== -1) {
return this._withProvider(providerHandle, (provider: sqlops.AccountProvider) => provider.getSecurityToken(account));
}
}
}
throw new Error(`Account ${account.key.accountId} not found.`);
throw new Error(`Account ${account.key.accountId} not found.`);
});
}
public get onDidChangeAccounts(): Event<sqlops.DidChangeAccountsParams> {

View File

@@ -32,14 +32,21 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
navContainer(): sqlops.ContainerBuilder<sqlops.NavContainer, any, any> {
let id = this.getNextComponentId();
let container: ContainerBuilderImpl<sqlops.NavContainer, any, any> = new ContainerBuilderImpl(this._proxy, this._handle, ModelComponentTypes.NavContainer, id);
let container: GenericContainerBuilder<sqlops.NavContainer, any, any> = new GenericContainerBuilder(this._proxy, this._handle, ModelComponentTypes.NavContainer, id);
this._componentBuilders.set(id, container);
return container;
}
divContainer(): sqlops.DivBuilder {
let id = this.getNextComponentId();
let container = new DivContainerBuilder(this._proxy, this._handle, ModelComponentTypes.DivContainer, id);
this._componentBuilders.set(id, container);
return container;
}
flexContainer(): sqlops.FlexBuilder {
let id = this.getNextComponentId();
let container: ContainerBuilderImpl<sqlops.FlexContainer, any, any> = new ContainerBuilderImpl<sqlops.FlexContainer, sqlops.FlexLayout, sqlops.FlexItemLayout>(this._proxy, this._handle, ModelComponentTypes.FlexContainer, id);
let container: GenericContainerBuilder<sqlops.FlexContainer, any, any> = new GenericContainerBuilder<sqlops.FlexContainer, sqlops.FlexLayout, sqlops.FlexItemLayout>(this._proxy, this._handle, ModelComponentTypes.FlexContainer, id);
this._componentBuilders.set(id, container);
return container;
}
@@ -60,7 +67,7 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
groupContainer(): sqlops.GroupBuilder {
let id = this.getNextComponentId();
let container: ContainerBuilderImpl<sqlops.GroupContainer, any, any> = new ContainerBuilderImpl<sqlops.GroupContainer, sqlops.GroupLayout, sqlops.GroupItemLayout>(this._proxy, this._handle, ModelComponentTypes.Group, id);
let container: GenericContainerBuilder<sqlops.GroupContainer, any, any> = new GenericContainerBuilder<sqlops.GroupContainer, sqlops.GroupLayout, sqlops.GroupItemLayout>(this._proxy, this._handle, ModelComponentTypes.Group, id);
this._componentBuilders.set(id, container);
return container;
}
@@ -241,17 +248,9 @@ class ComponentBuilderImpl<T extends sqlops.Component> implements sqlops.Compone
}
}
class GenericComponentBuilder<T extends sqlops.Component> extends ComponentBuilderImpl<T> {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
super(new ComponentWrapper(proxy, handle, type, id));
}
}
class ContainerBuilderImpl<T extends sqlops.Component, TLayout, TItemLayout> extends ComponentBuilderImpl<T> implements sqlops.ContainerBuilder<T, TLayout, TItemLayout> {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
super(new ComponentWrapper(proxy, handle, type, id));
constructor(componentWrapper: ComponentWrapper) {
super(componentWrapper);
}
withLayout(layout: TLayout): sqlops.ContainerBuilder<T, TLayout, TItemLayout> {
@@ -268,7 +267,19 @@ class ContainerBuilderImpl<T extends sqlops.Component, TLayout, TItemLayout> ext
}
}
class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sqlops.FormLayout, sqlops.FormItemLayout> implements sqlops.FormBuilder {
class GenericContainerBuilder<T extends sqlops.Component, TLayout, TItemLayout> extends ContainerBuilderImpl<T, TLayout, TItemLayout> {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
super(new ComponentWrapper(proxy, handle, type, id));
}
}
class DivContainerBuilder extends ContainerBuilderImpl<sqlops.DivContainer, sqlops.DivLayout, sqlops.DivItemLayout> {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
super(new DivContainerWrapper(proxy, handle, type, id));
}
}
class FormContainerBuilder extends GenericContainerBuilder<sqlops.FormContainer, sqlops.FormLayout, sqlops.FormItemLayout> implements sqlops.FormBuilder {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string, private _builder: ModelBuilderImpl) {
super(proxy, handle, type, id);
}
@@ -376,7 +387,7 @@ class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sq
}
}
class ToolbarContainerBuilder extends ContainerBuilderImpl<sqlops.ToolbarContainer, sqlops.ToolbarLayout, any> implements sqlops.ToolbarBuilder {
class ToolbarContainerBuilder extends GenericContainerBuilder<sqlops.ToolbarContainer, sqlops.ToolbarLayout, any> implements sqlops.ToolbarBuilder {
withToolbarItems(components: sqlops.ToolbarComponent[]): sqlops.ContainerBuilder<sqlops.ToolbarContainer, any, any> {
this._component.itemConfigs = components.map(item => {
return this.convertToItemConfig(item);
@@ -844,9 +855,6 @@ class WebViewWrapper extends ComponentWrapper implements sqlops.WebViewComponent
public set options(o: vscode.WebviewOptions) {
this.setProperty('options', o);
}
}
class EditorWrapper extends ComponentWrapper implements sqlops.EditorComponent {
@@ -1162,6 +1170,24 @@ class FileBrowserTreeComponentWrapper extends ComponentWrapper implements sqlops
}
}
class DivContainerWrapper extends ComponentWrapper implements sqlops.DivContainer {
public get overflowY(): string {
return this.properties['overflowY'];
}
public set overflowY(value: string) {
this.setProperty('overflowY', value);
}
public get yOffsetChange(): number {
return this.properties['yOffsetChange'];
}
public set yOffsetChange(value: number) {
this.setProperty('yOffsetChange', value);
}
}
class TreeComponentWrapper<T> extends ComponentWrapper implements sqlops.TreeComponent<T> {
constructor(

View File

@@ -75,6 +75,9 @@ class ModelViewPanelImpl implements sqlops.window.modelviewdialog.ModelViewPanel
}
class ModelViewEditorImpl extends ModelViewPanelImpl implements sqlops.workspace.ModelViewEditor {
private _isDirty: boolean;
private _saveHandler: () => Thenable<boolean>;
constructor(
extHostModelViewDialog: ExtHostModelViewDialog,
extHostModelView: ExtHostModelViewShape,
@@ -84,10 +87,32 @@ class ModelViewEditorImpl extends ModelViewPanelImpl implements sqlops.workspace
private _options: sqlops.ModelViewEditorOptions
) {
super('modelViewEditor', extHostModelViewDialog, extHostModelView, extensionLocation);
this._isDirty = false;
}
public openEditor(position?: vscode.ViewColumn): Thenable<void> {
return this._proxy.$openEditor(this._modelViewId, this._title, this._options, position);
return this._proxy.$openEditor(this.handle, this._modelViewId, this._title, this._options, position);
}
public get isDirty(): boolean {
return this._isDirty;
}
public set isDirty(value: boolean) {
this._isDirty = value;
this._proxy.$setDirty(this.handle, value);
}
registerSaveHandler(handler: () => Thenable<boolean>) {
this._saveHandler = handler;
}
public handleSave(): Thenable<boolean> {
if (this._saveHandler) {
return Promise.resolve(this._saveHandler());
} else {
return Promise.resolve(true);
}
}
}
@@ -470,6 +495,11 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
return dialog.validateClose();
}
public $handleSave(handle: number): Thenable<boolean> {
let editor = this._objectsByHandle.get(handle) as ModelViewEditorImpl;
return editor.handleSave();
}
public openDialog(dialog: sqlops.window.modelviewdialog.Dialog): void {
let handle = this.getHandle(dialog);
this.updateDialogContent(dialog);

View File

@@ -9,7 +9,7 @@ import { ExtHostObjectExplorerShape, SqlMainContext, MainThreadObjectExplorerSha
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
export class ExtHostObjectExplorer implements ExtHostObjectExplorerShape {
export class ExtHostObjectExplorer implements ExtHostObjectExplorerShape {
private _proxy: MainThreadObjectExplorerShape;
@@ -44,7 +44,7 @@ class ExtHostObjectExplorerNode implements sqlops.objectexplorer.ObjectExplorerN
public errorMessage: string;
constructor(nodeInfo: sqlops.NodeInfo, connectionId: string, private _proxy: MainThreadObjectExplorerShape) {
Object.entries(nodeInfo).forEach(([key, value]) => this[key] = value);
this.getDetailsFromInfo(nodeInfo);
this.connectionId = connectionId;
}
@@ -71,4 +71,12 @@ class ExtHostObjectExplorerNode implements sqlops.objectexplorer.ObjectExplorerN
}
return this._proxy.$getNode(this.connectionId, this.nodePath.slice(0, parentPathEndIndex)).then(nodeInfo => nodeInfo ? new ExtHostObjectExplorerNode(nodeInfo, this.connectionId, this._proxy) : undefined);
}
refresh(): Thenable<void> {
return this._proxy.$refresh(this.connectionId, this.nodePath).then(nodeInfo => this.getDetailsFromInfo(nodeInfo));
}
private getDetailsFromInfo(nodeInfo: sqlops.NodeInfo): void {
Object.entries(nodeInfo).forEach(([key, value]) => this[key] = value);
}
}

View File

@@ -6,6 +6,7 @@
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { IEditor } from 'vs/workbench/common/editor';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -14,7 +15,7 @@ import { MainThreadModelViewDialogShape, SqlMainContext, ExtHostModelViewDialogS
import { Dialog, DialogTab, DialogButton, WizardPage, Wizard } from 'sql/platform/dialog/dialogTypes';
import { CustomDialogService } from 'sql/platform/dialog/customDialogService';
import { IModelViewDialogDetails, IModelViewTabDetails, IModelViewButtonDetails, IModelViewWizardPageDetails, IModelViewWizardDetails } from 'sql/workbench/api/common/sqlExtHostTypes';
import { ModelViewInput } from 'sql/parts/modelComponents/modelEditor/modelViewInput';
import { ModelViewInput, ModelViewInputModel, ModeViewSaveHandler } from 'sql/parts/modelComponents/modelEditor/modelViewInput';
import * as vscode from 'vscode';
import * as sqlops from 'sqlops';
@@ -28,6 +29,7 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
private readonly _wizardPages = new Map<number, WizardPage>();
private readonly _wizardPageHandles = new Map<WizardPage, number>();
private readonly _wizards = new Map<number, Wizard>();
private readonly _editorInputModels = new Map<number, ModelViewInputModel>();
private _dialogService: CustomDialogService;
constructor(
@@ -43,15 +45,18 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
throw new Error('Method not implemented.');
}
public $openEditor(modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void> {
public $openEditor(handle: number, modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void> {
return new Promise<void>((resolve, reject) => {
let input = this._instatiationService.createInstance(ModelViewInput, title, modelViewId, options);
let saveHandler: ModeViewSaveHandler = options && options.supportsSave ? (h) => this.handleSave(h) : undefined;
let model = new ModelViewInputModel(modelViewId, handle, saveHandler);
let input = this._instatiationService.createInstance(ModelViewInput, title, model, options);
let editorOptions = {
preserveFocus: true,
pinned: true
};
this._editorService.openEditor(input, editorOptions, position as any).then(() => {
this._editorService.openEditor(input, editorOptions, position as any).then((editor) => {
this._editorInputModels.set(handle, model);
resolve();
}, error => {
reject(error);
@@ -59,6 +64,10 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
});
}
private handleSave(handle: number): Thenable<boolean> {
return this._proxy.$handleSave(handle);
}
public $openDialog(handle: number): Thenable<void> {
let dialog = this.getDialog(handle);
this._dialogService.showDialog(dialog);
@@ -213,6 +222,21 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
return Promise.resolve();
}
$setDirty(handle: number, isDirty: boolean): void {
let model = this.getEditor(handle);
if (model) {
model.setDirty(isDirty);
}
}
private getEditor(handle: number): ModelViewInputModel {
let model = this._editorInputModels.get(handle);
if (!model) {
throw new Error('No editor matching the given handle');
}
return model;
}
private getDialog(handle: number): Dialog {
let dialog = this._dialogs.get(handle);
if (!dialog) {

View File

@@ -15,6 +15,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { TreeItemCollapsibleState } from 'sql/parts/objectExplorer/common/treeNode';
@extHostNamedCustomer(SqlMainContext.MainThreadObjectExplorer)
export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
@@ -50,7 +51,7 @@ export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
public $getActiveConnectionNodes(): Thenable<NodeInfoWithConnection[]> {
let connectionNodes = this._objectExplorerService.getActiveConnectionNodes();
return Promise.resolve(connectionNodes.map(node => {
return {connectionId: node.connection.id, nodeInfo: node.toNodeInfo()};
return { connectionId: node.connection.id, nodeInfo: node.toNodeInfo() };
}));
}
@@ -73,4 +74,8 @@ export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
public $findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable<sqlops.NodeInfo[]> {
return this._objectExplorerService.findNodes(connectionId, type, schema, name, database, parentObjectNames);
}
public $refresh(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo> {
return this._objectExplorerService.refreshNodeInView(connectionId, nodePath).then(node => node.toNodeInfo());
}
}

View File

@@ -36,7 +36,7 @@ export abstract class ExtHostAccountManagementShape {
export abstract class ExtHostConnectionManagementShape {
$onConnectionOpened(handleId: string, connection: sqlops.connection.Connection): void { throw ni; }
}
}
export abstract class ExtHostDataProtocolShape {
@@ -663,6 +663,7 @@ export interface MainThreadObjectExplorerShape extends IDisposable {
$getChildren(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo[]>;
$isExpanded(connectionId: string, nodePath: string): Thenable<boolean>;
$findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable<sqlops.NodeInfo[]>;
$refresh(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo>;
}
export interface ExtHostModelViewDialogShape {
@@ -672,10 +673,11 @@ export interface ExtHostModelViewDialogShape {
$updateWizardPageInfo(handle: number, pageHandles: number[], currentPageIndex: number): void;
$validateNavigation(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean>;
$validateDialogClose(handle: number): Thenable<boolean>;
$handleSave(handle: number): Thenable<boolean>;
}
export interface MainThreadModelViewDialogShape extends IDisposable {
$openEditor(modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void>;
$openEditor(handle: number, modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void>;
$openDialog(handle: number): Thenable<void>;
$closeDialog(handle: number): Thenable<void>;
$setDialogDetails(handle: number, details: IModelViewDialogDetails): Thenable<void>;
@@ -688,6 +690,7 @@ export interface MainThreadModelViewDialogShape extends IDisposable {
$addWizardPage(wizardHandle: number, pageHandle: number, pageIndex: number): Thenable<void>;
$removeWizardPage(wizardHandle: number, pageIndex: number): Thenable<void>;
$setWizardPage(wizardHandle: number, pageIndex: number): Thenable<void>;
$setDirty(handle: number, isDirty: boolean): void;
}
export interface ExtHostQueryEditorShape {
}

View File

@@ -16,8 +16,8 @@ import { ShowCurrentReleaseNotesAction } from 'sql/workbench/update/releaseNotes
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry';
new Actions.BackupAction().registerTask(false);
new Actions.RestoreAction().registerTask(false);
new Actions.BackupAction().registerTask();
new Actions.RestoreAction().registerTask();
new Actions.NewQueryAction().registerTask();
new Actions.ConfigureDashboardAction().registerTask();

View File

@@ -302,6 +302,12 @@ export class BackupAction extends Task {
}
runTask(accessor: ServicesAccessor, profile: IConnectionProfile): TPromise<void> {
if (!profile) {
let objectExplorerService = accessor.get<IObjectExplorerService>(IObjectExplorerService);
let connectionManagementService = accessor.get<IConnectionManagementService>(IConnectionManagementService);
let workbenchEditorService = accessor.get<IEditorService>(IEditorService);
profile = TaskUtilities.getCurrentGlobalConnection(objectExplorerService, connectionManagementService, workbenchEditorService);
}
let configurationService = accessor.get<IWorkspaceConfigurationService>(IWorkspaceConfigurationService);
let previewFeaturesEnabled: boolean = configurationService.getValue('workbench')['enablePreviewFeatures'];
if (!previewFeaturesEnabled) {
@@ -435,7 +441,7 @@ export class NewDatabaseAction extends Action {
export class ConfigureDashboardAction extends Task {
public static readonly ID = 'configureDashboard';
public static readonly LABEL = nls.localize('configureDashboard', 'Configure');
public static readonly LABEL = nls.localize('configureDashboard', 'Learn How To Configure The Dashboard');
public static readonly ICON = 'configure-dashboard';
private static readonly configHelpUri = 'https://aka.ms/sqldashboardconfig';

View File

@@ -301,7 +301,8 @@ suite('SQL Object Explorer Service tests', () => {
reveal: element => Promise.resolve() as Thenable<void>,
setSelected: (element, selected, clearOtherSelections) => undefined,
isExpanded: element => undefined,
onSelectionOrFocusChange: Event.None
onSelectionOrFocusChange: Event.None,
refreshElement: (element) => Promise.resolve() as Thenable<void>
} as ServerTreeView);
});
@@ -738,4 +739,22 @@ suite('SQL Object Explorer Service tests', () => {
}, err => done(err));
});
});
test('refreshInView refreshes the node, expands it, and returns the refreshed node', async () => {
// Set up the session and tree view
await objectExplorerService.createNewSession('MSSQL', connection);
objectExplorerService.onSessionCreated(1, objectExplorerSession);
serverTreeView.setup(x => x.refreshElement(TypeMoq.It.isAny())).returns(() => Promise.resolve());
objectExplorerService.registerServerTreeView(serverTreeView.object);
// Refresh the node
let nodePath = objectExplorerSession.rootNode.nodePath;
let refreshedNode = await objectExplorerService.refreshNodeInView(connection.id, nodePath);
// Verify that it was refreshed, expanded, and the refreshed detailed were returned
sqlOEProvider.verify(x => x.refreshNode(TypeMoq.It.is(refreshNode => refreshNode.nodePath === nodePath)), TypeMoq.Times.once());
refreshedNode.children.forEach((childNode, index) => {
assert.equal(childNode.nodePath, objectExplorerExpandInfoRefresh.nodes[index].nodePath);
});
});
});

View File

@@ -28,6 +28,7 @@ import { ConnectionManagementService } from 'sql/parts/connection/common/connect
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { TestThemeService } from 'sqltest/stubs/themeTestService';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import * as TypeMoq from 'typemoq';
import * as assert from 'assert';
@@ -40,6 +41,7 @@ suite('SQL QueryAction Tests', () => {
let editor: TypeMoq.Mock<QueryEditor>;
let calledRunQueryOnInput: boolean = undefined;
let testQueryInput: TypeMoq.Mock<QueryInput>;
let configurationService: TypeMoq.Mock<ConfigurationService>;
setup(() => {
// Setup a reusable mock QueryInput
@@ -56,6 +58,13 @@ suite('SQL QueryAction Tests', () => {
editor.setup(x => x.getSelection()).returns(() => undefined);
editor.setup(x => x.getSelection(false)).returns(() => undefined);
editor.setup(x => x.isSelectionEmpty()).returns(() => false);
configurationService = TypeMoq.Mock.ofInstance({
getValue: () => undefined,
onDidChangeConfiguration: () => undefined
} as any);
configurationService.setup(x => x.getValue(TypeMoq.It.isAny())).returns(() => {
return {};
});
});
test('setClass sets child CSS class correctly', (done) => {
@@ -463,7 +472,7 @@ suite('SQL QueryAction Tests', () => {
});
// If I query without having initialized anything, state should be clear
listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, undefined, undefined);
listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, undefined, undefined, configurationService.object);
assert.equal(listItem.isEnabled(), false, 'do not expect dropdown enabled unless connected');
assert.equal(listItem.currentDatabaseName, undefined, 'do not expect dropdown to have entries unless connected');
@@ -498,7 +507,7 @@ suite('SQL QueryAction Tests', () => {
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
// ... Create a database dropdown that has been connected
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null);
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
listItem.onConnected();
// If: I raise a connection changed event
@@ -522,7 +531,7 @@ suite('SQL QueryAction Tests', () => {
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
// ... Create a database dropdown that has been connected
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null);
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
listItem.onConnected();
// If: I raise a connection changed event for the 'wrong' URI
@@ -549,7 +558,7 @@ suite('SQL QueryAction Tests', () => {
cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event);
// ... Create a database dropdown
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null);
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
// If: I raise a connection changed event
let eventParams = <IConnectionParams>{

View File

@@ -93,7 +93,7 @@ suite('SQL QueryEditor Tests', () => {
instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((classDef, editor, action) => {
if (classDef.ID) {
if (classDef.ID === 'listDatabaseQueryActionItem') {
return new ListDatabasesActionItem(editor, action, connectionManagementService.object, undefined, undefined, undefined);
return new ListDatabasesActionItem(editor, action, connectionManagementService.object, undefined, undefined, undefined, configurationService.object);
}
}
// Default
@@ -110,18 +110,26 @@ suite('SQL QueryEditor Tests', () => {
editorDescriptorService = TypeMoq.Mock.ofType(EditorDescriptorService, TypeMoq.MockBehavior.Loose);
editorDescriptorService.setup(x => x.getEditor(TypeMoq.It.isAny())).returns(() => descriptor);
configurationService = TypeMoq.Mock.ofInstance({
getValue: () => undefined,
onDidChangeConfiguration: () => undefined
} as any);
configurationService.setup(x => x.getValue(TypeMoq.It.isAny())).returns(() => {
return { enablePreviewFeatures: true };
});
// Create a QueryInput
let filePath = 'someFile.sql';
let uri: URI = URI.parse(filePath);
let fileInput = new UntitledEditorInput(uri, false, '', '', '', instantiationService.object, undefined, undefined, undefined);
let queryResultsInput: QueryResultsInput = new QueryResultsInput(uri.fsPath);
let queryResultsInput: QueryResultsInput = new QueryResultsInput(uri.fsPath, configurationService.object);
queryInput = new QueryInput('first', fileInput, queryResultsInput, undefined, undefined, undefined, undefined, undefined);
// Create a QueryInput to compare to the previous one
let filePath2 = 'someFile2.sql';
let uri2: URI = URI.parse(filePath2);
let fileInput2 = new UntitledEditorInput(uri2, false, '', '', '', instantiationService.object, undefined, undefined, undefined);
let queryResultsInput2: QueryResultsInput = new QueryResultsInput(uri2.fsPath);
let queryResultsInput2: QueryResultsInput = new QueryResultsInput(uri2.fsPath, configurationService.object);
queryInput2 = new QueryInput('second', fileInput2, queryResultsInput2, undefined, undefined, undefined, undefined, undefined);
// Mock IMessageService
@@ -136,14 +144,6 @@ suite('SQL QueryEditor Tests', () => {
// Create a QueryModelService
queryModelService = new QueryModelService(instantiationService.object, notificationService.object);
configurationService = TypeMoq.Mock.ofInstance({
getValue: () => undefined,
onDidChangeConfiguration: () => undefined
} as any);
configurationService.setup(x => x.getValue(TypeMoq.It.isAny())).returns(() => {
return { enablePreviewFeatures: true };
});
});
test('createEditor creates only the taskbar', (done) => {
@@ -344,7 +344,7 @@ suite('SQL QueryEditor Tests', () => {
queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns((definition, editor, action, selectBox) => {
if (definition.ID === 'listDatabaseQueryActionItem') {
let item = new ListDatabasesActionItem(editor, action, queryConnectionService.object, undefined, undefined, undefined);
let item = new ListDatabasesActionItem(editor, action, queryConnectionService.object, undefined, undefined, undefined,configurationService.object);
return item;
}
// Default
@@ -393,7 +393,7 @@ suite('SQL QueryEditor Tests', () => {
done();
});
test('Test that we attempt to dispose query when the queryInput is disposed', (done) => {
let queryResultsInput = new QueryResultsInput('testUri');
let queryResultsInput = new QueryResultsInput('testUri', configurationService.object);
queryInput['_results'] = queryResultsInput;
queryInput.dispose();
queryModelService.verify(x => x.disposeQuery(TypeMoq.It.isAnyString()), TypeMoq.Times.once());

View File

@@ -1233,6 +1233,7 @@ declare namespace Slick {
public scrollRowIntoView(row: number, doPaging: boolean): void;
public scrollRowToTop(row: number): void;
public scrollCellIntoView(row: number, cell: number, doPaging: boolean): void;
public scrollTo(y: number);
public getCanvasNode(): HTMLCanvasElement;
public focus(): void;

View File

@@ -61,7 +61,7 @@ function main() {
'bootstrap': `../${ out }/bootstrap`
},
catchError: true,
// {{SQL CARBON EDIT}}
// {{SQL CARBON EDIT}}
nodeModules: [
'@angular/common',
'@angular/core',
@@ -70,6 +70,7 @@ function main() {
'@angular/platform-browser-dynamic',
'@angular/router',
'angular2-grid',
'ng2-charts/ng2-charts',
'rxjs/add/observable/of',
'rxjs/Observable',
'rxjs/Subject',

View File

@@ -1393,10 +1393,6 @@ deep-extend@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
deep-extend@~0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
deep-is@~0.1.2, deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
@@ -5493,16 +5489,7 @@ raw-body@2.3.2:
iconv-lite "0.4.19"
unpipe "1.0.0"
rc@^1.1.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077"
dependencies:
deep-extend "~0.4.0"
ini "~1.3.0"
minimist "^1.2.0"
strip-json-comments "~2.0.1"
rc@^1.1.6:
rc@1.2.8, rc@^1.1.2, rc@^1.1.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
dependencies:
@@ -6111,9 +6098,9 @@ slice-ansi@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
"slickgrid@github:anthonydresser/SlickGrid#2.3.25":
version "2.3.25"
resolved "https://codeload.github.com/anthonydresser/SlickGrid/tar.gz/5fa498a3df7ed671958bedeac1863c41352c7825"
"slickgrid@github:anthonydresser/SlickGrid#2.3.27":
version "2.3.27"
resolved "https://codeload.github.com/anthonydresser/SlickGrid/tar.gz/712a59307942c8fdb18708b6d1a501880a74db8d"
dependencies:
jquery ">=1.8.0"
jquery-ui ">=1.8.0"