added dropdown and form layout to model view (#1269)

* added dropdown and form layout to model view
This commit is contained in:
Leila Lali
2018-04-27 15:43:23 -07:00
committed by GitHub
parent 26b27a616a
commit 886717d330
13 changed files with 569 additions and 45 deletions

View File

@@ -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: `
<div #input style="width: 100%"></div>
`
})
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<sqlops.ButtonProperties, string>((props) => props.label, '');
}
private set label(newValue: string) {
this.setPropertyFromUI<sqlops.ButtonProperties, string>(this.setValueProperties, newValue);
}
private setValueProperties(properties: sqlops.ButtonProperties, label: string): void {
properties.label = label;
}
}

View File

@@ -75,7 +75,7 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
return types.isUndefinedOrNull(property) ? defaultVal : property;
}
protected setProperty<TPropertyBag, TValue>(propertySetter: (TPropertyBag, TValue) => void, value: TValue) {
protected setPropertyFromUI<TPropertyBag, TValue>(propertySetter: (TPropertyBag, TValue) => void, value: TValue) {
propertySetter(this.getProperties<TPropertyBag>(), value);
this._onEventEmitter.fire({
eventType: ComponentEventType.PropertiesChanged,
@@ -86,6 +86,12 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
public get onEvent(): Event<IComponentEventArgs> {
return this._onEventEmitter.event;
}
public get title(): string {
let properties = this.getProperties();
let title = properties['title'];
return title ? <string>title : '';
}
}
export abstract class ContainerBase<T> extends ComponentBase {

View File

@@ -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);

View File

@@ -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: `
<div #input style="width: 100%"></div>
`
})
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<sqlops.DropDownProperties, string>((props) => props.value, '');
}
private set value(newValue: string) {
this.setPropertyFromUI<sqlops.DropDownProperties, string>(this.setValueProperties, newValue);
}
private get values(): string[] {
return this.getPropertyOrDefault<sqlops.DropDownProperties, string[]>((props) => props.values, undefined);
}
private set values(newValue: string[]) {
this.setPropertyFromUI<sqlops.DropDownProperties, string[]>(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;
}
}

View File

@@ -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: `
<div #container *ngIf="items" class="form-table"
[style.alignItems]="alignItems" [style.alignContent]="alignContent">
<div *ngFor="let item of items" class="form-row">
<ng-container *ngIf="isFormComponent(item)">
<div class="form-cell">{{getItemTitle(item)}}</div>
<div class="form-cell">
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
</model-component-wrapper>
</div>
<div *ngIf="itemHasActions(item)" class="form-cell">
<div *ngFor="let actionItem of getActionComponents(item)" >
<model-component-wrapper [descriptor]="actionItem.descriptor" [modelStore]="modelStore">
</model-component-wrapper>
</div>
</div>
</ng-container>
</div>
</div>
`
})
export default class FormContainer extends ContainerBase<FormItemLayout> implements IComponent, OnDestroy, AfterViewInit {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
private _alignItems: string;
private _alignContent: string;
@ViewChildren(ModelComponentWrapper) private _componentWrappers: QueryList<ModelComponentWrapper>;
@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 <FormItem>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();
}
}

View File

@@ -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;
}

View File

