+
-
-
{{label}}
-
{{value}}
-
-
-
-
+
+
+
+
+
+
+
+
{{label}}
+
{{value}}
+
+
+
+
+
+
\ 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 6ee4ca2a95..ba5c74a171 100644
--- a/src/sql/parts/modelComponents/card.component.ts
+++ b/src/sql/parts/modelComponents/card.component.ts
@@ -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
((props) => props.value, '');
}
+ public get cardType(): string {
+ return this.getPropertyOrDefault((props) => props.cardType, 'Details');
+ }
+
+ public get selected(): boolean {
+ return this.getPropertyOrDefault((props) => props.selected, false);
+ }
+
+ public set selected(newValue: boolean) {
+ this.setPropertyFromUI((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((props) => props.actions, []);
}
diff --git a/src/sql/parts/modelComponents/card.css b/src/sql/parts/modelComponents/card.css
index 49aecafa0f..3875663d90 100644
--- a/src/sql/parts/modelComponents/card.css
+++ b/src/sql/parts/modelComponents/card.css
@@ -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;
diff --git a/src/sql/parts/modelComponents/componentBase.ts b/src/sql/parts/modelComponents/componentBase.ts
index c51ebc5254..c25c5377ae 100644
--- a/src/sql/parts/modelComponents/componentBase.ts
+++ b/src/sql/parts/modelComponents/componentBase.ts
@@ -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 {
constructor(public descriptor: IComponentDescriptor, public config: T) { }
diff --git a/src/sql/parts/modelComponents/componentWithIconBase.ts b/src/sql/parts/modelComponents/componentWithIconBase.ts
new file mode 100644
index 0000000000..8dc3611d5b
--- /dev/null
+++ b/src/sql/parts/modelComponents/componentWithIconBase.ts
@@ -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 {
+ 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(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((props) => props.iconPath, undefined);
+ }
+
+ public get iconHeight(): number | string {
+ return this.getPropertyOrDefault((props) => props.iconHeight, '40px');
+ }
+
+ public get iconWidth(): number | string {
+ return this.getPropertyOrDefault((props) => props.iconWidth, '40px');
+ }
+
+
+ ngOnDestroy(): void {
+ if (this._iconClass) {
+ removeCSSRulesContainingSelector(this._iconClass);
+ }
+ super.ngOnDestroy();
+ }
+}
diff --git a/src/sql/parts/modelComponents/formContainer.component.ts b/src/sql/parts/modelComponents/formContainer.component.ts
index 45cbf91128..303b113a1f 100644
--- a/src/sql/parts/modelComponents/formContainer.component.ts
+++ b/src/sql/parts/modelComponents/formContainer.component.ts
@@ -26,7 +26,7 @@ export interface TitledFormItemLayout {
horizontal: boolean;
componentWidth?: number | string;
componentHeight?: number | string;
- titleFontSize?: number;
+ titleFontSize?: number | string;
required?: boolean;
info?: string;
}
@@ -48,7 +48,7 @@ class FormItem {
{{getItemTitle(item)}}*
-
+