mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-22 02:51:36 -05:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca755365ce | ||
|
|
ea979de19f | ||
|
|
473ddfcdf1 | ||
|
|
a627285a4c | ||
|
|
322847469d | ||
|
|
6c5fac997f | ||
|
|
1871fd383e | ||
|
|
f5b147ca4b | ||
|
|
9d2b206156 | ||
|
|
a372c76e07 | ||
|
|
b1ce07d3ae | ||
|
|
bc09fb30d8 | ||
|
|
c13f219318 | ||
|
|
6b018c5d06 | ||
|
|
e69158d9b2 | ||
|
|
520cfb780a | ||
|
|
e686fed209 | ||
|
|
80ab19ac23 | ||
|
|
25228fa58e | ||
|
|
676d35090f | ||
|
|
e1a36a356c | ||
|
|
3274c0b734 | ||
|
|
9c95e1289f |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -38,5 +38,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"git.ignoreLimitWarning": true
|
||||
}
|
||||
|
||||
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,5 +1,18 @@
|
||||
# Change Log
|
||||
|
||||
## Version 0.30.6
|
||||
* Release date: June 20, 2018
|
||||
* Release status: Public Preview
|
||||
|
||||
## What's new in this version
|
||||
* **SQL Server Profiler for SQL Operations Studio *Preview*** extension initial release
|
||||
* The new **SQL Data Warehouse** extension includes rich customizable dashboard widgets surfacing insights to your data warehouse. This unlocks key scenarios around managing and tuning your data warehouse to ensure it is optimized for consistent performance.
|
||||
* **Edit Data "Filtering and Sorting"** support
|
||||
* **SQL Server Agent for SQL Operations Studio *Preview*** extension enhancements for Jobs and Job History views
|
||||
* Improved **Wizard & Dialog UI Builder Framework** extensibility APIs
|
||||
* Update VS Code Platform source code integrating [March 2018 (1.22)](https://code.visualstudio.com/updates/v1_22) and [April 2018 (1.23)](https://code.visualstudio.com/updates/v1_23) releases
|
||||
* Fix GitHub Issues
|
||||
|
||||
## Version 0.29.3
|
||||
* Release date: May 7, 2018
|
||||
* Release status: Public Preview
|
||||
|
||||
15
README.md
15
README.md
@@ -4,16 +4,16 @@
|
||||
|
||||
SQL Operations Studio is a data management tool that enables you to work with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
|
||||
|
||||
**Download SQL Operations Studio May Public Preview**
|
||||
**Download SQL Operations Studio June Public Preview**
|
||||
|
||||
Platform | Link
|
||||
-- | --
|
||||
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=873386
|
||||
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=873387
|
||||
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=873388
|
||||
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=873389
|
||||
Linux RPM | https://go.microsoft.com/fwlink/?linkid=873390
|
||||
Linux DEB | https://go.microsoft.com/fwlink/?linkid=873391
|
||||
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=875602
|
||||
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=875603
|
||||
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=875604
|
||||
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=875605
|
||||
Linux RPM | https://go.microsoft.com/fwlink/?linkid=875606
|
||||
Linux DEB | https://go.microsoft.com/fwlink/?linkid=875607
|
||||
|
||||
Go to our [download page](https://aka.ms/sqlopsstudio) for more specific instructions.
|
||||
|
||||
@@ -61,6 +61,7 @@ The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.micro
|
||||
## Contributions and "thank you"
|
||||
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
|
||||
|
||||
* lanceklinger `Fix for double clicking column handle in results table #1504`
|
||||
* westerncj for `Removed duplicate contribution from README.md (#753)`
|
||||
* ntovas for `Fix for duplicate extensions shown in "Save File" dialog. (#779)`
|
||||
* SebastianPfliegel for `Add cursor snippet (#475)`
|
||||
|
||||
@@ -658,7 +658,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.8.2",
|
||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.9",
|
||||
"opener": "^1.4.3",
|
||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
|
||||
"vscode-extension-telemetry": "^0.0.15"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||
"version": "1.4.0-alpha.45",
|
||||
"version": "1.4.0-alpha.46",
|
||||
"downloadFileNames": {
|
||||
"Windows_86": "win-x86-netcoreapp2.1.zip",
|
||||
"Windows_64": "win-x64-netcoreapp2.1.zip",
|
||||
|
||||
@@ -49,9 +49,9 @@ core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
|
||||
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.1.7":
|
||||
version "0.1.7"
|
||||
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/d50285b03d0d5073c086362c5c96afb279320607"
|
||||
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.1.9":
|
||||
version "0.1.9"
|
||||
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/a1a79895cb79658b75d78aa5cfd745855019148d"
|
||||
dependencies:
|
||||
vscode-languageclient "3.5.0"
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ Common SQL Profiler use-cases taken from https://docs.microsoft.com/en-us/sql/to
|
||||
- Monitoring the performance of SQL Server to tune workloads.
|
||||
- Correlating performance counters to diagnose problems.
|
||||
|
||||
## SQL Server Profiler 0.1.1 Release
|
||||
The SQL Server Profiler for SQL Operations Studio *Preview* extension is now available. This is the initial preview release for a new lightweight XEvent-based profiler. The SQL Server Profiler extension tries to make it simple to quickly trace server activity for troubleshooting and monitoring.
|
||||
|
||||
We'll continue to enhance this extension over the next couple releases. Take a look at the below screenshot to see what's currently available.
|
||||
|
||||
<img width="850" src="https://user-images.githubusercontent.com/599935/41578613-fa10e8bc-7347-11e8-8b97-9fb7d186c9f6.png">
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "profiler",
|
||||
"displayName": "SQL Server Profiler",
|
||||
"description": "SQL Server Profiler for SQL Operations Studio",
|
||||
"version": "0.30.0",
|
||||
"version": "0.1.1",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sqlops",
|
||||
"version": "0.30.5",
|
||||
"version": "0.31.1",
|
||||
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
|
||||
@@ -27,14 +27,16 @@
|
||||
"reportIssueUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=customer%20reported%20issue",
|
||||
"requestFeatureUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=feature-request"
|
||||
},
|
||||
"releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=862039",
|
||||
"gettingStartedUrl": "https://go.microsoft.com/fwlink/?linkid=862039",
|
||||
"releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=875578",
|
||||
"documentationUrl": "https://go.microsoft.com/fwlink/?linkid=862277",
|
||||
"commit": "9ca6200018fc206d67a47229f991901a8a453781",
|
||||
"date": "2017-12-15T12:00:00.000Z",
|
||||
"recommendedExtensions": [
|
||||
"Microsoft.agent",
|
||||
"Microsoft.whoisactive",
|
||||
"Microsoft.profiler",
|
||||
"Microsoft.server-report",
|
||||
"Microsoft.whoisactive",
|
||||
"Redgate.sql-search"
|
||||
],
|
||||
"extensionsGallery": {
|
||||
|
||||
@@ -11,7 +11,7 @@ export class ToggleDropdownAction extends Action {
|
||||
private static readonly ID = 'dropdownAction.toggle';
|
||||
private static readonly ICON = 'dropdown-arrow';
|
||||
|
||||
constructor(label, private _fn: () => any) {
|
||||
constructor(private _fn: () => any, label: string) {
|
||||
super(ToggleDropdownAction.ID, label, ToggleDropdownAction.ICON);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ export interface IDropdownOptions extends IDropdownStyles {
|
||||
* Value to use as aria-label for the input box
|
||||
*/
|
||||
ariaLabel?: string;
|
||||
/**
|
||||
* Label for the dropdown action
|
||||
*/
|
||||
actionLabel: string;
|
||||
}
|
||||
|
||||
export interface IDropdownStyles {
|
||||
@@ -66,7 +70,8 @@ const defaults: IDropdownOptions = {
|
||||
strictSelection: true,
|
||||
maxHeight: 300,
|
||||
errorMessage: errorMessage,
|
||||
contextBorder: Color.fromHex('#696969')
|
||||
contextBorder: Color.fromHex('#696969'),
|
||||
actionLabel: nls.localize('dropdownAction.toggle', "Toggle dropdown")
|
||||
};
|
||||
|
||||
interface ListResource {
|
||||
@@ -115,11 +120,11 @@ export class Dropdown extends Disposable {
|
||||
this.$input = $('.dropdown-input').style('width', '100%').appendTo(this.$el);
|
||||
this.$treeContainer = $('.dropdown-tree');
|
||||
|
||||
this._toggleAction = new ToggleDropdownAction(nls.localize('dropdown.toggle', '{0} Toggle Dropdown', this._options.ariaLabel), () => {
|
||||
this._toggleAction = new ToggleDropdownAction(() => {
|
||||
this._showList();
|
||||
this._tree.domFocus();
|
||||
this._tree.focusFirst();
|
||||
});
|
||||
}, opt.actionLabel);
|
||||
|
||||
this._input = new InputBox(this.$input.getHTMLElement(), contextViewService, {
|
||||
validationOptions: {
|
||||
|
||||
@@ -172,4 +172,22 @@
|
||||
margin-right: 10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.modal .modal-footer .dialogErrorMessage {
|
||||
align-items: center;
|
||||
max-height: 30px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.modal .dialogErrorMessage .icon {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
width: auto;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.modal .modal-footer .dialogErrorMessage .errorMessage {
|
||||
max-height: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
@@ -21,9 +21,13 @@ import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import * as TelemetryUtils from 'sql/common/telemetryUtilities';
|
||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
import { localize } from 'vs/nls';
|
||||
import { MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
export const MODAL_SHOWING_KEY = 'modalShowing';
|
||||
export const MODAL_SHOWING_CONTEXT = new RawContextKey<Array<string>>(MODAL_SHOWING_KEY, []);
|
||||
const INFO_ALT_TEXT = localize('infoAltText', 'Info');
|
||||
const WARNING_ALT_TEXT = localize('warningAltText', 'Warning');
|
||||
const ERROR_ALT_TEXT = localize('errorAltText', 'Error');
|
||||
|
||||
export interface IModalDialogStyles {
|
||||
dialogForeground?: Color;
|
||||
@@ -145,7 +149,7 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
/**
|
||||
* Build and render the modal, will call {@link Modal#renderBody}
|
||||
*/
|
||||
public render() {
|
||||
public render(errorMessagesInFooter: boolean = false) {
|
||||
let modalBodyClass = (this._modalOptions.isAngular === false ? 'modal-body' : 'modal-body-and-footer');
|
||||
let parts: Array<HTMLElement> = [];
|
||||
// This modal header section refers to the header of of the dialog
|
||||
@@ -182,17 +186,6 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
|
||||
this.renderBody(body.getHTMLElement());
|
||||
|
||||
if (this._modalOptions.isAngular === false && this._modalOptions.hasErrors) {
|
||||
body.div({ class: 'dialogErrorMessage', id: 'dialogErrorMessage' }, (errorMessageContainer) => {
|
||||
errorMessageContainer.div({ class: 'icon error' }, (iconContainer) => {
|
||||
this._errorIconElement = iconContainer.getHTMLElement();
|
||||
this._errorIconElement.style.visibility = 'hidden';
|
||||
});
|
||||
errorMessageContainer.div({ class: 'errorMessage' }, (messageContainer) => {
|
||||
this._errorMessage = messageContainer;
|
||||
});
|
||||
});
|
||||
}
|
||||
// This modal footer section refers to the footer of of the dialog
|
||||
if (this._modalOptions.isAngular === false) {
|
||||
this._modalFooterSection = $().div({ class: 'modal-footer' }, (modelFooter) => {
|
||||
@@ -221,6 +214,19 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
builderClass += ' wide';
|
||||
}
|
||||
|
||||
if (this._modalOptions.isAngular === false && this._modalOptions.hasErrors) {
|
||||
let builder = errorMessagesInFooter ? this._leftFooter : body;
|
||||
builder.div({ class: 'dialogErrorMessage', id: 'dialogErrorMessage' }, (errorMessageContainer) => {
|
||||
errorMessageContainer.div({ class: 'icon error' }, (iconContainer) => {
|
||||
this._errorIconElement = iconContainer.getHTMLElement();
|
||||
this._errorIconElement.style.visibility = 'hidden';
|
||||
});
|
||||
errorMessageContainer.div({ class: 'errorMessage' }, (messageContainer) => {
|
||||
this._errorMessage = messageContainer;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// The builder builds the dialog. It append header, body and footer sections.
|
||||
this._builder = $().div({ class: builderClass, 'role': 'dialog' }, (dialogContainer) => {
|
||||
this._modalDialog = dialogContainer.div({ class: 'modal-dialog ', role: 'document' }, (modalDialog) => {
|
||||
@@ -355,14 +361,33 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
* Show an error in the error message element
|
||||
* @param err Text to show in the error message
|
||||
*/
|
||||
protected setError(err: string) {
|
||||
protected setError(err: string, level: MessageLevel = MessageLevel.Error) {
|
||||
if (this._modalOptions.hasErrors) {
|
||||
if (err === '') {
|
||||
this._errorIconElement.style.visibility = 'hidden';
|
||||
} else {
|
||||
const levelClasses = ['info', 'warning', 'error'];
|
||||
let selectedLevel = levelClasses[2];
|
||||
let altText = ERROR_ALT_TEXT;
|
||||
if (level === MessageLevel.Information) {
|
||||
selectedLevel = levelClasses[0];
|
||||
altText = INFO_ALT_TEXT;
|
||||
} else if (level === MessageLevel.Warning) {
|
||||
selectedLevel = levelClasses[1];
|
||||
altText = WARNING_ALT_TEXT;
|
||||
}
|
||||
levelClasses.forEach(level => {
|
||||
if (selectedLevel === level) {
|
||||
this._errorIconElement.classList.add(level);
|
||||
} else {
|
||||
this._errorIconElement.classList.remove(level);
|
||||
}
|
||||
});
|
||||
|
||||
this._errorIconElement.title = altText;
|
||||
this._errorIconElement.style.visibility = 'visible';
|
||||
}
|
||||
this._errorMessage.innerHtml(err);
|
||||
this._errorMessage.text(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-blue{fill:#1BA1E2;} .icon-white{fill:#FFFFFF;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 8c0-4.418 3.582-8 8-8s8 3.582 8 8-3.582 8-8 8-8-3.582-8-8z" id="outline"/><path class="icon-vs-blue" d="M8 1c-3.865 0-7 3.135-7 7s3.135 7 7 7 7-3.135 7-7-3.135-7-7-7zm1 12h-2v-7h2v7zm0-8h-2v-2h2v2z" id="iconBg"/><path class="icon-white" d="M7 6h2v7h-2v-7zm0-1h2v-2h-2v2z" id="iconFg"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-blue{fill:#1BA1E2;} .icon-white{fill:#FFFFFF;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 8c0-4.418 3.582-8 8-8s8 3.582 8 8-3.582 8-8 8-8-3.582-8-8z" id="outline"/><path class="icon-vs-blue" d="M8 1c-3.865 0-7 3.135-7 7s3.135 7 7 7 7-3.135 7-7-3.135-7-7-7zm1 12h-2v-7h2v7zm0-8h-2v-2h2v2z" id="iconBg"/><path class="icon-white" d="M7 6h2v7h-2v-7zm0-1h2v-2h-2v2z" id="iconFg"/></svg>
|
||||
|
Before Width: | Height: | Size: 627 B After Width: | Height: | Size: 624 B |
@@ -6,12 +6,14 @@
|
||||
import { NgModule, Inject, forwardRef, ApplicationRef, ComponentFactoryResolver, Type } from '@angular/core';
|
||||
import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
|
||||
import { CreateLoginComponent, CREATELOGIN_SELECTOR } from 'sql/parts/admin/security/createLogin.component';
|
||||
import { IBootstrapParams, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { CreateLoginComponent } from 'sql/parts/admin/security/createLogin.component';
|
||||
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
// Connection Dashboard main angular module
|
||||
export const CreateLoginModule = (params: IBootstrapParams, selector: string): Type<any> => {
|
||||
export const CreateLoginModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -24,7 +26,8 @@ export const CreateLoginModule = (params: IBootstrapParams, selector: string): T
|
||||
],
|
||||
providers: [
|
||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
||||
{ provide: IBootstrapParams, useValue: params }
|
||||
{ provide: IBootstrapParams, useValue: params },
|
||||
...providerIterator(instantiationService)
|
||||
]
|
||||
})
|
||||
class ModuleClass {
|
||||
|
||||
@@ -172,7 +172,8 @@ export class ConnectionWidget {
|
||||
strictSelection: false,
|
||||
placeholder: this._defaultDatabaseName,
|
||||
maxHeight: 125,
|
||||
ariaLabel: databaseOption.displayName
|
||||
ariaLabel: databaseOption.displayName,
|
||||
actionLabel: localize('toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
|
||||
});
|
||||
|
||||
let serverGroupLabel = localize('serverGroup', 'Server group');
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Inject, NgModule, forwardRef, ApplicationRef, ComponentFactoryResolver, NgModuleRef, NgModuleFactory } from '@angular/core';
|
||||
import { Inject, NgModule, forwardRef, ApplicationRef, ComponentFactoryResolver } from '@angular/core';
|
||||
import { CommonModule, APP_BASE_HREF } from '@angular/common';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { RouterModule, Routes, UrlSerializer, Router, NavigationEnd } from '@angular/router';
|
||||
@@ -14,7 +14,7 @@ import { ChartsModule } from 'ng2-charts/ng2-charts';
|
||||
import CustomUrlSerializer from 'sql/common/urlSerializer';
|
||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { Extensions as ComponentExtensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
@@ -32,7 +32,7 @@ import { CommonServiceInterface } from 'sql/services/common/commonServiceInterfa
|
||||
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
|
||||
|
||||
/* Base Components */
|
||||
import { DashboardComponent, DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component';
|
||||
import { DashboardComponent } from 'sql/parts/dashboard/dashboard.component';
|
||||
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
|
||||
import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component';
|
||||
import { DashboardGridContainer } from 'sql/parts/dashboard/containers/dashboardGridContainer.component';
|
||||
@@ -80,7 +80,8 @@ import { ExplorerWidget } from 'sql/parts/dashboard/widgets/explorer/explorerWid
|
||||
import { TasksWidget } from 'sql/parts/dashboard/widgets/tasks/tasksWidget.component';
|
||||
import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component';
|
||||
import { WebviewWidget } from 'sql/parts/dashboard/widgets/webview/webviewWidget.component';
|
||||
import { JobStepsViewComponent } from '../jobManagement/views/jobStepsView.component';
|
||||
import { JobStepsViewComponent } from 'sql/parts/jobManagement/views/jobStepsView.component';
|
||||
import { IInstantiationService, _util } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
let widgetComponents = [
|
||||
PropertiesWidgetComponent,
|
||||
@@ -109,7 +110,7 @@ const appRoutes: Routes = [
|
||||
];
|
||||
|
||||
// Connection Dashboard main angular module
|
||||
export const DashboardModule = (params, selector: string): any => {
|
||||
export const DashboardModule = (params, selector: string, instantiationService: IInstantiationService): any => {
|
||||
@NgModule({
|
||||
declarations: [
|
||||
...baseComponents,
|
||||
@@ -140,31 +141,31 @@ export const DashboardModule = (params, selector: string): any => {
|
||||
{ provide: IBreadcrumbService, useClass: BreadcrumbService },
|
||||
{ provide: CommonServiceInterface, useClass: DashboardServiceInterface },
|
||||
{ provide: UrlSerializer, useClass: CustomUrlSerializer },
|
||||
{ provide: IBootstrapParams, useValue: params }
|
||||
{ provide: IBootstrapParams, useValue: params },
|
||||
{ provide: ISelector, useValue: selector },
|
||||
...providerIterator(instantiationService)
|
||||
]
|
||||
})
|
||||
class ModuleClass {
|
||||
private _bootstrap: DashboardServiceInterface;
|
||||
private navigations = 0;
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) bootstrap: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => Router)) private _router: Router,
|
||||
@Inject(ITelemetryService) private telemetryService: ITelemetryService
|
||||
@Inject(ITelemetryService) private telemetryService: ITelemetryService,
|
||||
@Inject(ISelector) private selector: string
|
||||
) {
|
||||
this._bootstrap = bootstrap as DashboardServiceInterface;
|
||||
}
|
||||
|
||||
ngDoBootstrap(appRef: ApplicationRef) {
|
||||
const factory = this._resolver.resolveComponentFactory(DashboardComponent);
|
||||
this._bootstrap.selector = selector;
|
||||
(<any>factory).factory.selector = selector;
|
||||
(<any>factory).factory.selector = this.selector;
|
||||
appRef.bootstrap(factory);
|
||||
|
||||
this._router.events.subscribe(e => {
|
||||
if (e instanceof NavigationEnd) {
|
||||
this._bootstrap.handlePageNavigation();
|
||||
this.navigations++;
|
||||
TelemetryUtils.addTelemetry(this.telemetryService, TelemetryKeys.DashboardNavigated, {
|
||||
numberOfNavigations: this._bootstrap.getNumberOfPageNavigations(),
|
||||
numberOfNavigations: this.navigations,
|
||||
routeUrl: e.url
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,50 +4,29 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Node Modules */
|
||||
import { Injectable, Inject, forwardRef, OnDestroy } from '@angular/core';
|
||||
import { Injectable, Inject, forwardRef } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
/* SQL imports */
|
||||
import { IDashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams';
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { IMetadataService } from 'sql/services/metadata/metadataService';
|
||||
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
|
||||
import { IAdminService } from 'sql/parts/admin/common/adminService';
|
||||
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
|
||||
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
|
||||
import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces';
|
||||
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { AngularEventType, IAngularEvent, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
|
||||
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||
import { TabSettingConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { IDashboardViewService } from 'sql/services/dashboard/common/dashboardViewService';
|
||||
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
||||
import { ConnectionContextkey } from 'sql/parts/connection/common/connectionContextKey';
|
||||
import { SingleConnectionMetadataService, SingleConnectionManagementService, SingleAdminService, SingleQueryManagementService, CommonServiceInterface }
|
||||
from 'sql/services/common/commonServiceInterface.service';
|
||||
|
||||
import { ProviderMetadata, DatabaseInfo, SimpleExecuteResult } from 'sqlops';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
|
||||
/* VS imports */
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationEditingService, IConfigurationValue } from 'vs/workbench/services/configuration/node/configurationEditingService';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
const DASHBOARD_SETTINGS = 'dashboard';
|
||||
@@ -85,45 +64,22 @@ export class DashboardServiceInterface extends CommonServiceInterface {
|
||||
private _numberOfPageNavigations = 0;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => Router)) private _router: Router,
|
||||
@Inject(INotificationService) private _notificationService: INotificationService,
|
||||
@Inject(IMetadataService) metadataService: IMetadataService,
|
||||
@Inject(IConnectionManagementService) connectionManagementService: IConnectionManagementService,
|
||||
@Inject(IAdminService) adminService: IAdminService,
|
||||
@Inject(IQueryManagementService) queryManagementService: IQueryManagementService,
|
||||
@Inject(IBootstrapParams) params: IDashboardComponentParams,
|
||||
@Inject(forwardRef(() => Router)) private _router: Router,
|
||||
@Inject(INotificationService) private _notificationService: INotificationService,
|
||||
@Inject(IAngularEventingService) private angularEventingService: IAngularEventingService,
|
||||
@Inject(IConfigurationService) private _configService: IConfigurationService,
|
||||
@Inject(IBootstrapParams) _params: IDashboardComponentParams
|
||||
@Inject(IConfigurationService) private _configService: IConfigurationService
|
||||
) {
|
||||
super(_params, metadataService, connectionManagementService, adminService, queryManagementService);
|
||||
}
|
||||
|
||||
private get params(): IDashboardComponentParams {
|
||||
return this._params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selector for this dashboard instance, should only be set once
|
||||
*/
|
||||
public set selector(selector: string) {
|
||||
this._uniqueSelector = selector;
|
||||
this._getbootstrapParams();
|
||||
}
|
||||
|
||||
protected _getbootstrapParams(): void {
|
||||
this.scopedContextKeyService = this.params.scopedContextService;
|
||||
this._connectionContextKey = this.params.connectionContextKey;
|
||||
this.dashboardContextKey = this._dashboardContextKey.bindTo(this.scopedContextKeyService);
|
||||
this.uri = this.params.ownerUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the uri for this dashboard instance, should only be set once
|
||||
* Inits all the services that depend on knowing a uri
|
||||
*/
|
||||
protected set uri(uri: string) {
|
||||
super.setUri(uri);
|
||||
this._register(toDisposableSubscription(this.angularEventingService.onAngularEvent(this._uri, (event) => this.handleDashboardEvent(event))));
|
||||
super(params, metadataService, connectionManagementService, adminService, queryManagementService);
|
||||
// during testing there may not be params
|
||||
if (this._params) {
|
||||
this.dashboardContextKey = this._dashboardContextKey.bindTo(this.scopedContextKeyService);
|
||||
this._register(toDisposableSubscription(this.angularEventingService.onAngularEvent(this._uri, (event) => this.handleDashboardEvent(event))));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,20 +4,23 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import {
|
||||
ApplicationRef, ComponentFactoryResolver, ModuleWithProviders, NgModule,
|
||||
ApplicationRef, ComponentFactoryResolver, NgModule,
|
||||
Inject, forwardRef, Type
|
||||
} from '@angular/core';
|
||||
import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { BackupComponent, BACKUP_SELECTOR } from 'sql/parts/disasterRecovery/backup/backup.component';
|
||||
|
||||
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { BackupComponent } from 'sql/parts/disasterRecovery/backup/backup.component';
|
||||
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
// work around
|
||||
const BrowserAnimationsModule = (<any>require.__$__nodeRequire('@angular/platform-browser/animations')).BrowserAnimationsModule;
|
||||
|
||||
// Backup wizard main angular module
|
||||
export const BackupModule = (params: IBootstrapParams, selector: string): Type<any> => {
|
||||
export const BackupModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
|
||||
@NgModule({
|
||||
declarations: [
|
||||
BackupComponent
|
||||
@@ -31,19 +34,22 @@ export const BackupModule = (params: IBootstrapParams, selector: string): Type<a
|
||||
],
|
||||
providers: [
|
||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
||||
{ provide: IBootstrapParams, useValue: params }
|
||||
{ provide: IBootstrapParams, useValue: params },
|
||||
{ provide: ISelector, useValue: selector },
|
||||
...providerIterator(instantiationService)
|
||||
]
|
||||
})
|
||||
class ModuleClass {
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
||||
@Inject(ISelector) private selector: string
|
||||
) {
|
||||
}
|
||||
|
||||
ngDoBootstrap(appRef: ApplicationRef) {
|
||||
const factory = this._resolver.resolveComponentFactory(BackupComponent);
|
||||
(<any>factory).factory.selector = selector;
|
||||
(<any>factory).factory.selector = this.selector;
|
||||
appRef.bootstrap(factory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,8 @@ export class RestoreDialog extends Modal {
|
||||
this._databaseDropdown = new Dropdown(inputCellContainer.getHTMLElement(), this._contextViewService, this._themeService,
|
||||
{
|
||||
strictSelection: false,
|
||||
ariaLabel: LocalizedStrings.TARGETDATABASE
|
||||
ariaLabel: LocalizedStrings.TARGETDATABASE,
|
||||
actionLabel: localize('toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
|
||||
}
|
||||
);
|
||||
this._databaseDropdown.onValueChange(s => {
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
import { ApplicationRef, ComponentFactoryResolver, NgModule, Inject, forwardRef, Type } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { EditDataComponent, EDITDATA_SELECTOR } from 'sql/parts/grid/views/editData/editData.component';
|
||||
import { SlickGrid } from 'angular2-slickgrid';
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
|
||||
export const EditDataModule = (params: IBootstrapParams, selector: string): Type<any> => {
|
||||
import { EditDataComponent } from 'sql/parts/grid/views/editData/editData.component';
|
||||
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
||||
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const EditDataModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -30,19 +32,22 @@ export const EditDataModule = (params: IBootstrapParams, selector: string): Type
|
||||
EditDataComponent
|
||||
],
|
||||
providers: [
|
||||
{ provide: IBootstrapParams, useValue: params }
|
||||
{ provide: IBootstrapParams, useValue: params },
|
||||
{ provide: ISelector, useValue: selector },
|
||||
...providerIterator(instantiationService)
|
||||
]
|
||||
})
|
||||
class ModuleClass {
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
||||
@Inject(ISelector) private selector: string
|
||||
) {
|
||||
}
|
||||
|
||||
ngDoBootstrap(appRef: ApplicationRef) {
|
||||
const factory = this._resolver.resolveComponentFactory(EditDataComponent);
|
||||
(<any>factory).factory.selector = selector;
|
||||
(<any>factory).factory.selector = this.selector;
|
||||
appRef.bootstrap(factory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,9 @@
|
||||
|
||||
.editdata-component * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#workbench\.editor\.editDataEditor .monaco-toolbar .monaco-select-box {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
@@ -10,21 +10,16 @@ import {
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||
import { ComponentWithIconBase } from 'sql/parts/modelComponents/componentWithIconBase';
|
||||
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||
import { attachButtonStyler } from 'sql/common/theme/styler';
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
|
||||
import { SIDE_BAR_BACKGROUND, SIDE_BAR_TITLE_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import { createCSSRule, removeCSSRulesContainingSelector } from 'vs/base/browser/dom';
|
||||
import { focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
type IUserFriendlyIcon = string | URI | { light: string | URI; dark: string | URI };
|
||||
|
||||
@Component({
|
||||
selector: 'modelview-button',
|
||||
@@ -32,12 +27,10 @@ type IUserFriendlyIcon = string | URI | { light: string | URI; dark: string | UR
|
||||
<div #input style="width: 100%"></div>
|
||||
`
|
||||
})
|
||||
export default class ButtonComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||
export default class ButtonComponent extends ComponentWithIconBase implements IComponent, OnDestroy, AfterViewInit {
|
||||
@Input() descriptor: IComponentDescriptor;
|
||||
@Input() modelStore: IModelStore;
|
||||
private _button: Button;
|
||||
private _iconClass: string;
|
||||
private _iconPath: IUserFriendlyIcon;
|
||||
|
||||
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
|
||||
constructor(
|
||||
@@ -71,9 +64,6 @@ export default class ButtonComponent extends ComponentBase implements IComponent
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this._iconClass) {
|
||||
removeCSSRulesContainingSelector(this._iconClass);
|
||||
}
|
||||
this.baseDestroy();
|
||||
}
|
||||
|
||||
@@ -93,22 +83,19 @@ export default class ButtonComponent extends ComponentBase implements IComponent
|
||||
this._button.enabled = this.enabled;
|
||||
this._button.label = this.label;
|
||||
if (this.width) {
|
||||
this._button.setWidth(this.width.toString());
|
||||
this._button.setWidth(this.convertSize(this.width.toString()));
|
||||
}
|
||||
if (this.height) {
|
||||
this._button.setWidth(this.height.toString());
|
||||
this._button.setWidth(this.convertSize(this.height.toString()));
|
||||
}
|
||||
this.updateIcon();
|
||||
}
|
||||
|
||||
private updateIcon() {
|
||||
if (this.iconPath && this.iconPath !== this._iconPath) {
|
||||
this._iconPath = this.iconPath;
|
||||
protected updateIcon() {
|
||||
if (this.iconPath) {
|
||||
if (!this._iconClass) {
|
||||
const ids = new IdGenerator('button-component-icon-' + Math.round(Math.random() * 1000));
|
||||
this._iconClass = ids.nextId();
|
||||
super.updateIcon();
|
||||
this._button.icon = this._iconClass + ' icon';
|
||||
|
||||
// Styling for icon button
|
||||
this._register(attachButtonStyler(this._button, this.themeService, {
|
||||
buttonBackground: Color.transparent.toString(),
|
||||
@@ -117,36 +104,6 @@ export default class ButtonComponent extends ComponentBase implements IComponent
|
||||
buttonForeground: foreground
|
||||
}));
|
||||
}
|
||||
|
||||
removeCSSRulesContainingSelector(this._iconClass);
|
||||
const icon = this.getLightIconPath(this.iconPath);
|
||||
const iconDark = this.getDarkIconPath(this.iconPath) || icon;
|
||||
createCSSRule(`.icon.${this._iconClass}`, `background-image: url("${icon}")`);
|
||||
createCSSRule(`.vs-dark .icon.${this._iconClass}, .hc-black .icon.${this._iconClass}`, `background-image: url("${iconDark}")`);
|
||||
}
|
||||
}
|
||||
|
||||
private getLightIconPath(iconPath: IUserFriendlyIcon): string {
|
||||
if (iconPath && iconPath['light']) {
|
||||
return this.getIconPath(iconPath['light']);
|
||||
} else {
|
||||
return this.getIconPath(<string | URI>iconPath);
|
||||
}
|
||||
}
|
||||
|
||||
private getDarkIconPath(iconPath: IUserFriendlyIcon): string {
|
||||
if (iconPath && iconPath['dark']) {
|
||||
return this.getIconPath(iconPath['dark']);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private getIconPath(iconPath: string | URI): string {
|
||||
if (typeof iconPath === 'string') {
|
||||
return URI.file(iconPath).toString();
|
||||
} else {
|
||||
let uri = URI.revive(iconPath);
|
||||
return uri.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,13 +117,7 @@ export default class ButtonComponent extends ComponentBase implements IComponent
|
||||
this.setPropertyFromUI<sqlops.ButtonProperties, string>(this.setValueProperties, newValue);
|
||||
}
|
||||
|
||||
public get iconPath(): string | URI | { light: string | URI; dark: string | URI } {
|
||||
return this.getPropertyOrDefault<sqlops.ButtonProperties, IUserFriendlyIcon>((props) => props.iconPath, undefined);
|
||||
}
|
||||
|
||||
public set iconPath(newValue: string | URI | { light: string | URI; dark: string | URI }) {
|
||||
this.setPropertyFromUI<sqlops.ButtonProperties, IUserFriendlyIcon>((properties, iconPath) => { properties.iconPath = iconPath; }, newValue);
|
||||
}
|
||||
|
||||
private setValueProperties(properties: sqlops.ButtonProperties, label: string): void {
|
||||
properties.label = label;
|
||||
|
||||
@@ -1,19 +1,32 @@
|
||||
<div *ngIf="label" class="model-card">
|
||||
<div *ngIf="label" [class]="getClass()" (click)="onCardClick()" (mouseover)="onCardHoverChanged($event)" (mouseout)="onCardHoverChanged($event)">
|
||||
<span *ngIf="hasStatus" class="card-status">
|
||||
<div class="status-content" [style.backgroundColor]="statusColor"></div>
|
||||
</span>
|
||||
<div class="card-content">
|
||||
<h4 class="card-label">{{label}}</h4>
|
||||
<p class="card-value">{{value}}</p>
|
||||
<span *ngIf="actions">
|
||||
<table class="model-table">
|
||||
<tr *ngFor="let action of actions">
|
||||
<td class="table-row">{{action.label}}</td>
|
||||
<td *ngIf="action.actionTitle" class="table-row">
|
||||
<a class="pointer prominent" (click)="onDidActionClick(action)">{{action.actionTitle}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="isVerticalButton">
|
||||
<div class="card-vertical-button">
|
||||
<div *ngIf="iconPath" class="iconContainer"><div [class]="iconClass" [style.width]="iconWidth" [style.height]="iconHeight"></div>
|
||||
<hr/>
|
||||
<h4 class="card-label">{{label}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="isDetailsCard">
|
||||
<div class="card-content">
|
||||
<h4 class="card-label">{{label}}</h4>
|
||||
<p class="card-value">{{value}}</p>
|
||||
<span *ngIf="actions">
|
||||
<table class="model-table">
|
||||
<tr *ngFor="let action of actions">
|
||||
<td class="table-row">{{action.label}}</td>
|
||||
<td *ngIf="action.actionTitle" class="table-row">
|
||||
<a class="pointer prominent" (click)="onDidActionClick(action)">{{action.actionTitle}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
@@ -15,14 +15,14 @@ import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||
import { ComponentWithIconBase } from 'sql/parts/modelComponents/componentWithIconBase';
|
||||
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||
import { StatusIndicator, CardProperties, ActionDescriptor } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
@Component({
|
||||
templateUrl: decodeURI(require.toUrl('sql/parts/modelComponents/card.component.html'))
|
||||
})
|
||||
export default class CardComponent extends ComponentBase implements IComponent, OnDestroy {
|
||||
export default class CardComponent extends ComponentWithIconBase implements IComponent, OnDestroy {
|
||||
@Input() descriptor: IComponentDescriptor;
|
||||
@Input() modelStore: IModelStore;
|
||||
|
||||
@@ -30,7 +30,7 @@ export default class CardComponent extends ComponentBase implements IComponent,
|
||||
|
||||
constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||
) {
|
||||
super(changeRef);
|
||||
}
|
||||
@@ -46,6 +46,39 @@ export default class CardComponent extends ComponentBase implements IComponent,
|
||||
this.baseDestroy();
|
||||
}
|
||||
|
||||
private _defaultBorderColor = 'rgb(214, 214, 214)';
|
||||
private _hasFocus: boolean;
|
||||
|
||||
public onCardClick() {
|
||||
if (this.selectable) {
|
||||
this.selected = !this.selected;
|
||||
this._changeRef.detectChanges();
|
||||
this._onEventEmitter.fire({
|
||||
eventType: ComponentEventType.onDidClick,
|
||||
args: this.selected
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public getBorderColor() {
|
||||
if (this.selectable && this.selected || this._hasFocus) {
|
||||
return 'Blue';
|
||||
} else {
|
||||
return this._defaultBorderColor;
|
||||
}
|
||||
}
|
||||
|
||||
public getClass(): string {
|
||||
return (this.selectable && this.selected || this._hasFocus) ? 'model-card selected' :
|
||||
'model-card unselected';
|
||||
}
|
||||
|
||||
public onCardHoverChanged(event: any) {
|
||||
if (this.selectable) {
|
||||
this._hasFocus = event.type === 'mouseover';
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
}
|
||||
/// IComponent implementation
|
||||
|
||||
public layout(): void {
|
||||
@@ -57,6 +90,19 @@ export default class CardComponent extends ComponentBase implements IComponent,
|
||||
this.layout();
|
||||
}
|
||||
|
||||
public setProperties(properties: { [key: string]: any; }): void {
|
||||
super.setProperties(properties);
|
||||
this.updateIcon();
|
||||
}
|
||||
|
||||
public get iconClass(): string {
|
||||
return this._iconClass + ' icon' + ' cardIcon';
|
||||
}
|
||||
|
||||
private get selectable(): boolean {
|
||||
return this.cardType === 'VerticalButton';
|
||||
}
|
||||
|
||||
// CSS-bound properties
|
||||
|
||||
public get label(): string {
|
||||
@@ -67,6 +113,27 @@ export default class CardComponent extends ComponentBase implements IComponent,
|
||||
return this.getPropertyOrDefault<CardProperties, string>((props) => props.value, '');
|
||||
}
|
||||
|
||||
public get cardType(): string {
|
||||
return this.getPropertyOrDefault<CardProperties, string>((props) => props.cardType, 'Details');
|
||||
}
|
||||
|
||||
public get selected(): boolean {
|
||||
return this.getPropertyOrDefault<sqlops.CardProperties, boolean>((props) => props.selected, false);
|
||||
}
|
||||
|
||||
public set selected(newValue: boolean) {
|
||||
this.setPropertyFromUI<sqlops.CardProperties, boolean>((props, value) => props.selected = value, newValue);
|
||||
}
|
||||
|
||||
public get isDetailsCard(): boolean {
|
||||
return !this.cardType || this.cardType === 'Details';
|
||||
}
|
||||
|
||||
public get isVerticalButton(): boolean {
|
||||
return this.cardType === 'VerticalButton';
|
||||
}
|
||||
|
||||
|
||||
public get actions(): ActionDescriptor[] {
|
||||
return this.getPropertyOrDefault<CardProperties, ActionDescriptor[]>((props) => props.actions, []);
|
||||
}
|
||||
|
||||
@@ -7,12 +7,26 @@
|
||||
margin: 15px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: rgb(214, 214, 214);
|
||||
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
box-shadow: rgba(120, 120, 120, 0.75) 0px 0px 6px;
|
||||
}
|
||||
|
||||
.model-card.selected {
|
||||
border-color: darkblue
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench .model-card.selected,
|
||||
.hc-black .monaco-workbench .model-card.selected {
|
||||
border-color: darkblue
|
||||
}
|
||||
|
||||
.model-card.unselected {
|
||||
border-color: rgb(214, 214, 214);
|
||||
}
|
||||
|
||||
|
||||
.model-card .card-content {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
@@ -23,6 +37,16 @@
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
.model-card .card-vertical-button {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: auto;
|
||||
width: auto;
|
||||
padding: 5px 5px 5px 5px;
|
||||
min-height: 130px;
|
||||
min-width: 130px;
|
||||
}
|
||||
|
||||
.model-card .card-label {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
@@ -33,6 +57,19 @@
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.model-card .iconContainer {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
text-align: center;
|
||||
padding: 10px 0px 10px 0px;
|
||||
}
|
||||
|
||||
.model-card .cardIcon {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.model-card .card-status {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
|
||||
@@ -20,7 +20,7 @@ import { attachInputBoxStyler, attachListStyler } from 'vs/platform/theme/common
|
||||
@Component({
|
||||
selector: 'modelview-checkbox',
|
||||
template: `
|
||||
<div #input style="width: 100%"></div>
|
||||
<div #input [style.width]="getWidth()"></div>
|
||||
`
|
||||
})
|
||||
export default class CheckBoxComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||
|
||||
@@ -18,6 +18,12 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import { createCSSRule, removeCSSRulesContainingSelector } from 'vs/base/browser/dom';
|
||||
|
||||
|
||||
export type IUserFriendlyIcon = string | URI | { light: string | URI; dark: string | URI };
|
||||
|
||||
export class ItemDescriptor<T> {
|
||||
constructor(public descriptor: IComponentDescriptor, public config: T) { }
|
||||
@@ -98,10 +104,6 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
||||
if (enabled === undefined) {
|
||||
enabled = true;
|
||||
properties['enabled'] = enabled;
|
||||
this.fireEvent({
|
||||
eventType: ComponentEventType.PropertiesChanged,
|
||||
args: this.getProperties()
|
||||
});
|
||||
}
|
||||
return <boolean>enabled;
|
||||
}
|
||||
@@ -146,11 +148,12 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
||||
return this.height ? this.convertSize(this.height) : '';
|
||||
}
|
||||
|
||||
protected convertSize(size: number | string): string {
|
||||
protected convertSize(size: number | string, defaultValue?: string): string {
|
||||
defaultValue = defaultValue || '';
|
||||
if (types.isUndefinedOrNull(size)) {
|
||||
return '100%';
|
||||
return defaultValue;
|
||||
}
|
||||
let convertedSize: string = size ? size.toString() : '100%';
|
||||
let convertedSize: string = size ? size.toString() : defaultValue;
|
||||
if (!convertedSize.toLowerCase().endsWith('px') && !convertedSize.toLowerCase().endsWith('%')) {
|
||||
convertedSize = convertedSize + 'px';
|
||||
}
|
||||
|
||||
107
src/sql/parts/modelComponents/componentWithIconBase.ts
Normal file
107
src/sql/parts/modelComponents/componentWithIconBase.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import {
|
||||
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, OnInit, QueryList
|
||||
} from '@angular/core';
|
||||
|
||||
import { IComponent, IComponentDescriptor, IModelStore, IComponentEventArgs, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||
import * as sqlops from 'sqlops';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import { createCSSRule, removeCSSRulesContainingSelector } from 'vs/base/browser/dom';
|
||||
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||
|
||||
|
||||
export type IUserFriendlyIcon = string | URI | { light: string | URI; dark: string | URI };
|
||||
|
||||
export class ItemDescriptor<T> {
|
||||
constructor(public descriptor: IComponentDescriptor, public config: T) { }
|
||||
}
|
||||
|
||||
export abstract class ComponentWithIconBase extends ComponentBase {
|
||||
|
||||
protected _iconClass: string;
|
||||
protected _iconPath: IUserFriendlyIcon;
|
||||
constructor(
|
||||
changeRef: ChangeDetectorRef) {
|
||||
super(changeRef);
|
||||
}
|
||||
|
||||
/// IComponent implementation
|
||||
|
||||
public get iconClass(): string {
|
||||
return this._iconClass + ' icon';
|
||||
}
|
||||
|
||||
protected updateIcon() {
|
||||
if (this.iconPath && this.iconPath !== this._iconPath) {
|
||||
this._iconPath = this.iconPath;
|
||||
if (!this._iconClass) {
|
||||
const ids = new IdGenerator('model-view-component-icon-' + Math.round(Math.random() * 1000));
|
||||
this._iconClass = ids.nextId();
|
||||
}
|
||||
|
||||
removeCSSRulesContainingSelector(this._iconClass);
|
||||
const icon = this.getLightIconPath(this.iconPath);
|
||||
const iconDark = this.getDarkIconPath(this.iconPath) || icon;
|
||||
createCSSRule(`.icon.${this._iconClass}`, `background-image: url("${icon}")`);
|
||||
createCSSRule(`.vs-dark .icon.${this._iconClass}, .hc-black .icon.${this._iconClass}`, `background-image: url("${iconDark}")`);
|
||||
}
|
||||
}
|
||||
|
||||
private getLightIconPath(iconPath: IUserFriendlyIcon): string {
|
||||
if (iconPath && iconPath['light']) {
|
||||
return this.getIconPath(iconPath['light']);
|
||||
} else {
|
||||
return this.getIconPath(<string | URI>iconPath);
|
||||
}
|
||||
}
|
||||
|
||||
private getDarkIconPath(iconPath: IUserFriendlyIcon): string {
|
||||
if (iconPath && iconPath['dark']) {
|
||||
return this.getIconPath(iconPath['dark']);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private getIconPath(iconPath: string | URI): string {
|
||||
if (typeof iconPath === 'string') {
|
||||
return URI.file(iconPath).toString();
|
||||
} else {
|
||||
let uri = URI.revive(iconPath);
|
||||
return uri.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public getIconWidth(): string {
|
||||
return this.convertSize(this.iconWidth, '40px');
|
||||
}
|
||||
|
||||
public getIconHeight(): string {
|
||||
return this.convertSize(this.iconHeight, '40px');
|
||||
}
|
||||
|
||||
public get iconPath(): string | URI | { light: string | URI; dark: string | URI } {
|
||||
return this.getPropertyOrDefault<sqlops.ComponentWithIcon, IUserFriendlyIcon>((props) => props.iconPath, undefined);
|
||||
}
|
||||
|
||||
public get iconHeight(): number | string {
|
||||
return this.getPropertyOrDefault<sqlops.ComponentWithIcon, number | string>((props) => props.iconHeight, '40px');
|
||||
}
|
||||
|
||||
public get iconWidth(): number | string {
|
||||
return this.getPropertyOrDefault<sqlops.ComponentWithIcon, number | string>((props) => props.iconWidth, '40px');
|
||||
}
|
||||
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this._iconClass) {
|
||||
removeCSSRulesContainingSelector(this._iconClass);
|
||||
}
|
||||
super.ngOnDestroy();
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import {
|
||||
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
|
||||
Component, Input, Inject, ChangeDetectorRef, forwardRef,
|
||||
ViewChild, ElementRef, OnDestroy, AfterViewInit
|
||||
} from '@angular/core';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
@@ -14,14 +14,11 @@ import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||
import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/dropdown';
|
||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
|
||||
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
|
||||
@Component({
|
||||
selector: 'modelview-dropdown',
|
||||
@@ -60,7 +57,8 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
||||
strictSelection: false,
|
||||
placeholder: '',
|
||||
maxHeight: 125,
|
||||
ariaLabel: ''
|
||||
ariaLabel: '',
|
||||
actionLabel: ''
|
||||
};
|
||||
this._editableDropdown = new Dropdown(this._editableDropDownContainer.nativeElement, this.contextViewService, this.themeService,
|
||||
dropdownOptions);
|
||||
@@ -145,18 +143,22 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
||||
}
|
||||
|
||||
private getSelectedValue(): string {
|
||||
if (this.values && this.valuesHaveDisplayName()) {
|
||||
let valueCategory = (<sqlops.CategoryValue[]>this.values).find(v => v.name === this.value);
|
||||
if (this.values && this.values.length > 0 && this.valuesHaveDisplayName()) {
|
||||
let selectedValue = <sqlops.CategoryValue>this.value || <sqlops.CategoryValue>this.values[0];
|
||||
let valueCategory = (<sqlops.CategoryValue[]>this.values).find(v => v.name === selectedValue.name);
|
||||
return valueCategory && valueCategory.displayName;
|
||||
} else {
|
||||
return this.value;
|
||||
if (!this.value && this.values && this.values.length > 0) {
|
||||
return <string>this.values[0];
|
||||
}
|
||||
return <string>this.value;
|
||||
}
|
||||
}
|
||||
|
||||
private setSelectedValue(newValue: string): void {
|
||||
if (this.values && this.valuesHaveDisplayName()) {
|
||||
let valueCategory = (<sqlops.CategoryValue[]>this.values).find(v => v.displayName === newValue);
|
||||
this.value = valueCategory && valueCategory.name;
|
||||
this.value = valueCategory;
|
||||
} else {
|
||||
this.value = newValue;
|
||||
}
|
||||
@@ -164,8 +166,8 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
||||
|
||||
// CSS-bound properties
|
||||
|
||||
private get value(): string {
|
||||
return this.getPropertyOrDefault<sqlops.DropDownProperties, string>((props) => props.value, '');
|
||||
private get value(): string | sqlops.CategoryValue {
|
||||
return this.getPropertyOrDefault<sqlops.DropDownProperties, string | sqlops.CategoryValue>((props) => props.value, '');
|
||||
}
|
||||
|
||||
private get editable(): boolean {
|
||||
@@ -180,8 +182,8 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
||||
return !this.editable ? '' : 'none';
|
||||
}
|
||||
|
||||
private set value(newValue: string) {
|
||||
this.setPropertyFromUI<sqlops.DropDownProperties, string>(this.setValueProperties, newValue);
|
||||
private set value(newValue: string | sqlops.CategoryValue) {
|
||||
this.setPropertyFromUI<sqlops.DropDownProperties, string | sqlops.CategoryValue>(this.setValueProperties, newValue);
|
||||
}
|
||||
|
||||
private get values(): string[] | sqlops.CategoryValue[] {
|
||||
@@ -192,11 +194,11 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
||||
this.setPropertyFromUI<sqlops.DropDownProperties, string[] | sqlops.CategoryValue[]>(this.setValuesProperties, newValue);
|
||||
}
|
||||
|
||||
private setValueProperties(properties: sqlops.DropDownProperties, value: string): void {
|
||||
private setValueProperties(properties: sqlops.DropDownProperties, value: string | sqlops.CategoryValue): void {
|
||||
properties.value = value;
|
||||
}
|
||||
|
||||
private setValuesProperties(properties: sqlops.DropDownProperties, values: string[]): void {
|
||||
private setValuesProperties(properties: sqlops.DropDownProperties, values: string[] | sqlops.CategoryValue[]): void {
|
||||
properties.values = values;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class FlexItem {
|
||||
template: `
|
||||
<div *ngIf="items" class="flexContainer" [style.flexFlow]="flexFlow" [style.justifyContent]="justifyContent"
|
||||
[style.alignItems]="alignItems" [style.alignContent]="alignContent" [style.height]="height" [style.width]="width">
|
||||
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.order]="getItemOrder(item)" >
|
||||
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.textAlign]="textAlign" [style.order]="getItemOrder(item)" >
|
||||
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
|
||||
</model-component-wrapper>
|
||||
</div>
|
||||
@@ -40,6 +40,7 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
|
||||
private _justifyContent: string;
|
||||
private _alignItems: string;
|
||||
private _alignContent: string;
|
||||
private _textAlign: string;
|
||||
private _height: string;
|
||||
private _width: string;
|
||||
|
||||
@@ -65,6 +66,7 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
|
||||
this._justifyContent = layout.justifyContent ? layout.justifyContent : '';
|
||||
this._alignItems = layout.alignItems ? layout.alignItems : '';
|
||||
this._alignContent = layout.alignContent ? layout.alignContent : '';
|
||||
this._textAlign = layout.textAlign ? layout.textAlign : '';
|
||||
this._height = this.convertSize(layout.height);
|
||||
this._width = this.convertSize(layout.width);
|
||||
|
||||
@@ -96,6 +98,10 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
|
||||
return this._alignContent;
|
||||
}
|
||||
|
||||
public get textAlign(): string {
|
||||
return this._textAlign;
|
||||
}
|
||||
|
||||
private getItemFlex(item: FlexItem): string {
|
||||
return item.config ? item.config.flex : '1 1 auto';
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!./formLayout';
|
||||
import 'vs/css!sql/media/icons/common-icons';
|
||||
|
||||
import {
|
||||
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||
@@ -16,13 +17,18 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar
|
||||
import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
|
||||
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { getContentHeight, getContentWidth, Dimension } from 'vs/base/browser/dom';
|
||||
|
||||
export interface TitledFormItemLayout {
|
||||
title: string;
|
||||
actions?: string[];
|
||||
isFormComponent: Boolean;
|
||||
horizontal: boolean;
|
||||
componentWidth: number;
|
||||
componentWidth?: number | string;
|
||||
componentHeight?: number | string;
|
||||
titleFontSize?: number | string;
|
||||
required?: boolean;
|
||||
info?: string;
|
||||
}
|
||||
|
||||
export interface FormLayout {
|
||||
@@ -35,12 +41,15 @@ class FormItem {
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<div #container *ngIf="items" class="form-table" [style.width]="getFormWidth()" [style.height]="getFormHeight()">
|
||||
<div #container *ngIf="items" class="form-table" [style.padding]="getFormPadding()" [style.width]="getFormWidth()" [style.height]="getFormHeight()">
|
||||
<ng-container *ngFor="let item of items">
|
||||
<div class="form-row" *ngIf="isFormComponent(item)">
|
||||
<div class="form-row" *ngIf="isFormComponent(item)" [style.height]="getRowHeight(item)">
|
||||
|
||||
<ng-container *ngIf="isHorizontal(item)">
|
||||
<div class="form-cell">{{getItemTitle(item)}}</div>
|
||||
<div class="form-cell" [style.font-size]="getItemTitleFontSize(item)">
|
||||
{{getItemTitle(item)}}<span class="form-required" *ngIf="isItemRequired(item)">*</span>
|
||||
<span class="icon info form-info" *ngIf="itemHasInfo(item)" [title]="getItemInfo(item)"></span>
|
||||
</div>
|
||||
<div class="form-cell">
|
||||
<div class="form-component-container">
|
||||
<div [style.width]="getComponentWidth(item)" [ngClass]="{'form-input-flex': !getComponentWidth(item)}">
|
||||
@@ -56,10 +65,13 @@ class FormItem {
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="form-vertical-container" *ngIf="isVertical(item)">
|
||||
<div class="form-item-row">{{getItemTitle(item)}}</div>
|
||||
<div class="form-item-row" [style.width]="getComponentWidth(item)">
|
||||
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore" [style.width]="getComponentWidth(item)">
|
||||
<div class="form-vertical-container" *ngIf="isVertical(item)" [style.height]="getRowHeight(item)">
|
||||
<div class="form-item-row" [style.font-size]="getItemTitleFontSize(item)">
|
||||
{{getItemTitle(item)}}<span class="form-required" *ngIf="isItemRequired(item)">*</span>
|
||||
<span class="icon info form-info" *ngIf="itemHasInfo(item)" [title]="getItemInfo(item)"></span>
|
||||
</div>
|
||||
<div class="form-item-row" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)">
|
||||
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)">
|
||||
</model-component-wrapper>
|
||||
</div>
|
||||
<div *ngIf="itemHasActions(item)" class="form-item-row form-actions-table form-item-last-row">
|
||||
@@ -101,6 +113,10 @@ export default class FormContainer extends ContainerBase<FormItemLayout> impleme
|
||||
ngAfterViewInit(): void {
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
super.layout();
|
||||
}
|
||||
|
||||
/// IComponent implementation
|
||||
|
||||
public get alignItems(): string {
|
||||
@@ -112,23 +128,53 @@ export default class FormContainer extends ContainerBase<FormItemLayout> impleme
|
||||
}
|
||||
|
||||
private getFormWidth(): string {
|
||||
return this.convertSize(this._formLayout && this._formLayout.width);
|
||||
return this.convertSize(this._formLayout && this._formLayout.width, '');
|
||||
}
|
||||
|
||||
private getFormPadding(): string {
|
||||
return this._formLayout && this._formLayout.padding ? this._formLayout.padding : '10px 30px 0px 30px';
|
||||
}
|
||||
|
||||
private getFormHeight(): string {
|
||||
return this.convertSize(this._formLayout && this._formLayout.height);
|
||||
return this.convertSize(this._formLayout && this._formLayout.height, '');
|
||||
}
|
||||
|
||||
private getComponentWidth(item: FormItem): string {
|
||||
let itemConfig = item.config;
|
||||
return (itemConfig && itemConfig.componentWidth) ? itemConfig.componentWidth + 'px' : '';
|
||||
return (itemConfig && itemConfig.componentWidth) ? this.convertSize(itemConfig.componentWidth, '') : '';
|
||||
}
|
||||
|
||||
private getRowHeight(item: FormItem): string {
|
||||
let itemConfig = item.config;
|
||||
return (itemConfig && itemConfig.componentHeight) ? this.convertSize(itemConfig.componentHeight, '') : '';
|
||||
}
|
||||
|
||||
private isItemRequired(item: FormItem): boolean {
|
||||
let itemConfig = item.config;
|
||||
return itemConfig && itemConfig.required;
|
||||
}
|
||||
|
||||
private getItemInfo(item: FormItem): string {
|
||||
let itemConfig = item.config;
|
||||
return itemConfig && itemConfig.info;
|
||||
}
|
||||
|
||||
private itemHasInfo(item: FormItem): boolean {
|
||||
let itemConfig = item.config;
|
||||
return itemConfig && itemConfig.info !== undefined;
|
||||
}
|
||||
|
||||
|
||||
private getItemTitle(item: FormItem): string {
|
||||
let itemConfig = item.config;
|
||||
return itemConfig ? itemConfig.title : '';
|
||||
}
|
||||
|
||||
private getItemTitleFontSize(item: FormItem): string {
|
||||
let itemConfig = item.config;
|
||||
return itemConfig && itemConfig.titleFontSize ? this.convertSize(itemConfig.titleFontSize, '11px') : '11px';
|
||||
}
|
||||
|
||||
private getActionComponents(item: FormItem): FormItem[] {
|
||||
let items = this.items;
|
||||
let itemConfig = item.config;
|
||||
|
||||
@@ -37,6 +37,16 @@
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.form-required {
|
||||
color: red;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.form-info {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.form-component-actions {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export class ModelViewContent extends ViewBase implements OnInit, IModelView {
|
||||
|
||||
@memoize
|
||||
public get connection(): sqlops.connection.Connection {
|
||||
if (!this._commonService.connectionManagementService) {
|
||||
if (!this._commonService.connectionManagementService || !this._commonService.connectionManagementService.connectionInfo) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export class ModelViewContent extends ViewBase implements OnInit, IModelView {
|
||||
|
||||
@memoize
|
||||
public get serverInfo(): sqlops.ServerInfo {
|
||||
if (!this._commonService.connectionManagementService) {
|
||||
if (!this._commonService.connectionManagementService || !this._commonService.connectionManagementService.connectionInfo) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectio
|
||||
@Component({
|
||||
selector: 'modelview-table',
|
||||
template: `
|
||||
<div #table style="width: 100%"></div>
|
||||
<div #table style="width: 100%;height:100%"></div>
|
||||
`
|
||||
})
|
||||
export default class TableComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||
@@ -92,9 +92,8 @@ export default class TableComponent extends ComponentBase implements IComponent,
|
||||
let options = <Slick.GridOptions<any>>{
|
||||
syncColumnCellResize: true,
|
||||
enableColumnReorder: false,
|
||||
rowHeight: 45,
|
||||
enableCellNavigation: true,
|
||||
forceFitColumns: true
|
||||
forceFitColumns: true,
|
||||
};
|
||||
|
||||
this._table = new Table<Slick.SlickData>(this._inputContainer.nativeElement, this._tableData, this._tableColumns, options);
|
||||
|
||||
@@ -19,8 +19,10 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { IObjectExplorerService } from '../../objectExplorer/common/objectExplorerService';
|
||||
import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
|
||||
|
||||
// Contribute Global Actions
|
||||
const category = nls.localize('profilerCategory', "Profiler");
|
||||
@@ -37,20 +39,9 @@ CommandsRegistry.registerCommand({
|
||||
let editorService: IWorkbenchEditorService = accessor.get(IWorkbenchEditorService);
|
||||
let instantiationService: IInstantiationService = accessor.get(IInstantiationService);
|
||||
let connectionService: IConnectionManagementService = accessor.get(IConnectionManagementService);
|
||||
let objectExplorerService: IObjectExplorerService = accessor.get(IObjectExplorerService);
|
||||
|
||||
// TODO: for test-only, grab the first MSSQL active connection for the profiler session
|
||||
// TODO: when finishing the feature the connection should come from the launch context
|
||||
let connectionProfile: IConnectionProfile;
|
||||
let activeConnections = connectionService.getActiveConnections();
|
||||
if (activeConnections) {
|
||||
for (let i = 0; i < activeConnections.length; ++i) {
|
||||
if (activeConnections[i].providerName === 'MSSQL') {
|
||||
connectionProfile = activeConnections[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let connectionProfile = TaskUtilities.getCurrentGlobalConnection(objectExplorerService, connectionService, editorService);
|
||||
let profilerInput = instantiationService.createInstance(ProfilerInput, connectionProfile);
|
||||
return editorService.openEditor(profilerInput, { pinned: true }, false).then(() => TPromise.as(true));
|
||||
}
|
||||
|
||||
@@ -202,8 +202,6 @@ export class ProfilerEditor extends BaseEditor {
|
||||
this._register(attachSelectBoxStyler(this._sessionTemplateSelector, this.themeService));
|
||||
|
||||
this._actionBar.setContent([
|
||||
{ action: this._connectAction },
|
||||
{ element: Taskbar.createTaskbarSeparator() },
|
||||
{ action: this._startAction },
|
||||
{ action: this._stopAction },
|
||||
{ element: dropdownContainer },
|
||||
@@ -386,21 +384,20 @@ export class ProfilerEditor extends BaseEditor {
|
||||
|
||||
if (e.isConnected) {
|
||||
this._connectAction.connected = this.input.state.isConnected;
|
||||
this._startAction.enabled = this.input.state.isConnected;
|
||||
this._stopAction.enabled = false;
|
||||
this._pauseAction.enabled = false;
|
||||
|
||||
if (this.input.state.isConnected) {
|
||||
this._sessionTemplateSelector.disable();
|
||||
} else {
|
||||
this._sessionTemplateSelector.enable();
|
||||
this._startAction.enabled = this.input.state.isConnected;
|
||||
this._stopAction.enabled = false;
|
||||
this._pauseAction.enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.isPaused){
|
||||
this._pauseAction.paused = this.input.state.isPaused;
|
||||
this._pauseAction.enabled = !this.input.state.isStopped && (this.input.state.isRunning || this.input.state.isPaused);
|
||||
}
|
||||
|
||||
if (e.isStopped || e.isRunning) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EditorInput } from 'vs/workbench/common/editor';
|
||||
import { IEditorModel } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
|
||||
@@ -35,14 +36,15 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
|
||||
constructor(
|
||||
private _connection: IConnectionProfile,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IProfilerService private _profilerService: IProfilerService
|
||||
@IProfilerService private _profilerService: IProfilerService,
|
||||
@INotificationService private _notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
this._state = new ProfilerState();
|
||||
// set inital state
|
||||
this.state.change({
|
||||
isConnected: true,
|
||||
isStopped: false,
|
||||
isStopped: true,
|
||||
isPaused: false,
|
||||
isRunning: false,
|
||||
autoscroll: true
|
||||
@@ -123,7 +125,21 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public onSessionStopped(notification: sqlops.ProfilerSessionStoppedParams) {
|
||||
this._notificationService.error(nls.localize("profiler.sessionStopped", "XEvent Profiler Session stopped unexpectedly on the server {0}.", this._connection.serverName));
|
||||
|
||||
this.state.change({
|
||||
isStopped: true,
|
||||
isPaused: false,
|
||||
isRunning: false
|
||||
});
|
||||
}
|
||||
|
||||
public onMoreRows(eventMessage: sqlops.ProfilerSessionEvents) {
|
||||
if (eventMessage.eventsLost){
|
||||
this._notificationService.warn(nls.localize("profiler.eventsLost", "The XEvent Profiler session for {0} has lost events.", this._connection.serverName));
|
||||
}
|
||||
|
||||
for (let i: number = 0; i < eventMessage.events.length && i < 500; ++i) {
|
||||
let e: sqlops.ProfilerEvent = eventMessage.events[i];
|
||||
let data = {};
|
||||
|
||||
@@ -25,6 +25,10 @@ export interface IProfilerSession {
|
||||
* Called by the service when more rows are available to render
|
||||
*/
|
||||
onMoreRows(events: sqlops.ProfilerSessionEvents);
|
||||
/**
|
||||
* Called by the service when the session is closed unexpectedly
|
||||
*/
|
||||
onSessionStopped(events: sqlops.ProfilerSessionStoppedParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,6 +69,10 @@ export interface IProfilerService {
|
||||
* The method called by the service provider for when more rows are available to render
|
||||
*/
|
||||
onMoreRows(params: sqlops.ProfilerSessionEvents): void;
|
||||
/**
|
||||
* The method called by the service provider for when more rows are available to render
|
||||
*/
|
||||
onSessionStopped(params: sqlops.ProfilerSessionStoppedParams): void;
|
||||
/**
|
||||
* Gets a list of the session templates that are specified in the settings
|
||||
* @param provider An optional string to limit the session template to a specific
|
||||
|
||||
@@ -82,6 +82,11 @@ export class ProfilerService implements IProfilerService {
|
||||
this._sessionMap.get(this._idMap.reverseGet(params.sessionId)).onMoreRows(params);
|
||||
}
|
||||
|
||||
public onSessionStopped(params: sqlops.ProfilerSessionStoppedParams): void {
|
||||
|
||||
this._sessionMap.get(this._idMap.reverseGet(params.ownerUri)).onSessionStopped(params);
|
||||
}
|
||||
|
||||
public connectSession(id: ProfilerSessionID): Thenable<boolean> {
|
||||
return this._runAction(id, provider => provider.connectSession(this._idMap.get(id)));
|
||||
}
|
||||
|
||||
@@ -48,6 +48,10 @@ export class ProfilerTestBackend implements sqlops.ProfilerProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
registerOnSessionStopped(handler: (response: sqlops.ProfilerSessionStoppedParams) => any) {
|
||||
return;
|
||||
}
|
||||
|
||||
private intervalFn(guid: string): number {
|
||||
return setTimeout(() => {
|
||||
let data = this.testData[this.index++];
|
||||
|
||||
@@ -448,7 +448,8 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
|
||||
this._dropdown = new Dropdown(this.$databaseListDropdown.getHTMLElement(), contextViewProvider, themeService, {
|
||||
strictSelection: true,
|
||||
placeholder: selectString,
|
||||
ariaLabel: selectString
|
||||
ariaLabel: selectString,
|
||||
actionLabel: nls.localize('toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
|
||||
});
|
||||
this._dropdown.onValueChange(s => this.databaseSelected(s));
|
||||
|
||||
|
||||
@@ -13,13 +13,14 @@ import { ChartsModule } from 'ng2-charts/ng2-charts';
|
||||
|
||||
const BrowserAnimationsModule = (<any>require.__$__nodeRequire('@angular/platform-browser/animations')).BrowserAnimationsModule;
|
||||
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
|
||||
import { QueryOutputComponent, QUERY_OUTPUT_SELECTOR } from 'sql/parts/query/views/queryOutput.component';
|
||||
import { QueryOutputComponent } from 'sql/parts/query/views/queryOutput.component';
|
||||
import { QueryPlanComponent, } from 'sql/parts/queryPlan/queryPlan.component';
|
||||
import { QueryComponent } from 'sql/parts/grid/views/query/query.component';
|
||||
import { TopOperationsComponent } from 'sql/parts/queryPlan/topOperations.component';
|
||||
@@ -41,7 +42,7 @@ let baseComponents = [QueryComponent, ComponentHostDirective, QueryOutputCompone
|
||||
/* Insights */
|
||||
let insightComponents = Registry.as<IInsightRegistry>(Extensions.InsightContribution).getAllCtors();
|
||||
|
||||
export const QueryOutputModule = (params: IBootstrapParams, selector: string): Type<any> => {
|
||||
export const QueryOutputModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -67,19 +68,22 @@ export const QueryOutputModule = (params: IBootstrapParams, selector: string): T
|
||||
...insightComponents
|
||||
],
|
||||
providers: [
|
||||
{ provide: IBootstrapParams, useValue: params }
|
||||
{ provide: IBootstrapParams, useValue: params },
|
||||
{ provide: ISelector, useValue: selector },
|
||||
...providerIterator(instantiationService)
|
||||
]
|
||||
})
|
||||
class ModuleClass {
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
||||
@Inject(ISelector) private selector: string
|
||||
) {
|
||||
}
|
||||
|
||||
ngDoBootstrap(appRef: ApplicationRef) {
|
||||
const factory = this._resolver.resolveComponentFactory(QueryOutputComponent);
|
||||
(<any>factory).factory.selector = selector;
|
||||
(<any>factory).factory.selector = this.selector;
|
||||
appRef.bootstrap(factory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
import { NgModule, Inject, forwardRef, ApplicationRef, ComponentFactoryResolver, Type } from '@angular/core';
|
||||
import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { QueryPlanComponent, QUERYPLAN_SELECTOR } from 'sql/parts/queryPlan/queryPlan.component';
|
||||
|
||||
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { QueryPlanComponent } from 'sql/parts/queryPlan/queryPlan.component';
|
||||
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
// Connection Dashboard main angular module
|
||||
export const QueryPlanModule = (params: IBootstrapParams, selector: string): Type<any> => {
|
||||
export const QueryPlanModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -23,19 +26,22 @@ export const QueryPlanModule = (params: IBootstrapParams, selector: string): Typ
|
||||
],
|
||||
providers: [
|
||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
||||
{ provide: IBootstrapParams, useValue: params }
|
||||
{ provide: IBootstrapParams, useValue: params },
|
||||
{ provide: ISelector, useValue: selector },
|
||||
...providerIterator(instantiationService)
|
||||
]
|
||||
})
|
||||
class ModuleClass {
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
||||
@Inject(ISelector) private selector: string
|
||||
) {
|
||||
}
|
||||
|
||||
ngDoBootstrap(appRef: ApplicationRef) {
|
||||
const factory = this._resolver.resolveComponentFactory(QueryPlanComponent);
|
||||
(<any>factory).factory.selector = selector;
|
||||
(<any>factory).factory.selector = this.selector;
|
||||
appRef.bootstrap(factory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,11 @@ import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { TaskDialogComponent, TASKDIALOG_SELECTOR } from 'sql/parts/tasks/dialog/taskDialog.component';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
import { TaskDialogComponent } from 'sql/parts/tasks/dialog/taskDialog.component';
|
||||
import { CreateDatabaseComponent } from 'sql/parts/admin/database/create/createDatabase.component';
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
||||
|
||||
// Setup routes for various child components
|
||||
const appRoutes: Routes = [
|
||||
@@ -27,7 +29,7 @@ const appRoutes: Routes = [
|
||||
{ path: '**', component: CreateDatabaseComponent }
|
||||
];
|
||||
|
||||
export const TaskDialogModule = (params: IBootstrapParams, selector: string): Type<any> => {
|
||||
export const TaskDialogModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
|
||||
@NgModule({
|
||||
declarations: [
|
||||
TaskDialogComponent,
|
||||
@@ -42,19 +44,22 @@ export const TaskDialogModule = (params: IBootstrapParams, selector: string): Ty
|
||||
],
|
||||
providers: [
|
||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
||||
{ provide: IBootstrapParams, useValue: params }
|
||||
{ provide: IBootstrapParams, useValue: params },
|
||||
{ provide: ISelector, useValue: selector },
|
||||
...providerIterator(instantiationService)
|
||||
]
|
||||
})
|
||||
class ModuleClass {
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
||||
@Inject(ISelector) private selector: string
|
||||
) {
|
||||
}
|
||||
|
||||
ngDoBootstrap(appRef: ApplicationRef) {
|
||||
const factory = this._resolver.resolveComponentFactory(TaskDialogComponent);
|
||||
(<any>factory).factory.selector = selector;
|
||||
(<any>factory).factory.selector = this.selector;
|
||||
appRef.bootstrap(factory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import { Dialog, Wizard, DialogTab } from 'sql/platform/dialog/dialogTypes';
|
||||
import { IModalOptions } from 'sql/base/browser/ui/modal/modal';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
const defaultOptions: IModalOptions = { hasBackButton: false, isWide: false };
|
||||
const defaultWizardOptions: IModalOptions = { hasBackButton: false, isWide: true };
|
||||
const defaultOptions: IModalOptions = { hasBackButton: false, isWide: false, hasErrors: true };
|
||||
const defaultWizardOptions: IModalOptions = { hasBackButton: false, isWide: true, hasErrors: true };
|
||||
|
||||
export class CustomDialogService {
|
||||
private _dialogModals = new Map<Dialog, DialogModal>();
|
||||
|
||||
@@ -11,22 +11,25 @@ import { forwardRef, NgModule, ComponentFactoryResolver, Inject, ApplicationRef
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule, APP_BASE_HREF } from '@angular/common';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { DialogContainer } from 'sql/platform/dialog/dialogContainer.component';
|
||||
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||
import { ModelViewContent } from 'sql/parts/modelComponents/modelViewContent.component';
|
||||
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
|
||||
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
|
||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
||||
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
/* Model-backed components */
|
||||
let extensionComponents = Registry.as<IComponentRegistry>(Extensions.ComponentContribution).getAllCtors();
|
||||
|
||||
export const DialogModule = (params, selector: string): any => {
|
||||
export const DialogModule = (params, selector: string, instantiationService: IInstantiationService): any => {
|
||||
@NgModule({
|
||||
declarations: [
|
||||
Checkbox,
|
||||
@@ -47,20 +50,22 @@ export const DialogModule = (params, selector: string): any => {
|
||||
providers: [
|
||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
||||
CommonServiceInterface,
|
||||
{ provide: IBootstrapParams, useValue: params }
|
||||
{ provide: IBootstrapParams, useValue: params },
|
||||
{ provide: ISelector, useValue: selector },
|
||||
...providerIterator(instantiationService)
|
||||
]
|
||||
})
|
||||
class ModuleClass {
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) bootstrap: CommonServiceInterface,
|
||||
@Inject(ISelector) private selector: string
|
||||
) {
|
||||
}
|
||||
|
||||
ngDoBootstrap(appRef: ApplicationRef) {
|
||||
const factoryWrapper: any = this._resolver.resolveComponentFactory(DialogContainer);
|
||||
factoryWrapper.factory.selector = selector;
|
||||
factoryWrapper.factory.selector = this.selector;
|
||||
appRef.bootstrap(factoryWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { localize } from 'vs/nls';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { DialogMessage, MessageLevel } from '../../workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
export class DialogModal extends Modal {
|
||||
private _dialogPane: DialogPane;
|
||||
@@ -52,7 +53,7 @@ export class DialogModal extends Modal {
|
||||
}
|
||||
|
||||
public render() {
|
||||
super.render();
|
||||
super.render(true);
|
||||
attachModalDialogStyler(this, this._themeService);
|
||||
|
||||
if (this.backButton) {
|
||||
@@ -67,29 +68,43 @@ export class DialogModal extends Modal {
|
||||
});
|
||||
}
|
||||
|
||||
this._doneButton = this.addDialogButton(this._dialog.okButton, () => this.done(), false);
|
||||
this._doneButton = this.addDialogButton(this._dialog.okButton, () => this.done(), false, true);
|
||||
this._dialog.okButton.registerClickEvent(this._onDone.event);
|
||||
this._dialog.onValidityChanged(valid => {
|
||||
this._doneButton.enabled = valid && this._dialog.okButton.enabled;
|
||||
});
|
||||
this._cancelButton = this.addDialogButton(this._dialog.cancelButton, () => this.cancel(), false);
|
||||
this._dialog.cancelButton.registerClickEvent(this._onCancel.event);
|
||||
|
||||
let messageChangeHandler = (message: DialogMessage) => {
|
||||
if (message && message.text) {
|
||||
this.setError(message.text, message.level);
|
||||
} else {
|
||||
this.setError('');
|
||||
}
|
||||
};
|
||||
|
||||
messageChangeHandler(this._dialog.message);
|
||||
this._dialog.onMessageChange(message => messageChangeHandler(message));
|
||||
}
|
||||
|
||||
private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined, registerClickEvent: boolean = true): Button {
|
||||
private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined, registerClickEvent: boolean = true, requireDialogValid: boolean = false): Button {
|
||||
let buttonElement = this.addFooterButton(button.label, onSelect);
|
||||
buttonElement.enabled = button.enabled;
|
||||
if (registerClickEvent) {
|
||||
button.registerClickEvent(buttonElement.onDidClick);
|
||||
}
|
||||
button.onUpdate(() => {
|
||||
this.updateButtonElement(buttonElement, button);
|
||||
this.updateButtonElement(buttonElement, button, requireDialogValid);
|
||||
});
|
||||
attachButtonStyler(buttonElement, this._themeService);
|
||||
this.updateButtonElement(buttonElement, button);
|
||||
this.updateButtonElement(buttonElement, button, requireDialogValid);
|
||||
return buttonElement;
|
||||
}
|
||||
|
||||
private updateButtonElement(buttonElement: Button, dialogButton: DialogButton) {
|
||||
private updateButtonElement(buttonElement: Button, dialogButton: DialogButton, requireDialogValid: boolean = false) {
|
||||
buttonElement.label = dialogButton.label;
|
||||
buttonElement.enabled = dialogButton.enabled;
|
||||
buttonElement.enabled = requireDialogValid ? dialogButton.enabled && this._dialog.valid : dialogButton.enabled;
|
||||
dialogButton.hidden ? buttonElement.element.classList.add('dialogModal-hidden') : buttonElement.element.classList.remove('dialogModal-hidden');
|
||||
}
|
||||
|
||||
@@ -108,11 +123,13 @@ export class DialogModal extends Modal {
|
||||
this.show();
|
||||
}
|
||||
|
||||
public done(): void {
|
||||
public async done(): Promise<void> {
|
||||
if (this._dialog.okButton.enabled) {
|
||||
this._onDone.fire();
|
||||
this.dispose();
|
||||
this.hide();
|
||||
if (await this._dialog.validateClose()) {
|
||||
this._onDone.fire();
|
||||
this.dispose();
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/pa
|
||||
import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { DialogModule } from 'sql/platform/dialog/dialog.module';
|
||||
import { DialogComponentParams } from 'sql/platform/dialog/dialogContainer.component';
|
||||
import { DialogMessage } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { Builder } from 'vs/base/browser/builder';
|
||||
@@ -67,6 +68,7 @@ export class DialogPane extends Disposable implements IThemable {
|
||||
this._body.appendChild(tabContainer);
|
||||
this.initializeModelViewContainer(tabContainer, tab.content, tab);
|
||||
this._tabbedPanel.onTabChange(e => {
|
||||
tabContainer.style.height = (this.getTabDimension().height - this._tabbedPanel.headersize) + 'px';
|
||||
this._onTabChange.fire(tab.content);
|
||||
});
|
||||
this._tabbedPanel.pushTab({
|
||||
@@ -91,7 +93,7 @@ export class DialogPane extends Disposable implements IThemable {
|
||||
}
|
||||
|
||||
private getTabDimension(): DOM.Dimension {
|
||||
return new DOM.Dimension(DOM.getContentWidth(this._body), DOM.getContentHeight(this._body))
|
||||
return new DOM.Dimension(DOM.getContentWidth(this._body), DOM.getContentHeight(this._body));
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import * as sqlops from 'sqlops';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { DialogMessage } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
export class ModelViewPane {
|
||||
private _valid: boolean = true;
|
||||
@@ -45,6 +46,10 @@ export class Dialog extends ModelViewPane {
|
||||
public okButton: DialogButton = new DialogButton(Dialog.DONE_BUTTON_LABEL, true);
|
||||
public cancelButton: DialogButton = new DialogButton(Dialog.CANCEL_BUTTON_LABEL, true);
|
||||
public customButtons: DialogButton[];
|
||||
private _onMessageChange = new Emitter<DialogMessage>();
|
||||
public readonly onMessageChange = this._onMessageChange.event;
|
||||
private _message: DialogMessage;
|
||||
private _closeValidator: () => boolean | Thenable<boolean>;
|
||||
|
||||
constructor(public title: string, content?: string | DialogTab[]) {
|
||||
super();
|
||||
@@ -52,6 +57,29 @@ export class Dialog extends ModelViewPane {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
|
||||
public get message(): DialogMessage {
|
||||
return this._message;
|
||||
}
|
||||
|
||||
public set message(value: DialogMessage) {
|
||||
if (this._message && !value || !this._message && value || this._message && value && (this._message.level !== value.level || this._message.text !== value.text)) {
|
||||
this._message = value;
|
||||
this._onMessageChange.fire(this._message);
|
||||
}
|
||||
}
|
||||
|
||||
public registerCloseValidator(validator: () => boolean | Thenable<boolean>): void {
|
||||
this._closeValidator = validator;
|
||||
}
|
||||
|
||||
public validateClose(): Thenable<boolean> {
|
||||
if (this._closeValidator) {
|
||||
return Promise.resolve(this._closeValidator());
|
||||
} else {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class DialogButton implements sqlops.window.modelviewdialog.Button {
|
||||
@@ -140,6 +168,9 @@ export class Wizard {
|
||||
private _pageRemovedEmitter = new Emitter<WizardPage>();
|
||||
public readonly onPageRemoved = this._pageRemovedEmitter.event;
|
||||
private _navigationValidator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>;
|
||||
private _onMessageChange = new Emitter<DialogMessage>();
|
||||
public readonly onMessageChange = this._onMessageChange.event;
|
||||
private _message: DialogMessage;
|
||||
|
||||
constructor(public title: string) { }
|
||||
|
||||
@@ -207,4 +238,15 @@ export class Wizard {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
public get message(): DialogMessage {
|
||||
return this._message;
|
||||
}
|
||||
|
||||
public set message(value: DialogMessage) {
|
||||
if (this._message && !value || !this._message && value || this._message && value && (this._message.level !== value.level || this._message.text !== value.text)) {
|
||||
this._message = value;
|
||||
this._onMessageChange.fire(this._message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
.dialogModal-body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-width: 500px;
|
||||
@@ -29,4 +29,4 @@
|
||||
|
||||
.footer-button.dialogModal-hidden {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { localize } from 'vs/nls';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { DialogMessage, MessageLevel } from '../../workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
export class WizardModal extends Modal {
|
||||
private _dialogPanes = new Map<WizardPage, DialogPane>();
|
||||
@@ -58,7 +59,7 @@ export class WizardModal extends Modal {
|
||||
}
|
||||
|
||||
public render() {
|
||||
super.render();
|
||||
super.render(true);
|
||||
attachModalDialogStyler(this, this._themeService);
|
||||
|
||||
if (this.backButton) {
|
||||
@@ -66,32 +67,59 @@ export class WizardModal extends Modal {
|
||||
attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND });
|
||||
}
|
||||
|
||||
this._previousButton = this.addDialogButton(this._wizard.backButton, () => this.showPage(this.getCurrentPage() - 1));
|
||||
this._nextButton = this.addDialogButton(this._wizard.nextButton, () => this.showPage(this.getCurrentPage() + 1));
|
||||
if (this._wizard.customButtons) {
|
||||
this._wizard.customButtons.forEach(button => {
|
||||
let buttonElement = this.addDialogButton(button);
|
||||
this.updateButtonElement(buttonElement, button);
|
||||
});
|
||||
}
|
||||
|
||||
this._previousButton = this.addDialogButton(this._wizard.backButton, () => this.showPage(this._wizard.currentPage - 1));
|
||||
this._nextButton = this.addDialogButton(this._wizard.nextButton, () => this.showPage(this._wizard.currentPage + 1), true, true);
|
||||
this._generateScriptButton = this.addDialogButton(this._wizard.generateScriptButton, () => undefined);
|
||||
this._doneButton = this.addDialogButton(this._wizard.doneButton, () => this.done(), false);
|
||||
this._doneButton = this.addDialogButton(this._wizard.doneButton, () => this.done(), false, true);
|
||||
this._wizard.doneButton.registerClickEvent(this._onDone.event);
|
||||
this._cancelButton = this.addDialogButton(this._wizard.cancelButton, () => this.cancel(), false);
|
||||
this._wizard.cancelButton.registerClickEvent(this._onCancel.event);
|
||||
|
||||
let messageChangeHandler = (message: DialogMessage) => {
|
||||
if (message && message.text) {
|
||||
this.setError(message.text, message.level);
|
||||
} else {
|
||||
this.setError('');
|
||||
}
|
||||
};
|
||||
|
||||
messageChangeHandler(this._wizard.message);
|
||||
this._wizard.onMessageChange(message => messageChangeHandler(message));
|
||||
|
||||
this._wizard.pages.forEach((page, index) => {
|
||||
page.onValidityChanged(valid => {
|
||||
if (index === this._wizard.currentPage) {
|
||||
this._nextButton.enabled = this._wizard.nextButton.enabled && page.valid;
|
||||
this._doneButton.enabled = this._wizard.doneButton.enabled && page.valid;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined, registerClickEvent: boolean = true): Button {
|
||||
private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined, registerClickEvent: boolean = true, requirePageValid: boolean = false): Button {
|
||||
let buttonElement = this.addFooterButton(button.label, onSelect);
|
||||
buttonElement.enabled = button.enabled;
|
||||
if (registerClickEvent) {
|
||||
button.registerClickEvent(buttonElement.onDidClick);
|
||||
}
|
||||
button.onUpdate(() => {
|
||||
this.updateButtonElement(buttonElement, button);
|
||||
this.updateButtonElement(buttonElement, button, requirePageValid);
|
||||
});
|
||||
attachButtonStyler(buttonElement, this._themeService);
|
||||
this.updateButtonElement(buttonElement, button);
|
||||
this.updateButtonElement(buttonElement, button, requirePageValid);
|
||||
return buttonElement;
|
||||
}
|
||||
|
||||
private updateButtonElement(buttonElement: Button, dialogButton: DialogButton) {
|
||||
private updateButtonElement(buttonElement: Button, dialogButton: DialogButton, requirePageValid: boolean = false) {
|
||||
buttonElement.label = dialogButton.label;
|
||||
buttonElement.enabled = dialogButton.enabled;
|
||||
buttonElement.enabled = requirePageValid ? dialogButton.enabled && this._wizard.pages[this._wizard.currentPage].valid : dialogButton.enabled;
|
||||
dialogButton.hidden ? buttonElement.element.classList.add('dialogModal-hidden') : buttonElement.element.classList.remove('dialogModal-hidden');
|
||||
}
|
||||
|
||||
@@ -100,18 +128,17 @@ export class WizardModal extends Modal {
|
||||
this._body = bodyBuilder.getHTMLElement();
|
||||
});
|
||||
|
||||
let builder = new Builder(this._body);
|
||||
this._wizard.pages.forEach(page => {
|
||||
this.registerPage(page);
|
||||
});
|
||||
this._wizard.onPageAdded(page => {
|
||||
this.registerPage(page);
|
||||
this.showPage(this.getCurrentPage(), false);
|
||||
this.showPage(this._wizard.currentPage, false);
|
||||
});
|
||||
this._wizard.onPageRemoved(page => {
|
||||
let dialogPane = this._dialogPanes.get(page);
|
||||
this._dialogPanes.delete(page);
|
||||
this.showPage(this.getCurrentPage(), false);
|
||||
this.showPage(this._wizard.currentPage, false);
|
||||
dialogPane.dispose();
|
||||
});
|
||||
}
|
||||
@@ -141,6 +168,9 @@ export class WizardModal extends Modal {
|
||||
});
|
||||
this.setButtonsForPage(index);
|
||||
this._wizard.setCurrentPage(index);
|
||||
let currentPageValid = this._wizard.pages[this._wizard.currentPage].valid;
|
||||
this._nextButton.enabled = this._wizard.nextButton.enabled && currentPageValid;
|
||||
this._doneButton.enabled = this._wizard.doneButton.enabled && currentPageValid;
|
||||
}
|
||||
|
||||
private setButtonsForPage(index: number) {
|
||||
@@ -161,10 +191,6 @@ export class WizardModal extends Modal {
|
||||
}
|
||||
}
|
||||
|
||||
private getCurrentPage(): number {
|
||||
return this._wizard.currentPage;
|
||||
}
|
||||
|
||||
public open(): void {
|
||||
this.showPage(0, false);
|
||||
this.show();
|
||||
|
||||
@@ -3,20 +3,31 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { NgModuleRef, enableProdMode, InjectionToken, ReflectiveInjector, Type, PlatformRef, Provider } from '@angular/core';
|
||||
import { NgModuleRef, enableProdMode, InjectionToken, Type, PlatformRef, Provider, Injector, Optional, Inject, ComponentFactoryResolver } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService, _util } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
const selectorCounter = new Map<string, number>();
|
||||
const serviceMap = new Map<string, IInstantiationService>();
|
||||
|
||||
export const IBootstrapParams = new InjectionToken('bootstrap_params');
|
||||
export function providerIterator(service: IInstantiationService): Provider[] {
|
||||
return Array.from(_util.serviceIds.values()).map(v => {
|
||||
return {
|
||||
provide: v, useFactory: () => {
|
||||
return (<any>service)._getOrCreateServiceInstance(v);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export const ISelector = new InjectionToken<string>('selector');
|
||||
|
||||
export const IBootstrapParams = new InjectionToken<IBootstrapParams>('bootstrap_params');
|
||||
export interface IBootstrapParams {
|
||||
}
|
||||
|
||||
export type IModuleFactory<T> = (params: IBootstrapParams, selector: string) => Type<T>;
|
||||
export type IModuleFactory<T> = (params: IBootstrapParams, selector: string, service: IInstantiationService) => Type<T>;
|
||||
|
||||
function createUniqueSelector(selector: string): string {
|
||||
let num: number;
|
||||
@@ -37,31 +48,13 @@ export function bootstrapAngular<T>(service: IInstantiationService, moduleType:
|
||||
let selector = document.createElement(uniqueSelectorString);
|
||||
container.appendChild(selector);
|
||||
|
||||
serviceMap.set(uniqueSelectorString, service);
|
||||
|
||||
if (!platform) {
|
||||
// Perform the bootsrap
|
||||
|
||||
const providers: Provider = [];
|
||||
|
||||
_util.serviceIds.forEach(id => {
|
||||
providers.push({
|
||||
provide: id, useFactory: () => {
|
||||
return (<any>serviceMap.get(uniqueSelectorString))._getOrCreateServiceInstance(id);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
platform = platformBrowserDynamic(providers);
|
||||
platform = platformBrowserDynamic();
|
||||
}
|
||||
|
||||
platform.bootstrapModule(moduleType(params, uniqueSelectorString)).then(moduleRef => {
|
||||
platform.bootstrapModule(moduleType(params, uniqueSelectorString, service)).then(moduleRef => {
|
||||
if (input) {
|
||||
input.onDispose(() => {
|
||||
serviceMap.delete(uniqueSelectorString);
|
||||
moduleRef.onDestroy(() => {
|
||||
serviceMap.delete(uniqueSelectorString);
|
||||
});
|
||||
moduleRef.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -95,7 +95,6 @@ export class SingleQueryManagementService {
|
||||
*/
|
||||
@Injectable()
|
||||
export class CommonServiceInterface extends AngularDisposable {
|
||||
protected _uniqueSelector: string;
|
||||
protected _uri: string;
|
||||
|
||||
/* Special Services */
|
||||
@@ -115,6 +114,12 @@ export class CommonServiceInterface extends AngularDisposable {
|
||||
@Inject(IQueryManagementService) protected _queryManagementService: IQueryManagementService
|
||||
) {
|
||||
super();
|
||||
// during testing there may not be params
|
||||
if (this._params) {
|
||||
this.scopedContextKeyService = this._params.scopedContextService;
|
||||
this._connectionContextKey = this._params.connectionContextKey;
|
||||
this.uri = this._params.ownerUri;
|
||||
}
|
||||
}
|
||||
|
||||
public get metadataService(): SingleConnectionMetadataService {
|
||||
@@ -133,20 +138,6 @@ export class CommonServiceInterface extends AngularDisposable {
|
||||
return this._singleQueryManagementService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selector for this instance, should only be set once
|
||||
*/
|
||||
public set selector(selector: string) {
|
||||
this._uniqueSelector = selector;
|
||||
this._getbootstrapParams();
|
||||
}
|
||||
|
||||
protected _getbootstrapParams(): void {
|
||||
this.scopedContextKeyService = this._params.scopedContextService;
|
||||
this._connectionContextKey = this._params.connectionContextKey;
|
||||
this.uri = this._params.ownerUri;
|
||||
}
|
||||
|
||||
protected setUri(uri: string) {
|
||||
this._uri = uri;
|
||||
this._singleMetadataService = new SingleConnectionMetadataService(this._metadataService, this._uri);
|
||||
|
||||
10
src/sql/sqlops.d.ts
vendored
10
src/sql/sqlops.d.ts
vendored
@@ -1409,6 +1409,7 @@ declare module 'sqlops' {
|
||||
disconnectSession(sessionId: string): Thenable<boolean>;
|
||||
|
||||
registerOnSessionEventsAvailable(handler: (response: ProfilerSessionEvents) => any): void;
|
||||
registerOnSessionStopped(handler: (response: ProfilerSessionStoppedParams) => any): void;
|
||||
}
|
||||
|
||||
export interface IProfilerTableRow {
|
||||
@@ -1449,6 +1450,15 @@ declare module 'sqlops' {
|
||||
sessionId: string;
|
||||
|
||||
events: ProfilerEvent[];
|
||||
|
||||
eventsLost: boolean;
|
||||
}
|
||||
|
||||
export interface ProfilerSessionStoppedParams {
|
||||
|
||||
ownerUri: string;
|
||||
|
||||
sessionId: number;
|
||||
}
|
||||
|
||||
// File browser interfaces -----------------------------------------------------------------------
|
||||
|
||||
83
src/sql/sqlops.proposed.d.ts
vendored
83
src/sql/sqlops.proposed.d.ts
vendored
@@ -216,6 +216,11 @@ declare module 'sqlops' {
|
||||
* Container Width
|
||||
*/
|
||||
width?: number | string;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
textAlign?: string
|
||||
}
|
||||
|
||||
export interface FlexItemLayout {
|
||||
@@ -232,12 +237,17 @@ declare module 'sqlops' {
|
||||
|
||||
export interface FormItemLayout {
|
||||
horizontal?: boolean;
|
||||
componentWidth?: number;
|
||||
componentWidth?: number | string;
|
||||
componentHeight?: number | string;
|
||||
titleFontSize?: number | string;
|
||||
required?: boolean;
|
||||
info?: string;
|
||||
}
|
||||
|
||||
export interface FormLayout {
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
padding?: string;
|
||||
}
|
||||
|
||||
export interface GroupLayout {
|
||||
@@ -290,22 +300,35 @@ declare module 'sqlops' {
|
||||
Error = 3
|
||||
}
|
||||
|
||||
export enum CardType {
|
||||
VerticalButton = 'VerticalButton',
|
||||
Details = 'Details'
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties representing the card component, can be used
|
||||
* when using ModelBuilder to create the component
|
||||
*/
|
||||
export interface CardProperties {
|
||||
export interface CardProperties extends ComponentWithIcon {
|
||||
label: string;
|
||||
value?: string;
|
||||
actions?: ActionDescriptor[];
|
||||
status?: StatusIndicator;
|
||||
selected?: boolean;
|
||||
cardType: CardType;
|
||||
}
|
||||
|
||||
export type InputBoxInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'range' | 'search' | 'text' | 'time' | 'url' | 'week';
|
||||
|
||||
export interface ComponentProperties {
|
||||
height: number | string;
|
||||
width: number | string;
|
||||
height?: number | string;
|
||||
width?: number | string;
|
||||
}
|
||||
|
||||
export interface ComponentWithIcon {
|
||||
iconPath?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
|
||||
iconHeight?: number | string;
|
||||
iconWidth?: number | string;
|
||||
}
|
||||
|
||||
export interface InputBoxProperties extends ComponentProperties {
|
||||
@@ -354,7 +377,7 @@ declare module 'sqlops' {
|
||||
}
|
||||
|
||||
export interface DropDownProperties extends ComponentProperties {
|
||||
value?: string;
|
||||
value?: string | CategoryValue;
|
||||
values?: string[] | CategoryValue[];
|
||||
editable?: boolean;
|
||||
}
|
||||
@@ -383,20 +406,17 @@ declare module 'sqlops' {
|
||||
html?: string;
|
||||
}
|
||||
|
||||
export interface ButtonProperties extends ComponentProperties {
|
||||
export interface ButtonProperties extends ComponentProperties, ComponentWithIcon {
|
||||
label?: string;
|
||||
iconPath?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
|
||||
}
|
||||
|
||||
export interface LoadingComponentProperties {
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
export interface CardComponent extends Component {
|
||||
label: string;
|
||||
value: string;
|
||||
actions?: ActionDescriptor[];
|
||||
export interface CardComponent extends Component, CardProperties {
|
||||
onDidActionClick: vscode.Event<ActionDescriptor>;
|
||||
onCardSelectedChanged: vscode.Event<any>;
|
||||
}
|
||||
|
||||
export interface TextComponent extends Component {
|
||||
@@ -418,7 +438,7 @@ declare module 'sqlops' {
|
||||
}
|
||||
|
||||
export interface DropDownComponent extends Component, DropDownProperties {
|
||||
value: string;
|
||||
value: string | CategoryValue;
|
||||
values: string[] | CategoryValue[];
|
||||
onValueChanged: vscode.Event<any>;
|
||||
}
|
||||
@@ -576,6 +596,24 @@ declare module 'sqlops' {
|
||||
*/
|
||||
export function createWizard(title: string): Wizard;
|
||||
|
||||
/**
|
||||
* Used to control whether a message in a dialog/wizard is displayed as an error,
|
||||
* warning, or informational message. Default is error.
|
||||
*/
|
||||
export enum MessageLevel {
|
||||
Error = 0,
|
||||
Warning = 1,
|
||||
Information = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* A message shown in a dialog. If the level is not set it defaults to error.
|
||||
*/
|
||||
export type DialogMessage = {
|
||||
readonly text: string,
|
||||
readonly level?: MessageLevel
|
||||
};
|
||||
|
||||
export interface ModelViewPanel {
|
||||
/**
|
||||
* Register model view content for the dialog.
|
||||
@@ -626,6 +664,21 @@ declare module 'sqlops' {
|
||||
* Any additional buttons that should be displayed
|
||||
*/
|
||||
customButtons: Button[];
|
||||
|
||||
/**
|
||||
* Set the informational message shown in the dialog. Hidden when the message is
|
||||
* undefined or the text is empty or undefined. The default level is error.
|
||||
*/
|
||||
message: DialogMessage;
|
||||
|
||||
/**
|
||||
* Register a callback that will be called when the user tries to click done. Only
|
||||
* one callback can be registered at once, so each registration call will clear
|
||||
* the previous registration.
|
||||
* @param validator The callback that gets executed when the user tries to click
|
||||
* done. Return true to allow the dialog to close or false to block it from closing
|
||||
*/
|
||||
registerCloseValidator(validator: () => boolean | Thenable<boolean>): void;
|
||||
}
|
||||
|
||||
export interface DialogTab extends ModelViewPanel {
|
||||
@@ -792,6 +845,12 @@ declare module 'sqlops' {
|
||||
* cancel it.
|
||||
*/
|
||||
registerNavigationValidator(validator: (pageChangeInfo: WizardPageChangeInfo) => boolean | Thenable<boolean>): void;
|
||||
|
||||
/**
|
||||
* Set the informational message shown in the wizard. Hidden when the message is
|
||||
* undefined or the text is empty or undefined. The default level is error.
|
||||
*/
|
||||
message: DialogMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ export interface IModelViewDialogDetails {
|
||||
okButton: number;
|
||||
cancelButton: number;
|
||||
customButtons: number[];
|
||||
message: DialogMessage;
|
||||
}
|
||||
|
||||
export interface IModelViewTabDetails {
|
||||
@@ -179,6 +180,18 @@ export interface IModelViewWizardDetails {
|
||||
nextButton: number;
|
||||
backButton: number;
|
||||
customButtons: number[];
|
||||
message: DialogMessage;
|
||||
}
|
||||
|
||||
export enum MessageLevel {
|
||||
Error = 0,
|
||||
Warning = 1,
|
||||
Information = 2
|
||||
}
|
||||
|
||||
export interface DialogMessage {
|
||||
text: string;
|
||||
level?: MessageLevel;
|
||||
}
|
||||
|
||||
/// Card-related APIs that need to be here to avoid early load issues
|
||||
@@ -195,6 +208,8 @@ export interface CardProperties {
|
||||
value?: string;
|
||||
actions?: ActionDescriptor[];
|
||||
status?: StatusIndicator;
|
||||
selected?: boolean;
|
||||
cardType: CardType;
|
||||
}
|
||||
|
||||
export interface ActionDescriptor {
|
||||
@@ -223,4 +238,9 @@ export enum DeclarativeDataType {
|
||||
string = 'string',
|
||||
category = 'category',
|
||||
boolean = 'boolean'
|
||||
}
|
||||
|
||||
export enum CardType {
|
||||
VerticalButton = 'VerticalButton',
|
||||
Details = 'Details'
|
||||
}
|
||||
@@ -518,6 +518,13 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
|
||||
this._proxy.$onSessionEventsAvailable(handle, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Profiler session stopped unexpectedly notification
|
||||
*/
|
||||
public $onSessionStopped(handle: number, response: sqlops.ProfilerSessionStoppedParams): void {
|
||||
this._proxy.$onSessionStopped(handle, response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Agent Job Provider methods
|
||||
|
||||
@@ -15,7 +15,7 @@ import * as vscode from 'vscode';
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
import { SqlMainContext, ExtHostModelViewShape, MainThreadModelViewShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||
import { IItemConfig, ModelComponentTypes, IComponentShape, IComponentEventArgs, ComponentEventType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { IItemConfig, ModelComponentTypes, IComponentShape, IComponentEventArgs, ComponentEventType, CardType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
class ModelBuilderImpl implements sqlops.ModelBuilder {
|
||||
private nextComponentId: number;
|
||||
@@ -255,6 +255,9 @@ class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sq
|
||||
|
||||
private convertToItemConfig(formComponent: sqlops.FormComponent, itemLayout?: sqlops.FormItemLayout): InternalItemConfig {
|
||||
let componentWrapper = formComponent.component as ComponentWrapper;
|
||||
if (itemLayout && itemLayout.required && componentWrapper) {
|
||||
componentWrapper.required = true;
|
||||
}
|
||||
let actions: string[] = undefined;
|
||||
if (formComponent.actions) {
|
||||
actions = formComponent.actions.map(action => {
|
||||
@@ -377,7 +380,8 @@ class ComponentWrapper implements sqlops.Component {
|
||||
}
|
||||
|
||||
public get enabled(): boolean {
|
||||
return this.properties['enabled'];
|
||||
let isEnabled = this.properties['enabled'];
|
||||
return (isEnabled === undefined) ? true : isEnabled;
|
||||
}
|
||||
|
||||
public set enabled(value: boolean) {
|
||||
@@ -400,6 +404,13 @@ class ComponentWrapper implements sqlops.Component {
|
||||
this.setProperty('width', v);
|
||||
}
|
||||
|
||||
public get required(): boolean {
|
||||
return this.properties['required'];
|
||||
}
|
||||
public set required(v: boolean) {
|
||||
this.setProperty('required', v);
|
||||
}
|
||||
|
||||
public toComponentShape(): IComponentShape {
|
||||
return <IComponentShape>{
|
||||
id: this.id,
|
||||
@@ -512,6 +523,7 @@ class CardWrapper extends ComponentWrapper implements sqlops.CardComponent {
|
||||
super(proxy, handle, ModelComponentTypes.Card, id);
|
||||
this.properties = {};
|
||||
this._emitterMap.set(ComponentEventType.onDidClick, new Emitter<any>());
|
||||
this._emitterMap.set(ComponentEventType.onDidClick, new Emitter<any>());
|
||||
}
|
||||
|
||||
public get label(): string {
|
||||
@@ -526,17 +538,53 @@ class CardWrapper extends ComponentWrapper implements sqlops.CardComponent {
|
||||
public set value(v: string) {
|
||||
this.setProperty('value', v);
|
||||
}
|
||||
public get selected(): boolean {
|
||||
return this.properties['selected'];
|
||||
}
|
||||
public set selected(v: boolean) {
|
||||
this.setProperty('selected', v);
|
||||
}
|
||||
public get cardType(): sqlops.CardType {
|
||||
return this.properties['cardType'];
|
||||
}
|
||||
public set cardType(v: sqlops.CardType) {
|
||||
this.setProperty('cardType', v);
|
||||
}
|
||||
public get actions(): sqlops.ActionDescriptor[] {
|
||||
return this.properties['actions'];
|
||||
}
|
||||
public set actions(a: sqlops.ActionDescriptor[]) {
|
||||
this.setProperty('actions', a);
|
||||
}
|
||||
public get iconPath(): string | URI | { light: string | URI; dark: string | URI } {
|
||||
return this.properties['iconPath'];
|
||||
}
|
||||
public set iconPath(v: string | URI | { light: string | URI; dark: string | URI }) {
|
||||
this.setProperty('iconPath', v);
|
||||
}
|
||||
|
||||
public get iconHeight(): number | string {
|
||||
return this.properties['iconHeight'];
|
||||
}
|
||||
public set iconHeight(v: number | string) {
|
||||
this.setProperty('iconHeight', v);
|
||||
}
|
||||
public get iconWidth(): number | string {
|
||||
return this.properties['iconWidth'];
|
||||
}
|
||||
public set iconWidth(v: number | string) {
|
||||
this.setProperty('iconWidth', v);
|
||||
}
|
||||
|
||||
public get onDidActionClick(): vscode.Event<sqlops.ActionDescriptor> {
|
||||
let emitter = this._emitterMap.get(ComponentEventType.onDidClick);
|
||||
return emitter && emitter.event;
|
||||
}
|
||||
|
||||
public get onCardSelectedChanged(): vscode.Event<any> {
|
||||
let emitter = this._emitterMap.get(ComponentEventType.onDidClick);
|
||||
return emitter && emitter.event;
|
||||
}
|
||||
}
|
||||
|
||||
class InputBoxWrapper extends ComponentWrapper implements sqlops.InputBoxComponent {
|
||||
@@ -771,10 +819,14 @@ class DropDownWrapper extends ComponentWrapper implements sqlops.DropDownCompone
|
||||
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<any>());
|
||||
}
|
||||
|
||||
public get value(): string {
|
||||
return this.properties['value'];
|
||||
public get value(): string | sqlops.CategoryValue {
|
||||
let val = this.properties['value'];
|
||||
if (!val && this.values && this.values.length > 0) {
|
||||
val = this.values[0];
|
||||
}
|
||||
return val;
|
||||
}
|
||||
public set value(v: string) {
|
||||
public set value(v: string | sqlops.CategoryValue) {
|
||||
this.setProperty('value', v);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,8 @@ class DialogImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdi
|
||||
public okButton: sqlops.window.modelviewdialog.Button;
|
||||
public cancelButton: sqlops.window.modelviewdialog.Button;
|
||||
public customButtons: sqlops.window.modelviewdialog.Button[];
|
||||
private _message: sqlops.window.modelviewdialog.DialogMessage;
|
||||
private _closeValidator: () => boolean | Thenable<boolean>;
|
||||
|
||||
constructor(extHostModelViewDialog: ExtHostModelViewDialog,
|
||||
extHostModelView: ExtHostModelViewShape) {
|
||||
@@ -105,6 +107,27 @@ class DialogImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdi
|
||||
super.setModelViewId(value);
|
||||
this.content = value;
|
||||
}
|
||||
|
||||
public get message(): sqlops.window.modelviewdialog.DialogMessage {
|
||||
return this._message;
|
||||
}
|
||||
|
||||
public set message(value: sqlops.window.modelviewdialog.DialogMessage) {
|
||||
this._message = value;
|
||||
this._extHostModelViewDialog.updateDialogContent(this);
|
||||
}
|
||||
|
||||
public registerCloseValidator(validator: () => boolean | Thenable<boolean>): void {
|
||||
this._closeValidator = validator;
|
||||
}
|
||||
|
||||
public validateClose(): Thenable<boolean> {
|
||||
if (this._closeValidator) {
|
||||
return Promise.resolve(this._closeValidator());
|
||||
} else {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TabImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdialog.DialogTab {
|
||||
@@ -218,6 +241,7 @@ class WizardImpl implements sqlops.window.modelviewdialog.Wizard {
|
||||
private _pageChangedEmitter = new Emitter<sqlops.window.modelviewdialog.WizardPageChangeInfo>();
|
||||
public readonly onPageChanged = this._pageChangedEmitter.event;
|
||||
private _navigationValidator: (info: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>;
|
||||
private _message: sqlops.window.modelviewdialog.DialogMessage;
|
||||
|
||||
constructor(public title: string, private _extHostModelViewDialog: ExtHostModelViewDialog) {
|
||||
this.doneButton = this._extHostModelViewDialog.createButton(DONE_LABEL);
|
||||
@@ -234,6 +258,15 @@ class WizardImpl implements sqlops.window.modelviewdialog.Wizard {
|
||||
return this._currentPage;
|
||||
}
|
||||
|
||||
public get message(): sqlops.window.modelviewdialog.DialogMessage {
|
||||
return this._message;
|
||||
}
|
||||
|
||||
public set message(value: sqlops.window.modelviewdialog.DialogMessage) {
|
||||
this._message = value;
|
||||
this._extHostModelViewDialog.updateWizard(this);
|
||||
}
|
||||
|
||||
public addPage(page: sqlops.window.modelviewdialog.WizardPage, index?: number): Thenable<void> {
|
||||
return this._extHostModelViewDialog.updateWizardPage(page).then(() => {
|
||||
this._extHostModelViewDialog.addPage(this, page, index);
|
||||
@@ -354,6 +387,11 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
||||
return wizard.validateNavigation(info);
|
||||
}
|
||||
|
||||
public $validateDialogClose(handle: number): Thenable<boolean> {
|
||||
let dialog = this._objectsByHandle.get(handle) as DialogImpl;
|
||||
return dialog.validateClose();
|
||||
}
|
||||
|
||||
public openDialog(dialog: sqlops.window.modelviewdialog.Dialog): void {
|
||||
let handle = this.getHandle(dialog);
|
||||
this.updateDialogContent(dialog);
|
||||
@@ -387,7 +425,8 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
||||
okButton: this.getHandle(dialog.okButton),
|
||||
cancelButton: this.getHandle(dialog.cancelButton),
|
||||
content: dialog.content && typeof dialog.content !== 'string' ? dialog.content.map(tab => this.getHandle(tab)) : dialog.content as string,
|
||||
customButtons: dialog.customButtons ? dialog.customButtons.map(button => this.getHandle(button)) : undefined
|
||||
customButtons: dialog.customButtons ? dialog.customButtons.map(button => this.getHandle(button)) : undefined,
|
||||
message: dialog.message
|
||||
});
|
||||
}
|
||||
|
||||
@@ -495,7 +534,8 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
||||
generateScriptButton: this.getHandle(wizard.generateScriptButton),
|
||||
doneButton: this.getHandle(wizard.doneButton),
|
||||
nextButton: this.getHandle(wizard.nextButton),
|
||||
customButtons: wizard.customButtons ? wizard.customButtons.map(button => this.getHandle(button)) : undefined
|
||||
customButtons: wizard.customButtons ? wizard.customButtons.map(button => this.getHandle(button)) : undefined,
|
||||
message: wizard.message
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -432,6 +432,10 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
|
||||
this._profilerService.onMoreRows(response);
|
||||
}
|
||||
|
||||
public $onSessionStopped(handle: number, response: sqlops.ProfilerSessionStoppedParams): void {
|
||||
this._profilerService.onSessionStopped(response);
|
||||
}
|
||||
|
||||
public $unregisterProvider(handle: number): TPromise<any> {
|
||||
let capabilitiesRegistration = this._capabilitiesRegistrations[handle];
|
||||
if (capabilitiesRegistration) {
|
||||
|
||||
@@ -80,6 +80,7 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
||||
dialog.okButton = okButton;
|
||||
dialog.cancelButton = cancelButton;
|
||||
dialog.onValidityChanged(valid => this._proxy.$onPanelValidityChanged(handle, valid));
|
||||
dialog.registerCloseValidator(() => this.validateDialogClose(handle));
|
||||
this._dialogs.set(handle, dialog);
|
||||
}
|
||||
|
||||
@@ -94,6 +95,8 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
||||
dialog.customButtons = details.customButtons.map(buttonHandle => this.getButton(buttonHandle));
|
||||
}
|
||||
|
||||
dialog.message = details.message;
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -169,6 +172,7 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
||||
if (details.customButtons !== undefined) {
|
||||
wizard.customButtons = details.customButtons.map(buttonHandle => this.getButton(buttonHandle));
|
||||
}
|
||||
wizard.message = details.message;
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -259,4 +263,8 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
||||
private validateNavigation(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean> {
|
||||
return this._proxy.$validateNavigation(handle, info);
|
||||
}
|
||||
|
||||
private validateDialogClose(handle: number): Thenable<boolean> {
|
||||
return this._proxy.$validateDialogClose(handle);
|
||||
}
|
||||
}
|
||||
@@ -237,6 +237,10 @@ export function createApiFactory(
|
||||
extHostDataProvider.$onSessionEventsAvailable(provider.handle, response);
|
||||
});
|
||||
|
||||
provider.registerOnSessionStopped((response: sqlops.ProfilerSessionStoppedParams) => {
|
||||
extHostDataProvider.$onSessionStopped(provider.handle, response);
|
||||
});
|
||||
|
||||
return extHostDataProvider.$registerProfilerProvider(provider);
|
||||
};
|
||||
|
||||
@@ -311,7 +315,8 @@ export function createApiFactory(
|
||||
},
|
||||
createWizard(title: string): sqlops.window.modelviewdialog.Wizard {
|
||||
return extHostModelViewDialog.createWizard(title);
|
||||
}
|
||||
},
|
||||
MessageLevel: sqlExtHostTypes.MessageLevel
|
||||
};
|
||||
|
||||
const window: typeof sqlops.window = {
|
||||
@@ -386,7 +391,8 @@ export function createApiFactory(
|
||||
workspace,
|
||||
queryeditor: queryEditor,
|
||||
ui: ui,
|
||||
StatusIndicator: sqlExtHostTypes.StatusIndicator
|
||||
StatusIndicator: sqlExtHostTypes.StatusIndicator,
|
||||
CardType: sqlExtHostTypes.CardType
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -416,6 +416,7 @@ export interface MainThreadDataProtocolShape extends IDisposable {
|
||||
$onFilePathsValidated(handle: number, response: sqlops.FileBrowserValidatedParams): void;
|
||||
$onScriptingComplete(handle: number, message: sqlops.ScriptingCompleteResult): void;
|
||||
$onSessionEventsAvailable(handle: number, response: sqlops.ProfilerSessionEvents): void;
|
||||
$onSessionStopped(handle: number, response: sqlops.ProfilerSessionStoppedParams): void;
|
||||
|
||||
/**
|
||||
* Callback when a session has completed initialization
|
||||
@@ -562,6 +563,7 @@ export interface ExtHostModelViewDialogShape {
|
||||
$onWizardPageChanged(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): void;
|
||||
$updateWizardPageInfo(handle: number, pageHandles: number[], currentPageIndex: number): void;
|
||||
$validateNavigation(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean>;
|
||||
$validateDialogClose(handle: number): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export interface MainThreadModelViewDialogShape extends IDisposable {
|
||||
|
||||
@@ -29,14 +29,14 @@ export class OpenGettingStartedInBrowserAction extends Action {
|
||||
}
|
||||
|
||||
run(): TPromise<any> {
|
||||
const uri = URI.parse(product.releaseNotesUrl);
|
||||
const uri = URI.parse(product.gettingStartedUrl);
|
||||
return this.openerService.open(uri);
|
||||
}
|
||||
}
|
||||
|
||||
export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesAction {
|
||||
|
||||
static ID = 'update.showCurrentCarbonReleaseNotes';
|
||||
static ID = 'update.showGettingStarted';
|
||||
static LABEL = nls.localize('showReleaseNotes', "Show Getting Started");
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Mock, It, Times } from 'typemoq';
|
||||
import { ExtHostModelViewDialog } from 'sql/workbench/api/node/extHostModelViewDialog';
|
||||
import { MainThreadModelViewDialogShape, ExtHostModelViewShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||
import { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -290,4 +291,39 @@ suite('ExtHostModelViewDialog Tests', () => {
|
||||
assert.equal(validationInfo.lastPage, lastPage);
|
||||
assert.equal(validationInfo.newPage, newPage);
|
||||
});
|
||||
|
||||
test('Changing the wizard message sends the new message to the main thread', () => {
|
||||
// Set up the main thread mock to record the call
|
||||
mockProxy.setup(x => x.$setWizardDetails(It.isAny(), It.isAny()));
|
||||
let wizard = extHostModelViewDialog.createWizard('wizard_1');
|
||||
|
||||
// If I update the wizard's message
|
||||
let newMessage = {
|
||||
level: MessageLevel.Error,
|
||||
text: 'test message'
|
||||
};
|
||||
wizard.message = newMessage;
|
||||
|
||||
// Then the main thread gets notified of the new details
|
||||
mockProxy.verify(x => x.$setWizardDetails(It.isAny(), It.is(x => x.message === newMessage)), Times.once());
|
||||
});
|
||||
|
||||
test('Main thread can execute dialog close validation', () => {
|
||||
// Set up the main thread mock to record the dialog handle
|
||||
let dialogHandle: number;
|
||||
mockProxy.setup(x => x.$setDialogDetails(It.isAny(), It.isAny())).callback((handle, details) => dialogHandle = handle);
|
||||
|
||||
// Create the dialog and add a validation that records that it has been called
|
||||
let dialog = extHostModelViewDialog.createDialog('dialog_1');
|
||||
extHostModelViewDialog.updateDialogContent(dialog);
|
||||
let callCount = 0;
|
||||
dialog.registerCloseValidator(() => {
|
||||
callCount++;
|
||||
return true;
|
||||
});
|
||||
|
||||
// If I call the validation from the main thread then it should run
|
||||
extHostModelViewDialog.$validateDialogClose(dialogHandle);
|
||||
assert.equal(callCount, 1);
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,7 @@ import * as assert from 'assert';
|
||||
import { Mock, It, Times } from 'typemoq';
|
||||
import { MainThreadModelViewDialog } from 'sql/workbench/api/node/mainThreadModelViewDialog';
|
||||
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { IModelViewButtonDetails, IModelViewTabDetails, IModelViewDialogDetails, IModelViewWizardPageDetails, IModelViewWizardDetails } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { IModelViewButtonDetails, IModelViewTabDetails, IModelViewDialogDetails, IModelViewWizardPageDetails, IModelViewWizardDetails, DialogMessage, MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { CustomDialogService } from 'sql/platform/dialog/customDialogService';
|
||||
import { Dialog, DialogTab, Wizard } from 'sql/platform/dialog/dialogTypes';
|
||||
import { ExtHostModelViewDialogShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||
@@ -60,7 +60,8 @@ suite('MainThreadModelViewDialog Tests', () => {
|
||||
$onPanelValidityChanged: (handle, valid) => undefined,
|
||||
$onWizardPageChanged: (handle, info) => undefined,
|
||||
$updateWizardPageInfo: (wizardHandle, pageHandles, currentPageIndex) => undefined,
|
||||
$validateNavigation: (handle, info) => undefined
|
||||
$validateNavigation: (handle, info) => undefined,
|
||||
$validateDialogClose: handle => undefined
|
||||
});
|
||||
let extHostContext = <IExtHostContext>{
|
||||
getProxy: proxyType => mockExtHostModelViewDialog.object
|
||||
@@ -112,7 +113,8 @@ suite('MainThreadModelViewDialog Tests', () => {
|
||||
content: [tab1Handle, tab2Handle],
|
||||
okButton: okButtonHandle,
|
||||
cancelButton: cancelButtonHandle,
|
||||
customButtons: [button1Handle, button2Handle]
|
||||
customButtons: [button1Handle, button2Handle],
|
||||
message: undefined
|
||||
};
|
||||
|
||||
// Set up the wizard details
|
||||
@@ -152,7 +154,8 @@ suite('MainThreadModelViewDialog Tests', () => {
|
||||
currentPage: undefined,
|
||||
title: 'wizard_title',
|
||||
customButtons: [],
|
||||
pages: [page1Handle, page2Handle]
|
||||
pages: [page1Handle, page2Handle],
|
||||
message: undefined
|
||||
};
|
||||
|
||||
// Register the buttons, tabs, and dialog
|
||||
@@ -322,10 +325,38 @@ suite('MainThreadModelViewDialog Tests', () => {
|
||||
mockExtHostModelViewDialog.setup(x => x.$validateNavigation(It.isAny(), It.isAny()));
|
||||
|
||||
// If I call validateNavigation on the wizard that gets created
|
||||
let wizard: Wizard = (mainThreadModelViewDialog as any).getWizard(wizardHandle);
|
||||
wizard.validateNavigation(1);
|
||||
mainThreadModelViewDialog.$openWizard(wizardHandle);
|
||||
openedWizard.validateNavigation(1);
|
||||
|
||||
// Then the call gets forwarded to the extension host
|
||||
mockExtHostModelViewDialog.verify(x => x.$validateNavigation(It.is(handle => handle === wizardHandle), It.is(info => info.newPage === 1)), Times.once());
|
||||
});
|
||||
|
||||
test('Adding a message to a wizard fires events on the created wizard', () => {
|
||||
mainThreadModelViewDialog.$openWizard(wizardHandle);
|
||||
let newMessage: DialogMessage;
|
||||
openedWizard.onMessageChange(message => newMessage = message);
|
||||
|
||||
// If I change the wizard's message
|
||||
wizardDetails.message = {
|
||||
level: MessageLevel.Error,
|
||||
text: 'test message'
|
||||
};
|
||||
mainThreadModelViewDialog.$setWizardDetails(wizardHandle, wizardDetails);
|
||||
|
||||
// Then the message gets changed on the wizard
|
||||
assert.equal(newMessage, wizardDetails.message, 'New message was not included in the fired event');
|
||||
assert.equal(openedWizard.message, wizardDetails.message, 'New message was not set on the wizard');
|
||||
});
|
||||
|
||||
test('Creating a dialog adds a close validation that calls the extension host', () => {
|
||||
mockExtHostModelViewDialog.setup(x => x.$validateDialogClose(It.isAny()));
|
||||
|
||||
// If I call validateClose on the dialog that gets created
|
||||
mainThreadModelViewDialog.$openDialog(dialogHandle);
|
||||
openedDialog.validateClose();
|
||||
|
||||
// Then the call gets forwarded to the extension host
|
||||
mockExtHostModelViewDialog.verify(x => x.$validateDialogClose(It.is(handle => handle === dialogHandle)), Times.once());
|
||||
});
|
||||
});
|
||||
@@ -397,8 +397,16 @@ export class SelectBoxList implements ISelectBoxDelegate, IDelegate<ISelectOptio
|
||||
container.appendChild(this.selectDropDownContainer);
|
||||
|
||||
this.layoutSelectDropDown();
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
return {
|
||||
dispose: () => container.removeChild(this.selectDropDownContainer) // remove to take out the CSS rules we add
|
||||
dispose: () => {
|
||||
try {
|
||||
container.removeChild(this.selectDropDownContainer); // remove to take out the CSS rules we add
|
||||
} catch(e) {
|
||||
// if this fails it means it is already removed
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -999,8 +999,8 @@ export class CodeMenu {
|
||||
arrays.coalesce([
|
||||
// new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miWelcome', comment: ['&& denotes a mnemonic'] }, "&&Welcome")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.showWelcomePage') }),
|
||||
// {{SQL CARBON EDIT}}
|
||||
product.releaseNotesUrl ?
|
||||
this.createMenuItem(nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "Getting &&Started"), 'update.showCurrentCarbonReleaseNotes')
|
||||
product.gettingStartedUrl ?
|
||||
this.createMenuItem(nls.localize({ key: 'miGettingStarted', comment: ['&& denotes a mnemonic'] }, "Getting &&Started"), 'update.showGettingStarted')
|
||||
: null,
|
||||
product.documentationUrl ?
|
||||
this.createMenuItem(nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation"), 'workbench.action.openDocumentationUrl')
|
||||
|
||||
@@ -52,6 +52,8 @@ export interface IProductConfiguration {
|
||||
};
|
||||
documentationUrl: string;
|
||||
releaseNotesUrl: string;
|
||||
// {SQL CARBON EDIT}
|
||||
gettingStartedUrl: string;
|
||||
keyboardShortcutsUrlMac: string;
|
||||
keyboardShortcutsUrlLinux: string;
|
||||
keyboardShortcutsUrlWin: string;
|
||||
|
||||
Reference in New Issue
Block a user