@@ -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<sqlops.InputBoxProperties, string>(this.setInputBoxProperties, newValue);
this.setPropertyFromUI<sqlops.InputBoxProperties, string>(this.setInputBoxProperties, newValue);
}
private setInputBoxProperties(properties: sqlops.InputBoxProperties, value: string): void {

View File

@@ -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<IComponentEventArgs>;
}
@@ -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 {

View File

@@ -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 = <IComponentRegistry> Registry.as(Extensions.ComponentContribution);
const componentRegistry = <IComponentRegistry>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<T>(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<any> {
public get onEvent(): Event<IComponentEventArgs> {
return this._onEventEmitter.event;
}
}

View File

@@ -20,23 +20,30 @@ declare module 'sqlops' {
flexContainer(): FlexBuilder;
card(): ComponentBuilder<CardComponent>;
inputBox(): ComponentBuilder<InputBoxComponent>;
button(): ComponentBuilder<ButtonComponent>;
dropDown(): ComponentBuilder<DropDownComponent>;
dashboardWidget(widgetId: string): ComponentBuilder<WidgetComponent>;
dashboardWebview(webviewId: string): ComponentBuilder<WebviewComponent>;
formContainer(): FormBuilder;
}
export interface ComponentBuilder<T extends Component> {
component(): T;
withProperties<U>(properties: U): ComponentBuilder<T>;
}
export interface ContainerBuilder<T extends Component, TLayout,TItemLayout> extends ComponentBuilder<T> {
export interface ContainerBuilder<T extends Component, TLayout, TItemLayout> extends ComponentBuilder<T> {
withLayout(layout: TLayout): ContainerBuilder<T, TLayout, TItemLayout>;
withItems(components: Array<Component>, itemLayout ?: TItemLayout): ContainerBuilder<T, TLayout, TItemLayout>;
withItems(components: Array<Component>, itemLayout?: TItemLayout): ContainerBuilder<T, TLayout, TItemLayout>;
}
export interface FlexBuilder extends ContainerBuilder<FlexContainer, FlexLayout, FlexItemLayout> {
}
export interface FormBuilder extends ContainerBuilder<FormContainer, FormLayout, FormItemLayout> {
withFormItems(components: FormComponent[], itemLayout?: FormItemLayout): ContainerBuilder<FormContainer, FormLayout, FormItemLayout>;
}
export interface Component {
readonly id: string;
@@ -50,10 +57,16 @@ declare module 'sqlops' {
updateProperties(properties: { [key: string]: any }): Thenable<boolean>;
}
export interface FormComponent {
component: Component;
title: string;
actions?: Component[];
}
/**
* A component that contains other components
*/
export interface Container<TLayout,TItemLayout> extends Component {
export interface Container<TLayout, TItemLayout> 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<Component>, itemLayout ?: TItemLayout): void;
addItems(itemConfigs: Array<Component>, 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<FlexLayout, FlexItemLayout> {
}
export interface FormContainer extends Container<FormLayout, FormItemLayout> {
}
/**
* 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<any>;
}
export interface DropDownComponent extends Component {
value: string;
values: string[];
onValueChanged: vscode.Event<any>;
}
export interface ButtonComponent extends Component {
label: string;
onDidClick: vscode.Event<any>;
}
export interface WidgetComponent extends Component {
widgetId: string;
}

View File

@@ -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 {

View File

@@ -33,6 +33,11 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
return new ContainerBuilderImpl<sqlops.FlexContainer, sqlops.FlexLayout, sqlops.FlexItemLayout>(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<sqlops.CardComponent> {
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<sqlops.ButtonComponent> {
let id = this.getNextComponentId();
return this.withEventHandler(new ButtonWrapper(this._proxy, this._handle, id), id);
}
dropDown(): sqlops.ComponentBuilder<sqlops.DropDownComponent> {
let id = this.getNextComponentId();
return this.withEventHandler(new DropDownWrapper(this._proxy, this._handle, id), id);
}
dashboardWidget(widgetId: string): sqlops.ComponentBuilder<sqlops.WidgetComponent> {
let id = this.getNextComponentId();
return this.withEventHandler<sqlops.WidgetComponent>(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWidget, id), id);
@@ -122,9 +137,40 @@ class ContainerBuilderImpl<T extends sqlops.Component, TLayout, TItemLayout> ext
}
}
class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sqlops.FormLayout, sqlops.FormItemLayout> {
withFormItems(components: sqlops.FormComponent[], itemLayout?: sqlops.FormItemLayout): sqlops.ContainerBuilder<sqlops.FormContainer, sqlops.FormLayout, sqlops.FormItemLayout> {
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<Error>();
public readonly onError: vscode.Event<Error> = this._onErrorEmitter.event;
protected _emitterMap = new Map<ComponentEventType, Emitter<any>>();
constructor(protected readonly _proxy: MainThreadModelViewShape,
protected readonly _handle: number,
@@ -169,7 +216,7 @@ class ComponentWrapper implements sqlops.Component {
}
public toComponentShape(): IComponentShape {
return <IComponentShape> {
return <IComponentShape>{
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<sqlops.Component>, itemLayout ?: any): void {
for(let item of items) {
public addItems(items: Array<sqlops.Component>, 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<boolean> {
@@ -278,9 +330,6 @@ class InputBoxWrapper extends ComponentWrapper implements sqlops.InputBoxCompone
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<any>());
}
private _onTextChangedEmitter = new Emitter<any>();
private _emitterMap = new Map<ComponentEventType, Emitter<any>>();
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<any>());
}
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<any> {
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<any>());
}
public get label(): string {
return this.properties['label'];
}
public set label(v: string) {
this.setProperty('label', v);
}
public get onDidClick(): vscode.Event<any> {
let emitter = this._emitterMap.get(ComponentEventType.onDidClick);
return emitter && emitter.event;
}
}

View File

@@ -45,7 +45,7 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi
}
$initializeModel(handle: number, rootComponent: IComponentShape): Thenable<void> {
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<void> {
$registerEvent(handle: number, componentId: string): Thenable<void> {
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);
}
}));
});
}