diff --git a/src/sql/parts/modelComponents/button.component.ts b/src/sql/parts/modelComponents/button.component.ts
new file mode 100644
index 0000000000..e884d71a1b
--- /dev/null
+++ b/src/sql/parts/modelComponents/button.component.ts
@@ -0,0 +1,97 @@
+/*---------------------------------------------------------------------------------------------
+ * 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, QueryList, AfterViewInit
+} from '@angular/core';
+
+import * as sqlops from 'sqlops';
+import Event, { Emitter } from 'vs/base/common/event';
+
+import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
+import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
+import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
+import { attachListStyler } from 'vs/platform/theme/common/styler';
+import { attachButtonStyler } from 'sql/common/theme/styler';
+import { Button } from 'sql/base/browser/ui/button/button';
+import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
+
+@Component({
+ selector: 'button',
+ template: `
+
+ `
+})
+export default class ButtonComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
+ @Input() descriptor: IComponentDescriptor;
+ @Input() modelStore: IModelStore;
+ private _button: Button;
+
+ @ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
+ constructor(
+ @Inject(forwardRef(() => CommonServiceInterface)) private _commonService: CommonServiceInterface,
+ @Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
+ super(changeRef);
+ }
+
+ ngOnInit(): void {
+ this.baseInit();
+
+ }
+
+ ngAfterViewInit(): void {
+ if (this._inputContainer) {
+
+
+ this._button = new Button(this._inputContainer.nativeElement);
+
+ this._register(this._button);
+ this._register(attachButtonStyler(this._button, this._commonService.themeService, {
+ buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND
+ }));
+ this._register(this._button.onDidClick(e => {
+ this._onEventEmitter.fire({
+ eventType: ComponentEventType.onDidClick,
+ args: e
+ });
+ }));
+ }
+ }
+
+ ngOnDestroy(): void {
+ this.baseDestroy();
+ }
+
+ /// IComponent implementation
+
+ public layout(): void {
+ this._changeRef.detectChanges();
+ }
+
+ public setLayout(layout: any): void {
+ // TODO allow configuring the look and feel
+ this.layout();
+ }
+
+ public setProperties(properties: { [key: string]: any; }): void {
+ super.setProperties(properties);
+ this._button.label = this.label;
+ }
+
+ // CSS-bound properties
+
+ private get label(): string {
+ return this.getPropertyOrDefault((props) => props.label, '');
+ }
+
+ private set label(newValue: string) {
+ this.setPropertyFromUI(this.setValueProperties, newValue);
+ }
+
+ private setValueProperties(properties: sqlops.ButtonProperties, label: string): void {
+ properties.label = label;
+ }
+}
diff --git a/src/sql/parts/modelComponents/componentBase.ts b/src/sql/parts/modelComponents/componentBase.ts
index 558019c63a..3ffb9d4827 100644
--- a/src/sql/parts/modelComponents/componentBase.ts
+++ b/src/sql/parts/modelComponents/componentBase.ts
@@ -75,7 +75,7 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
return types.isUndefinedOrNull(property) ? defaultVal : property;
}
- protected setProperty(propertySetter: (TPropertyBag, TValue) => void, value: TValue) {
+ protected setPropertyFromUI(propertySetter: (TPropertyBag, TValue) => void, value: TValue) {
propertySetter(this.getProperties(), value);
this._onEventEmitter.fire({
eventType: ComponentEventType.PropertiesChanged,
@@ -86,6 +86,12 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
public get onEvent(): Event {
return this._onEventEmitter.event;
}
+
+ public get title(): string {
+ let properties = this.getProperties();
+ let title = properties['title'];
+ return title ? title : '';
+ }
}
export abstract class ContainerBase extends ComponentBase {
diff --git a/src/sql/parts/modelComponents/components.contribution.ts b/src/sql/parts/modelComponents/components.contribution.ts
index 25485ff4fb..45047b89d8 100644
--- a/src/sql/parts/modelComponents/components.contribution.ts
+++ b/src/sql/parts/modelComponents/components.contribution.ts
@@ -4,16 +4,28 @@
*--------------------------------------------------------------------------------------------*/
import FlexContainer from './flexContainer.component';
+import FormContainer from './formContainer.component';
import CardComponent from './card.component';
import InputBoxComponent from './inputbox.component';
+import DropDownComponent from './dropdown.component';
+import ButtonComponent from './button.component';
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
export const FLEX_CONTAINER = 'flex-container';
registerComponentType(FLEX_CONTAINER, ModelComponentTypes.FlexContainer, FlexContainer);
+export const FORM_CONTAINER = 'form-container';
+registerComponentType(FORM_CONTAINER, ModelComponentTypes.Form, FormContainer);
+
export const CARD_COMPONENT = 'card-component';
registerComponentType(CARD_COMPONENT, ModelComponentTypes.Card, CardComponent);
export const INPUTBOX_COMPONENT = 'inputbox-component';
registerComponentType(INPUTBOX_COMPONENT, ModelComponentTypes.InputBox, InputBoxComponent);
+
+export const DROPDOWN_COMPONENT = 'dropdown-component';
+registerComponentType(DROPDOWN_COMPONENT, ModelComponentTypes.DropDown, DropDownComponent);
+
+export const BUTTON_COMPONENT = 'button-component';
+registerComponentType(BUTTON_COMPONENT, ModelComponentTypes.Button, ButtonComponent);
diff --git a/src/sql/parts/modelComponents/dropdown.component.ts b/src/sql/parts/modelComponents/dropdown.component.ts
new file mode 100644
index 0000000000..c4afb27ac4
--- /dev/null
+++ b/src/sql/parts/modelComponents/dropdown.component.ts
@@ -0,0 +1,117 @@
+/*---------------------------------------------------------------------------------------------
+ * 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, QueryList, AfterViewInit
+} from '@angular/core';
+
+import * as sqlops from 'sqlops';
+import Event, { Emitter } from 'vs/base/common/event';
+
+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 { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
+import { attachListStyler } from 'vs/platform/theme/common/styler';
+import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
+
+@Component({
+ selector: 'inputBox',
+ template: `
+
+ `
+})
+export default class DropDownComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
+ @Input() descriptor: IComponentDescriptor;
+ @Input() modelStore: IModelStore;
+ private _dropdown: Dropdown;
+
+ @ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
+ constructor(
+ @Inject(forwardRef(() => CommonServiceInterface)) private _commonService: CommonServiceInterface,
+ @Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
+ super(changeRef);
+ }
+
+ ngOnInit(): void {
+ this.baseInit();
+
+ }
+
+ ngAfterViewInit(): void {
+ if (this._inputContainer) {
+ let dropdownOptions: IDropdownOptions = {
+ values: [],
+ strictSelection: false,
+ placeholder: '',
+ maxHeight: 125,
+ ariaLabel: ''
+ };
+
+ this._dropdown = new Dropdown(this._inputContainer.nativeElement, this._commonService.contextViewService, this._commonService.themeService,
+ dropdownOptions);
+
+ this._register(this._dropdown);
+ this._register(attachEditableDropdownStyler(this._dropdown, this._commonService.themeService));
+ this._register(this._dropdown.onValueChange(e => {
+ this.value = this._dropdown.value;
+ this._onEventEmitter.fire({
+ eventType: ComponentEventType.onDidChange,
+ args: e
+ });
+ }));
+ }
+ }
+
+ ngOnDestroy(): void {
+ this.baseDestroy();
+ }
+
+ /// IComponent implementation
+
+ public layout(): void {
+ this._changeRef.detectChanges();
+ }
+
+ public setLayout(layout: any): void {
+ // TODO allow configuring the look and feel
+ this.layout();
+ }
+
+ public setProperties(properties: { [key: string]: any; }): void {
+ super.setProperties(properties);
+ this._dropdown.values = this.values ? this.values : [];
+ if (this.value) {
+ this._dropdown.value = this.value;
+ }
+ }
+
+ // CSS-bound properties
+
+ private get value(): string {
+ return this.getPropertyOrDefault((props) => props.value, '');
+ }
+
+ private set value(newValue: string) {
+ this.setPropertyFromUI(this.setValueProperties, newValue);
+ }
+
+ private get values(): string[] {
+ return this.getPropertyOrDefault((props) => props.values, undefined);
+ }
+
+ private set values(newValue: string[]) {
+ this.setPropertyFromUI(this.setValuesProperties, newValue);
+ }
+
+ private setValueProperties(properties: sqlops.DropDownProperties, value: string): void {
+ properties.value = value;
+ }
+
+ private setValuesProperties(properties: sqlops.DropDownProperties, values: string[]): void {
+ properties.values = values;
+ }
+}
diff --git a/src/sql/parts/modelComponents/formContainer.component.ts b/src/sql/parts/modelComponents/formContainer.component.ts
new file mode 100644
index 0000000000..0190b9dab8
--- /dev/null
+++ b/src/sql/parts/modelComponents/formContainer.component.ts
@@ -0,0 +1,128 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import 'vs/css!./formLayout';
+
+import {
+ Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
+ ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
+} from '@angular/core';
+
+import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
+import { FormLayout, FormItemLayout } from 'sqlops';
+
+import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
+import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
+import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
+import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
+
+export interface TitledFormItemLayout {
+ title: string;
+ actions?: string[];
+ isFormComponent: Boolean;
+}
+class FormItem {
+ constructor(public descriptor: IComponentDescriptor, public config: TitledFormItemLayout) { }
+}
+
+@Component({
+ template: `
+
+ `
+})
+export default class FormContainer extends ContainerBase implements IComponent, OnDestroy, AfterViewInit {
+ @Input() descriptor: IComponentDescriptor;
+ @Input() modelStore: IModelStore;
+
+ private _alignItems: string;
+ private _alignContent: string;
+
+ @ViewChildren(ModelComponentWrapper) private _componentWrappers: QueryList;
+ @ViewChild('container', { read: ElementRef }) private _container: ElementRef;
+
+ constructor (
+ @Inject(forwardRef(() => CommonServiceInterface)) private _commonService: CommonServiceInterface,
+ @Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
+ super(changeRef);
+ }
+
+ ngOnInit(): void {
+ this.baseInit();
+ }
+
+ ngOnDestroy(): void {
+ this.baseDestroy();
+ }
+
+ ngAfterViewInit(): void {
+ }
+
+ /// IComponent implementation
+
+ public layout(): void {
+ if (this._componentWrappers) {
+ this._componentWrappers.forEach(wrapper => {
+ wrapper.layout();
+ });
+ }
+ }
+
+ public get alignItems(): string {
+ return this._alignItems;
+ }
+
+ public get alignContent(): string {
+ return this._alignContent;
+ }
+
+ private getItemTitle(item: FormItem): string {
+ let itemConfig = item.config;
+ return itemConfig ? itemConfig.title : '';
+ }
+
+ private getActionComponents(item: FormItem): FormItem[]{
+ let items = this.items;
+ let itemConfig = item.config;
+ if (itemConfig && itemConfig.actions) {
+ let resultItems = itemConfig.actions.map(x => {
+ let actionComponent = items.find(i => i.descriptor.id === x);
+ return actionComponent;
+ });
+
+ return resultItems.filter(r => r && r.descriptor);
+ }
+
+ return [];
+ }
+
+ private isFormComponent(item: FormItem): Boolean {
+ return item && item.config && item.config.isFormComponent;
+ }
+
+ private itemHasActions(item: FormItem): Boolean {
+ let itemConfig = item.config;
+ return itemConfig && itemConfig.actions !== undefined && itemConfig.actions.length > 0;
+ }
+
+ public setLayout(layout: any): void {
+ this.layout();
+ }
+}
diff --git a/src/sql/parts/modelComponents/formLayout.css b/src/sql/parts/modelComponents/formLayout.css
new file mode 100644
index 0000000000..fa4291768a
--- /dev/null
+++ b/src/sql/parts/modelComponents/formLayout.css
@@ -0,0 +1,20 @@
+
+.form-table {
+ width:400px;
+ display:table;
+ padding: 30px;
+}
+
+.form-row {
+ display: table-row;
+ width: 100px;
+}
+
+.form-cell {
+ padding: 5px;
+ display: table-cell;
+}
+
+.form-action {
+ width: 20px;
+}
\ No newline at end of file
diff --git a/src/sql/parts/modelComponents/inputbox.component.ts b/src/sql/parts/modelComponents/inputbox.component.ts
index af8ec53fe2..680e5a5689 100644
--- a/src/sql/parts/modelComponents/inputbox.component.ts
+++ b/src/sql/parts/modelComponents/inputbox.component.ts
@@ -3,7 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
+import {
+ Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
} from '@angular/core';
@@ -70,7 +71,7 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
this._changeRef.detectChanges();
}
- public setLayout (layout: any): void {
+ public setLayout(layout: any): void {
// TODO allow configuring the look and feel
this.layout();
}
@@ -87,7 +88,7 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
}
public set value(newValue: string) {
- this.setProperty(this.setInputBoxProperties, newValue);
+ this.setPropertyFromUI(this.setInputBoxProperties, newValue);
}
private setInputBoxProperties(properties: sqlops.InputBoxProperties, value: string): void {
diff --git a/src/sql/parts/modelComponents/interfaces.ts b/src/sql/parts/modelComponents/interfaces.ts
index b455d6d63e..d457839444 100644
--- a/src/sql/parts/modelComponents/interfaces.ts
+++ b/src/sql/parts/modelComponents/interfaces.ts
@@ -21,6 +21,7 @@ export interface IComponent {
addToContainer?: (componentDescriptor: IComponentDescriptor, config: any) => void;
setLayout?: (layout: any) => void;
setProperties?: (properties: { [key: string]: any; }) => void;
+ title?: string;
onEvent?: Event;
}
@@ -53,11 +54,13 @@ export interface IComponentDescriptor {
export interface IComponentEventArgs {
eventType: ComponentEventType;
args: any;
+ componentId?: string;
}
export enum ComponentEventType {
PropertiesChanged,
- onDidChange
+ onDidChange,
+ onDidClick
}
export interface IModelStore {
diff --git a/src/sql/parts/modelComponents/viewBase.ts b/src/sql/parts/modelComponents/viewBase.ts
index ab203ff81e..ce3a37f757 100644
--- a/src/sql/parts/modelComponents/viewBase.ts
+++ b/src/sql/parts/modelComponents/viewBase.ts
@@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import nls = require('vs/nls');
import * as sqlops from 'sqlops';
-import { IModelStore, IComponentDescriptor, IComponent } from './interfaces';
+import { IModelStore, IComponentDescriptor, IComponent, IComponentEventArgs } from './interfaces';
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IModelView } from 'sql/services/model/modelViewService';
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
@@ -18,7 +18,7 @@ import { AngularDisposable } from 'sql/base/common/lifecycle';
import { ModelStore } from 'sql/parts/modelComponents/modelStore';
import Event, { Emitter } from 'vs/base/common/event';
-const componentRegistry = Registry.as(Extensions.ComponentContribution);
+const componentRegistry = Registry.as(Extensions.ComponentContribution);
/**
* Provides common logic required for any implementation that hooks to a model provided by
@@ -57,7 +57,7 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
this.setLayout(component.id, component.layout);
this.registerEvent(component.id);
if (component.itemConfigs) {
- for(let item of component.itemConfigs) {
+ for (let item of component.itemConfigs) {
this.addToContainer(component.id, item);
}
}
@@ -66,12 +66,12 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
}
clearContainer(componentId: string): void {
- this.queueAction(componentId, (component) => component.clearContainer());
+ this.queueAction(componentId, (component) => component.clearContainer());
}
addToContainer(containerId: string, itemConfig: IItemConfig): void {
// Do not return the promise as this should be non-blocking
- this.queueAction(containerId, (component) => {
+ this.queueAction(containerId, (component) => {
let childDescriptor = this.defineComponent(itemConfig.componentShape);
component.addToContainer(childDescriptor, itemConfig.config);
});
@@ -81,14 +81,14 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
if (!layout) {
return;
}
- this.queueAction(componentId, (component) => component.setLayout(layout));
+ this.queueAction(componentId, (component) => component.setLayout(layout));
}
setProperties(componentId: string, properties: { [key: string]: any; }): void {
if (!properties) {
return;
}
- this.queueAction(componentId, (component) => component.setProperties(properties));
+ this.queueAction(componentId, (component) => component.setProperties(properties));
}
private queueAction(componentId: string, action: (component: IComponent) => T): void {
@@ -98,16 +98,17 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
}
registerEvent(componentId: string) {
- this.queueAction(componentId, (component) => {
+ this.queueAction(componentId, (component) => {
if (component.onEvent) {
this._register(component.onEvent(e => {
+ e.componentId = componentId;
this._onEventEmitter.fire(e);
}));
}
});
}
- public get onEvent(): Event {
+ public get onEvent(): Event {
return this._onEventEmitter.event;
}
}
\ No newline at end of file
diff --git a/src/sql/sqlops.proposed.d.ts b/src/sql/sqlops.proposed.d.ts
index 13e93af70e..895ee4c6c4 100644
--- a/src/sql/sqlops.proposed.d.ts
+++ b/src/sql/sqlops.proposed.d.ts
@@ -20,23 +20,30 @@ declare module 'sqlops' {
flexContainer(): FlexBuilder;
card(): ComponentBuilder;
inputBox(): ComponentBuilder;
+ button(): ComponentBuilder;
+ dropDown(): ComponentBuilder;
dashboardWidget(widgetId: string): ComponentBuilder;
dashboardWebview(webviewId: string): ComponentBuilder;
+ formContainer(): FormBuilder;
}
export interface ComponentBuilder {
component(): T;
withProperties(properties: U): ComponentBuilder;
}
- export interface ContainerBuilder extends ComponentBuilder {
+ export interface ContainerBuilder extends ComponentBuilder {
withLayout(layout: TLayout): ContainerBuilder;
- withItems(components: Array, itemLayout ?: TItemLayout): ContainerBuilder;
+ withItems(components: Array, itemLayout?: TItemLayout): ContainerBuilder;
}
export interface FlexBuilder extends ContainerBuilder {
}
+ export interface FormBuilder extends ContainerBuilder {
+ withFormItems(components: FormComponent[], itemLayout?: FormItemLayout): ContainerBuilder;
+ }
+
export interface Component {
readonly id: string;
@@ -50,10 +57,16 @@ declare module 'sqlops' {
updateProperties(properties: { [key: string]: any }): Thenable;
}
+ export interface FormComponent {
+ component: Component;
+ title: string;
+ actions?: Component[];
+ }
+
/**
* A component that contains other components
*/
- export interface Container extends Component {
+ export interface Container extends Component {
/**
* A copy of the child items array. This cannot be added to directly -
* components must be created using the create methods instead
@@ -70,7 +83,7 @@ declare module 'sqlops' {
* @param itemConfigs the definitions
* @param {*} [itemLayout] Optional layout for the child items
*/
- addItems(itemConfigs: Array, itemLayout ?: TItemLayout): void;
+ addItems(itemConfigs: Array, itemLayout?: TItemLayout): void;
/**
* Creates a child component and adds it to this container.
@@ -78,7 +91,7 @@ declare module 'sqlops' {
* @param {Component} component the component to be added
* @param {*} [itemLayout] Optional layout for this child item
*/
- addItem(component: Component, itemLayout ?: TItemLayout): void;
+ addItem(component: Component, itemLayout?: TItemLayout): void;
/**
* Defines the layout for this container
@@ -130,9 +143,21 @@ declare module 'sqlops' {
flex?: string;
}
+ export interface FormItemLayout {
+
+ }
+
+ export interface FormLayout {
+
+ }
+
export interface FlexContainer extends Container {
}
+ export interface FormContainer extends Container {
+ }
+
+
/**
* Describes an action to be shown in the UI, with a user-readable label
* and a callback to execute the action
@@ -153,16 +178,25 @@ declare module 'sqlops' {
* Properties representing the card component, can be used
* when using ModelBuilder to create the component
*/
- export interface CardProperties {
+ export interface CardProperties {
label: string;
value?: string;
actions?: ActionDescriptor[];
}
- export interface InputBoxProperties {
+ export interface InputBoxProperties {
value?: string;
}
+ export interface DropDownProperties {
+ value?: string;
+ values?: string[];
+ }
+
+ export interface ButtonProperties {
+ label?: string;
+ }
+
export interface CardComponent extends Component {
label: string;
value: string;
@@ -174,6 +208,17 @@ declare module 'sqlops' {
onTextChanged: vscode.Event;
}
+ export interface DropDownComponent extends Component {
+ value: string;
+ values: string[];
+ onValueChanged: vscode.Event;
+ }
+
+ export interface ButtonComponent extends Component {
+ label: string;
+ onDidClick: vscode.Event;
+ }
+
export interface WidgetComponent extends Component {
widgetId: string;
}
diff --git a/src/sql/workbench/api/common/sqlExtHostTypes.ts b/src/sql/workbench/api/common/sqlExtHostTypes.ts
index 9fc4ab3bbe..24f98ad7f1 100644
--- a/src/sql/workbench/api/common/sqlExtHostTypes.ts
+++ b/src/sql/workbench/api/common/sqlExtHostTypes.ts
@@ -68,14 +68,17 @@ export enum ModelComponentTypes {
FlexContainer,
Card,
InputBox,
+ DropDown,
+ Button,
DashboardWidget,
- DashboardWebview
+ DashboardWebview,
+ Form
}
export interface IComponentShape {
type: ModelComponentTypes;
id: string;
- properties?: { [key: string]: any };
+ properties?: { [key: string]: any };
layout?: any;
itemConfigs?: IItemConfig[];
}
@@ -87,7 +90,8 @@ export interface IItemConfig {
export enum ComponentEventType {
PropertiesChanged,
- onDidChange
+ onDidChange,
+ onDidClick
}
export interface IComponentEventArgs {
diff --git a/src/sql/workbench/api/node/extHostModelView.ts b/src/sql/workbench/api/node/extHostModelView.ts
index c0458bd97f..d5d517da19 100644
--- a/src/sql/workbench/api/node/extHostModelView.ts
+++ b/src/sql/workbench/api/node/extHostModelView.ts
@@ -33,6 +33,11 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
return new ContainerBuilderImpl(this._proxy, this._handle, ModelComponentTypes.FlexContainer, id);
}
+ formContainer(): sqlops.FormBuilder {
+ let id = this.getNextComponentId();
+ return new FormContainerBuilder(this._proxy, this._handle, ModelComponentTypes.Form, id);
+ }
+
card(): sqlops.ComponentBuilder {
let id = this.getNextComponentId();
return this.withEventHandler(new CardWrapper(this._proxy, this._handle, id), id);
@@ -43,6 +48,16 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
return this.withEventHandler(new InputBoxWrapper(this._proxy, this._handle, id), id);
}
+ button(): sqlops.ComponentBuilder {
+ let id = this.getNextComponentId();
+ return this.withEventHandler(new ButtonWrapper(this._proxy, this._handle, id), id);
+ }
+
+ dropDown(): sqlops.ComponentBuilder {
+ let id = this.getNextComponentId();
+ return this.withEventHandler(new DropDownWrapper(this._proxy, this._handle, id), id);
+ }
+
dashboardWidget(widgetId: string): sqlops.ComponentBuilder {
let id = this.getNextComponentId();
return this.withEventHandler(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWidget, id), id);
@@ -122,9 +137,40 @@ class ContainerBuilderImpl ext
}
}
+class FormContainerBuilder extends ContainerBuilderImpl {
+
+ withFormItems(components: sqlops.FormComponent[], itemLayout?: sqlops.FormItemLayout): sqlops.ContainerBuilder {
+
+ this._component.itemConfigs = components.map(item => {
+ let componentWrapper = item.component as ComponentWrapper;
+ let actions: string[] = undefined;
+ if (item.actions) {
+ actions = item.actions.map(action => {
+ let actionComponentWrapper = action as ComponentWrapper;
+ return actionComponentWrapper.id;
+ });
+ }
+ return new InternalItemConfig(componentWrapper, Object.assign({}, itemLayout, {
+ title: item.title,
+ actions: actions,
+ isFormComponent: true
+ }));
+ });
+
+ components.forEach(formItem => {
+ if (formItem.actions) {
+ formItem.actions.forEach(component => {
+ let componentWrapper = component as ComponentWrapper;
+ this._component.itemConfigs.push(new InternalItemConfig(componentWrapper, itemLayout));
+ });
+ }
+ });
+ return this;
+ }
+}
class InternalItemConfig {
- constructor(private _component: ComponentWrapper, public config: any) {}
+ constructor(private _component: ComponentWrapper, public config: any) { }
public toIItemConfig(): IItemConfig {
return {
@@ -146,6 +192,7 @@ class ComponentWrapper implements sqlops.Component {
private _onErrorEmitter = new Emitter();
public readonly onError: vscode.Event = this._onErrorEmitter.event;
+ protected _emitterMap = new Map>();
constructor(protected readonly _proxy: MainThreadModelViewShape,
protected readonly _handle: number,
@@ -169,7 +216,7 @@ class ComponentWrapper implements sqlops.Component {
}
public toComponentShape(): IComponentShape {
- return {
+ return {
id: this.id,
type: this.type,
layout: this.layout,
@@ -183,13 +230,13 @@ class ComponentWrapper implements sqlops.Component {
return this._proxy.$clearContainer(this._handle, this.id);
}
- public addItems(items: Array, itemLayout ?: any): void {
- for(let item of items) {
+ public addItems(items: Array, itemLayout?: any): void {
+ for (let item of items) {
this.addItem(item, itemLayout);
}
}
- public addItem(item: sqlops.Component, itemLayout ?: any): void {
+ public addItem(item: sqlops.Component, itemLayout?: any): void {
let itemImpl = item as ComponentWrapper;
if (!itemImpl) {
throw new Error(nls.localize('unknownComponentType', 'Unkown component type. Must use ModelBuilder to create objects'));
@@ -218,7 +265,12 @@ class ComponentWrapper implements sqlops.Component {
public onEvent(eventArgs: IComponentEventArgs) {
if (eventArgs && eventArgs.eventType === ComponentEventType.PropertiesChanged) {
this.properties = eventArgs.args;
- }
+ } else if (eventArgs) {
+ let emitter = this._emitterMap.get(eventArgs.eventType);
+ if (emitter) {
+ emitter.fire();
+ }
+ }
}
protected setProperty(key: string, value: any): Thenable {
@@ -278,9 +330,6 @@ class InputBoxWrapper extends ComponentWrapper implements sqlops.InputBoxCompone
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter());
}
- private _onTextChangedEmitter = new Emitter();
- private _emitterMap = new Map>();
-
public get value(): string {
return this.properties['value'];
}
@@ -292,15 +341,54 @@ class InputBoxWrapper extends ComponentWrapper implements sqlops.InputBoxCompone
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
return emitter && emitter.event;
}
+}
- public onEvent(eventArgs: IComponentEventArgs) {
- super.onEvent(eventArgs);
- if (eventArgs) {
- let emitter = this._emitterMap.get(eventArgs.eventType);
- if (emitter) {
- emitter.fire();
- }
- }
+class DropDownWrapper extends ComponentWrapper implements sqlops.DropDownComponent {
+
+ constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
+ super(proxy, handle, ModelComponentTypes.DropDown, id);
+ this.properties = {};
+ this._emitterMap.set(ComponentEventType.onDidChange, new Emitter());
+ }
+
+ public get value(): string {
+ return this.properties['value'];
+ }
+ public set value(v: string) {
+ this.setProperty('value', v);
+ }
+
+ public get values(): string[] {
+ return this.properties['values'];
+ }
+ public set values(v: string[]) {
+ this.setProperty('values', v);
+ }
+
+ public get onValueChanged(): vscode.Event {
+ let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
+ return emitter && emitter.event;
+ }
+}
+
+class ButtonWrapper extends ComponentWrapper implements sqlops.ButtonComponent {
+
+ constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
+ super(proxy, handle, ModelComponentTypes.Button, id);
+ this.properties = {};
+ this._emitterMap.set(ComponentEventType.onDidClick, new Emitter());
+ }
+
+ public get label(): string {
+ return this.properties['label'];
+ }
+ public set label(v: string) {
+ this.setProperty('label', v);
+ }
+
+ public get onDidClick(): vscode.Event {
+ let emitter = this._emitterMap.get(ComponentEventType.onDidClick);
+ return emitter && emitter.event;
}
}
diff --git a/src/sql/workbench/api/node/mainThreadModelView.ts b/src/sql/workbench/api/node/mainThreadModelView.ts
index 4c30fe6f5e..0cb1726819 100644
--- a/src/sql/workbench/api/node/mainThreadModelView.ts
+++ b/src/sql/workbench/api/node/mainThreadModelView.ts
@@ -45,7 +45,7 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi
}
$initializeModel(handle: number, rootComponent: IComponentShape): Thenable {
- return this.execModelViewAction(handle, (modelView) => {
+ return this.execModelViewAction(handle, (modelView) => {
modelView.initializeModel(rootComponent);
});
}
@@ -67,11 +67,13 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi
this._proxy.$handleEvent(handle, componentId, eventArgs);
}
- $registerEvent(handle: number, componentId: string): Thenable {
+ $registerEvent(handle: number, componentId: string): Thenable {
let properties: { [key: string]: any; } = { eventName: this.onEvent };
return this.execModelViewAction(handle, (modelView) => {
- this._register(modelView.onEvent (e => {
- this.onEvent(handle, componentId, e);
+ this._register(modelView.onEvent(e => {
+ if (e.componentId && e.componentId === componentId) {
+ this.onEvent(handle, componentId, e);
+ }
}));
});
}