diff --git a/src/sql/parts/dashboard/common/dashboardPanelStyles.ts b/src/sql/parts/dashboard/common/dashboardPanelStyles.ts index c25fc78688..15fef079bf 100644 --- a/src/sql/parts/dashboard/common/dashboardPanelStyles.ts +++ b/src/sql/parts/dashboard/common/dashboardPanelStyles.ts @@ -71,12 +71,12 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { } // Panel title background - const tabBoarder = theme.getColor(TAB_BORDER); - if (tabBoarder) { + const tabBorder = theme.getColor(TAB_BORDER); + if (tabBorder) { collector.addRule(` panel.dashboard-panel > .tabbedPanel > .title .tabList .tab-header { - border-right-color: ${tabBoarder}; - border-bottom-color: ${tabBoarder}; + border-right-color: ${tabBorder}; + border-bottom-color: ${tabBorder}; } `); } @@ -86,13 +86,13 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { if (outline) { collector.addRule(` panel.dashboard-panel > .tabbedPanel > .title { - border-bottom-color: ${tabBoarder}; + border-bottom-color: ${tabBorder}; border-bottom-width: 1px; border-bottom-style: solid; } panel.dashboard-panel > .tabbedPanel.vertical > .title { - border-right-color: ${tabBoarder}; + border-right-color: ${tabBorder}; border-right-width: 1px; border-right-style: solid; } diff --git a/src/sql/parts/modelComponents/card.component.html b/src/sql/parts/modelComponents/card.component.html new file mode 100644 index 0000000000..48e5153d95 --- /dev/null +++ b/src/sql/parts/modelComponents/card.component.html @@ -0,0 +1,19 @@ +
+ +
+
+
+

{{label}}

+

{{value}}

