mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-17 11:03:14 -04:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc09fb30d8 | ||
|
|
c13f219318 | ||
|
|
6b018c5d06 | ||
|
|
e69158d9b2 | ||
|
|
520cfb780a | ||
|
|
e686fed209 | ||
|
|
80ab19ac23 | ||
|
|
25228fa58e | ||
|
|
676d35090f | ||
|
|
e1a36a356c | ||
|
|
3274c0b734 | ||
|
|
9c95e1289f | ||
|
|
ef29871b62 | ||
|
|
a2a87f8d2b | ||
|
|
83c01c6bcb | ||
|
|
c1d850804c | ||
|
|
6590d5f58a | ||
|
|
a225925bc4 | ||
|
|
ab39f1f44f | ||
|
|
8d89364d72 | ||
|
|
af2bc859d1 | ||
|
|
8e72fdaa52 | ||
|
|
e9661f90d0 | ||
|
|
30b111034d | ||
|
|
df18359309 | ||
|
|
03857e0afd | ||
|
|
eaf1e08752 | ||
|
|
d39ceffa94 | ||
|
|
406b171c66 | ||
|
|
6d89b9e203 | ||
|
|
733bb69d25 | ||
|
|
4609694141 | ||
|
|
3be0c5130a | ||
|
|
e50b512580 | ||
|
|
eb62d054de | ||
|
|
e870a309c0 | ||
|
|
3afd3b0ff3 | ||
|
|
e3a2ed95d4 | ||
|
|
20c4f085c8 | ||
|
|
02af7e9299 | ||
|
|
0ae9b36d93 | ||
|
|
2bbb2842e5 |
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,5 +1,18 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## Version 0.30.6
|
||||||
|
* Release date: June 20, 2018
|
||||||
|
* Release status: Public Preview
|
||||||
|
|
||||||
|
## What's new in this version
|
||||||
|
* **SQL Server Profiler for SQL Operations Studio *Preview*** extension initial release
|
||||||
|
* The new **SQL Data Warehouse** extension includes rich customizable dashboard widgets surfacing insights to your data warehouse. This unlocks key scenarios around managing and tuning your data warehouse to ensure it is optimized for consistent performance.
|
||||||
|
* **Edit Data "Filtering and Sorting"** support
|
||||||
|
* **SQL Server Agent for SQL Operations Studio *Preview*** extension enhancements for Jobs and Job History views
|
||||||
|
* Improved **Wizard & Dialog UI Builder Framework** extensibility APIs
|
||||||
|
* Update VS Code Platform source code integrating [March 2018 (1.22)](https://code.visualstudio.com/updates/v1_22) and [April 2018 (1.23)](https://code.visualstudio.com/updates/v1_23) releases
|
||||||
|
* Fix GitHub Issues
|
||||||
|
|
||||||
## Version 0.29.3
|
## Version 0.29.3
|
||||||
* Release date: May 7, 2018
|
* Release date: May 7, 2018
|
||||||
* Release status: Public Preview
|
* Release status: Public Preview
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -4,16 +4,16 @@
|
|||||||
|
|
||||||
SQL Operations Studio is a data management tool that enables you to work with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
|
SQL Operations Studio is a data management tool that enables you to work with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
|
||||||
|
|
||||||
**Download SQL Operations Studio May Public Preview**
|
**Download SQL Operations Studio June Public Preview**
|
||||||
|
|
||||||
Platform | Link
|
Platform | Link
|
||||||
-- | --
|
-- | --
|
||||||
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=873386
|
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=875602
|
||||||
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=873387
|
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=875603
|
||||||
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=873388
|
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=875604
|
||||||
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=873389
|
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=875605
|
||||||
Linux RPM | https://go.microsoft.com/fwlink/?linkid=873390
|
Linux RPM | https://go.microsoft.com/fwlink/?linkid=875606
|
||||||
Linux DEB | https://go.microsoft.com/fwlink/?linkid=873391
|
Linux DEB | https://go.microsoft.com/fwlink/?linkid=875607
|
||||||
|
|
||||||
Go to our [download page](https://aka.ms/sqlopsstudio) for more specific instructions.
|
Go to our [download page](https://aka.ms/sqlopsstudio) for more specific instructions.
|
||||||
|
|
||||||
@@ -61,6 +61,7 @@ The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.micro
|
|||||||
## Contributions and "thank you"
|
## Contributions and "thank you"
|
||||||
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
|
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
|
||||||
|
|
||||||
|
* lanceklinger `Fix for double clicking column handle in results table #1504`
|
||||||
* westerncj for `Removed duplicate contribution from README.md (#753)`
|
* westerncj for `Removed duplicate contribution from README.md (#753)`
|
||||||
* ntovas for `Fix for duplicate extensions shown in "Save File" dialog. (#779)`
|
* ntovas for `Fix for duplicate extensions shown in "Save File" dialog. (#779)`
|
||||||
* SebastianPfliegel for `Add cursor snippet (#475)`
|
* SebastianPfliegel for `Add cursor snippet (#475)`
|
||||||
|
|||||||
@@ -658,7 +658,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.7",
|
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.9",
|
||||||
"opener": "^1.4.3",
|
"opener": "^1.4.3",
|
||||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
|
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
|
||||||
"vscode-extension-telemetry": "^0.0.15"
|
"vscode-extension-telemetry": "^0.0.15"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||||
"version": "1.4.0-alpha.35",
|
"version": "1.4.0-alpha.46",
|
||||||
"downloadFileNames": {
|
"downloadFileNames": {
|
||||||
"Windows_86": "win-x86-netcoreapp2.1.zip",
|
"Windows_86": "win-x86-netcoreapp2.1.zip",
|
||||||
"Windows_64": "win-x64-netcoreapp2.1.zip",
|
"Windows_64": "win-x64-netcoreapp2.1.zip",
|
||||||
|
|||||||
@@ -5,9 +5,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { NotificationType, RequestType } from 'vscode-languageclient';
|
import { NotificationType, RequestType } from 'vscode-languageclient';
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
|
|
||||||
import { ITelemetryEventProperties, ITelemetryEventMeasures } from './telemetry';
|
import { ITelemetryEventProperties, ITelemetryEventMeasures } from './telemetry';
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
|
||||||
// ------------------------------- < Telemetry Sent Event > ------------------------------------
|
// ------------------------------- < Telemetry Sent Event > ------------------------------------
|
||||||
|
|
||||||
@@ -31,50 +30,207 @@ export class TelemetryParams {
|
|||||||
|
|
||||||
// ------------------------------- </ Telemetry Sent Event > ----------------------------------
|
// ------------------------------- </ Telemetry Sent Event > ----------------------------------
|
||||||
|
|
||||||
|
// ------------------------------- < Agent Management > ------------------------------------
|
||||||
// Job Management types
|
// Job management parameters
|
||||||
export interface AgentJobsParams {
|
export interface AgentJobsParams {
|
||||||
ownerUri: string;
|
ownerUri: string;
|
||||||
jobId: string;
|
jobId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentJobsResult {
|
|
||||||
succeeded: boolean;
|
|
||||||
errorMessage: string;
|
|
||||||
jobs: sqlops.AgentJobInfo[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AgentJobHistoryParams {
|
export interface AgentJobHistoryParams {
|
||||||
ownerUri: string;
|
ownerUri: string;
|
||||||
jobId: string;
|
jobId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentJobHistoryResult {
|
|
||||||
succeeded: boolean;
|
|
||||||
errorMessage: string;
|
|
||||||
jobs: sqlops.AgentJobHistoryInfo[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AgentJobActionParams {
|
export interface AgentJobActionParams {
|
||||||
ownerUri: string;
|
ownerUri: string;
|
||||||
jobName: string;
|
jobName: string;
|
||||||
action: string;
|
action: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentJobActionResult {
|
export interface CreateAgentJobParams {
|
||||||
succeeded: boolean;
|
ownerUri: string;
|
||||||
errorMessage: string;
|
job: sqlops.AgentJobInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UpdateAgentJobParams {
|
||||||
|
ownerUri: string;
|
||||||
|
originalJobName: string;
|
||||||
|
job: sqlops.AgentJobInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteAgentJobParams {
|
||||||
|
ownerUri: string;
|
||||||
|
job: sqlops.AgentJobInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Job Step management parameters
|
||||||
|
export interface CreateAgentJobStepParams {
|
||||||
|
ownerUri: string;
|
||||||
|
step: sqlops.AgentJobStepInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAgentJobStepParams {
|
||||||
|
ownerUri: string;
|
||||||
|
originalJobStepName: string;
|
||||||
|
step: sqlops.AgentJobStepInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteAgentJobStepParams {
|
||||||
|
ownerUri: string;
|
||||||
|
step: sqlops.AgentJobStepInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alert management parameters
|
||||||
|
export interface AgentAlertsParams {
|
||||||
|
ownerUri: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateAgentAlertParams {
|
||||||
|
ownerUri: string;
|
||||||
|
alert: sqlops.AgentAlertInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAgentAlertParams {
|
||||||
|
ownerUri: string;
|
||||||
|
originalAlertName: string;
|
||||||
|
alert: sqlops.AgentAlertInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteAgentAlertParams {
|
||||||
|
ownerUri: string;
|
||||||
|
alert: sqlops.AgentAlertInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operator management parameters
|
||||||
|
export interface AgentOperatorsParams {
|
||||||
|
ownerUri: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateAgentOperatorParams {
|
||||||
|
ownerUri: string;
|
||||||
|
operator: sqlops.AgentOperatorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAgentOperatorParams {
|
||||||
|
ownerUri: string;
|
||||||
|
originalOperatorName: string;
|
||||||
|
operator: sqlops.AgentOperatorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteAgentOperatorParams {
|
||||||
|
ownerUri: string;
|
||||||
|
operator: sqlops.AgentOperatorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proxy management parameters
|
||||||
|
export interface AgentProxiesParams {
|
||||||
|
ownerUri: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateAgentProxyParams {
|
||||||
|
ownerUri: string;
|
||||||
|
proxy: sqlops.AgentProxyInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAgentProxyParams {
|
||||||
|
ownerUri: string;
|
||||||
|
originalProxyName: string;
|
||||||
|
proxy: sqlops.AgentProxyInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteAgentProxyParams {
|
||||||
|
ownerUri: string;
|
||||||
|
proxy: sqlops.AgentProxyInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agent Job management requests
|
||||||
export namespace AgentJobsRequest {
|
export namespace AgentJobsRequest {
|
||||||
export const type = new RequestType<AgentJobsParams, AgentJobsResult, void, void>('agent/jobs');
|
export const type = new RequestType<AgentJobsParams, sqlops.AgentJobsResult, void, void>('agent/jobs');
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace AgentJobHistoryRequest {
|
export namespace AgentJobHistoryRequest {
|
||||||
export const type = new RequestType<AgentJobHistoryParams, AgentJobHistoryResult, void, void>('agent/jobhistory');
|
export const type = new RequestType<AgentJobHistoryParams, sqlops.AgentJobHistoryResult, void, void>('agent/jobhistory');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export namespace AgentJobActionRequest {
|
export namespace AgentJobActionRequest {
|
||||||
export const type = new RequestType<AgentJobActionParams, AgentJobActionResult, void, void>('agent/jobaction');
|
export const type = new RequestType<AgentJobActionParams, sqlops.ResultStatus, void, void>('agent/jobaction');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export namespace CreateAgentJobRequest {
|
||||||
|
export const type = new RequestType<CreateAgentJobParams, sqlops.CreateAgentJobResult, void, void>('agent/createjob');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace UpdateAgentJobRequest {
|
||||||
|
export const type = new RequestType<UpdateAgentJobParams, sqlops.UpdateAgentJobResult, void, void>('agent/updatejob');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace DeleteAgentJobRequest {
|
||||||
|
export const type = new RequestType<DeleteAgentJobParams, sqlops.ResultStatus, void, void>('agent/deletejob');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Job Step requests
|
||||||
|
export namespace CreateAgentJobStepRequest {
|
||||||
|
export const type = new RequestType<CreateAgentJobStepParams, sqlops.CreateAgentJobStepResult, void, void>('agent/createjobstep');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace UpdateAgentJobStepRequest {
|
||||||
|
export const type = new RequestType<UpdateAgentJobStepParams, sqlops.UpdateAgentJobStepResult, void, void>('agent/updatejobstep');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace DeleteAgentJobStepRequest {
|
||||||
|
export const type = new RequestType<DeleteAgentJobStepParams, sqlops.ResultStatus, void, void>('agent/deletejobstep');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alerts requests
|
||||||
|
export namespace AgentAlertsRequest {
|
||||||
|
export const type = new RequestType<CreateAgentAlertParams, sqlops.AgentAlertsResult, void, void>('agent/alerts');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace CreateAgentAlertRequest {
|
||||||
|
export const type = new RequestType<CreateAgentAlertParams, sqlops.CreateAgentAlertResult, void, void>('agent/createalert');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace UpdateAgentAlertRequest {
|
||||||
|
export const type = new RequestType<UpdateAgentAlertParams, sqlops.UpdateAgentAlertResult, void, void>('agent/updatealert');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace DeleteAgentAlertRequest {
|
||||||
|
export const type = new RequestType<DeleteAgentAlertParams, sqlops.ResultStatus, void, void>('agent/deletealert');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operators requests
|
||||||
|
export namespace AgentOperatorsRequest {
|
||||||
|
export const type = new RequestType<CreateAgentOperatorParams, sqlops.AgentOperatorsResult, void, void>('agent/operators');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace CreateAgentOperatorRequest {
|
||||||
|
export const type = new RequestType<CreateAgentOperatorParams, sqlops.CreateAgentOperatorResult, void, void>('agent/createoperator');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace UpdateAgentOperatorRequest {
|
||||||
|
export const type = new RequestType<UpdateAgentOperatorParams, sqlops.UpdateAgentOperatorResult, void, void>('agent/updateoperator');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace DeleteAgentOperatorRequest {
|
||||||
|
export const type = new RequestType<DeleteAgentOperatorParams, sqlops.ResultStatus, void, void>('agent/deleteoperator');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proxies requests
|
||||||
|
export namespace AgentProxiesRequest {
|
||||||
|
export const type = new RequestType<CreateAgentProxyParams, sqlops.AgentProxiesResult, void, void>('agent/proxies');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace CreateAgentProxyRequest {
|
||||||
|
export const type = new RequestType<CreateAgentProxyParams, sqlops.CreateAgentProxyResult, void, void>('agent/createproxy');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace UpdateAgentProxyRequest {
|
||||||
|
export const type = new RequestType<UpdateAgentProxyParams, sqlops.UpdateAgentProxyResult, void, void>('agent/updateproxy');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace DeleteAgentProxyRequest {
|
||||||
|
export const type = new RequestType<DeleteAgentProxyParams, sqlops.ResultStatus, void, void>('agent/deleteproxy');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------- < Agent Management > ------------------------------------
|
||||||
|
|||||||
@@ -6,13 +6,12 @@
|
|||||||
|
|
||||||
import { SqlOpsDataClient, SqlOpsFeature } from 'dataprotocol-client';
|
import { SqlOpsDataClient, SqlOpsFeature } from 'dataprotocol-client';
|
||||||
import { ClientCapabilities, StaticFeature, RPCMessageType, ServerCapabilities } from 'vscode-languageclient';
|
import { ClientCapabilities, StaticFeature, RPCMessageType, ServerCapabilities } from 'vscode-languageclient';
|
||||||
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
|
|
||||||
import { Disposable } from 'vscode';
|
import { Disposable } from 'vscode';
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
|
|
||||||
import { Telemetry } from './telemetry';
|
import { Telemetry } from './telemetry';
|
||||||
|
import * as contracts from './contracts';
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
import * as Utils from './utils';
|
import * as Utils from './utils';
|
||||||
import { TelemetryNotification, AgentJobsRequest, AgentJobActionRequest, AgentJobHistoryRequest, AgentJobsParams, AgentJobHistoryParams, AgentJobActionParams } from './contracts';
|
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
|
||||||
|
|
||||||
export class TelemetryFeature implements StaticFeature {
|
export class TelemetryFeature implements StaticFeature {
|
||||||
|
|
||||||
@@ -23,7 +22,7 @@ export class TelemetryFeature implements StaticFeature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialize(): void {
|
initialize(): void {
|
||||||
this._client.onNotification(TelemetryNotification.type, e => {
|
this._client.onNotification(contracts.TelemetryNotification.type, e => {
|
||||||
Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures);
|
Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -31,9 +30,9 @@ export class TelemetryFeature implements StaticFeature {
|
|||||||
|
|
||||||
export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
||||||
private static readonly messagesTypes: RPCMessageType[] = [
|
private static readonly messagesTypes: RPCMessageType[] = [
|
||||||
AgentJobsRequest.type,
|
contracts.AgentJobsRequest.type,
|
||||||
AgentJobHistoryRequest.type,
|
contracts.AgentJobHistoryRequest.type,
|
||||||
AgentJobActionRequest.type
|
contracts.AgentJobActionRequest.type
|
||||||
];
|
];
|
||||||
|
|
||||||
constructor(client: SqlOpsDataClient) {
|
constructor(client: SqlOpsDataClient) {
|
||||||
@@ -55,35 +54,312 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
protected registerProvider(options: undefined): Disposable {
|
protected registerProvider(options: undefined): Disposable {
|
||||||
const client = this._client;
|
const client = this._client;
|
||||||
|
|
||||||
|
// Job management methods
|
||||||
let getJobs = (ownerUri: string): Thenable<sqlops.AgentJobsResult> => {
|
let getJobs = (ownerUri: string): Thenable<sqlops.AgentJobsResult> => {
|
||||||
let params: AgentJobsParams = { ownerUri: ownerUri, jobId: null };
|
let params: contracts.AgentJobsParams = { ownerUri: ownerUri, jobId: null };
|
||||||
return client.sendRequest(AgentJobsRequest.type, params).then(
|
return client.sendRequest(contracts.AgentJobsRequest.type, params).then(
|
||||||
r => r,
|
r => r,
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(AgentJobsRequest.type, e);
|
client.logFailedRequest(contracts.AgentJobsRequest.type, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let getJobHistory = (connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> => {
|
let getJobHistory = (ownerUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> => {
|
||||||
let params: AgentJobHistoryParams = { ownerUri: connectionUri, jobId: jobID };
|
let params: contracts.AgentJobHistoryParams = { ownerUri: ownerUri, jobId: jobID };
|
||||||
|
|
||||||
return client.sendRequest(AgentJobHistoryRequest.type, params).then(
|
return client.sendRequest(contracts.AgentJobHistoryRequest.type, params).then(
|
||||||
r => r,
|
r => r,
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(AgentJobHistoryRequest.type, e);
|
client.logFailedRequest(contracts.AgentJobHistoryRequest.type, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let jobAction = (connectionUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult> => {
|
let jobAction = (ownerUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> => {
|
||||||
let params: AgentJobActionParams = { ownerUri: connectionUri, jobName: jobName, action: action };
|
let params: contracts.AgentJobActionParams = { ownerUri: ownerUri, jobName: jobName, action: action };
|
||||||
return client.sendRequest(AgentJobActionRequest.type, params).then(
|
return client.sendRequest(contracts.AgentJobActionRequest.type, params).then(
|
||||||
r => r,
|
r => r,
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(AgentJobActionRequest.type, e);
|
client.logFailedRequest(contracts.AgentJobActionRequest.type, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let createJob = (ownerUri: string, jobInfo: sqlops.AgentJobInfo): Thenable<sqlops.CreateAgentJobResult> => {
|
||||||
|
let params: contracts.CreateAgentJobParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
job: jobInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.CreateAgentJobRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateJob = (ownerUri: string, originalJobName: string, jobInfo: sqlops.AgentJobInfo): Thenable<sqlops.UpdateAgentJobResult> => {
|
||||||
|
let params: contracts.UpdateAgentJobParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
originalJobName: originalJobName,
|
||||||
|
job: jobInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.UpdateAgentJobRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let deleteJob = (ownerUri: string, jobInfo: sqlops.AgentJobInfo): Thenable<sqlops.ResultStatus> => {
|
||||||
|
let params: contracts.DeleteAgentJobParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
job: jobInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.DeleteAgentJobRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Job Step management methods
|
||||||
|
let createJobStep = (ownerUri: string, stepInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.CreateAgentJobStepResult> => {
|
||||||
|
let params: contracts.CreateAgentJobStepParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
step: stepInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.CreateAgentJobStepRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateJobStep = (ownerUri: string, originalJobStepName: string, stepInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.UpdateAgentJobStepResult> => {
|
||||||
|
let params: contracts.UpdateAgentJobStepParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
originalJobStepName: originalJobStepName,
|
||||||
|
step: stepInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.UpdateAgentJobStepRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let deleteJobStep = (ownerUri: string, stepInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.ResultStatus> => {
|
||||||
|
let params: contracts.DeleteAgentJobStepParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
step: stepInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.DeleteAgentJobStepRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Alert management methods
|
||||||
|
let getAlerts = (ownerUri: string): Thenable<sqlops.AgentAlertsResult> => {
|
||||||
|
let params: contracts.AgentAlertsParams = {
|
||||||
|
ownerUri: ownerUri
|
||||||
|
};
|
||||||
|
let requestType = contracts.AgentAlertsRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let createAlert = (ownerUri: string, alertInfo: sqlops.AgentAlertInfo): Thenable<sqlops.CreateAgentAlertResult> => {
|
||||||
|
let params: contracts.CreateAgentAlertParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
alert: alertInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.CreateAgentAlertRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateAlert = (ownerUri: string, originalAlertName: string, alertInfo: sqlops.AgentAlertInfo): Thenable<sqlops.UpdateAgentAlertResult> => {
|
||||||
|
let params: contracts.UpdateAgentAlertParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
originalAlertName: originalAlertName,
|
||||||
|
alert: alertInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.UpdateAgentAlertRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let deleteAlert = (ownerUri: string, alertInfo: sqlops.AgentAlertInfo): Thenable<sqlops.ResultStatus> => {
|
||||||
|
let params: contracts.DeleteAgentAlertParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
alert: alertInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.DeleteAgentAlertRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Operator management methods
|
||||||
|
let getOperators = (ownerUri: string): Thenable<sqlops.AgentOperatorsResult> => {
|
||||||
|
let params: contracts.AgentOperatorsParams = {
|
||||||
|
ownerUri: ownerUri
|
||||||
|
};
|
||||||
|
let requestType = contracts.AgentOperatorsRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let createOperator = (ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo): Thenable<sqlops.CreateAgentOperatorResult> => {
|
||||||
|
let params: contracts.CreateAgentOperatorParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
operator: operatorInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.CreateAgentOperatorRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateOperator = (ownerUri: string, originalOperatorName: string, operatorInfo: sqlops.AgentOperatorInfo): Thenable<sqlops.UpdateAgentOperatorResult> => {
|
||||||
|
let params: contracts.UpdateAgentOperatorParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
originalOperatorName: originalOperatorName,
|
||||||
|
operator: operatorInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.UpdateAgentOperatorRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let deleteOperator = (ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo): Thenable<sqlops.ResultStatus> => {
|
||||||
|
let params: contracts.DeleteAgentOperatorParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
operator: operatorInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.DeleteAgentOperatorRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Proxy management methods
|
||||||
|
let getProxies = (ownerUri: string): Thenable<sqlops.AgentProxiesResult> => {
|
||||||
|
let params: contracts.AgentProxiesParams = {
|
||||||
|
ownerUri: ownerUri
|
||||||
|
};
|
||||||
|
let requestType = contracts.AgentProxiesRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let createProxy = (ownerUri: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.CreateAgentOperatorResult> => {
|
||||||
|
let params: contracts.CreateAgentProxyParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
proxy: proxyInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.CreateAgentProxyRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateProxy = (ownerUri: string, originalProxyName: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.UpdateAgentOperatorResult> => {
|
||||||
|
let params: contracts.UpdateAgentProxyParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
originalProxyName: originalProxyName,
|
||||||
|
proxy: proxyInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.UpdateAgentProxyRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let deleteProxy = (ownerUri: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.ResultStatus> => {
|
||||||
|
let params: contracts.DeleteAgentProxyParams = {
|
||||||
|
ownerUri: ownerUri,
|
||||||
|
proxy: proxyInfo
|
||||||
|
};
|
||||||
|
let requestType = contracts.DeleteAgentProxyRequest.type;
|
||||||
|
return client.sendRequest(requestType, params).then(
|
||||||
|
r => r,
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -93,7 +369,25 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
providerId: client.providerId,
|
providerId: client.providerId,
|
||||||
getJobs,
|
getJobs,
|
||||||
getJobHistory,
|
getJobHistory,
|
||||||
jobAction
|
jobAction,
|
||||||
|
createJob,
|
||||||
|
updateJob,
|
||||||
|
deleteJob,
|
||||||
|
createJobStep,
|
||||||
|
updateJobStep,
|
||||||
|
deleteJobStep,
|
||||||
|
getAlerts,
|
||||||
|
createAlert,
|
||||||
|
updateAlert,
|
||||||
|
deleteAlert,
|
||||||
|
getOperators,
|
||||||
|
createOperator,
|
||||||
|
updateOperator,
|
||||||
|
deleteOperator,
|
||||||
|
getProxies,
|
||||||
|
createProxy,
|
||||||
|
updateProxy,
|
||||||
|
deleteProxy
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,13 @@ Common SQL Profiler use-cases taken from https://docs.microsoft.com/en-us/sql/to
|
|||||||
- Monitoring the performance of SQL Server to tune workloads.
|
- Monitoring the performance of SQL Server to tune workloads.
|
||||||
- Correlating performance counters to diagnose problems.
|
- Correlating performance counters to diagnose problems.
|
||||||
|
|
||||||
|
## SQL Server Profiler 0.1.1 Release
|
||||||
|
The SQL Server Profiler for SQL Operations Studio *Preview* extension is now available. This is the initial preview release for a new lightweight XEvent-based profiler. The SQL Server Profiler extension tries to make it simple to quickly trace server activity for troubleshooting and monitoring.
|
||||||
|
|
||||||
|
We'll continue to enhance this extension over the next couple releases. Take a look at the below screenshot to see what's currently available.
|
||||||
|
|
||||||
|
<img width="850" src="https://user-images.githubusercontent.com/599935/41578613-fa10e8bc-7347-11e8-8b97-9fb7d186c9f6.png">
|
||||||
|
|
||||||
## Code of Conduct
|
## Code of Conduct
|
||||||
|
|
||||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "profiler",
|
"name": "profiler",
|
||||||
"displayName": "SQL Server Profiler",
|
"displayName": "SQL Server Profiler",
|
||||||
"description": "SQL Server Profiler for SQL Operations Studio",
|
"description": "SQL Server Profiler for SQL Operations Studio",
|
||||||
"version": "0.30.0",
|
"version": "0.1.1",
|
||||||
"publisher": "Microsoft",
|
"publisher": "Microsoft",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sqlops",
|
"name": "sqlops",
|
||||||
"version": "0.30.4",
|
"version": "0.30.6",
|
||||||
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Microsoft Corporation"
|
"name": "Microsoft Corporation"
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"reflect-metadata": "^0.1.8",
|
"reflect-metadata": "^0.1.8",
|
||||||
"rxjs": "5.4.0",
|
"rxjs": "5.4.0",
|
||||||
"semver": "4.3.6",
|
"semver": "4.3.6",
|
||||||
"slickgrid": "github:anthonydresser/SlickGrid#2.3.17",
|
"slickgrid": "github:anthonydresser/SlickGrid#2.3.20",
|
||||||
"spdlog": "0.6.0",
|
"spdlog": "0.6.0",
|
||||||
"sudo-prompt": "^8.0.0",
|
"sudo-prompt": "^8.0.0",
|
||||||
"svg.js": "^2.2.5",
|
"svg.js": "^2.2.5",
|
||||||
|
|||||||
@@ -27,14 +27,16 @@
|
|||||||
"reportIssueUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=customer%20reported%20issue",
|
"reportIssueUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=customer%20reported%20issue",
|
||||||
"requestFeatureUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=feature-request"
|
"requestFeatureUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=feature-request"
|
||||||
},
|
},
|
||||||
"releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=862039",
|
"gettingStartedUrl": "https://go.microsoft.com/fwlink/?linkid=862039",
|
||||||
|
"releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=875578",
|
||||||
"documentationUrl": "https://go.microsoft.com/fwlink/?linkid=862277",
|
"documentationUrl": "https://go.microsoft.com/fwlink/?linkid=862277",
|
||||||
"commit": "9ca6200018fc206d67a47229f991901a8a453781",
|
"commit": "9ca6200018fc206d67a47229f991901a8a453781",
|
||||||
"date": "2017-12-15T12:00:00.000Z",
|
"date": "2017-12-15T12:00:00.000Z",
|
||||||
"recommendedExtensions": [
|
"recommendedExtensions": [
|
||||||
"Microsoft.agent",
|
"Microsoft.agent",
|
||||||
"Microsoft.whoisactive",
|
"Microsoft.profiler",
|
||||||
"Microsoft.server-report",
|
"Microsoft.server-report",
|
||||||
|
"Microsoft.whoisactive",
|
||||||
"Redgate.sql-search"
|
"Redgate.sql-search"
|
||||||
],
|
],
|
||||||
"extensionsGallery": {
|
"extensionsGallery": {
|
||||||
|
|||||||
@@ -82,7 +82,8 @@ export default class MainController implements vscode.Disposable {
|
|||||||
tab1.registerContent(async (view) => {
|
tab1.registerContent(async (view) => {
|
||||||
let inputBox = view.modelBuilder.inputBox()
|
let inputBox = view.modelBuilder.inputBox()
|
||||||
.withProperties({
|
.withProperties({
|
||||||
//width: 300
|
multiline: true,
|
||||||
|
height: 100
|
||||||
}).component();
|
}).component();
|
||||||
let inputBoxWrapper = view.modelBuilder.loadingComponent().withItem(inputBox).component();
|
let inputBoxWrapper = view.modelBuilder.loadingComponent().withItem(inputBox).component();
|
||||||
inputBoxWrapper.loading = false;
|
inputBoxWrapper.loading = false;
|
||||||
@@ -171,12 +172,67 @@ export default class MainController implements vscode.Disposable {
|
|||||||
inputBox.value = radioButton.value;
|
inputBox.value = radioButton.value;
|
||||||
groupModel1.enabled = false;
|
groupModel1.enabled = false;
|
||||||
});
|
});
|
||||||
|
let table = view.modelBuilder.table().withProperties({
|
||||||
|
data: [
|
||||||
|
['1', '2', '2'],
|
||||||
|
['4', '5', '6'],
|
||||||
|
['7', '8', '9']
|
||||||
|
], columns: ['c1', 'c2', 'c3'],
|
||||||
|
height: 250,
|
||||||
|
selectedRows: [0]
|
||||||
|
}).component();
|
||||||
|
table.onRowSelected(e => {
|
||||||
|
// TODO:
|
||||||
|
});
|
||||||
|
let listBox = view.modelBuilder.listBox().withProperties({
|
||||||
|
values: ['1', '2', '3'],
|
||||||
|
selectedRow: 2
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
let declarativeTable = view.modelBuilder.declarativeTable()
|
||||||
|
.withProperties({
|
||||||
|
columns: [{
|
||||||
|
displayName: 'Column 1',
|
||||||
|
valueType: sqlops.DeclarativeDataType.string,
|
||||||
|
width: '20px',
|
||||||
|
isReadOnly: true
|
||||||
|
}, {
|
||||||
|
displayName: 'Column 2',
|
||||||
|
valueType: sqlops.DeclarativeDataType.string,
|
||||||
|
width: '100px',
|
||||||
|
isReadOnly: false
|
||||||
|
}, {
|
||||||
|
displayName: 'Column 3',
|
||||||
|
valueType: sqlops.DeclarativeDataType.boolean,
|
||||||
|
width: '20px',
|
||||||
|
isReadOnly: false
|
||||||
|
}, {
|
||||||
|
displayName: 'Column 4',
|
||||||
|
valueType: sqlops.DeclarativeDataType.category,
|
||||||
|
isReadOnly: false,
|
||||||
|
width: '120px',
|
||||||
|
categoryValues: [
|
||||||
|
{ name: 'options1', displayName: 'option 1' },
|
||||||
|
{ name: 'options2', displayName: 'option 2' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
data: [
|
||||||
|
['Data00', 'Data01', false, 'options2'],
|
||||||
|
['Data10', 'Data11', true, 'options1']
|
||||||
|
]
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
declarativeTable.onDataChanged(e => {
|
||||||
|
inputBox2.value = e.row.toString() + ' ' + e.column.toString() + ' ' + e.value.toString();
|
||||||
|
inputBox3.value = declarativeTable.data[e.row][e.column];
|
||||||
|
});
|
||||||
|
|
||||||
let flexRadioButtonsModel = view.modelBuilder.flexContainer()
|
let flexRadioButtonsModel = view.modelBuilder.flexContainer()
|
||||||
.withLayout({
|
.withLayout({
|
||||||
flexFlow: 'column',
|
flexFlow: 'column',
|
||||||
alignItems: 'left',
|
alignItems: 'left',
|
||||||
height: 50
|
height: 150
|
||||||
}).withItems([
|
}).withItems([
|
||||||
radioButton, groupModel1, radioButton2]
|
radioButton, groupModel1, radioButton2]
|
||||||
, { flex: '1 1 50%' }).component();
|
, { flex: '1 1 50%' }).component();
|
||||||
@@ -200,6 +256,15 @@ export default class MainController implements vscode.Disposable {
|
|||||||
}, {
|
}, {
|
||||||
component: flexRadioButtonsModel,
|
component: flexRadioButtonsModel,
|
||||||
title: 'Options'
|
title: 'Options'
|
||||||
|
}, {
|
||||||
|
component: declarativeTable,
|
||||||
|
title: 'Declarative Table'
|
||||||
|
}, {
|
||||||
|
component: table,
|
||||||
|
title: 'Table'
|
||||||
|
}, {
|
||||||
|
component: listBox,
|
||||||
|
title: 'List Box'
|
||||||
}], {
|
}], {
|
||||||
horizontal: false,
|
horizontal: false,
|
||||||
componentWidth: 400
|
componentWidth: 400
|
||||||
@@ -322,7 +387,7 @@ export default class MainController implements vscode.Disposable {
|
|||||||
height: '100%'
|
height: '100%'
|
||||||
});
|
});
|
||||||
|
|
||||||
let templateValues = {url: 'http://whoisactive.com/docs/'};
|
let templateValues = { url: 'http://whoisactive.com/docs/' };
|
||||||
Utils.renderTemplateHtml(path.join(__dirname, '..'), 'templateTab.html', templateValues)
|
Utils.renderTemplateHtml(path.join(__dirname, '..'), 'templateTab.html', templateValues)
|
||||||
.then(html => {
|
.then(html => {
|
||||||
webview.html = html;
|
webview.html = html;
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
|||||||
<ng-template ngFor let-item let-first="first" let-last="last" [ngForOf]="menuItems">
|
<ng-template ngFor let-item let-first="first" let-last="last" [ngForOf]="menuItems">
|
||||||
<span style="padding: 5px; display: flex; align-items: center">
|
<span style="padding: 5px; display: flex; align-items: center">
|
||||||
<span *ngIf="item.icon" class="icon" style="display: inline-block; margin-right: 5px" [ngClass]="item.icon"></span>
|
<span *ngIf="item.icon" class="icon" style="display: inline-block; margin-right: 5px" [ngClass]="item.icon"></span>
|
||||||
<span *ngIf="first" style="font-weight: 200">{{item.label}}</span>
|
<span *ngIf="first">{{item.label}}</span>
|
||||||
<span *ngIf="last" style="">{{item.label}}</span>
|
<span *ngIf="last" style="">{{item.label}}</span>
|
||||||
<a class="router-link" *ngIf="!last && !first" (click)="route(item.routerLink)" style=" font-weight: 200" >{{item.label}}</a>
|
<a class="router-link" *ngIf="!last && !first" (click)="route(item.routerLink)" >{{item.label}}</a>
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="!last" class="icon chevron-right"></span>
|
<span *ngIf="!last" class="icon chevron-right"></span>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|||||||
@@ -33,4 +33,12 @@ export class Button extends vsButton {
|
|||||||
public set title(value: string) {
|
public set title(value: string) {
|
||||||
this.$el.title(value);
|
this.$el.title(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setHeight(value: string) {
|
||||||
|
this.$el.style('height', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setWidth(value: string) {
|
||||||
|
this.$el.style('width', value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ export class Dropdown extends Disposable {
|
|||||||
ariaLabel: this._options.ariaLabel
|
ariaLabel: this._options.ariaLabel
|
||||||
});
|
});
|
||||||
|
|
||||||
this._register(DOM.addDisposableListener(this._input.inputElement, DOM.EventType.FOCUS, () => {
|
this._register(DOM.addDisposableListener(this._input.inputElement, DOM.EventType.CLICK, () => {
|
||||||
this._showList();
|
this._showList();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -145,8 +145,12 @@ export class Dropdown extends Disposable {
|
|||||||
this._register(DOM.addStandardDisposableListener(this._input.inputElement, DOM.EventType.KEY_DOWN, (e: StandardKeyboardEvent) => {
|
this._register(DOM.addStandardDisposableListener(this._input.inputElement, DOM.EventType.KEY_DOWN, (e: StandardKeyboardEvent) => {
|
||||||
switch (e.keyCode) {
|
switch (e.keyCode) {
|
||||||
case KeyCode.Enter:
|
case KeyCode.Enter:
|
||||||
if (this._input.validate()) {
|
if (this._contextView.isVisible()) {
|
||||||
this._onValueChange.fire(this._input.value);
|
if (this._input.validate()) {
|
||||||
|
this._onValueChange.fire(this._input.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._showList();
|
||||||
}
|
}
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
break;
|
break;
|
||||||
@@ -192,6 +196,11 @@ export class Dropdown extends Disposable {
|
|||||||
this._contextView.hide();
|
this._contextView.hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._controller.onDropdownEscape(() => {
|
||||||
|
this._input.focus();
|
||||||
|
this._contextView.hide();
|
||||||
|
});
|
||||||
|
|
||||||
this._input.onDidChange(e => {
|
this._input.onDidChange(e => {
|
||||||
if (this._dataSource.options) {
|
if (this._dataSource.options) {
|
||||||
this._filter.filterString = e;
|
this._filter.filterString = e;
|
||||||
@@ -231,17 +240,19 @@ export class Dropdown extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _layoutTree(): void {
|
private _layoutTree(): void {
|
||||||
let filteredLength = this._dataSource.options.reduce((p, i) => {
|
if (this._dataSource && this._dataSource.options && this._dataSource.options.length > 0) {
|
||||||
if (this._filter.isVisible(undefined, i)) {
|
let filteredLength = this._dataSource.options.reduce((p, i) => {
|
||||||
return p + 1;
|
if (this._filter.isVisible(undefined, i)) {
|
||||||
} else {
|
return p + 1;
|
||||||
return p;
|
} else {
|
||||||
}
|
return p;
|
||||||
}, 0);
|
}
|
||||||
let height = filteredLength * this._renderer.getHeight(undefined, undefined) > this._options.maxHeight ? this._options.maxHeight : filteredLength * this._renderer.getHeight(undefined, undefined);
|
}, 0);
|
||||||
this.$treeContainer.style('height', height + 'px').style('width', DOM.getContentWidth(this.$input.getHTMLElement()) - 2 + 'px');
|
let height = filteredLength * this._renderer.getHeight(undefined, undefined) > this._options.maxHeight ? this._options.maxHeight : filteredLength * this._renderer.getHeight(undefined, undefined);
|
||||||
this._tree.layout(parseInt(this.$treeContainer.style('height')));
|
this.$treeContainer.style('height', height + 'px').style('width', DOM.getContentWidth(this.$input.getHTMLElement()) - 2 + 'px');
|
||||||
this._tree.refresh();
|
this._tree.layout(parseInt(this.$treeContainer.style('height')));
|
||||||
|
this._tree.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public set values(vals: string[]) {
|
public set values(vals: string[]) {
|
||||||
|
|||||||
@@ -101,10 +101,19 @@ export class DropdownController extends TreeDefaults.DefaultController {
|
|||||||
private _onSelectionChange = new Emitter<Resource>();
|
private _onSelectionChange = new Emitter<Resource>();
|
||||||
public readonly onSelectionChange: Event<Resource> = this._onSelectionChange.event;
|
public readonly onSelectionChange: Event<Resource> = this._onSelectionChange.event;
|
||||||
|
|
||||||
|
private _onDropdownEscape = new Emitter<void>();
|
||||||
|
public readonly onDropdownEscape: Event<void> = this._onDropdownEscape.event;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected onEscape(tree: tree.ITree, event: IKeyboardEvent): boolean {
|
||||||
|
let response = super.onEscape(tree, event);
|
||||||
|
this._onDropdownEscape.fire();
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
protected onLeftClick(tree: tree.ITree, element: any, eventish: TreeDefaults.ICancelableEvent, origin: string): boolean {
|
protected onLeftClick(tree: tree.ITree, element: any, eventish: TreeDefaults.ICancelableEvent, origin: string): boolean {
|
||||||
let response = super.onLeftClick(tree, element, eventish, origin);
|
let response = super.onLeftClick(tree, element, eventish, origin);
|
||||||
if (response) {
|
if (response) {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export class InputBox extends AngularDisposable implements OnInit, OnChanges {
|
|||||||
@Input() type: string;
|
@Input() type: string;
|
||||||
@Input() placeholder: string;
|
@Input() placeholder: string;
|
||||||
@Input() ariaLabel: string;
|
@Input() ariaLabel: string;
|
||||||
|
@Input() value: string;
|
||||||
|
|
||||||
@Output() onDidChange = new EventEmitter<string | number>();
|
@Output() onDidChange = new EventEmitter<string | number>();
|
||||||
|
|
||||||
@@ -49,6 +50,9 @@ export class InputBox extends AngularDisposable implements OnInit, OnChanges {
|
|||||||
placeholder: this.placeholder,
|
placeholder: this.placeholder,
|
||||||
ariaLabel: this.ariaLabel
|
ariaLabel: this.ariaLabel
|
||||||
});
|
});
|
||||||
|
if (this.value) {
|
||||||
|
this._inputbox.value = this.value;
|
||||||
|
}
|
||||||
this._inputbox.onDidChange(e => {
|
this._inputbox.onDidChange(e => {
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'number':
|
case 'number':
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export class InputBox extends vsInputBox {
|
|||||||
private _onLoseFocus = this._register(new Emitter<OnLoseFocusParams>());
|
private _onLoseFocus = this._register(new Emitter<OnLoseFocusParams>());
|
||||||
public onLoseFocus: Event<OnLoseFocusParams> = this._onLoseFocus.event;
|
public onLoseFocus: Event<OnLoseFocusParams> = this._onLoseFocus.event;
|
||||||
|
|
||||||
|
private _isTextAreaInput: boolean;
|
||||||
|
|
||||||
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, options?: IInputOptions) {
|
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, options?: IInputOptions) {
|
||||||
super(container, contextViewProvider, options);
|
super(container, contextViewProvider, options);
|
||||||
@@ -48,6 +49,10 @@ export class InputBox extends vsInputBox {
|
|||||||
self._onLoseFocus.fire({ value: self.value, hasChanged: self._lastLoseFocusValue !== self.value });
|
self._onLoseFocus.fire({ value: self.value, hasChanged: self._lastLoseFocusValue !== self.value });
|
||||||
self._lastLoseFocusValue = self.value;
|
self._lastLoseFocusValue = self.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (options && options.type === 'textarea') {
|
||||||
|
this._isTextAreaInput = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public style(styles: IInputBoxStyles): void {
|
public style(styles: IInputBoxStyles): void {
|
||||||
@@ -67,6 +72,20 @@ export class InputBox extends vsInputBox {
|
|||||||
this.applyStyles();
|
this.applyStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public set rows(value: number) {
|
||||||
|
this.inputElement.setAttribute('rows', value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public set columns(value: number) {
|
||||||
|
this.inputElement.setAttribute('cols', value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public layout(): void {
|
||||||
|
if (!this._isTextAreaInput) {
|
||||||
|
super.layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public disable(): void {
|
public disable(): void {
|
||||||
super.disable();
|
super.disable();
|
||||||
this.inputBackground = this.disabledInputBackground;
|
this.inputBackground = this.disabledInputBackground;
|
||||||
@@ -75,6 +94,12 @@ export class InputBox extends vsInputBox {
|
|||||||
this.applyStyles();
|
this.applyStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setHeight(value: string) {
|
||||||
|
if (this._isTextAreaInput) {
|
||||||
|
this.inputElement.style.height = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public isEnabled(): boolean {
|
public isEnabled(): boolean {
|
||||||
return !this.inputElement.hasAttribute('disabled');
|
return !this.inputElement.hasAttribute('disabled');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,8 +115,7 @@ export class SelectBox extends vsSelectBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enable(): void {
|
public enable(): void {
|
||||||
//@SQLTODO
|
this.selectElement.disabled = false;
|
||||||
//this.selectElement.disabled = false;
|
|
||||||
this.selectBackground = this.enabledSelectBackground;
|
this.selectBackground = this.enabledSelectBackground;
|
||||||
this.selectForeground = this.enabledSelectForeground;
|
this.selectForeground = this.enabledSelectForeground;
|
||||||
this.selectBorder = this.enabledSelectBorder;
|
this.selectBorder = this.enabledSelectBorder;
|
||||||
@@ -124,8 +123,7 @@ export class SelectBox extends vsSelectBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public disable(): void {
|
public disable(): void {
|
||||||
//@SQLTODO
|
this.selectElement.disabled = true;
|
||||||
//this.selectElement.disabled = true;
|
|
||||||
this.selectBackground = this.disabledSelectBackground;
|
this.selectBackground = this.disabledSelectBackground;
|
||||||
this.selectForeground = this.disabledSelectForeground;
|
this.selectForeground = this.disabledSelectForeground;
|
||||||
this.selectBorder = this.disabledSelectBorder;
|
this.selectBorder = this.disabledSelectBorder;
|
||||||
|
|||||||
1
src/sql/base/browser/ui/table/media/down-inverse.svg
Normal file
1
src/sql/base/browser/ui/table/media/down-inverse.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#2d2d30;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>CollapseChevronDown_md_16x</title><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/><path class="icon-vs-out" d="M15.444,6.061,8,13.5.556,6.061,3.03,3.586,8,8.556l4.97-4.97Z" style="display: none;"/><path class="icon-vs-bg" d="M14.03,6.061,8,12.091,1.97,6.061,3.03,5,8,9.97,12.97,5Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 507 B |
1
src/sql/base/browser/ui/table/media/down.svg
Normal file
1
src/sql/base/browser/ui/table/media/down.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>CollapseChevronDown_md_16x</title><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/><path class="icon-vs-out" d="M15.444,6.061,8,13.5.556,6.061,3.03,3.586,8,8.556l4.97-4.97Z" style="display: none;"/><path class="icon-vs-bg" d="M14.03,6.061,8,12.091,1.97,6.061,3.03,5,8,9.97,12.97,5Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 507 B |
1
src/sql/base/browser/ui/table/media/filter.svg
Normal file
1
src/sql/base/browser/ui/table/media/filter.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{font-size:12px;font-family:FullMDL2Assets, Full MDL2 Assets;}</style></defs><title>filter_16x16</title><text class="cls-1" transform="translate(0 12)"> </text><path d="M0,1.53H16V3.24l-6,6v6.27H6V9.22l-6-6ZM15,2.82V2.53H1v.29l6,6v5.69H9V8.8Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 363 B |
1
src/sql/base/browser/ui/table/media/filter_inverse.svg
Normal file
1
src/sql/base/browser/ui/table/media/filter_inverse.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{font-size:12px;font-family:FullMDL2Assets, Full MDL2 Assets;}.cls-1,.cls-2{fill:#fff;}</style></defs><title>filter_inverse_16x16</title><text class="cls-1" transform="translate(0.03 12.1)"> </text><path class="cls-2" d="M.05,1.63H16V3.33l-6,6v6.27H6V9.31l-6-6ZM15,2.91V2.62H1v.29l6,6v5.69H9V8.89Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 418 B |
@@ -32,3 +32,116 @@
|
|||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.slick-header-menubutton {
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
bottom: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 0;
|
||||||
|
width: 18px;
|
||||||
|
background-image: url('down.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs-dark .slick-header-menubutton {
|
||||||
|
background-image: url('down-inverse.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-header-menubutton.filtered {
|
||||||
|
background-image: url('filter.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs-dark .slick-header-menubutton.filtered {
|
||||||
|
background-image: url('filter_inverse.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-header-menu {
|
||||||
|
background: none repeat scroll 0 0 white;
|
||||||
|
border: 1px solid #BFBDBD;
|
||||||
|
min-width: 175px;
|
||||||
|
padding: 4px;
|
||||||
|
z-index: 100000;
|
||||||
|
cursor: default;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs-dark .slick-header-menu {
|
||||||
|
background: none repeat scroll 0 0 #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-header-menu a.monaco-button.monaco-text-button {
|
||||||
|
width: 60px;
|
||||||
|
margin: 6px 6px 6px 6px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-header-menu .filter
|
||||||
|
{
|
||||||
|
border: 1px solid #BFBDBD;
|
||||||
|
font-size: 8pt;
|
||||||
|
height: 400px;
|
||||||
|
margin-top: 6px;
|
||||||
|
overflow: scroll;
|
||||||
|
padding: 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-header-menuitem
|
||||||
|
{
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 2px 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none outside none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-header-menuicon
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 4px;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-header-menuicon.ascending {
|
||||||
|
background: url('sort-asc.gif');
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-header-menuicon.descending {
|
||||||
|
background: url('sort-desc.gif');
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-header-menucontent {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-header-menuitem:hover {
|
||||||
|
border-color: #BFBDBD;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-overlay, .cell-overlay, .selection-cell-overlay {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs-dark .slick-header-menu > input.input {
|
||||||
|
color: #4a4a4a;
|
||||||
|
}
|
||||||
353
src/sql/base/browser/ui/table/plugins/headerFilter.plugin.ts
Normal file
353
src/sql/base/browser/ui/table/plugins/headerFilter.plugin.ts
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
// Adopted and converted to typescript from https://github.com/danny-sg/slickgrid-spreadsheet-plugins/blob/master/ext.headerfilter.js
|
||||||
|
// heavily modified
|
||||||
|
import 'vs/css!sql/base/browser/ui/table/media/table';
|
||||||
|
|
||||||
|
import { mixin } from 'vs/base/common/objects';
|
||||||
|
import { SlickGrid } from 'angular2-slickgrid';
|
||||||
|
import { Button } from '../../button/button';
|
||||||
|
import { attachButtonStyler } from 'sql/common/theme/styler';
|
||||||
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
|
|
||||||
|
export class HeaderFilter {
|
||||||
|
|
||||||
|
public onFilterApplied = new Slick.Event();
|
||||||
|
public onCommand = new Slick.Event();
|
||||||
|
|
||||||
|
private grid;
|
||||||
|
private handler = new Slick.EventHandler();
|
||||||
|
private defaults = {
|
||||||
|
filterImage: 'src/sql/media/icons/filter.svg',
|
||||||
|
sortAscImage: 'sort-asc.gif',
|
||||||
|
sortDescImage: 'sort-desc.gif'
|
||||||
|
};
|
||||||
|
|
||||||
|
private $menu;
|
||||||
|
private options: any;
|
||||||
|
private okButton: Button;
|
||||||
|
private clearButton: Button;
|
||||||
|
private cancelButton: Button;
|
||||||
|
private workingFilters: any;
|
||||||
|
private columnDef: any;
|
||||||
|
|
||||||
|
constructor(options: any, private _themeService: IThemeService) {
|
||||||
|
this.options = mixin(options, this.defaults, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(grid: Slick.Grid<any>): void {
|
||||||
|
this.grid = grid;
|
||||||
|
this.handler.subscribe(this.grid.onHeaderCellRendered, (e, args) => this.handleHeaderCellRendered(e , args))
|
||||||
|
.subscribe(this.grid.onBeforeHeaderCellDestroy, (e, args) => this.handleBeforeHeaderCellDestroy(e, args))
|
||||||
|
.subscribe(this.grid.onClick, (e) => this.handleBodyMouseDown)
|
||||||
|
.subscribe(this.grid.onColumnsResized, () => this.columnsResized());
|
||||||
|
|
||||||
|
this.grid.setColumns(this.grid.getColumns());
|
||||||
|
|
||||||
|
$(document.body).bind('mousedown', this.handleBodyMouseDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
this.handler.unsubscribeAll();
|
||||||
|
$(document.body).unbind('mousedown', this.handleBodyMouseDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleBodyMouseDown = (e) => {
|
||||||
|
if (this.$menu && this.$menu[0] !== e.target && !$.contains(this.$menu[0], e.target)) {
|
||||||
|
this.hideMenu();
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private hideMenu() {
|
||||||
|
if (this.$menu) {
|
||||||
|
this.$menu.remove();
|
||||||
|
this.$menu = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleHeaderCellRendered(e, args) {
|
||||||
|
let column = args.column;
|
||||||
|
if (column.id === '_detail_selector') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let $el = $('<div></div>')
|
||||||
|
.addClass('slick-header-menubutton')
|
||||||
|
.data('column', column);
|
||||||
|
|
||||||
|
$el.bind('click', (e) => this.showFilter(e)).appendTo(args.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleBeforeHeaderCellDestroy(e, args) {
|
||||||
|
$(args.node)
|
||||||
|
.find('.slick-header-menubutton')
|
||||||
|
.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
private addMenuItem(menu, columnDef, title, command, image) {
|
||||||
|
let $item = $('<div class="slick-header-menuitem">')
|
||||||
|
.data('command', command)
|
||||||
|
.data('column', columnDef)
|
||||||
|
.bind('click', (e) => this.handleMenuItemClick(e, command, columnDef))
|
||||||
|
.appendTo(menu);
|
||||||
|
|
||||||
|
let $icon = $('<div class="slick-header-menuicon">')
|
||||||
|
.appendTo($item);
|
||||||
|
|
||||||
|
if (title === 'Sort Ascending') {
|
||||||
|
$icon.get(0).className += ' ascending';
|
||||||
|
} else if (title === 'Sort Descending') {
|
||||||
|
$icon.get(0).className += ' descending';
|
||||||
|
}
|
||||||
|
|
||||||
|
$('<span class="slick-header-menucontent">')
|
||||||
|
.text(title)
|
||||||
|
.appendTo($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private addMenuInput(menu, columnDef) {
|
||||||
|
const self = this;
|
||||||
|
$('<input class="input" placeholder="Search" style="margin-top: 5px; width: 206px">')
|
||||||
|
.data('column', columnDef)
|
||||||
|
.bind('keyup', (e) => {
|
||||||
|
let filterVals = this.getFilterValuesByInput($(e.target));
|
||||||
|
self.updateFilterInputs(menu, columnDef, filterVals);
|
||||||
|
})
|
||||||
|
.appendTo(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateFilterInputs(menu, columnDef, filterItems) {
|
||||||
|
let filterOptions = '<label><input type="checkbox" value="-1" />(Select All)</label>';
|
||||||
|
columnDef.filterValues = columnDef.filterValues || [];
|
||||||
|
|
||||||
|
// WorkingFilters is a copy of the filters to enable apply/cancel behaviour
|
||||||
|
this.workingFilters = columnDef.filterValues.slice(0);
|
||||||
|
|
||||||
|
for (let i = 0; i < filterItems.length; i++) {
|
||||||
|
let filtered = _.contains(this.workingFilters, filterItems[i]);
|
||||||
|
|
||||||
|
filterOptions += '<label><input type="checkbox" value="' + i + '"'
|
||||||
|
+ (filtered ? ' checked="checked"' : '')
|
||||||
|
+ '/>' + filterItems[i] + '</label>';
|
||||||
|
}
|
||||||
|
let $filter = menu.find('.filter');
|
||||||
|
$filter.empty().append($(filterOptions));
|
||||||
|
|
||||||
|
$(':checkbox', $filter).bind('click', (e) => {
|
||||||
|
this.workingFilters = this.changeWorkingFilter(filterItems, this.workingFilters, $(e.target));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private showFilter(e) {
|
||||||
|
let $menuButton = $(e.target);
|
||||||
|
this.columnDef = $menuButton.data('column');
|
||||||
|
|
||||||
|
this.columnDef.filterValues = this.columnDef.filterValues || [];
|
||||||
|
|
||||||
|
// WorkingFilters is a copy of the filters to enable apply/cancel behaviour
|
||||||
|
this.workingFilters = this.columnDef.filterValues.slice(0);
|
||||||
|
|
||||||
|
let filterItems;
|
||||||
|
|
||||||
|
if (this.workingFilters.length === 0) {
|
||||||
|
// Filter based all available values
|
||||||
|
filterItems = this.getFilterValues(this.grid.getData(), this.columnDef);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Filter based on current dataView subset
|
||||||
|
filterItems = this.getAllFilterValues(this.grid.getData().getItems(), this.columnDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.$menu) {
|
||||||
|
this.$menu = $('<div class="slick-header-menu">').appendTo(document.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$menu.empty();
|
||||||
|
|
||||||
|
this.addMenuItem(this.$menu, this.columnDef, 'Sort Ascending', 'sort-asc', this.options.sortAscImage);
|
||||||
|
this.addMenuItem(this.$menu, this.columnDef, 'Sort Descending', 'sort-desc', this.options.sortDescImage);
|
||||||
|
this.addMenuInput(this.$menu, this.columnDef);
|
||||||
|
|
||||||
|
let filterOptions = '<label><input type="checkbox" value="-1" />(Select All)</label>';
|
||||||
|
|
||||||
|
for (let i = 0; i < filterItems.length; i++) {
|
||||||
|
let filtered = _.contains(this.workingFilters, filterItems[i]);
|
||||||
|
if (filterItems[i] && filterItems[i].indexOf('Error:') < 0) {
|
||||||
|
filterOptions += '<label><input type="checkbox" value="' + i + '"'
|
||||||
|
+ (filtered ? ' checked="checked"' : '')
|
||||||
|
+ '/>' + filterItems[i] + '</label>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let $filter = $('<div class="filter">')
|
||||||
|
.append($(filterOptions))
|
||||||
|
.appendTo(this.$menu);
|
||||||
|
|
||||||
|
this.okButton = new Button(this.$menu.get(0));
|
||||||
|
this.okButton.label = 'OK';
|
||||||
|
this.okButton.title = 'OK';
|
||||||
|
this.okButton.element.id = 'filter-ok-button';
|
||||||
|
let okElement = $('#filter-ok-button');
|
||||||
|
okElement.bind('click', (ev) => {
|
||||||
|
this.columnDef.filterValues = this.workingFilters.splice(0);
|
||||||
|
this.setButtonImage($menuButton, this.columnDef.filterValues.length > 0);
|
||||||
|
this.handleApply(ev, this.columnDef);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.clearButton = new Button(this.$menu.get(0));
|
||||||
|
this.clearButton.label = 'Clear';
|
||||||
|
this.clearButton.title = 'Clear';
|
||||||
|
this.clearButton.element.id = 'filter-clear-button';
|
||||||
|
let clearElement = $('#filter-clear-button');
|
||||||
|
clearElement.bind('click', (ev) => {
|
||||||
|
this.columnDef.filterValues.length = 0;
|
||||||
|
this.setButtonImage($menuButton, false);
|
||||||
|
this.handleApply(ev, this.columnDef);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.cancelButton = new Button(this.$menu.get(0));
|
||||||
|
this.cancelButton.label = 'Cancel';
|
||||||
|
this.cancelButton.title = 'Cancel';
|
||||||
|
this.cancelButton.element.id = 'filter-cancel-button';
|
||||||
|
let cancelElement = $('#filter-cancel-button');
|
||||||
|
cancelElement.bind('click', () => this.hideMenu());
|
||||||
|
attachButtonStyler(this.okButton, this._themeService);
|
||||||
|
attachButtonStyler(this.clearButton, this._themeService);
|
||||||
|
attachButtonStyler(this.cancelButton, this._themeService);
|
||||||
|
|
||||||
|
$(':checkbox', $filter).bind('click', (e) => {
|
||||||
|
this.workingFilters = this.changeWorkingFilter(filterItems, this.workingFilters, $(e.target));
|
||||||
|
});
|
||||||
|
|
||||||
|
let offset = $(e.target).offset();
|
||||||
|
let left = offset.left - this.$menu.width() + $(e.target).width() - 8;
|
||||||
|
|
||||||
|
let menutop = offset.top + $(e.target).height();
|
||||||
|
|
||||||
|
if (menutop + offset.top > $(window).height()) {
|
||||||
|
menutop -= (this.$menu.height() + $(e.target).height() + 8);
|
||||||
|
}
|
||||||
|
this.$menu.css('top', menutop)
|
||||||
|
.css('left', (left > 0 ? left : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private columnsResized() {
|
||||||
|
this.hideMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
private changeWorkingFilter(filterItems, workingFilters, $checkbox) {
|
||||||
|
let value = $checkbox.val();
|
||||||
|
let $filter = $checkbox.parent().parent();
|
||||||
|
|
||||||
|
if ($checkbox.val() < 0) {
|
||||||
|
// Select All
|
||||||
|
if ($checkbox.prop('checked')) {
|
||||||
|
$(':checkbox', $filter).prop('checked', true);
|
||||||
|
workingFilters = filterItems.slice(0);
|
||||||
|
} else {
|
||||||
|
$(':checkbox', $filter).prop('checked', false);
|
||||||
|
workingFilters.length = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let index = _.indexOf(workingFilters, filterItems[value]);
|
||||||
|
|
||||||
|
if ($checkbox.prop('checked') && index < 0) {
|
||||||
|
workingFilters.push(filterItems[value]);
|
||||||
|
let nextRow = filterItems[(parseInt(value)+1).toString()];
|
||||||
|
if (nextRow && nextRow.indexOf('Error:') >= 0) {
|
||||||
|
workingFilters.push(nextRow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (index > -1) {
|
||||||
|
workingFilters.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return workingFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setButtonImage($el, filtered) {
|
||||||
|
let element: HTMLElement = $el.get(0);
|
||||||
|
if (filtered) {
|
||||||
|
element.className += ' filtered';
|
||||||
|
} else {
|
||||||
|
let classList = element.classList;
|
||||||
|
if (classList.contains('filtered')) {
|
||||||
|
classList.remove('filtered');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleApply(e, columnDef) {
|
||||||
|
this.hideMenu();
|
||||||
|
|
||||||
|
this.onFilterApplied.notify({ 'grid': this.grid, 'column': columnDef }, e, self);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFilterValues(dataView, column) {
|
||||||
|
let seen = [];
|
||||||
|
for (let i = 0; i < dataView.getLength() ; i++) {
|
||||||
|
let value = dataView.getItem(i)[column.field];
|
||||||
|
|
||||||
|
if (!_.contains(seen, value)) {
|
||||||
|
seen.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return seen;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFilterValuesByInput($input) {
|
||||||
|
let column = $input.data('column'),
|
||||||
|
filter = $input.val(),
|
||||||
|
dataView = this.grid.getData(),
|
||||||
|
seen = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < dataView.getLength() ; i++) {
|
||||||
|
let value = dataView.getItem(i)[column.field];
|
||||||
|
|
||||||
|
if (filter.length > 0) {
|
||||||
|
let itemValue = !value ? '' : value;
|
||||||
|
let lowercaseFilter = filter.toString().toLowerCase();
|
||||||
|
let lowercaseVal = itemValue.toString().toLowerCase();
|
||||||
|
if (!_.contains(seen, value) && lowercaseVal.indexOf(lowercaseFilter) > -1) {
|
||||||
|
seen.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!_.contains(seen, value)) {
|
||||||
|
seen.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _.sortBy(seen, (v) => { return v; });
|
||||||
|
}
|
||||||
|
|
||||||
|
private getAllFilterValues(data, column) {
|
||||||
|
let seen = [];
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
let value = data[i][column.field];
|
||||||
|
|
||||||
|
if (!_.contains(seen, value)) {
|
||||||
|
seen.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _.sortBy(seen, (v) => { return v; });
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleMenuItemClick(e, command, columnDef) {
|
||||||
|
this.hideMenu();
|
||||||
|
|
||||||
|
this.onCommand.notify({
|
||||||
|
'grid': this.grid,
|
||||||
|
'column': columnDef,
|
||||||
|
'command': command
|
||||||
|
}, e, self);
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -277,8 +277,8 @@ export class RowDetailView {
|
|||||||
item._isPadding = false;
|
item._isPadding = false;
|
||||||
item._parent = parent;
|
item._parent = parent;
|
||||||
item._offset = offset;
|
item._offset = offset;
|
||||||
item.jobId = parent.jobId;
|
|
||||||
item.name = parent.message ? parent.message : nls.localize('rowDetailView.loadError','Loading Error...');
|
item.name = parent.message ? parent.message : nls.localize('rowDetailView.loadError','Loading Error...');
|
||||||
|
parent._child = item;
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export interface IFindPosition {
|
|||||||
row: number;
|
row: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function defaultSort<T>(args: Slick.OnSortEventArgs<T>, data: Array<T>): Array<T> {
|
export function defaultSort<T>(args: Slick.OnSortEventArgs<T>, data: Array<T>): Array<T> {
|
||||||
let field = args.sortCol.field;
|
let field = args.sortCol.field;
|
||||||
let sign = args.sortAsc ? 1 : -1;
|
let sign = args.sortAsc ? 1 : -1;
|
||||||
return data.sort((a, b) => (a[field] === b[field] ? 0 : (a[field] > b[field] ? 1 : -1)) * sign);
|
return data.sort((a, b) => (a[field] === b[field] ? 0 : (a[field] > b[field] ? 1 : -1)) * sign);
|
||||||
|
|||||||
10
src/sql/base/browser/ui/table/utils.ts
Normal file
10
src/sql/base/browser/ui/table/utils.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
export function defaultFormatter<T>(valueProperty: keyof T): Slick.Formatter<T> {
|
||||||
|
return (row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: Slick.SlickData): string => {
|
||||||
|
return value[valueProperty];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -72,6 +72,7 @@ export function attachInputBoxStyler(widget: IThemable, themeService: IThemeServ
|
|||||||
export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeService, style?:
|
export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeService, style?:
|
||||||
{
|
{
|
||||||
selectBackground?: cr.ColorIdentifier,
|
selectBackground?: cr.ColorIdentifier,
|
||||||
|
selectListBackground?: cr.ColorIdentifier,
|
||||||
selectForeground?: cr.ColorIdentifier,
|
selectForeground?: cr.ColorIdentifier,
|
||||||
selectBorder?: cr.ColorIdentifier,
|
selectBorder?: cr.ColorIdentifier,
|
||||||
disabledSelectBackground?: cr.ColorIdentifier,
|
disabledSelectBackground?: cr.ColorIdentifier,
|
||||||
@@ -81,10 +82,17 @@ export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeSer
|
|||||||
inputValidationWarningBorder?: cr.ColorIdentifier,
|
inputValidationWarningBorder?: cr.ColorIdentifier,
|
||||||
inputValidationWarningBackground?: cr.ColorIdentifier,
|
inputValidationWarningBackground?: cr.ColorIdentifier,
|
||||||
inputValidationErrorBorder?: cr.ColorIdentifier,
|
inputValidationErrorBorder?: cr.ColorIdentifier,
|
||||||
inputValidationErrorBackground?: cr.ColorIdentifier
|
inputValidationErrorBackground?: cr.ColorIdentifier,
|
||||||
|
focusBorder?: cr.ColorIdentifier,
|
||||||
|
listFocusBackground?: cr.ColorIdentifier,
|
||||||
|
listFocusForeground?: cr.ColorIdentifier,
|
||||||
|
listFocusOutline?: cr.ColorIdentifier,
|
||||||
|
listHoverBackground?: cr.ColorIdentifier,
|
||||||
|
listHoverForeground?: cr.ColorIdentifier
|
||||||
}): IDisposable {
|
}): IDisposable {
|
||||||
return attachStyler(themeService, {
|
return attachStyler(themeService, {
|
||||||
selectBackground: (style && style.selectBackground) || cr.selectBackground,
|
selectBackground: (style && style.selectBackground) || cr.selectBackground,
|
||||||
|
selectListBackground: (style && style.selectListBackground) || cr.selectListBackground,
|
||||||
selectForeground: (style && style.selectForeground) || cr.selectForeground,
|
selectForeground: (style && style.selectForeground) || cr.selectForeground,
|
||||||
selectBorder: (style && style.selectBorder) || cr.selectBorder,
|
selectBorder: (style && style.selectBorder) || cr.selectBorder,
|
||||||
disabledSelectBackground: (style && style.disabledSelectBackground) || sqlcolors.disabledInputBackground,
|
disabledSelectBackground: (style && style.disabledSelectBackground) || sqlcolors.disabledInputBackground,
|
||||||
@@ -94,7 +102,14 @@ export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeSer
|
|||||||
inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder,
|
inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder,
|
||||||
inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground,
|
inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground,
|
||||||
inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder,
|
inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder,
|
||||||
inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground
|
inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground,
|
||||||
|
focusBorder: (style && style.focusBorder) || cr.focusBorder,
|
||||||
|
listFocusBackground: (style && style.listFocusBackground) || cr.listFocusBackground,
|
||||||
|
listFocusForeground: (style && style.listFocusForeground) || cr.listFocusForeground,
|
||||||
|
listFocusOutline: (style && style.listFocusOutline) || cr.activeContrastBorder,
|
||||||
|
listHoverBackground: (style && style.listHoverBackground) || cr.listHoverBackground,
|
||||||
|
listHoverForeground: (style && style.listHoverForeground) || cr.listHoverForeground,
|
||||||
|
listHoverOutline: (style && style.listFocusOutline) || cr.activeContrastBorder
|
||||||
}, widget);
|
}, widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, Que
|
|||||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
import { CommonServiceInterface, SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface, SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { WidgetConfig, TabConfig, TabSettingConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
import { WidgetConfig, TabConfig, TabSettingConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
|
||||||
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
|
|
||||||
import { IPropertiesConfig } from 'sql/parts/dashboard/pages/serverDashboardPage.contribution';
|
import { IPropertiesConfig } from 'sql/parts/dashboard/pages/serverDashboardPage.contribution';
|
||||||
import { PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
|
import { PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
|
||||||
import { IDashboardRegistry, Extensions as DashboardExtensions, IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
import { IDashboardRegistry, Extensions as DashboardExtensions, IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
@@ -27,13 +25,8 @@ import { AngularDisposable } from 'sql/base/common/lifecycle';
|
|||||||
|
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
import * as types from 'vs/base/common/types';
|
import * as types from 'vs/base/common/types';
|
||||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
|
||||||
import { addDisposableListener, getContentHeight, EventType } from 'vs/base/browser/dom';
|
|
||||||
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
|
||||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
|
||||||
import * as themeColors from 'vs/workbench/common/theme';
|
|
||||||
import * as objects from 'vs/base/common/objects';
|
import * as objects from 'vs/base/common/objects';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import { Action } from 'vs/base/common/actions';
|
import { Action } from 'vs/base/common/actions';
|
||||||
|
|||||||
@@ -7,11 +7,7 @@ import 'vs/css!./controlHostContent';
|
|||||||
import { Component, forwardRef, Input, OnInit, Inject, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
|
import { Component, forwardRef, Input, OnInit, Inject, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
|
||||||
|
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import { Parts } from 'vs/workbench/services/part/common/partService';
|
|
||||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
|
|
||||||
import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
|
||||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
|
|||||||
@@ -52,11 +52,16 @@ import { DashboardControlHostContainer } from 'sql/parts/dashboard/containers/da
|
|||||||
import { JobsViewComponent } from 'sql/parts/jobManagement/views/jobsView.component';
|
import { JobsViewComponent } from 'sql/parts/jobManagement/views/jobsView.component';
|
||||||
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
||||||
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
||||||
|
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
||||||
|
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
||||||
|
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
||||||
|
|
||||||
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
|
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
|
||||||
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
|
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
|
||||||
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
|
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
|
||||||
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, DashboardModelViewContainer, ModelComponentWrapper];
|
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, DashboardModelViewContainer, ModelComponentWrapper, Checkbox,
|
||||||
|
SelectBox,
|
||||||
|
InputBox,];
|
||||||
|
|
||||||
/* Panel */
|
/* Panel */
|
||||||
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ export class DatabaseDashboardPage extends DashboardPage implements OnInit {
|
|||||||
background_color: colors.editorBackground,
|
background_color: colors.editorBackground,
|
||||||
border: 'none',
|
border: 'none',
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
fontWeight: '200',
|
|
||||||
padding: '5px 0 0 0',
|
padding: '5px 0 0 0',
|
||||||
provider: undefined,
|
provider: undefined,
|
||||||
edition: undefined
|
edition: undefined
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ export class ServerDashboardPage extends DashboardPage implements OnInit {
|
|||||||
background_color: colors.editorBackground,
|
background_color: colors.editorBackground,
|
||||||
border: 'none',
|
border: 'none',
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
fontWeight: '200',
|
|
||||||
padding: '5px 0 0 0',
|
padding: '5px 0 0 0',
|
||||||
provider: undefined,
|
provider: undefined,
|
||||||
edition: undefined
|
edition: undefined
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<span #child style="white-space : nowrap; width: fit-content">
|
<span #child style="white-space : nowrap; width: fit-content">
|
||||||
<ng-template ngFor let-item [ngForOf]="properties">
|
<ng-template ngFor let-item [ngForOf]="properties">
|
||||||
<span style="margin-left: 10px; display: inline-block;">
|
<span style="margin-left: 10px; display: inline-block;">
|
||||||
<div style="font-size: 11px; font-weight: lighter">{{item.displayName}}</div>
|
<div style="font-size: 11px;">{{item.displayName}}</div>
|
||||||
<div>{{item.value}}</div>
|
<div>{{item.value}}</div>
|
||||||
</span>
|
</span>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
|
|||||||
}).filter(i => !!i);
|
}).filter(i => !!i);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._tasks = tasks.map(i => MenuRegistry.getCommand(i));
|
this._tasks = tasks.map(i => MenuRegistry.getCommand(i)).filter(v => !!v);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { IEditDataComponentParams } from 'sql/services/bootstrap/bootstrapParams
|
|||||||
import { GridParentComponent } from 'sql/parts/grid/views/gridParentComponent';
|
import { GridParentComponent } from 'sql/parts/grid/views/gridParentComponent';
|
||||||
import { EditDataGridActionProvider } from 'sql/parts/grid/views/editData/editDataGridActions';
|
import { EditDataGridActionProvider } from 'sql/parts/grid/views/editData/editDataGridActions';
|
||||||
import { error } from 'sql/base/common/log';
|
import { error } from 'sql/base/common/log';
|
||||||
import { clone } from 'sql/base/common/objects';
|
import { clone, mixin } from 'sql/base/common/objects';
|
||||||
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
|
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
|
||||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||||
|
|
||||||
@@ -195,7 +195,11 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
|||||||
let gridData: IGridDataRow[] = result.subset.map(row => {
|
let gridData: IGridDataRow[] = result.subset.map(row => {
|
||||||
self.idMapping[rowIndex] = row.id;
|
self.idMapping[rowIndex] = row.id;
|
||||||
rowIndex++;
|
rowIndex++;
|
||||||
return { values: row.cells, row: row.id };
|
return {
|
||||||
|
values: row.cells.map(c => {
|
||||||
|
return mixin({ ariaLabel: c.displayValue }, c);
|
||||||
|
}), row: row.id
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Append a NULL row to the end of gridData
|
// Append a NULL row to the end of gridData
|
||||||
@@ -210,23 +214,21 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
|||||||
onDeleteRow(): (index: number) => void {
|
onDeleteRow(): (index: number) => void {
|
||||||
const self = this;
|
const self = this;
|
||||||
return (index: number): void => {
|
return (index: number): void => {
|
||||||
self.dataService.deleteRow(index)
|
// If the user is deleting a new row that hasn't been committed yet then use the revert code
|
||||||
.then(() => self.dataService.commitEdit())
|
if (self.newRowVisible && index === self.dataSet.dataRows.getLength() - 2) {
|
||||||
.then(() => self.removeRow(index));
|
self.revertCurrentRow();
|
||||||
|
} else {
|
||||||
|
self.dataService.deleteRow(index)
|
||||||
|
.then(() => self.dataService.commitEdit())
|
||||||
|
.then(() => self.removeRow(index));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onRevertRow(): (index: number) => void {
|
onRevertRow(): () => void {
|
||||||
const self = this;
|
const self = this;
|
||||||
return (index: number): void => {
|
return (): void => {
|
||||||
// Force focus to the first cell (completing any active edit operation)
|
self.revertCurrentRow();
|
||||||
self.focusCell(index, 0, false);
|
|
||||||
this.currentEditCellValue = null;
|
|
||||||
// Perform a revert row operation
|
|
||||||
self.dataService.revertRow(index)
|
|
||||||
.then(() => {
|
|
||||||
self.refreshResultsets();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,24 +277,27 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.currentCell.row !== row) {
|
if (this.currentCell.row !== row) {
|
||||||
// We're changing row, commit the changes
|
// If we're currently adding a new row, only commit it if it has changes or the user is trying to add another new row
|
||||||
cellSelectTasks = cellSelectTasks.then(() => {
|
if (this.newRowVisible && this.currentCell.row === this.dataSet.dataRows.getLength() - 2 && !this.isNullRow(row) && this.currentEditCellValue === null) {
|
||||||
return self.dataService.commitEdit()
|
cellSelectTasks = cellSelectTasks.then(() => {
|
||||||
.then(
|
return this.revertCurrentRow().then(() => this.focusCell(row, column));
|
||||||
result => {
|
});
|
||||||
|
} else {
|
||||||
|
// We're changing row, commit the changes
|
||||||
|
cellSelectTasks = cellSelectTasks.then(() => {
|
||||||
|
return self.dataService.commitEdit().then(result => {
|
||||||
// Committing was successful, clean the grid
|
// Committing was successful, clean the grid
|
||||||
self.setGridClean();
|
self.setGridClean();
|
||||||
self.rowIdMappings = {};
|
self.rowIdMappings = {};
|
||||||
self.newRowVisible = false;
|
self.newRowVisible = false;
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
}, error => {
|
||||||
error => {
|
|
||||||
// Committing failed, jump back to the last selected cell
|
// Committing failed, jump back to the last selected cell
|
||||||
self.focusCell(self.currentCell.row, self.currentCell.column);
|
self.focusCell(self.currentCell.row, self.currentCell.column);
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
}
|
});
|
||||||
);
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isNullRow(row) && !this.removingNewRow) {
|
if (this.isNullRow(row) && !this.removingNewRow) {
|
||||||
@@ -428,7 +433,18 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
|||||||
// If the esc key was pressed while in a create session
|
// If the esc key was pressed while in a create session
|
||||||
let currentNewRowIndex = this.dataSet.totalRows - 2;
|
let currentNewRowIndex = this.dataSet.totalRows - 2;
|
||||||
|
|
||||||
if (e.keyCode === KeyCode.Escape && this.newRowVisible && this.currentCell.row === currentNewRowIndex) {
|
if (e.keyCode === KeyCode.Escape) {
|
||||||
|
this.revertCurrentRow();
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private Helper Functions ////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private async revertCurrentRow(): Promise<void> {
|
||||||
|
let currentNewRowIndex = this.dataSet.totalRows - 2;
|
||||||
|
if (this.newRowVisible && this.currentCell.row === currentNewRowIndex) {
|
||||||
// revert our last new row
|
// revert our last new row
|
||||||
this.removingNewRow = true;
|
this.removingNewRow = true;
|
||||||
|
|
||||||
@@ -437,17 +453,21 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
|||||||
this.removeRow(currentNewRowIndex);
|
this.removeRow(currentNewRowIndex);
|
||||||
this.newRowVisible = false;
|
this.newRowVisible = false;
|
||||||
});
|
});
|
||||||
handled = true;
|
} else {
|
||||||
} else if (e.keyCode === KeyCode.Escape) {
|
try {
|
||||||
this.currentEditCellValue = null;
|
// Perform a revert row operation
|
||||||
this.onRevertRow()(this.currentCell.row);
|
if (this.currentCell) {
|
||||||
handled = true;
|
await this.dataService.revertRow(this.currentCell.row);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// The operation may fail if there were no changes sent to the service to revert,
|
||||||
|
// so clear any existing client-side edit and refresh the table regardless
|
||||||
|
this.currentEditCellValue = null;
|
||||||
|
this.refreshResultsets();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return handled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private Helper Functions ////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Checks if input row is our NULL new row
|
// Checks if input row is our NULL new row
|
||||||
private isNullRow(row: number): boolean {
|
private isNullRow(row: number): boolean {
|
||||||
// Null row is always at index (totalRows - 1)
|
// Null row is always at index (totalRows - 1)
|
||||||
@@ -535,9 +555,11 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
|||||||
// refresh results view
|
// refresh results view
|
||||||
this.onScroll(0);
|
this.onScroll(0);
|
||||||
|
|
||||||
// Set focus to the row index column of the removed row
|
// Set focus to the row index column of the removed row if the current selection is in the removed row
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.focusCell(row, 0);
|
if (this.currentCell.row === row) {
|
||||||
|
this.focusCell(row, 0);
|
||||||
|
}
|
||||||
this.removingNewRow = false;
|
this.removingNewRow = false;
|
||||||
}, this.scrollTimeOutTime);
|
}, this.scrollTimeOutTime);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class EditDataGridActionProvider extends GridActionProvider {
|
|||||||
dataService: DataService,
|
dataService: DataService,
|
||||||
selectAllCallback: (index: number) => void,
|
selectAllCallback: (index: number) => void,
|
||||||
private _deleteRowCallback: (index: number) => void,
|
private _deleteRowCallback: (index: number) => void,
|
||||||
private _revertRowCallback: (index: number) => void,
|
private _revertRowCallback: () => void,
|
||||||
@IInstantiationService instantiationService: IInstantiationService
|
@IInstantiationService instantiationService: IInstantiationService
|
||||||
) {
|
) {
|
||||||
super(dataService, selectAllCallback, instantiationService);
|
super(dataService, selectAllCallback, instantiationService);
|
||||||
@@ -56,18 +56,18 @@ export class DeleteRowAction extends Action {
|
|||||||
|
|
||||||
export class RevertRowAction extends Action {
|
export class RevertRowAction extends Action {
|
||||||
public static ID = 'grid.revertRow';
|
public static ID = 'grid.revertRow';
|
||||||
public static LABEL = localize('revertRow', 'Revert Row');
|
public static LABEL = localize('revertRow', 'Revert Current Row');
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
id: string,
|
id: string,
|
||||||
label: string,
|
label: string,
|
||||||
private callback: (index: number) => void
|
private callback: () => void
|
||||||
) {
|
) {
|
||||||
super(id, label);
|
super(id, label);
|
||||||
}
|
}
|
||||||
|
|
||||||
public run(gridInfo: IGridInfo): TPromise<boolean> {
|
public run(gridInfo: IGridInfo): TPromise<boolean> {
|
||||||
this.callback(gridInfo.rowIndex);
|
this.callback();
|
||||||
return TPromise.as(true);
|
return TPromise.as(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,3 +6,8 @@
|
|||||||
.editdata-component * {
|
.editdata-component * {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#workbench\.editor\.editDataEditor .monaco-toolbar .monaco-select-box {
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ import { GridActionProvider } from 'sql/parts/grid/views/gridActions';
|
|||||||
import { IQueryComponentParams } from 'sql/services/bootstrap/bootstrapParams';
|
import { IQueryComponentParams } from 'sql/services/bootstrap/bootstrapParams';
|
||||||
import { error } from 'sql/base/common/log';
|
import { error } from 'sql/base/common/log';
|
||||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||||
import { clone } from 'sql/base/common/objects';
|
import { clone, mixin } from 'sql/base/common/objects';
|
||||||
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
|
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
|
||||||
|
|
||||||
import * as strings from 'vs/base/common/strings';
|
import * as strings from 'vs/base/common/strings';
|
||||||
@@ -301,7 +301,9 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
|
|||||||
for (let row = 0; row < rows.rows.length; row++) {
|
for (let row = 0; row < rows.rows.length; row++) {
|
||||||
// Push row values onto end of gridData for slickgrid
|
// Push row values onto end of gridData for slickgrid
|
||||||
gridData.push({
|
gridData.push({
|
||||||
values: rows.rows[row]
|
values: rows.rows[row].map(c => {
|
||||||
|
return mixin({ ariaLabel: c.displayValue }, c);
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -168,13 +168,16 @@ export class InsightsDialogView extends Modal {
|
|||||||
|
|
||||||
this._splitView = new SplitView(container);
|
this._splitView = new SplitView(container);
|
||||||
|
|
||||||
|
const itemsHeaderTitle = nls.localize("insights.dialog.items", "Items");
|
||||||
|
const itemsDetailHeaderTitle = nls.localize("insights.dialog.itemDetails", "Item Details");
|
||||||
|
|
||||||
this._topTableData = new TableDataView();
|
this._topTableData = new TableDataView();
|
||||||
this._bottomTableData = new TableDataView();
|
this._bottomTableData = new TableDataView();
|
||||||
let topTableView = new TableCollapsibleView(nls.localize("insights.dialog.items", "Items"), { sizing: ViewSizing.Flexible, ariaHeaderLabel: 'title' }, this._topTableData, this._topColumns, { forceFitColumns: true });
|
let topTableView = new TableCollapsibleView(itemsHeaderTitle, { sizing: ViewSizing.Flexible, ariaHeaderLabel: itemsHeaderTitle }, this._topTableData, this._topColumns, { forceFitColumns: true });
|
||||||
this._topTable = topTableView.table;
|
this._topTable = topTableView.table;
|
||||||
topTableView.addContainerClass('insights');
|
topTableView.addContainerClass('insights');
|
||||||
this._topTable.setSelectionModel(new RowSelectionModel<ListResource>());
|
this._topTable.setSelectionModel(new RowSelectionModel<ListResource>());
|
||||||
let bottomTableView = new TableCollapsibleView(nls.localize("insights.dialog.itemDetails", "Item Details"), { sizing: ViewSizing.Flexible, ariaHeaderLabel: 'title' }, this._bottomTableData, this._bottomColumns, { forceFitColumns: true });
|
let bottomTableView = new TableCollapsibleView(itemsDetailHeaderTitle, { sizing: ViewSizing.Flexible, ariaHeaderLabel: itemsDetailHeaderTitle }, this._bottomTableData, this._bottomColumns, { forceFitColumns: true });
|
||||||
this._bottomTable = bottomTableView.table;
|
this._bottomTable = bottomTableView.table;
|
||||||
this._bottomTable.setSelectionModel(new RowSelectionModel<ListResource>());
|
this._bottomTable.setSelectionModel(new RowSelectionModel<ListResource>());
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ export class AgentViewComponent {
|
|||||||
|
|
||||||
public set jobId(value: string) {
|
public set jobId(value: string) {
|
||||||
this._jobId = value;
|
this._jobId = value;
|
||||||
this._cd.detectChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public set showHistory(value: boolean) {
|
public set showHistory(value: boolean) {
|
||||||
@@ -92,7 +91,6 @@ export class AgentViewComponent {
|
|||||||
|
|
||||||
public set agentJobInfo(value: AgentJobInfo) {
|
public set agentJobInfo(value: AgentJobInfo) {
|
||||||
this._agentJobInfo = value;
|
this._agentJobInfo = value;
|
||||||
this._cd.detectChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public set refresh(value: boolean) {
|
public set refresh(value: boolean) {
|
||||||
@@ -104,6 +102,10 @@ export class AgentViewComponent {
|
|||||||
this._expanded.set(jobId, errorMessage);
|
this._expanded.set(jobId, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public set expanded(value: Map<string, string>) {
|
||||||
|
this._expanded = value;
|
||||||
|
}
|
||||||
|
|
||||||
public layout() {
|
public layout() {
|
||||||
this._panel.layout();
|
this._panel.layout();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,4 +92,58 @@ export class AgentJobUtilities {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static convertDurationToSeconds(duration: string): number {
|
||||||
|
let split = duration.split(':');
|
||||||
|
let seconds = (+split[0]) * 60 * 60 + (+split[1]) * 60 + (+split[2]);
|
||||||
|
return seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static convertColFieldToName(colField: string) {
|
||||||
|
switch(colField) {
|
||||||
|
case('name'):
|
||||||
|
return 'Name';
|
||||||
|
case('lastRun'):
|
||||||
|
return 'Last Run';
|
||||||
|
case('nextRun'):
|
||||||
|
return 'Next Run';
|
||||||
|
case('enabled'):
|
||||||
|
return 'Enabled';
|
||||||
|
case('status'):
|
||||||
|
return 'Status';
|
||||||
|
case('category'):
|
||||||
|
return 'Category';
|
||||||
|
case('runnable'):
|
||||||
|
return 'Runnable';
|
||||||
|
case('schedule'):
|
||||||
|
return 'Schedule';
|
||||||
|
case('lastRunOutcome'):
|
||||||
|
return 'Last Run Outcome';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static convertColNameToField(columnName: string) {
|
||||||
|
switch(columnName) {
|
||||||
|
case('Name'):
|
||||||
|
return 'name';
|
||||||
|
case('Last Run'):
|
||||||
|
return 'lastRun';
|
||||||
|
case('Next Run'):
|
||||||
|
return 'nextRun';
|
||||||
|
case('Enabled'):
|
||||||
|
return 'enabled';
|
||||||
|
case('Status'):
|
||||||
|
return 'status';
|
||||||
|
case('Category'):
|
||||||
|
return 'category';
|
||||||
|
case('Runnable'):
|
||||||
|
return 'runnable';
|
||||||
|
case('Schedule'):
|
||||||
|
return 'schedule';
|
||||||
|
case('Last Run Outcome'):
|
||||||
|
return 'lastRunOutcome';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ export interface IJobManagementService {
|
|||||||
|
|
||||||
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult>;
|
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult>;
|
||||||
|
|
||||||
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult>;
|
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus>;
|
||||||
|
|
||||||
addToCache(server: string, cache: JobCacheObject);
|
addToCache(server: string, cache: JobCacheObject);
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export class JobManagementService implements IJobManagementService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult> {
|
public jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
|
||||||
return this._runAction(connectionUri, (runner) => {
|
return this._runAction(connectionUri, (runner) => {
|
||||||
return runner.jobAction(connectionUri, jobName, action);
|
return runner.jobAction(connectionUri, jobName, action);
|
||||||
});
|
});
|
||||||
@@ -82,6 +82,7 @@ export class JobCacheObject {
|
|||||||
private _jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = {};
|
private _jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = {};
|
||||||
private _prevJobID: string;
|
private _prevJobID: string;
|
||||||
private _serverName: string;
|
private _serverName: string;
|
||||||
|
private _dataView: Slick.Data.DataView<any>;
|
||||||
|
|
||||||
/* Getters */
|
/* Getters */
|
||||||
public get jobs(): sqlops.AgentJobInfo[] {
|
public get jobs(): sqlops.AgentJobInfo[] {
|
||||||
@@ -104,6 +105,10 @@ export class JobCacheObject {
|
|||||||
return this._serverName;
|
return this._serverName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get dataView(): Slick.Data.DataView<any> {
|
||||||
|
return this._dataView;
|
||||||
|
}
|
||||||
|
|
||||||
/* Setters */
|
/* Setters */
|
||||||
public set jobs(value: sqlops.AgentJobInfo[]) {
|
public set jobs(value: sqlops.AgentJobInfo[]) {
|
||||||
this._jobs = value;
|
this._jobs = value;
|
||||||
@@ -125,4 +130,7 @@ export class JobCacheObject {
|
|||||||
this._serverName = value;
|
this._serverName = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public set dataView(value: Slick.Data.DataView<any>) {
|
||||||
|
this._dataView = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -49,12 +49,12 @@ jobhistory-component {
|
|||||||
font-size: larger;
|
font-size: larger;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vs-dark #jobsDiv .slick-cell {
|
.vs-dark #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
|
||||||
background:#333333 !important;
|
background:#333333;
|
||||||
}
|
}
|
||||||
|
|
||||||
#jobsDiv .slick-cell {
|
#jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
|
||||||
background: white !important;
|
background: white;
|
||||||
border-right: transparent !important;
|
border-right: transparent !important;
|
||||||
border-left: transparent !important;
|
border-left: transparent !important;
|
||||||
line-height: 33px !important;
|
line-height: 33px !important;
|
||||||
@@ -206,3 +206,36 @@ agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.od
|
|||||||
width: 0;
|
width: 0;
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
|
||||||
|
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
|
||||||
|
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
|
||||||
|
background: #dcdcdc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
|
||||||
|
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
|
||||||
|
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
|
||||||
|
background: #444444 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.jobprevruns div.bar1, table.jobprevruns div.bar2, table.jobprevruns div.bar3,
|
||||||
|
table.jobprevruns div.bar4, table.jobprevruns div.bar5 {
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-left: 5px;
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jobview-grid .slick-cell.l10.r10 {
|
||||||
|
text-align: center;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.jobprevruns {
|
||||||
|
margin: auto;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.jobprevruns > tbody {
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
@@ -7,31 +7,22 @@ import 'vs/css!./jobHistory';
|
|||||||
import 'vs/css!sql/media/icons/common-icons';
|
import 'vs/css!sql/media/icons/common-icons';
|
||||||
import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
|
import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
|
||||||
import { AgentJobHistoryInfo, AgentJobInfo } from 'sqlops';
|
import { AgentJobHistoryInfo, AgentJobInfo } from 'sqlops';
|
||||||
|
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||||
import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
|
|
||||||
import { RunJobAction, StopJobAction } from 'sql/parts/jobManagement/views/jobHistoryActions';
|
import { RunJobAction, StopJobAction } from 'sql/parts/jobManagement/views/jobHistoryActions';
|
||||||
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
|
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
|
||||||
import { AgentJobUtilities } from '../common/agentJobUtilities';
|
import { AgentJobUtilities } from '../common/agentJobUtilities';
|
||||||
import { PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
|
|
||||||
import { IJobManagementService } from '../common/interfaces';
|
import { IJobManagementService } from '../common/interfaces';
|
||||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
||||||
import { JobHistoryController, JobHistoryDataSource,
|
import { JobHistoryController, JobHistoryDataSource,
|
||||||
JobHistoryRenderer, JobHistoryFilter, JobHistoryModel, JobHistoryRow } from 'sql/parts/jobManagement/views/jobHistoryTree';
|
JobHistoryRenderer, JobHistoryFilter, JobHistoryModel, JobHistoryRow } from 'sql/parts/jobManagement/views/jobHistoryTree';
|
||||||
import { JobStepsViewComponent } from 'sql/parts/jobManagement/views/jobStepsView.component';
|
|
||||||
import { JobStepsViewRow } from './jobStepsViewTree';
|
import { JobStepsViewRow } from './jobStepsViewTree';
|
||||||
|
|
||||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
|
||||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { localize } from 'vs/nls';
|
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
import Severity from 'vs/base/common/severity';
|
|
||||||
import { ITreeOptions } from 'vs/base/parts/tree/browser/tree';
|
|
||||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||||
|
|
||||||
@@ -301,7 +292,4 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
this._showSteps = value;
|
this._showSteps = value;
|
||||||
this._cd.detectChanges();
|
this._cd.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -245,6 +245,3 @@ jobhistory-component > .actionbar-container .monaco-action-bar > ul.actions-cont
|
|||||||
border-top: 3px solid #444444;
|
border-top: 3px solid #444444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ import { Action } from 'vs/base/common/actions';
|
|||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
import Severity from 'vs/base/common/severity';
|
import Severity from 'vs/base/common/severity';
|
||||||
import { BaseActionContext } from '../../../workbench/common/actions';
|
|
||||||
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
||||||
import { JobManagementService } from '../common/jobManagementService';
|
|
||||||
import { IJobManagementService } from '../common/interfaces';
|
import { IJobManagementService } from '../common/interfaces';
|
||||||
|
|
||||||
export enum JobHistoryActions {
|
export enum JobHistoryActions {
|
||||||
@@ -33,7 +31,7 @@ export class RunJobAction extends Action {
|
|||||||
let ownerUri = context.ownerUri;
|
let ownerUri = context.ownerUri;
|
||||||
return new TPromise<boolean>((resolve, reject) => {
|
return new TPromise<boolean>((resolve, reject) => {
|
||||||
this.jobManagementService.jobAction(ownerUri, jobName, JobHistoryActions.Run).then(result => {
|
this.jobManagementService.jobAction(ownerUri, jobName, JobHistoryActions.Run).then(result => {
|
||||||
if (result.succeeded) {
|
if (result.success) {
|
||||||
var startMsg = nls.localize('jobSuccessfullyStarted', ': The job was successfully started.');
|
var startMsg = nls.localize('jobSuccessfullyStarted', ': The job was successfully started.');
|
||||||
this.notificationService.notify({
|
this.notificationService.notify({
|
||||||
severity: Severity.Info,
|
severity: Severity.Info,
|
||||||
@@ -68,7 +66,7 @@ export class StopJobAction extends Action {
|
|||||||
let ownerUri = context.ownerUri;
|
let ownerUri = context.ownerUri;
|
||||||
return new TPromise<boolean>((resolve, reject) => {
|
return new TPromise<boolean>((resolve, reject) => {
|
||||||
this.jobManagementService.jobAction(ownerUri, jobName, JobHistoryActions.Stop).then(result => {
|
this.jobManagementService.jobAction(ownerUri, jobName, JobHistoryActions.Stop).then(result => {
|
||||||
if (result.succeeded) {
|
if (result.success) {
|
||||||
var stopMsg = nls.localize('jobSuccessfullyStopped', ': The job was successfully stopped.');
|
var stopMsg = nls.localize('jobSuccessfullyStopped', ': The job was successfully stopped.');
|
||||||
this.notificationService.notify({
|
this.notificationService.notify({
|
||||||
severity: Severity.Info,
|
severity: Severity.Info,
|
||||||
|
|||||||
@@ -3,33 +3,18 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
|
|
||||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
|
||||||
import { MetadataType } from 'sql/parts/connection/common/connectionManagement';
|
|
||||||
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
|
|
||||||
import {
|
import {
|
||||||
NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction, ScriptExecuteAction, ScriptAlterAction,
|
NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction, ScriptExecuteAction, ScriptAlterAction,
|
||||||
BackupAction, ManageActionContext, BaseActionContext, ManageAction, RestoreAction
|
BackupAction, ManageActionContext, BaseActionContext, ManageAction, RestoreAction
|
||||||
} from 'sql/workbench/common/actions';
|
} from 'sql/workbench/common/actions';
|
||||||
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
|
||||||
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
|
|
||||||
import * as Constants from 'sql/parts/connection/common/constants';
|
|
||||||
import * as tree from 'vs/base/parts/tree/browser/tree';
|
import * as tree from 'vs/base/parts/tree/browser/tree';
|
||||||
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
|
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
|
||||||
import { Promise, TPromise } from 'vs/base/common/winjs.base';
|
import { Promise, TPromise } from 'vs/base/common/winjs.base';
|
||||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
|
||||||
import { IAction } from 'vs/base/common/actions';
|
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
||||||
import { generateUuid } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
import * as DOM from 'vs/base/browser/dom';
|
import * as DOM from 'vs/base/browser/dom';
|
||||||
import { OEAction } from 'sql/parts/objectExplorer/viewlet/objectExplorerActions';
|
|
||||||
import { $ } from 'vs/base/browser/builder';
|
|
||||||
import { AgentJobHistoryInfo } from 'sqlops';
|
import { AgentJobHistoryInfo } from 'sqlops';
|
||||||
import { Agent } from 'vs/base/node/request';
|
|
||||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||||
import { JobHistoryComponent } from './jobHistory.component';
|
|
||||||
|
|
||||||
export class JobHistoryRow {
|
export class JobHistoryRow {
|
||||||
runDate: string;
|
runDate: string;
|
||||||
|
|||||||
@@ -6,22 +6,14 @@
|
|||||||
import 'vs/css!./jobStepsView';
|
import 'vs/css!./jobStepsView';
|
||||||
|
|
||||||
import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable, AfterContentChecked } from '@angular/core';
|
import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable, AfterContentChecked } from '@angular/core';
|
||||||
|
|
||||||
import { AgentJobHistoryInfo } from 'sqlops';
|
|
||||||
|
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
|
||||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
|
||||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
|
|
||||||
import { IJobManagementService } from '../common/interfaces';
|
|
||||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { JobStepsViewController, JobStepsViewDataSource, JobStepsViewFilter,
|
import { JobStepsViewController, JobStepsViewDataSource, JobStepsViewFilter,
|
||||||
JobStepsViewRenderer, JobStepsViewRow, JobStepsViewModel} from 'sql/parts/jobManagement/views/jobStepsViewTree';
|
JobStepsViewRenderer, JobStepsViewModel} from 'sql/parts/jobManagement/views/jobStepsViewTree';
|
||||||
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
||||||
|
|
||||||
export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component';
|
export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component';
|
||||||
|
|||||||
@@ -3,29 +3,17 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
|
|
||||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
|
||||||
import { MetadataType } from 'sql/parts/connection/common/connectionManagement';
|
|
||||||
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
|
|
||||||
import {
|
import {
|
||||||
NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction, ScriptExecuteAction, ScriptAlterAction,
|
NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction, ScriptExecuteAction, ScriptAlterAction,
|
||||||
BackupAction, ManageActionContext, BaseActionContext, ManageAction, RestoreAction
|
BackupAction, ManageActionContext, BaseActionContext, ManageAction, RestoreAction
|
||||||
} from 'sql/workbench/common/actions';
|
} from 'sql/workbench/common/actions';
|
||||||
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
|
||||||
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
|
|
||||||
import * as Constants from 'sql/parts/connection/common/constants';
|
|
||||||
import * as tree from 'vs/base/parts/tree/browser/tree';
|
import * as tree from 'vs/base/parts/tree/browser/tree';
|
||||||
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
|
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
|
||||||
import { Promise, TPromise } from 'vs/base/common/winjs.base';
|
import { Promise, TPromise } from 'vs/base/common/winjs.base';
|
||||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||||
import { IAction } from 'vs/base/common/actions';
|
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
||||||
import { generateUuid } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
import * as DOM from 'vs/base/browser/dom';
|
import * as DOM from 'vs/base/browser/dom';
|
||||||
import { OEAction } from 'sql/parts/objectExplorer/viewlet/objectExplorerActions';
|
|
||||||
import { AgentJobHistoryInfo } from 'sqlops';
|
import { AgentJobHistoryInfo } from 'sqlops';
|
||||||
import { Agent } from 'vs/base/node/request';
|
|
||||||
import { AgentJobUtilities } from 'sql/parts/jobManagement/common/agentJobUtilities';
|
import { AgentJobUtilities } from 'sql/parts/jobManagement/common/agentJobUtilities';
|
||||||
|
|
||||||
export class JobStepsViewRow {
|
export class JobStepsViewRow {
|
||||||
|
|||||||
@@ -10,26 +10,26 @@ import 'vs/css!sql/parts/grid/media/slick.grid';
|
|||||||
import 'vs/css!sql/parts/grid/media/slickGrid';
|
import 'vs/css!sql/parts/grid/media/slickGrid';
|
||||||
import 'vs/css!../common/media/jobs';
|
import 'vs/css!../common/media/jobs';
|
||||||
import 'vs/css!sql/media/icons/common-icons';
|
import 'vs/css!sql/media/icons/common-icons';
|
||||||
|
import 'vs/css!sql/base/browser/ui/table/media/table';
|
||||||
|
|
||||||
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, AfterContentChecked } from '@angular/core';
|
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, AfterContentChecked } from '@angular/core';
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
import * as dom from 'vs/base/browser/dom';
|
|
||||||
|
|
||||||
import { Table } from 'sql/base/browser/ui/table/table';
|
import { Table } from 'sql/base/browser/ui/table/table';
|
||||||
import { AgentViewComponent } from '../agent/agentView.component';
|
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
||||||
import { RowDetailView } from 'sql/base/browser/ui/table/plugins/rowdetailview';
|
import { RowDetailView } from 'sql/base/browser/ui/table/plugins/rowdetailview';
|
||||||
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
|
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
|
||||||
import { AgentJobUtilities } from '../common/agentJobUtilities';
|
import { AgentJobUtilities } from 'sql/parts/jobManagement/common/agentJobUtilities';
|
||||||
|
import { HeaderFilter } from 'sql/base/browser/ui/table/plugins/headerFilter.plugin';
|
||||||
|
import * as dom from 'vs/base/browser/dom';
|
||||||
import { IJobManagementService } from '../common/interfaces';
|
import { IJobManagementService } from '../common/interfaces';
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||||
|
|
||||||
|
|
||||||
export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
|
export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
|
||||||
|
export const ROW_HEIGHT: number = 45;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: JOBSVIEW_SELECTOR,
|
selector: JOBSVIEW_SELECTOR,
|
||||||
@@ -44,23 +44,35 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
private _disposables = new Array<vscode.Disposable>();
|
private _disposables = new Array<vscode.Disposable>();
|
||||||
|
|
||||||
private columns: Array<Slick.Column<any>> = [
|
private columns: Array<Slick.Column<any>> = [
|
||||||
{ name: nls.localize('jobColumns.name', 'Name'), field: 'name', formatter: this.renderName, width: 200, id: 'name' },
|
{ name: nls.localize('jobColumns.name','Name'), field: 'name', formatter: (row, cell, value, columnDef, dataContext) => this.renderName(row, cell, value, columnDef, dataContext), width: 200 , id: 'name' },
|
||||||
{ name: nls.localize('jobColumns.lastRun', 'Last Run'), field: 'lastRun', width: 150, id: 'lastRun' },
|
{ name: nls.localize('jobColumns.lastRun','Last Run'), field: 'lastRun', width: 120, id: 'lastRun' },
|
||||||
{ name: nls.localize('jobColumns.nextRun', 'Next Run'), field: 'nextRun', width: 150, id: 'nextRun' },
|
{ name: nls.localize('jobColumns.nextRun','Next Run'), field: 'nextRun', width: 120, id: 'nextRun' },
|
||||||
{ name: nls.localize('jobColumns.enabled', 'Enabled'), field: 'enabled', width: 70, id: 'enabled' },
|
{ name: nls.localize('jobColumns.enabled','Enabled'), field: 'enabled', width: 50, id: 'enabled' },
|
||||||
{ name: nls.localize('jobColumns.status', 'Status'), field: 'currentExecutionStatus', width: 60, id: 'currentExecutionStatus' },
|
{ name: nls.localize('jobColumns.status','Status'), field: 'currentExecutionStatus', width: 60, id: 'currentExecutionStatus' },
|
||||||
{ name: nls.localize('jobColumns.category', 'Category'), field: 'category', width: 150, id: 'category' },
|
{ name: nls.localize('jobColumns.category','Category'), field: 'category', width: 120, id: 'category' },
|
||||||
{ name: nls.localize('jobColumns.runnable', 'Runnable'), field: 'runnable', width: 50, id: 'runnable' },
|
{ name: nls.localize('jobColumns.runnable','Runnable'), field: 'runnable', width: 70, id: 'runnable' },
|
||||||
{ name: nls.localize('jobColumns.schedule', 'Schedule'), field: 'hasSchedule', width: 50, id: 'hasSchedule' },
|
{ name: nls.localize('jobColumns.schedule','Schedule'), field: 'hasSchedule', width: 60, id: 'hasSchedule' },
|
||||||
{ name: nls.localize('jobColumns.lastRunOutcome', 'Last Run Outcome'), field: 'lastRunOutcome', width: 150, id: 'lastRunOutcome' },
|
{ name: nls.localize('jobColumns.lastRunOutcome', 'Last Run Outcome'), field: 'lastRunOutcome', width: 120, id: 'lastRunOutcome' },
|
||||||
|
{ name: nls.localize('jobColumns.previousRuns', 'Previous Runs'), formatter: this.renderChartsPostHistory, field: 'previousRuns', width: 80, id: 'previousRuns'}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
private options: Slick.GridOptions<any> = {
|
||||||
|
syncColumnCellResize: true,
|
||||||
|
enableColumnReorder: false,
|
||||||
|
rowHeight: 45,
|
||||||
|
enableCellNavigation: true,
|
||||||
|
editable: true
|
||||||
|
};
|
||||||
|
|
||||||
private rowDetail: RowDetailView;
|
private rowDetail: RowDetailView;
|
||||||
private dataView: Slick.Data.DataView<any>;
|
private filterPlugin: any;
|
||||||
|
private dataView: any;
|
||||||
|
|
||||||
@ViewChild('jobsgrid') _gridEl: ElementRef;
|
@ViewChild('jobsgrid') _gridEl: ElementRef;
|
||||||
private isVisible: boolean = false;
|
private isVisible: boolean = false;
|
||||||
private isInitialized: boolean = false;
|
private isInitialized: boolean = false;
|
||||||
|
private isRefreshing: boolean = false;
|
||||||
private _table: Table<any>;
|
private _table: Table<any>;
|
||||||
public jobs: sqlops.AgentJobInfo[];
|
public jobs: sqlops.AgentJobInfo[];
|
||||||
public jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = Object.create(null);
|
public jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = Object.create(null);
|
||||||
@@ -68,13 +80,18 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
private _isCloud: boolean;
|
private _isCloud: boolean;
|
||||||
private _showProgressWheel: boolean;
|
private _showProgressWheel: boolean;
|
||||||
private _tabHeight: number;
|
private _tabHeight: number;
|
||||||
|
private filterStylingMap: { [columnName: string]: [any] ;} = {};
|
||||||
|
private filterStack = ['start'];
|
||||||
|
private filterValueMap: { [columnName: string]: string[] ;} = {};
|
||||||
|
private sortingStylingMap: { [columnName: string]: any; } = {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface,
|
@Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface,
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
||||||
@Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent,
|
@Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent,
|
||||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService
|
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||||
|
@Inject(IThemeService) private _themeService: IThemeService
|
||||||
) {
|
) {
|
||||||
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
|
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
|
||||||
this._serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
this._serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
||||||
@@ -111,10 +128,14 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
} else if (this.isVisible === true && this._agentViewComponent.refresh === true) {
|
} else if (this.isVisible === true && this._agentViewComponent.refresh === true) {
|
||||||
this._showProgressWheel = true;
|
this._showProgressWheel = true;
|
||||||
this.onFirstVisible(false);
|
this.onFirstVisible(false);
|
||||||
|
this.isRefreshing = true;
|
||||||
this._agentViewComponent.refresh = false;
|
this._agentViewComponent.refresh = false;
|
||||||
} else if (this.isVisible === true && this._agentViewComponent.refresh === false) {
|
} else if (this.isVisible === true && this._agentViewComponent.refresh === false) {
|
||||||
this._showProgressWheel = true;
|
if (!this.isRefreshing) {
|
||||||
this.onFirstVisible(true);
|
this._showProgressWheel = true;
|
||||||
|
this.onFirstVisible(true);
|
||||||
|
}
|
||||||
|
this.isRefreshing = false;
|
||||||
} else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) {
|
} else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) {
|
||||||
this.isVisible = false;
|
this.isVisible = false;
|
||||||
}
|
}
|
||||||
@@ -129,7 +150,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
let options = <Slick.GridOptions<any>>{
|
let options = <Slick.GridOptions<any>>{
|
||||||
syncColumnCellResize: true,
|
syncColumnCellResize: true,
|
||||||
enableColumnReorder: false,
|
enableColumnReorder: false,
|
||||||
rowHeight: 45,
|
rowHeight: ROW_HEIGHT,
|
||||||
enableCellNavigation: true,
|
enableCellNavigation: true,
|
||||||
forceFitColumns: true
|
forceFitColumns: true
|
||||||
};
|
};
|
||||||
@@ -147,9 +168,11 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
panelRows: 1
|
panelRows: 1
|
||||||
});
|
});
|
||||||
this.rowDetail = rowDetail;
|
this.rowDetail = rowDetail;
|
||||||
|
|
||||||
columns.unshift(this.rowDetail.getColumnDefinition());
|
columns.unshift(this.rowDetail.getColumnDefinition());
|
||||||
this._table = new Table(this._gridEl.nativeElement, undefined, columns, options);
|
let filterPlugin = new HeaderFilter({}, this._themeService);
|
||||||
|
this.filterPlugin = filterPlugin;
|
||||||
|
$(this._gridEl.nativeElement).empty();
|
||||||
|
this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options);
|
||||||
this._table.grid.setData(this.dataView, true);
|
this._table.grid.setData(this.dataView, true);
|
||||||
this._table.grid.onClick.subscribe((e, args) => {
|
this._table.grid.onClick.subscribe((e, args) => {
|
||||||
let job = self.getJob(args);
|
let job = self.getJob(args);
|
||||||
@@ -158,7 +181,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
self._agentViewComponent.showHistory = true;
|
self._agentViewComponent.showHistory = true;
|
||||||
});
|
});
|
||||||
if (cached && this._agentViewComponent.refresh !== true) {
|
if (cached && this._agentViewComponent.refresh !== true) {
|
||||||
this.onJobsAvailable(this._jobCacheObject.jobs);
|
this.onJobsAvailable(null);
|
||||||
} else {
|
} else {
|
||||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
||||||
this._jobManagementService.getJobs(ownerUri).then((result) => {
|
this._jobManagementService.getJobs(ownerUri).then((result) => {
|
||||||
@@ -172,50 +195,137 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onJobsAvailable(jobs: sqlops.AgentJobInfo[]) {
|
private onJobsAvailable(jobs: sqlops.AgentJobInfo[]) {
|
||||||
let jobViews = jobs.map((job) => {
|
let jobViews: any;
|
||||||
return {
|
let start: boolean = true;
|
||||||
id: job.jobId,
|
if (!jobs) {
|
||||||
jobId: job.jobId,
|
let dataView = this._jobCacheObject.dataView;
|
||||||
name: job.name,
|
jobViews = dataView.getItems();
|
||||||
lastRun: AgentJobUtilities.convertToLastRun(job.lastRun),
|
start = false;
|
||||||
nextRun: AgentJobUtilities.convertToNextRun(job.nextRun),
|
} else {
|
||||||
enabled: AgentJobUtilities.convertToResponse(job.enabled),
|
jobViews = jobs.map((job) => {
|
||||||
currentExecutionStatus: AgentJobUtilities.convertToExecutionStatusString(job.currentExecutionStatus),
|
return {
|
||||||
category: job.category,
|
id: job.jobId,
|
||||||
runnable: AgentJobUtilities.convertToResponse(job.runnable),
|
jobId: job.jobId,
|
||||||
hasSchedule: AgentJobUtilities.convertToResponse(job.hasSchedule),
|
name: job.name,
|
||||||
lastRunOutcome: AgentJobUtilities.convertToStatusString(job.lastRunOutcome)
|
lastRun: AgentJobUtilities.convertToLastRun(job.lastRun),
|
||||||
};
|
nextRun: AgentJobUtilities.convertToNextRun(job.nextRun),
|
||||||
});
|
enabled: AgentJobUtilities.convertToResponse(job.enabled),
|
||||||
|
currentExecutionStatus: AgentJobUtilities.convertToExecutionStatusString(job.currentExecutionStatus),
|
||||||
|
category: job.category,
|
||||||
|
runnable: AgentJobUtilities.convertToResponse(job.runnable),
|
||||||
|
hasSchedule: AgentJobUtilities.convertToResponse(job.hasSchedule),
|
||||||
|
lastRunOutcome: AgentJobUtilities.convertToStatusString(job.lastRunOutcome)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
this._table.registerPlugin(<any>this.rowDetail);
|
this._table.registerPlugin(<any>this.rowDetail);
|
||||||
|
this.filterPlugin.onFilterApplied.subscribe((e, args) => {
|
||||||
|
this.dataView.refresh();
|
||||||
|
this._table.grid.resetActiveCell();
|
||||||
|
let filterValues = args.column.filterValues;
|
||||||
|
if (filterValues) {
|
||||||
|
if (filterValues.length === 0) {
|
||||||
|
// if an associated styling exists with the current filters
|
||||||
|
if (this.filterStylingMap[args.column.name]) {
|
||||||
|
let filterLength = this.filterStylingMap[args.column.name].length;
|
||||||
|
// then remove the filtered styling
|
||||||
|
for (let i = 0; i < filterLength; i++) {
|
||||||
|
let lastAppliedStyle = this.filterStylingMap[args.column.name].pop();
|
||||||
|
this._table.grid.removeCellCssStyles(lastAppliedStyle[0]);
|
||||||
|
}
|
||||||
|
delete this.filterStylingMap[args.column.name];
|
||||||
|
let index = this.filterStack.indexOf(args.column.name, 0);
|
||||||
|
if (index > -1) {
|
||||||
|
this.filterStack.splice(index, 1);
|
||||||
|
delete this.filterValueMap[args.column.name];
|
||||||
|
}
|
||||||
|
// apply the previous filter styling
|
||||||
|
let currentItems = this.dataView.getFilteredItems();
|
||||||
|
let styledItems = this.filterValueMap[this.filterStack[this.filterStack.length-1]][1];
|
||||||
|
if (styledItems === currentItems) {
|
||||||
|
let lastColStyle = this.filterStylingMap[this.filterStack[this.filterStack.length-1]];
|
||||||
|
for (let i = 0; i < lastColStyle.length; i++) {
|
||||||
|
this._table.grid.setCellCssStyles(lastColStyle[i][0], lastColStyle[i][1]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// style it all over again
|
||||||
|
let seenJobs = 0;
|
||||||
|
for (let i = 0; i < currentItems.length; i++) {
|
||||||
|
this._table.grid.removeCellCssStyles('error-row'+i.toString());
|
||||||
|
let item = this.dataView.getFilteredItems()[i];
|
||||||
|
if (item.lastRunOutcome === 'Failed') {
|
||||||
|
this.addToStyleHash(seenJobs, false, this.filterStylingMap, args.column.name);
|
||||||
|
if (this.filterStack.indexOf(args.column.name) < 0) {
|
||||||
|
this.filterStack.push(args.column.name);
|
||||||
|
this.filterValueMap[args.column.name] = [filterValues];
|
||||||
|
}
|
||||||
|
// one expansion for the row and one for
|
||||||
|
// the error detail
|
||||||
|
seenJobs ++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
seenJobs++;
|
||||||
|
}
|
||||||
|
this.dataView.refresh();
|
||||||
|
this.filterValueMap[args.column.name].push(this.dataView.getFilteredItems());
|
||||||
|
this._table.grid.resetActiveCell();
|
||||||
|
}
|
||||||
|
if (this.filterStack.length === 0) {
|
||||||
|
this.filterStack = ['start'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let seenJobs = 0;
|
||||||
|
for (let i = 0; i < this.jobs.length; i++) {
|
||||||
|
this._table.grid.removeCellCssStyles('error-row'+i.toString());
|
||||||
|
let item = this.dataView.getItemByIdx(i);
|
||||||
|
// current filter
|
||||||
|
if (_.contains(filterValues, item[args.column.field])) {
|
||||||
|
// check all previous filters
|
||||||
|
if (this.checkPreviousFilters(item)) {
|
||||||
|
if (item.lastRunOutcome === 'Failed') {
|
||||||
|
this.addToStyleHash(seenJobs, false, this.filterStylingMap, args.column.name);
|
||||||
|
if (this.filterStack.indexOf(args.column.name) < 0) {
|
||||||
|
this.filterStack.push(args.column.name);
|
||||||
|
this.filterValueMap[args.column.name] = [filterValues];
|
||||||
|
}
|
||||||
|
// one expansion for the row and one for
|
||||||
|
// the error detail
|
||||||
|
seenJobs ++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
seenJobs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.dataView.refresh();
|
||||||
|
if (this.filterValueMap[args.column.name]) {
|
||||||
|
this.filterValueMap[args.column.name].push(this.dataView.getFilteredItems());
|
||||||
|
} else {
|
||||||
|
this.filterValueMap[args.column.name] = this.dataView.getFilteredItems();
|
||||||
|
}
|
||||||
|
|
||||||
this.rowDetail.onBeforeRowDetailToggle.subscribe(function (e, args) {
|
this._table.grid.resetActiveCell();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.expandJobs(false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.rowDetail.onAfterRowDetailToggle.subscribe(function (e, args) {
|
this.filterPlugin.onCommand.subscribe((e, args: any) => {
|
||||||
});
|
this.columnSort(args.column.name, args.command === 'sort-asc');
|
||||||
this.rowDetail.onAsyncEndUpdate.subscribe(function (e, args) {
|
|
||||||
});
|
});
|
||||||
|
this._table.registerPlugin(<HeaderFilter>this.filterPlugin);
|
||||||
|
|
||||||
this.dataView.beginUpdate();
|
this.dataView.beginUpdate();
|
||||||
this.dataView.setItems(jobViews);
|
this.dataView.setItems(jobViews);
|
||||||
|
this.dataView.setFilter((item) => this.filter(item));
|
||||||
|
|
||||||
this.dataView.endUpdate();
|
this.dataView.endUpdate();
|
||||||
this._table.autosizeColumns();
|
this._table.autosizeColumns();
|
||||||
this._table.resizeCanvas();
|
this._table.resizeCanvas();
|
||||||
let expandedJobs = this._agentViewComponent.expanded;
|
|
||||||
let expansions = 0;
|
|
||||||
for (let i = 0; i < jobs.length; i++) {
|
|
||||||
let job = jobs[i];
|
|
||||||
if (job.lastRunOutcome === 0 && !expandedJobs.get(job.jobId)) {
|
|
||||||
this.expandJobRowDetails(i + expandedJobs.size);
|
|
||||||
this.addToStyleHash(i + expandedJobs.size);
|
|
||||||
this._agentViewComponent.setExpanded(job.jobId, 'Loading Error...');
|
|
||||||
} else if (job.lastRunOutcome === 0 && expandedJobs.get(job.jobId)) {
|
|
||||||
this.expandJobRowDetails(i + expansions);
|
|
||||||
this.addToStyleHash(i + expansions);
|
|
||||||
expansions++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this.expandJobs(start);
|
||||||
|
// tooltip for job name
|
||||||
$('.jobview-jobnamerow').hover(e => {
|
$('.jobview-jobnamerow').hover(e => {
|
||||||
let currentTarget = e.currentTarget;
|
let currentTarget = e.currentTarget;
|
||||||
currentTarget.title = currentTarget.innerText;
|
currentTarget.title = currentTarget.innerText;
|
||||||
@@ -244,7 +354,56 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
$('#jobsDiv .jobview-grid .slick-cell.l1.r1 .jobview-jobnametext').css('width', `${nameWidth - 10}px`);
|
$('#jobsDiv .jobview-grid .slick-cell.l1.r1 .jobview-jobnametext').css('width', `${nameWidth - 10}px`);
|
||||||
// adjust error message when resized
|
// adjust error message when resized
|
||||||
$('#jobsDiv .jobview-grid .slick-cell.l1.r1.error-row .jobview-jobnametext').css('width', '100%');
|
$('#jobsDiv .jobview-grid .slick-cell.l1.r1.error-row .jobview-jobnametext').css('width', '100%');
|
||||||
|
|
||||||
|
// generate job charts again
|
||||||
|
self.jobs.forEach(job => {
|
||||||
|
let jobId = job.jobId;
|
||||||
|
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
|
||||||
|
let previousRuns = jobHistories.slice(jobHistories.length-5, jobHistories.length);
|
||||||
|
self.createJobChart(job.jobId, previousRuns);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
$('#jobsDiv .jobview-grid .monaco-table .slick-viewport .grid-canvas .ui-widget-content.slick-row').hover((e) => {
|
||||||
|
// highlight the error row as well if a failing job row is hovered
|
||||||
|
if (e.currentTarget.children.item(0).classList.contains('job-with-error')) {
|
||||||
|
let target = $(e.currentTarget);
|
||||||
|
let targetChildren = $(e.currentTarget.children);
|
||||||
|
let siblings = target.nextAll().toArray();
|
||||||
|
let top = parseInt(target.css('top'), 10);
|
||||||
|
for (let i = 0; i < siblings.length; i++) {
|
||||||
|
let sibling = siblings[i];
|
||||||
|
let siblingTop = parseInt($(sibling).css('top'), 10);
|
||||||
|
if (siblingTop === top + ROW_HEIGHT) {
|
||||||
|
$(sibling.children).addClass('hovered');
|
||||||
|
sibling.onmouseenter = (e) => {
|
||||||
|
targetChildren.addClass('hovered');
|
||||||
|
};
|
||||||
|
sibling.onmouseleave = (e) => {
|
||||||
|
targetChildren.removeClass('hovered');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (e) => {
|
||||||
|
// switch back to original background
|
||||||
|
if (e.currentTarget.children.item(0).classList.contains('job-with-error')) {
|
||||||
|
let target = $(e.currentTarget);
|
||||||
|
let siblings = target.nextAll().toArray();
|
||||||
|
let top = parseInt(target.css('top'), 10);
|
||||||
|
for (let i = 0; i < siblings.length; i++) {
|
||||||
|
let sibling = siblings[i];
|
||||||
|
let siblingTop = parseInt($(sibling).css('top'), 10);
|
||||||
|
if (siblingTop === top + ROW_HEIGHT) {
|
||||||
|
$(sibling.children).removeClass('hovered');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// cache the dataview for future use
|
||||||
|
this._jobCacheObject.dataView = this.dataView;
|
||||||
|
this.filterValueMap['start'] = [[], this.dataView.getItems()];
|
||||||
this.loadJobHistories();
|
this.loadJobHistories();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,20 +420,34 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
'category': errorClass,
|
'category': errorClass,
|
||||||
'runnable': errorClass,
|
'runnable': errorClass,
|
||||||
'hasSchedule': errorClass,
|
'hasSchedule': errorClass,
|
||||||
'lastRunOutcome': errorClass
|
'lastRunOutcome': errorClass,
|
||||||
|
'previousRuns': errorClass
|
||||||
};
|
};
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
private addToStyleHash(row: number) {
|
private addToStyleHash(row: number, start: boolean, map: any, columnName: string) {
|
||||||
let hash: {
|
let hash : {
|
||||||
[index: number]: {
|
[index: number]: {
|
||||||
[id: string]: string;
|
[id: string]: string;
|
||||||
}
|
}
|
||||||
} = {};
|
} = {};
|
||||||
hash = this.setRowWithErrorClass(hash, row, 'job-with-error');
|
hash = this.setRowWithErrorClass(hash, row, 'job-with-error');
|
||||||
hash = this.setRowWithErrorClass(hash, row + 1, 'error-row');
|
hash = this.setRowWithErrorClass(hash, row+1, 'error-row');
|
||||||
this._table.grid.setCellCssStyles('error-row' + row.toString(), hash);
|
if (start) {
|
||||||
|
if (map['start']) {
|
||||||
|
map['start'].push(['error-row'+row.toString(), hash]);
|
||||||
|
} else {
|
||||||
|
map['start'] = [['error-row'+row.toString(), hash]];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (map[columnName]) {
|
||||||
|
map[columnName].push(['error-row'+row.toString(), hash]);
|
||||||
|
} else {
|
||||||
|
map[columnName] = [['error-row'+row.toString(), hash]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._table.grid.setCellCssStyles('error-row'+row.toString(), hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderName(row, cell, value, columnDef, dataContext) {
|
private renderName(row, cell, value, columnDef, dataContext) {
|
||||||
@@ -303,6 +476,17 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
'</tr></table>';
|
'</tr></table>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private renderChartsPostHistory(row, cell, value, columnDef, dataContext) {
|
||||||
|
return `<table class="jobprevruns" id="${dataContext.id}">
|
||||||
|
<tr>
|
||||||
|
<td><div class="bar1"></div></td>
|
||||||
|
<td><div class="bar2"></div></td>
|
||||||
|
<td><div class="bar3"></div></td>
|
||||||
|
<td><div class="bar4"></div></td>
|
||||||
|
</tr>
|
||||||
|
</table>`;
|
||||||
|
}
|
||||||
|
|
||||||
private expandJobRowDetails(rowIdx: number, message?: string): void {
|
private expandJobRowDetails(rowIdx: number, message?: string): void {
|
||||||
let item = this.dataView.getItemByIdx(rowIdx);
|
let item = this.dataView.getItemByIdx(rowIdx);
|
||||||
item.message = this._agentViewComponent.expanded.get(item.jobId);
|
item.message = this._agentViewComponent.expanded.get(item.jobId);
|
||||||
@@ -336,6 +520,17 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
return [failing, nonFailing];
|
return [failing, nonFailing];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private checkPreviousFilters(item): boolean {
|
||||||
|
for (let column in this.filterValueMap) {
|
||||||
|
if (column !== 'start' && this.filterValueMap[column][0].length > 0) {
|
||||||
|
if (!_.contains(this.filterValueMap[column][0], item[AgentJobUtilities.convertColNameToField(column)])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private isErrorRow(cell: HTMLElement) {
|
private isErrorRow(cell: HTMLElement) {
|
||||||
return cell.classList.contains('error-row');
|
return cell.classList.contains('error-row');
|
||||||
}
|
}
|
||||||
@@ -361,11 +556,14 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
if (result && result.jobs) {
|
if (result && result.jobs) {
|
||||||
self.jobHistories[job.jobId] = result.jobs;
|
self.jobHistories[job.jobId] = result.jobs;
|
||||||
self._jobCacheObject.setJobHistory(job.jobId, result.jobs);
|
self._jobCacheObject.setJobHistory(job.jobId, result.jobs);
|
||||||
|
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
|
||||||
|
let previousRuns = jobHistories.slice(jobHistories.length-5, jobHistories.length);
|
||||||
|
self.createJobChart(job.jobId, previousRuns);
|
||||||
if (self._agentViewComponent.expanded.has(job.jobId)) {
|
if (self._agentViewComponent.expanded.has(job.jobId)) {
|
||||||
let jobHistory = self._jobCacheObject.getJobHistory(job.jobId)[result.jobs.length - 1];
|
let lastJobHistory = jobHistories[result.jobs.length-1];
|
||||||
let item = self.dataView.getItemById(job.jobId + '.error');
|
let item = self.dataView.getItemById(job.jobId + '.error');
|
||||||
let noStepsMessage = nls.localize('jobsView.noSteps', 'No Steps available for this job.');
|
let noStepsMessage = nls.localize('jobsView.noSteps', 'No Steps available for this job.');
|
||||||
let errorMessage = jobHistory ? jobHistory.message : noStepsMessage;
|
let errorMessage = lastJobHistory ? lastJobHistory.message: noStepsMessage;
|
||||||
item['name'] = nls.localize('jobsView.error', 'Error: ') + errorMessage;
|
item['name'] = nls.localize('jobsView.error', 'Error: ') + errorMessage;
|
||||||
self._agentViewComponent.setExpanded(job.jobId, item['name']);
|
self._agentViewComponent.setExpanded(job.jobId, item['name']);
|
||||||
self.dataView.updateItem(job.jobId + '.error', item);
|
self.dataView.updateItem(job.jobId + '.error', item);
|
||||||
@@ -374,4 +572,216 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createJobChart(jobId: string, jobHistories: sqlops.AgentJobHistoryInfo[]): void {
|
||||||
|
let chartHeights = this.getChartHeights(jobHistories);
|
||||||
|
for (let i = 0; i < jobHistories.length; i++) {
|
||||||
|
let runGraph = $(`table#${jobId}.jobprevruns > tbody > tr > td > div.bar${i+1}`);
|
||||||
|
if (jobHistories && jobHistories.length > 0) {
|
||||||
|
runGraph.css('height', chartHeights[i]);
|
||||||
|
let bgColor = jobHistories[i].runStatus === 0 ? 'red' : 'green';
|
||||||
|
runGraph.css('background', bgColor);
|
||||||
|
runGraph.hover((e) => {
|
||||||
|
let currentTarget = e.currentTarget;
|
||||||
|
currentTarget.title = jobHistories[i].runDuration;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
runGraph.css('height', '5px');
|
||||||
|
runGraph.css('background', 'red');
|
||||||
|
runGraph.hover((e) => {
|
||||||
|
let currentTarget = e.currentTarget;
|
||||||
|
currentTarget.title = 'Job not run.';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// chart height normalization logic
|
||||||
|
private getChartHeights(jobHistories: sqlops.AgentJobHistoryInfo[]): string[] {
|
||||||
|
if (!jobHistories || jobHistories.length === 0) {
|
||||||
|
return ['5px','5px','5px','5px','5px'];
|
||||||
|
}
|
||||||
|
let maxDuration: number = 0;
|
||||||
|
jobHistories.forEach(history => {
|
||||||
|
let historyDuration = AgentJobUtilities.convertDurationToSeconds(history.runDuration) ;
|
||||||
|
if (historyDuration > maxDuration) {
|
||||||
|
maxDuration = historyDuration;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
maxDuration = maxDuration === 0 ? 1 : maxDuration;
|
||||||
|
let maxBarHeight: number = 24;
|
||||||
|
let chartHeights = [];
|
||||||
|
for (let i = 0; i < jobHistories.length; i++) {
|
||||||
|
let duration = jobHistories[i].runDuration;
|
||||||
|
let chartHeight = (maxBarHeight * AgentJobUtilities.convertDurationToSeconds(duration))/maxDuration;
|
||||||
|
chartHeights.push(`${chartHeight}px`);
|
||||||
|
}
|
||||||
|
return chartHeights;
|
||||||
|
}
|
||||||
|
|
||||||
|
private expandJobs(start: boolean): void {
|
||||||
|
if (start) {
|
||||||
|
this._agentViewComponent.expanded = new Map<string, string>();
|
||||||
|
}
|
||||||
|
let expandedJobs = this._agentViewComponent.expanded;
|
||||||
|
let expansions = 0;
|
||||||
|
for (let i = 0; i < this.jobs.length; i++){
|
||||||
|
let job = this.jobs[i];
|
||||||
|
if (job.lastRunOutcome === 0 && !expandedJobs.get(job.jobId)) {
|
||||||
|
this.expandJobRowDetails(i+expandedJobs.size);
|
||||||
|
this.addToStyleHash(i+expandedJobs.size, start, this.filterStylingMap, undefined);
|
||||||
|
this._agentViewComponent.setExpanded(job.jobId, 'Loading Error...');
|
||||||
|
} else if (job.lastRunOutcome === 0 && expandedJobs.get(job.jobId)) {
|
||||||
|
this.addToStyleHash(i+expansions, start, this.filterStylingMap, undefined);
|
||||||
|
expansions++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private filter(item: any) {
|
||||||
|
let columns = this._table.grid.getColumns();
|
||||||
|
let value = true;
|
||||||
|
for (let i = 0; i < columns.length; i++) {
|
||||||
|
let col: any = columns[i];
|
||||||
|
let filterValues = col.filterValues;
|
||||||
|
if (filterValues && filterValues.length > 0) {
|
||||||
|
if (item._parent) {
|
||||||
|
value = value && _.contains(filterValues, item._parent[col.field]);
|
||||||
|
} else {
|
||||||
|
value = value && _.contains(filterValues, item[col.field]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private columnSort(column: string, isAscending: boolean) {
|
||||||
|
let items = this.dataView.getItems();
|
||||||
|
// get error items here and remove them
|
||||||
|
let jobItems = items.filter(x => x._parent === undefined);
|
||||||
|
let errorItems = items.filter(x => x._parent !== undefined);
|
||||||
|
this.sortingStylingMap[column] = items;
|
||||||
|
switch(column) {
|
||||||
|
case('Name'): {
|
||||||
|
this.dataView.setItems(jobItems);
|
||||||
|
// sort the actual jobs
|
||||||
|
this.dataView.sort((item1, item2) => {
|
||||||
|
return item1.name.localeCompare(item2.name);
|
||||||
|
}, isAscending);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case('Last Run'): {
|
||||||
|
this.dataView.setItems(jobItems);
|
||||||
|
// sort the actual jobs
|
||||||
|
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, true), isAscending);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ('Next Run') : {
|
||||||
|
this.dataView.setItems(jobItems);
|
||||||
|
// sort the actual jobs
|
||||||
|
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, false), isAscending);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ('Enabled'): {
|
||||||
|
this.dataView.setItems(jobItems);
|
||||||
|
// sort the actual jobs
|
||||||
|
this.dataView.sort((item1, item2) => {
|
||||||
|
return item1.enabled.localeCompare(item2.enabled);
|
||||||
|
}, isAscending);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ('Status'): {
|
||||||
|
this.dataView.setItems(jobItems);
|
||||||
|
// sort the actual jobs
|
||||||
|
this.dataView.sort((item1, item2) => {
|
||||||
|
return item1.currentExecutionStatus.localeCompare(item2.currentExecutionStatus);
|
||||||
|
}, isAscending);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ('Category'): {
|
||||||
|
this.dataView.setItems(jobItems);
|
||||||
|
// sort the actual jobs
|
||||||
|
this.dataView.sort((item1, item2) => {
|
||||||
|
return item1.category.localeCompare(item2.category);
|
||||||
|
}, isAscending);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ('Runnable'): {
|
||||||
|
this.dataView.setItems(jobItems);
|
||||||
|
// sort the actual jobs
|
||||||
|
this.dataView.sort((item1, item2) => {
|
||||||
|
return item1.runnable.localeCompare(item2.runnable);
|
||||||
|
}, isAscending);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ('Schedule'): {
|
||||||
|
this.dataView.setItems(jobItems);
|
||||||
|
// sort the actual jobs
|
||||||
|
this.dataView.sort((item1, item2) => {
|
||||||
|
return item1.hasSchedule.localeCompare(item2.hasSchedule);
|
||||||
|
}, isAscending);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ('Last Run Outcome'): {
|
||||||
|
this.dataView.setItems(jobItems);
|
||||||
|
// sort the actual jobs
|
||||||
|
this.dataView.sort((item1, item2) => {
|
||||||
|
return item1.lastRunOutcome.localeCompare(item2.lastRunOutcome);
|
||||||
|
}, isAscending);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// insert the errors back again
|
||||||
|
let jobItemsLength = jobItems.length;
|
||||||
|
for (let i = 0; i < jobItemsLength; i++) {
|
||||||
|
let item = jobItems[i];
|
||||||
|
if (item._child) {
|
||||||
|
let child = errorItems.find(error => error === item._child);
|
||||||
|
jobItems.splice(i+1, 0, child);
|
||||||
|
jobItemsLength++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.dataView.setItems(jobItems);
|
||||||
|
// remove old style
|
||||||
|
if (this.filterStylingMap[column]) {
|
||||||
|
let filterLength = this.filterStylingMap[column].length;
|
||||||
|
for (let i = 0; i < filterLength; i++) {
|
||||||
|
let lastAppliedStyle = this.filterStylingMap[column].pop();
|
||||||
|
this._table.grid.removeCellCssStyles(lastAppliedStyle[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < this.jobs.length; i++) {
|
||||||
|
this._table.grid.removeCellCssStyles('error-row'+i.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add new style to the items back again
|
||||||
|
items = this.filterStack.length > 1 ? this.dataView.getFilteredItems() : this.dataView.getItems();
|
||||||
|
for (let i = 0; i < items.length; i ++) {
|
||||||
|
let item = items[i];
|
||||||
|
if (item.lastRunOutcome === 'Failed') {
|
||||||
|
this.addToStyleHash(i, false, this.sortingStylingMap, column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private dateCompare(item1: any, item2: any, lastRun: boolean): number {
|
||||||
|
let exceptionString = lastRun ? 'Never Run' : 'Not Scheduled';
|
||||||
|
if (item2.lastRun === exceptionString && item1.lastRun !== exceptionString) {
|
||||||
|
return -1;
|
||||||
|
} else if (item1.lastRun === exceptionString && item2.lastRun !== exceptionString) {
|
||||||
|
return 1;
|
||||||
|
} else if (item1.lastRun === exceptionString && item2.lastRun === exceptionString) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
let date1 = new Date(item1.lastRun);
|
||||||
|
let date2 = new Date(item2.lastRun);
|
||||||
|
if (date1 > date2) {
|
||||||
|
return 1;
|
||||||
|
} else if (date1 === date2) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } fro
|
|||||||
import { attachButtonStyler } from 'sql/common/theme/styler';
|
import { attachButtonStyler } from 'sql/common/theme/styler';
|
||||||
import { Button } from 'sql/base/browser/ui/button/button';
|
import { Button } from 'sql/base/browser/ui/button/button';
|
||||||
|
|
||||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
import { SIDE_BAR_BACKGROUND, SIDE_BAR_TITLE_FOREGROUND } from 'vs/workbench/common/theme';
|
||||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||||
import URI from 'vs/base/common/uri';
|
import URI from 'vs/base/common/uri';
|
||||||
@@ -55,13 +55,11 @@ export default class ButtonComponent extends ComponentBase implements IComponent
|
|||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
if (this._inputContainer) {
|
if (this._inputContainer) {
|
||||||
|
|
||||||
|
|
||||||
this._button = new Button(this._inputContainer.nativeElement);
|
this._button = new Button(this._inputContainer.nativeElement);
|
||||||
|
|
||||||
this._register(this._button);
|
this._register(this._button);
|
||||||
this._register(attachButtonStyler(this._button, this.themeService, {
|
this._register(attachButtonStyler(this._button, this.themeService, {
|
||||||
buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND
|
buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND, buttonForeground: SIDE_BAR_TITLE_FOREGROUND
|
||||||
}));
|
}));
|
||||||
this._register(this._button.onDidClick(e => {
|
this._register(this._button.onDidClick(e => {
|
||||||
this._onEventEmitter.fire({
|
this._onEventEmitter.fire({
|
||||||
@@ -94,6 +92,12 @@ export default class ButtonComponent extends ComponentBase implements IComponent
|
|||||||
super.setProperties(properties);
|
super.setProperties(properties);
|
||||||
this._button.enabled = this.enabled;
|
this._button.enabled = this.enabled;
|
||||||
this._button.label = this.label;
|
this._button.label = this.label;
|
||||||
|
if (this.width) {
|
||||||
|
this._button.setWidth(this.convertSize(this.width.toString()));
|
||||||
|
}
|
||||||
|
if (this.height) {
|
||||||
|
this._button.setWidth(this.convertSize(this.height.toString()));
|
||||||
|
}
|
||||||
this.updateIcon();
|
this.updateIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { attachInputBoxStyler, attachListStyler } from 'vs/platform/theme/common
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'modelview-checkbox',
|
selector: 'modelview-checkbox',
|
||||||
template: `
|
template: `
|
||||||
<div #input style="width: 100%"></div>
|
<div #input [style.width]="getWidth()"></div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export default class CheckBoxComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
export default class CheckBoxComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||||
@@ -50,7 +50,7 @@ export default class CheckBoxComponent extends ComponentBase implements ICompone
|
|||||||
|
|
||||||
this._register(this._input);
|
this._register(this._input);
|
||||||
this._register(this._input.onChange(e => {
|
this._register(this._input.onChange(e => {
|
||||||
this.value = this._input.checked;
|
this.checked = this._input.checked;
|
||||||
this._onEventEmitter.fire({
|
this._onEventEmitter.fire({
|
||||||
eventType: ComponentEventType.onDidChange,
|
eventType: ComponentEventType.onDidChange,
|
||||||
args: e
|
args: e
|
||||||
@@ -88,10 +88,10 @@ export default class CheckBoxComponent extends ComponentBase implements ICompone
|
|||||||
// CSS-bound properties
|
// CSS-bound properties
|
||||||
|
|
||||||
public get checked(): boolean {
|
public get checked(): boolean {
|
||||||
return this.getPropertyOrDefault<sqlops.CheckBoxProperties, boolean>((props) => props.value, false);
|
return this.getPropertyOrDefault<sqlops.CheckBoxProperties, boolean>((props) => props.checked, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public set value(newValue: boolean) {
|
public set checked(newValue: boolean) {
|
||||||
this.setPropertyFromUI<sqlops.CheckBoxProperties, boolean>((properties, value) => { properties.checked = value; }, newValue);
|
this.setPropertyFromUI<sqlops.CheckBoxProperties, boolean>((properties, value) => { properties.checked = value; }, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import * as types from 'vs/base/common/types';
|
import * as types from 'vs/base/common/types';
|
||||||
|
|
||||||
import { IComponent, IComponentDescriptor, IModelStore, IComponentEventArgs, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
import { IComponent, IComponentDescriptor, IModelStore, IComponentEventArgs, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||||
import { FlexLayout, FlexItemLayout } from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
|
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
|
||||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
@@ -112,6 +112,52 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
|||||||
this.setProperties(properties);
|
this.setProperties(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get height(): number | string {
|
||||||
|
return this.getPropertyOrDefault<sqlops.ComponentProperties, number | string>((props) => props.height, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set height(newValue: number | string) {
|
||||||
|
this.setPropertyFromUI<sqlops.ComponentProperties, number | string>((props, value) => props.height = value, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get width(): number | string {
|
||||||
|
return this.getPropertyOrDefault<sqlops.ComponentProperties, number | string>((props) => props.width, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set width(newValue: number | string) {
|
||||||
|
this.setPropertyFromUI<sqlops.ComponentProperties, number | string>((props, value) => props.width = value, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected convertSizeToNumber(size: number | string): number {
|
||||||
|
if (size && typeof (size) === 'string') {
|
||||||
|
if (size.toLowerCase().endsWith('px')) {
|
||||||
|
return +size.replace('px', '');
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return +size;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getWidth(): string {
|
||||||
|
return this.width ? this.convertSize(this.width) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getHeight(): string {
|
||||||
|
return this.height ? this.convertSize(this.height) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected convertSize(size: number | string, defaultValue?: string): string {
|
||||||
|
defaultValue = defaultValue || '';
|
||||||
|
if (types.isUndefinedOrNull(size)) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
let convertedSize: string = size ? size.toString() : defaultValue;
|
||||||
|
if (!convertedSize.toLowerCase().endsWith('px') && !convertedSize.toLowerCase().endsWith('%')) {
|
||||||
|
convertedSize = convertedSize + 'px';
|
||||||
|
}
|
||||||
|
return convertedSize;
|
||||||
|
}
|
||||||
|
|
||||||
public get valid(): boolean {
|
public get valid(): boolean {
|
||||||
return this._valid;
|
return this._valid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import GroupContainer from './groupContainer.component';
|
|||||||
import CardComponent from './card.component';
|
import CardComponent from './card.component';
|
||||||
import InputBoxComponent from './inputbox.component';
|
import InputBoxComponent from './inputbox.component';
|
||||||
import DropDownComponent from './dropdown.component';
|
import DropDownComponent from './dropdown.component';
|
||||||
|
import DeclarativeTableComponent from './declarativeTable.component';
|
||||||
|
import ListBoxComponent from './listbox.component';
|
||||||
import ButtonComponent from './button.component';
|
import ButtonComponent from './button.component';
|
||||||
import CheckBoxComponent from './checkbox.component';
|
import CheckBoxComponent from './checkbox.component';
|
||||||
import RadioButtonComponent from './radioButton.component';
|
import RadioButtonComponent from './radioButton.component';
|
||||||
@@ -41,6 +43,12 @@ registerComponentType(INPUTBOX_COMPONENT, ModelComponentTypes.InputBox, InputBox
|
|||||||
export const DROPDOWN_COMPONENT = 'dropdown-component';
|
export const DROPDOWN_COMPONENT = 'dropdown-component';
|
||||||
registerComponentType(DROPDOWN_COMPONENT, ModelComponentTypes.DropDown, DropDownComponent);
|
registerComponentType(DROPDOWN_COMPONENT, ModelComponentTypes.DropDown, DropDownComponent);
|
||||||
|
|
||||||
|
export const DECLARATIVETABLE_COMPONENT = 'declarativeTable-component';
|
||||||
|
registerComponentType(DECLARATIVETABLE_COMPONENT, ModelComponentTypes.DeclarativeTable, DeclarativeTableComponent);
|
||||||
|
|
||||||
|
export const LISTBOX_COMPONENT = 'lisbox-component';
|
||||||
|
registerComponentType(LISTBOX_COMPONENT, ModelComponentTypes.ListBox, ListBoxComponent);
|
||||||
|
|
||||||
export const BUTTON_COMPONENT = 'button-component';
|
export const BUTTON_COMPONENT = 'button-component';
|
||||||
registerComponentType(BUTTON_COMPONENT, ModelComponentTypes.Button, ButtonComponent);
|
registerComponentType(BUTTON_COMPONENT, ModelComponentTypes.Button, ButtonComponent);
|
||||||
|
|
||||||
|
|||||||
194
src/sql/parts/modelComponents/declarativeTable.component.ts
Normal file
194
src/sql/parts/modelComponents/declarativeTable.component.ts
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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!./declarativeTable';
|
||||||
|
import {
|
||||||
|
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||||
|
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
|
||||||
|
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||||
|
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||||
|
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
|
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
|
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
||||||
|
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
||||||
|
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||||
|
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
||||||
|
import * as nls from 'vs/nls';
|
||||||
|
|
||||||
|
export enum DeclarativeDataType {
|
||||||
|
string = 'string',
|
||||||
|
category = 'category',
|
||||||
|
boolean = 'boolean'
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'modelview-declarativeTable',
|
||||||
|
template: `
|
||||||
|
<table role=grid aria-labelledby="ID_REF" #container *ngIf="columns" class="declarative-table">
|
||||||
|
<ng-container *ngFor="let column of columns;let h = index">
|
||||||
|
<th class="declarative-table-header" tabindex="-1" role="button" aria-sort="none">{{column.displayName}}</th>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="data">
|
||||||
|
<ng-container *ngFor="let row of data;let r = index">
|
||||||
|
<tr class="declarative-table-row">
|
||||||
|
<ng-container *ngFor="let cellData of row;let c = index">
|
||||||
|
<td class="declarative-table-cell" tabindex="-1" role="button" [style.width]="getColumnWidth(c)">
|
||||||
|
<checkbox *ngIf="isCheckBox(c)" label="" (onChange)="onCheckBoxChanged($event,r,c)" [enabled]="isControlEnabled(c)" [checked]="isChecked(r,c)"></checkbox>
|
||||||
|
<select-box *ngIf="isSelectBox(c)" [options]="GetOptions(c)" (onDidSelect)="onSelectBoxChanged($event,r,c)" [selectedOption]="GetSelectedOptionDisplayName(r,c)"></select-box>
|
||||||
|
<input-box *ngIf="isInputBox(c)" [value]="cellData" (onDidChange)="onInputBoxChanged($event,r,c)"></input-box>
|
||||||
|
<ng-container *ngIf="isLabel(c)" >{{cellData}}</ng-container>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
</tr>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</table>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export default class DeclarativeTableComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||||
|
@Input() descriptor: IComponentDescriptor;
|
||||||
|
@Input() modelStore: IModelStore;
|
||||||
|
|
||||||
|
@ViewChild('container', { read: ElementRef }) private _tableContainer: ElementRef;
|
||||||
|
constructor(
|
||||||
|
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||||
|
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||||
|
@Inject(IContextViewService) private contextViewService: IContextViewService
|
||||||
|
) {
|
||||||
|
super(changeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.baseInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
public validate(): Thenable<boolean> {
|
||||||
|
return super.validate().then(valid => {
|
||||||
|
return valid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.baseDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private isCheckBox(cell: number): boolean {
|
||||||
|
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
|
||||||
|
return column.valueType === DeclarativeDataType.boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isControlEnabled(cell: number): boolean {
|
||||||
|
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
|
||||||
|
return !column.isReadOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isLabel(cell: number): boolean {
|
||||||
|
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
|
||||||
|
return column.isReadOnly && column.valueType === DeclarativeDataType.string;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isChecked(row: number, cell: number): boolean {
|
||||||
|
let cellData = this.data[row][cell];
|
||||||
|
return cellData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private onInputBoxChanged(e: string, row: number, cell: number): void {
|
||||||
|
this.onCellDataChanged(e, row, cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onCheckBoxChanged(e: boolean, row: number, cell: number): void {
|
||||||
|
this.onCellDataChanged(e, row, cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSelectBoxChanged(e: ISelectData, row: number, cell: number): void {
|
||||||
|
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
|
||||||
|
if (column.categoryValues) {
|
||||||
|
this.onCellDataChanged(column.categoryValues[e.index].name, row, cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onCellDataChanged(newValue: any, row: number, cell: number): void {
|
||||||
|
this.data[row][cell] = newValue;
|
||||||
|
this.data = this.data;
|
||||||
|
let newCellData : sqlops.TableCell = {
|
||||||
|
row: row,
|
||||||
|
column: cell,
|
||||||
|
value: newValue
|
||||||
|
};
|
||||||
|
this._onEventEmitter.fire({
|
||||||
|
eventType: ComponentEventType.onDidChange,
|
||||||
|
args: newCellData
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private isSelectBox(cell: number): boolean {
|
||||||
|
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
|
||||||
|
return column.valueType === DeclarativeDataType.category;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isInputBox(cell: number): boolean {
|
||||||
|
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
|
||||||
|
return column.valueType === DeclarativeDataType.string && !column.isReadOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getColumnWidth(cell: number): string {
|
||||||
|
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
|
||||||
|
return this.convertSize(column.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GetOptions(cell: number): string[] {
|
||||||
|
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
|
||||||
|
return column.categoryValues ? column.categoryValues.map(x => x.displayName) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private GetSelectedOptionDisplayName(row: number, cell: number): string {
|
||||||
|
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
|
||||||
|
let cellData = this.data[row][cell];
|
||||||
|
if (cellData && column.categoryValues) {
|
||||||
|
let category = column.categoryValues.find(v => v.name === cellData);
|
||||||
|
return category.displayName;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IComponent implementation
|
||||||
|
|
||||||
|
public layout(): void {
|
||||||
|
this._changeRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLayout(layout: any): void {
|
||||||
|
// TODO allow configuring the look and feel
|
||||||
|
this.layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setProperties(properties: { [key: string]: any; }): void {
|
||||||
|
super.setProperties(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): any[][] {
|
||||||
|
return this.getPropertyOrDefault<sqlops.DeclarativeTableProperties, any[]>((props) => props.data, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set data(newValue: any[][]) {
|
||||||
|
this.setPropertyFromUI<sqlops.DeclarativeTableProperties, any[][]>((props, value) => props.data = value, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get columns(): sqlops.DeclarativeTableColumn[] {
|
||||||
|
return this.getPropertyOrDefault<sqlops.DeclarativeTableProperties, sqlops.DeclarativeTableColumn[]>((props) => props.columns, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set columns(newValue: sqlops.DeclarativeTableColumn[]) {
|
||||||
|
this.setPropertyFromUI<sqlops.DeclarativeTableProperties, sqlops.DeclarativeTableColumn[]>((props, value) => props.columns = value, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/sql/parts/modelComponents/declarativeTable.css
Normal file
41
src/sql/parts/modelComponents/declarativeTable.css
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
.declarative-table {
|
||||||
|
padding: 5px 30px 0px 30px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width:100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.declarative-table-row {
|
||||||
|
}
|
||||||
|
|
||||||
|
.declarative-table-header {
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid gray;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs-dark .declarative-table-header {
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid gray;
|
||||||
|
background-color: #333334;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hc-black .declarative-table-header {
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid gray;
|
||||||
|
background-color: #333334;
|
||||||
|
}
|
||||||
|
|
||||||
|
.declarative-table-cell {
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role="gridcell"]:focus,
|
||||||
|
[role="gridcell"] *:focus,
|
||||||
|
[role="grid"] [tabindex="0"]:focus {
|
||||||
|
outline: #005a9c;
|
||||||
|
outline-style: dotted;
|
||||||
|
outline-width: 3px;
|
||||||
|
}
|
||||||
@@ -15,7 +15,8 @@ import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } fro
|
|||||||
import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/dropdown';
|
import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/dropdown';
|
||||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
|
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { attachEditableDropdownStyler , attachSelectBoxStyler} from 'sql/common/theme/styler';
|
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
|
||||||
|
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
||||||
|
|
||||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
@@ -26,7 +27,7 @@ import { attachListStyler } from 'vs/platform/theme/common/styler';
|
|||||||
selector: 'modelview-dropdown',
|
selector: 'modelview-dropdown',
|
||||||
template: `
|
template: `
|
||||||
|
|
||||||
<div>
|
<div [style.width]="getWidth()">
|
||||||
<div [style.display]="getEditableDisplay()" #editableDropDown style="width: 100%;"></div>
|
<div [style.display]="getEditableDisplay()" #editableDropDown style="width: 100%;"></div>
|
||||||
<div [style.display]="getNotEditableDisplay()" #dropDown style="width: 100%;"></div>
|
<div [style.display]="getNotEditableDisplay()" #dropDown style="width: 100%;"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,7 +69,7 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
|||||||
this._register(attachEditableDropdownStyler(this._editableDropdown, this.themeService));
|
this._register(attachEditableDropdownStyler(this._editableDropdown, this.themeService));
|
||||||
this._register(this._editableDropdown.onValueChange(e => {
|
this._register(this._editableDropdown.onValueChange(e => {
|
||||||
if (this.editable) {
|
if (this.editable) {
|
||||||
this.value = this._editableDropdown.value;
|
this.setSelectedValue(this._editableDropdown.value);
|
||||||
this._onEventEmitter.fire({
|
this._onEventEmitter.fire({
|
||||||
eventType: ComponentEventType.onDidChange,
|
eventType: ComponentEventType.onDidChange,
|
||||||
args: e
|
args: e
|
||||||
@@ -77,14 +78,14 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
if (this._dropDownContainer) {
|
if (this._dropDownContainer) {
|
||||||
this._selectBox = new SelectBox(this.values || [], this.value, this.contextViewService, this._dropDownContainer.nativeElement);
|
this._selectBox = new SelectBox(this.getValues(), this.getSelectedValue(), this.contextViewService, this._dropDownContainer.nativeElement);
|
||||||
this._selectBox.render(this._dropDownContainer.nativeElement);
|
this._selectBox.render(this._dropDownContainer.nativeElement);
|
||||||
this._register(this._selectBox);
|
this._register(this._selectBox);
|
||||||
|
|
||||||
this._register(attachSelectBoxStyler(this._selectBox, this.themeService));
|
this._register(attachSelectBoxStyler(this._selectBox, this.themeService));
|
||||||
this._register(this._selectBox.onDidSelect(e => {
|
this._register(this._selectBox.onDidSelect(e => {
|
||||||
if (!this.editable) {
|
if (!this.editable) {
|
||||||
this.value = this._selectBox.value;
|
this.setSelectedValue(this._selectBox.value);
|
||||||
this._onEventEmitter.fire({
|
this._onEventEmitter.fire({
|
||||||
eventType: ComponentEventType.onDidChange,
|
eventType: ComponentEventType.onDidChange,
|
||||||
args: e
|
args: e
|
||||||
@@ -112,14 +113,14 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
|||||||
public setProperties(properties: { [key: string]: any; }): void {
|
public setProperties(properties: { [key: string]: any; }): void {
|
||||||
super.setProperties(properties);
|
super.setProperties(properties);
|
||||||
if (this.editable) {
|
if (this.editable) {
|
||||||
this._editableDropdown.values = this.values ? this.values : [];
|
this._editableDropdown.values = this.getValues();
|
||||||
if (this.value) {
|
if (this.value) {
|
||||||
this._editableDropdown.value = this.value;
|
this._editableDropdown.value = this.getSelectedValue();
|
||||||
}
|
}
|
||||||
this._editableDropdown.enabled = this.enabled;
|
this._editableDropdown.enabled = this.enabled;
|
||||||
} else {
|
} else {
|
||||||
this._selectBox.setOptions(this.values || []);
|
this._selectBox.setOptions(this.getValues());
|
||||||
this._selectBox.selectWithOptionName(this.value);
|
this._selectBox.selectWithOptionName(this.getSelectedValue());
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
this._selectBox.enable();
|
this._selectBox.enable();
|
||||||
} else {
|
} else {
|
||||||
@@ -128,34 +129,75 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getValues(): string[] {
|
||||||
|
if (this.values && this.values.length > 0) {
|
||||||
|
if (!this.valuesHaveDisplayName()) {
|
||||||
|
return this.values as string[];
|
||||||
|
} else {
|
||||||
|
return (<sqlops.CategoryValue[]>this.values).map(v => v.displayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private valuesHaveDisplayName(): boolean {
|
||||||
|
return typeof (this.values[0]) !== 'string';
|
||||||
|
}
|
||||||
|
|
||||||
|
private getSelectedValue(): string {
|
||||||
|
if (this.values && this.values.length > 0 && this.valuesHaveDisplayName()) {
|
||||||
|
let selectedValue = <sqlops.CategoryValue>this.value || <sqlops.CategoryValue>this.values[0];
|
||||||
|
if (!this.value) {
|
||||||
|
this.value = selectedValue;
|
||||||
|
}
|
||||||
|
let valueCategory = (<sqlops.CategoryValue[]>this.values).find(v => v.name === selectedValue.name);
|
||||||
|
|
||||||
|
return valueCategory && valueCategory.displayName;
|
||||||
|
} else {
|
||||||
|
if (!this.value && this.values && this.values.length > 0) {
|
||||||
|
this.value = <string>this.values[0];
|
||||||
|
}
|
||||||
|
return <string>this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setSelectedValue(newValue: string): void {
|
||||||
|
if (this.values && this.valuesHaveDisplayName()) {
|
||||||
|
let valueCategory = (<sqlops.CategoryValue[]>this.values).find(v => v.displayName === newValue);
|
||||||
|
this.value = valueCategory;
|
||||||
|
} else {
|
||||||
|
this.value = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CSS-bound properties
|
// CSS-bound properties
|
||||||
|
|
||||||
private get value(): string {
|
private get value(): string | sqlops.CategoryValue {
|
||||||
return this.getPropertyOrDefault<sqlops.DropDownProperties, string>((props) => props.value, '');
|
return this.getPropertyOrDefault<sqlops.DropDownProperties, string | sqlops.CategoryValue>((props) => props.value, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
private get editable(): boolean {
|
private get editable(): boolean {
|
||||||
return this.getPropertyOrDefault<sqlops.DropDownProperties, boolean>((props) => props.editable, false);
|
return this.getPropertyOrDefault<sqlops.DropDownProperties, boolean>((props) => props.editable, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getEditableDisplay() : string {
|
public getEditableDisplay(): string {
|
||||||
return this.editable ? '' : 'none';
|
return this.editable ? '' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
public getNotEditableDisplay() : string {
|
public getNotEditableDisplay(): string {
|
||||||
return !this.editable ? '' : 'none';
|
return !this.editable ? '' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
private set value(newValue: string) {
|
private set value(newValue: string | sqlops.CategoryValue) {
|
||||||
this.setPropertyFromUI<sqlops.DropDownProperties, string>(this.setValueProperties, newValue);
|
this.setPropertyFromUI<sqlops.DropDownProperties, string | sqlops.CategoryValue>(this.setValueProperties, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private get values(): string[] {
|
private get values(): string[] | sqlops.CategoryValue[] {
|
||||||
return this.getPropertyOrDefault<sqlops.DropDownProperties, string[]>((props) => props.values, undefined);
|
return this.getPropertyOrDefault<sqlops.DropDownProperties, string[] | sqlops.CategoryValue[]>((props) => props.values, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
private set values(newValue: string[]) {
|
private set values(newValue: string[] | sqlops.CategoryValue[]) {
|
||||||
this.setPropertyFromUI<sqlops.DropDownProperties, string[]>(this.setValuesProperties, newValue);
|
this.setPropertyFromUI<sqlops.DropDownProperties, string[] | sqlops.CategoryValue[]>(this.setValuesProperties, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private setValueProperties(properties: sqlops.DropDownProperties, value: string): void {
|
private setValueProperties(properties: sqlops.DropDownProperties, value: string): void {
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import 'vs/css!./flexContainer';
|
import 'vs/css!./flexContainer';
|
||||||
|
|
||||||
import { Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
import {
|
||||||
|
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||||
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList,
|
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
@@ -18,14 +19,14 @@ import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentW
|
|||||||
import types = require('vs/base/common/types');
|
import types = require('vs/base/common/types');
|
||||||
|
|
||||||
class FlexItem {
|
class FlexItem {
|
||||||
constructor(public descriptor: IComponentDescriptor, public config: FlexItemLayout) {}
|
constructor(public descriptor: IComponentDescriptor, public config: FlexItemLayout) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<div *ngIf="items" class="flexContainer" [style.flexFlow]="flexFlow" [style.justifyContent]="justifyContent"
|
<div *ngIf="items" class="flexContainer" [style.flexFlow]="flexFlow" [style.justifyContent]="justifyContent"
|
||||||
[style.alignItems]="alignItems" [style.alignContent]="alignContent" [style.height]="height">
|
[style.alignItems]="alignItems" [style.alignContent]="alignContent" [style.height]="height" [style.width]="width">
|
||||||
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.order]="getItemOrder(item)" >
|
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.textAlign]="textAlign" [style.order]="getItemOrder(item)" >
|
||||||
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
|
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
|
||||||
</model-component-wrapper>
|
</model-component-wrapper>
|
||||||
</div>
|
</div>
|
||||||
@@ -39,7 +40,9 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
|
|||||||
private _justifyContent: string;
|
private _justifyContent: string;
|
||||||
private _alignItems: string;
|
private _alignItems: string;
|
||||||
private _alignContent: string;
|
private _alignContent: string;
|
||||||
|
private _textAlign: string;
|
||||||
private _height: string;
|
private _height: string;
|
||||||
|
private _width: string;
|
||||||
|
|
||||||
constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
|
constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
|
||||||
super(changeRef);
|
super(changeRef);
|
||||||
@@ -58,18 +61,15 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
|
|||||||
|
|
||||||
/// IComponent implementation
|
/// IComponent implementation
|
||||||
|
|
||||||
public setLayout (layout: FlexLayout): void {
|
public setLayout(layout: FlexLayout): void {
|
||||||
this._flexFlow = layout.flexFlow ? layout.flexFlow : '';
|
this._flexFlow = layout.flexFlow ? layout.flexFlow : '';
|
||||||
this._justifyContent = layout.justifyContent ? layout.justifyContent : '';
|
this._justifyContent = layout.justifyContent ? layout.justifyContent : '';
|
||||||
this._alignItems = layout.alignItems ? layout.alignItems : '';
|
this._alignItems = layout.alignItems ? layout.alignItems : '';
|
||||||
this._alignContent = layout.alignContent ? layout.alignContent : '';
|
this._alignContent = layout.alignContent ? layout.alignContent : '';
|
||||||
if (types.isUndefinedOrNull(layout.height)) {
|
this._textAlign = layout.textAlign ? layout.textAlign : '';
|
||||||
this._height = '';
|
this._height = this.convertSize(layout.height);
|
||||||
} else if (types.isNumber(layout.height)) {
|
this._width = this.convertSize(layout.width);
|
||||||
this._height = layout.height + 'px';
|
|
||||||
} else {
|
|
||||||
this._height = layout.height;
|
|
||||||
}
|
|
||||||
this.layout();
|
this.layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,10 +90,18 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
|
|||||||
return this._height;
|
return this._height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get width(): string {
|
||||||
|
return this._width;
|
||||||
|
}
|
||||||
|
|
||||||
public get alignContent(): string {
|
public get alignContent(): string {
|
||||||
return this._alignContent;
|
return this._alignContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get textAlign(): string {
|
||||||
|
return this._textAlign;
|
||||||
|
}
|
||||||
|
|
||||||
private getItemFlex(item: FlexItem): string {
|
private getItemFlex(item: FlexItem): string {
|
||||||
return item.config ? item.config.flex : '1 1 auto';
|
return item.config ? item.config.flex : '1 1 auto';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,15 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar
|
|||||||
import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
|
import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
|
||||||
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
|
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
|
import { getContentHeight, getContentWidth, Dimension } from 'vs/base/browser/dom';
|
||||||
|
|
||||||
export interface TitledFormItemLayout {
|
export interface TitledFormItemLayout {
|
||||||
title: string;
|
title: string;
|
||||||
actions?: string[];
|
actions?: string[];
|
||||||
isFormComponent: Boolean;
|
isFormComponent: Boolean;
|
||||||
horizontal: boolean;
|
horizontal: boolean;
|
||||||
componentWidth: number;
|
componentWidth?: number | string;
|
||||||
|
componentHeight?: number | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormLayout {
|
export interface FormLayout {
|
||||||
@@ -35,10 +37,10 @@ class FormItem {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<div #container *ngIf="items" class="form-table" [style.width]="getFormWidth()">
|
<div #container *ngIf="items" class="form-table" [style.width]="getFormWidth()" [style.height]="getFormHeight()">
|
||||||
<ng-container *ngFor="let item of items">
|
<ng-container *ngFor="let item of items">
|
||||||
<div class="form-row" >
|
<div class="form-row" *ngIf="isFormComponent(item)" [style.height]="getRowHeight(item)">
|
||||||
<ng-container *ngIf="isFormComponent(item)">
|
|
||||||
<ng-container *ngIf="isHorizontal(item)">
|
<ng-container *ngIf="isHorizontal(item)">
|
||||||
<div class="form-cell">{{getItemTitle(item)}}</div>
|
<div class="form-cell">{{getItemTitle(item)}}</div>
|
||||||
<div class="form-cell">
|
<div class="form-cell">
|
||||||
@@ -56,10 +58,10 @@ class FormItem {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<div class="form-vertical-container" *ngIf="isVertical(item)">
|
<div class="form-vertical-container" *ngIf="isVertical(item)" [style.height]="getRowHeight(item)">
|
||||||
<div class="form-item-row">{{getItemTitle(item)}}</div>
|
<div class="form-item-row">{{getItemTitle(item)}}</div>
|
||||||
<div class="form-item-row" [style.width]="getComponentWidth(item)">
|
<div class="form-item-row" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)">
|
||||||
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore" [style.width]="getComponentWidth(item)">
|
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)">
|
||||||
</model-component-wrapper>
|
</model-component-wrapper>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="itemHasActions(item)" class="form-item-row form-actions-table form-item-last-row">
|
<div *ngIf="itemHasActions(item)" class="form-item-row form-actions-table form-item-last-row">
|
||||||
@@ -69,7 +71,6 @@ class FormItem {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,6 +103,10 @@ export default class FormContainer extends ContainerBase<FormItemLayout> impleme
|
|||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public layout(): void {
|
||||||
|
super.layout();
|
||||||
|
}
|
||||||
|
|
||||||
/// IComponent implementation
|
/// IComponent implementation
|
||||||
|
|
||||||
public get alignItems(): string {
|
public get alignItems(): string {
|
||||||
@@ -113,12 +118,21 @@ export default class FormContainer extends ContainerBase<FormItemLayout> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getFormWidth(): string {
|
private getFormWidth(): string {
|
||||||
return this._formLayout && this._formLayout.width ? +this._formLayout.width + 'px' : '100%';
|
return this.convertSize(this._formLayout && this._formLayout.width, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFormHeight(): string {
|
||||||
|
return this.convertSize(this._formLayout && this._formLayout.height, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
private getComponentWidth(item: FormItem): string {
|
private getComponentWidth(item: FormItem): string {
|
||||||
let itemConfig = item.config;
|
let itemConfig = item.config;
|
||||||
return (itemConfig && itemConfig.componentWidth) ? itemConfig.componentWidth + 'px' : '';
|
return (itemConfig && itemConfig.componentWidth) ? this.convertSize(itemConfig.componentWidth, '') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRowHeight(item: FormItem): string {
|
||||||
|
let itemConfig = item.config;
|
||||||
|
return (itemConfig && itemConfig.componentHeight) ? this.convertSize(itemConfig.componentHeight, '') : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
private getItemTitle(item: FormItem): string {
|
private getItemTitle(item: FormItem): string {
|
||||||
|
|||||||
@@ -20,19 +20,23 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work
|
|||||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
|
import { TextAreaInput } from 'vs/editor/browser/controller/textAreaInput';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'modelview-inputBox',
|
selector: 'modelview-inputBox',
|
||||||
template: `
|
template: `
|
||||||
<div #input style="width: 100%"></div>
|
<div [style.display]="getInputBoxDisplay()" #input style="width: 100%"></div>
|
||||||
|
<div [style.display]="getTextAreaDisplay()" #textarea style="width: 100%"></div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export default class InputBoxComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
export default class InputBoxComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||||
@Input() descriptor: IComponentDescriptor;
|
@Input() descriptor: IComponentDescriptor;
|
||||||
@Input() modelStore: IModelStore;
|
@Input() modelStore: IModelStore;
|
||||||
private _input: InputBox;
|
private _input: InputBox;
|
||||||
|
private _textAreaInput: InputBox;
|
||||||
|
|
||||||
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
|
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
|
||||||
|
@ViewChild('textarea', { read: ElementRef }) private _textareaContainer: ElementRef;
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||||
@@ -43,47 +47,71 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.baseInit();
|
this.baseInit();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
if (this._inputContainer) {
|
let inputOptions: IInputOptions = {
|
||||||
let inputOptions: IInputOptions = {
|
placeholder: '',
|
||||||
placeholder: '',
|
ariaLabel: '',
|
||||||
ariaLabel: '',
|
validationOptions: {
|
||||||
validationOptions: {
|
validation: () => {
|
||||||
validation: () => {
|
if (this.valid) {
|
||||||
if (this.valid) {
|
return undefined;
|
||||||
return undefined;
|
} else {
|
||||||
} else {
|
return {
|
||||||
return {
|
content: this.inputElement.inputElement.validationMessage || nls.localize('invalidValueError', 'Invalid value'),
|
||||||
content: this._input.inputElement.validationMessage || nls.localize('invalidValueError', 'Invalid value'),
|
type: MessageType.ERROR
|
||||||
type: MessageType.ERROR
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
useDefaultValidation: true
|
},
|
||||||
};
|
useDefaultValidation: true
|
||||||
|
};
|
||||||
|
if (this._inputContainer) {
|
||||||
this._input = new InputBox(this._inputContainer.nativeElement, this.contextViewService, inputOptions);
|
this._input = new InputBox(this._inputContainer.nativeElement, this.contextViewService, inputOptions);
|
||||||
this._validations.push(() => !this._input.inputElement.validationMessage);
|
this.registerInput(this._input, () => !this.multiline);
|
||||||
|
|
||||||
this._register(this._input);
|
}
|
||||||
this._register(attachInputBoxStyler(this._input, this.themeService));
|
if (this._textareaContainer) {
|
||||||
this._register(this._input.onDidChange(e => {
|
let textAreaInputOptions = Object.assign({}, inputOptions, { flexibleHeight: true, type: 'textarea' });
|
||||||
this.value = this._input.value;
|
this._textAreaInput = new InputBox(this._textareaContainer.nativeElement, this.contextViewService, textAreaInputOptions);
|
||||||
this._onEventEmitter.fire({
|
this.registerInput(this._textAreaInput, () => this.multiline);
|
||||||
eventType: ComponentEventType.onDidChange,
|
}
|
||||||
args: e
|
}
|
||||||
});
|
|
||||||
|
private get inputElement(): InputBox {
|
||||||
|
return this.multiline ? this._textAreaInput : this._input;
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerInput(input: InputBox, checkOption: () => boolean): void {
|
||||||
|
if (input) {
|
||||||
|
this._validations.push(() => !input.inputElement.validationMessage);
|
||||||
|
|
||||||
|
this._register(input);
|
||||||
|
this._register(attachInputBoxStyler(input, this.themeService));
|
||||||
|
this._register(input.onDidChange(e => {
|
||||||
|
if (checkOption()) {
|
||||||
|
this.value = input.value;
|
||||||
|
this._onEventEmitter.fire({
|
||||||
|
eventType: ComponentEventType.onDidChange,
|
||||||
|
args: e
|
||||||
|
});
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getInputBoxDisplay(): string {
|
||||||
|
return !this.multiline ? '' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTextAreaDisplay(): string {
|
||||||
|
return this.multiline ? '' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
public validate(): Thenable<boolean> {
|
public validate(): Thenable<boolean> {
|
||||||
return super.validate().then(valid => {
|
return super.validate().then(valid => {
|
||||||
this._input.validate();
|
this.inputElement.validate();
|
||||||
return valid;
|
return valid;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -96,6 +124,16 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
|||||||
|
|
||||||
public layout(): void {
|
public layout(): void {
|
||||||
this._changeRef.detectChanges();
|
this._changeRef.detectChanges();
|
||||||
|
this.layoutInputBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
private layoutInputBox(): void {
|
||||||
|
if (this.width) {
|
||||||
|
this.inputElement.width = this.convertSizeToNumber(this.width);
|
||||||
|
}
|
||||||
|
if (this.height) {
|
||||||
|
this.inputElement.setHeight(this.convertSize(this.height));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setLayout(layout: any): void {
|
public setLayout(layout: any): void {
|
||||||
@@ -105,21 +143,41 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
|||||||
|
|
||||||
public setProperties(properties: { [key: string]: any; }): void {
|
public setProperties(properties: { [key: string]: any; }): void {
|
||||||
super.setProperties(properties);
|
super.setProperties(properties);
|
||||||
this._input.inputElement.type = this.inputType;
|
this.setInputProperties(this.inputElement);
|
||||||
if (this.inputType === 'number') {
|
|
||||||
this._input.inputElement.step = 'any';
|
|
||||||
}
|
|
||||||
this._input.value = this.value;
|
|
||||||
this._input.setAriaLabel(this.ariaLabel);
|
|
||||||
this._input.setPlaceHolder(this.placeHolder);
|
|
||||||
this._input.setEnabled(this.enabled);
|
|
||||||
if (this.width) {
|
|
||||||
this._input.width = this.width;
|
|
||||||
}
|
|
||||||
this._input.inputElement.required = this.required;
|
|
||||||
this.validate();
|
this.validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setInputProperties(input: InputBox): void {
|
||||||
|
if (!this.multiline) {
|
||||||
|
input.inputElement.type = this.inputType;
|
||||||
|
if (this.inputType === 'number') {
|
||||||
|
input.inputElement.step = 'any';
|
||||||
|
if (this.min) {
|
||||||
|
input.inputElement.min = this.min.toString();
|
||||||
|
}
|
||||||
|
if (this.max) {
|
||||||
|
input.inputElement.max = this.max.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input.value = this.value;
|
||||||
|
input.setAriaLabel(this.ariaLabel);
|
||||||
|
input.setPlaceHolder(this.placeHolder);
|
||||||
|
input.setEnabled(this.enabled);
|
||||||
|
this.layoutInputBox();
|
||||||
|
if (this.multiline) {
|
||||||
|
if (this.rows) {
|
||||||
|
this.inputElement.rows = this.rows;
|
||||||
|
}
|
||||||
|
if (this.columns) {
|
||||||
|
this.inputElement.columns = this.columns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
input.inputElement.required = this.required;
|
||||||
|
}
|
||||||
|
|
||||||
// CSS-bound properties
|
// CSS-bound properties
|
||||||
|
|
||||||
public get value(): string {
|
public get value(): string {
|
||||||
@@ -146,20 +204,36 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
|||||||
this.setPropertyFromUI<sqlops.InputBoxProperties, string>((props, value) => props.placeHolder = value, newValue);
|
this.setPropertyFromUI<sqlops.InputBoxProperties, string>((props, value) => props.placeHolder = value, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get height(): number {
|
public set columns(newValue: number) {
|
||||||
return this.getPropertyOrDefault<sqlops.InputBoxProperties, number>((props) => props.height, undefined);
|
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.columns = value, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public set height(newValue: number) {
|
public get rows(): number {
|
||||||
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.height = value, newValue);
|
return this.getPropertyOrDefault<sqlops.InputBoxProperties, number>((props) => props.rows, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get width(): number {
|
public get columns(): number {
|
||||||
return this.getPropertyOrDefault<sqlops.InputBoxProperties, number>((props) => props.width, undefined);
|
return this.getPropertyOrDefault<sqlops.InputBoxProperties, number>((props) => props.columns, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
public set width(newValue: number) {
|
public set rows(newValue: number) {
|
||||||
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.width = value, newValue);
|
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.rows = value, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get min(): number {
|
||||||
|
return this.getPropertyOrDefault<sqlops.InputBoxProperties, number>((props) => props.min, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set min(newValue: number) {
|
||||||
|
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.min = value, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get max(): number {
|
||||||
|
return this.getPropertyOrDefault<sqlops.InputBoxProperties, number>((props) => props.max, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set max(newValue: number) {
|
||||||
|
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.max = value, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get inputType(): string {
|
public get inputType(): string {
|
||||||
@@ -170,6 +244,14 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
|||||||
this.setPropertyFromUI<sqlops.InputBoxProperties, string>((props, value) => props.inputType = value, newValue);
|
this.setPropertyFromUI<sqlops.InputBoxProperties, string>((props, value) => props.inputType = value, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get multiline(): boolean {
|
||||||
|
return this.getPropertyOrDefault<sqlops.InputBoxProperties, boolean>((props) => props.multiline, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set multiline(newValue: boolean) {
|
||||||
|
this.setPropertyFromUI<sqlops.InputBoxProperties, boolean>((props, value) => props.multiline = value, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
public get required(): boolean {
|
public get required(): boolean {
|
||||||
return this.getPropertyOrDefault<sqlops.InputBoxProperties, boolean>((props) => props.required, false);
|
return this.getPropertyOrDefault<sqlops.InputBoxProperties, boolean>((props) => props.required, false);
|
||||||
}
|
}
|
||||||
|
|||||||
112
src/sql/parts/modelComponents/listbox.component.ts
Normal file
112
src/sql/parts/modelComponents/listbox.component.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||||
|
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
|
||||||
|
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||||
|
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||||
|
|
||||||
|
import { ListBox } from 'sql/base/browser/ui/listBox/listBox';
|
||||||
|
import { attachListBoxStyler } from 'sql/common/theme/styler';
|
||||||
|
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
|
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
|
import { Emitter } from 'vs/base/common/event';
|
||||||
|
import * as nls from 'vs/nls';
|
||||||
|
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'modelview-listBox',
|
||||||
|
template: `
|
||||||
|
<div #input style="width: 100%"></div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export default class ListBoxComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||||
|
@Input() descriptor: IComponentDescriptor;
|
||||||
|
@Input() modelStore: IModelStore;
|
||||||
|
private _input: ListBox;
|
||||||
|
|
||||||
|
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
|
||||||
|
constructor(
|
||||||
|
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||||
|
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||||
|
@Inject(IContextViewService) private contextViewService: IContextViewService,
|
||||||
|
@Inject(IClipboardService) private clipboardService: IClipboardService
|
||||||
|
) {
|
||||||
|
super(changeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.baseInit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
if (this._inputContainer) {
|
||||||
|
this._input = new ListBox([], undefined, this.contextViewService, this.clipboardService);
|
||||||
|
this._input.render(this._inputContainer.nativeElement);
|
||||||
|
|
||||||
|
this._register(this._input);
|
||||||
|
this._register(attachListBoxStyler(this._input, this.themeService));
|
||||||
|
this._register(this._input.onDidSelect(e => {
|
||||||
|
this.selectedRow = e.index;
|
||||||
|
this._onEventEmitter.fire({
|
||||||
|
eventType: ComponentEventType.onSelectedRowChanged,
|
||||||
|
args: e
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public validate(): Thenable<boolean> {
|
||||||
|
return super.validate().then(valid => {
|
||||||
|
return valid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.baseDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IComponent implementation
|
||||||
|
|
||||||
|
public layout(): void {
|
||||||
|
this._changeRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLayout(layout: any): void {
|
||||||
|
// TODO allow configuring the look and feel
|
||||||
|
this.layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setProperties(properties: { [key: string]: any; }): void {
|
||||||
|
super.setProperties(properties);
|
||||||
|
this._input.setOptions(this.values, this.selectedRow);
|
||||||
|
|
||||||
|
this.validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSS-bound properties
|
||||||
|
|
||||||
|
private get values(): string[] {
|
||||||
|
return this.getPropertyOrDefault<sqlops.ListBoxProperties, string[]>((props) => props.values, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
private set values(newValue: string[]) {
|
||||||
|
this.setPropertyFromUI<sqlops.ListBoxProperties, string[]>((props, value) => props.values = value, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private get selectedRow(): number {
|
||||||
|
return this.getPropertyOrDefault<sqlops.ListBoxProperties, number>((props) => props.selectedRow, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
private set selectedRow(newValue: number) {
|
||||||
|
this.setPropertyFromUI<sqlops.ListBoxProperties, number>((props, value) => props.selectedRow = value, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,10 +25,18 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
|||||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||||
import { memoize } from 'vs/base/common/decorators';
|
import { memoize } from 'vs/base/common/decorators';
|
||||||
import { generateUuid } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
|
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||||
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
|
|
||||||
const componentRegistry = <IComponentRegistry>Registry.as(Extensions.ComponentContribution);
|
const componentRegistry = <IComponentRegistry>Registry.as(Extensions.ComponentContribution);
|
||||||
|
|
||||||
|
export interface ModelComponentParams extends IBootstrapParams {
|
||||||
|
|
||||||
|
onLayoutRequested: Event<string>;
|
||||||
|
modelViewId: string;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'model-component-wrapper',
|
selector: 'model-component-wrapper',
|
||||||
template: `
|
template: `
|
||||||
@@ -46,6 +54,7 @@ export class ModelComponentWrapper extends AngularDisposable implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _componentInstance: IComponent;
|
private _componentInstance: IComponent;
|
||||||
|
private _modelViewId: string;
|
||||||
|
|
||||||
@ViewChild(ComponentHostDirective) componentHost: ComponentHostDirective;
|
@ViewChild(ComponentHostDirective) componentHost: ComponentHostDirective;
|
||||||
|
|
||||||
@@ -54,9 +63,18 @@ export class ModelComponentWrapper extends AngularDisposable implements OnInit {
|
|||||||
@Inject(forwardRef(() => ElementRef)) private _ref: ElementRef,
|
@Inject(forwardRef(() => ElementRef)) private _ref: ElementRef,
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeref: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeref: ChangeDetectorRef,
|
||||||
@Inject(forwardRef(() => Injector)) private _injector: Injector,
|
@Inject(forwardRef(() => Injector)) private _injector: Injector,
|
||||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
|
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||||
|
@Inject(IBootstrapParams) private _params: ModelComponentParams
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
if (_params && _params.onLayoutRequested) {
|
||||||
|
this._modelViewId = _params.modelViewId;
|
||||||
|
_params.onLayoutRequested(modelViewId => {
|
||||||
|
if (modelViewId === this._modelViewId) {
|
||||||
|
this.layout();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export class ModelViewContent extends ViewBase implements OnInit, IModelView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public layout(): void {
|
public layout(): void {
|
||||||
|
this.changeRef.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get id(): string {
|
public get id(): string {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectio
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'modelview-table',
|
selector: 'modelview-table',
|
||||||
template: `
|
template: `
|
||||||
<div #table style="width: 100%"></div>
|
<div #table style="width: 100%;height:100%"></div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export default class TableComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
export default class TableComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||||
@@ -92,9 +92,8 @@ export default class TableComponent extends ComponentBase implements IComponent,
|
|||||||
let options = <Slick.GridOptions<any>>{
|
let options = <Slick.GridOptions<any>>{
|
||||||
syncColumnCellResize: true,
|
syncColumnCellResize: true,
|
||||||
enableColumnReorder: false,
|
enableColumnReorder: false,
|
||||||
rowHeight: 45,
|
|
||||||
enableCellNavigation: true,
|
enableCellNavigation: true,
|
||||||
forceFitColumns: true
|
forceFitColumns: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
this._table = new Table<Slick.SlickData>(this._inputContainer.nativeElement, this._tableData, this._tableColumns, options);
|
this._table = new Table<Slick.SlickData>(this._inputContainer.nativeElement, this._tableData, this._tableColumns, options);
|
||||||
@@ -127,13 +126,19 @@ export default class TableComponent extends ComponentBase implements IComponent,
|
|||||||
/// IComponent implementation
|
/// IComponent implementation
|
||||||
|
|
||||||
public layout(): void {
|
public layout(): void {
|
||||||
this._table.layout(new Dimension(
|
this.layoutTable();
|
||||||
this.width ? this.width : getContentWidth(this._inputContainer.nativeElement),
|
|
||||||
this.height ? this.height : getContentHeight(this._inputContainer.nativeElement)));
|
|
||||||
|
|
||||||
this._changeRef.detectChanges();
|
this._changeRef.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private layoutTable(): void {
|
||||||
|
let width: number = this.convertSizeToNumber(this.width);
|
||||||
|
let height: number = this.convertSizeToNumber(this.height);
|
||||||
|
this._table.layout(new Dimension(
|
||||||
|
width && width > 0 ? width : getContentWidth(this._inputContainer.nativeElement),
|
||||||
|
height && height > 0 ? height : getContentHeight(this._inputContainer.nativeElement)));
|
||||||
|
}
|
||||||
|
|
||||||
public setLayout(): void {
|
public setLayout(): void {
|
||||||
// TODO allow configuring the look and feel
|
// TODO allow configuring the look and feel
|
||||||
this.layout();
|
this.layout();
|
||||||
@@ -149,10 +154,8 @@ export default class TableComponent extends ComponentBase implements IComponent,
|
|||||||
if (this.selectedRows) {
|
if (this.selectedRows) {
|
||||||
this._table.setSelectedRows(this.selectedRows);
|
this._table.setSelectedRows(this.selectedRows);
|
||||||
}
|
}
|
||||||
this._table.layout(new Dimension(
|
|
||||||
this.width ? this.width : getContentWidth(this._inputContainer.nativeElement),
|
|
||||||
this.height ? this.height : getContentHeight(this._inputContainer.nativeElement)));
|
|
||||||
|
|
||||||
|
this.layoutTable();
|
||||||
this.validate();
|
this.validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,20 +184,4 @@ export default class TableComponent extends ComponentBase implements IComponent,
|
|||||||
public set selectedRows(newValue: number[]) {
|
public set selectedRows(newValue: number[]) {
|
||||||
this.setPropertyFromUI<sqlops.TableComponentProperties, number[]>((props, value) => props.selectedRows = value, newValue);
|
this.setPropertyFromUI<sqlops.TableComponentProperties, number[]>((props, value) => props.selectedRows = value, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get height(): number {
|
|
||||||
return this.getPropertyOrDefault<sqlops.TableComponentProperties, number>((props) => props.height, undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
public set height(newValue: number) {
|
|
||||||
this.setPropertyFromUI<sqlops.TableComponentProperties, number>((props, value) => props.height = value, newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get width(): number {
|
|
||||||
return this.getPropertyOrDefault<sqlops.TableComponentProperties, number>((props) => props.width, undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
public set width(newValue: number) {
|
|
||||||
this.setPropertyFromUI<sqlops.TableComponentProperties, number>((props, value) => props.width = value, newValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
|||||||
abstract serverInfo: sqlops.ServerInfo;
|
abstract serverInfo: sqlops.ServerInfo;
|
||||||
private _onEventEmitter = new Emitter<any>();
|
private _onEventEmitter = new Emitter<any>();
|
||||||
|
|
||||||
|
|
||||||
initializeModel(rootComponent: IComponentShape, validationCallback: (componentId: string) => Thenable<boolean>): void {
|
initializeModel(rootComponent: IComponentShape, validationCallback: (componentId: string) => Thenable<boolean>): void {
|
||||||
let descriptor = this.defineComponent(rootComponent);
|
let descriptor = this.defineComponent(rootComponent);
|
||||||
this.rootDescriptor = descriptor;
|
this.rootDescriptor = descriptor;
|
||||||
@@ -50,6 +49,10 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private defineComponent(component: IComponentShape): IComponentDescriptor {
|
private defineComponent(component: IComponentShape): IComponentDescriptor {
|
||||||
|
let existingDescriptor = this.modelStore.getComponentDescriptor(component.id);
|
||||||
|
if (existingDescriptor) {
|
||||||
|
return existingDescriptor;
|
||||||
|
}
|
||||||
let typeId = componentRegistry.getIdForTypeMapping(component.type);
|
let typeId = componentRegistry.getIdForTypeMapping(component.type);
|
||||||
if (!typeId) {
|
if (!typeId) {
|
||||||
// failure case
|
// failure case
|
||||||
|
|||||||
@@ -92,33 +92,6 @@ const profilerSessionTemplateSchema: IJSONSchema = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'TSQL'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Blank'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'SP_Counts'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'TQL_Duration'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'TSQL_Grouped'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'TSQL_Locks'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'TSQL_Replay'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'TSQL_SPs'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Tuning'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
|||||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||||
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||||
|
import { IObjectExplorerService } from '../../objectExplorer/common/objectExplorerService';
|
||||||
import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput';
|
import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput';
|
||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
|
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
|
||||||
|
|
||||||
// Contribute Global Actions
|
// Contribute Global Actions
|
||||||
const category = nls.localize('profilerCategory', "Profiler");
|
const category = nls.localize('profilerCategory', "Profiler");
|
||||||
@@ -37,20 +39,9 @@ CommandsRegistry.registerCommand({
|
|||||||
let editorService: IWorkbenchEditorService = accessor.get(IWorkbenchEditorService);
|
let editorService: IWorkbenchEditorService = accessor.get(IWorkbenchEditorService);
|
||||||
let instantiationService: IInstantiationService = accessor.get(IInstantiationService);
|
let instantiationService: IInstantiationService = accessor.get(IInstantiationService);
|
||||||
let connectionService: IConnectionManagementService = accessor.get(IConnectionManagementService);
|
let connectionService: IConnectionManagementService = accessor.get(IConnectionManagementService);
|
||||||
|
let objectExplorerService: IObjectExplorerService = accessor.get(IObjectExplorerService);
|
||||||
|
|
||||||
// TODO: for test-only, grab the first MSSQL active connection for the profiler session
|
let connectionProfile = TaskUtilities.getCurrentGlobalConnection(objectExplorerService, connectionService, editorService);
|
||||||
// TODO: when finishing the feature the connection should come from the launch context
|
|
||||||
let connectionProfile: IConnectionProfile;
|
|
||||||
let activeConnections = connectionService.getActiveConnections();
|
|
||||||
if (activeConnections) {
|
|
||||||
for (let i = 0; i < activeConnections.length; ++i) {
|
|
||||||
if (activeConnections[i].providerName === 'MSSQL') {
|
|
||||||
connectionProfile = activeConnections[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let profilerInput = instantiationService.createInstance(ProfilerInput, connectionProfile);
|
let profilerInput = instantiationService.createInstance(ProfilerInput, connectionProfile);
|
||||||
return editorService.openEditor(profilerInput, { pinned: true }, false).then(() => TPromise.as(true));
|
return editorService.openEditor(profilerInput, { pinned: true }, false).then(() => TPromise.as(true));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export class ProfilerConnect extends Action {
|
|||||||
public set connected(value: boolean) {
|
public set connected(value: boolean) {
|
||||||
this._connected = value;
|
this._connected = value;
|
||||||
this._setClass(value ? 'disconnect' : 'connect');
|
this._setClass(value ? 'disconnect' : 'connect');
|
||||||
this._setLabel(value ? nls.localize('profilerAction.disconnect', 'Disconnected') : nls.localize('profilerAction.connect', "Connect"));
|
this._setLabel(value ? nls.localize('profilerAction.disconnect', 'Disconnect') : nls.localize('profilerAction.connect', "Connect"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public get connected(): boolean {
|
public get connected(): boolean {
|
||||||
@@ -78,17 +78,19 @@ export class ProfilerStart extends Action {
|
|||||||
|
|
||||||
public run(input: ProfilerInput): TPromise<boolean> {
|
public run(input: ProfilerInput): TPromise<boolean> {
|
||||||
this.enabled = false;
|
this.enabled = false;
|
||||||
|
input.data.clear();
|
||||||
return TPromise.wrap(this._profilerService.startSession(input.id).then(() => {
|
return TPromise.wrap(this._profilerService.startSession(input.id).then(() => {
|
||||||
input.state.change({ isRunning: true, isStopped: false, isPaused: false });
|
input.state.change({ isRunning: true, isStopped: false, isPaused: false });
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProfilerPause extends Action {
|
export class ProfilerPause extends Action {
|
||||||
public static ID = 'profiler.pause';
|
public static ID = 'profiler.pause';
|
||||||
public static LABEL = nls.localize('pause', "Pause");
|
public static LABEL = nls.localize('profiler.capture', "Pause Capture");
|
||||||
|
|
||||||
|
private _paused: boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
id: string, label: string,
|
id: string, label: string,
|
||||||
@@ -98,12 +100,22 @@ export class ProfilerPause extends Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public run(input: ProfilerInput): TPromise<boolean> {
|
public run(input: ProfilerInput): TPromise<boolean> {
|
||||||
this.enabled = false;
|
|
||||||
return TPromise.wrap(this._profilerService.pauseSession(input.id).then(() => {
|
return TPromise.wrap(this._profilerService.pauseSession(input.id).then(() => {
|
||||||
input.state.change({ isPaused: true, isStopped: false, isRunning: false });
|
this.paused = !this._paused;
|
||||||
|
input.state.change({ isPaused: this.paused, isStopped: false, isRunning: !this.paused });
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public set paused(value: boolean) {
|
||||||
|
this._paused = value;
|
||||||
|
this._setClass(value ? 'start' : 'stop');
|
||||||
|
this._setLabel(value ? nls.localize('profilerAction.resumeCapture', "Resume Capture") : nls.localize('profilerAction.pauseCapture', "Pause Capture"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public get paused(): boolean {
|
||||||
|
return this._paused;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProfilerStop extends Action {
|
export class ProfilerStop extends Action {
|
||||||
|
|||||||
@@ -203,14 +203,12 @@ export class ProfilerEditor extends BaseEditor {
|
|||||||
|
|
||||||
this._actionBar.setContent([
|
this._actionBar.setContent([
|
||||||
{ action: this._startAction },
|
{ action: this._startAction },
|
||||||
{ action: this._pauseAction },
|
|
||||||
{ action: this._stopAction },
|
{ action: this._stopAction },
|
||||||
{ action: this._connectAction },
|
|
||||||
{ element: Taskbar.createTaskbarSeparator() },
|
|
||||||
{ action: this._autoscrollAction },
|
|
||||||
{ action: this._instantiationService.createInstance(Actions.ProfilerClear, Actions.ProfilerClear.ID, Actions.ProfilerClear.LABEL) },
|
|
||||||
{ element: dropdownContainer },
|
{ element: dropdownContainer },
|
||||||
{ action: this._instantiationService.createInstance(Actions.ProfilerEditColumns, Actions.ProfilerEditColumns.ID, Actions.ProfilerEditColumns.LABEL) }
|
{ element: Taskbar.createTaskbarSeparator() },
|
||||||
|
{ action: this._pauseAction },
|
||||||
|
{ action: this._autoscrollAction },
|
||||||
|
{ action: this._instantiationService.createInstance(Actions.ProfilerClear, Actions.ProfilerClear.ID, Actions.ProfilerClear.LABEL) }
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +226,9 @@ export class ProfilerEditor extends BaseEditor {
|
|||||||
if (data) {
|
if (data) {
|
||||||
this._modelService.updateModel(this._editorModel, data['TextData']);
|
this._modelService.updateModel(this._editorModel, data['TextData']);
|
||||||
this._detailTableData.clear();
|
this._detailTableData.clear();
|
||||||
this._detailTableData.push(Object.keys(data).map(key => {
|
this._detailTableData.push(Object.keys(data).filter(key => {
|
||||||
|
return data[key] !== ' ';
|
||||||
|
}).map(key => {
|
||||||
return {
|
return {
|
||||||
label: key,
|
label: key,
|
||||||
value: data[key]
|
value: data[key]
|
||||||
@@ -384,29 +384,26 @@ export class ProfilerEditor extends BaseEditor {
|
|||||||
|
|
||||||
if (e.isConnected) {
|
if (e.isConnected) {
|
||||||
this._connectAction.connected = this.input.state.isConnected;
|
this._connectAction.connected = this.input.state.isConnected;
|
||||||
this._startAction.enabled = this.input.state.isConnected;
|
|
||||||
this._stopAction.enabled = false;
|
|
||||||
this._pauseAction.enabled = false;
|
|
||||||
|
|
||||||
if (this.input.state.isConnected) {
|
if (this.input.state.isConnected) {
|
||||||
this._sessionTemplateSelector.disable();
|
this._sessionTemplateSelector.disable();
|
||||||
} else {
|
} else {
|
||||||
this._sessionTemplateSelector.enable();
|
this._sessionTemplateSelector.enable();
|
||||||
|
this._startAction.enabled = this.input.state.isConnected;
|
||||||
|
this._stopAction.enabled = false;
|
||||||
|
this._pauseAction.enabled = false;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.isRunning) {
|
if (e.isPaused){
|
||||||
this._startAction.enabled = !this.input.state.isRunning;
|
this._pauseAction.paused = this.input.state.isPaused;
|
||||||
|
this._pauseAction.enabled = !this.input.state.isStopped && (this.input.state.isRunning || this.input.state.isPaused);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.isStopped || e.isRunning) {
|
if (e.isStopped || e.isRunning) {
|
||||||
this._stopAction.enabled = !this.input.state.isStopped && this.input.state.isRunning;
|
this._startAction.enabled = !this.input.state.isRunning && !this.input.state.isPaused;
|
||||||
}
|
this._stopAction.enabled = !this.input.state.isStopped && (this.input.state.isRunning || this.input.state.isPaused);
|
||||||
|
this._pauseAction.enabled = !this.input.state.isStopped && (this.input.state.isRunning || this.input.state.isPaused);
|
||||||
if (e.isPaused || e.isRunning) {
|
|
||||||
this._pauseAction.enabled = !this.input.state.isPaused && this.input.state.isRunning;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
|||||||
import { EditorInput } from 'vs/workbench/common/editor';
|
import { EditorInput } from 'vs/workbench/common/editor';
|
||||||
import { IEditorModel } from 'vs/platform/editor/common/editor';
|
import { IEditorModel } from 'vs/platform/editor/common/editor';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import { generateUuid } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
|
|
||||||
@@ -35,14 +36,15 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
|
|||||||
constructor(
|
constructor(
|
||||||
private _connection: IConnectionProfile,
|
private _connection: IConnectionProfile,
|
||||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||||
@IProfilerService private _profilerService: IProfilerService
|
@IProfilerService private _profilerService: IProfilerService,
|
||||||
|
@INotificationService private _notificationService: INotificationService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._state = new ProfilerState();
|
this._state = new ProfilerState();
|
||||||
// set inital state
|
// set inital state
|
||||||
this.state.change({
|
this.state.change({
|
||||||
isConnected: true,
|
isConnected: true,
|
||||||
isStopped: false,
|
isStopped: true,
|
||||||
isPaused: false,
|
isPaused: false,
|
||||||
isRunning: false,
|
isRunning: false,
|
||||||
autoscroll: true
|
autoscroll: true
|
||||||
@@ -123,13 +125,26 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
|
|||||||
return this._state;
|
return this._state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onSessionStopped(notification: sqlops.ProfilerSessionStoppedParams) {
|
||||||
|
this._notificationService.error(nls.localize("profiler.sessionStopped", "XEvent Profiler Session stopped unexpectedly on the server {0}.", this._connection.serverName));
|
||||||
|
|
||||||
|
this.state.change({
|
||||||
|
isStopped: true,
|
||||||
|
isPaused: false,
|
||||||
|
isRunning: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public onMoreRows(eventMessage: sqlops.ProfilerSessionEvents) {
|
public onMoreRows(eventMessage: sqlops.ProfilerSessionEvents) {
|
||||||
|
if (eventMessage.eventsLost){
|
||||||
|
this._notificationService.warn(nls.localize("profiler.eventsLost", "The XEvent Profiler session for {0} has lost events.", this._connection.serverName));
|
||||||
|
}
|
||||||
|
|
||||||
for (let i: number = 0; i < eventMessage.events.length && i < 500; ++i) {
|
for (let i: number = 0; i < eventMessage.events.length && i < 500; ++i) {
|
||||||
let e: sqlops.ProfilerEvent = eventMessage.events[i];
|
let e: sqlops.ProfilerEvent = eventMessage.events[i];
|
||||||
let data = {};
|
let data = {};
|
||||||
data['EventClass'] = e.name;
|
data['EventClass'] = e.name;
|
||||||
data['StartTime'] = e.timestamp;
|
data['StartTime'] = e.timestamp;
|
||||||
data['EndTime'] = e.timestamp;
|
|
||||||
const columns = [
|
const columns = [
|
||||||
'TextData',
|
'TextData',
|
||||||
'ApplicationName',
|
'ApplicationName',
|
||||||
@@ -156,12 +171,14 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
|
|||||||
columnNameMap['cpu_time'] = 'CPU';
|
columnNameMap['cpu_time'] = 'CPU';
|
||||||
columnNameMap['duration'] = 'Duration';
|
columnNameMap['duration'] = 'Duration';
|
||||||
columnNameMap['logical_reads'] = 'Reads';
|
columnNameMap['logical_reads'] = 'Reads';
|
||||||
|
columnNameMap['event_sequence'] = 'EventSequence';
|
||||||
|
columnNameMap['client_pid'] = 'ClientProcessID';
|
||||||
|
columnNameMap['writes'] = 'Writes';
|
||||||
|
|
||||||
for (let idx = 0; idx < columns.length; ++idx) {
|
// Using ' ' instead of '' fixed the error where clicking through events
|
||||||
let columnName = columns[idx];
|
// with empty text fields causes future text panes to be highlighted.
|
||||||
data[columnName] = '';
|
// This is a temporary fix, and should be changed before the July release
|
||||||
}
|
data['TextData'] = ' ';
|
||||||
|
|
||||||
for (let key in e.values) {
|
for (let key in e.values) {
|
||||||
let columnName = columnNameMap[key];
|
let columnName = columnNameMap[key];
|
||||||
if (columnName) {
|
if (columnName) {
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ export interface IProfilerSession {
|
|||||||
* Called by the service when more rows are available to render
|
* Called by the service when more rows are available to render
|
||||||
*/
|
*/
|
||||||
onMoreRows(events: sqlops.ProfilerSessionEvents);
|
onMoreRows(events: sqlops.ProfilerSessionEvents);
|
||||||
|
/**
|
||||||
|
* Called by the service when the session is closed unexpectedly
|
||||||
|
*/
|
||||||
|
onSessionStopped(events: sqlops.ProfilerSessionStoppedParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,6 +69,10 @@ export interface IProfilerService {
|
|||||||
* The method called by the service provider for when more rows are available to render
|
* The method called by the service provider for when more rows are available to render
|
||||||
*/
|
*/
|
||||||
onMoreRows(params: sqlops.ProfilerSessionEvents): void;
|
onMoreRows(params: sqlops.ProfilerSessionEvents): void;
|
||||||
|
/**
|
||||||
|
* The method called by the service provider for when more rows are available to render
|
||||||
|
*/
|
||||||
|
onSessionStopped(params: sqlops.ProfilerSessionStoppedParams): void;
|
||||||
/**
|
/**
|
||||||
* Gets a list of the session templates that are specified in the settings
|
* Gets a list of the session templates that are specified in the settings
|
||||||
* @param provider An optional string to limit the session template to a specific
|
* @param provider An optional string to limit the session template to a specific
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ export class ProfilerService implements IProfilerService {
|
|||||||
this._sessionMap.get(this._idMap.reverseGet(params.sessionId)).onMoreRows(params);
|
this._sessionMap.get(this._idMap.reverseGet(params.sessionId)).onMoreRows(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onSessionStopped(params: sqlops.ProfilerSessionStoppedParams): void {
|
||||||
|
|
||||||
|
this._sessionMap.get(this._idMap.reverseGet(params.ownerUri)).onSessionStopped(params);
|
||||||
|
}
|
||||||
|
|
||||||
public connectSession(id: ProfilerSessionID): Thenable<boolean> {
|
public connectSession(id: ProfilerSessionID): Thenable<boolean> {
|
||||||
return this._runAction(id, provider => provider.connectSession(this._idMap.get(id)));
|
return this._runAction(id, provider => provider.connectSession(this._idMap.get(id)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ export class ProfilerTestBackend implements sqlops.ProfilerProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerOnSessionStopped(handler: (response: sqlops.ProfilerSessionStoppedParams) => any) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
private intervalFn(guid: string): number {
|
private intervalFn(guid: string): number {
|
||||||
return setTimeout(() => {
|
return setTimeout(() => {
|
||||||
let data = this.testData[this.index++];
|
let data = this.testData[this.index++];
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost
|
|||||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
|
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
||||||
|
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
||||||
|
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
||||||
|
|
||||||
/* Model-backed components */
|
/* Model-backed components */
|
||||||
let extensionComponents = Registry.as<IComponentRegistry>(Extensions.ComponentContribution).getAllCtors();
|
let extensionComponents = Registry.as<IComponentRegistry>(Extensions.ComponentContribution).getAllCtors();
|
||||||
@@ -26,6 +29,9 @@ let extensionComponents = Registry.as<IComponentRegistry>(Extensions.ComponentCo
|
|||||||
export const DialogModule = (params, selector: string): any => {
|
export const DialogModule = (params, selector: string): any => {
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
Checkbox,
|
||||||
|
SelectBox,
|
||||||
|
InputBox,
|
||||||
DialogContainer,
|
DialogContainer,
|
||||||
ModelViewContent,
|
ModelViewContent,
|
||||||
ModelComponentWrapper,
|
ModelComponentWrapper,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { ComponentEventType } from '../../parts/modelComponents/interfaces';
|
|||||||
export interface DialogComponentParams extends IBootstrapParams {
|
export interface DialogComponentParams extends IBootstrapParams {
|
||||||
modelViewId: string;
|
modelViewId: string;
|
||||||
validityChangedCallback: (valid: boolean) => void;
|
validityChangedCallback: (valid: boolean) => void;
|
||||||
|
onLayoutRequested: Event<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -35,6 +36,11 @@ export class DialogContainer implements AfterContentInit {
|
|||||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
||||||
@Inject(IBootstrapParams) private _params: DialogComponentParams) {
|
@Inject(IBootstrapParams) private _params: DialogComponentParams) {
|
||||||
this.modelViewId = this._params.modelViewId;
|
this.modelViewId = this._params.modelViewId;
|
||||||
|
this._params.onLayoutRequested(e => {
|
||||||
|
if (this.modelViewId === e) {
|
||||||
|
this.layout();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentInit(): void {
|
ngAfterContentInit(): void {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { Builder } from 'vs/base/browser/builder';
|
|||||||
import { IThemable } from 'vs/platform/theme/common/styler';
|
import { IThemable } from 'vs/platform/theme/common/styler';
|
||||||
import { Disposable } from 'vs/base/common/lifecycle';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
export class DialogPane extends Disposable implements IThemable {
|
export class DialogPane extends Disposable implements IThemable {
|
||||||
private _tabbedPanel: TabbedPanel;
|
private _tabbedPanel: TabbedPanel;
|
||||||
@@ -34,6 +35,9 @@ export class DialogPane extends Disposable implements IThemable {
|
|||||||
private _tabBar: HTMLElement;
|
private _tabBar: HTMLElement;
|
||||||
private _tabs: HTMLElement[];
|
private _tabs: HTMLElement[];
|
||||||
private _tabContent: HTMLElement[];
|
private _tabContent: HTMLElement[];
|
||||||
|
private _selectedTabIndex: number = 0; //TODO: can be an option
|
||||||
|
private _onTabChange = new Emitter<string>();
|
||||||
|
private _selectedTabContent: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _title: string,
|
private _title: string,
|
||||||
@@ -55,10 +59,17 @@ export class DialogPane extends Disposable implements IThemable {
|
|||||||
} else {
|
} else {
|
||||||
this._tabbedPanel = new TabbedPanel(this._body);
|
this._tabbedPanel = new TabbedPanel(this._body);
|
||||||
this._content.forEach((tab, tabIndex) => {
|
this._content.forEach((tab, tabIndex) => {
|
||||||
|
if (this._selectedTabIndex === tabIndex) {
|
||||||
|
this._selectedTabContent = tab.content;
|
||||||
|
}
|
||||||
let tabContainer = document.createElement('div');
|
let tabContainer = document.createElement('div');
|
||||||
tabContainer.style.display = 'none';
|
tabContainer.style.display = 'none';
|
||||||
this._body.appendChild(tabContainer);
|
this._body.appendChild(tabContainer);
|
||||||
this.initializeModelViewContainer(tabContainer, tab.content, tab);
|
this.initializeModelViewContainer(tabContainer, tab.content, tab);
|
||||||
|
this._tabbedPanel.onTabChange(e => {
|
||||||
|
tabContainer.style.height = (this.getTabDimension().height - this._tabbedPanel.headersize) + 'px';
|
||||||
|
this._onTabChange.fire(tab.content);
|
||||||
|
});
|
||||||
this._tabbedPanel.pushTab({
|
this._tabbedPanel.pushTab({
|
||||||
title: tab.title,
|
title: tab.title,
|
||||||
identifier: 'dialogPane.' + this._title + '.' + tabIndex,
|
identifier: 'dialogPane.' + this._title + '.' + tabIndex,
|
||||||
@@ -70,7 +81,7 @@ export class DialogPane extends Disposable implements IThemable {
|
|||||||
container.appendChild(tabContainer);
|
container.appendChild(tabContainer);
|
||||||
tabContainer.style.display = 'block';
|
tabContainer.style.display = 'block';
|
||||||
},
|
},
|
||||||
layout: (dimension) => { }
|
layout: (dimension) => { this.getTabDimension(); }
|
||||||
} as IPanelView
|
} as IPanelView
|
||||||
} as IPanelTab);
|
} as IPanelTab);
|
||||||
});
|
});
|
||||||
@@ -80,9 +91,14 @@ export class DialogPane extends Disposable implements IThemable {
|
|||||||
return this._body;
|
return this._body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getTabDimension(): DOM.Dimension {
|
||||||
|
return new DOM.Dimension(DOM.getContentWidth(this._body), DOM.getContentHeight(this._body));
|
||||||
|
}
|
||||||
|
|
||||||
public layout(): void {
|
public layout(): void {
|
||||||
if (this._tabbedPanel) {
|
if (this._tabbedPanel) {
|
||||||
this._tabbedPanel.layout(new DOM.Dimension(DOM.getContentWidth(this._body), DOM.getContentHeight(this._body)));
|
this._tabbedPanel.layout(new DOM.Dimension(DOM.getContentWidth(this._body), DOM.getContentHeight(this._body)));
|
||||||
|
this._onTabChange.fire(this._selectedTabContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,10 +117,13 @@ export class DialogPane extends Disposable implements IThemable {
|
|||||||
if (tab) {
|
if (tab) {
|
||||||
tab.notifyValidityChanged(valid);
|
tab.notifyValidityChanged(valid);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
onLayoutRequested: this._onTabChange.event
|
||||||
} as DialogComponentParams,
|
} as DialogComponentParams,
|
||||||
undefined,
|
undefined,
|
||||||
(moduleRef) => this._moduleRefs.push(moduleRef));
|
(moduleRef) => {
|
||||||
|
return this._moduleRefs.push(moduleRef);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public show(): void {
|
public show(): void {
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ export class Wizard {
|
|||||||
public readonly onPageAdded = this._pageAddedEmitter.event;
|
public readonly onPageAdded = this._pageAddedEmitter.event;
|
||||||
private _pageRemovedEmitter = new Emitter<WizardPage>();
|
private _pageRemovedEmitter = new Emitter<WizardPage>();
|
||||||
public readonly onPageRemoved = this._pageRemovedEmitter.event;
|
public readonly onPageRemoved = this._pageRemovedEmitter.event;
|
||||||
|
private _navigationValidator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>;
|
||||||
|
|
||||||
constructor(public title: string) { }
|
constructor(public title: string) { }
|
||||||
|
|
||||||
@@ -191,4 +192,19 @@ export class Wizard {
|
|||||||
this.pages.splice(index, 1);
|
this.pages.splice(index, 1);
|
||||||
this._pageRemovedEmitter.fire(removedPage);
|
this._pageRemovedEmitter.fire(removedPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public registerNavigationValidator(validator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>): void {
|
||||||
|
this._navigationValidator = validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public validateNavigation(newPage: number): Thenable<boolean> {
|
||||||
|
if (this._navigationValidator) {
|
||||||
|
return Promise.resolve(this._navigationValidator({
|
||||||
|
lastPage: this._currentPage,
|
||||||
|
newPage: newPage
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -106,12 +106,12 @@ export class WizardModal extends Modal {
|
|||||||
});
|
});
|
||||||
this._wizard.onPageAdded(page => {
|
this._wizard.onPageAdded(page => {
|
||||||
this.registerPage(page);
|
this.registerPage(page);
|
||||||
this.showPage(this.getCurrentPage());
|
this.showPage(this.getCurrentPage(), false);
|
||||||
});
|
});
|
||||||
this._wizard.onPageRemoved(page => {
|
this._wizard.onPageRemoved(page => {
|
||||||
let dialogPane = this._dialogPanes.get(page);
|
let dialogPane = this._dialogPanes.get(page);
|
||||||
this._dialogPanes.delete(page);
|
this._dialogPanes.delete(page);
|
||||||
this.showPage(this.getCurrentPage());
|
this.showPage(this.getCurrentPage(), false);
|
||||||
dialogPane.dispose();
|
dialogPane.dispose();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -123,10 +123,13 @@ export class WizardModal extends Modal {
|
|||||||
page.onUpdate(() => this.setButtonsForPage(this._wizard.currentPage));
|
page.onUpdate(() => this.setButtonsForPage(this._wizard.currentPage));
|
||||||
}
|
}
|
||||||
|
|
||||||
private showPage(index: number): void {
|
private async showPage(index: number, validate: boolean = true): Promise<void> {
|
||||||
let pageToShow = this._wizard.pages[index];
|
let pageToShow = this._wizard.pages[index];
|
||||||
if (!pageToShow) {
|
if (!pageToShow) {
|
||||||
this.done();
|
this.done(validate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (validate && !await this._wizard.validateNavigation(index)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._dialogPanes.forEach((dialogPane, page) => {
|
this._dialogPanes.forEach((dialogPane, page) => {
|
||||||
@@ -163,12 +166,15 @@ export class WizardModal extends Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public open(): void {
|
public open(): void {
|
||||||
this.showPage(0);
|
this.showPage(0, false);
|
||||||
this.show();
|
this.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public done(): void {
|
public async done(validate: boolean = true): Promise<void> {
|
||||||
if (this._wizard.doneButton.enabled) {
|
if (this._wizard.doneButton.enabled) {
|
||||||
|
if (validate && !await this._wizard.validateNavigation(undefined)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this._onDone.fire();
|
this._onDone.fire();
|
||||||
this.dispose();
|
this.dispose();
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|||||||
211
src/sql/sqlops.d.ts
vendored
211
src/sql/sqlops.d.ts
vendored
@@ -1025,22 +1025,33 @@ declare module 'sqlops' {
|
|||||||
getDatabaseInfo(connectionUri: string): Thenable<DatabaseInfo>;
|
getDatabaseInfo(connectionUri: string): Thenable<DatabaseInfo>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Agent Services interfaces
|
// Agent Services types
|
||||||
export interface AgentJobsResult {
|
export enum WeekDays {
|
||||||
succeeded: boolean;
|
sunday = 1,
|
||||||
errorMessage: string;
|
monday = 2,
|
||||||
jobs: AgentJobInfo[];
|
tuesday = 4,
|
||||||
|
wednesday = 8,
|
||||||
|
thursday = 16,
|
||||||
|
friday = 32,
|
||||||
|
weekDays = 62,
|
||||||
|
saturday = 64,
|
||||||
|
weekEnds = 65,
|
||||||
|
everyDay = 127
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentJobHistoryResult {
|
export enum NotifyMethods {
|
||||||
succeeded: boolean;
|
none = 0,
|
||||||
errorMessage: string;
|
notifyEmail = 1,
|
||||||
jobs: AgentJobHistoryInfo[];
|
pager = 2,
|
||||||
|
netSend = 4,
|
||||||
|
notifyAll = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentJobActionResult {
|
export enum AlertType {
|
||||||
succeeded: boolean;
|
sqlServerEvent = 1,
|
||||||
errorMessage: string;
|
sqlServerPerformanceCondition = 2,
|
||||||
|
nonSqlServerEvent = 3,
|
||||||
|
wmiEvent = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentJobInfo {
|
export interface AgentJobInfo {
|
||||||
@@ -1061,7 +1072,8 @@ declare module 'sqlops' {
|
|||||||
jobId: string;
|
jobId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentJobStep {
|
export interface AgentJobStepInfo {
|
||||||
|
jobId: string;
|
||||||
stepId: string;
|
stepId: string;
|
||||||
stepName: string;
|
stepName: string;
|
||||||
message: string;
|
message: string;
|
||||||
@@ -1086,13 +1098,168 @@ declare module 'sqlops' {
|
|||||||
operatorPaged: string;
|
operatorPaged: string;
|
||||||
retriesAttempted: string;
|
retriesAttempted: string;
|
||||||
server: string;
|
server: string;
|
||||||
steps: AgentJobStep[];
|
steps: AgentJobStepInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentProxyInfo {
|
||||||
|
id: number;
|
||||||
|
accountName: string;
|
||||||
|
description: string;
|
||||||
|
credentialName: string;
|
||||||
|
credentialIdentity: string;
|
||||||
|
credentialId: number;
|
||||||
|
isEnabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentAlertInfo {
|
||||||
|
id: number;
|
||||||
|
delayBetweenResponses: number;
|
||||||
|
eventDescriptionKeyword: string;
|
||||||
|
eventSource: string;
|
||||||
|
hasNotification: number;
|
||||||
|
includeEventDescription: NotifyMethods;
|
||||||
|
isEnabled: boolean;
|
||||||
|
jobId: string;
|
||||||
|
jobName: string;
|
||||||
|
lastOccurrenceDate: string;
|
||||||
|
lastResponseDate: string;
|
||||||
|
messageId: number;
|
||||||
|
notificationMessage: string;
|
||||||
|
occurrenceCount: number;
|
||||||
|
performanceCondition: string;
|
||||||
|
severity: number;
|
||||||
|
databaseName: string;
|
||||||
|
countResetDate: string;
|
||||||
|
categoryName: string;
|
||||||
|
alertType: AlertType;
|
||||||
|
wmiEventNamespace: string;
|
||||||
|
wmiEventQuery: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentOperatorInfo {
|
||||||
|
name: string;
|
||||||
|
id: number;
|
||||||
|
emailAddress: string;
|
||||||
|
enabled: boolean;
|
||||||
|
lastEmailDate: string;
|
||||||
|
lastNetSendDate: string;
|
||||||
|
lastPagerDate: string;
|
||||||
|
pagerAddress: string;
|
||||||
|
categoryName: string;
|
||||||
|
pagerDays: WeekDays;
|
||||||
|
saturdayPagerEndTime: string;
|
||||||
|
saturdayPagerStartTime: string;
|
||||||
|
sundayPagerEndTime: string;
|
||||||
|
sundayPagerStartTime: string;
|
||||||
|
netSendAddress: string;
|
||||||
|
weekdayPagerStartTime: string;
|
||||||
|
weekdayPagerEndTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResultStatus {
|
||||||
|
success: boolean;
|
||||||
|
errorMessage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentJobsResult extends ResultStatus {
|
||||||
|
jobs: AgentJobInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentJobHistoryResult extends ResultStatus {
|
||||||
|
jobs: AgentJobHistoryInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateAgentJobResult extends ResultStatus {
|
||||||
|
job: AgentJobInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAgentJobResult extends ResultStatus {
|
||||||
|
job: AgentJobInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateAgentJobStepResult extends ResultStatus {
|
||||||
|
step: AgentJobStepInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAgentJobStepResult extends ResultStatus {
|
||||||
|
step: AgentJobStepInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateAgentProxyResult extends ResultStatus {
|
||||||
|
step: AgentJobStepInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAgentProxyResult extends ResultStatus {
|
||||||
|
step: AgentJobStepInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentAlertsResult extends ResultStatus {
|
||||||
|
alerts: AgentAlertInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateAgentAlertResult extends ResultStatus {
|
||||||
|
alert: AgentJobStepInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAgentAlertResult extends ResultStatus {
|
||||||
|
alert: AgentJobStepInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentOperatorsResult extends ResultStatus {
|
||||||
|
operators: AgentOperatorInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateAgentOperatorResult extends ResultStatus {
|
||||||
|
operator: AgentOperatorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAgentOperatorResult extends ResultStatus {
|
||||||
|
operator: AgentOperatorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentProxiesResult extends ResultStatus {
|
||||||
|
operators: AgentOperatorInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateAgentProxyResult extends ResultStatus {
|
||||||
|
operator: AgentOperatorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAgentProxyResult extends ResultStatus {
|
||||||
|
operator: AgentOperatorInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentServicesProvider extends DataProvider {
|
export interface AgentServicesProvider extends DataProvider {
|
||||||
getJobs(connectionUri: string): Thenable<AgentJobsResult>;
|
// Job management methods
|
||||||
getJobHistory(connectionUri: string, jobId: string): Thenable<AgentJobHistoryResult>;
|
getJobs(ownerUri: string): Thenable<AgentJobsResult>;
|
||||||
jobAction(connectionUri: string, jobName: string, action: string): Thenable<AgentJobActionResult>;
|
getJobHistory(ownerUri: string, jobId: string): Thenable<AgentJobHistoryResult>;
|
||||||
|
jobAction(ownerUri: string, jobName: string, action: string): Thenable<ResultStatus>;
|
||||||
|
createJob(ownerUri: string, jobInfo: AgentJobInfo): Thenable<CreateAgentJobResult>;
|
||||||
|
updateJob(ownerUri: string, originalJobName: string, jobInfo: AgentJobInfo): Thenable<UpdateAgentJobResult>;
|
||||||
|
deleteJob(ownerUri: string, jobInfo: AgentJobInfo): Thenable<ResultStatus>;
|
||||||
|
|
||||||
|
// Job Step management methods
|
||||||
|
createJobStep(ownerUri: string, jobInfo: AgentJobStepInfo): Thenable<CreateAgentJobStepResult>;
|
||||||
|
updateJobStep(ownerUri: string, originalJobStepName: string, jobInfo: AgentJobStepInfo): Thenable<UpdateAgentJobStepResult>;
|
||||||
|
deleteJobStep(ownerUri: string, jobInfo: AgentJobStepInfo): Thenable<ResultStatus>;
|
||||||
|
|
||||||
|
// Alert management methods
|
||||||
|
getAlerts(ownerUri: string): Thenable<AgentAlertsResult>;
|
||||||
|
createAlert(ownerUri: string, alertInfo: AgentAlertInfo): Thenable<CreateAgentAlertResult>;
|
||||||
|
updateAlert(ownerUri: string, originalAlertName: string, alertInfo: AgentAlertInfo): Thenable<UpdateAgentAlertResult>;
|
||||||
|
deleteAlert(ownerUri: string, alertInfo: AgentAlertInfo): Thenable<ResultStatus>;
|
||||||
|
|
||||||
|
// Operator management methods
|
||||||
|
getOperators(ownerUri: string): Thenable<AgentOperatorsResult>;
|
||||||
|
createOperator(ownerUri: string, operatorInfo: AgentOperatorInfo): Thenable<CreateAgentOperatorResult>;
|
||||||
|
updateOperator(ownerUri: string, originalOperatorName: string, operatorInfo: AgentOperatorInfo): Thenable<UpdateAgentOperatorResult>;
|
||||||
|
deleteOperator(ownerUri: string, operatorInfo: AgentOperatorInfo): Thenable<ResultStatus>;
|
||||||
|
|
||||||
|
// Proxy management methods
|
||||||
|
getProxies(ownerUri: string): Thenable<AgentProxiesResult>;
|
||||||
|
createProxy(ownerUri: string, proxyInfo: AgentProxyInfo): Thenable<CreateAgentOperatorResult>;
|
||||||
|
updateProxy(ownerUri: string, originalProxyName: string, proxyInfo: AgentProxyInfo): Thenable<UpdateAgentOperatorResult>;
|
||||||
|
deleteProxy(ownerUri: string, proxyInfo: AgentProxyInfo): Thenable<ResultStatus>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Task service interfaces ----------------------------------------------------------------------------
|
// Task service interfaces ----------------------------------------------------------------------------
|
||||||
@@ -1242,6 +1409,7 @@ declare module 'sqlops' {
|
|||||||
disconnectSession(sessionId: string): Thenable<boolean>;
|
disconnectSession(sessionId: string): Thenable<boolean>;
|
||||||
|
|
||||||
registerOnSessionEventsAvailable(handler: (response: ProfilerSessionEvents) => any): void;
|
registerOnSessionEventsAvailable(handler: (response: ProfilerSessionEvents) => any): void;
|
||||||
|
registerOnSessionStopped(handler: (response: ProfilerSessionStoppedParams) => any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IProfilerTableRow {
|
export interface IProfilerTableRow {
|
||||||
@@ -1282,6 +1450,15 @@ declare module 'sqlops' {
|
|||||||
sessionId: string;
|
sessionId: string;
|
||||||
|
|
||||||
events: ProfilerEvent[];
|
events: ProfilerEvent[];
|
||||||
|
|
||||||
|
eventsLost: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProfilerSessionStoppedParams {
|
||||||
|
|
||||||
|
ownerUri: string;
|
||||||
|
|
||||||
|
sessionId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// File browser interfaces -----------------------------------------------------------------------
|
// File browser interfaces -----------------------------------------------------------------------
|
||||||
|
|||||||
106
src/sql/sqlops.proposed.d.ts
vendored
106
src/sql/sqlops.proposed.d.ts
vendored
@@ -26,7 +26,9 @@ declare module 'sqlops' {
|
|||||||
text(): ComponentBuilder<TextComponent>;
|
text(): ComponentBuilder<TextComponent>;
|
||||||
button(): ComponentBuilder<ButtonComponent>;
|
button(): ComponentBuilder<ButtonComponent>;
|
||||||
dropDown(): ComponentBuilder<DropDownComponent>;
|
dropDown(): ComponentBuilder<DropDownComponent>;
|
||||||
|
listBox(): ComponentBuilder<ListBoxComponent>;
|
||||||
table(): ComponentBuilder<TableComponent>;
|
table(): ComponentBuilder<TableComponent>;
|
||||||
|
declarativeTable(): ComponentBuilder<DeclarativeTableComponent>;
|
||||||
dashboardWidget(widgetId: string): ComponentBuilder<DashboardWidgetComponent>;
|
dashboardWidget(widgetId: string): ComponentBuilder<DashboardWidgetComponent>;
|
||||||
dashboardWebview(webviewId: string): ComponentBuilder<DashboardWebviewComponent>;
|
dashboardWebview(webviewId: string): ComponentBuilder<DashboardWebviewComponent>;
|
||||||
formContainer(): FormBuilder;
|
formContainer(): FormBuilder;
|
||||||
@@ -104,11 +106,11 @@ declare module 'sqlops' {
|
|||||||
/**
|
/**
|
||||||
* Sends any updated properties of the component to the UI
|
* Sends any updated properties of the component to the UI
|
||||||
*
|
*
|
||||||
* @returns {Thenable<boolean>} Thenable that completes once the update
|
* @returns {Thenable<void>} Thenable that completes once the update
|
||||||
* has been applied in the UI
|
* has been applied in the UI
|
||||||
* @memberof Component
|
* @memberof Component
|
||||||
*/
|
*/
|
||||||
updateProperties(properties: { [key: string]: any }): Thenable<boolean>;
|
updateProperties(properties: { [key: string]: any }): Thenable<void>;
|
||||||
|
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
/**
|
/**
|
||||||
@@ -205,7 +207,20 @@ declare module 'sqlops' {
|
|||||||
*/
|
*/
|
||||||
alignContent?: string;
|
alignContent?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container Height
|
||||||
|
*/
|
||||||
height?: number | string;
|
height?: number | string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container Width
|
||||||
|
*/
|
||||||
|
width?: number | string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
textAlign?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FlexItemLayout {
|
export interface FlexItemLayout {
|
||||||
@@ -222,11 +237,13 @@ declare module 'sqlops' {
|
|||||||
|
|
||||||
export interface FormItemLayout {
|
export interface FormItemLayout {
|
||||||
horizontal?: boolean;
|
horizontal?: boolean;
|
||||||
componentWidth?: number;
|
componentWidth?: number | string;
|
||||||
|
componentHeight?: number | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormLayout {
|
export interface FormLayout {
|
||||||
width?: number;
|
width?: number | string;
|
||||||
|
height?: number | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupLayout {
|
export interface GroupLayout {
|
||||||
@@ -292,21 +309,29 @@ declare module 'sqlops' {
|
|||||||
|
|
||||||
export type InputBoxInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'range' | 'search' | 'text' | 'time' | 'url' | 'week';
|
export type InputBoxInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'range' | 'search' | 'text' | 'time' | 'url' | 'week';
|
||||||
|
|
||||||
export interface InputBoxProperties {
|
export interface ComponentProperties {
|
||||||
|
height?: number | string;
|
||||||
|
width?: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputBoxProperties extends ComponentProperties {
|
||||||
value?: string;
|
value?: string;
|
||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
placeHolder?: string;
|
placeHolder?: string;
|
||||||
height: number;
|
|
||||||
width: number;
|
|
||||||
inputType?: InputBoxInputType;
|
inputType?: InputBoxInputType;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
|
multiline?: boolean;
|
||||||
|
rows?: number;
|
||||||
|
columns?: number;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TableColumn {
|
export interface TableColumn {
|
||||||
value: string
|
value: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TableComponentProperties {
|
export interface TableComponentProperties extends ComponentProperties {
|
||||||
data: any[][];
|
data: any[][];
|
||||||
columns: string[] | TableColumn[];
|
columns: string[] | TableColumn[];
|
||||||
selectedRows?: number[];
|
selectedRows?: number[];
|
||||||
@@ -317,6 +342,12 @@ declare module 'sqlops' {
|
|||||||
label?: string;
|
label?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum DeclarativeDataType {
|
||||||
|
string = 'string',
|
||||||
|
category = 'category',
|
||||||
|
boolean = 'boolean'
|
||||||
|
}
|
||||||
|
|
||||||
export interface RadioButtonProperties {
|
export interface RadioButtonProperties {
|
||||||
name?: string;
|
name?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
@@ -328,18 +359,37 @@ declare module 'sqlops' {
|
|||||||
value?: string;
|
value?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DropDownProperties {
|
export interface DropDownProperties extends ComponentProperties {
|
||||||
value?: string;
|
value?: string | CategoryValue;
|
||||||
values?: string[];
|
values?: string[] | CategoryValue[];
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DeclarativeTableColumn {
|
||||||
|
displayName: string;
|
||||||
|
categoryValues: CategoryValue[];
|
||||||
|
valueType: DeclarativeDataType;
|
||||||
|
isReadOnly: boolean;
|
||||||
|
width: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeclarativeTableProperties {
|
||||||
|
data: any[][];
|
||||||
|
columns: DeclarativeTableColumn[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ListBoxProperties {
|
||||||
|
selectedRow?: number;
|
||||||
|
values?: string[];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export interface WebViewProperties {
|
export interface WebViewProperties {
|
||||||
message?: any;
|
message?: any;
|
||||||
html?: string;
|
html?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ButtonProperties {
|
export interface ButtonProperties extends ComponentProperties {
|
||||||
label?: string;
|
label?: string;
|
||||||
iconPath?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
|
iconPath?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
|
||||||
}
|
}
|
||||||
@@ -374,11 +424,27 @@ declare module 'sqlops' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DropDownComponent extends Component, DropDownProperties {
|
export interface DropDownComponent extends Component, DropDownProperties {
|
||||||
value: string;
|
value: string | CategoryValue;
|
||||||
values: string[];
|
values: string[] | CategoryValue[];
|
||||||
onValueChanged: vscode.Event<any>;
|
onValueChanged: vscode.Event<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TableCell {
|
||||||
|
row: number;
|
||||||
|
column: number;
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeclarativeTableComponent extends Component, DeclarativeTableProperties {
|
||||||
|
onDataChanged: vscode.Event<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ListBoxComponent extends Component, ListBoxProperties {
|
||||||
|
selectedRow?: number;
|
||||||
|
values: string[];
|
||||||
|
onRowSelected: vscode.Event<any>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface TableComponent extends Component, TableComponentProperties {
|
export interface TableComponent extends Component, TableComponentProperties {
|
||||||
onRowSelected: vscode.Event<any>;
|
onRowSelected: vscode.Event<any>;
|
||||||
}
|
}
|
||||||
@@ -609,7 +675,7 @@ declare module 'sqlops' {
|
|||||||
lastPage: number,
|
lastPage: number,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The new page number
|
* The new page number or undefined if the user is closing the wizard
|
||||||
*/
|
*/
|
||||||
newPage: number
|
newPage: number
|
||||||
}
|
}
|
||||||
@@ -722,6 +788,16 @@ declare module 'sqlops' {
|
|||||||
* Close the wizard. Does nothing if the wizard is not open.
|
* Close the wizard. Does nothing if the wizard is not open.
|
||||||
*/
|
*/
|
||||||
close(): Thenable<void>;
|
close(): Thenable<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a callback that will be called when the user tries to navigate by
|
||||||
|
* changing pages or clicking done. Only one callback can be registered at once, so
|
||||||
|
* each registration call will clear the previous registration.
|
||||||
|
* @param validator The callback that gets executed when the user tries to
|
||||||
|
* navigate. Return true to allow the navigation to proceed, or false to
|
||||||
|
* cancel it.
|
||||||
|
*/
|
||||||
|
registerNavigationValidator(validator: (pageChangeInfo: WizardPageChangeInfo) => boolean | Thenable<boolean>): void;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,12 +63,45 @@ export enum ScriptOperation {
|
|||||||
Alter = 6
|
Alter = 6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum WeekDays
|
||||||
|
{
|
||||||
|
sunday = 1,
|
||||||
|
monday = 2,
|
||||||
|
tuesday = 4,
|
||||||
|
wednesday = 8,
|
||||||
|
thursday = 16,
|
||||||
|
friday = 32,
|
||||||
|
weekDays = 62,
|
||||||
|
saturday = 64,
|
||||||
|
weekEnds = 65,
|
||||||
|
everyDay = 127
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum NotifyMethods
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
notifyEmail = 1,
|
||||||
|
pager = 2,
|
||||||
|
netSend = 4,
|
||||||
|
notifyAll = 7
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AlertType
|
||||||
|
{
|
||||||
|
sqlServerEvent = 1,
|
||||||
|
sqlServerPerformanceCondition = 2,
|
||||||
|
nonSqlServerEvent = 3,
|
||||||
|
wmiEvent = 4
|
||||||
|
}
|
||||||
|
|
||||||
export enum ModelComponentTypes {
|
export enum ModelComponentTypes {
|
||||||
NavContainer,
|
NavContainer,
|
||||||
FlexContainer,
|
FlexContainer,
|
||||||
Card,
|
Card,
|
||||||
InputBox,
|
InputBox,
|
||||||
DropDown,
|
DropDown,
|
||||||
|
DeclarativeTable,
|
||||||
|
ListBox,
|
||||||
Button,
|
Button,
|
||||||
CheckBox,
|
CheckBox,
|
||||||
RadioButton,
|
RadioButton,
|
||||||
@@ -185,3 +218,9 @@ export enum DataProviderType {
|
|||||||
AgentServicesProvider = 'AgentServicesProvider',
|
AgentServicesProvider = 'AgentServicesProvider',
|
||||||
CapabilitiesProvider = 'CapabilitiesProvider'
|
CapabilitiesProvider = 'CapabilitiesProvider'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum DeclarativeDataType {
|
||||||
|
string = 'string',
|
||||||
|
category = 'category',
|
||||||
|
boolean = 'boolean'
|
||||||
|
}
|
||||||
@@ -503,6 +503,14 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
|
|||||||
return this._resolveProvider<sqlops.ProfilerProvider>(handle).stopSession(sessionId);
|
return this._resolveProvider<sqlops.ProfilerProvider>(handle).stopSession(sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pause a profiler session
|
||||||
|
*/
|
||||||
|
public $pauseSession(handle: number, sessionId: string): Thenable<boolean> {
|
||||||
|
return this._resolveProvider<sqlops.ProfilerProvider>(handle).pauseSession(sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Profiler session events available notification
|
* Profiler session events available notification
|
||||||
*/
|
*/
|
||||||
@@ -510,6 +518,13 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
|
|||||||
this._proxy.$onSessionEventsAvailable(handle, response);
|
this._proxy.$onSessionEventsAvailable(handle, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Profiler session stopped unexpectedly notification
|
||||||
|
*/
|
||||||
|
public $onSessionStopped(handle: number, response: sqlops.ProfilerSessionStoppedParams): void {
|
||||||
|
this._proxy.$onSessionStopped(handle, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Agent Job Provider methods
|
* Agent Job Provider methods
|
||||||
@@ -532,7 +547,7 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
|
|||||||
/**
|
/**
|
||||||
* Run an action on a job
|
* Run an action on a job
|
||||||
*/
|
*/
|
||||||
public $jobAction(handle: number, ownerUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult> {
|
public $jobAction(handle: number, ownerUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
|
||||||
return this._resolveProvider<sqlops.AgentServicesProvider>(handle).jobAction(ownerUri, jobName, action);
|
return this._resolveProvider<sqlops.AgentServicesProvider>(handle).jobAction(ownerUri, jobName, action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
|
|||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listBox(): sqlops.ComponentBuilder<sqlops.ListBoxComponent> {
|
||||||
|
let id = this.getNextComponentId();
|
||||||
|
let builder: ComponentBuilderImpl<sqlops.ListBoxComponent> = this.getComponentBuilder(new ListBoxWrapper(this._proxy, this._handle, id), id);
|
||||||
|
this._componentBuilders.set(id, builder);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
table(): sqlops.ComponentBuilder<sqlops.TableComponent> {
|
table(): sqlops.ComponentBuilder<sqlops.TableComponent> {
|
||||||
let id = this.getNextComponentId();
|
let id = this.getNextComponentId();
|
||||||
let builder: ComponentBuilderImpl<sqlops.TableComponent> = this.getComponentBuilder(new TableComponentWrapper(this._proxy, this._handle, id), id);
|
let builder: ComponentBuilderImpl<sqlops.TableComponent> = this.getComponentBuilder(new TableComponentWrapper(this._proxy, this._handle, id), id);
|
||||||
@@ -123,6 +130,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
|
|||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declarativeTable(): sqlops.ComponentBuilder<sqlops.DeclarativeTableComponent> {
|
||||||
|
let id = this.getNextComponentId();
|
||||||
|
let builder: ComponentBuilderImpl<sqlops.DeclarativeTableComponent> = this.getComponentBuilder(new DeclarativeTableWrapper(this._proxy, this._handle, id), id);
|
||||||
|
this._componentBuilders.set(id, builder);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
dashboardWidget(widgetId: string): sqlops.ComponentBuilder<sqlops.DashboardWidgetComponent> {
|
dashboardWidget(widgetId: string): sqlops.ComponentBuilder<sqlops.DashboardWidgetComponent> {
|
||||||
let id = this.getNextComponentId();
|
let id = this.getNextComponentId();
|
||||||
let builder = this.getComponentBuilder<sqlops.DashboardWidgetComponent>(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWidget, id), id);
|
let builder = this.getComponentBuilder<sqlops.DashboardWidgetComponent>(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWidget, id), id);
|
||||||
@@ -328,7 +342,6 @@ class InternalItemConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ComponentWrapper implements sqlops.Component {
|
class ComponentWrapper implements sqlops.Component {
|
||||||
public properties: { [key: string]: any } = {};
|
public properties: { [key: string]: any } = {};
|
||||||
public layout: any;
|
public layout: any;
|
||||||
@@ -371,6 +384,22 @@ class ComponentWrapper implements sqlops.Component {
|
|||||||
this.setProperty('enabled', value);
|
this.setProperty('enabled', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get height(): number | string {
|
||||||
|
return this.properties['height'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public set height(v: number | string) {
|
||||||
|
this.setProperty('height', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get width(): number | string {
|
||||||
|
return this.properties['width'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public set width(v: number | string) {
|
||||||
|
this.setProperty('width', v);
|
||||||
|
}
|
||||||
|
|
||||||
public toComponentShape(): IComponentShape {
|
public toComponentShape(): IComponentShape {
|
||||||
return <IComponentShape>{
|
return <IComponentShape>{
|
||||||
id: this.id,
|
id: this.id,
|
||||||
@@ -406,12 +435,13 @@ class ComponentWrapper implements sqlops.Component {
|
|||||||
return this._proxy.$setLayout(this._handle, this.id, layout);
|
return this._proxy.$setLayout(this._handle, this.id, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateProperties(): Thenable<boolean> {
|
public updateProperties(properties: { [key: string]: any }): Thenable<void> {
|
||||||
|
this.properties = Object.assign(this.properties, properties);
|
||||||
return this.notifyPropertyChanged();
|
return this.notifyPropertyChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected notifyPropertyChanged(): Thenable<boolean> {
|
protected notifyPropertyChanged(): Thenable<void> {
|
||||||
return this._proxy.$setProperties(this._handle, this._id, this.properties).then(() => true);
|
return this._proxy.$setProperties(this._handle, this._id, this.properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public registerEvent(): Thenable<boolean> {
|
public registerEvent(): Thenable<boolean> {
|
||||||
@@ -421,9 +451,9 @@ class ComponentWrapper implements sqlops.Component {
|
|||||||
public onEvent(eventArgs: IComponentEventArgs) {
|
public onEvent(eventArgs: IComponentEventArgs) {
|
||||||
if (eventArgs && eventArgs.eventType === ComponentEventType.PropertiesChanged) {
|
if (eventArgs && eventArgs.eventType === ComponentEventType.PropertiesChanged) {
|
||||||
this.properties = eventArgs.args;
|
this.properties = eventArgs.args;
|
||||||
}
|
} else if (eventArgs && eventArgs.eventType === ComponentEventType.validityChanged) {
|
||||||
else if (eventArgs && eventArgs.eventType === ComponentEventType.validityChanged) {
|
|
||||||
this._valid = eventArgs.args;
|
this._valid = eventArgs.args;
|
||||||
|
this._onValidityChangedEmitter.fire(this._valid);
|
||||||
} else if (eventArgs) {
|
} else if (eventArgs) {
|
||||||
let emitter = this._emitterMap.get(eventArgs.eventType);
|
let emitter = this._emitterMap.get(eventArgs.eventType);
|
||||||
if (emitter) {
|
if (emitter) {
|
||||||
@@ -432,13 +462,13 @@ class ComponentWrapper implements sqlops.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async setProperty(key: string, value: any): Promise<boolean> {
|
protected async setProperty(key: string, value: any): Promise<void> {
|
||||||
if (!this.properties[key] || this.properties[key] !== value) {
|
if (!this.properties[key] || this.properties[key] !== value) {
|
||||||
// Only notify the front end if a value has been updated
|
// Only notify the front end if a value has been updated
|
||||||
this.properties[key] = value;
|
this.properties[key] = value;
|
||||||
return this.notifyPropertyChanged();
|
return this.notifyPropertyChanged();
|
||||||
}
|
}
|
||||||
return Promise.resolve(true);
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleError(err: Error): void {
|
private handleError(err: Error): void {
|
||||||
@@ -538,18 +568,39 @@ class InputBoxWrapper extends ComponentWrapper implements sqlops.InputBoxCompone
|
|||||||
this.setProperty('placeHolder', v);
|
this.setProperty('placeHolder', v);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get height(): number {
|
public get rows(): number {
|
||||||
return this.properties['height'];
|
return this.properties['rows'];
|
||||||
}
|
}
|
||||||
public set height(v: number) {
|
public set rows(v: number) {
|
||||||
this.setProperty('height', v);
|
this.setProperty('rows', v);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get width(): number {
|
public get min(): number {
|
||||||
return this.properties['width'];
|
return this.properties['min'];
|
||||||
}
|
}
|
||||||
public set width(v: number) {
|
public set min(v: number) {
|
||||||
this.setProperty('width', v);
|
this.setProperty('min', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get max(): number {
|
||||||
|
return this.properties['max'];
|
||||||
|
}
|
||||||
|
public set max(v: number) {
|
||||||
|
this.setProperty('max', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get columns(): number {
|
||||||
|
return this.properties['columns'];
|
||||||
|
}
|
||||||
|
public set columns(v: number) {
|
||||||
|
this.setProperty('columns', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get multiline(): boolean {
|
||||||
|
return this.properties['multiline'];
|
||||||
|
}
|
||||||
|
public set multiline(v: boolean) {
|
||||||
|
this.setProperty('multiline', v);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get inputType(): sqlops.InputBoxInputType {
|
public get inputType(): sqlops.InputBoxInputType {
|
||||||
@@ -720,17 +771,17 @@ class DropDownWrapper extends ComponentWrapper implements sqlops.DropDownCompone
|
|||||||
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<any>());
|
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<any>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public get value(): string {
|
public get value(): string | sqlops.CategoryValue {
|
||||||
return this.properties['value'];
|
return this.properties['value'];
|
||||||
}
|
}
|
||||||
public set value(v: string) {
|
public set value(v: string | sqlops.CategoryValue) {
|
||||||
this.setProperty('value', v);
|
this.setProperty('value', v);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get values(): string[] {
|
public get values(): string[] | sqlops.CategoryValue[] {
|
||||||
return this.properties['values'];
|
return this.properties['values'];
|
||||||
}
|
}
|
||||||
public set values(v: string[]) {
|
public set values(v: string[] | sqlops.CategoryValue[]) {
|
||||||
this.setProperty('values', v);
|
this.setProperty('values', v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -747,6 +798,63 @@ class DropDownWrapper extends ComponentWrapper implements sqlops.DropDownCompone
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DeclarativeTableWrapper extends ComponentWrapper implements sqlops.DeclarativeTableComponent {
|
||||||
|
|
||||||
|
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
|
||||||
|
super(proxy, handle, ModelComponentTypes.DeclarativeTable, id);
|
||||||
|
this.properties = {};
|
||||||
|
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<any>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): any[][] {
|
||||||
|
return this.properties['data'];
|
||||||
|
}
|
||||||
|
public set data(v: any[][]) {
|
||||||
|
this.setProperty('data', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get columns(): sqlops.DeclarativeTableColumn[] {
|
||||||
|
return this.properties['columns'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public set columns(v: sqlops.DeclarativeTableColumn[]) {
|
||||||
|
this.setProperty('columns', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get onDataChanged(): vscode.Event<any> {
|
||||||
|
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
|
||||||
|
return emitter && emitter.event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListBoxWrapper extends ComponentWrapper implements sqlops.ListBoxComponent {
|
||||||
|
|
||||||
|
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
|
||||||
|
super(proxy, handle, ModelComponentTypes.ListBox, id);
|
||||||
|
this.properties = {};
|
||||||
|
this._emitterMap.set(ComponentEventType.onSelectedRowChanged, new Emitter<any>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public get selectedRow(): number {
|
||||||
|
return this.properties['selectedRow'];
|
||||||
|
}
|
||||||
|
public set selectedRow(v: number) {
|
||||||
|
this.setProperty('selectedRow', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get values(): string[] {
|
||||||
|
return this.properties['values'];
|
||||||
|
}
|
||||||
|
public set values(v: string[]) {
|
||||||
|
this.setProperty('values', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get onRowSelected(): vscode.Event<any> {
|
||||||
|
let emitter = this._emitterMap.get(ComponentEventType.onSelectedRowChanged);
|
||||||
|
return emitter && emitter.event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ButtonWrapper extends ComponentWrapper implements sqlops.ButtonComponent {
|
class ButtonWrapper extends ComponentWrapper implements sqlops.ButtonComponent {
|
||||||
|
|
||||||
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
|
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ class WizardImpl implements sqlops.window.modelviewdialog.Wizard {
|
|||||||
public customButtons: sqlops.window.modelviewdialog.Button[];
|
public customButtons: sqlops.window.modelviewdialog.Button[];
|
||||||
private _pageChangedEmitter = new Emitter<sqlops.window.modelviewdialog.WizardPageChangeInfo>();
|
private _pageChangedEmitter = new Emitter<sqlops.window.modelviewdialog.WizardPageChangeInfo>();
|
||||||
public readonly onPageChanged = this._pageChangedEmitter.event;
|
public readonly onPageChanged = this._pageChangedEmitter.event;
|
||||||
|
private _navigationValidator: (info: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>;
|
||||||
|
|
||||||
constructor(public title: string, private _extHostModelViewDialog: ExtHostModelViewDialog) {
|
constructor(public title: string, private _extHostModelViewDialog: ExtHostModelViewDialog) {
|
||||||
this.doneButton = this._extHostModelViewDialog.createButton(DONE_LABEL);
|
this.doneButton = this._extHostModelViewDialog.createButton(DONE_LABEL);
|
||||||
@@ -225,6 +226,7 @@ class WizardImpl implements sqlops.window.modelviewdialog.Wizard {
|
|||||||
this.nextButton = this._extHostModelViewDialog.createButton(NEXT_LABEL);
|
this.nextButton = this._extHostModelViewDialog.createButton(NEXT_LABEL);
|
||||||
this.backButton = this._extHostModelViewDialog.createButton(PREVIOUS_LABEL);
|
this.backButton = this._extHostModelViewDialog.createButton(PREVIOUS_LABEL);
|
||||||
this._extHostModelViewDialog.registerWizardPageInfoChangedCallback(this, info => this.handlePageInfoChanged(info));
|
this._extHostModelViewDialog.registerWizardPageInfoChangedCallback(this, info => this.handlePageInfoChanged(info));
|
||||||
|
this._currentPage = 0;
|
||||||
this.onPageChanged(info => this._currentPage = info.newPage);
|
this.onPageChanged(info => this._currentPage = info.newPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,6 +256,18 @@ class WizardImpl implements sqlops.window.modelviewdialog.Wizard {
|
|||||||
return this._extHostModelViewDialog.closeWizard(this);
|
return this._extHostModelViewDialog.closeWizard(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public registerNavigationValidator(validator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>): void {
|
||||||
|
this._navigationValidator = validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public validateNavigation(info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean> {
|
||||||
|
if (this._navigationValidator) {
|
||||||
|
return Promise.resolve(this._navigationValidator(info));
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private handlePageInfoChanged(info: WizardPageEventInfo): void {
|
private handlePageInfoChanged(info: WizardPageEventInfo): void {
|
||||||
this._currentPage = info.pageChangeInfo.newPage;
|
this._currentPage = info.pageChangeInfo.newPage;
|
||||||
if (info.eventType === WizardPageInfoEventType.PageAddedOrRemoved) {
|
if (info.eventType === WizardPageInfoEventType.PageAddedOrRemoved) {
|
||||||
@@ -335,6 +349,11 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public $validateNavigation(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean> {
|
||||||
|
let wizard = this._objectsByHandle.get(handle) as WizardImpl;
|
||||||
|
return wizard.validateNavigation(info);
|
||||||
|
}
|
||||||
|
|
||||||
public openDialog(dialog: sqlops.window.modelviewdialog.Dialog): void {
|
public openDialog(dialog: sqlops.window.modelviewdialog.Dialog): void {
|
||||||
let handle = this.getHandle(dialog);
|
let handle = this.getHandle(dialog);
|
||||||
this.updateDialogContent(dialog);
|
this.updateDialogContent(dialog);
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
|
|||||||
return self._proxy.$stopSession(handle, sessionId);
|
return self._proxy.$stopSession(handle, sessionId);
|
||||||
},
|
},
|
||||||
pauseSession(sessionId: string): Thenable<boolean> {
|
pauseSession(sessionId: string): Thenable<boolean> {
|
||||||
return TPromise.as(true);
|
return self._proxy.$pauseSession(handle, sessionId);
|
||||||
},
|
},
|
||||||
connectSession(sessionId: string): Thenable<boolean> {
|
connectSession(sessionId: string): Thenable<boolean> {
|
||||||
return TPromise.as(true);
|
return TPromise.as(true);
|
||||||
@@ -339,7 +339,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
|
|||||||
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> {
|
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> {
|
||||||
return self._proxy.$getJobHistory(handle, connectionUri, jobID);
|
return self._proxy.$getJobHistory(handle, connectionUri, jobID);
|
||||||
},
|
},
|
||||||
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult> {
|
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
|
||||||
return self._proxy.$jobAction(handle, connectionUri, jobName, action);
|
return self._proxy.$jobAction(handle, connectionUri, jobName, action);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -432,6 +432,10 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
|
|||||||
this._profilerService.onMoreRows(response);
|
this._profilerService.onMoreRows(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public $onSessionStopped(handle: number, response: sqlops.ProfilerSessionStoppedParams): void {
|
||||||
|
this._profilerService.onSessionStopped(response);
|
||||||
|
}
|
||||||
|
|
||||||
public $unregisterProvider(handle: number): TPromise<any> {
|
public $unregisterProvider(handle: number): TPromise<any> {
|
||||||
let capabilitiesRegistration = this._capabilitiesRegistrations[handle];
|
let capabilitiesRegistration = this._capabilitiesRegistrations[handle];
|
||||||
if (capabilitiesRegistration) {
|
if (capabilitiesRegistration) {
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
|||||||
wizard.onPageChanged(info => this._proxy.$onWizardPageChanged(handle, info));
|
wizard.onPageChanged(info => this._proxy.$onWizardPageChanged(handle, info));
|
||||||
wizard.onPageAdded(() => this.handleWizardPageAddedOrRemoved(handle));
|
wizard.onPageAdded(() => this.handleWizardPageAddedOrRemoved(handle));
|
||||||
wizard.onPageRemoved(() => this.handleWizardPageAddedOrRemoved(handle));
|
wizard.onPageRemoved(() => this.handleWizardPageAddedOrRemoved(handle));
|
||||||
|
wizard.registerNavigationValidator(info => this.validateNavigation(handle, info));
|
||||||
this._wizards.set(handle, wizard);
|
this._wizards.set(handle, wizard);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,4 +255,8 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
|||||||
let wizard = this._wizards.get(handle);
|
let wizard = this._wizards.get(handle);
|
||||||
this._proxy.$updateWizardPageInfo(handle, wizard.pages.map(page => this._wizardPageHandles.get(page)), wizard.currentPage);
|
this._proxy.$updateWizardPageInfo(handle, wizard.pages.map(page => this._wizardPageHandles.get(page)), wizard.currentPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private validateNavigation(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean> {
|
||||||
|
return this._proxy.$validateNavigation(handle, info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -237,6 +237,10 @@ export function createApiFactory(
|
|||||||
extHostDataProvider.$onSessionEventsAvailable(provider.handle, response);
|
extHostDataProvider.$onSessionEventsAvailable(provider.handle, response);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
provider.registerOnSessionStopped((response: sqlops.ProfilerSessionStoppedParams) => {
|
||||||
|
extHostDataProvider.$onSessionStopped(provider.handle, response);
|
||||||
|
});
|
||||||
|
|
||||||
return extHostDataProvider.$registerProfilerProvider(provider);
|
return extHostDataProvider.$registerProfilerProvider(provider);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -369,6 +373,7 @@ export function createApiFactory(
|
|||||||
serialization,
|
serialization,
|
||||||
dataprotocol,
|
dataprotocol,
|
||||||
DataProviderType: sqlExtHostTypes.DataProviderType,
|
DataProviderType: sqlExtHostTypes.DataProviderType,
|
||||||
|
DeclarativeDataType: sqlExtHostTypes.DeclarativeDataType,
|
||||||
ServiceOptionType: sqlExtHostTypes.ServiceOptionType,
|
ServiceOptionType: sqlExtHostTypes.ServiceOptionType,
|
||||||
ConnectionOptionSpecialType: sqlExtHostTypes.ConnectionOptionSpecialType,
|
ConnectionOptionSpecialType: sqlExtHostTypes.ConnectionOptionSpecialType,
|
||||||
EditRowState: sqlExtHostTypes.EditRowState,
|
EditRowState: sqlExtHostTypes.EditRowState,
|
||||||
@@ -376,6 +381,9 @@ export function createApiFactory(
|
|||||||
TaskStatus: sqlExtHostTypes.TaskStatus,
|
TaskStatus: sqlExtHostTypes.TaskStatus,
|
||||||
TaskExecutionMode: sqlExtHostTypes.TaskExecutionMode,
|
TaskExecutionMode: sqlExtHostTypes.TaskExecutionMode,
|
||||||
ScriptOperation: sqlExtHostTypes.ScriptOperation,
|
ScriptOperation: sqlExtHostTypes.ScriptOperation,
|
||||||
|
WeekDays: sqlExtHostTypes.WeekDays,
|
||||||
|
NotifyMethods: sqlExtHostTypes.NotifyMethods,
|
||||||
|
AlertType: sqlExtHostTypes.AlertType,
|
||||||
window,
|
window,
|
||||||
tasks,
|
tasks,
|
||||||
dashboard,
|
dashboard,
|
||||||
|
|||||||
@@ -313,6 +313,11 @@ export abstract class ExtHostDataProtocolShape {
|
|||||||
*/
|
*/
|
||||||
$stopSession(handle: number, sessionId: string): Thenable<boolean> { throw ni(); }
|
$stopSession(handle: number, sessionId: string): Thenable<boolean> { throw ni(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pause a profiler session
|
||||||
|
*/
|
||||||
|
$pauseSession(handle: number, sessionId: string): Thenable<boolean> { throw ni(); }
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Agent Job list
|
* Get Agent Job list
|
||||||
@@ -327,7 +332,7 @@ export abstract class ExtHostDataProtocolShape {
|
|||||||
/**
|
/**
|
||||||
* Run an action on a Job
|
* Run an action on a Job
|
||||||
*/
|
*/
|
||||||
$jobAction(handle: number, ownerUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult> { throw ni(); }
|
$jobAction(handle: number, ownerUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> { throw ni(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -411,6 +416,7 @@ export interface MainThreadDataProtocolShape extends IDisposable {
|
|||||||
$onFilePathsValidated(handle: number, response: sqlops.FileBrowserValidatedParams): void;
|
$onFilePathsValidated(handle: number, response: sqlops.FileBrowserValidatedParams): void;
|
||||||
$onScriptingComplete(handle: number, message: sqlops.ScriptingCompleteResult): void;
|
$onScriptingComplete(handle: number, message: sqlops.ScriptingCompleteResult): void;
|
||||||
$onSessionEventsAvailable(handle: number, response: sqlops.ProfilerSessionEvents): void;
|
$onSessionEventsAvailable(handle: number, response: sqlops.ProfilerSessionEvents): void;
|
||||||
|
$onSessionStopped(handle: number, response: sqlops.ProfilerSessionStoppedParams): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback when a session has completed initialization
|
* Callback when a session has completed initialization
|
||||||
@@ -556,6 +562,7 @@ export interface ExtHostModelViewDialogShape {
|
|||||||
$onPanelValidityChanged(handle: number, valid: boolean): void;
|
$onPanelValidityChanged(handle: number, valid: boolean): void;
|
||||||
$onWizardPageChanged(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): void;
|
$onWizardPageChanged(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): void;
|
||||||
$updateWizardPageInfo(handle: number, pageHandles: number[], currentPageIndex: number): void;
|
$updateWizardPageInfo(handle: number, pageHandles: number[], currentPageIndex: number): void;
|
||||||
|
$validateNavigation(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MainThreadModelViewDialogShape extends IDisposable {
|
export interface MainThreadModelViewDialogShape extends IDisposable {
|
||||||
|
|||||||
@@ -29,14 +29,14 @@ export class OpenGettingStartedInBrowserAction extends Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run(): TPromise<any> {
|
run(): TPromise<any> {
|
||||||
const uri = URI.parse(product.releaseNotesUrl);
|
const uri = URI.parse(product.gettingStartedUrl);
|
||||||
return this.openerService.open(uri);
|
return this.openerService.open(uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesAction {
|
export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesAction {
|
||||||
|
|
||||||
static ID = 'update.showCurrentCarbonReleaseNotes';
|
static ID = 'update.showGettingStarted';
|
||||||
static LABEL = nls.localize('showReleaseNotes', "Show Getting Started");
|
static LABEL = nls.localize('showReleaseNotes', "Show Getting Started");
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -118,4 +118,14 @@ suite('ExtHostModelView Validation Tests', () => {
|
|||||||
});
|
});
|
||||||
assert.equal(inputBox.valid, true, 'Input box did not update validity to true based on the validityChanged event');
|
assert.equal(inputBox.valid, true, 'Input box did not update validity to true based on the validityChanged event');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Main thread validityChanged events cause component to fire validity changed events', () => {
|
||||||
|
let validityFromEvent: boolean = undefined;
|
||||||
|
inputBox.onValidityChanged(valid => validityFromEvent = valid);
|
||||||
|
extHostModelView.$handleEvent(handle, inputBox.id, {
|
||||||
|
eventType: ComponentEventType.validityChanged,
|
||||||
|
args: false
|
||||||
|
});
|
||||||
|
assert.equal(validityFromEvent, false, 'Main thread validityChanged event did not cause component to fire its own event');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import { Mock, It, Times } from 'typemoq';
|
import { Mock, It, Times } from 'typemoq';
|
||||||
import { ExtHostModelViewDialog } from 'sql/workbench/api/node/extHostModelViewDialog';
|
import { ExtHostModelViewDialog } from 'sql/workbench/api/node/extHostModelViewDialog';
|
||||||
@@ -263,4 +264,30 @@ suite('ExtHostModelViewDialog Tests', () => {
|
|||||||
extHostModelViewDialog.$onPanelValidityChanged(pageHandle, false);
|
extHostModelViewDialog.$onPanelValidityChanged(pageHandle, false);
|
||||||
assert.equal(page.valid, false);
|
assert.equal(page.valid, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Main thread can execute wizard navigation validation', () => {
|
||||||
|
// Set up the main thread mock to record the wizard handle
|
||||||
|
let wizardHandle: number;
|
||||||
|
mockProxy.setup(x => x.$setWizardDetails(It.isAny(), It.isAny())).callback((handle, details) => wizardHandle = handle);
|
||||||
|
|
||||||
|
// Create the wizard and add a validation that records that it has been called
|
||||||
|
let wizard = extHostModelViewDialog.createWizard('wizard_1');
|
||||||
|
extHostModelViewDialog.updateWizard(wizard);
|
||||||
|
let validationInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo;
|
||||||
|
wizard.registerNavigationValidator(info => {
|
||||||
|
validationInfo = info;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// If I call the validation from the main thread then it should run and record the correct page change info
|
||||||
|
let lastPage = 0;
|
||||||
|
let newPage = 1;
|
||||||
|
extHostModelViewDialog.$validateNavigation(wizardHandle, {
|
||||||
|
lastPage: lastPage,
|
||||||
|
newPage: newPage
|
||||||
|
});
|
||||||
|
assert.notEqual(validationInfo, undefined);
|
||||||
|
assert.equal(validationInfo.lastPage, lastPage);
|
||||||
|
assert.equal(validationInfo.newPage, newPage);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -59,7 +59,8 @@ suite('MainThreadModelViewDialog Tests', () => {
|
|||||||
$onButtonClick: handle => undefined,
|
$onButtonClick: handle => undefined,
|
||||||
$onPanelValidityChanged: (handle, valid) => undefined,
|
$onPanelValidityChanged: (handle, valid) => undefined,
|
||||||
$onWizardPageChanged: (handle, info) => undefined,
|
$onWizardPageChanged: (handle, info) => undefined,
|
||||||
$updateWizardPageInfo: (wizardHandle, pageHandles, currentPageIndex) => undefined
|
$updateWizardPageInfo: (wizardHandle, pageHandles, currentPageIndex) => undefined,
|
||||||
|
$validateNavigation: (handle, info) => undefined
|
||||||
});
|
});
|
||||||
let extHostContext = <IExtHostContext>{
|
let extHostContext = <IExtHostContext>{
|
||||||
getProxy: proxyType => mockExtHostModelViewDialog.object
|
getProxy: proxyType => mockExtHostModelViewDialog.object
|
||||||
@@ -316,4 +317,15 @@ suite('MainThreadModelViewDialog Tests', () => {
|
|||||||
It.is(pageHandles => pageHandles.length === 1 && pageHandles[0] === page2Handle),
|
It.is(pageHandles => pageHandles.length === 1 && pageHandles[0] === page2Handle),
|
||||||
It.is(currentPage => currentPage === 0)), Times.once());
|
It.is(currentPage => currentPage === 0)), Times.once());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Creating a wizard adds a navigation validation that calls the extension host', () => {
|
||||||
|
mockExtHostModelViewDialog.setup(x => x.$validateNavigation(It.isAny(), It.isAny()));
|
||||||
|
|
||||||
|
// If I call validateNavigation on the wizard that gets created
|
||||||
|
let wizard: Wizard = (mainThreadModelViewDialog as any).getWizard(wizardHandle);
|
||||||
|
wizard.validateNavigation(1);
|
||||||
|
|
||||||
|
// Then the call gets forwarded to the extension host
|
||||||
|
mockExtHostModelViewDialog.verify(x => x.$validateNavigation(It.is(handle => handle === wizardHandle), It.is(info => info.newPage === 1)), Times.once());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -248,7 +248,8 @@ export class ContextView {
|
|||||||
this.$view.hide();
|
this.$view.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
private isVisible(): boolean {
|
// {{SQL CARBON EDIT}}
|
||||||
|
public isVisible(): boolean {
|
||||||
return !!this.delegate;
|
return !!this.delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -364,7 +364,11 @@ export class InputBox extends Widget {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!errorMsg) {
|
if (errorMsg) {
|
||||||
|
this.inputElement.setAttribute('aria-invalid', 'true');
|
||||||
|
this.showMessage(errorMsg);
|
||||||
|
}
|
||||||
|
else if (this.inputElement.hasAttribute('aria-invalid')) {
|
||||||
this.inputElement.removeAttribute('aria-invalid');
|
this.inputElement.removeAttribute('aria-invalid');
|
||||||
this.hideMessage();
|
this.hideMessage();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -397,8 +397,16 @@ export class SelectBoxList implements ISelectBoxDelegate, IDelegate<ISelectOptio
|
|||||||
container.appendChild(this.selectDropDownContainer);
|
container.appendChild(this.selectDropDownContainer);
|
||||||
|
|
||||||
this.layoutSelectDropDown();
|
this.layoutSelectDropDown();
|
||||||
|
|
||||||
|
// {{SQL CARBON EDIT}}
|
||||||
return {
|
return {
|
||||||
dispose: () => container.removeChild(this.selectDropDownContainer) // remove to take out the CSS rules we add
|
dispose: () => {
|
||||||
|
try {
|
||||||
|
container.removeChild(this.selectDropDownContainer); // remove to take out the CSS rules we add
|
||||||
|
} catch(e) {
|
||||||
|
// if this fails it means it is already removed
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,21 +549,22 @@ export class SelectBoxList implements ISelectBoxDelegate, IDelegate<ISelectOptio
|
|||||||
|
|
||||||
this.selectList.setFocus([this.selected]);
|
this.selectList.setFocus([this.selected]);
|
||||||
this.selectList.reveal(this.selectList.getFocus()[0]);
|
this.selectList.reveal(this.selectList.getFocus()[0]);
|
||||||
|
|
||||||
|
// {{SQL CARBON EDIT}} - Update the selection before firing the handler instead of after
|
||||||
|
// Reset Selection Handler
|
||||||
|
this._currentSelection = -1;
|
||||||
|
this.hideSelectDropDown(true);
|
||||||
|
|
||||||
this._onDidSelect.fire({
|
this._onDidSelect.fire({
|
||||||
index: this.selectElement.selectedIndex,
|
index: this.selectElement.selectedIndex,
|
||||||
selected: this.selectElement.title
|
selected: this.selectElement.title
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset Selection Handler
|
|
||||||
this._currentSelection = -1;
|
|
||||||
this.hideSelectDropDown(true);
|
|
||||||
}
|
}
|
||||||
dom.EventHelper.stop(e);
|
dom.EventHelper.stop(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// List Exit - passive - hide drop-down, fire onDidSelect
|
// List Exit - passive - hide drop-down, fire onDidSelect
|
||||||
private onListBlur(): void {
|
private onListBlur(): void {
|
||||||
|
|
||||||
if (this._currentSelection >= 0) {
|
if (this._currentSelection >= 0) {
|
||||||
this.select(this._currentSelection);
|
this.select(this._currentSelection);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -771,7 +771,8 @@ export class IssueReporter extends Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryStringPrefix = product.reportIssueUrl.indexOf('?') === -1 ? '?' : '&';
|
// {{SQL CARBON EDIT}}
|
||||||
|
const queryStringPrefix = repositoryUrl.indexOf('?') === -1 ? '?' : '&';
|
||||||
return `${repositoryUrl}${queryStringPrefix}title=${encodeURIComponent(issueTitle)}`;
|
return `${repositoryUrl}${queryStringPrefix}title=${encodeURIComponent(issueTitle)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -999,8 +999,8 @@ export class CodeMenu {
|
|||||||
arrays.coalesce([
|
arrays.coalesce([
|
||||||
// new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miWelcome', comment: ['&& denotes a mnemonic'] }, "&&Welcome")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.showWelcomePage') }),
|
// new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miWelcome', comment: ['&& denotes a mnemonic'] }, "&&Welcome")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.showWelcomePage') }),
|
||||||
// {{SQL CARBON EDIT}}
|
// {{SQL CARBON EDIT}}
|
||||||
product.releaseNotesUrl ?
|
product.gettingStartedUrl ?
|
||||||
this.createMenuItem(nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "Getting &&Started"), 'update.showCurrentCarbonReleaseNotes')
|
this.createMenuItem(nls.localize({ key: 'miGettingStarted', comment: ['&& denotes a mnemonic'] }, "Getting &&Started"), 'update.showGettingStarted')
|
||||||
: null,
|
: null,
|
||||||
product.documentationUrl ?
|
product.documentationUrl ?
|
||||||
this.createMenuItem(nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation"), 'workbench.action.openDocumentationUrl')
|
this.createMenuItem(nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation"), 'workbench.action.openDocumentationUrl')
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export interface IProductConfiguration {
|
|||||||
};
|
};
|
||||||
documentationUrl: string;
|
documentationUrl: string;
|
||||||
releaseNotesUrl: string;
|
releaseNotesUrl: string;
|
||||||
|
gettingStartedUrl: string;
|
||||||
keyboardShortcutsUrlMac: string;
|
keyboardShortcutsUrlMac: string;
|
||||||
keyboardShortcutsUrlLinux: string;
|
keyboardShortcutsUrlLinux: string;
|
||||||
keyboardShortcutsUrlWin: string;
|
keyboardShortcutsUrlWin: string;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user