Feature/selectable card component (#1703)

* added selectable card

* creating new card type
This commit is contained in:
Leila Lali
2018-06-22 14:25:21 -07:00
committed by GitHub
parent 322847469d
commit a627285a4c
12 changed files with 327 additions and 86 deletions

View File

@@ -10,21 +10,16 @@ import {
import * as sqlops from 'sqlops'; 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 { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
import { attachButtonStyler } from 'sql/common/theme/styler'; import { attachButtonStyler } from 'sql/common/theme/styler';
import { Button } from 'sql/base/browser/ui/button/button'; import { Button } from 'sql/base/browser/ui/button/button';
import { SIDE_BAR_BACKGROUND, SIDE_BAR_TITLE_FOREGROUND } from 'vs/workbench/common/theme'; import { SIDE_BAR_BACKGROUND, SIDE_BAR_TITLE_FOREGROUND } from 'vs/workbench/common/theme';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import 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 { focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
type IUserFriendlyIcon = string | URI | { light: string | URI; dark: string | URI };
@Component({ @Component({
selector: 'modelview-button', selector: 'modelview-button',
@@ -32,12 +27,10 @@ type IUserFriendlyIcon = string | URI | { light: string | URI; dark: string | UR
<div #input style="width: 100%"></div> <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() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore; @Input() modelStore: IModelStore;
private _button: Button; private _button: Button;
private _iconClass: string;
private _iconPath: IUserFriendlyIcon;
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef; @ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
constructor( constructor(
@@ -71,9 +64,6 @@ export default class ButtonComponent extends ComponentBase implements IComponent
} }
ngOnDestroy(): void { ngOnDestroy(): void {
if (this._iconClass) {
removeCSSRulesContainingSelector(this._iconClass);
}
this.baseDestroy(); this.baseDestroy();
} }
@@ -101,14 +91,11 @@ export default class ButtonComponent extends ComponentBase implements IComponent
this.updateIcon(); this.updateIcon();
} }
private updateIcon() { protected updateIcon() {
if (this.iconPath && this.iconPath !== this._iconPath) { if (this.iconPath) {
this._iconPath = this.iconPath;
if (!this._iconClass) { if (!this._iconClass) {
const ids = new IdGenerator('button-component-icon-' + Math.round(Math.random() * 1000)); super.updateIcon();
this._iconClass = ids.nextId();
this._button.icon = this._iconClass + ' icon'; this._button.icon = this._iconClass + ' icon';
// Styling for icon button // Styling for icon button
this._register(attachButtonStyler(this._button, this.themeService, { this._register(attachButtonStyler(this._button, this.themeService, {
buttonBackground: Color.transparent.toString(), buttonBackground: Color.transparent.toString(),
@@ -117,36 +104,6 @@ export default class ButtonComponent extends ComponentBase implements IComponent
buttonForeground: foreground 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); 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 { private setValueProperties(properties: sqlops.ButtonProperties, label: string): void {
properties.label = label; properties.label = label;

View File

@@ -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"> <span *ngIf="hasStatus" class="card-status">
<div class="status-content" [style.backgroundColor]="statusColor"></div> <div class="status-content" [style.backgroundColor]="statusColor"></div>
</span> </span>
<div class="card-content">
<h4 class="card-label">{{label}}</h4> <ng-container *ngIf="isVerticalButton">
<p class="card-value">{{value}}</p> <div class="card-vertical-button">
<span *ngIf="actions"> <div *ngIf="iconPath" class="iconContainer"><div [class]="iconClass" [style.width]="iconWidth" [style.height]="iconHeight"></div>
<table class="model-table"> <hr/>
<tr *ngFor="let action of actions"> <h4 class="card-label">{{label}}</h4>
<td class="table-row">{{action.label}}</td> </div>
<td *ngIf="action.actionTitle" class="table-row"> </div>
<a class="pointer prominent" (click)="onDidActionClick(action)">{{action.actionTitle}}</a> </ng-container>
</td>
</tr> <ng-container *ngIf="isDetailsCard">
</table> <div class="card-content">
</span> <h4 class="card-label">{{label}}</h4>
</div> <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> </div>

View File

@@ -15,14 +15,14 @@ import * as colors from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; 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 { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
import { StatusIndicator, CardProperties, ActionDescriptor } from 'sql/workbench/api/common/sqlExtHostTypes'; import { StatusIndicator, CardProperties, ActionDescriptor } from 'sql/workbench/api/common/sqlExtHostTypes';
@Component({ @Component({
templateUrl: decodeURI(require.toUrl('sql/parts/modelComponents/card.component.html')) 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() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore; @Input() modelStore: IModelStore;
@@ -30,7 +30,7 @@ export default class CardComponent extends ComponentBase implements IComponent,
constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef, constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef, @Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
) { ) {
super(changeRef); super(changeRef);
} }
@@ -46,6 +46,39 @@ export default class CardComponent extends ComponentBase implements IComponent,
this.baseDestroy(); 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 /// IComponent implementation
public layout(): void { public layout(): void {
@@ -57,6 +90,19 @@ export default class CardComponent extends ComponentBase implements IComponent,
this.layout(); 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 // CSS-bound properties
public get label(): string { public get label(): string {
@@ -67,6 +113,27 @@ export default class CardComponent extends ComponentBase implements IComponent,
return this.getPropertyOrDefault<CardProperties, string>((props) => props.value, ''); 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[] { public get actions(): ActionDescriptor[] {
return this.getPropertyOrDefault<CardProperties, ActionDescriptor[]>((props) => props.actions, []); return this.getPropertyOrDefault<CardProperties, ActionDescriptor[]>((props) => props.actions, []);
} }

View File

@@ -7,12 +7,26 @@
margin: 15px; margin: 15px;
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
border-color: rgb(214, 214, 214);
text-align: left; text-align: left;
vertical-align: top; vertical-align: top;
box-shadow: rgba(120, 120, 120, 0.75) 0px 0px 6px; 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 { .model-card .card-content {
position: relative; position: relative;
display: inline-block; display: inline-block;
@@ -23,6 +37,16 @@
min-width: 30px; 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 { .model-card .card-label {
font-size: 12px; font-size: 12px;
font-weight: bold; font-weight: bold;
@@ -33,6 +57,19 @@
line-height: 18px; 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 { .model-card .card-status {
position: absolute; position: absolute;
top: 7px; top: 7px;

View File

@@ -18,6 +18,12 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component'; 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> { export class ItemDescriptor<T> {
constructor(public descriptor: IComponentDescriptor, public config: T) { } constructor(public descriptor: IComponentDescriptor, public config: T) { }

View 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();
}
}

View File

@@ -26,7 +26,7 @@ export interface TitledFormItemLayout {
horizontal: boolean; horizontal: boolean;
componentWidth?: number | string; componentWidth?: number | string;
componentHeight?: number | string; componentHeight?: number | string;
titleFontSize?: number; titleFontSize?: number | string;
required?: boolean; required?: boolean;
info?: string; info?: string;
} }
@@ -48,7 +48,7 @@ class FormItem {
<ng-container *ngIf="isHorizontal(item)"> <ng-container *ngIf="isHorizontal(item)">
<div class="form-cell" [style.font-size]="getItemTitleFontSize(item)"> <div class="form-cell" [style.font-size]="getItemTitleFontSize(item)">
{{getItemTitle(item)}}<span class="form-required" *ngIf="isItemRequired(item)">*</span> {{getItemTitle(item)}}<span class="form-required" *ngIf="isItemRequired(item)">*</span>
<span class="icon info" *ngIf="itemHasInfo(item)" [title]="getItemInfo(item)"></span> <span class="icon info form-info" *ngIf="itemHasInfo(item)" [title]="getItemInfo(item)"></span>
</div> </div>
<div class="form-cell"> <div class="form-cell">
<div class="form-component-container"> <div class="form-component-container">
@@ -68,7 +68,7 @@ class FormItem {
<div class="form-vertical-container" *ngIf="isVertical(item)" [style.height]="getRowHeight(item)"> <div class="form-vertical-container" *ngIf="isVertical(item)" [style.height]="getRowHeight(item)">
<div class="form-item-row" [style.font-size]="getItemTitleFontSize(item)"> <div class="form-item-row" [style.font-size]="getItemTitleFontSize(item)">
{{getItemTitle(item)}}<span class="form-required" *ngIf="isItemRequired(item)">*</span> {{getItemTitle(item)}}<span class="form-required" *ngIf="isItemRequired(item)">*</span>
<span class="icon info" *ngIf="itemHasInfo(item)" [title]="getItemInfo(item)"></span> <span class="icon info form-info" *ngIf="itemHasInfo(item)" [title]="getItemInfo(item)"></span>
</div> </div>
<div class="form-item-row" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)"> <div class="form-item-row" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)">
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)"> <model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)">

View File

@@ -42,6 +42,11 @@
padding-left: 5px; padding-left: 5px;
} }
.form-info {
width: 15px;
height: 15px;
}
.form-component-actions { .form-component-actions {
padding-left: 5px; padding-left: 5px;
} }

View File

@@ -239,7 +239,7 @@ declare module 'sqlops' {
horizontal?: boolean; horizontal?: boolean;
componentWidth?: number | string; componentWidth?: number | string;
componentHeight?: number | string; componentHeight?: number | string;
titleFontSize?: number; titleFontSize?: number | string;
required?: boolean; required?: boolean;
info?: string; info?: string;
} }
@@ -300,15 +300,22 @@ declare module 'sqlops' {
Error = 3 Error = 3
} }
export enum CardType {
VerticalButton = 'VerticalButton',
Details = 'Details'
}
/** /**
* Properties representing the card component, can be used * Properties representing the card component, can be used
* when using ModelBuilder to create the component * when using ModelBuilder to create the component
*/ */
export interface CardProperties { export interface CardProperties extends ComponentWithIcon {
label: string; label: string;
value?: string; value?: string;
actions?: ActionDescriptor[]; actions?: ActionDescriptor[];
status?: StatusIndicator; status?: StatusIndicator;
selected?: boolean;
cardType: CardType;
} }
export type InputBoxInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'range' | 'search' | 'text' | 'time' | 'url' | 'week'; export type InputBoxInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'range' | 'search' | 'text' | 'time' | 'url' | 'week';
@@ -318,6 +325,12 @@ declare module 'sqlops' {
width?: 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 { export interface InputBoxProperties extends ComponentProperties {
value?: string; value?: string;
ariaLabel?: string; ariaLabel?: string;
@@ -393,20 +406,17 @@ declare module 'sqlops' {
html?: string; html?: string;
} }
export interface ButtonProperties extends ComponentProperties { export interface ButtonProperties extends ComponentProperties, ComponentWithIcon {
label?: string; label?: string;
iconPath?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
} }
export interface LoadingComponentProperties { export interface LoadingComponentProperties {
loading?: boolean; loading?: boolean;
} }
export interface CardComponent extends Component { export interface CardComponent extends Component, CardProperties {
label: string;
value: string;
actions?: ActionDescriptor[];
onDidActionClick: vscode.Event<ActionDescriptor>; onDidActionClick: vscode.Event<ActionDescriptor>;
onCardSelectedChanged: vscode.Event<any>;
} }
export interface TextComponent extends Component { export interface TextComponent extends Component {

View File

@@ -208,6 +208,8 @@ export interface CardProperties {
value?: string; value?: string;
actions?: ActionDescriptor[]; actions?: ActionDescriptor[];
status?: StatusIndicator; status?: StatusIndicator;
selected?: boolean;
cardType: CardType;
} }
export interface ActionDescriptor { export interface ActionDescriptor {
@@ -237,3 +239,8 @@ export enum DeclarativeDataType {
category = 'category', category = 'category',
boolean = 'boolean' boolean = 'boolean'
} }
export enum CardType {
VerticalButton = 'VerticalButton',
Details = 'Details'
}

View File

@@ -15,7 +15,7 @@ import * as vscode from 'vscode';
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';
import { SqlMainContext, ExtHostModelViewShape, MainThreadModelViewShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; 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 { class ModelBuilderImpl implements sqlops.ModelBuilder {
private nextComponentId: number; private nextComponentId: number;
@@ -523,6 +523,7 @@ class CardWrapper extends ComponentWrapper implements sqlops.CardComponent {
super(proxy, handle, ModelComponentTypes.Card, id); super(proxy, handle, ModelComponentTypes.Card, id);
this.properties = {}; this.properties = {};
this._emitterMap.set(ComponentEventType.onDidClick, new Emitter<any>()); this._emitterMap.set(ComponentEventType.onDidClick, new Emitter<any>());
this._emitterMap.set(ComponentEventType.onDidClick, new Emitter<any>());
} }
public get label(): string { public get label(): string {
@@ -537,17 +538,53 @@ class CardWrapper extends ComponentWrapper implements sqlops.CardComponent {
public set value(v: string) { public set value(v: string) {
this.setProperty('value', v); 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[] { public get actions(): sqlops.ActionDescriptor[] {
return this.properties['actions']; return this.properties['actions'];
} }
public set actions(a: sqlops.ActionDescriptor[]) { public set actions(a: sqlops.ActionDescriptor[]) {
this.setProperty('actions', a); 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> { public get onDidActionClick(): vscode.Event<sqlops.ActionDescriptor> {
let emitter = this._emitterMap.get(ComponentEventType.onDidClick); let emitter = this._emitterMap.get(ComponentEventType.onDidClick);
return emitter && emitter.event; 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 { class InputBoxWrapper extends ComponentWrapper implements sqlops.InputBoxComponent {

View File

@@ -391,7 +391,8 @@ export function createApiFactory(
workspace, workspace,
queryeditor: queryEditor, queryeditor: queryEditor,
ui: ui, ui: ui,
StatusIndicator: sqlExtHostTypes.StatusIndicator StatusIndicator: sqlExtHostTypes.StatusIndicator,
CardType: sqlExtHostTypes.CardType
}; };
} }
}; };