+ + + + + + +
{{action.label}} + {{action.actionTitle}} +
+
+
+
\ No newline at end of file diff --git a/src/sql/parts/modelComponents/card.component.ts b/src/sql/parts/modelComponents/card.component.ts index 823053866c..89bbfb1aff 100644 --- a/src/sql/parts/modelComponents/card.component.ts +++ b/src/sql/parts/modelComponents/card.component.ts @@ -9,29 +9,37 @@ import { Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFacto } from '@angular/core'; import * as sqlops from 'sqlops'; +import { ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import * as colors from 'vs/platform/theme/common/colorRegistry'; import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; import { ComponentBase } from 'sql/parts/modelComponents/componentBase'; -import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces'; +import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces'; +import { BOOTSTRAP_SERVICE_ID, IBootstrapService } from 'sql/services/bootstrap/bootstrapService'; +import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { StatusIndicator, CardProperties, ActionDescriptor } from 'sql/workbench/api/common/sqlExtHostTypes'; @Component({ - template: ` -
-

{{label}}

-

{{value}}

-
- ` + templateUrl: decodeURI(require.toUrl('sql/parts/modelComponents/card.component.html')) }) export default class CardComponent extends ComponentBase implements IComponent, OnDestroy { @Input() descriptor: IComponentDescriptor; @Input() modelStore: IModelStore; - constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) { + private backgroundColor: string; + + constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef, + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, + @Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService + ) { super(changeRef); } ngOnInit(): void { this.baseInit(); + this._register(this._bootstrapService.themeService.onDidColorThemeChange(this.updateTheme, this)); + this.updateTheme(this._bootstrapService.themeService.getColorTheme()); + } ngOnDestroy(): void { @@ -52,15 +60,45 @@ export default class CardComponent extends ComponentBase implements IComponent, // CSS-bound properties public get label(): string { - return this.getPropertyOrDefault((props) => props.label, ''); + return this.getPropertyOrDefault((props) => props.label, ''); } public get value(): string { - return this.getPropertyOrDefault((props) => props.value, ''); + return this.getPropertyOrDefault((props) => props.value, ''); } - public get actions(): sqlops.ActionDescriptor[] { - return this.getPropertyOrDefault((props) => props.actions, []); + public get actions(): ActionDescriptor[] { + return this.getPropertyOrDefault((props) => props.actions, []); } + public hasStatus(): boolean { + let status = this.getPropertyOrDefault((props) => props.status, StatusIndicator.None); + return status !== StatusIndicator.None; + } + + public get statusColor(): string { + let status = this.getPropertyOrDefault((props) => props.status, StatusIndicator.None); + switch(status) { + case StatusIndicator.Ok: + return 'green'; + case StatusIndicator.Warning: + return 'orange'; + case StatusIndicator.Error: + return 'red'; + default: + return this.backgroundColor; + } + } + + private updateTheme(theme: IColorTheme) { + this.backgroundColor = theme.getColor(colors.editorBackground, true).toString(); + } + + private onDidActionClick(action: ActionDescriptor): void { + this._onEventEmitter.fire({ + eventType: ComponentEventType.onDidClick, + args: action + }); + + } } diff --git a/src/sql/parts/modelComponents/card.css b/src/sql/parts/modelComponents/card.css index 29e21d6f0f..49aecafa0f 100644 --- a/src/sql/parts/modelComponents/card.css +++ b/src/sql/parts/modelComponents/card.css @@ -2,12 +2,9 @@ .model-card { position: relative; display: inline-block; - height: auto; + height: 90%; width: auto; margin: 15px; - padding: 10px 45px 20px 45px; - min-height: 30px; - min-width: 30px; border-width: 1px; border-style: solid; border-color: rgb(214, 214, 214); @@ -16,6 +13,16 @@ box-shadow: rgba(120, 120, 120, 0.75) 0px 0px 6px; } +.model-card .card-content { + position: relative; + display: inline-block; + height: auto; + width: auto; + padding: 10px 45px 20px 45px; + min-height: 30px; + min-width: 30px; +} + .model-card .card-label { font-size: 12px; font-weight: bold; @@ -24,4 +31,42 @@ .model-card .card-value { font-size: 12px; line-height: 18px; -} \ No newline at end of file +} + +.model-card .card-status { + position: absolute; + top: 7px; + left: 5px; + overflow: hidden; + width: 22px; + height: 22px; +} + +.model-card .status-content { + position: absolute; + top: 0px; + right: 0px; + min-width: 16px; + height: 16px; + border-radius: 8px; + text-align: center; +} + +.model-card .model-table { + border-spacing: 5px; +} + +.model-table .table-row { + width: auto; + clear: both; +} + +.model-table .table-cell { + vertical-align: top; + padding: 7px; +} + +.model-table a { + cursor: pointer; + text-decoration: underline +} diff --git a/src/sql/services/accountManagement/accountManagementService.ts b/src/sql/services/accountManagement/accountManagementService.ts index 8d97c240ac..017a43c02a 100644 --- a/src/sql/services/accountManagement/accountManagementService.ts +++ b/src/sql/services/accountManagement/accountManagementService.ts @@ -142,7 +142,7 @@ export class AccountManagementService implements IAccountManagementService { }) .then(null, err => { // On error, check to see if the error is because the user cancelled. If so, just ignore - if ('userCancelledSignIn' in err) { + if (err && 'userCancelledSignIn' in err) { return Promise.resolve(); } return Promise.reject(err); diff --git a/src/sql/sqlops.proposed.d.ts b/src/sql/sqlops.proposed.d.ts index cb340bbc45..b315cac59f 100644 --- a/src/sql/sqlops.proposed.d.ts +++ b/src/sql/sqlops.proposed.d.ts @@ -188,10 +188,24 @@ declare module 'sqlops' { */ label: string; /** - * ID of the task to be called when this is clicked on. - * These should be registered using the {tasks.registerTask} API. + * Name of the clickable action. If not defined then no action will be shown */ - taskId: string; + actionTitle?: string; + /** + * Data sent on callback being run. + */ + callbackData?: string; + } + + /** + * Defines status indicators that can be shown to the user as part of + * components such as the Card UI + */ + export enum StatusIndicator { + None = 0, + Ok = 1, + Warning = 2, + Error = 3 } /** @@ -202,6 +216,7 @@ declare module 'sqlops' { label: string; value?: string; actions?: ActionDescriptor[]; + status?: StatusIndicator; } export interface InputBoxProperties { @@ -230,6 +245,7 @@ declare module 'sqlops' { label: string; value: string; actions?: ActionDescriptor[]; + onDidActionClick: vscode.Event; } export interface InputBoxComponent extends Component, InputBoxProperties { diff --git a/src/sql/workbench/api/common/sqlExtHostTypes.ts b/src/sql/workbench/api/common/sqlExtHostTypes.ts index 921db2e2b7..e97fb1589c 100644 --- a/src/sql/workbench/api/common/sqlExtHostTypes.ts +++ b/src/sql/workbench/api/common/sqlExtHostTypes.ts @@ -118,3 +118,26 @@ export interface IModelViewButtonDetails { enabled: boolean; hidden: boolean; } + +/// Card-related APIs that need to be here to avoid early load issues +// with enums causing requiring of sqlops API to fail. +export enum StatusIndicator { + None = 0, + Ok = 1, + Warning = 2, + Error = 3 +} + +export interface CardProperties { + label: string; + value?: string; + actions?: ActionDescriptor[]; + status?: StatusIndicator; +} + +export interface ActionDescriptor { + label: string; + actionTitle?: string; + callbackData?: string; +} + diff --git a/src/sql/workbench/api/node/extHostModelView.ts b/src/sql/workbench/api/node/extHostModelView.ts index 43519a6559..8ffae9e542 100644 --- a/src/sql/workbench/api/node/extHostModelView.ts +++ b/src/sql/workbench/api/node/extHostModelView.ts @@ -14,6 +14,7 @@ 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 { IActionDescriptor } from 'vs/editor/standalone/browser/standaloneCodeEditor'; class ModelBuilderImpl implements sqlops.ModelBuilder { private nextComponentId: number; @@ -306,7 +307,7 @@ class ComponentWrapper implements sqlops.Component { } else if (eventArgs) { let emitter = this._emitterMap.get(eventArgs.eventType); if (emitter) { - emitter.fire(); + emitter.fire(eventArgs.args); } } } @@ -362,6 +363,7 @@ class CardWrapper extends ComponentWrapper implements sqlops.CardComponent { constructor(proxy: MainThreadModelViewShape, handle: number, id: string) { super(proxy, handle, ModelComponentTypes.Card, id); this.properties = {}; + this._emitterMap.set(ComponentEventType.onDidClick, new Emitter()); } public get label(): string { @@ -382,6 +384,11 @@ class CardWrapper extends ComponentWrapper implements sqlops.CardComponent { public set actions(a: sqlops.ActionDescriptor[]) { this.setProperty('actions', a); } + + public get onDidActionClick(): vscode.Event { + let emitter = this._emitterMap.get(ComponentEventType.onDidClick); + return emitter && emitter.event; + } } class InputBoxWrapper extends ComponentWrapper implements sqlops.InputBoxComponent { diff --git a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts index 8569b31930..f904642a9e 100644 --- a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts @@ -365,7 +365,8 @@ export function createApiFactory( dashboard, workspace, queryeditor: queryEditor, - ui: ui + ui: ui, + StatusIndicator: sqlExtHostTypes.StatusIndicator }; } };