diff --git a/samples/sqlservices/src/controllers/mainController.ts b/samples/sqlservices/src/controllers/mainController.ts index dbbe38c633..7f02a3ca63 100644 --- a/samples/sqlservices/src/controllers/mainController.ts +++ b/samples/sqlservices/src/controllers/mainController.ts @@ -171,12 +171,28 @@ export default class MainController implements vscode.Disposable { inputBox.value = radioButton.value; groupModel1.enabled = false; }); + let table = view.modelBuilder.table().withProperties({ + data: [ + ['1', '2', '2'], + ['4', '5', '6'], + ['7', '8', '9'] + ], columns: ['c1', 'c2', 'c3'], + height: 250, + selectedRows: [0] + }).component(); + table.onRowSelected(e => { + // TODO: + }); + let listBox = view.modelBuilder.listBox().withProperties({ + values: ['1', '2', '3'], + selectedRow: 2 + }).component(); let flexRadioButtonsModel = view.modelBuilder.flexContainer() .withLayout({ flexFlow: 'column', alignItems: 'left', - height: 50 + height: 150 }).withItems([ radioButton, groupModel1, radioButton2] , { flex: '1 1 50%' }).component(); @@ -200,6 +216,12 @@ export default class MainController implements vscode.Disposable { }, { component: flexRadioButtonsModel, title: 'Options' + }, { + component: table, + title: 'Table' + }, { + component: listBox, + title: 'List Box' }], { horizontal: false, componentWidth: 400 @@ -322,7 +344,7 @@ export default class MainController implements vscode.Disposable { height: '100%' }); - let templateValues = {url: 'http://whoisactive.com/docs/'}; + let templateValues = { url: 'http://whoisactive.com/docs/' }; Utils.renderTemplateHtml(path.join(__dirname, '..'), 'templateTab.html', templateValues) .then(html => { webview.html = html; diff --git a/src/sql/parts/modelComponents/components.contribution.ts b/src/sql/parts/modelComponents/components.contribution.ts index e694cdf54a..508567f6a2 100644 --- a/src/sql/parts/modelComponents/components.contribution.ts +++ b/src/sql/parts/modelComponents/components.contribution.ts @@ -10,6 +10,7 @@ import GroupContainer from './groupContainer.component'; import CardComponent from './card.component'; import InputBoxComponent from './inputbox.component'; import DropDownComponent from './dropdown.component'; +import ListBoxComponent from './listbox.component'; import ButtonComponent from './button.component'; import CheckBoxComponent from './checkbox.component'; import RadioButtonComponent from './radioButton.component'; @@ -41,6 +42,9 @@ registerComponentType(INPUTBOX_COMPONENT, ModelComponentTypes.InputBox, InputBox export const DROPDOWN_COMPONENT = 'dropdown-component'; registerComponentType(DROPDOWN_COMPONENT, ModelComponentTypes.DropDown, DropDownComponent); +export const LISTBOX_COMPONENT = 'lisbox-component'; +registerComponentType(LISTBOX_COMPONENT, ModelComponentTypes.ListBox, ListBoxComponent); + 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 index 28b38117c0..478b9f234c 100644 --- a/src/sql/parts/modelComponents/dropdown.component.ts +++ b/src/sql/parts/modelComponents/dropdown.component.ts @@ -15,7 +15,8 @@ import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } fro import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/dropdown'; import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service'; -import { attachEditableDropdownStyler , attachSelectBoxStyler} from 'sql/common/theme/styler'; +import { attachEditableDropdownStyler } from 'sql/common/theme/styler'; +import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; diff --git a/src/sql/parts/modelComponents/listbox.component.ts b/src/sql/parts/modelComponents/listbox.component.ts new file mode 100644 index 0000000000..bc2e6702a4 --- /dev/null +++ b/src/sql/parts/modelComponents/listbox.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 { + Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver, + ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit +} from '@angular/core'; + +import * as sqlops from 'sqlops'; + +import { ComponentBase } from 'sql/parts/modelComponents/componentBase'; +import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces'; + +import { ListBox } from 'sql/base/browser/ui/listBox/listBox'; +import { attachListBoxStyler } from 'sql/common/theme/styler'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import Event, { Emitter } from 'vs/base/common/event'; +import * as nls from 'vs/nls'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; + +@Component({ + selector: 'modelview-listBox', + template: ` +
+ ` +}) +export default class ListBoxComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit { + @Input() descriptor: IComponentDescriptor; + @Input() modelStore: IModelStore; + private _input: ListBox; + + @ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef; + constructor( + @Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef, + @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService, + @Inject(IContextViewService) private contextViewService: IContextViewService, + @Inject(IClipboardService) private clipboardService: IClipboardService + ) { + super(changeRef); + } + + ngOnInit(): void { + this.baseInit(); + + } + + ngAfterViewInit(): void { + if (this._inputContainer) { + this._input = new ListBox([], undefined, this.contextViewService, this.clipboardService); + this._input.render(this._inputContainer.nativeElement); + + this._register(this._input); + this._register(attachListBoxStyler(this._input, this.themeService)); + this._register(this._input.onDidSelect(e => { + this.selectedRow = e.index; + this._onEventEmitter.fire({ + eventType: ComponentEventType.onSelectedRowChanged, + args: e + }); + })); + } + } + + public validate(): Thenable { + return super.validate().then(valid => { + return valid; + }); + } + + 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._input.setOptions(this.values, this.selectedRow); + + this.validate(); + } + + // CSS-bound properties + + private get values(): string[] { + return this.getPropertyOrDefault((props) => props.values, undefined); + } + + private set values(newValue: string[]) { + this.setPropertyFromUI((props, value) => props.values = value, newValue); + } + + private get selectedRow(): number { + return this.getPropertyOrDefault((props) => props.selectedRow, undefined); + } + + private set selectedRow(newValue: number) { + this.setPropertyFromUI((props, value) => props.selectedRow = value, newValue); + } + + public get height(): number { + return this.getPropertyOrDefault((props) => props.height, undefined); + } + + public set height(newValue: number) { + this.setPropertyFromUI((props, value) => props.height = value, newValue); + } + + public get width(): number { + return this.getPropertyOrDefault((props) => props.width, undefined); + } + + public set width(newValue: number) { + this.setPropertyFromUI((props, value) => props.width = value, newValue); + } +} diff --git a/src/sql/sqlops.proposed.d.ts b/src/sql/sqlops.proposed.d.ts index bbd5b48965..7e5952c98c 100644 --- a/src/sql/sqlops.proposed.d.ts +++ b/src/sql/sqlops.proposed.d.ts @@ -26,6 +26,7 @@ declare module 'sqlops' { text(): ComponentBuilder; button(): ComponentBuilder; dropDown(): ComponentBuilder; + listBox(): ComponentBuilder; table(): ComponentBuilder; dashboardWidget(widgetId: string): ComponentBuilder; dashboardWebview(webviewId: string): ComponentBuilder; @@ -334,6 +335,11 @@ declare module 'sqlops' { editable?: boolean; } + export interface ListBoxProperties { + selectedRow?: number; + values?: string[]; + } + export interface WebViewProperties { message?: any; html?: string; @@ -379,6 +385,12 @@ declare module 'sqlops' { onValueChanged: vscode.Event; } + export interface ListBoxComponent extends Component, ListBoxProperties { + selectedRow?: number; + values: string[]; + onRowSelected: vscode.Event; + } + export interface TableComponent extends Component, TableComponentProperties { onRowSelected: vscode.Event; } diff --git a/src/sql/workbench/api/common/sqlExtHostTypes.ts b/src/sql/workbench/api/common/sqlExtHostTypes.ts index e6dfd9b746..c6cf6649c7 100644 --- a/src/sql/workbench/api/common/sqlExtHostTypes.ts +++ b/src/sql/workbench/api/common/sqlExtHostTypes.ts @@ -69,6 +69,7 @@ export enum ModelComponentTypes { Card, InputBox, DropDown, + ListBox, Button, CheckBox, RadioButton, diff --git a/src/sql/workbench/api/node/extHostModelView.ts b/src/sql/workbench/api/node/extHostModelView.ts index 621c45fdbf..6deb9e0005 100644 --- a/src/sql/workbench/api/node/extHostModelView.ts +++ b/src/sql/workbench/api/node/extHostModelView.ts @@ -116,6 +116,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder { return builder; } + listBox(): sqlops.ComponentBuilder { + let id = this.getNextComponentId(); + let builder: ComponentBuilderImpl = this.getComponentBuilder(new ListBoxWrapper(this._proxy, this._handle, id), id); + this._componentBuilders.set(id, builder); + return builder; + } + table(): sqlops.ComponentBuilder { let id = this.getNextComponentId(); let builder: ComponentBuilderImpl = this.getComponentBuilder(new TableComponentWrapper(this._proxy, this._handle, id), id); @@ -748,6 +755,34 @@ class DropDownWrapper extends ComponentWrapper implements sqlops.DropDownCompone } } +class ListBoxWrapper extends ComponentWrapper implements sqlops.ListBoxComponent { + + constructor(proxy: MainThreadModelViewShape, handle: number, id: string) { + super(proxy, handle, ModelComponentTypes.ListBox, id); + this.properties = {}; + this._emitterMap.set(ComponentEventType.onSelectedRowChanged, new Emitter()); + } + + public get selectedRow(): number { + return this.properties['selectedRow']; + } + public set selectedRow(v: number) { + this.setProperty('selectedRow', v); + } + + public get values(): string[] { + return this.properties['values']; + } + public set values(v: string[]) { + this.setProperty('values', v); + } + + public get onRowSelected(): vscode.Event { + let emitter = this._emitterMap.get(ComponentEventType.onSelectedRowChanged); + return emitter && emitter.event; + } +} + class ButtonWrapper extends ComponentWrapper implements sqlops.ButtonComponent { constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {