Compare commits

..

70 Commits

Author SHA1 Message Date
Leila Lali
9bf4a4b18c Added node selectdEvent (#2153)
* Added model view tree node selectedEvent
2018-08-06 13:19:37 -07:00
Karl Burtram
8a01553c49 Remove edit under vs directory from previous commit (#2154) 2018-08-06 15:32:15 -04:00
AlexFsmn
21b913845f Fixed bug where proper file extension wasn't appended to filename. (#2151)
#892
2018-08-06 14:54:58 -04:00
Karl Burtram
220e4feb1d Bump MIME module (#2148) 2018-08-03 21:24:26 -04:00
Matt Irvine
13884c0457 Support QueryInput when handling renamed files (#2146) 2018-08-03 21:12:31 -04:00
Matt Irvine
b3bb6ebc6e Use event element to determine whether to hide dropdown (#2145) 2018-08-03 21:12:18 -04:00
Matt Irvine
424eb90dd8 Save edit data scroll position when switching tabs (#2129) 2018-08-03 16:18:18 -07:00
Matt Irvine
df804d0729 Make sure chart viewer height gets set correctly (#2143) 2018-08-03 16:17:49 -07:00
Madeline MacDonald
79269cdfd5 Add session templates to profiler (#2115)
* Initial support for handling available sessions

* Displaying sessions in drop down, send session name in start profiling request

* More support for starting existing sessions

* New session dialog and session templates in user files

* Create profiler dialog and session templates

* Preliminary session template changes

* Saving some changes

* Send session templates when creating sessions

* Saving changes

* UI Fixes for dialog

* Formatting fixes

* removing comments

* Fixing PR comments

* bumping toolsservice and dataprotocolclient versions

* Fixing starting existing sessions
2018-08-03 15:24:50 -07:00
Matt Irvine
2a650d4d74 Do not disconnect editor when canceling change connection (#2144) 2018-08-03 15:22:11 -07:00
Aditya Bist
017b4ecdb3 fixed breaking bug in job history and fixed accordion style (#2138) 2018-08-03 13:04:47 -07:00
Matt Irvine
9c84bf3fd5 Fix directory name error exporting results multiple times (#2134) 2018-08-03 11:52:01 -07:00
Karl Burtram
397b54a8c3 Update dashboard, profiler and query icons (#2135) 2018-08-03 00:32:12 -04:00
Chris LaFreniere
cb3604c0a1 Add NodeLabel to TreeNodeContextKey (#2113) 2018-08-02 13:38:55 -07:00
Karl Burtram
1a7f0673ea Prevent the insights dialog from showing duplicate buttons (#2122) 2018-08-02 13:58:05 -04:00
Leila Lali
0d043207b9 Feature/tree component (#2077)
*added tree component to the model builder
2018-08-02 10:50:05 -07:00
Karl Burtram
f995dea971 Connect the editor for Script operations (#2123) 2018-08-02 13:48:47 -04:00
Sebastian Pfliegel
49e20488bc Added more saveAsCsv options (#2099)
Fixes #203
Options:
lineSeperator
textIdentifier
encoding
2018-08-01 22:51:31 -04:00
Karl Burtram
3e47b27192 Bump Tools Service to 1.5.20 (#2121)
This picks up a fix for https://github.com/Microsoft/sqlopsstudio/issues/2117
2018-08-01 21:05:30 -04:00
Karl Burtram
f4fa18ec05 Add GetConnectionString command (without build break) (#2120)
* Revert "Revert "Adds "Get Connection String" command (#2108)" (#2116)"

This reverts commit c6d1fa2b7d.

* Fix build breaks
2018-08-01 20:15:14 -04:00
Karl Burtram
c6d1fa2b7d Revert "Adds "Get Connection String" command (#2108)" (#2116)
This reverts commit c4df7667ff.
2018-08-01 19:00:24 -04:00
Karl Burtram
c4df7667ff Adds "Get Connection String" command (#2108)
* Initial connection string changes

* Add Get Connection String methods

* Add IncludePassword parameter

* Update Data Protocol and Tools Service versions

* Update dataprotocol to 0.2.2

* Try to update the dataprotocol component again

* Fix a few bugs related to getting the current editor connection uri
2018-08-01 18:44:54 -04:00
Karl Burtram
6a303cfa25 Bump tools service to 1.5.0-alpha.18 2018-08-01 00:17:49 -07:00
Karl Burtram
4ffa5cc1da Bump Server Reports extension to 0.1.3 (#2104) 2018-07-31 22:15:56 -04:00
Aditya Bist
172e1cf3bf put parse syntax in command palette instead of editor (#2103) 2018-07-31 16:27:11 -07:00
Karl Burtram
e6a32e52f5 Bump SQL Ops to 0.32.3 (#2088) 2018-07-30 18:06:44 -04:00
Matt Irvine
d2b6f6844d Add connection API method to get URI (#2021) 2018-07-30 10:52:24 -07:00
Matt Irvine
e9ef95ef1f Save query result selection/scroll when switching tabs (#2052) 2018-07-27 14:01:34 -07:00
Aditya Bist
10eeb5374f enabled button to import queries from sql files (#2042) 2018-07-27 13:44:20 -07:00
Karl Burtram
332951bc8e Pick-up newer version proxy node module (#2074) 2018-07-27 15:20:25 -04:00
Amir Ali Omidi
4daf3280ff Bring in all the extensibility updates we added during the hackathon (#2056)
Extensibility updates
2018-07-26 16:33:32 -07:00
Aditya Bist
87bb2c74d9 added .sql to associated files to sql ops (#2060) 2018-07-26 16:33:08 -07:00
Abbie Petchtes
ba011853a0 Add text editor component for model view (#2058)
* add text editor component for model view

* add comments
2018-07-26 15:52:33 -07:00
Aditya Bist
461a158ac3 bumped version for insider build (#2053) 2018-07-26 13:41:21 -07:00
Aditya Bist
85f59f1103 removed the export from defaultSort function (#2048) 2018-07-26 11:10:14 -07:00
Aditya Bist
335b9f445f Feature: Parse Query Syntax (#1997)
* feature/parseSyntax

* added error when no connection

* removed elapsed time logic

* code review comments

* changed error message to match SSMS
2018-07-25 16:11:58 -07:00
ranasaria
7cda45c904 fix for issue #1604 (#2029)
* Adding min height to .connection-dialog .tabbedPanel so that connection dialog add a vertical scroll bar appropriately when the ops studio window is vertically resized.
2018-07-25 12:31:17 -07:00
Anthony Dresser
4159fdc1a3 add off by one handler for selection (#2009) 2018-07-25 12:30:38 -07:00
Matt Irvine
190da30979 Update some Agent license headers (#2008) 2018-07-24 18:14:57 -07:00
Aditya Bist
ed9c74b900 localized the unlocalized strings (#2018) 2018-07-24 16:49:06 -07:00
Leila Lali
6783766c33 declarative table layout and option (#2007)
* added scroll bar to table content
2018-07-24 15:04:21 -07:00
Karl Burtram
b27018b379 Update SQL Tools to 1.5.0-alpha.14 2018-07-24 12:22:21 -07:00
Karl Burtram
021d07e04a Add VS Code version to product metadata (#1998) 2018-07-24 13:54:29 -04:00
Ian Y. Choi
bf0baec392 Fixes a typo: Mimunum -> Minimum (#1994)
(trivial)
2018-07-24 12:35:46 -04:00
Karl Burtram
923cbac400 Add // {{SQL CARBON EDIT}} for previous commit 2018-07-23 19:09:56 -07:00
Karl Burtram
ab938f2536 Remove @ from word separators (#1990) 2018-07-23 21:10:01 -04:00
Amir Ali Omidi
a55b1804e9 Tackles issue #1723 (#1988)
* Tackles issue #1723
2018-07-23 16:55:53 -07:00
Matt Irvine
4bfa6b3a5d Save editor cursor/scroll position when switching sql files (#1978)
* Save editor layouts when switching sql files

* Move import

* Restore the view state after laying out the editor
2018-07-20 10:48:12 -04:00
Anthony Dresser
d20f24be18 Bump slickgrid to fix html content issue (#1980)
* add ' to escape strings for html

* update slickgrid to fix injection
2018-07-20 10:47:16 -04:00
Matt Irvine
8a17bae7a6 Loading spinner while validating next/done (#1975) 2018-07-19 17:31:30 -07:00
Anthony Dresser
d14c73fad5 vbump service-downloader (#1965) 2018-07-19 14:06:41 -07:00
Anthony Dresser
ce878e1def Rework slickgrid keyboard navigation (#1930)
* rewrite keybind nav to handle ctrl + home and end

* testing different options

* working on removed slickgrid changes we don't need

* formatting

* handle click handler to rowNumber

* fixing various bugs

* formatting

* readd click column to select

* add shift key to column select

* added logic for additional keybindings on grid

* add down and up arrow into keyboard navigation

* update styling and update slickgrid

* formatting

* update angular-slickgrid version

* remove index.js changes
2018-07-19 14:05:54 -07:00
Karl Burtram
a64a0d1db6 Bump SQL Ops to 0.32.1 for August iteration 2018-07-19 11:43:32 -07:00
Anthony Dresser
feab43f16d add ' to escape strings for html (#1974) 2018-07-19 14:42:29 -04:00
Karl Burtram
0d60fe775f Update Readme and Changelog (#1968) 2018-07-19 01:21:06 -04:00
Leila Lali
6680be6a73 adding task integration with wizard and dialog framework (#1929)
* adding task integration with wizard and dialog framework
2018-07-18 16:28:36 -07:00
Karl Burtram
e026ab85a7 Escape the aria string for Edit Data grid (#1958) 2018-07-17 18:45:10 -07:00
Karl Burtram
e53c903205 Update tools service to 1.5.0-alpha.12 2018-07-17 18:44:45 -07:00
Matt Irvine
03dbe8565f Set element text instead of HTML where possible (#1956) 2018-07-17 16:48:38 -07:00
Karl Burtram
708793cb23 Update tools service to 1.5.0-alpha.11 2018-07-17 16:47:31 -07:00
Aditya Bist
43ae4fb0aa Fixed 2 bugs in Agent Steps page. (#1953)
* fixed bug where steps werent being shown

* fixed customer reported issue
2018-07-17 15:49:38 -07:00
Aditya Bist
6b1d552277 Agent/step finishes (#1948)
* misc fixes in dialogs

* removed unused import

* disabled more advanced options
2018-07-17 10:51:31 -07:00
Madeline MacDonald
f24f576b72 Profiler display fixes (#1949)
* Fixing details tab, window resizing, and having profiler options in object explorer

* Fixing displaying connection names

* spacing

* Removing unnecessary code
2018-07-16 17:20:13 -07:00
Matt Irvine
c23328564f Hide correct element when hiding buttons (#1945) 2018-07-16 17:09:44 -07:00
Karl Burtram
cd6dd3dafa Bump Tools Service to 1.5.0-alpha.10 (#1947) 2018-07-16 15:40:41 -07:00
Aditya Bist
4081e15bef misc fixes in dialogs (#1942)
* misc fixes in dialogs

* removed unused import
2018-07-16 15:25:11 -07:00
Karl Burtram
b05e3813d1 Bump product version to 0.31.4 from July Public Preview (#1944) 2018-07-16 15:13:10 -07:00
Karl Burtram
3048311f40 Bump agent extension version to 0.31.4 (#1943) 2018-07-16 15:12:54 -07:00
Madeline MacDonald
1045392d91 Escaping profiler text (#1940) 2018-07-16 13:20:56 -07:00
Madeline MacDonald
7b23ca8ee7 Improving profiler controls and toolbar (#1931)
* Profiler toolbar improvements

* Fixing formatting issues
2018-07-16 11:44:14 -07:00
168 changed files with 4620 additions and 1080 deletions

View File

@@ -1,5 +1,26 @@
# Change Log # Change Log
## Version 0.31.4
* Release date: July 19, 2018
* Release status: Public Preview
## What's new in this version
* SQL Server Agent for SQL Operations Studio extension improvements
* Added view of Alerts, Operators, and Proxies and icons on left pane
* Added dialogs for New Job, New Job Step, New Alert, and New Operator
* Added Delete Job, Delete Alert, and Delete Operator (right-click)
* Added Previous Runs visualization
* Added Filters for each column name
* SQL Server Profiler for SQL Operations Studio extension improvements
* Added Hotkeys to quickly launch and start/stop Profiler
* Added 5 Default Templates to view Extended Events
* Added Server/Database connection name
* Added support for Azure SQL Database instances
* Added suggestion to exit Profiler when tab is closed when Profiler is still running
* Release of Combine Scripts Extension
* Wizard and Dialog Extensibility
* Fix GitHub Issues
## Version 0.30.6 ## Version 0.30.6
* Release date: June 20, 2018 * Release date: June 20, 2018
* Release status: Public Preview * Release status: Public Preview

View File

@@ -8,12 +8,12 @@ SQL Operations Studio is a data management tool that enables you to work with SQ
Platform | Link Platform | Link
-- | -- -- | --
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=875602 Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=2005949
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=875603 Windows ZIP | https://go.microsoft.com/fwlink/?linkid=2005950
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=875604 macOS ZIP | https://go.microsoft.com/fwlink/?linkid=2005959
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=875605 Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=2005960
Linux RPM | https://go.microsoft.com/fwlink/?linkid=875606 Linux RPM | https://go.microsoft.com/fwlink/?linkid=2006083
Linux DEB | https://go.microsoft.com/fwlink/?linkid=875607 Linux DEB | https://go.microsoft.com/fwlink/?linkid=2006084
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.
@@ -85,7 +85,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Chinese (Traditional): Bruce Chen, Chiayi Yen, Kevin Yang, Winnie Lin, 保哥 Will, 謝政廷 * Chinese (Traditional): Bruce Chen, Chiayi Yen, Kevin Yang, Winnie Lin, 保哥 Will, 謝政廷
* Korean: Do-Kyun Kim, Evelyn Kim, Helen Jung, Hong Jmee, jeongwoo choi, Jun Hyoung Lee, Jungsun Kim정선, Justin Yoo, Kavrith mucha, Kiwoong Youm, MinGyu Ju, MVP_JUNO BEA, Sejun Kim, SOONMAN KWON, sung man ko, Yeongrak Choi, younggun kim, Youngjae Kim, 소영 이 * Korean: Do-Kyun Kim, Evelyn Kim, Helen Jung, Hong Jmee, jeongwoo choi, Jun Hyoung Lee, Jungsun Kim정선, Justin Yoo, Kavrith mucha, Kiwoong Youm, MinGyu Ju, MVP_JUNO BEA, Sejun Kim, SOONMAN KWON, sung man ko, Yeongrak Choi, younggun kim, Youngjae Kim, 소영 이
* Russian: Andrey Veselov, Anton Fontanov, Anton Savin, Elena Ostrovskaia, Igor Babichev, Maxim Zelensky, Rodion Fedechkin, Tasha T, Vladimir Zyryanov * Russian: Andrey Veselov, Anton Fontanov, Anton Savin, Elena Ostrovskaia, Igor Babichev, Maxim Zelensky, Rodion Fedechkin, Tasha T, Vladimir Zyryanov
* Portuguese Brazil: Daniel de Sousa, Diogo Duarte, Douglas Correa, Douglas Eccker, José Emanuel Mendes, Marcelo Fernandes, Marcondes Alexandre, Roberto Fonseca, Rodrigo Crespi * Portuguese Brazil: Daniel de Sousa, Diogo Duarte, Douglas Correa, Douglas Eccker, José Emanuel Mendes, Marcelo Fernandes, Marcondes Alexandre, Roberto Fonseca, Rodrigo Crespi
And of course we'd like to thank the authors of all upstream dependencies. Please see a full list in the [ThirdPartyNotices.txt](https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/ThirdPartyNotices.txt) And of course we'd like to thank the authors of all upstream dependencies. Please see a full list in the [ThirdPartyNotices.txt](https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/ThirdPartyNotices.txt)

View File

@@ -61,6 +61,7 @@ Type: filesandordirs; Name: "{app}\_"
[Tasks] [Tasks]
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1 Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
Name: "associatewithfiles"; Description: "{cm:AssociateWithFiles,{#NameShort}}"; GroupDescription: "{cm:Other}"; Flags: unchecked
Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}" Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}"
Name: "runcode"; Description: "{cm:RunAfter,{#NameShort}}"; GroupDescription: "{cm:Other}"; Check: WizardSilent Name: "runcode"; Description: "{cm:RunAfter,{#NameShort}}"; GroupDescription: "{cm:Other}"; Check: WizardSilent
@@ -82,6 +83,13 @@ Root: HKCR; Subkey: "{#RegValueName}SourceFile\DefaultIcon"; ValueType: string;
Root: HKCR; Subkey: "{#RegValueName}SourceFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1""" Root: HKCR; Subkey: "{#RegValueName}SourceFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\bin"; Tasks: addtopath; Check: NeedsAddPath(ExpandConstant('{app}\bin')) Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\bin"; Tasks: addtopath; Check: NeedsAddPath(ExpandConstant('{app}\bin'))
Root: HKCU; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.sql"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SQL}"; Flags: uninsdeletekey; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\code_file.ico"; Tasks: associatewithfiles
Root: HKCU; Subkey: "Software\Classes\{#RegValueName}.sql\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles
[Code] [Code]
// Don't allow installing conflicting architectures // Don't allow installing conflicting architectures
function InitializeSetup(): Boolean; function InitializeSetup(): Boolean;

View File

@@ -2,7 +2,7 @@
"name": "agent", "name": "agent",
"displayName": "SQL Server Agent", "displayName": "SQL Server Agent",
"description": "Manage and troubleshoot SQL Server Agent jobs", "description": "Manage and troubleshoot SQL Server Agent jobs",
"version": "0.31.1", "version": "0.31.4",
"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",

View File

@@ -63,8 +63,9 @@ export class AlertDialog extends AgentDialog<AlertData> {
private static readonly AlertTypes: string[] = [ private static readonly AlertTypes: string[] = [
AlertData.AlertTypeSqlServerEventString, AlertData.AlertTypeSqlServerEventString,
AlertData.AlertTypePerformanceConditionString, // Disabled until next release
AlertData.AlertTypeWmiEventString // AlertData.AlertTypePerformanceConditionString,
// AlertData.AlertTypeWmiEventString
]; ];
private static readonly AlertSeverities: string[] = [ private static readonly AlertSeverities: string[] = [

View File

@@ -219,6 +219,9 @@ export class JobDialog extends AgentDialog<JobData> {
width: 80 width: 80
}).component(); }).component();
this.moveStepUpButton.enabled = false;
this.moveStepDownButton.enabled = false;
this.newStepButton = view.modelBuilder.button().withProperties({ this.newStepButton = view.modelBuilder.button().withProperties({
label: this.NewStepButtonString, label: this.NewStepButtonString,
width: 80 width: 80

View File

@@ -30,6 +30,7 @@ export class JobStepDialog {
private readonly PreviousButtonText: string = localize('jobStepDialog.previous','Previous'); private readonly PreviousButtonText: string = localize('jobStepDialog.previous','Previous');
private readonly SuccessfulParseText: string = localize('jobStepDialog.successParse', 'The command was successfully parsed.'); private readonly SuccessfulParseText: string = localize('jobStepDialog.successParse', 'The command was successfully parsed.');
private readonly FailureParseText: string = localize('jobStepDialog.failParse', 'The command failed.'); private readonly FailureParseText: string = localize('jobStepDialog.failParse', 'The command failed.');
private readonly BlankStepNameErrorText: string = localize('jobStepDialog.blankStepName', 'The step name cannot be left blank');
// General Control Titles // General Control Titles
private readonly StepNameLabelString: string = localize('jobStepDialog.stepNameLabel', 'Step Name'); private readonly StepNameLabelString: string = localize('jobStepDialog.stepNameLabel', 'Step Name');
@@ -139,13 +140,19 @@ export class JobStepDialog {
this.openButton = view.modelBuilder.button() this.openButton = view.modelBuilder.button()
.withProperties({ .withProperties({
label: this.OpenCommandText, label: this.OpenCommandText,
width: '80px' width: '80px',
isFile: true
}).component(); }).component();
this.parseButton = view.modelBuilder.button() this.parseButton = view.modelBuilder.button()
.withProperties({ .withProperties({
label: this.ParseCommandText, label: this.ParseCommandText,
width: '80px' width: '80px',
isFile: false
}).component(); }).component();
this.openButton.onDidClick(e => {
let queryContent = e;
this.commandTextBox.value = queryContent;
});
this.parseButton.onDidClick(e => { this.parseButton.onDidClick(e => {
if (this.commandTextBox.value) { if (this.commandTextBox.value) {
queryProvider.parseSyntax(this.ownerUri, this.commandTextBox.value).then(result => { queryProvider.parseSyntax(this.ownerUri, this.commandTextBox.value).then(result => {
@@ -185,6 +192,12 @@ export class JobStepDialog {
.withProperties({ .withProperties({
}).component(); }).component();
this.nameTextBox.required = true; this.nameTextBox.required = true;
this.nameTextBox.onTextChanged(() => {
if (this.nameTextBox.value.length > 0) {
this.dialog.message = null;
}
});
this.typeDropdown = view.modelBuilder.dropDown() this.typeDropdown = view.modelBuilder.dropDown()
.withProperties({ .withProperties({
value: this.TSQLScript, value: this.TSQLScript,
@@ -465,7 +478,12 @@ export class JobStepDialog {
return outputFileForm; return outputFileForm;
} }
private async execute() { protected execute() {
this.model.stepName = this.nameTextBox.value;
if (!this.model.stepName || this.model.stepName.length === 0) {
this.dialog.message = this.dialog.message = { text: this.BlankStepNameErrorText };
return;
}
this.model.jobName = this.jobName; this.model.jobName = this.jobName;
this.model.id = this.stepId; this.model.id = this.stepId;
this.model.server = this.server; this.model.server = this.server;
@@ -479,7 +497,6 @@ export class JobStepDialog {
this.model.failureAction = this.failureActionDropdown.value as string; this.model.failureAction = this.failureActionDropdown.value as string;
this.model.outputFileName = this.outputFileNameBox.value; this.model.outputFileName = this.outputFileNameBox.value;
this.model.appendToLogFile = this.appendToExistingFileCheckbox.checked; this.model.appendToLogFile = this.appendToExistingFileCheckbox.checked;
await this.model.save();
} }
public async openNewStepDialog() { public async openNewStepDialog() {

View File

@@ -1,6 +1,6 @@
/*--------------------------------------------------------------------------------------------- /*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved. * Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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 assert from 'assert'; import * as assert from 'assert';

View File

@@ -1,6 +1,6 @@
/*--------------------------------------------------------------------------------------------- /*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved. * Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/

View File

@@ -658,9 +658,9 @@
} }
}, },
"dependencies": { "dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.9", "dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.4",
"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.4",
"vscode-extension-telemetry": "^0.0.15" "vscode-extension-telemetry": "^0.0.15"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -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.5.0-alpha.9", "version": "1.5.0-alpha.22",
"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",

View File

@@ -49,9 +49,9 @@ core-util-is@~1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.1.9": "dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.4":
version "0.1.9" version "0.2.4"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/a1a79895cb79658b75d78aa5cfd745855019148d" resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/b4e2696563d0075e1b674ac8b179b3b8c441f59f"
dependencies: dependencies:
vscode-languageclient "3.5.0" vscode-languageclient "3.5.0"
@@ -296,9 +296,9 @@ semver@^5.3.0:
version "5.5.0" version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
"service-downloader@github:anthonydresser/service-downloader#0.1.2": "service-downloader@github:anthonydresser/service-downloader#0.1.4":
version "0.1.2" version "0.1.4"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/2aa9b336b6442e17e24693ddc907030575539798" resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/3c0abdf8603aca85d2eacfac3c547173e41bf0c7"
dependencies: dependencies:
decompress "^4.2.0" decompress "^4.2.0"
eventemitter2 "^5.0.1" eventemitter2 "^5.0.1"

View File

@@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
export class CreateSessionData {
public ownerUri: string;
public sessionName: string;
public templates: Array<sqlops.ProfilerSessionTemplate> = new Array<sqlops.ProfilerSessionTemplate>();
constructor(ownerUri: string, templates: Array<sqlops.ProfilerSessionTemplate>) {
this.ownerUri = ownerUri;
this.templates = templates;
}
public getTemplateNames(): string[] {
return this.templates.map(e => e.name);
}
public selectTemplate(name: string): sqlops.ProfilerSessionTemplate {
return this.templates.find((t) => { return t.name === name; });
}
}

View File

@@ -0,0 +1,108 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { CreateSessionData } from '../data/createSessionData';
const localize = nls.loadMessageBundle();
export class CreateSessionDialog {
// Top level
private readonly DialogTitle: string = localize('createSessionDialog.newSession', 'New Session');
private readonly CancelButtonText: string = localize('createSessionDialog.cancel', 'Cancel');
private readonly CreateButtonText: string = localize('createSessionDialog.create', 'Create');
private readonly DialogTitleText: string = localize('createSessionDialog.title', 'Create New Profiler Session');
// UI Components
private dialog: sqlops.window.modelviewdialog.Dialog;
private templatesBox: sqlops.DropDownComponent;
private sessionNameBox: sqlops.InputBoxComponent;
private model: CreateSessionData;
private _onSuccess: vscode.EventEmitter<CreateSessionData> = new vscode.EventEmitter<CreateSessionData>();
public readonly onSuccess: vscode.Event<CreateSessionData> = this._onSuccess.event;
constructor(ownerUri: string, templates: Array<sqlops.ProfilerSessionTemplate>) {
if (typeof (templates) === 'undefined' || templates === null) {
throw new Error(localize('createSessionDialog.templatesInvalid', "Invalid templates list, cannot open dialog"));
}
if (typeof (ownerUri) === 'undefined' || ownerUri === null) {
throw new Error(localize('createSessionDialog.dialogOwnerInvalid', "Invalid dialog owner, cannot open dialog"));
}
this.model = new CreateSessionData(ownerUri, templates);
}
public async showDialog(): Promise<void> {
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
this.initializeContent();
this.dialog.okButton.onClick(() => this.execute());
this.dialog.cancelButton.onClick(() => { });
this.dialog.okButton.label = this.CreateButtonText;
this.dialog.cancelButton.label = this.CancelButtonText;
sqlops.window.modelviewdialog.openDialog(this.dialog);
}
private initializeContent(): void {
this.dialog.registerContent(async view => {
this.templatesBox = view.modelBuilder.dropDown()
.withProperties({
values: []
}).component();
this.sessionNameBox = view.modelBuilder.inputBox()
.withProperties({
required: true,
multiline: false,
value: ''
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
components: [{
component: this.templatesBox,
title: localize('createSessionDialog.selectTemplates', "Select session template:")
},
{
component: this.sessionNameBox,
title: localize('createSessionDialog.enterSessionName', "Enter session name:")
}],
title: this.DialogTitleText
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
if (this.model.templates) {
this.templatesBox.values = this.model.getTemplateNames();
}
this.sessionNameBox.onTextChanged(() => {
if (this.sessionNameBox.value.length > 0) {
this.model.sessionName = this.sessionNameBox.value;
this.dialog.okButton.enabled = true;
} else {
this.dialog.okButton.enabled = false;
}
});
});
}
private async execute(): Promise<void> {
let currentConnection = await sqlops.connection.getCurrentConnection();
let profilerService = sqlops.dataprotocol.getProvider<sqlops.ProfilerProvider>(currentConnection.providerName, sqlops.DataProviderType.ProfilerProvider);
let name = this.sessionNameBox.value;
let selected = this.templatesBox.value.toString();
let temp = this.model.selectTemplate(selected);
profilerService.createSession(this.model.ownerUri, this.sessionNameBox.value, temp);
}
}

View File

@@ -5,28 +5,33 @@
'use strict'; 'use strict';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as data from 'sqlops'; import * as sqlops from 'sqlops';
import { ApiWrapper } from './apiWrapper'; import { ApiWrapper } from './apiWrapper';
import { CreateSessionDialog } from './dialogs/profilerCreateSessionDialog';
/** /**
* The main controller class that initializes the extension * The main controller class that initializes the extension
*/ */
export class MainController { export class MainController {
protected _apiWrapper: ApiWrapper; protected _apiWrapper: ApiWrapper;
protected _context: vscode.ExtensionContext; protected _context: vscode.ExtensionContext;
// PUBLIC METHODS ////////////////////////////////////////////////////// // PUBLIC METHODS
public constructor(context: vscode.ExtensionContext, apiWrapper?: ApiWrapper) { public constructor(context: vscode.ExtensionContext, apiWrapper?: ApiWrapper) {
this._apiWrapper = apiWrapper || new ApiWrapper(); this._apiWrapper = apiWrapper || new ApiWrapper();
this._context = context; this._context = context;
} }
/** /**
* Deactivates the extension * Deactivates the extension
*/ */
public deactivate(): void { public deactivate(): void {
} }
public activate(): void { public activate(): void {
} vscode.commands.registerCommand('profiler.openCreateSessionDialog', (ownerUri: string, templates: Array<sqlops.ProfilerSessionTemplate>) => {
let dialog = new CreateSessionDialog(ownerUri, templates);
dialog.showDialog();
});
}
} }

View File

@@ -5,4 +5,5 @@
/// <reference path='../../../../../src/vs/vscode.d.ts'/> /// <reference path='../../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../../src/sql/sqlops.d.ts'/> /// <reference path='../../../../../src/sql/sqlops.d.ts'/>
/// <reference path='../../../../../src/sql/sqlops.proposed.d.ts'/>
/// <reference types='@types/node'/> /// <reference types='@types/node'/>

View File

@@ -27,7 +27,7 @@
], ],
"contributes": { "contributes": {
"commands": [ "commands": [
{ {
"command": "profiler.newProfiler", "command": "profiler.newProfiler",
"title": "New Profiler", "title": "New Profiler",
@@ -42,12 +42,19 @@
"command": "profiler.stop", "command": "profiler.stop",
"title": "Stop", "title": "Stop",
"category": "Profiler" "category": "Profiler"
},
{
"command": "profiler.openCreateSessionDialog",
"category": "Profiler"
} }
], ],
"outputChannels": [ "outputChannels": [
"sqlprofiler" "sqlprofiler"
] ]
}, },
"dependencies": {
"vscode-nls": "^3.2.1"
},
"devDependencies": { "devDependencies": {
"vscode": "1.0.1" "vscode": "1.0.1"
} }

View File

@@ -2035,6 +2035,10 @@ vinyl@~2.0.1:
remove-trailing-separator "^1.0.1" remove-trailing-separator "^1.0.1"
replace-ext "^1.0.0" replace-ext "^1.0.0"
vscode-nls@^3.2.1:
version "3.2.4"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
vscode@1.0.1: vscode@1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.0.1.tgz#3d161200615fe2af1d92ddc650751159411a513b" resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.0.1.tgz#3d161200615fe2af1d92ddc650751159411a513b"

View File

@@ -12,8 +12,9 @@
["{", "}"], ["{", "}"],
["[", "]"], ["[", "]"],
["(", ")"], ["(", ")"],
["\"", "\""], { "open": "\"", "close": "\"", "notIn": ["string"] },
["'", "'"] { "open": "N'", "close": "'", "notIn": ["string", "comment"] },
{ "open": "'", "close": "'", "notIn": ["string", "comment"] }
], ],
"surroundingPairs": [ "surroundingPairs": [
["{", "}"], ["{", "}"],

View File

@@ -936,11 +936,11 @@
}, },
"_shell_light": { "_shell_light": {
"fontCharacter": "\\E073", "fontCharacter": "\\E073",
"fontColor": "#455155" "fontColor": "#bfc2c1"
}, },
"_shell": { "_shell": {
"fontCharacter": "\\E073", "fontCharacter": "\\E073",
"fontColor": "#4d5a5e" "fontColor": "#d4d7d6"
}, },
"_slim_light": { "_slim_light": {
"fontCharacter": "\\E074", "fontCharacter": "\\E074",
@@ -1374,7 +1374,9 @@
"cmakelists.txt": "_makefile_3", "cmakelists.txt": "_makefile_3",
"procfile": "_heroku", "procfile": "_heroku",
"todo": "_todo", "todo": "_todo",
"npm-debug.log": "_npm_ignored" "npm-debug.log": "_npm_ignored",
"dashboard": "_shell",
"profiler": "_csv"
}, },
"languageIds": { "languageIds": {
"bat": "_windows", "bat": "_windows",
@@ -1409,7 +1411,7 @@
"rust": "_rust", "rust": "_rust",
"scss": "_sass", "scss": "_sass",
"shellscript": "_shell", "shellscript": "_shell",
"sql": "_db", "sql": "_default",
"swift": "_swift", "swift": "_swift",
"typescript": "_typescript", "typescript": "_typescript",
"typescriptreact": "_react", "typescriptreact": "_react",
@@ -1614,7 +1616,7 @@
"rust": "_rust_light", "rust": "_rust_light",
"scss": "_sass_light", "scss": "_sass_light",
"shellscript": "_shell_light", "shellscript": "_shell_light",
"sql": "_db_light", "sql": "_default_light",
"swift": "_swift_light", "swift": "_swift_light",
"typescript": "_typescript_light", "typescript": "_typescript_light",
"typescriptreact": "_react_light", "typescriptreact": "_react_light",
@@ -1660,7 +1662,9 @@
"omakefile": "_makefile_2_light", "omakefile": "_makefile_2_light",
"cmakelists.txt": "_makefile_3_light", "cmakelists.txt": "_makefile_3_light",
"procfile": "_heroku_light", "procfile": "_heroku_light",
"npm-debug.log": "_npm_ignored_light" "npm-debug.log": "_npm_ignored_light",
"dashboard": "_shell_light",
"profiler": "_csv_light"
} }
}, },
"version": "https://github.com/jesseweed/seti-ui/commit/188dda34a56b9555c7d363771264c24f4693983d" "version": "https://github.com/jesseweed/seti-ui/commit/188dda34a56b9555c7d363771264c24f4693983d"

View File

@@ -1,6 +1,6 @@
{ {
"name": "sqlops", "name": "sqlops",
"version": "0.31.3", "version": "0.32.3",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee", "distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": { "author": {
"name": "Microsoft Corporation" "name": "Microsoft Corporation"
@@ -34,7 +34,7 @@
"@angular/router": "~4.1.3", "@angular/router": "~4.1.3",
"@angular/upgrade": "~4.1.3", "@angular/upgrade": "~4.1.3",
"angular2-grid": "2.0.6", "angular2-grid": "2.0.6",
"angular2-slickgrid": "git://github.com/Microsoft/angular2-slickgrid.git#1.3.11", "angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.3.12",
"applicationinsights": "0.18.0", "applicationinsights": "0.18.0",
"chart.js": "^2.6.0", "chart.js": "^2.6.0",
"fast-plist": "0.1.2", "fast-plist": "0.1.2",
@@ -43,8 +43,8 @@
"getmac": "1.0.7", "getmac": "1.0.7",
"graceful-fs": "4.1.11", "graceful-fs": "4.1.11",
"html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.4", "html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.4",
"http-proxy-agent": "0.2.7", "http-proxy-agent": "^2.1.0",
"https-proxy-agent": "0.3.6", "https-proxy-agent": "^2.2.1",
"iconv-lite": "0.4.19", "iconv-lite": "0.4.19",
"jquery": "3.1.0", "jquery": "3.1.0",
"jschardet": "1.6.0", "jschardet": "1.6.0",
@@ -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.23", "slickgrid": "github:anthonydresser/SlickGrid#2.3.25",
"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",
@@ -127,7 +127,7 @@
"istanbul": "^0.3.17", "istanbul": "^0.3.17",
"jsdom-no-contextify": "^3.1.0", "jsdom-no-contextify": "^3.1.0",
"lazy.js": "^0.4.2", "lazy.js": "^0.4.2",
"mime": "1.2.11", "mime": "^1.4.1",
"minimatch": "^2.0.10", "minimatch": "^2.0.10",
"mkdirp": "^0.5.0", "mkdirp": "^0.5.0",
"mocha": "^2.2.5", "mocha": "^2.2.5",

View File

@@ -30,6 +30,7 @@
"gettingStartedUrl": "https://go.microsoft.com/fwlink/?linkid=862039", "gettingStartedUrl": "https://go.microsoft.com/fwlink/?linkid=862039",
"releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=875578", "releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=875578",
"documentationUrl": "https://go.microsoft.com/fwlink/?linkid=862277", "documentationUrl": "https://go.microsoft.com/fwlink/?linkid=862277",
"vscodeVersion": "1.23.1",
"commit": "9ca6200018fc206d67a47229f991901a8a453781", "commit": "9ca6200018fc206d67a47229f991901a8a453781",
"date": "2017-12-15T12:00:00.000Z", "date": "2017-12-15T12:00:00.000Z",
"recommendedExtensions": [ "recommendedExtensions": [
@@ -42,4 +43,4 @@
"extensionsGallery": { "extensionsGallery": {
"serviceUrl": "https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery.json" "serviceUrl": "https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery.json"
} }
} }

View File

@@ -37,6 +37,10 @@ See [Paul Randal's wait types library] for more information about each wait type
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:
* flyfishingdba for Add square brackets for ms_foreachdb call (#1023) * flyfishingdba for Add square brackets for ms_foreachdb call (#1023)
* Peter-Schneider for Changed the stored procedure call to work on case sensitive instances (#1809)
## What's new in Server Reports v1.3?
* Changed the stored procedure call to work on case sensitive instances
## What's new in Server Reports v1.2? ## What's new in Server Reports v1.2?
* Created left nav bar and added 2 categories for insight widgets: monitor and performance * Created left nav bar and added 2 categories for insight widgets: monitor and performance

View File

@@ -2,7 +2,7 @@
"name": "server-report", "name": "server-report",
"displayName": "Server Reports", "displayName": "Server Reports",
"description": "Server Reports", "description": "Server Reports",
"version": "0.1.2", "version": "0.1.3",
"publisher": "Microsoft", "publisher": "Microsoft",
"preview": true, "preview": true,
"engines": { "engines": {

View File

@@ -100,7 +100,7 @@
"sqlops": "github:anthonydresser/sqlops-extension-sqlops", "sqlops": "github:anthonydresser/sqlops-extension-sqlops",
"tslint": "^3.14.0", "tslint": "^3.14.0",
"typescript": "^2.6.1", "typescript": "^2.6.1",
"vscode": "^1.1.14", "vscode": "^1.1.18",
"@types/handlebars": "^4.0.11", "@types/handlebars": "^4.0.11",
"vsce": "1.36.2" "vsce": "1.36.2"
}, },

View File

@@ -11,6 +11,7 @@ import * as vscode from 'vscode';
import SplitPropertiesPanel from './splitPropertiesPanel'; import SplitPropertiesPanel from './splitPropertiesPanel';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import {TreeNode, TreeDataProvider} from './treeDataProvider';
/** /**
* The main controller class that initializes the extension * The main controller class that initializes the extension
@@ -67,12 +68,83 @@ export default class MainController implements vscode.Disposable {
return Promise.resolve(true); return Promise.resolve(true);
} }
private async getTab3Content(view: sqlops.ModelView): Promise<void> {
let treeData = {
label: '1',
children: [
{
label: '11',
id: '11',
children: [
{
label: '111',
id: '111',
checked: false
},
{
label: '112',
id: '112',
children: [
{
label: '1121',
id: '1121',
checked: true
},
{
label: '1122',
id: '1122',
checked: false
}
]
}
]
},
{
label: '12',
id: '12',
checked: true
}
],
id: '1'
};
let root = TreeNode.createTree(treeData);
let treeDataProvider = new TreeDataProvider(root);
let tree: sqlops.TreeComponent<TreeNode> = view.modelBuilder.tree<TreeNode>().withProperties({
'withCheckbox': true
}).component();
let treeView = tree.registerDataProvider(treeDataProvider);
treeView.onNodeCheckedChanged(item => {
if (item && item.element) {
item.element.changeNodeCheckedState(item.checked);
}
});
treeView.onDidChangeSelection(selectedNodes => {
selectedNodes.forEach(node => {
console.info('tree node selected: ' + node.label);
});
});
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: tree,
title: 'Tree'
}], {
horizontal: false,
componentWidth: 800,
componentHeight: 800
}).component();
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false;
await view.initializeModel(formWrapper);
}
private async getTabContent(view: sqlops.ModelView, customButton1: sqlops.window.modelviewdialog.Button, customButton2: sqlops.window.modelviewdialog.Button, componentWidth: number | string): Promise<void> { private async getTabContent(view: sqlops.ModelView, customButton1: sqlops.window.modelviewdialog.Button, customButton2: sqlops.window.modelviewdialog.Button, componentWidth: number | string): Promise<void> {
let inputBox = view.modelBuilder.inputBox() let inputBox = view.modelBuilder.inputBox()
.withProperties({ .withProperties({
multiline: true, multiline: true,
height: 100 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;
customButton1.onClick(() => { customButton1.onClick(() => {
@@ -88,7 +160,7 @@ export default class MainController implements vscode.Disposable {
}) })
.component(); .component();
checkbox.onChanged(e => { checkbox.onChanged(e => {
console.info("inputBox.enabled " + inputBox.enabled); console.info('inputBox.enabled ' + inputBox.enabled);
inputBox.enabled = !inputBox.enabled; inputBox.enabled = !inputBox.enabled;
}); });
let button = view.modelBuilder.button() let button = view.modelBuilder.button()
@@ -145,8 +217,8 @@ export default class MainController implements vscode.Disposable {
component: inputBox4, component: inputBox4,
title: 'inputBox4' title: 'inputBox4'
}], { }], {
horizontal: true horizontal: true
}).component(); }).component();
let groupModel1 = view.modelBuilder.groupContainer() let groupModel1 = view.modelBuilder.groupContainer()
.withLayout({ .withLayout({
}).withItems([ }).withItems([
@@ -178,8 +250,8 @@ export default class MainController implements vscode.Disposable {
}).component(); }).component();
let declarativeTable = view.modelBuilder.declarativeTable() let declarativeTable = view.modelBuilder.declarativeTable()
.withProperties({ .withProperties({
columns: [{ columns: [{
displayName: 'Column 1', displayName: 'Column 1',
valueType: sqlops.DeclarativeDataType.string, valueType: sqlops.DeclarativeDataType.string,
width: '20px', width: '20px',
@@ -204,12 +276,12 @@ export default class MainController implements vscode.Disposable {
{ name: 'options2', displayName: 'option 2' } { name: 'options2', displayName: 'option 2' }
] ]
} }
], ],
data: [ data: [
['Data00', 'Data01', false, 'options2'], ['Data00', 'Data01', false, 'options2'],
['Data10', 'Data11', true, 'options1'] ['Data10', 'Data11', true, 'options1']
] ]
}).component(); }).component();
declarativeTable.onDataChanged(e => { declarativeTable.onDataChanged(e => {
inputBox2.value = e.row.toString() + ' ' + e.column.toString() + ' ' + e.value.toString(); inputBox2.value = e.row.toString() + ' ' + e.column.toString() + ' ' + e.value.toString();
@@ -223,7 +295,7 @@ export default class MainController implements vscode.Disposable {
height: 150 height: 150
}).withItems([ }).withItems([
radioButton, groupModel1, radioButton2] radioButton, groupModel1, radioButton2]
, { flex: '1 1 50%' }).component(); , { flex: '1 1 50%' }).component();
let formModel = view.modelBuilder.formContainer() let formModel = view.modelBuilder.formContainer()
.withFormItems([{ .withFormItems([{
component: inputBoxWrapper, component: inputBoxWrapper,
@@ -254,9 +326,9 @@ export default class MainController implements vscode.Disposable {
component: listBox, component: listBox,
title: 'List Box' title: 'List Box'
}], { }], {
horizontal: false, horizontal: false,
componentWidth: componentWidth componentWidth: componentWidth
}).component(); }).component();
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component(); let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false; formWrapper.loading = false;
customButton2.onClick(() => { customButton2.onClick(() => {
@@ -271,8 +343,9 @@ export default class MainController implements vscode.Disposable {
let tab1 = sqlops.window.modelviewdialog.createTab('Test tab 1'); let tab1 = sqlops.window.modelviewdialog.createTab('Test tab 1');
let tab2 = sqlops.window.modelviewdialog.createTab('Test tab 2'); let tab2 = sqlops.window.modelviewdialog.createTab('Test tab 2');
let tab3 = sqlops.window.modelviewdialog.createTab('Test tab 3');
tab2.content = 'sqlservices'; tab2.content = 'sqlservices';
dialog.content = [tab1, tab2]; dialog.content = [tab1, tab2, tab3];
dialog.okButton.onClick(() => console.log('ok clicked!')); dialog.okButton.onClick(() => console.log('ok clicked!'));
dialog.cancelButton.onClick(() => console.log('cancel clicked!')); dialog.cancelButton.onClick(() => console.log('cancel clicked!'));
dialog.okButton.label = 'ok'; dialog.okButton.label = 'ok';
@@ -285,6 +358,10 @@ export default class MainController implements vscode.Disposable {
tab1.registerContent(async (view) => { tab1.registerContent(async (view) => {
await this.getTabContent(view, customButton1, customButton2, 400); await this.getTabContent(view, customButton1, customButton2, 400);
}); });
tab3.registerContent(async (view) => {
await this.getTab3Content(view);
});
sqlops.window.modelviewdialog.openDialog(dialog); sqlops.window.modelviewdialog.openDialog(dialog);
} }
@@ -301,6 +378,18 @@ export default class MainController implements vscode.Disposable {
page1.registerContent(async (view) => { page1.registerContent(async (view) => {
await this.getTabContent(view, customButton1, customButton2, 800); await this.getTabContent(view, customButton1, customButton2, 800);
}); });
/*
wizard.registerOperation({
displayName: 'test task',
description: 'task description',
isCancelable: true
}, op => {
op.updateStatus(sqlops.TaskStatus.InProgress);
op.updateStatus(sqlops.TaskStatus.InProgress, 'Task is running');
setTimeout(() => {
op.updateStatus(sqlops.TaskStatus.Succeeded);
}, 5000);
});*/
wizard.pages = [page1, page2]; wizard.pages = [page1, page2];
wizard.open(); wizard.open();
} }
@@ -343,15 +432,32 @@ export default class MainController implements vscode.Disposable {
webview2.message = count; webview2.message = count;
}); });
let flexModel = view.modelBuilder.flexContainer() let editor1 = view.modelBuilder.editor()
.withLayout({ .withProperties({
flexFlow: 'column', content: 'select * from sys.tables'
alignItems: 'flex-start', })
height: 500
}).withItems([
webview1, webview2
], { flex: '1 1 50%' })
.component(); .component();
let editor2 = view.modelBuilder.editor()
.withProperties({
content: 'print("Hello World !")',
languageMode: 'python'
})
.component();
let flexModel = view.modelBuilder.flexContainer().component();
flexModel.addItem(editor1, { flex: '1' });
flexModel.addItem(editor2, { flex: '1' });
flexModel.setLayout({
flexFlow: 'column',
alignItems: 'stretch',
height: '100%'
});
view.onClosed((params) => {
vscode.window.showInformationMessage('editor1: language: ' + editor1.languageMode + ' Content1: ' + editor1.content);
vscode.window.showInformationMessage('editor2: language: ' + editor2.languageMode + ' Content2: ' + editor2.content);
});
await view.initializeModel(flexModel); await view.initializeModel(flexModel);
}); });
editor.openEditor(); editor.openEditor();
@@ -378,13 +484,14 @@ export default class MainController implements vscode.Disposable {
let monitorLightPath = vscode.Uri.file(path.join(__dirname, '..', 'media', 'monitor.svg')); let monitorLightPath = vscode.Uri.file(path.join(__dirname, '..', 'media', 'monitor.svg'));
let monitorIcon = { let monitorIcon = {
light: monitorLightPath, light: monitorLightPath,
dark: path.join(__dirname, '..', 'media', 'monitor_inverse.svg') }; dark: path.join(__dirname, '..', 'media', 'monitor_inverse.svg')
};
let monitorButton = view.modelBuilder.button() let monitorButton = view.modelBuilder.button()
.withProperties({ .withProperties({
label: 'Monitor', label: 'Monitor',
iconPath: monitorIcon iconPath: monitorIcon
}).component(); }).component();
let toolbarModel = view.modelBuilder.toolbarContainer() let toolbarModel = view.modelBuilder.toolbarContainer()
.withToolbarItems([{ .withToolbarItems([{
component: inputBox, component: inputBox,

View File

@@ -0,0 +1,294 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import * as sqlops from 'sqlops';
import * as path from 'path';
export enum TreeCheckboxState {
Intermediate = 0,
Checked = 1,
Unchecked = 2
}
export interface TreeComponentDataModel {
label?: string;
children?: TreeComponentDataModel[];
id?: string;
checked?: boolean;
}
export class TreeNode implements sqlops.TreeComponentItem {
private _onNodeChange = new vscode.EventEmitter<void>();
private _onTreeChange = new vscode.EventEmitter<TreeNode>();
private _data: TreeComponentDataModel;
private _parent?: TreeNode;
private _root: TreeNode;
private _isAlwaysLeaf: boolean;
private _nodeMap: Map<string, TreeNode>;
private _children: TreeNode[];
public readonly onNodeChange: vscode.Event<void> = this._onNodeChange.event;
public readonly onTreeChange: vscode.Event<TreeNode> = this._onTreeChange.event;
/**
* Creates new instance of tree node
* @param data the underlining data that's bind to the tree node, any change in the tree will affect the same node in data
* @param root the root node of the tree. If passed null, the current node will be the root
*/
constructor(data: TreeComponentDataModel, root: TreeNode) {
if (!data) {
throw new Error(`Invalid tree node data`);
}
if (root === undefined) {
root = this;
root._nodeMap = new Map<string, TreeNode>();
}
this._root = root;
if (this.findNode(data.id)) {
throw new Error(`tree node with id: '${data.id}' already exists`);
}
this._data = data;
}
/**
* id for TreeNode
*/
public get id(): string {
return this.data.id;
}
public set id(value: string) {
this.data.id = value;
}
/**
* Label to display to the user, describing this node
*/
public set label(value: string) {
this.data.label = value;
}
public get label(): string {
return this.data.label;
}
/**
* Is this a leaf node (in which case no children can be generated) or is it expandable?
*/
public get isAlwaysLeaf(): boolean {
return this._isAlwaysLeaf;
}
/**
* Parent of this node
*/
public get parent(): TreeNode {
return this._parent;
}
public get root(): TreeNode {
return this._root;
}
/**
* Path identifying this node
*/
public get nodePath(): string {
return `${this.parent ? this.parent.nodePath + '-' : ''}${this.id}`;
}
public get data(): TreeComponentDataModel {
if (this._data === undefined) {
this._data = {
label: undefined
};
}
return this._data;
}
public changeNodeCheckedState(value: boolean, fromParent?: boolean): void {
if (value !== this.checked) {
if (value !== undefined && this.children) {
this.children.forEach(child => {
child.changeNodeCheckedState(value, true);
});
}
this.checked = value;
if (!fromParent && this.parent) {
this.parent.refreshState();
}
this.onValueChanged();
}
}
public set checked(value: boolean) {
this.data.checked = value;
}
public refreshState(): void {
if (this.hasChildren) {
if (this.children.every(c => c.checked)) {
this.changeNodeCheckedState(true);
} else if (this.children.every(c => c.checked !== undefined && !c.checked)) {
this.changeNodeCheckedState(false);
} else {
this.changeNodeCheckedState(undefined);
}
}
}
public get hasChildren(): boolean {
return this.children !== undefined && this.children.length > 0;
}
public get checked(): boolean {
return this.data.checked;
}
private onValueChanged(): void {
this._onNodeChange.fire();
if (this.root) {
this.root._onTreeChange.fire(this);
}
}
public get checkboxState(): TreeCheckboxState {
if (this.checked === undefined) {
return TreeCheckboxState.Intermediate;
} else {
return this.checked ? TreeCheckboxState.Checked : TreeCheckboxState.Unchecked;
}
}
public findNode(id: string): TreeNode {
if (this.id === id) {
return this;
} else if (this.root) {
return this.root._nodeMap.has(id) ? this.root._nodeMap.get(id) : undefined;
} else {
let node: TreeNode;
if (this.children) {
this.children.forEach(child => {
node = child.findNode(id);
if (node) {
return;
}
});
}
return node;
}
}
/**
* Children of this node
*/
public get children(): TreeNode[] {
return this._children;
}
public addChildNode(node: TreeNode): void {
if (node) {
if (!node.root) {
node._root = this.root;
}
if (!node.parent) {
node._parent = this;
}
if (node.root) {
node.root._nodeMap.set(node.id, node);
}
this._children.push(node);
}
}
public static createNode(nodeData: TreeComponentDataModel, parent?: TreeNode, root?: TreeNode): TreeNode {
let rootNode = root || (parent !== undefined ? parent.root : undefined);
let treeNode = new TreeNode(nodeData, rootNode);
treeNode._parent = parent;
return treeNode;
}
public static createTree(nodeData: TreeComponentDataModel, parent?: TreeNode, root?: TreeNode): TreeNode {
if (nodeData) {
let treeNode = TreeNode.createNode(nodeData, parent, root);
if (nodeData.children && nodeData.children.length > 0) {
treeNode._isAlwaysLeaf = false;
treeNode._children = [];
nodeData.children.forEach(childNode => {
if (childNode) {
let childTreeNode = TreeNode.createTree(childNode, treeNode, root || treeNode.root);
treeNode.addChildNode(childTreeNode);
}
});
treeNode.refreshState();
} else {
treeNode._isAlwaysLeaf = true;
}
return treeNode;
} else {
return undefined;
}
}
}
export class TreeDataProvider implements sqlops.TreeComponentDataProvider<TreeNode> {
private _onDidChangeTreeData = new vscode.EventEmitter<TreeNode>();
constructor(private _root: TreeNode) {
if(this._root) {
this._root.onTreeChange(node => {
this._onDidChangeTreeData.fire(node);
});
}
}
onDidChangeTreeData?: vscode.Event<TreeNode | undefined | null> = this._onDidChangeTreeData.event ;
/**
* Get [TreeItem](#TreeItem) representation of the `element`
*
* @param element The element for which [TreeItem](#TreeItem) representation is asked for.
* @return [TreeItem](#TreeItem) representation of the element
*/
getTreeItem(element: TreeNode): sqlops.TreeComponentItem | Thenable<sqlops.TreeComponentItem> {
let item: sqlops.TreeComponentItem = {};
item.label = element.label;
item.checked = element.checked;
item.iconPath = vscode.Uri.file(path.join(__dirname, '..', 'media', 'monitor.svg'));
return item;
}
/**
* Get the children of `element` or root if no element is passed.
*
* @param element The element from which the provider gets children. Can be `undefined`.
* @return Children of `element` or root if no element is passed.
*/
getChildren(element?: TreeNode): vscode.ProviderResult<TreeNode[]> {
if (element) {
return Promise.resolve(element.children);
} else {
return Promise.resolve(this._root.children);
}
}
getParent(element?: TreeNode): vscode.ProviderResult<TreeNode> {
if (element) {
return Promise.resolve(element.parent);
} else {
return Promise.resolve(this._root);
}
}
}

View File

@@ -233,8 +233,8 @@ export class Dropdown extends Disposable {
this._layoutTree(); this._layoutTree();
return { dispose: () => { } }; return { dispose: () => { } };
}, },
onDOMEvent: (e, activeElement) => { onDOMEvent: e => {
if (!DOM.isAncestor(activeElement, this.$el.getHTMLElement()) && !DOM.isAncestor(activeElement, this.$treeContainer.getHTMLElement())) { if (!DOM.isAncestor(e.srcElement, this.$el.getHTMLElement()) && !DOM.isAncestor(e.srcElement, this.$treeContainer.getHTMLElement())) {
this._input.validate(); this._input.validate();
this._onBlur.fire(); this._onBlur.fire();
this._contextView.hide(); this._contextView.hide();

View File

@@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import {
Component, Inject, forwardRef, ElementRef, OnInit, Input,
Output, OnChanges, SimpleChanges, EventEmitter
} from '@angular/core';
import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/dropdown';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@Component({
selector: 'editable-select-box',
template: ''
})
export class EditableDropDown extends AngularDisposable implements OnInit, OnChanges {
private _selectbox: Dropdown;
@Input() options: string[];
@Input() selectedOption: string;
@Input() onlyEmitOnChange = false;
@Output() onDidSelect = new EventEmitter<string>();
private _previousVal: string;
constructor(
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(IThemeService) private themeService: IThemeService,
@Inject(IContextViewService) private contextViewService: IContextViewService
) {
super();
}
ngOnInit(): void {
let dropdownOptions: IDropdownOptions = {
values: [],
strictSelection: false,
placeholder: '',
maxHeight: 125,
ariaLabel: '',
actionLabel: ''
};
this._selectbox = new Dropdown(this._el.nativeElement, this.contextViewService, this.themeService, dropdownOptions);
this._selectbox.values = this.options;
this._selectbox.value = this.selectedOption;
this._selectbox.onValueChange(e => {
if (this.onlyEmitOnChange) {
if (this._previousVal !== e) {
this.onDidSelect.emit(e);
this._previousVal = e;
}
} else {
this.onDidSelect.emit(e);
}
});
this._register(attachEditableDropdownStyler(this._selectbox, this.themeService));
}
ngOnChanges(changes: SimpleChanges): void {
}
public get value(): string {
return this._selectbox.value;
}
}

View File

@@ -33,7 +33,7 @@ export function appendRowLink(container: Builder, label: string, labelClass: str
container.element('tr', {}, (rowContainer) => { container.element('tr', {}, (rowContainer) => {
rowContainer.element('td', { class: labelClass }, (labelCellContainer) => { rowContainer.element('td', { class: labelClass }, (labelCellContainer) => {
labelCellContainer.div({}, (labelContainer) => { labelCellContainer.div({}, (labelContainer) => {
labelContainer.innerHtml(label); labelContainer.text(label);
}); });
}); });
rowContainer.element('td', { class: cellContainerClass }, (inputCellContainer) => { rowContainer.element('td', { class: cellContainerClass }, (inputCellContainer) => {

View File

@@ -357,6 +357,21 @@ export abstract class Modal extends Disposable implements IThemable {
return button; return button;
} }
/**
* Returns a footer button matching the provided label
* @param label Label to show on the button
* @param onSelect The callback to call when the button is selected
*/
protected findFooterButton(label: string): Button {
return this._footerButtons.find(e => {
try {
return e && e.element.innerText === label;
} catch {
return false;
}
});
}
/** /**
* Show an error in the error message element * Show an error in the error message element
* @param err Text to show in the error message * @param err Text to show in the error message

View File

@@ -150,8 +150,8 @@ export class OptionsDialog extends Modal {
private onOptionLinkClicked(optionName: string): void { private onOptionLinkClicked(optionName: string): void {
var option = this._optionElements[optionName].option; var option = this._optionElements[optionName].option;
this._optionTitle.innerHtml(option.displayName); this._optionTitle.text(option.displayName);
this._optionDescription.innerHtml(option.description); this._optionDescription.text(option.description);
} }
private fillInOptions(container: Builder, options: sqlops.ServiceOption[]): void { private fillInOptions(container: Builder, options: sqlops.ServiceOption[]): void {

View File

@@ -62,7 +62,7 @@ export class TabHeaderComponent extends Disposable implements AfterContentInit,
tabLabelcontainer.classList.add(this.tab.iconClass); tabLabelcontainer.classList.add(this.tab.iconClass);
} else { } else {
tabLabelcontainer.className = 'tabLabel'; tabLabelcontainer.className = 'tabLabel';
tabLabelcontainer.innerHTML = this.tab.title; tabLabelcontainer.textContent = this.tab.title;
} }
tabLabelcontainer.title = this.tab.title; tabLabelcontainer.title = this.tab.title;
} }

View File

@@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
/**
* Implements the various additional navigation keybindings we want out of slickgrid
*/
export class AdditionalKeyBindings<T> implements Slick.Plugin<T> {
private grid: Slick.Grid<T>;
private handler = new Slick.EventHandler();
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.handler.subscribe(this.grid.onKeyDown, (e, args) => this.handleKeyDown(e, args));
}
public destroy() {
this.handler.unsubscribeAll();
}
private handleKeyDown(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs<T>): void {
let event = new StandardKeyboardEvent(e);
let handled = true;
if (event.equals(KeyCode.RightArrow | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(args.row, this.grid.getColumns().length - 1);
} else if (event.equals(KeyCode.LeftArrow | KeyMod.CtrlCmd)) {
// account for row column
if (this.grid.canCellBeActive(args.row, 0)) {
this.grid.setActiveCell(args.row, 0);
} else {
this.grid.setActiveCell(args.row, 1);
}
} else if (event.equals(KeyCode.UpArrow | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(0, args.cell);
} else if (event.equals(KeyCode.DownArrow | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(this.grid.getDataLength() - 1, args.cell);
} else if (event.equals(KeyCode.Home | KeyMod.CtrlCmd)) {
// account for row column
if (this.grid.canCellBeActive(0, 0)) {
this.grid.setActiveCell(0, 0);
} else {
this.grid.setActiveCell(0, 1);
}
} else if (event.equals(KeyCode.End | KeyMod.CtrlCmd)) {
this.grid.setActiveCell(this.grid.getDataLength() - 1, this.grid.getColumns().length - 1);
} else {
handled = false;
}
if (handled) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}
}
}

View File

@@ -16,7 +16,7 @@ export class AutoColumnSize<T> implements Slick.Plugin<T> {
private _context: CanvasRenderingContext2D; private _context: CanvasRenderingContext2D;
private _options: IAutoColumnSizeOptions; private _options: IAutoColumnSizeOptions;
constructor(options: IAutoColumnSizeOptions) { constructor(options: IAutoColumnSizeOptions = defaultOptions) {
this._options = mixin(options, defaultOptions, false); this._options = mixin(options, defaultOptions, false);
} }

View File

@@ -0,0 +1,192 @@
// Drag select selection model gist taken from https://gist.github.com/skoon/5312536
// heavily modified
import { mixin } from 'vs/base/common/objects';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { IThemeService } from 'vs/platform/theme/common/themeService';
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangeselector');
export interface ICellRangeSelector<T> extends Slick.Plugin<T> {
onCellRangeSelected: Slick.Event<{ range: Slick.Range }>;
onBeforeCellRangeSelected: Slick.Event<Slick.Cell>;
}
export interface ICellSelectionModelOptions {
cellRangeSelector?: any;
selectActiveCell?: boolean;
}
const defaults: ICellSelectionModelOptions = {
selectActiveCell: true
};
export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slick.Range>> {
private grid: Slick.Grid<T>;
private selector: ICellRangeSelector<T>;
private ranges: Array<Slick.Range> = [];
public onSelectedRangesChanged = new Slick.Event<Array<Slick.Range>>();
constructor(private options: ICellSelectionModelOptions = defaults) {
this.options = mixin(this.options, defaults, false);
if (this.options.cellRangeSelector) {
this.selector = this.options.cellRangeSelector;
} else {
// this is added by the noderequires above
this.selector = new (<any>Slick).CellRangeSelector({ selectionCss: { 'border': '2px dashed grey' } });
}
}
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.grid.onActiveCellChanged.subscribe((e, args) => this.handleActiveCellChange(e, args));
this.grid.onKeyDown.subscribe(e => this.handleKeyDown(e));
this.grid.onHeaderClick.subscribe((e: MouseEvent, args) => this.handleHeaderClick(e, args));
this.grid.registerPlugin(this.selector);
this.selector.onCellRangeSelected.subscribe((e, args) => this.handleCellRangeSelected(e, args));
this.selector.onBeforeCellRangeSelected.subscribe((e, args) => this.handleBeforeCellRangeSelected(e, args));
}
public destroy() {
this.grid.onActiveCellChanged.unsubscribe((e, args) => this.handleActiveCellChange(e, args));
this.grid.onKeyDown.unsubscribe(e => this.handleKeyDown(e));
this.selector.onCellRangeSelected.unsubscribe((e, args) => this.handleCellRangeSelected(e, args));
this.selector.onBeforeCellRangeSelected.unsubscribe((e, args) => this.handleBeforeCellRangeSelected(e, args));
this.grid.unregisterPlugin(this.selector);
}
private removeInvalidRanges(ranges: Array<Slick.Range>): Array<Slick.Range> {
let result: Array<Slick.Range> = [];
for (let i = 0; i < ranges.length; i++) {
let r = ranges[i];
if (this.grid.canCellBeSelected(r.fromRow, r.fromCell) && this.grid.canCellBeSelected(r.toRow, r.toCell)) {
result.push(r);
} else if (this.grid.canCellBeSelected(r.fromRow, r.fromCell + 1) && this.grid.canCellBeSelected(r.toRow, r.toCell)) {
// account for number row
result.push(new Slick.Range(r.fromRow, r.fromCell + 1, r.toRow, r.toCell));
}
}
return result;
}
public setSelectedRanges(ranges: Array<Slick.Range>): void {
// simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged
if ((!this.ranges || this.ranges.length === 0) && (!ranges || ranges.length === 0)) {
return;
}
this.ranges = this.removeInvalidRanges(ranges);
this.onSelectedRangesChanged.notify(this.ranges);
}
public getSelectedRanges() {
return this.ranges;
}
private handleBeforeCellRangeSelected(e, args: Slick.Cell) {
if (this.grid.getEditorLock().isActive()) {
e.stopPropagation();
return false;
}
return true;
}
private handleCellRangeSelected(e, args: { range: Slick.Range }) {
this.grid.setActiveCell(args.range.fromRow, args.range.fromCell, false, false, true);
this.setSelectedRanges([args.range]);
}
private handleActiveCellChange(e, args) {
if (this.options.selectActiveCell && !isUndefinedOrNull(args.row) && !isUndefinedOrNull(args.cell)) {
this.setSelectedRanges([new Slick.Range(args.row, args.cell)]);
} else if (!this.options.selectActiveCell) {
// clear the previous selection once the cell changes
this.setSelectedRanges([]);
}
}
private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) {
if (!isUndefinedOrNull(args.column)) {
let columnIndex = this.grid.getColumnIndex(args.column.id);
if (this.grid.canCellBeSelected(0, columnIndex)) {
let ranges: Array<Slick.Range>;
if (e.shiftKey) {
ranges = this.getSelectedRanges();
ranges.push(new Slick.Range(0, columnIndex, this.grid.getDataLength() - 1, columnIndex));
} else {
ranges = [new Slick.Range(0, columnIndex, this.grid.getDataLength() - 1, columnIndex)];
}
this.grid.setActiveCell(0, columnIndex);
this.setSelectedRanges(ranges);
}
}
}
private handleKeyDown(e) {
/***
* Кey codes
* 37 left
* 38 up
* 39 right
* 40 down
*/
let ranges, last;
let active = this.grid.getActiveCell();
let metaKey = e.ctrlKey || e.metaKey;
if (active && e.shiftKey && !metaKey && !e.altKey &&
(e.which === 37 || e.which === 39 || e.which === 38 || e.which === 40)) {
ranges = this.getSelectedRanges();
if (!ranges.length) {
ranges.push(new Slick.Range(active.row, active.cell));
}
// keyboard can work with last range only
last = ranges.pop();
// can't handle selection out of active cell
if (!last.contains(active.row, active.cell)) {
last = new Slick.Range(active.row, active.cell);
}
let dRow = last.toRow - last.fromRow,
dCell = last.toCell - last.fromCell,
// walking direction
dirRow = active.row === last.fromRow ? 1 : -1,
dirCell = active.cell === last.fromCell ? 1 : -1;
if (e.which === 37) {
dCell -= dirCell;
} else if (e.which === 39) {
dCell += dirCell;
} else if (e.which === 38) {
dRow -= dirRow;
} else if (e.which === 40) {
dRow += dirRow;
}
// define new selection range
let new_last = new Slick.Range(active.row, active.cell, active.row + dirRow * dRow, active.cell + dirCell * dCell);
if (this.removeInvalidRanges([new_last]).length) {
ranges.push(new_last);
let viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow;
let viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell;
this.grid.scrollRowIntoView(viewRow, false);
this.grid.scrollCellIntoView(viewRow, viewCell, false);
} else {
ranges.push(last);
}
this.setSelectedRanges(ranges);
e.preventDefault();
e.stopPropagation();
}
}
}

View File

@@ -1,364 +0,0 @@
// Drag select selection model gist taken from https://gist.github.com/skoon/5312536
// heavily modified
import { clone } from 'sql/base/common/objects';
export class DragCellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slick.Range>> {
private readonly keyColResizeIncr = 5;
private _grid: Slick.Grid<T>;
private _ranges: Array<Slick.Range> = [];
private _dragging = false;
private _handler = new Slick.EventHandler();
public onSelectedRangesChanged = new Slick.Event<Slick.Range[]>();
public init(grid: Slick.Grid<T>): void {
this._grid = grid;
this._handler.subscribe(this._grid.onActiveCellChanged, (e: Event, data: Slick.OnActiveCellChangedEventArgs<T>) => this.handleActiveCellChange(e, data));
this._handler.subscribe(this._grid.onKeyDown, (e: JQueryInputEventObject) => this.handleKeyDown(e));
this._handler.subscribe(this._grid.onClick, (e: MouseEvent) => this.handleClick(e));
this._handler.subscribe(this._grid.onDrag, (e: MouseEvent) => this.handleDrag(e));
this._handler.subscribe(this._grid.onDragInit, (e: MouseEvent) => this.handleDragInit(e));
this._handler.subscribe(this._grid.onDragStart, (e: MouseEvent) => this.handleDragStart(e));
this._handler.subscribe(this._grid.onDragEnd, (e: MouseEvent) => this.handleDragEnd(e));
this._handler.subscribe(this._grid.onHeaderClick, (e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) => this.handleHeaderClick(e, args));
}
public destroy(): void {
this._handler.unsubscribeAll();
}
private rangesToRows(ranges: Array<Slick.Range>): Array<number> {
let rows = [];
for (let i = 0; i < ranges.length; i++) {
for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
rows.push(j);
}
}
return rows;
}
private rowsToRanges(rows: Array<number>): Array<Slick.Range> {
let ranges = [];
let lastCell = this._grid.getColumns().length - 1;
for (let i = 0; i < rows.length; i++) {
ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell));
}
return ranges;
}
public getSelectedRows(): Array<number> {
return this.rangesToRows(this._ranges);
}
public setSelectedRows(rows: Array<number>) {
this.setSelectedRanges(this.rowsToRanges(rows));
}
public setSelectedRanges(ranges: Array<Slick.Range>) {
this._ranges = ranges;
this.onSelectedRangesChanged.notify(this._ranges);
}
public getSelectedRanges(): Array<Slick.Range> {
return this._ranges;
}
private handleActiveCellChange(e: Event, data: Slick.OnActiveCellChangedEventArgs<T>) { }
private isNavigationKey(e: BaseJQueryEventObject) {
// Nave keys (home, end, arrows) are all in sequential order so use a
switch (e.which) {
case $.ui.keyCode.HOME:
case $.ui.keyCode.END:
case $.ui.keyCode.LEFT:
case $.ui.keyCode.UP:
case $.ui.keyCode.RIGHT:
case $.ui.keyCode.DOWN:
return true;
default:
return false;
}
}
private navigateLeft(e: JQueryInputEventObject, activeCell: Slick.Cell) {
if (activeCell.cell > 1) {
let isHome = e.which === $.ui.keyCode.HOME;
let newActiveCellColumn = isHome ? 1 : activeCell.cell - 1;
// Unsure why but for range, must record 1 index less than expected
let newRangeColumn = newActiveCellColumn - 1;
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the rightmost edge of the range and we navigate left,
// we want to deselect the rightmost cell
if (last.fromCell <= newRangeColumn) { last.toCell -= 1; }
let fromRow = Math.min(activeCell.row, last.fromRow);
let fromCell = Math.min(newRangeColumn, last.fromCell);
let toRow = Math.max(activeCell.row, last.toRow);
let toCell = Math.max(newRangeColumn, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row, newRangeColumn, activeCell.row, newRangeColumn)];
}
this._grid.setActiveCell(activeCell.row, newActiveCellColumn);
this.setSelectedRanges(this._ranges);
}
}
private navigateRight(e: JQueryInputEventObject, activeCell: Slick.Cell) {
let columnLength = this._grid.getColumns().length;
if (activeCell.cell < columnLength) {
let isEnd = e.which === $.ui.keyCode.END;
let newActiveCellColumn = isEnd ? columnLength : activeCell.cell + 1;
// Unsure why but for range, must record 1 index less than expected
let newRangeColumn = newActiveCellColumn - 1;
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the leftmost edge of the range and we navigate right,
// we want to deselect the leftmost cell
if (newRangeColumn <= last.toCell) { last.fromCell += 1; }
let fromRow = Math.min(activeCell.row, last.fromRow);
let fromCell = Math.min(newRangeColumn, last.fromCell);
let toRow = Math.max(activeCell.row, last.toRow);
let toCell = Math.max(newRangeColumn, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row, newRangeColumn, activeCell.row, newRangeColumn)];
}
this._grid.setActiveCell(activeCell.row, newActiveCellColumn);
this.setSelectedRanges(this._ranges);
}
}
private handleKeyDown(e: JQueryInputEventObject) {
let activeCell = this._grid.getActiveCell();
if (activeCell) {
// navigation keys
if (this.isNavigationKey(e)) {
e.stopImmediatePropagation();
if (e.ctrlKey || e.metaKey) {
let event = new CustomEvent('gridnav', {
detail: {
which: e.which,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
shiftKey: e.shiftKey,
altKey: e.altKey
}
});
window.dispatchEvent(event);
return;
}
// end key
if (e.which === $.ui.keyCode.END) {
this.navigateRight(e, activeCell);
}
// home key
if (e.which === $.ui.keyCode.HOME) {
this.navigateLeft(e, activeCell);
}
// left arrow
if (e.which === $.ui.keyCode.LEFT) {
// column resize
if ((e.ctrlKey || e.metaKey) && e.shiftKey) {
let allColumns = clone(this._grid.getColumns());
allColumns[activeCell.cell - 1].width = allColumns[activeCell.cell - 1].width - this.keyColResizeIncr;
this._grid.setColumnWidths(allColumns);
} else {
this.navigateLeft(e, activeCell);
}
// up arrow
} else if (e.which === $.ui.keyCode.UP && activeCell.row > 0) {
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the bottommost edge of the range and we navigate up,
// we want to deselect the bottommost row
let newRangeRow = activeCell.row - 1;
if (last.fromRow <= newRangeRow) { last.toRow -= 1; }
let fromRow = Math.min(activeCell.row - 1, last.fromRow);
let fromCell = Math.min(activeCell.cell - 1, last.fromCell);
let toRow = Math.max(newRangeRow, last.toRow);
let toCell = Math.max(activeCell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row - 1, activeCell.cell - 1, activeCell.row - 1, activeCell.cell - 1)];
}
this._grid.setActiveCell(activeCell.row - 1, activeCell.cell);
this.setSelectedRanges(this._ranges);
// right arrow
} else if (e.which === $.ui.keyCode.RIGHT) {
// column resize
if ((e.ctrlKey || e.metaKey) && e.shiftKey) {
let allColumns = clone(this._grid.getColumns());
allColumns[activeCell.cell - 1].width = allColumns[activeCell.cell - 1].width + this.keyColResizeIncr;
this._grid.setColumnWidths(allColumns);
} else {
this.navigateRight(e, activeCell);
}
// down arrow
} else if (e.which === $.ui.keyCode.DOWN && activeCell.row < this._grid.getDataLength() - 1) {
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the topmost edge of the range and we navigate down,
// we want to deselect the topmost row
let newRangeRow = activeCell.row + 1;
if (newRangeRow <= last.toRow) { last.fromRow += 1; }
let fromRow = Math.min(activeCell.row + 1, last.fromRow);
let fromCell = Math.min(activeCell.cell - 1, last.fromCell);
let toRow = Math.max(activeCell.row + 1, last.toRow);
let toCell = Math.max(activeCell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row + 1, activeCell.cell - 1, activeCell.row + 1, activeCell.cell - 1)];
}
this._grid.setActiveCell(activeCell.row + 1, activeCell.cell);
this.setSelectedRanges(this._ranges);
}
}
}
}
private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) {
let columnIndex = this._grid.getColumnIndex(args.column.id);
if (e.ctrlKey || e.metaKey) {
this._ranges.push(new Slick.Range(0, columnIndex, this._grid.getDataLength() - 1, columnIndex));
this._grid.setActiveCell(0, columnIndex + 1);
} else if (e.shiftKey && this._ranges.length) {
let last = this._ranges.pop().fromCell;
let from = Math.min(columnIndex, last);
let to = Math.max(columnIndex, last);
this._ranges = [];
for (let i = from; i <= to; i++) {
if (i !== last) {
this._ranges.push(new Slick.Range(0, i, this._grid.getDataLength() - 1, i));
}
}
this._ranges.push(new Slick.Range(0, last, this._grid.getDataLength() - 1, last));
} else {
this._ranges = [new Slick.Range(0, columnIndex, this._grid.getDataLength() - 1, columnIndex)];
this._grid.resetActiveCell();
}
this.setSelectedRanges(this._ranges);
e.stopImmediatePropagation();
return true;
}
private handleClick(e: MouseEvent) {
let cell = this._grid.getCellFromEvent(e);
if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {
return false;
}
if (!e.ctrlKey && !e.shiftKey && !e.metaKey) {
if (cell.cell !== 0) {
this._ranges = [new Slick.Range(cell.row, cell.cell - 1, cell.row, cell.cell - 1)];
this.setSelectedRanges(this._ranges);
this._grid.setActiveCell(cell.row, cell.cell);
return true;
} else {
this._ranges = [new Slick.Range(cell.row, 0, cell.row, this._grid.getColumns().length - 1)];
this.setSelectedRanges(this._ranges);
this._grid.setActiveCell(cell.row, 1);
return true;
}
}
else if (this._grid.getOptions().multiSelect) {
if (e.ctrlKey || e.metaKey) {
if (cell.cell === 0) {
this._ranges.push(new Slick.Range(cell.row, 0, cell.row, this._grid.getColumns().length - 1));
this._grid.setActiveCell(cell.row, 1);
} else {
this._ranges.push(new Slick.Range(cell.row, cell.cell - 1, cell.row, cell.cell - 1));
this._grid.setActiveCell(cell.row, cell.cell);
}
} else if (this._ranges.length && e.shiftKey) {
let last = this._ranges.pop();
if (cell.cell === 0) {
let fromRow = Math.min(cell.row, last.fromRow);
let toRow = Math.max(cell.row, last.fromRow);
this._ranges = [new Slick.Range(fromRow, 0, toRow, this._grid.getColumns().length - 1)];
} else {
let fromRow = Math.min(cell.row, last.fromRow);
let fromCell = Math.min(cell.cell - 1, last.fromCell);
let toRow = Math.max(cell.row, last.toRow);
let toCell = Math.max(cell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
}
}
}
this.setSelectedRanges(this._ranges);
return true;
}
private handleDragInit(e: MouseEvent) {
e.stopImmediatePropagation();
}
private handleDragStart(e: MouseEvent) {
let cell = this._grid.getCellFromEvent(e);
e.stopImmediatePropagation();
this._dragging = true;
if (e.ctrlKey || e.metaKey) {
this._ranges.push(new Slick.Range(cell.row, cell.cell));
this._grid.setActiveCell(cell.row, cell.cell);
} else if (this._ranges.length && e.shiftKey) {
let last = this._ranges.pop();
let fromRow = Math.min(cell.row, last.fromRow);
let fromCell = Math.min(cell.cell - 1, last.fromCell);
let toRow = Math.max(cell.row, last.toRow);
let toCell = Math.max(cell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(cell.row, cell.cell)];
this._grid.setActiveCell(cell.row, cell.cell);
}
this.setSelectedRanges(this._ranges);
}
private handleDrag(e: MouseEvent) {
if (this._dragging) {
let cell = this._grid.getCellFromEvent(e);
let activeCell = this._grid.getActiveCell();
if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {
return false;
}
this._ranges.pop();
if (activeCell.cell === 0) {
let lastCell = this._grid.getColumns().length - 1;
let firstRow = Math.min(cell.row, activeCell.row);
let lastRow = Math.max(cell.row, activeCell.row);
this._ranges.push(new Slick.Range(firstRow, 0, lastRow, lastCell));
} else {
let firstRow = Math.min(cell.row, activeCell.row);
let lastRow = Math.max(cell.row, activeCell.row);
let firstColumn = Math.min(cell.cell - 1, activeCell.cell - 1);
let lastColumn = Math.max(cell.cell - 1, activeCell.cell - 1);
this._ranges.push(new Slick.Range(firstRow, firstColumn, lastRow, lastColumn));
}
this.setSelectedRanges(this._ranges);
return true;
}
return false;
}
private handleDragEnd(e: MouseEvent) {
this._dragging = false;
}
}

View File

@@ -6,6 +6,7 @@ import { mixin } from 'vs/base/common/objects';
import { SlickGrid } from 'angular2-slickgrid'; import { SlickGrid } from 'angular2-slickgrid';
import { Button } from '../../button/button'; import { Button } from '../../button/button';
import { attachButtonStyler } from 'sql/common/theme/styler'; import { attachButtonStyler } from 'sql/common/theme/styler';
import { escape } from 'sql/base/common/strings';
import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IThemeService } from 'vs/platform/theme/common/themeService';
export class HeaderFilter { export class HeaderFilter {
@@ -174,7 +175,7 @@ export class HeaderFilter {
if (filterItems[i] && filterItems[i].indexOf('Error:') < 0) { if (filterItems[i] && filterItems[i].indexOf('Error:') < 0) {
filterOptions += '<label><input type="checkbox" value="' + i + '"' filterOptions += '<label><input type="checkbox" value="' + i + '"'
+ (filtered ? ' checked="checked"' : '') + (filtered ? ' checked="checked"' : '')
+ '/>' + filterItems[i] + '</label>'; + '/>' + escape(filterItems[i]) + '</label>';
} }
} }
let $filter = $('<div class="filter">') let $filter = $('<div class="filter">')

View File

@@ -1,5 +1,6 @@
// Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.rowdetailview.js // Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.rowdetailview.js
// heavily modified // heavily modified
import { escape } from 'sql/base/common/strings';
import { mixin } from 'vs/base/common/objects'; import { mixin } from 'vs/base/common/objects';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
@@ -354,7 +355,7 @@ export class RowDetailView {
html.push("style='height:", dataContext._height, "px;"); //set total height of padding html.push("style='height:", dataContext._height, "px;"); //set total height of padding
html.push("top:", rowHeight, "px'>"); //shift detail below 1st row html.push("top:", rowHeight, "px'>"); //shift detail below 1st row
html.push("<div id='detailViewContainer_", dataContext.id, "' class='detail-container' style='max-height:" + (dataContext._height - rowHeight + bottomMargin) + "px'>"); //sub ctr for custom styling html.push("<div id='detailViewContainer_", dataContext.id, "' class='detail-container' style='max-height:" + (dataContext._height - rowHeight + bottomMargin) + "px'>"); //sub ctr for custom styling
html.push("<div id='innerDetailView_", dataContext.id, "'>", dataContext._detailContent, "</div></div>"); html.push("<div id='innerDetailView_", dataContext.id, "'>", escape(dataContext._detailContent), "</div></div>");
//&omit a final closing detail container </div> that would come next //&omit a final closing detail container </div> that would come next
return html.join(''); return html.join('');

View File

@@ -0,0 +1,70 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { range } from 'vs/base/common/arrays';
export interface IRowNumberColumnOptions {
numberOfRows: number;
cssClass?: string;
}
const sizePerDigit = 15;
export class RowNumberColumn<T> implements Slick.Plugin<T> {
private handler = new Slick.EventHandler();
private grid: Slick.Grid<T>;
constructor(private options: IRowNumberColumnOptions) {
}
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.handler
.subscribe(this.grid.onClick, (e, args) => this.handleClick(e, args))
.subscribe(this.grid.onHeaderClick, (e, args) => this.handleHeaderClick(e, args));
}
public destroy() {
this.handler.unsubscribeAll();
}
private handleClick(e: MouseEvent, args: Slick.OnClickEventArgs<T>): void {
if (this.grid.getColumns()[args.cell].id === 'rowNumber') {
this.grid.setActiveCell(args.row, 1);
this.grid.setSelectedRows([args.row]);
}
}
private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>): void {
if (args.column.id === 'rowNumber') {
this.grid.setActiveCell(0, 1);
this.grid.setSelectedRows(range(this.grid.getDataLength()));
}
}
public getColumnDefinition(): Slick.Column<T> {
return {
id: 'rowNumber',
name: '',
field: 'rowNumber',
width: this.options.numberOfRows.toString().length * sizePerDigit,
resizable: false,
cssClass: this.options.cssClass,
focusable: false,
selectable: false,
formatter: (r, c, v, cd, dc) => this.formatter(r, c, v, cd, dc)
};
}
private formatter(row, cell, value, columnDef: Slick.Column<T>, dataContext): string {
if (dataContext) {
return `<span>${row}</span>`;
}
return null;
}
}

View File

@@ -14,7 +14,7 @@ export interface IFindPosition {
row: number; row: number;
} }
export function defaultSort<T>(args: Slick.OnSortEventArgs<T>, data: Array<T>): Array<T> { 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);

View 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>add</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,16H0V0H16Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14,6v4H10v4H6V10H2V6H6V2h4V6Z"/></g><g id="iconBg"><path class="icon-vs-bg" d="M13,7V9H9v4H7V9H3V7H7V3H9V7Z"/></g></svg>

After

Width:  |  Height:  |  Size: 486 B

View File

@@ -18,6 +18,12 @@
background-image: url('start.svg'); background-image: url('start.svg');
} }
.vs .icon.add,
.vs-dark .icon.add,
.hc-black .icon.add {
background-image: url('add.svg');
}
.vs .icon.stop, .vs .icon.stop,
.vs-dark .icon.stop, .vs-dark .icon.stop,
.hc-black .icon.stop { .hc-black .icon.stop {

View File

@@ -82,7 +82,7 @@ export class Taskbar {
public static createTaskbarText(inputText: string): HTMLElement { public static createTaskbarText(inputText: string): HTMLElement {
let element = document.createElement('div'); let element = document.createElement('div');
element.className = 'taskbarTextSeparator'; element.className = 'taskbarTextSeparator';
element.innerHTML = inputText; element.textContent = inputText;
return element; return element;
} }

View File

@@ -15,6 +15,7 @@ export function escape(html: string): string {
case '>': return '&gt;'; case '>': return '&gt;';
case '&': return '&amp;'; case '&': return '&amp;';
case '"': return '&quot;'; case '"': return '&quot;';
case '\'': return '&#39';
default: return match; default: return match;
} }
}); });

View File

@@ -100,7 +100,7 @@ export class AutoOAuthDialog extends Modal {
let inputBox: InputBox; let inputBox: InputBox;
container.div({ class: 'dialog-input-section' }, (inputContainer) => { container.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => { inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(label); labelContainer.text(label);
}); });
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => { inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {

View File

@@ -145,7 +145,7 @@ export class FirewallRuleDialog extends Modal {
subnetIPRangeSection = subnetIPRangeContainer.getHTMLElement(); subnetIPRangeSection = subnetIPRangeContainer.getHTMLElement();
subnetIPRangeContainer.div({ 'class': 'dialog-input-section' }, (inputContainer) => { subnetIPRangeContainer.div({ 'class': 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => { inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(LocalizedStrings.FROM); labelContainer.text(LocalizedStrings.FROM);
}); });
inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => { inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => {
@@ -155,7 +155,7 @@ export class FirewallRuleDialog extends Modal {
}); });
inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => { inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(LocalizedStrings.TO); labelContainer.text(LocalizedStrings.TO);
}); });
inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => { inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => {
@@ -234,7 +234,7 @@ export class FirewallRuleDialog extends Modal {
className += ' header'; className += ' header';
} }
container.div({ 'class': className }, (labelContainer) => { container.div({ 'class': className }, (labelContainer) => {
labelContainer.innerHtml(content); labelContainer.text(content);
}); });
} }

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { IExtensionGalleryService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionGalleryService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions';
import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
@@ -13,10 +13,9 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { DashboardEditor } from 'sql/parts/dashboard/dashboardEditor'; import { DashboardEditor } from 'sql/parts/dashboard/dashboardEditor';
import { DashboardInput } from 'sql/parts/dashboard/dashboardInput'; import { DashboardInput } from 'sql/parts/dashboard/dashboardInput';
import { AddServerGroupAction, AddServerAction } from 'sql/parts/objectExplorer/viewlet/connectionTreeAction'; import { AddServerGroupAction, AddServerAction } from 'sql/parts/objectExplorer/viewlet/connectionTreeAction';
import { ClearRecentConnectionsAction } from 'sql/parts/connection/common/connectionActions'; import { ClearRecentConnectionsAction, GetCurrentConnectionStringAction } from 'sql/parts/connection/common/connectionActions';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
import { EditorDescriptor } from 'vs/workbench/browser/editor';
import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService';
import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
@@ -49,6 +48,7 @@ actionRegistry.registerWorkbenchAction(
), ),
ClearRecentConnectionsAction.LABEL ClearRecentConnectionsAction.LABEL
); );
actionRegistry.registerWorkbenchAction( actionRegistry.registerWorkbenchAction(
new SyncActionDescriptor( new SyncActionDescriptor(
AddServerGroupAction, AddServerGroupAction,
@@ -67,6 +67,15 @@ actionRegistry.registerWorkbenchAction(
AddServerAction.LABEL AddServerAction.LABEL
); );
actionRegistry.registerWorkbenchAction(
new SyncActionDescriptor(
GetCurrentConnectionStringAction,
GetCurrentConnectionStringAction.ID,
GetCurrentConnectionStringAction.LABEL
),
GetCurrentConnectionStringAction.LABEL
);
let configurationRegistry = <IConfigurationRegistry>Registry.as(ConfigExtensions.Configuration); let configurationRegistry = <IConfigurationRegistry>Registry.as(ConfigExtensions.Configuration);
configurationRegistry.registerConfiguration({ configurationRegistry.registerConfiguration({
'id': 'connection', 'id': 'connection',

View File

@@ -13,6 +13,11 @@ import { IConnectionManagementService } from 'sql/parts/connection/common/connec
import { INotificationService, INotificationActions } from 'vs/platform/notification/common/notification'; import { INotificationService, INotificationActions } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity'; import Severity from 'vs/base/common/severity';
import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IObjectExplorerService } from '../../objectExplorer/common/objectExplorerService';
import { QueryInput } from 'sql/parts/query/common/queryInput';
import { EditDataInput } from 'sql/parts/editData/common/editDataInput';
import { DashboardInput } from 'sql/parts/dashboard/dashboardInput';
/** /**
* Workbench action to clear the recent connnections list * Workbench action to clear the recent connnections list
@@ -126,4 +131,44 @@ export class ClearSingleRecentConnectionAction extends Action {
this._onRecentConnectionRemoved.fire(); this._onRecentConnectionRemoved.fire();
}); });
} }
} }
/**
* Action to retrieve the current connection string
*/
export class GetCurrentConnectionStringAction extends Action {
public static ID = 'getCurrentConnectionStringAction';
public static LABEL = nls.localize('connectionAction.GetCurrentConnectionString', "Get Current Connection String");
constructor(
id: string,
label: string,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IWorkbenchEditorService private _editorService: IWorkbenchEditorService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@INotificationService private readonly _notificationService: INotificationService
) {
super(GetCurrentConnectionStringAction.ID, GetCurrentConnectionStringAction.LABEL);
this.enabled = true;
}
public run(): TPromise<void> {
return new TPromise<void>((resolve, reject) => {
let activeInput = this._editorService.getActiveEditorInput();
if (activeInput && (activeInput instanceof QueryInput || activeInput instanceof EditDataInput || activeInput instanceof DashboardInput)
&& this._connectionManagementService.isConnected(activeInput.uri)) {
let includePassword = false;
this._connectionManagementService.getConnectionString(activeInput.uri, includePassword).then(result => {
let message = result
? result
: nls.localize('connectionAction.connectionString', "Connection string not available");
this._notificationService.info(message);
});
} else {
let message = nls.localize('connectionAction.noConnection', "No active connection available");
this._notificationService.info(message);
}
});
}
}

View File

@@ -152,10 +152,12 @@ export interface IConnectionManagementService {
getAdvancedProperties(): sqlops.ConnectionOption[]; getAdvancedProperties(): sqlops.ConnectionOption[];
getConnectionId(connectionProfile: IConnectionProfile): string; getConnectionUri(connectionProfile: IConnectionProfile): string;
getFormattedUri(uri: string, connectionProfile: IConnectionProfile): string; getFormattedUri(uri: string, connectionProfile: IConnectionProfile): string;
getConnectionUriFromId(connectionId: string): string;
isConnected(fileUri: string): boolean; isConnected(fileUri: string): boolean;
/** /**
@@ -258,6 +260,11 @@ export interface IConnectionManagementService {
* in the connection profile's options dictionary, or undefined if the profile is not connected * in the connection profile's options dictionary, or undefined if the profile is not connected
*/ */
getActiveConnectionCredentials(profileId: string): { [name: string]: string }; getActiveConnectionCredentials(profileId: string): { [name: string]: string };
/**
* Get the connection string for the provided connection profile
*/
getConnectionString(ownerUri: string, includePassword: boolean): Thenable<string>;
} }
export const IConnectionDialogService = createDecorator<IConnectionDialogService>('connectionDialogService'); export const IConnectionDialogService = createDecorator<IConnectionDialogService>('connectionDialogService');
@@ -320,12 +327,13 @@ export enum MetadataType {
} }
export enum TaskStatus { export enum TaskStatus {
notStarted = 0, NotStarted = 0,
inProgress = 1, InProgress = 1,
succeeded = 2, Succeeded = 2,
succeededWithWarning = 3, SucceededWithWarning = 3,
failed = 4, Failed = 4,
canceled = 5 Canceled = 5,
Canceling = 6
} }
export interface IConnectionParams { export interface IConnectionParams {

View File

@@ -648,6 +648,15 @@ export class ConnectionManagementService extends Disposable implements IConnecti
return this._connectionStatusManager.getActiveConnectionProfiles(); return this._connectionStatusManager.getActiveConnectionProfiles();
} }
public getConnectionUriFromId(connectionId: string): string {
let connection = this.getActiveConnections().find(connection => connection.id === connectionId);
if (connection) {
return this.getConnectionUri(connection);
} else {
return undefined;
}
}
public saveProfileGroup(profile: IConnectionProfileGroup): Promise<string> { public saveProfileGroup(profile: IConnectionProfileGroup): Promise<string> {
TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.AddServerGroup); TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.AddServerGroup);
return new Promise<string>((resolve, reject) => { return new Promise<string>((resolve, reject) => {
@@ -704,7 +713,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
return false; return false;
} }
public getConnectionId(connectionProfile: IConnectionProfile): string { public getConnectionUri(connectionProfile: IConnectionProfile): string {
return this._connectionStatusManager.getOriginalOwnerUri(Utils.generateUri(connectionProfile)); return this._connectionStatusManager.getOriginalOwnerUri(Utils.generateUri(connectionProfile));
} }
@@ -716,7 +725,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
*/ */
public getFormattedUri(uri: string, connectionProfile: IConnectionProfile): string { public getFormattedUri(uri: string, connectionProfile: IConnectionProfile): string {
if (this._connectionStatusManager.isDefaultTypeUri(uri)) { if (this._connectionStatusManager.isDefaultTypeUri(uri)) {
return this.getConnectionId(connectionProfile); return this.getConnectionUri(connectionProfile);
} else { } else {
return uri; return uri;
} }
@@ -1111,7 +1120,8 @@ export class ConnectionManagementService extends Disposable implements IConnecti
resolve(result); resolve(result);
}); });
} else { } else {
resolve(self.disconnectEditor(owner)); // If the editor is connected then there is nothing to cancel
resolve(false);
} }
}); });
} }
@@ -1336,4 +1346,24 @@ export class ConnectionManagementService extends Disposable implements IConnecti
credentials[passwordOption.name] = profile.options[passwordOption.name]; credentials[passwordOption.name] = profile.options[passwordOption.name];
return credentials; return credentials;
} }
/**
* Get the connection string for the provided connection profile
*/
public getConnectionString(ownerUri: string, includePassword: boolean = false): Thenable<string> {
if (!ownerUri) {
return Promise.resolve(undefined);
}
let providerId = this.getProviderIdFromUri(ownerUri);
if (!providerId) {
return Promise.resolve(undefined);
}
return this._providers.get(providerId).onReady.then(provider => {
return provider.getConnectionString(ownerUri, includePassword).then(connectionString => {
return connectionString;
});
});
}
} }

View File

@@ -63,7 +63,7 @@ export class ConnectionController implements IConnectionComponentController {
tempProfile.password = password; tempProfile.password = password;
tempProfile.groupFullName = ''; tempProfile.groupFullName = '';
tempProfile.saveProfile = false; tempProfile.saveProfile = false;
let uri = this._connectionManagementService.getConnectionId(tempProfile); let uri = this._connectionManagementService.getConnectionUri(tempProfile);
return new Promise<string[]>((resolve, reject) => { return new Promise<string[]>((resolve, reject) => {
if (this._databaseCache.has(uri)) { if (this._databaseCache.has(uri)) {
let cachedDatabases: string[] = this._databaseCache.get(uri); let cachedDatabases: string[] = this._databaseCache.get(uri);

View File

@@ -144,9 +144,6 @@ export class ConnectionDialogService implements IConnectionDialogService {
} else { } else {
this._connectionManagementService.cancelConnection(this._model); this._connectionManagementService.cancelConnection(this._model);
} }
if (params && params.input && params.input.onConnectReject) {
params.input.onConnectReject();
}
this._connectionDialog.resetConnection(); this._connectionDialog.resetConnection();
this._connecting = false; this._connecting = false;
} }

View File

@@ -262,7 +262,7 @@ export class ConnectionDialogWidget extends Modal {
let recentHistoryLabel = localize('recentHistory', 'Recent history'); let recentHistoryLabel = localize('recentHistory', 'Recent history');
recentConnectionContainer.div({ class: 'recent-titles-container' }, (container) => { recentConnectionContainer.div({ class: 'recent-titles-container' }, (container) => {
container.div({ class: 'connection-history-label' }, (recentTitle) => { container.div({ class: 'connection-history-label' }, (recentTitle) => {
recentTitle.innerHtml(recentHistoryLabel); recentTitle.text(recentHistoryLabel);
}); });
container.div({ class: 'connection-history-actions' }, (actionsContainer) => { container.div({ class: 'connection-history-actions' }, (actionsContainer) => {
this._actionbar = this._register(new ActionBar(actionsContainer.getHTMLElement(), { animated: false })); this._actionbar = this._register(new ActionBar(actionsContainer.getHTMLElement(), { animated: false }));
@@ -303,7 +303,7 @@ export class ConnectionDialogWidget extends Modal {
this._noRecentConnectionBuilder.div({ class: 'connection-recent-content' }, (noRecentConnectionContainer) => { this._noRecentConnectionBuilder.div({ class: 'connection-recent-content' }, (noRecentConnectionContainer) => {
let noRecentHistoryLabel = localize('noRecentConnections', 'No recent connection'); let noRecentHistoryLabel = localize('noRecentConnections', 'No recent connection');
noRecentConnectionContainer.div({ class: 'no-recent-connections' }, (noRecentTitle) => { noRecentConnectionContainer.div({ class: 'no-recent-connections' }, (noRecentTitle) => {
noRecentTitle.innerHtml(noRecentHistoryLabel); noRecentTitle.text(noRecentHistoryLabel);
}); });
}); });
} }
@@ -335,7 +335,7 @@ export class ConnectionDialogWidget extends Modal {
this._noSavedConnectionBuilder.div({ class: 'connection-saved-content' }, (noSavedConnectionContainer) => { this._noSavedConnectionBuilder.div({ class: 'connection-saved-content' }, (noSavedConnectionContainer) => {
let noSavedConnectionLabel = localize('noSavedConnections', 'No saved connection'); let noSavedConnectionLabel = localize('noSavedConnections', 'No saved connection');
noSavedConnectionContainer.div({ class: 'no-saved-connections' }, (titleContainer) => { noSavedConnectionContainer.div({ class: 'no-saved-connections' }, (titleContainer) => {
titleContainer.innerHtml(noSavedConnectionLabel); titleContainer.text(noSavedConnectionLabel);
}); });
}); });
} }

View File

@@ -22,6 +22,7 @@
border-top-color: transparent; border-top-color: transparent;
height: calc(100% - 350px); height: calc(100% - 350px);
display: block; display: block;
min-height: 120px;
} }
.connection-recent, .connection-saved { .connection-recent, .connection-saved {

View File

@@ -57,13 +57,14 @@ import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsV
import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component'; import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component'; import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component'; import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editableDropdown.component';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.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, AlertsViewComponent, ProxiesViewComponent, OperatorsViewComponent, JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, AlertsViewComponent, ProxiesViewComponent, OperatorsViewComponent,
DashboardModelViewContainer, ModelComponentWrapper, Checkbox, SelectBox, InputBox,]; DashboardModelViewContainer, ModelComponentWrapper, Checkbox, EditableDropDown, SelectBox, InputBox,];
/* Panel */ /* Panel */
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module'; import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';

View File

@@ -163,7 +163,7 @@ export class NewDashboardTabDialog extends Modal {
this._noExtensionViewContainer = DOM.$('.no-extension-view'); this._noExtensionViewContainer = DOM.$('.no-extension-view');
let noExtensionTitle = DOM.append(this._noExtensionViewContainer, DOM.$('.no-extensionTab-label')); let noExtensionTitle = DOM.append(this._noExtensionViewContainer, DOM.$('.no-extensionTab-label'));
let noExtensionLabel = localize('newdashboardTabDialog.noExtensionLabel', 'No dashboard extensions are installed at this time. Go to Extension Manager to explore recommended extensions.'); let noExtensionLabel = localize('newdashboardTabDialog.noExtensionLabel', 'No dashboard extensions are installed at this time. Go to Extension Manager to explore recommended extensions.');
noExtensionTitle.innerHTML = noExtensionLabel; noExtensionTitle.textContent = noExtensionLabel;
DOM.append(container, this._noExtensionViewContainer); DOM.append(container, this._noExtensionViewContainer);
} }

View File

@@ -16,7 +16,7 @@ const properties: IJSONSchema = {
properties: { properties: {
yAxisMin: { yAxisMin: {
type: 'number', type: 'number',
description: nls.localize('yAxisMin', "Minumum value of the y axis") description: nls.localize('yAxisMin', "Minimum value of the y axis")
}, },
yAxisMax: { yAxisMax: {
type: 'number', type: 'number',
@@ -28,7 +28,7 @@ const properties: IJSONSchema = {
}, },
xAxisMin: { xAxisMin: {
type: 'number', type: 'number',
description: nls.localize('xAxisMin', "Minumum value of the x axis") description: nls.localize('xAxisMin', "Minimum value of the x axis")
}, },
xAxisMax: { xAxisMax: {
type: 'number', type: 'number',

View File

@@ -11,8 +11,8 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work
import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces'; import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { Table } from 'sql/base/browser/ui/table/table'; import { Table } from 'sql/base/browser/ui/table/table';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView'; import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { DragCellSelectionModel } from 'sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin'; import { attachTableStyler } from 'sql/common/theme/styler';
import { attachTableStyler} from 'sql/common/theme/styler'; import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
@Component({ @Component({
template: '' template: ''
@@ -63,7 +63,7 @@ export default class TableInsight extends Disposable implements IInsightsView, O
private createTable() { private createTable() {
if (!this.table) { if (!this.table) {
this.table = new Table(this._elementRef.nativeElement, this.dataView, this.columns, { showRowNumber: true }); this.table = new Table(this._elementRef.nativeElement, this.dataView, this.columns, { showRowNumber: true });
this.table.setSelectionModel(new DragCellSelectionModel()); this.table.setSelectionModel(new CellSelectionModel());
this._register(attachTableStyler(this.table, this.themeService)); this._register(attachTableStyler(this.table, this.themeService));
} }
} }

View File

@@ -146,7 +146,7 @@ export class BackupUiService implements IBackupUiService {
let backupOptions = this.getOptions(this._currentProvider); let backupOptions = this.getOptions(this._currentProvider);
return new TPromise<void>(() => { return new TPromise<void>(() => {
let uri = this._connectionManagementService.getConnectionId(connection) let uri = this._connectionManagementService.getConnectionUri(connection)
+ ProviderConnectionInfo.idSeparator + ProviderConnectionInfo.idSeparator
+ ConnectionUtils.ConnectionUriBackupIdAttributeName + ConnectionUtils.ConnectionUriBackupIdAttributeName
+ ProviderConnectionInfo.nameValueSeparator + ProviderConnectionInfo.nameValueSeparator

View File

@@ -179,8 +179,8 @@ export class RestoreDialogController implements IRestoreDialogController {
private isSuccessfulRestore(response: TaskNode): boolean { private isSuccessfulRestore(response: TaskNode): boolean {
return (response.taskName === this._restoreTaskName && return (response.taskName === this._restoreTaskName &&
response.message === this._restoreCompleted && response.message === this._restoreCompleted &&
(response.status === TaskStatus.succeeded || (response.status === TaskStatus.Succeeded ||
response.status === TaskStatus.succeededWithWarning) && response.status === TaskStatus.SucceededWithWarning) &&
(response.taskExecutionMode === TaskExecutionMode.execute || (response.taskExecutionMode === TaskExecutionMode.execute ||
response.taskExecutionMode === TaskExecutionMode.executeAndScript)); response.taskExecutionMode === TaskExecutionMode.executeAndScript));
} }
@@ -293,7 +293,7 @@ export class RestoreDialogController implements IRestoreDialogController {
return new TPromise<void>((resolve, reject) => { return new TPromise<void>((resolve, reject) => {
let result: void; let result: void;
this._ownerUri = this._connectionService.getConnectionId(connection) this._ownerUri = this._connectionService.getConnectionUri(connection)
+ ProviderConnectionInfo.idSeparator + ProviderConnectionInfo.idSeparator
+ Utils.ConnectionUriRestoreIdAttributeName + Utils.ConnectionUriRestoreIdAttributeName
+ ProviderConnectionInfo.nameValueSeparator + ProviderConnectionInfo.nameValueSeparator

View File

@@ -226,7 +226,7 @@ export class RestoreDialog extends Modal {
destinationContainer.div({ class: 'dialog-input-section' }, (inputContainer) => { destinationContainer.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => { inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(LocalizedStrings.TARGETDATABASE); labelContainer.text(LocalizedStrings.TARGETDATABASE);
}); });
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => { inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
@@ -471,7 +471,7 @@ export class RestoreDialog extends Modal {
className += ' header'; className += ' header';
} }
container.div({ class: className }, (labelContainer) => { container.div({ class: className }, (labelContainer) => {
labelContainer.innerHtml(content); labelContainer.text(content);
}); });
} }
@@ -535,7 +535,7 @@ export class RestoreDialog extends Modal {
let selectBox: SelectBox; let selectBox: SelectBox;
container.div({ class: 'dialog-input-section' }, (inputContainer) => { container.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => { inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(label); labelContainer.text(label);
}); });
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => { inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {

View File

@@ -16,6 +16,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import Severity from 'vs/base/common/severity'; import Severity from 'vs/base/common/severity';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput'; import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
/** /**
* Input for the EditDataEditor. * Input for the EditDataEditor.
@@ -37,6 +38,8 @@ export class EditDataInput extends EditorInput implements IConnectableInput {
private _css: HTMLStyleElement; private _css: HTMLStyleElement;
private _useQueryFilter: boolean; private _useQueryFilter: boolean;
public savedViewState: IEditorViewState;
constructor( constructor(
private _uri: URI, private _uri: URI,
private _schemaName, private _schemaName,

View File

@@ -8,6 +8,7 @@
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { EditorInput } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor';
import { Emitter } from 'vs/base/common/event';
/** /**
* Input for the EditDataResultsEditor. This input helps with logic for the viewing and editing of * Input for the EditDataResultsEditor. This input helps with logic for the viewing and editing of
@@ -25,6 +26,9 @@ export class EditDataResultsInput extends EditorInput {
private _editorContainer: HTMLElement; private _editorContainer: HTMLElement;
public css: HTMLStyleElement; public css: HTMLStyleElement;
public readonly onRestoreViewStateEmitter = new Emitter<void>();
public readonly onSaveViewStateEmitter = new Emitter<void>();
constructor(private _uri: string) { constructor(private _uri: string) {
super(); super();
this._visible = false; this._visible = false;

View File

@@ -13,7 +13,7 @@ import { Builder } from 'vs/base/browser/builder';
import { EditorOptions, EditorInput } from 'vs/workbench/common/editor'; import { EditorOptions, EditorInput } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { Position, IEditorControl, IEditor } from 'vs/platform/editor/common/editor'; import { Position, IEditorControl, IEditor, IEditorInput } from 'vs/platform/editor/common/editor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -43,6 +43,8 @@ import { IFlexibleSash, VerticalFlexibleSash, HorizontalFlexibleSash } from 'sql
import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { Orientation } from 'vs/base/browser/ui/sash/sash';
import { EditDataResultsEditor } from 'sql/parts/editData/editor/editDataResultsEditor'; import { EditDataResultsEditor } from 'sql/parts/editData/editor/editDataResultsEditor';
import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput'; import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { Emitter } from 'vs/base/common/event';
/** /**
* Editor that hosts an action bar and a resultSetInput for an edit data session * Editor that hosts an action bar and a resultSetInput for an edit data session
@@ -96,6 +98,14 @@ export class EditDataEditor extends BaseEditor {
if (contextKeyService) { if (contextKeyService) {
this._queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService); this._queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService);
} }
if (_editorGroupService) {
_editorGroupService.onEditorOpening(e => {
if (this.isVisible() && (e.input !== this.input || e.position !== this.position)) {
this.saveEditorViewState();
}
});
}
} }
// PUBLIC METHODS //////////////////////////////////////////////////////////// // PUBLIC METHODS ////////////////////////////////////////////////////////////
@@ -594,7 +604,15 @@ export class EditDataEditor extends BaseEditor {
// Run all three steps synchronously // Run all three steps synchronously
return createEditors() return createEditors()
.then(onEditorsCreated) .then(onEditorsCreated)
.then(doLayout); .then(doLayout)
.then(() => {
if (newInput.results) {
newInput.results.onRestoreViewStateEmitter.fire();
}
if (newInput.savedViewState) {
this._sqlEditor.getControl().restoreViewState(newInput.savedViewState);
}
});
} }
private _setSashDimension(): void { private _setSashDimension(): void {
@@ -631,7 +649,6 @@ export class EditDataEditor extends BaseEditor {
* has been opened with the same editor, or we are opening the editor for the first time). * has been opened with the same editor, or we are opening the editor for the first time).
*/ */
private _updateInput(oldInput: EditDataInput, newInput: EditDataInput, options?: EditorOptions): TPromise<void> { private _updateInput(oldInput: EditDataInput, newInput: EditDataInput, options?: EditorOptions): TPromise<void> {
if (this._sqlEditor) { if (this._sqlEditor) {
this._sqlEditor.clearInput(); this._sqlEditor.clearInput();
} }
@@ -727,4 +744,16 @@ export class EditDataEditor extends BaseEditor {
public queryPaneEnabled(): boolean { public queryPaneEnabled(): boolean {
return this.editDataInput.queryPaneEnabled; return this.editDataInput.queryPaneEnabled;
} }
private saveEditorViewState(): void {
let editDataInput = this.input as EditDataInput;
if (editDataInput) {
if (this._sqlEditor) {
editDataInput.savedViewState = this._sqlEditor.getControl().saveViewState();
}
if (editDataInput.results) {
editDataInput.results.onSaveViewStateEmitter.fire();
}
}
}
} }

View File

@@ -23,6 +23,7 @@ import { IEditDataComponentParams } from 'sql/services/bootstrap/bootstrapParams
import { EditDataModule } from 'sql/parts/grid/views/editData/editData.module'; import { EditDataModule } from 'sql/parts/grid/views/editData/editData.module';
import { EDITDATA_SELECTOR } from 'sql/parts/grid/views/editData/editData.component'; import { EDITDATA_SELECTOR } from 'sql/parts/grid/views/editData/editData.component';
import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput'; import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput';
import { Event } from 'vs/base/common/event';
export class EditDataResultsEditor extends BaseEditor { export class EditDataResultsEditor extends BaseEditor {
@@ -109,7 +110,11 @@ export class EditDataResultsEditor extends BaseEditor {
// Otherwise many components will be left around and be subscribed // Otherwise many components will be left around and be subscribed
// to events from the backing data service // to events from the backing data service
const parent = input.container; const parent = input.container;
let params: IEditDataComponentParams = { dataService: dataService }; let params: IEditDataComponentParams = {
dataService: dataService,
onSaveViewState: input.onSaveViewStateEmitter.event,
onRestoreViewState: input.onRestoreViewStateEmitter.event
};
bootstrapAngular(this._instantiationService, bootstrapAngular(this._instantiationService,
EditDataModule, EditDataModule,
parent, parent,

View File

@@ -4,7 +4,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 { IColumnDefinition, IObservableCollection, IGridDataRow } from 'angular2-slickgrid'; import { ISlickColumn, IObservableCollection, IGridDataRow } from 'angular2-slickgrid';
export interface ISlickRange { export interface ISlickRange {
fromCell: number; fromCell: number;
@@ -42,7 +42,7 @@ export interface IGridIcon {
export interface IGridDataSet { export interface IGridDataSet {
dataRows: IObservableCollection<IGridDataRow>; dataRows: IObservableCollection<IGridDataRow>;
columnDefinitions: IColumnDefinition[]; columnDefinitions: ISlickColumn<any>[];
resized: any; // EventEmitter<any>; resized: any; // EventEmitter<any>;
totalRows: number; totalRows: number;
batchId: number; batchId: number;

View File

@@ -17,14 +17,13 @@
showDataTypeIcon="false" showDataTypeIcon="false"
showHeader="true" showHeader="true"
[resized]="dataSet.resized" [resized]="dataSet.resized"
[plugins]="slickgridPlugins" [plugins]="plugins[i]"
(activeCellChanged)="onActiveCellChanged($event)" (activeCellChanged)="onActiveCellChanged($event)"
(cellEditBegin)="onCellEditBegin($event)" (cellEditBegin)="onCellEditBegin($event)"
(cellEditExit)="onCellEditEnd($event)" (cellEditExit)="onCellEditEnd($event)"
(rowEditBegin)="onRowEditBegin($event)" (rowEditBegin)="onRowEditBegin($event)"
(rowEditExit)="onRowEditEnd($event)" (rowEditExit)="onRowEditEnd($event)"
(contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)" (contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
[isColumnEditable]="onIsColumnEditable"
[isCellEditValid]="onIsCellEditValid" [isCellEditValid]="onIsCellEditValid"
[overrideCellFn]="overrideCellFn" [overrideCellFn]="overrideCellFn"
enableEditing="true" enableEditing="true"

View File

@@ -13,7 +13,7 @@ import 'vs/css!sql/parts/grid/media/slickGrid';
import 'vs/css!./media/editData'; import 'vs/css!./media/editData';
import { ElementRef, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject, forwardRef, EventEmitter } from '@angular/core'; import { ElementRef, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject, forwardRef, EventEmitter } from '@angular/core';
import { IGridDataRow, VirtualizedCollection } from 'angular2-slickgrid'; import { IGridDataRow, VirtualizedCollection, ISlickRange } from 'angular2-slickgrid';
import { IGridDataSet } from 'sql/parts/grid/common/interfaces'; import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
import * as Services from 'sql/parts/grid/services/sharedServices'; import * as Services from 'sql/parts/grid/services/sharedServices';
@@ -24,6 +24,9 @@ import { error } from 'sql/base/common/log';
import { clone, mixin } 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';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
import { escape } from 'sql/base/common/strings'; import { escape } from 'sql/base/common/strings';
import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -36,7 +39,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { KeyCode } from 'vs/base/common/keyCodes'; import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export const EDITDATA_SELECTOR: string = 'editdata-component'; export const EDITDATA_SELECTOR: string = 'editdata-component';
@Component({ @Component({
@@ -66,6 +68,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
private newRowVisible: boolean; private newRowVisible: boolean;
private removingNewRow: boolean; private removingNewRow: boolean;
private rowIdMappings: { [gridRowId: number]: number } = {}; private rowIdMappings: { [gridRowId: number]: number } = {};
protected plugins = new Array<Array<Slick.Plugin<any>>>();
// Edit Data functions // Edit Data functions
public onActiveCellChanged: (event: { row: number, column: number }) => void; public onActiveCellChanged: (event: { row: number, column: number }) => void;
@@ -78,6 +81,12 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
public overrideCellFn: (rowNumber, columnId, value?, data?) => string; public overrideCellFn: (rowNumber, columnId, value?, data?) => string;
public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>; public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>;
private savedViewState: {
gridSelections: ISlickRange[];
scrollTop;
scrollLeft;
};
constructor( constructor(
@Inject(forwardRef(() => ElementRef)) el: ElementRef, @Inject(forwardRef(() => ElementRef)) el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef, @Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef,
@@ -95,6 +104,8 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this._el.nativeElement.className = 'slickgridContainer'; this._el.nativeElement.className = 'slickgridContainer';
this.dataService = params.dataService; this.dataService = params.dataService;
this.actionProvider = this.instantiationService.createInstance(EditDataGridActionProvider, this.dataService, this.onGridSelectAll(), this.onDeleteRow(), this.onRevertRow()); this.actionProvider = this.instantiationService.createInstance(EditDataGridActionProvider, this.dataService, this.onGridSelectAll(), this.onDeleteRow(), this.onRevertRow());
params.onRestoreViewState(() => this.restoreViewState());
params.onSaveViewState(() => this.saveViewState());
} }
/** /**
@@ -167,17 +178,6 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.onRowEditEnd = (event: { row: number }): void => { }; this.onRowEditEnd = (event: { row: number }): void => { };
this.onIsColumnEditable = (column: number): boolean => {
let result = false;
// Check that our variables exist
if (column !== undefined && !!this.dataSet && !!this.dataSet.columnDefinitions[column]) {
result = this.dataSet.columnDefinitions[column].isEditable;
}
// If no column definition exists then the row is not editable
return result;
};
this.overrideCellFn = (rowNumber, columnId, value?, data?): string => { this.overrideCellFn = (rowNumber, columnId, value?, data?): string => {
let returnVal = ''; let returnVal = '';
if (Services.DBCellValue.isDBCellValue(value)) { if (Services.DBCellValue.isDBCellValue(value)) {
@@ -197,9 +197,9 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
self.idMapping[rowIndex] = row.id; self.idMapping[rowIndex] = row.id;
rowIndex++; rowIndex++;
return { return {
values: row.cells.map(c => { values: [{}].concat(row.cells.map(c => {
return mixin({ ariaLabel: c.displayValue }, c); return mixin({ ariaLabel: escape(c.displayValue) }, c);
}), row: row.id })), row: row.id
}; };
}); });
@@ -351,6 +351,8 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
let maxHeight = this.getMaxHeight(resultSet.rowCount); let maxHeight = this.getMaxHeight(resultSet.rowCount);
let minHeight = this.getMinHeight(resultSet.rowCount); let minHeight = this.getMinHeight(resultSet.rowCount);
let rowNumberColumn = new RowNumberColumn({ numberOfRows: resultSet.rowCount });
// Store the result set from the event // Store the result set from the event
let dataSet: IGridDataSet = { let dataSet: IGridDataSet = {
resized: undefined, resized: undefined,
@@ -365,7 +367,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.loadDataFunction, this.loadDataFunction,
index => { return { values: [] }; } index => { return { values: [] }; }
), ),
columnDefinitions: resultSet.columnInfo.map((c, i) => { columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson; let isLinked = c.isXml || c.isJson;
let linkType = c.isXml ? 'xml' : 'json'; let linkType = c.isXml ? 'xml' : 'json';
@@ -374,13 +376,14 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan' name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
? 'XML Showplan' ? 'XML Showplan'
: escape(c.columnName), : escape(c.columnName),
type: self.stringToFieldType('string'), field: i.toString(),
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter, formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined, asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined,
isEditable: c.isUpdatable isEditable: c.isUpdatable
}; };
}) }))
}; };
self.plugins.push([rowNumberColumn, new AutoColumnSize(), new AdditionalKeyBindings()]);
self.dataSet = dataSet; self.dataSet = dataSet;
// Create a dataSet to render without rows to reduce DOM size // Create a dataSet to render without rows to reduce DOM size
@@ -583,4 +586,25 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
? (this._defaultNumShowingRows + 1) * this._rowHeight + 10 ? (this._defaultNumShowingRows + 1) * this._rowHeight + 10
: this.getMaxHeight(rowCount); : this.getMaxHeight(rowCount);
} }
private saveViewState(): void {
let gridSelections = this.slickgrids.toArray()[0].getSelectedRanges();
let viewport = ((this.slickgrids.toArray()[0] as any)._grid.getCanvasNode() as HTMLElement).parentElement;
this.savedViewState = {
gridSelections,
scrollTop: viewport.scrollTop,
scrollLeft: viewport.scrollLeft
};
}
private restoreViewState(): void {
if (this.savedViewState) {
this.slickgrids.toArray()[0].selection = this.savedViewState.gridSelections;
let viewport = ((this.slickgrids.toArray()[0] as any)._grid.getCanvasNode() as HTMLElement).parentElement;
viewport.scrollLeft = this.savedViewState.scrollLeft;
viewport.scrollTop = this.savedViewState.scrollTop;
this.savedViewState = undefined;
}
}
} }

View File

@@ -26,6 +26,7 @@ import * as GridContentEvents from 'sql/parts/grid/common/gridContentEvents';
import { ResultsVisibleContext, ResultsGridFocussedContext, ResultsMessagesFocussedContext, QueryEditorVisibleContext } from 'sql/parts/query/common/queryContext'; import { ResultsVisibleContext, ResultsGridFocussedContext, ResultsMessagesFocussedContext, QueryEditorVisibleContext } from 'sql/parts/query/common/queryContext';
import { error } from 'sql/base/common/log'; import { error } from 'sql/base/common/log';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService'; import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
import { IAction } from 'vs/base/common/actions'; import { IAction } from 'vs/base/common/actions';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
@@ -33,8 +34,6 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { DragCellSelectionModel } from 'sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
@@ -42,14 +41,8 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export abstract class GridParentComponent { export abstract class GridParentComponent {
// CONSTANTS // CONSTANTS
// tslint:disable:no-unused-variable // tslint:disable:no-unused-variable
protected get selectionModel(): DragCellSelectionModel<any> {
return new DragCellSelectionModel<any>(); protected get selectionModel() { return new CellSelectionModel(); }
}
protected get slickgridPlugins(): Array<any> {
return [
new AutoColumnSize<any>({})
];
}
protected _rowHeight = 29; protected _rowHeight = 29;
protected _defaultNumShowingRows = 8; protected _defaultNumShowingRows = 8;
protected Constants = Constants; protected Constants = Constants;
@@ -94,7 +87,6 @@ export abstract class GridParentComponent {
public onRowEditBegin: (event: { row: number }) => void; public onRowEditBegin: (event: { row: number }) => void;
public onRowEditEnd: (event: { row: number }) => void; public onRowEditEnd: (event: { row: number }) => void;
public onIsCellEditValid: (row: number, column: number, newValue: any) => boolean; public onIsCellEditValid: (row: number, column: number, newValue: any) => boolean;
public onIsColumnEditable: (column: number) => boolean;
public overrideCellFn: (rowNumber, columnId, value?, data?) => string; public overrideCellFn: (rowNumber, columnId, value?, data?) => string;
public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>; public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>;
@@ -244,21 +236,25 @@ export abstract class GridParentComponent {
this.messagesFocussedContextKey.set(false); this.messagesFocussedContextKey.set(false);
} }
protected getSelection(index?: number): ISlickRange[] {
let selection = this.slickgrids.toArray()[index || this.activeGrid].getSelectedRanges();
selection = selection.map(c => { return <ISlickRange>{ fromCell: c.fromCell - 1, toCell: c.toCell - 1, toRow: c.toRow, fromRow: c.fromRow }; });
return selection;
}
private copySelection(): void { private copySelection(): void {
let messageText = this.getMessageText(); let messageText = this.getMessageText();
if (messageText.length > 0) { if (messageText.length > 0) {
this.clipboardService.writeText(messageText); this.clipboardService.writeText(messageText);
} else { } else {
let activeGrid = this.activeGrid; let activeGrid = this.activeGrid;
let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges(); this.dataService.copyResults(this.getSelection(activeGrid), this.renderedDataSets[activeGrid].batchId, this.renderedDataSets[activeGrid].resultId);
this.dataService.copyResults(selection, this.renderedDataSets[activeGrid].batchId, this.renderedDataSets[activeGrid].resultId);
} }
} }
private copyWithHeaders(): void { private copyWithHeaders(): void {
let activeGrid = this.activeGrid; let activeGrid = this.activeGrid;
let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges(); this.dataService.copyResults(this.getSelection(activeGrid), this.renderedDataSets[activeGrid].batchId,
this.dataService.copyResults(selection, this.renderedDataSets[activeGrid].batchId,
this.renderedDataSets[activeGrid].resultId, true); this.renderedDataSets[activeGrid].resultId, true);
} }
@@ -372,8 +368,7 @@ export abstract class GridParentComponent {
let activeGrid = this.activeGrid; let activeGrid = this.activeGrid;
let batchId = this.renderedDataSets[activeGrid].batchId; let batchId = this.renderedDataSets[activeGrid].batchId;
let resultId = this.renderedDataSets[activeGrid].resultId; let resultId = this.renderedDataSets[activeGrid].resultId;
let selection = this.slickgrids.toArray()[activeGrid].getSelectedRanges(); this.dataService.sendSaveRequest({ batchIndex: batchId, resultSetNumber: resultId, format: format, selection: this.getSelection(activeGrid) });
this.dataService.sendSaveRequest({ batchIndex: batchId, resultSetNumber: resultId, format: format, selection: selection });
} }
protected _keybindingFor(action: IAction): ResolvedKeybinding { protected _keybindingFor(action: IAction): ResolvedKeybinding {
@@ -385,7 +380,7 @@ export abstract class GridParentComponent {
let slick: any = this.slickgrids.toArray()[index]; let slick: any = this.slickgrids.toArray()[index];
let grid = slick._grid; let grid = slick._grid;
let selection = this.slickgrids.toArray()[index].getSelectedRanges(); let selection = this.getSelection(index);
if (selection && selection.length === 0) { if (selection && selection.length === 0) {
let cell = (grid as Slick.Grid<any>).getCellFromEvent(event); let cell = (grid as Slick.Grid<any>).getCellFromEvent(event);
@@ -423,7 +418,9 @@ export abstract class GridParentComponent {
let self = this; let self = this;
return (gridIndex: number) => { return (gridIndex: number) => {
self.activeGrid = gridIndex; self.activeGrid = gridIndex;
self.slickgrids.toArray()[this.activeGrid].selection = true; let grid = self.slickgrids.toArray()[self.activeGrid];
grid.setActive();
grid.selection = true;
}; };
} }
@@ -433,22 +430,6 @@ export abstract class GridParentComponent {
} }
} }
/**
* Used to convert the string to a enum compatible with SlickGrid
*/
protected stringToFieldType(input: string): FieldType {
let fieldtype: FieldType;
switch (input) {
case 'string':
fieldtype = FieldType.String;
break;
default:
fieldtype = FieldType.String;
break;
}
return fieldtype;
}
/** /**
* Makes a resultset take up the full result height if this is not already true * Makes a resultset take up the full result height if this is not already true
* Otherwise rerenders the result sets from default * Otherwise rerenders the result sets from default

View File

@@ -10,9 +10,9 @@
<span> {{LocalizedConstants.resultPaneLabel}} </span> <span> {{LocalizedConstants.resultPaneLabel}} </span>
<span class="queryResultsShortCut"> {{resultShortcut}} </span> <span class="queryResultsShortCut"> {{resultShortcut}} </span>
</div> </div>
<div id="results" *ngIf="renderedDataSets.length > 0" class="results vertBox scrollable" <div #resultsScrollBox id="results" *ngIf="renderedDataSets.length > 0" class="results vertBox scrollable"
(onScroll)="onScroll($event)" [scrollEnabled]="scrollEnabled" [class.hidden]="!resultActive" (onScroll)="onScroll($event)" [scrollEnabled]="scrollEnabled" [class.hidden]="!resultActive"
(focusin)="onGridFocus()" (focusout)="onGridFocusout()"> (focusin)="onGridFocus()" (focusout)="onGridFocusout()">
<div class="boxRow content horzBox slickgrid" *ngFor="let dataSet of renderedDataSets; let i = index" <div class="boxRow content horzBox slickgrid" *ngFor="let dataSet of renderedDataSets; let i = index"
[style.max-height]="dataSet.maxHeight" [style.min-height]="dataSet.minHeight"> [style.max-height]="dataSet.maxHeight" [style.min-height]="dataSet.minHeight">
<slick-grid #slickgrid id="slickgrid_{{i}}" [columnDefinitions]="dataSet.columnDefinitions" <slick-grid #slickgrid id="slickgrid_{{i}}" [columnDefinitions]="dataSet.columnDefinitions"
@@ -20,12 +20,11 @@
[dataRows]="dataSet.dataRows" [dataRows]="dataSet.dataRows"
(contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)" (contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
enableAsyncPostRender="true" enableAsyncPostRender="true"
showDataTypeIcon="false"
showHeader="true" showHeader="true"
[resized]="dataSet.resized" [resized]="dataSet.resized"
(mousedown)="navigateToGrid(i)" (mousedown)="navigateToGrid(i)"
[selectionModel]="selectionModel" [selectionModel]="selectionModel"
[plugins]="slickgridPlugins" [plugins]="plugins[i]"
class="boxCol content vertBox slickgrid" class="boxCol content vertBox slickgrid"
[rowHeight]="rowHeight"> [rowHeight]="rowHeight">
</slick-grid> </slick-grid>

View File

@@ -15,7 +15,7 @@ import {
ElementRef, QueryList, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject, ElementRef, QueryList, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject,
ViewChildren, forwardRef, EventEmitter, Input, ViewChild ViewChildren, forwardRef, EventEmitter, Input, ViewChild
} from '@angular/core'; } from '@angular/core';
import { IGridDataRow, SlickGrid, VirtualizedCollection } from 'angular2-slickgrid'; import { IGridDataRow, SlickGrid, VirtualizedCollection, ISlickRange } from 'angular2-slickgrid';
import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants'; import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants';
import * as Services from 'sql/parts/grid/services/sharedServices'; import * as Services from 'sql/parts/grid/services/sharedServices';
@@ -28,6 +28,9 @@ import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { clone, mixin } 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 { escape } from 'sql/base/common/strings'; import { escape } from 'sql/base/common/strings';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
import { format } from 'vs/base/common/strings'; import { format } from 'vs/base/common/strings';
import * as DOM from 'vs/base/browser/dom'; import * as DOM from 'vs/base/browser/dom';
@@ -61,7 +64,9 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
// create a function alias to use inside query.component // create a function alias to use inside query.component
// tslint:disable-next-line:no-unused-variable // tslint:disable-next-line:no-unused-variable
private stringsFormat: any = format; protected stringsFormat: any = format;
protected plugins = new Array<Array<Slick.Plugin<any>>>();
// tslint:disable-next-line:no-unused-variable // tslint:disable-next-line:no-unused-variable
private dataIcons: IGridIcon[] = [ private dataIcons: IGridIcon[] = [
@@ -86,7 +91,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
icon: () => { return 'saveCsv'; }, icon: () => { return 'saveCsv'; },
hoverText: () => { return LocalizedConstants.saveCSVLabel; }, hoverText: () => { return LocalizedConstants.saveCSVLabel; },
functionality: (batchId, resultId, index) => { functionality: (batchId, resultId, index) => {
let selection = this.slickgrids.toArray()[index].getSelectedRanges(); let selection = this.getSelection(index);
if (selection.length <= 1) { if (selection.length <= 1) {
this.handleContextClick({ type: 'savecsv', batchId: batchId, resultId: resultId, index: index, selection: selection }); this.handleContextClick({ type: 'savecsv', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else { } else {
@@ -99,7 +104,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
icon: () => { return 'saveJson'; }, icon: () => { return 'saveJson'; },
hoverText: () => { return LocalizedConstants.saveJSONLabel; }, hoverText: () => { return LocalizedConstants.saveJSONLabel; },
functionality: (batchId, resultId, index) => { functionality: (batchId, resultId, index) => {
let selection = this.slickgrids.toArray()[index].getSelectedRanges(); let selection = this.getSelection(index);
if (selection.length <= 1) { if (selection.length <= 1) {
this.handleContextClick({ type: 'savejson', batchId: batchId, resultId: resultId, index: index, selection: selection }); this.handleContextClick({ type: 'savejson', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else { } else {
@@ -112,7 +117,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
icon: () => { return 'saveExcel'; }, icon: () => { return 'saveExcel'; },
hoverText: () => { return LocalizedConstants.saveExcelLabel; }, hoverText: () => { return LocalizedConstants.saveExcelLabel; },
functionality: (batchId, resultId, index) => { functionality: (batchId, resultId, index) => {
let selection = this.slickgrids.toArray()[index].getSelectedRanges(); let selection = this.getSelection(index);
if (selection.length <= 1) { if (selection.length <= 1) {
this.handleContextClick({ type: 'saveexcel', batchId: batchId, resultId: resultId, index: index, selection: selection }); this.handleContextClick({ type: 'saveexcel', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else { } else {
@@ -156,6 +161,13 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
public showChartRequested: EventEmitter<IGridDataSet> = new EventEmitter<IGridDataSet>(); public showChartRequested: EventEmitter<IGridDataSet> = new EventEmitter<IGridDataSet>();
public goToNextQueryOutputTabRequested: EventEmitter<void> = new EventEmitter<void>(); public goToNextQueryOutputTabRequested: EventEmitter<void> = new EventEmitter<void>();
private savedViewState: {
gridSelections: ISlickRange[][];
resultsScroll: number;
messagePaneScroll: number;
slickGridScrolls: { vertical: number; horizontal: number }[];
};
@Input() public queryParameters: IQueryComponentParams; @Input() public queryParameters: IQueryComponentParams;
@ViewChildren('slickgrid') slickgrids: QueryList<SlickGrid>; @ViewChildren('slickgrid') slickgrids: QueryList<SlickGrid>;
@@ -163,6 +175,8 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
@ViewChild('resultsPane', { read: ElementRef }) private _resultsPane: ElementRef; @ViewChild('resultsPane', { read: ElementRef }) private _resultsPane: ElementRef;
@ViewChild('queryLink', { read: ElementRef }) private _queryLinkElement: ElementRef; @ViewChild('queryLink', { read: ElementRef }) private _queryLinkElement: ElementRef;
@ViewChild('messagesContainer', { read: ElementRef }) private _messagesContainer: ElementRef; @ViewChild('messagesContainer', { read: ElementRef }) private _messagesContainer: ElementRef;
@ViewChild('resultsScrollBox', { read: ElementRef }) private _resultsScrollBox: ElementRef;
@ViewChildren('slickgrid', { read: ElementRef }) private _slickgridElements: QueryList<ElementRef>;
constructor( constructor(
@Inject(forwardRef(() => ElementRef)) el: ElementRef, @Inject(forwardRef(() => ElementRef)) el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef, @Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef,
@@ -220,6 +234,10 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
} }
self._cd.detectChanges(); self._cd.detectChanges();
}); });
this.queryParameters.onSaveViewState(() => this.saveViewState());
this.queryParameters.onRestoreViewState(() => this.restoreViewState());
this.dataService.onAngularLoaded(); this.dataService.onAngularLoaded();
} }
@@ -302,9 +320,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].map(c => { values: [{}].concat(rows.rows[row].map(c => {
return mixin({ ariaLabel: escape(c.displayValue) }, c); return mixin({ ariaLabel: escape(c.displayValue) }, c);
}) }))
}); });
} }
@@ -331,6 +349,8 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
minHeight = minHeightNumber.toString() + 'px'; minHeight = minHeightNumber.toString() + 'px';
} }
let rowNumberColumn = new RowNumberColumn({ numberOfRows: resultSet.rowCount });
// Store the result set from the event // Store the result set from the event
let dataSet: IGridDataSet = { let dataSet: IGridDataSet = {
resized: undefined, resized: undefined,
@@ -345,7 +365,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
loadDataFunction, loadDataFunction,
index => { return { values: [] }; } index => { return { values: [] }; }
), ),
columnDefinitions: resultSet.columnInfo.map((c, i) => { columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson; let isLinked = c.isXml || c.isJson;
let linkType = c.isXml ? 'xml' : 'json'; let linkType = c.isXml ? 'xml' : 'json';
@@ -354,12 +374,13 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan' name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
? 'XML Showplan' ? 'XML Showplan'
: escape(c.columnName), : escape(c.columnName),
type: self.stringToFieldType('string'), field: i.toString(),
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter, formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined
}; };
}) }))
}; };
self.plugins.push([rowNumberColumn, new AutoColumnSize(), new AdditionalKeyBindings()]);
self.dataSets.push(dataSet); self.dataSets.push(dataSet);
// check if the resultset is for a query plan // check if the resultset is for a query plan
@@ -643,6 +664,43 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
} }
} }
private saveViewState(): void {
let gridSelections = this.slickgrids.map(grid => grid.getSelectedRanges());
let resultsScrollElement = (this._resultsScrollBox.nativeElement as HTMLElement);
let resultsScroll = resultsScrollElement.scrollTop;
let messagePaneScroll = (this._messagesContainer.nativeElement as HTMLElement).scrollTop;
let slickGridScrolls = this._slickgridElements.map(element => {
// Get the slick grid's viewport element and save its scroll position
let scrollElement = (element.nativeElement as HTMLElement).children[0].children[3];
return {
vertical: scrollElement.scrollTop,
horizontal: scrollElement.scrollLeft
};
});
this.savedViewState = {
gridSelections,
messagePaneScroll,
resultsScroll,
slickGridScrolls
};
}
private restoreViewState(): void {
if (this.savedViewState) {
this.slickgrids.forEach((grid, index) => grid.selection = this.savedViewState.gridSelections[index]);
(this._resultsScrollBox.nativeElement as HTMLElement).scrollTop = this.savedViewState.resultsScroll;
(this._messagesContainer.nativeElement as HTMLElement).scrollTop = this.savedViewState.messagePaneScroll;
this._slickgridElements.forEach((element, index) => {
let scrollElement = (element.nativeElement as HTMLElement).children[0].children[3];
let savedScroll = this.savedViewState.slickGridScrolls[index];
scrollElement.scrollTop = savedScroll.vertical;
scrollElement.scrollLeft = savedScroll.horizontal;
});
this.savedViewState = undefined;
}
}
layout() { layout() {
this.resizeGrids(); this.resizeGrids();
} }

View File

@@ -295,8 +295,9 @@ export class InsightsDialogView extends Modal {
for (let action of this._insight.actions.types) { for (let action of this._insight.actions.types) {
let task = tasks.includes(action); let task = tasks.includes(action);
let commandAction = MenuRegistry.getCommand(action); let commandAction = MenuRegistry.getCommand(action);
if (task) { let commandLabel = types.isString(commandAction.title) ? commandAction.title : commandAction.title.value;
let button = this.addFooterButton(types.isString(commandAction.title) ? commandAction.title : commandAction.title.value, () => { if (task && !this.findFooterButton(commandLabel)) {
let button = this.addFooterButton(commandLabel, () => {
let element = this._topTable.getSelectedRows(); let element = this._topTable.getSelectedRows();
let resource: ListResource; let resource: ListResource;
if (element && element.length > 0) { if (element && element.length > 0) {

View File

@@ -421,3 +421,7 @@ jobsview-component .jobview-grid .slick-cell.error-row {
.jobproxiesview-grid > .monaco-table .slick-header-columns .slick-resizable-handle { .jobproxiesview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
border-left: 1px dotted #444444; border-left: 1px dotted #444444;
} }
.overview-container > .overview-tab > label {
margin-bottom: 0px;
}

View File

@@ -154,7 +154,7 @@
</td> </td>
</tr> </tr>
</table> </table>
<div #jobsteps> <div #jobsteps style="height: 100%">
<jobstepsview-component *ngIf="showSteps === true"></jobstepsview-component> <jobstepsview-component *ngIf="showSteps === true"></jobstepsview-component>
</div> </div>
<h3 *ngIf="showSteps === false">No Steps Available</h3> <h3 *ngIf="showSteps === false">No Steps Available</h3>

View File

@@ -120,7 +120,11 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
} else { } else {
tree.setFocus(element, payload); tree.setFocus(element, payload);
tree.setSelection([element], payload); tree.setSelection([element], payload);
self.setStepsTree(element); if (element.rowID) {
self.setStepsTree(element);
} else {
event.preventDefault();
}
} }
return true; return true;
}; };
@@ -174,10 +178,12 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
if (self.agentJobHistoryInfo) { if (self.agentJobHistoryInfo) {
self.agentJobHistoryInfo.runDate = self.formatTime(self.agentJobHistoryInfo.runDate); self.agentJobHistoryInfo.runDate = self.formatTime(self.agentJobHistoryInfo.runDate);
if (self.agentJobHistoryInfo.steps) { if (self.agentJobHistoryInfo.steps) {
let jobStepStatus = this.didJobFail(self.agentJobHistoryInfo);
self._stepRows = self.agentJobHistoryInfo.steps.map(step => { self._stepRows = self.agentJobHistoryInfo.steps.map(step => {
let stepViewRow = new JobStepsViewRow(); let stepViewRow = new JobStepsViewRow();
stepViewRow.message = step.message; stepViewRow.message = step.message;
stepViewRow.runStatus = JobManagementUtilities.convertToStatusString(step.runStatus); stepViewRow.runStatus = jobStepStatus ? JobManagementUtilities.convertToStatusString(0) :
JobManagementUtilities.convertToStatusString(step.runStatus);
self._runStatus = JobManagementUtilities.convertToStatusString(self.agentJobHistoryInfo.runStatus); self._runStatus = JobManagementUtilities.convertToStatusString(self.agentJobHistoryInfo.runStatus);
stepViewRow.stepName = step.stepName; stepViewRow.stepName = step.stepName;
stepViewRow.stepID = step.stepId.toString(); stepViewRow.stepID = step.stepId.toString();
@@ -191,6 +197,15 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
} }
} }
private didJobFail(job: sqlops.AgentJobHistoryInfo): boolean {
for (let i = 0; i < job.steps.length; i++) {
if (job.steps[i].runStatus === 0) {
return true;
}
}
return false;
}
private buildHistoryTree(self: any, jobHistories: sqlops.AgentJobHistoryInfo[]) { private buildHistoryTree(self: any, jobHistories: sqlops.AgentJobHistoryInfo[]) {
self._treeController.jobHistories = jobHistories; self._treeController.jobHistories = jobHistories;
self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, jobHistories); self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, jobHistories);

View File

@@ -104,7 +104,6 @@ export interface IListTemplate {
} }
export class JobHistoryRenderer implements tree.IRenderer { export class JobHistoryRenderer implements tree.IRenderer {
private _statusIcon: HTMLElement;
public getHeight(tree: tree.ITree, element: JobHistoryRow): number { public getHeight(tree: tree.ITree, element: JobHistoryRow): number {
return 30; return 30;
@@ -121,11 +120,10 @@ export class JobHistoryRenderer implements tree.IRenderer {
public renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement): IListTemplate { public renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement): IListTemplate {
let row = DOM.$('.list-row'); let row = DOM.$('.list-row');
let label = DOM.$('.label'); let label = DOM.$('.label');
this._statusIcon = this.createStatusIcon(); let statusIcon = this.createStatusIcon();
row.appendChild(this._statusIcon); row.appendChild(statusIcon);
row.appendChild(label); row.appendChild(label);
container.appendChild(row); container.appendChild(row);
let statusIcon = this._statusIcon;
return { statusIcon, label }; return { statusIcon, label };
} }
@@ -133,13 +131,13 @@ export class JobHistoryRenderer implements tree.IRenderer {
templateData.label.innerHTML = element.runDate + '&nbsp;&nbsp;' + element.runStatus; templateData.label.innerHTML = element.runDate + '&nbsp;&nbsp;' + element.runStatus;
let statusClass: string; let statusClass: string;
if (element.runStatus === 'Succeeded') { if (element.runStatus === 'Succeeded') {
statusClass = ' job-passed'; statusClass = 'status-icon job-passed';
} else if (element.runStatus === 'Failed') { } else if (element.runStatus === 'Failed') {
statusClass = ' job-failed'; statusClass = 'status-icon job-failed';
} else { } else {
statusClass = ' job-unknown'; statusClass = 'status-icon job-unknown';
} }
this._statusIcon.className += statusClass; templateData.statusIcon.className = statusClass;
} }
public disposeTemplate(tree: tree.ITree, templateId: string, templateData: IListTemplate): void { public disposeTemplate(tree: tree.ITree, templateId: string, templateData: IListTemplate): void {
@@ -148,7 +146,6 @@ export class JobHistoryRenderer implements tree.IRenderer {
private createStatusIcon(): HTMLElement { private createStatusIcon(): HTMLElement {
let statusIcon: HTMLElement = DOM.$('div'); let statusIcon: HTMLElement = DOM.$('div');
statusIcon.className += ' status-icon';
return statusIcon; return statusIcon;
} }
} }

View File

@@ -88,15 +88,6 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit,
} }
public layout() { public layout() {
let jobsViewToolbar = $('jobhistory-component .actionbar-container').get(0);
let statusBar = $('.part.statusbar').get(0);
if (jobsViewToolbar && statusBar) {
let toolbarBottom = jobsViewToolbar.getBoundingClientRect().bottom;
let statusTop = statusBar.getBoundingClientRect().top;
this._table.layout(new dom.Dimension(
dom.getContentWidth(this._tableContainer.nativeElement),
statusTop - toolbarBottom));
}
} }
} }

View File

@@ -35,6 +35,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IAction } from 'vs/base/common/actions'; import { IAction } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService'; import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
import { escape } from 'sql/base/common/strings';
export const JOBSVIEW_SELECTOR: string = 'jobsview-component'; export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
export const ROW_HEIGHT: number = 45; export const ROW_HEIGHT: number = 45;
@@ -485,7 +486,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
return '<table class="jobview-jobnametable"><tr class="jobview-jobnamerow">' + return '<table class="jobview-jobnametable"><tr class="jobview-jobnamerow">' +
'<td nowrap class=' + resultIndicatorClass + '></td>' + '<td nowrap class=' + resultIndicatorClass + '></td>' +
'<td nowrap class="jobview-jobnametext">' + dataContext.name + '</td>' + '<td nowrap class="jobview-jobnametext">' + escape(dataContext.name) + '</td>' +
'</tr></table>'; '</tr></table>';
} }

View File

@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import 'vs/css!./button'; import 'vs/css!./button';
import { import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver, Component, Input, Inject, ChangeDetectorRef, forwardRef,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit ViewChild, ElementRef, OnDestroy, AfterViewInit
} from '@angular/core'; } from '@angular/core';
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';
@@ -24,7 +24,13 @@ import { Color } from 'vs/base/common/color';
@Component({ @Component({
selector: 'modelview-button', selector: 'modelview-button',
template: ` template: `
<div #input style="width: 100%"></div> <div>
<label for={{this.label}}>
<div #input style="width: 100%">
<input #fileInput *ngIf="this.isFile === true" id={{this.label}} type="file" accept=".sql" style="display: none">
</div>
</label>
</div>
` `
}) })
export default class ButtonComponent extends ComponentWithIconBase implements IComponent, OnDestroy, AfterViewInit { export default class ButtonComponent extends ComponentWithIconBase implements IComponent, OnDestroy, AfterViewInit {
@@ -33,17 +39,16 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
private _button: Button; private _button: Button;
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef; @ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
@ViewChild('fileInput', { read: ElementRef }) private _fileInputContainer: ElementRef;
constructor( constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef, @Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
) { ) {
super(changeRef); super(changeRef);
} }
ngOnInit(): void { ngOnInit(): void {
this.baseInit(); this.baseInit();
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
@@ -55,10 +60,27 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND, buttonForeground: SIDE_BAR_TITLE_FOREGROUND 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({ if (this._fileInputContainer) {
eventType: ComponentEventType.onDidClick, const self = this;
args: e this._fileInputContainer.nativeElement.onchange = () => {
}); let file = self._fileInputContainer.nativeElement.files[0];
let reader = new FileReader();
reader.onload = (e) => {
let text = (<FileReader>e.target).result;
self.fileContent = text;
self._onEventEmitter.fire({
eventType: ComponentEventType.onDidClick,
args: self.fileContent
});
};
reader.readAsText(file);
};
} else {
this._onEventEmitter.fire({
eventType: ComponentEventType.onDidClick,
args: e
});
}
})); }));
} }
} }
@@ -119,9 +141,31 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
this.setPropertyFromUI<sqlops.ButtonProperties, string>(this.setValueProperties, newValue); this.setPropertyFromUI<sqlops.ButtonProperties, string>(this.setValueProperties, newValue);
} }
private get isFile(): boolean {
return this.getPropertyOrDefault<sqlops.ButtonProperties, boolean>((props) => props.isFile, false);
}
private set isFile(newValue: boolean) {
this.setPropertyFromUI<sqlops.ButtonProperties, boolean>(this.setFileProperties, newValue);
}
private get fileContent(): string {
return this.getPropertyOrDefault<sqlops.ButtonProperties, string>((props) => props.fileContent, '');
}
private set fileContent(newValue: string) {
this.setPropertyFromUI<sqlops.ButtonProperties, string>(this.setFileContentProperties, newValue);
}
private setFileContentProperties(properties: sqlops.ButtonProperties, fileContent: string) : void {
properties.fileContent = fileContent;
}
private setValueProperties(properties: sqlops.ButtonProperties, label: string): void { private setValueProperties(properties: sqlops.ButtonProperties, label: string): void {
properties.label = label; properties.label = label;
} }
private setFileProperties(properties: sqlops.ButtonProperties, isFile: boolean): void {
properties.isFile = isFile;
}
} }

View File

@@ -71,6 +71,12 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
abstract setLayout(layout: any): void; abstract setLayout(layout: any): void;
public setDataProvider(handle: number, componentId: string, context: any): void {
}
public refreshDataProvider(item: any): void {
}
public setProperties(properties: { [key: string]: any; }): void { public setProperties(properties: { [key: string]: any; }): void {
if (!properties) { if (!properties) {
this.properties = {}; this.properties = {};

View File

@@ -14,12 +14,14 @@ import DeclarativeTableComponent from './declarativeTable.component';
import ListBoxComponent from './listbox.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 TreeComponent from './tree/tree.component';
import RadioButtonComponent from './radioButton.component'; import RadioButtonComponent from './radioButton.component';
import WebViewComponent from './webview.component'; import WebViewComponent from './webview.component';
import TableComponent from './table.component'; import TableComponent from './table.component';
import TextComponent from './text.component'; import TextComponent from './text.component';
import LoadingComponent from './loadingComponent.component'; import LoadingComponent from './loadingComponent.component';
import FileBrowserTreeComponent from './fileBrowserTree.component'; import FileBrowserTreeComponent from './fileBrowserTree.component';
import EditorComponent from './editor.component';
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry'; import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes'; import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
@@ -72,5 +74,11 @@ registerComponentType(TABLE_COMPONENT, ModelComponentTypes.Table, TableComponent
export const LOADING_COMPONENT = 'loading-component'; export const LOADING_COMPONENT = 'loading-component';
registerComponentType(LOADING_COMPONENT, ModelComponentTypes.LoadingComponent, LoadingComponent); registerComponentType(LOADING_COMPONENT, ModelComponentTypes.LoadingComponent, LoadingComponent);
export const TREE_COMPONENT = 'tree-component';
registerComponentType(TREE_COMPONENT, ModelComponentTypes.TreeComponent, TreeComponent);
export const FILEBROWSERTREE_COMPONENT = 'filebrowsertree-component'; export const FILEBROWSERTREE_COMPONENT = 'filebrowsertree-component';
registerComponentType(FILEBROWSERTREE_COMPONENT, ModelComponentTypes.FileBrowserTree, FileBrowserTreeComponent); registerComponentType(FILEBROWSERTREE_COMPONENT, ModelComponentTypes.FileBrowserTree, FileBrowserTreeComponent);
export const EDITOR_COMPONENT = 'editor-component';
registerComponentType(EDITOR_COMPONENT, ModelComponentTypes.Editor, EditorComponent);

View File

@@ -17,6 +17,7 @@ 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 { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component'; import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component'; import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editableDropdown.component';
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox'; import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component'; import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
@@ -24,29 +25,38 @@ import * as nls from 'vs/nls';
export enum DeclarativeDataType { export enum DeclarativeDataType {
string = 'string', string = 'string',
category = 'category', category = 'category',
boolean = 'boolean' boolean = 'boolean',
editableCategory = 'editableCategory'
} }
@Component({ @Component({
selector: 'modelview-declarativeTable', selector: 'modelview-declarativeTable',
template: ` template: `
<table role=grid aria-labelledby="ID_REF" #container *ngIf="columns" class="declarative-table"> <table role=grid aria-labelledby="ID_REF" #container *ngIf="columns" class="declarative-table">
<thead>
<tr style="display:block;">
<ng-container *ngFor="let column of columns;let h = index"> <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> <td class="declarative-table-header" tabindex="-1" [style.width]="getColumnWidth(h)" role="button" aria-sort="none">{{column.displayName}}</td>
</ng-container> </ng-container>
</tr>
</thead>
<ng-container *ngIf="data"> <ng-container *ngIf="data">
<tbody style="display: block; width:100%; overflow-y: scroll" [style.height]="getHeight()">
<ng-container *ngFor="let row of data;let r = index"> <ng-container *ngFor="let row of data;let r = index">
<tr class="declarative-table-row"> <tr class="declarative-table-row">
<ng-container *ngFor="let cellData of row;let c = index"> <ng-container *ngFor="let cellData of row;let c = index">
<td class="declarative-table-cell" tabindex="-1" role="button" [style.width]="getColumnWidth(c)"> <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> <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> <select-box *ngIf="isSelectBox(c)" [options]="GetOptions(c)" (onDidSelect)="onSelectBoxChanged($event,r,c)" [selectedOption]="GetSelectedOptionDisplayName(r,c)"></select-box>
<editable-select-box *ngIf="isEditableSelectBox(c)" [options]="GetOptions(c)" (onDidSelect)="onSelectBoxChanged($event,r,c)" [selectedOption]="GetSelectedOptionDisplayName(r,c)"></editable-select-box>
<input-box *ngIf="isInputBox(c)" [value]="cellData" (onDidChange)="onInputBoxChanged($event,r,c)"></input-box> <input-box *ngIf="isInputBox(c)" [value]="cellData" (onDidChange)="onInputBoxChanged($event,r,c)"></input-box>
<ng-container *ngIf="isLabel(c)" >{{cellData}}</ng-container> <ng-container *ngIf="isLabel(c)" >{{cellData}}</ng-container>
</td> </td>
</ng-container> </ng-container>
</tr> </tr>
</ng-container> </ng-container>
</tbody>
</ng-container> </ng-container>
</table> </table>
` `
@@ -109,10 +119,20 @@ export default class DeclarativeTableComponent extends ComponentBase implements
this.onCellDataChanged(e, row, cell); this.onCellDataChanged(e, row, cell);
} }
private onSelectBoxChanged(e: ISelectData, row: number, cell: number): void { private onSelectBoxChanged(e: ISelectData | string, row: number, cell: number): void {
let column: sqlops.DeclarativeTableColumn = this.columns[cell]; let column: sqlops.DeclarativeTableColumn = this.columns[cell];
if (column.categoryValues) { if (column.categoryValues) {
this.onCellDataChanged(column.categoryValues[e.index].name, row, cell); if (typeof e === 'string') {
let category = column.categoryValues.find(c => c.displayName === e);
if (category) {
this.onCellDataChanged(category.name, row, cell);
} else {
this.onCellDataChanged(e, row, cell);
}
} else {
this.onCellDataChanged(column.categoryValues[e.index].name, row, cell);
}
} }
} }
@@ -135,6 +155,11 @@ export default class DeclarativeTableComponent extends ComponentBase implements
return column.valueType === DeclarativeDataType.category; return column.valueType === DeclarativeDataType.category;
} }
private isEditableSelectBox(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.valueType === DeclarativeDataType.editableCategory;
}
private isInputBox(cell: number): boolean { private isInputBox(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell]; let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.valueType === DeclarativeDataType.string && !column.isReadOnly; return column.valueType === DeclarativeDataType.string && !column.isReadOnly;
@@ -142,7 +167,7 @@ export default class DeclarativeTableComponent extends ComponentBase implements
private getColumnWidth(cell: number): string { private getColumnWidth(cell: number): string {
let column: sqlops.DeclarativeTableColumn = this.columns[cell]; let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return this.convertSize(column.width); return this.convertSize(column.width, '30px');
} }
private GetOptions(cell: number): string[] { private GetOptions(cell: number): string[] {
@@ -155,7 +180,13 @@ export default class DeclarativeTableComponent extends ComponentBase implements
let cellData = this.data[row][cell]; let cellData = this.data[row][cell];
if (cellData && column.categoryValues) { if (cellData && column.categoryValues) {
let category = column.categoryValues.find(v => v.name === cellData); let category = column.categoryValues.find(v => v.name === cellData);
return category.displayName; if (category) {
return category.displayName;
} else if (this.isEditableSelectBox(cell)) {
return cellData;
} else {
return undefined;
}
} else { } else {
return ''; return '';
} }

View File

@@ -2,7 +2,6 @@
.declarative-table { .declarative-table {
padding: 5px 30px 0px 30px; padding: 5px 30px 0px 30px;
box-sizing: border-box; box-sizing: border-box;
width:100%;
border-collapse: collapse; border-collapse: collapse;
} }
@@ -13,6 +12,7 @@
padding: 5px; padding: 5px;
border: 1px solid gray; border: 1px solid gray;
background-color: #F5F5F5; background-color: #F5F5F5;
vertical-align: top;
} }
.vs-dark .declarative-table-header { .vs-dark .declarative-table-header {

View File

@@ -25,7 +25,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
template: ` template: `
<div [style.width]="getWidth()"> <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>
` `
@@ -79,7 +79,6 @@ export default class DropDownComponent extends ComponentBase implements ICompone
this._selectBox = new SelectBox(this.getValues(), this.getSelectedValue(), 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) {

View File

@@ -0,0 +1,135 @@
/*---------------------------------------------------------------------------------------------
* 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!./editor';
import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList
} from '@angular/core';
import * as sqlops from 'sqlops';
import * as DOM from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITextModel } from 'vs/editor/common/model';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import URI from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces';
import { QueryTextEditor } from 'sql/parts/modelComponents/queryTextEditor';
@Component({
template: '',
selector: 'modelview-editor-component'
})
export default class EditorComponent extends ComponentBase implements IComponent, OnDestroy {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
private _editor: QueryTextEditor;
private _editorInput: UntitledEditorInput;
private _editorModel: ITextModel;
private _renderedContent: string;
private _langaugeMode: string;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(IInstantiationService) private _instantiationService: IInstantiationService,
@Inject(IModelService) private _modelService: IModelService,
@Inject(IModeService) private _modeService: IModeService
) {
super(changeRef);
}
ngOnInit(): void {
this.baseInit();
this._createEditor();
this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, e => {
this.layout();
}));
}
private _createEditor(): void {
this._editor = this._instantiationService.createInstance(QueryTextEditor);
this._editor.create(this._el.nativeElement);
this._editor.setVisible(true);
this._editorInput = this._instantiationService.createInstance(UntitledEditorInput, URI.from({ scheme: Schemas.untitled, path: `${this.descriptor.type}-${this.descriptor.id}` }), false, 'sql', '', '');
this._editor.setInput(this._editorInput, undefined);
this._editorInput.resolve().then(model => this._editorModel = model.textEditorModel);
this._register(this._editor);
this._register(this._editorInput);
this._register(this._editorModel.onDidChangeContent(e => {
this.content = this._editorModel.getValue();
}));
}
ngOnDestroy(): void {
this.baseDestroy();
}
/// IComponent implementation
public layout(): void {
let width: number = this.convertSizeToNumber(this.width);
let height: number = this.convertSizeToNumber(this.height);
this._editor.layout(new DOM.Dimension(
width && width > 0 ? width : DOM.getContentWidth(this._el.nativeElement),
height && height > 0 ? height : DOM.getContentHeight(this._el.nativeElement)));
}
/// Editor Functions
private updateModel() {
if (this._editorModel) {
this._renderedContent = this.content;
this._modelService.updateModel(this._editorModel, this._renderedContent);
}
}
private updateLanguageMode() {
if (this._editorModel && this._editor) {
this._langaugeMode = this.languageMode;
this._modeService.getOrCreateMode(this._langaugeMode).then((modeValue) => {
this._modelService.setMode(this._editorModel, modeValue);
});
}
}
/// IComponent implementation
public setLayout(layout: any): void {
// TODO allow configuring the look and feel
this.layout();
}
public setProperties(properties: { [key: string]: any; }): void {
super.setProperties(properties);
if (this.content !== this._renderedContent) {
this.updateModel();
}
if (this.languageMode !== this._langaugeMode) {
this.updateLanguageMode();
}
}
// CSS-bound properties
public get content(): string {
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.content, undefined);
}
public set content(newValue: string) {
this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, content) => { properties.content = content; }, newValue);
}
public get languageMode(): string {
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.languageMode, undefined);
}
public set languageMode(newValue: string) {
this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, languageMode) => { properties.languageMode = languageMode; }, newValue);
}
}

View 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.
*--------------------------------------------------------------------------------------------*/
modelview-editor-component {
height: 100%;
width : 100%;
display: block;
}

View File

@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver, Component, Input, Inject, ChangeDetectorRef, forwardRef,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit ViewChild, ElementRef, OnDestroy, AfterViewInit
} from '@angular/core'; } from '@angular/core';
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';

View File

@@ -26,6 +26,8 @@ export interface IComponent {
enabled: boolean; enabled: boolean;
readonly valid?: boolean; readonly valid?: boolean;
validate(): Thenable<boolean>; validate(): Thenable<boolean>;
setDataProvider(handle: number, componentId: string, context: any): void;
refreshDataProvider(item: any): void;
} }
export const COMPONENT_CONFIG = new InjectionToken<IComponentConfig>('component_config'); export const COMPONENT_CONFIG = new InjectionToken<IComponentConfig>('component_config');

View File

@@ -0,0 +1,113 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import * as nls from 'vs/nls';
import * as DOM from 'vs/base/browser/dom';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { TPromise } from 'vs/base/common/winjs.base';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IModeService } from 'vs/editor/common/services/modeService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { EditorOptions } from 'vs/workbench/common/editor';
import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { IEditorContributionCtor } from 'vs/editor/browser/editorExtensions';
import { FoldingController } from 'vs/editor/contrib/folding/folding';
class QueryCodeEditor extends CodeEditor {
protected _getContributions(): IEditorContributionCtor[] {
let contributions = super._getContributions();
let skipContributions = [FoldingController.prototype];
contributions = contributions.filter(c => skipContributions.indexOf(c.prototype) === -1);
return contributions;
}
}
/**
* Extension of TextResourceEditor that is always readonly rather than only with non UntitledInputs
*/
export class QueryTextEditor extends BaseTextEditor {
public static ID = 'modelview.editors.textEditor';
private _dimension: DOM.Dimension;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IModeService modeService: IModeService,
@ITextFileService textFileService: ITextFileService,
@IEditorGroupService editorGroupService: IEditorGroupService
) {
super(QueryTextEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
}
public createEditorControl(parent: HTMLElement, configuration: IEditorOptions): editorCommon.IEditor {
return this.instantiationService.createInstance(QueryCodeEditor, parent, configuration);
}
protected getConfigurationOverrides(): IEditorOptions {
const options = super.getConfigurationOverrides();
if (this.input) {
options.inDiffEditor = true;
options.scrollBeyondLastLine = false;
options.folding = false;
options.renderWhitespace = 'none';
options.wordWrap = 'on';
options.renderIndentGuides = false;
options.rulers = [];
options.glyphMargin = true;
options.minimap = {
enabled: false
};
}
return options;
}
setInput(input: UntitledEditorInput, options: EditorOptions): TPromise<void> {
return super.setInput(input, options)
.then(() => this.input.resolve()
.then(editorModel => editorModel.load())
.then(editorModel => this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel)));
}
protected getAriaLabel(): string {
return nls.localize('queryTextEditorAriaLabel', 'modelview code editor for view model.');
}
public layout(dimension?: DOM.Dimension){
if (dimension) {
this._dimension = dimension;
}
this.getControl().layout(dimension);
}
public setWidth(width: number) {
if (this._dimension) {
this._dimension.width = width;
this.layout();
}
}
public setHeight(height: number) {
if (this._dimension) {
this._dimension.height = height;
this.layout();
}
}
}

View File

@@ -16,14 +16,13 @@ import { Table } from 'sql/base/browser/ui/table/table';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView'; import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { attachTableStyler } from 'sql/common/theme/styler'; import { attachTableStyler } from 'sql/common/theme/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 { getContentHeight, getContentWidth, Dimension } from 'vs/base/browser/dom'; import { getContentHeight, getContentWidth, Dimension } from 'vs/base/browser/dom';
import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin'; import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin';
@Component({ @Component({
selector: 'modelview-table', selector: 'modelview-table',
template: ` template: `
<div #table style="width: 100%;height:100%"></div> <div #table style="width: 100%;height:100%" [style.font-size]="fontSize"></div>
` `
}) })
export default class TableComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit { export default class TableComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@@ -99,7 +98,7 @@ export default class TableComponent extends ComponentBase implements IComponent,
syncColumnCellResize: true, syncColumnCellResize: true,
enableColumnReorder: false, enableColumnReorder: false,
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);
@@ -179,6 +178,10 @@ export default class TableComponent extends ComponentBase implements IComponent,
return this.getPropertyOrDefault<sqlops.TableComponentProperties, string[]>((props) => props.columns, []); return this.getPropertyOrDefault<sqlops.TableComponentProperties, string[]>((props) => props.columns, []);
} }
public get fontSize(): number | string {
return this.getPropertyOrDefault<sqlops.TableComponentProperties, number | string>((props) => props.fontSize, '');
}
public set columns(newValue: string[]) { public set columns(newValue: string[]) {
this.setPropertyFromUI<sqlops.TableComponentProperties, string[]>((props, value) => props.columns = value, newValue); this.setPropertyFromUI<sqlops.TableComponentProperties, string[]>((props, value) => props.columns = value, newValue);
} }

View File

@@ -0,0 +1,151 @@
/*---------------------------------------------------------------------------------------------
* 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!sql/parts/modelComponents/tree/treeComponent';
import 'vs/css!sql/media/icons/common-icons';
import {
Component, Input, Inject, ChangeDetectorRef, forwardRef,
ViewChild, ElementRef, OnDestroy, 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 { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { TreeComponentRenderer } from 'sql/parts/modelComponents/tree/treeComponentRenderer';
import { TreeComponentDataSource } from 'sql/parts/modelComponents/tree/treeDataSource';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { DefaultFilter, DefaultAccessibilityProvider, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITreeComponentItem, IModelViewTreeViewDataProvider } from 'sql/workbench/common/views';
import { TreeViewDataProvider } from './treeViewDataProvider';
class Root implements ITreeComponentItem {
label = 'root';
handle = '0';
parentHandle = null;
collapsibleState = 0;
children = void 0;
options = undefined;
}
@Component({
selector: 'modelview-tree',
template: `
<div #input style="width: 100%;height:100%"></div>
`
})
export default class TreeComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
private _tree: Tree;
private _treeRenderer: TreeComponentRenderer;
private _dataProvider: TreeViewDataProvider;
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
constructor(
@Inject(forwardRef(() => CommonServiceInterface)) private _commonService: CommonServiceInterface,
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
@Inject(IContextViewService) private contextViewService: IContextViewService,
@Inject(IInstantiationService) private _instantiationService: IInstantiationService) {
super(changeRef);
}
ngOnInit(): void {
this.baseInit();
}
ngAfterViewInit(): void {
if (this._inputContainer) {
this.createTreeControl();
}
}
ngOnDestroy(): void {
this.baseDestroy();
}
public setDataProvider(handle: number, componentId: string, context: any): any {
this._dataProvider = new TreeViewDataProvider(handle, componentId, context);
this.createTreeControl();
}
public refreshDataProvider(itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeComponentItem }): void {
if (this._dataProvider) {
this._dataProvider.refresh(itemsToRefreshByHandle);
}
if (this._tree) {
for (const item of Object.values(itemsToRefreshByHandle)) {
this._tree.refresh(<ITreeComponentItem>item);
}
}
}
private createTreeControl(): void {
if (!this._tree && this._dataProvider) {
const dataSource = this._instantiationService.createInstance(TreeComponentDataSource, this._dataProvider);
const renderer = this._instantiationService.createInstance(TreeComponentRenderer, this._dataProvider, this.themeService, { withCheckbox: this.withCheckbox });
this._treeRenderer = renderer;
const controller = new DefaultController();
const filter = new DefaultFilter();
const sorter = undefined;
const dnd = undefined;
const accessibilityProvider = new DefaultAccessibilityProvider();
this._tree = new Tree(this._inputContainer.nativeElement,
{ dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider },
{
indentPixels: 10,
twistiePixels: 20,
ariaLabel: 'Tree Node'
});
this._tree.setInput(new Root());
this._tree.domFocus();
this._register(this._tree);
this._register(attachListStyler(this._tree, this.themeService));
this._register(this._tree.onDidChangeSelection( e => {
this._dataProvider.onNodeSelected(e.selection);
}));
this._tree.refresh();
this.layout();
}
}
/// IComponent implementation
public layout(): void {
this._changeRef.detectChanges();
this.createTreeControl();
if (this._tree) {
this._tree.layout(this.convertSizeToNumber(this.width), this.convertSizeToNumber(this.height));
this._tree.refresh();
}
}
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._treeRenderer.options.withCheckbox = this.withCheckbox;
}
public get withCheckbox(): boolean {
return this.getPropertyOrDefault<sqlops.TreeProperties, boolean>((props) => props.withCheckbox, false);
}
public set withCheckbox(newValue: boolean) {
this.setPropertyFromUI<sqlops.TreeProperties, boolean>((properties, value) => { properties.withCheckbox = value; }, newValue);
}
}

View File

@@ -0,0 +1,8 @@
.tree-component-node-tile {
display: flex;
}
.tree-component-node-tile .model-view-tree-node-item-icon{
width: 15px;
height: 15px;
}

View File

@@ -0,0 +1,173 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!sql/media/icons/common-icons';
import * as dom from 'vs/base/browser/dom';
import { localize } from 'vs/nls';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITree, IRenderer } from 'vs/base/parts/tree/browser/tree';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { LIGHT } from 'vs/platform/theme/common/themeService';
import { Disposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { ITreeComponentItem } from 'sql/workbench/common/views';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { TreeViewDataProvider } from './treeViewDataProvider';
export enum TreeCheckboxState {
Intermediate = 0,
Checked = 1,
Unchecked = 2
}
export class TreeDataTemplate extends Disposable {
root: HTMLElement;
label: HTMLSpanElement;
icon: HTMLElement;
private _checkbox: HTMLInputElement;
model: ITreeComponentItem;
private _onChange = new Emitter<boolean>();
public readonly onChange: Event<boolean> = this._onChange.event;
public set checkbox(input: HTMLInputElement) {
this._checkbox = input;
this.handleOnChange(this._checkbox, () => {
this._onChange.fire(this._checkbox.checked);
if (this.model && this.model.onCheckedChanged) {
this.model.onCheckedChanged(this._checkbox.checked);
}
});
}
public get checkboxState(): TreeCheckboxState {
if (this._checkbox.indeterminate) {
return TreeCheckboxState.Intermediate;
} else {
return this.checkbox.checked ? TreeCheckboxState.Checked : TreeCheckboxState.Unchecked;
}
}
public set checkboxState(value: TreeCheckboxState) {
if (this.checkboxState !== value) {
switch (value) {
case TreeCheckboxState.Checked:
this._checkbox.indeterminate = false;
this._checkbox.checked = true;
break;
case TreeCheckboxState.Unchecked:
this._checkbox.indeterminate = false;
this._checkbox.checked = false;
break;
case TreeCheckboxState.Intermediate:
this._checkbox.indeterminate = true;
break;
default:
break;
}
}
}
public get checkbox(): HTMLInputElement {
return this._checkbox;
}
protected handleOnChange(domNode: HTMLElement, listener: (e: Event<void>) => void): void {
this._register(dom.addDisposableListener(domNode, dom.EventType.CHANGE, listener));
}
}
/**
* Renders the tree items.
* Uses the dom template to render connection groups and connections.
*/
export class TreeComponentRenderer extends Disposable implements IRenderer {
public static DEFAULT_TEMPLATE = 'DEFAULT_TEMPLATE';
public static DEFAULT_HEIGHT = 20;
constructor(
private _dataProvider: TreeViewDataProvider,
private themeService: IWorkbenchThemeService,
public options?: { withCheckbox: boolean }
) {
super();
}
/**
* Returns the element's height in the tree, in pixels.
*/
public getHeight(tree: ITree, element: any): number {
return TreeComponentRenderer.DEFAULT_HEIGHT;
}
/**
* Returns a template ID for a given element.
*/
public getTemplateId(tree: ITree, element: any): string {
return TreeComponentRenderer.DEFAULT_TEMPLATE;
}
/**
* Render template in a dom element based on template id
*/
public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
if (templateId === TreeComponentRenderer.DEFAULT_TEMPLATE) {
const nodeTemplate: TreeDataTemplate = new TreeDataTemplate();
nodeTemplate.root = dom.append(container, dom.$('.tree-component-node-tile'));
nodeTemplate.icon = dom.append(nodeTemplate.root, dom.$('div.model-view-tree-node-item-icon'));
if (this.options && this.options.withCheckbox) {
let checkboxWrapper = dom.append(nodeTemplate.root, dom.$('div.checkboxWrapper'));
nodeTemplate.checkbox = dom.append(checkboxWrapper, dom.$<HTMLInputElement>('input.checkbox', { type: 'checkbox' }));
}
nodeTemplate.label = dom.append(nodeTemplate.root, dom.$('div.model-view-tree-node-item-label'));
return nodeTemplate;
}
}
/**
* Render a element, given an object bag returned by the template
*/
public renderElement(tree: ITree, element: ITreeComponentItem, templateId: string, templateData: TreeDataTemplate): void {
const icon = this.themeService.getTheme().type === LIGHT ? element.icon : element.iconDark;
templateData.icon.style.backgroundImage = icon ? `url('${icon}')` : '';
dom.toggleClass(templateData.icon, 'model-view-tree-node-item-icon', !!icon);
if (element) {
element.onCheckedChanged = (checked: boolean) => {
this._dataProvider.onNodeCheckedChanged(element.handle, checked);
}
templateData.model = element;
}
if (templateId === TreeComponentRenderer.DEFAULT_TEMPLATE) {
this.renderNode(element, templateData);
}
}
private renderNode(treeNode: ITreeComponentItem, templateData: TreeDataTemplate): void {
let label = treeNode.label;
templateData.label.textContent = label;
templateData.root.title = label;
templateData.checkboxState = this.getCheckboxState(treeNode);
}
private getCheckboxState(treeNode: ITreeComponentItem): TreeCheckboxState {
if (treeNode.checked === undefined) {
return TreeCheckboxState.Intermediate;
} else {
return treeNode.checked ? TreeCheckboxState.Checked : TreeCheckboxState.Unchecked;
}
}
public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
this.dispose();
// no op
}
}

View File

@@ -0,0 +1,57 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree';
import { TPromise } from 'vs/base/common/winjs.base';
import { IModelViewTreeViewDataProvider, ITreeComponentItem } from 'sql/workbench/common/views';
/**
* Implements the DataSource(that returns a parent/children of an element) for the recent connection tree
*/
export class TreeComponentDataSource implements IDataSource {
/**
*
*/
constructor(
private _dataProvider: IModelViewTreeViewDataProvider) {
}
/**
* Returns the unique identifier of the given element.
* No more than one element may use a given identifier.
*/
public getId(tree: ITree, node: ITreeComponentItem): string {
return node.handle;
}
/**
* Returns a boolean value indicating whether the element has children.
*/
public hasChildren(tree: ITree, node: ITreeComponentItem): boolean {
return this._dataProvider !== undefined;
}
/**
* Returns the element's children as an array in a promise.
*/
public getChildren(tree: ITree, node: ITreeComponentItem): TPromise<any> {
if (this._dataProvider) {
if (node && node.handle === '0') {
return this._dataProvider.getChildren(undefined);
} else {
return this._dataProvider.getChildren(node);
}
}
return TPromise.as([]);
}
public getParent(tree: ITree, node: any): TPromise<any> {
return TPromise.as(null);
}
}

View File

@@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ExtHostModelViewTreeViewsShape, SqlExtHostContext } from 'sql/workbench/api/node/sqlExtHost.protocol';
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
import { IModelViewTreeViewDataProvider, ITreeComponentItem } from 'sql/workbench/common/views';
import { INotificationService } from 'vs/platform/notification/common/notification';
import * as vsTreeView from 'vs/workbench/api/electron-browser/mainThreadTreeViews';
export class TreeViewDataProvider extends vsTreeView.TreeViewDataProvider implements IModelViewTreeViewDataProvider {
constructor(handle: number, treeViewId: string,
context: IExtHostContext,
notificationService?: INotificationService
) {
super(`${handle}-${treeViewId}`, context.getProxy(SqlExtHostContext.ExtHostModelViewTreeViews), notificationService);
}
onNodeCheckedChanged(treeItemHandle?: string, checked?: boolean) {
(<ExtHostModelViewTreeViewsShape>this._proxy).$onNodeCheckedChanged(this.treeViewId, treeItemHandle, checked);
}
onNodeSelected(items: ITreeComponentItem[]) {
if (items) {
(<ExtHostModelViewTreeViewsShape>this._proxy).$onNodeSelected(this.treeViewId, items.map(i => i.handle));
}
}
}

View File

@@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import nls = require('vs/nls'); import nls = require('vs/nls');
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';
import { IModelStore, IComponentDescriptor, IComponent, IComponentEventArgs } from './interfaces'; import { IModelStore, IComponentDescriptor, IComponent } from './interfaces';
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes'; import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IModelView, IModelViewEventArgs } from 'sql/services/model/modelViewService'; import { IModelView, IModelViewEventArgs } from 'sql/services/model/modelViewService';
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry'; import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
@@ -97,6 +97,10 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
this.queueAction(componentId, (component) => component.setProperties(properties)); this.queueAction(componentId, (component) => component.setProperties(properties));
} }
refreshDataProvider(componentId: string, item: any): void {
this.queueAction(componentId, (component) => component.refreshDataProvider(item));
}
private queueAction<T>(componentId: string, action: (component: IComponent) => T): void { private queueAction<T>(componentId: string, action: (component: IComponent) => T): void {
this.modelStore.eventuallyRunOnComponent(componentId, action).catch(err => { this.modelStore.eventuallyRunOnComponent(componentId, action).catch(err => {
// TODO add error handling // TODO add error handling
@@ -122,4 +126,8 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
public validate(componentId: string): Thenable<boolean> { public validate(componentId: string): Thenable<boolean> {
return new Promise(resolve => this.modelStore.eventuallyRunOnComponent(componentId, component => resolve(component.validate()))); return new Promise(resolve => this.modelStore.eventuallyRunOnComponent(componentId, component => resolve(component.validate())));
} }
public setDataProvider(handle: number, componentId: string, context: any): any {
return this.queueAction(componentId, (component) => component.setDataProvider(handle, componentId, context));
}
} }

View File

@@ -79,7 +79,7 @@ export class ServerGroupDialog extends Modal {
// Connection Group Name // Connection Group Name
let serverGroupNameLabel = localize('connectionGroupName', 'Server group name'); let serverGroupNameLabel = localize('connectionGroupName', 'Server group name');
this._bodyBuilder.div({ class: 'dialog-label' }, (labelContainer) => { this._bodyBuilder.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(serverGroupNameLabel); labelContainer.text(serverGroupNameLabel);
}); });
this._bodyBuilder.div({ class: 'input-divider' }, (inputCellContainer) => { this._bodyBuilder.div({ class: 'input-divider' }, (inputCellContainer) => {
let errorMessage = localize('MissingGroupNameError', 'Group name is required.'); let errorMessage = localize('MissingGroupNameError', 'Group name is required.');
@@ -94,7 +94,7 @@ export class ServerGroupDialog extends Modal {
// Connection Group Description // Connection Group Description
let groupDescriptionLabel = localize('groupDescription', 'Group description'); let groupDescriptionLabel = localize('groupDescription', 'Group description');
this._bodyBuilder.div({ class: 'dialog-label' }, (labelContainer) => { this._bodyBuilder.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(groupDescriptionLabel); labelContainer.text(groupDescriptionLabel);
}); });
this._bodyBuilder.div({ class: 'input-divider' }, (inputCellContainer) => { this._bodyBuilder.div({ class: 'input-divider' }, (inputCellContainer) => {
this._groupDescriptionInputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, { this._groupDescriptionInputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, {
@@ -105,7 +105,7 @@ export class ServerGroupDialog extends Modal {
// Connection Group Color // Connection Group Color
this._bodyBuilder.div({ class: 'dialog-label' }, (labelContainer) => { this._bodyBuilder.div({ class: 'dialog-label' }, (labelContainer) => {
let groupColorLabel = localize('groupColor', 'Group color'); let groupColorLabel = localize('groupColor', 'Group color');
labelContainer.innerHtml(groupColorLabel); labelContainer.text(groupColorLabel);
}); });
this._bodyBuilder.div({ class: 'group-color-options' }, (groupColorContainer) => { this._bodyBuilder.div({ class: 'group-color-options' }, (groupColorContainer) => {

View File

@@ -185,7 +185,7 @@ export class OEScriptSelectAction extends ScriptSelectAction {
} }
this._treeSelectionHandler.onTreeActionStateChange(true); this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(this._objectExplorerTreeNode); var connectionProfile = TreeUpdateUtils.getConnectionProfile(this._objectExplorerTreeNode);
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile); var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile); ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
var metadata = this._objectExplorerTreeNode.metadata; var metadata = this._objectExplorerTreeNode.metadata;
@@ -255,7 +255,7 @@ export class OEScriptCreateAction extends ScriptCreateAction {
this._treeSelectionHandler.onTreeActionStateChange(true); this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode); var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata; var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile); var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile); ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
return super.run({ profile: connectionProfile, object: metadata }).then((result) => { return super.run({ profile: connectionProfile, object: metadata }).then((result) => {
@@ -291,7 +291,7 @@ export class OEScriptExecuteAction extends ScriptExecuteAction {
this._treeSelectionHandler.onTreeActionStateChange(true); this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode); var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata; var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile); var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile); ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
return super.run({ profile: connectionProfile, object: metadata }).then((result) => { return super.run({ profile: connectionProfile, object: metadata }).then((result) => {
@@ -327,7 +327,7 @@ export class OEScriptAlterAction extends ScriptAlterAction {
this._treeSelectionHandler.onTreeActionStateChange(true); this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode); var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata; var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile); var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile); ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
return super.run({ profile: connectionProfile, object: metadata }).then((result) => { return super.run({ profile: connectionProfile, object: metadata }).then((result) => {
@@ -363,7 +363,7 @@ export class OEScriptDeleteAction extends ScriptDeleteAction {
this._treeSelectionHandler.onTreeActionStateChange(true); this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode); var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata; var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile); var ownerUri = this._connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile); ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
return super.run({ profile: connectionProfile, object: metadata }).then((result) => { return super.run({ profile: connectionProfile, object: metadata }).then((result) => {

View File

@@ -15,11 +15,13 @@ export class TreeNodeContextKey implements IContextKey<TreeNode> {
static SubType = new RawContextKey<string>('nodeSubType', undefined); static SubType = new RawContextKey<string>('nodeSubType', undefined);
static Status = new RawContextKey<string>('nodeStatus', undefined); static Status = new RawContextKey<string>('nodeStatus', undefined);
static TreeNode = new RawContextKey<TreeNode>('treeNode', undefined); static TreeNode = new RawContextKey<TreeNode>('treeNode', undefined);
static NodeLabel = new RawContextKey<string>('nodeLabel', undefined);
private _nodeTypeKey: IContextKey<string>; private _nodeTypeKey: IContextKey<string>;
private _subTypeKey: IContextKey<string>; private _subTypeKey: IContextKey<string>;
private _statusKey: IContextKey<string>; private _statusKey: IContextKey<string>;
private _treeNodeKey: IContextKey<TreeNode>; private _treeNodeKey: IContextKey<TreeNode>;
private _nodeLabelKey: IContextKey<string>;
constructor( constructor(
@IContextKeyService contextKeyService: IContextKeyService @IContextKeyService contextKeyService: IContextKeyService
@@ -28,6 +30,7 @@ export class TreeNodeContextKey implements IContextKey<TreeNode> {
this._subTypeKey = TreeNodeContextKey.SubType.bindTo(contextKeyService); this._subTypeKey = TreeNodeContextKey.SubType.bindTo(contextKeyService);
this._statusKey = TreeNodeContextKey.Status.bindTo(contextKeyService); this._statusKey = TreeNodeContextKey.Status.bindTo(contextKeyService);
this._treeNodeKey = TreeNodeContextKey.TreeNode.bindTo(contextKeyService); this._treeNodeKey = TreeNodeContextKey.TreeNode.bindTo(contextKeyService);
this._nodeLabelKey = TreeNodeContextKey.NodeLabel.bindTo(contextKeyService);
} }
set(value: TreeNode) { set(value: TreeNode) {
@@ -35,6 +38,7 @@ export class TreeNodeContextKey implements IContextKey<TreeNode> {
this._nodeTypeKey.set(value && value.nodeTypeId); this._nodeTypeKey.set(value && value.nodeTypeId);
this._subTypeKey.set(value && value.nodeSubType); this._subTypeKey.set(value && value.nodeSubType);
this._statusKey.set(value && value.nodeStatus); this._statusKey.set(value && value.nodeStatus);
this._nodeLabelKey.set(value && value.label);
} }
reset(): void { reset(): void {
@@ -42,6 +46,7 @@ export class TreeNodeContextKey implements IContextKey<TreeNode> {
this._subTypeKey.reset(); this._subTypeKey.reset();
this._statusKey.reset(); this._statusKey.reset();
this._treeNodeKey.reset(); this._treeNodeKey.reset();
this._nodeLabelKey.reset();
} }
public get(): TreeNode { public get(): TreeNode {

Some files were not shown because too many files have changed in this diff Show More