added radio button model view component (#1439)

* added radio button model view component
This commit is contained in:
Leila Lali
2018-05-21 11:45:27 -07:00
committed by GitHub
parent 5de002e5c1
commit ba264d8311
12 changed files with 389 additions and 36 deletions

View File

@@ -0,0 +1,83 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as DOM from 'vs/base/browser/dom';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { Color } from 'vs/base/common/color';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import Event, { Emitter } from 'vs/base/common/event';
import { Widget } from 'vs/base/browser/ui/widget';
export interface IRadioButtonOptions {
label: string;
enabled?: boolean;
checked?: boolean;
}
export class RadioButton extends Widget {
private inputElement: HTMLInputElement;
private _onClicked = new Emitter<void>();
public readonly onClicked: Event<void> = this._onClicked.event;
private _label: HTMLSpanElement;
constructor(container: HTMLElement, opts: IRadioButtonOptions) {
super();
this.inputElement = document.createElement('input');
this.inputElement.type = 'radio';
this._label = document.createElement('span');
this.label = opts.label;
this.enabled = opts.enabled || true;
this.checked = opts.checked || false;
this.onclick(this.inputElement, () => this._onClicked.fire());
container.appendChild(this.inputElement);
container.appendChild(this._label);
}
public set name(value: string) {
this.inputElement.setAttribute('name', value);
}
public get name(): string {
return this.inputElement.getAttribute('name');
}
public set value(value: string) {
this.inputElement.setAttribute('value', value);
}
public get value(): string {
return this.inputElement.getAttribute('value');
}
public set checked(val: boolean) {
this.inputElement.checked = val;
}
public get checked(): boolean {
return this.inputElement.checked;
}
public set enabled(val: boolean) {
this.inputElement.disabled = !val;
}
public get enabled(): boolean {
return !this.inputElement.disabled;
}
public isEnabled(): boolean {
return !this.inputElement.hasAttribute('disabled');
}
public set label(val: string) {
this._label.innerText = val;
}
}

View File

@@ -78,6 +78,11 @@ export default class CheckBoxComponent extends ComponentBase implements ICompone
super.setProperties(properties);
this._input.checked = this.checked;
this._input.label = this.label;
if (this.enabled) {
this._input.enable();
} else {
this._input.disable();
}
}
// CSS-bound properties
@@ -87,11 +92,7 @@ export default class CheckBoxComponent extends ComponentBase implements ICompone
}
public set value(newValue: boolean) {
this.setPropertyFromUI<sqlops.CheckBoxProperties, boolean>(this.setInputBoxProperties, newValue);
}
private setInputBoxProperties(properties: sqlops.CheckBoxProperties, value: boolean): void {
properties.checked = value;
this.setPropertyFromUI<sqlops.CheckBoxProperties, boolean>((properties, value) => { properties.checked = value; }, newValue);
}
private get label(): string {
@@ -99,10 +100,6 @@ export default class CheckBoxComponent extends ComponentBase implements ICompone
}
private set label(newValue: string) {
this.setPropertyFromUI<sqlops.CheckBoxProperties, string>(this.setValueProperties, newValue);
}
private setValueProperties(properties: sqlops.CheckBoxProperties, label: string): void {
properties.label = label;
this.setPropertyFromUI<sqlops.CheckBoxProperties, string>((properties, label) => { properties.label = label; }, newValue);
}
}

View File

@@ -94,7 +94,15 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
public get enabled(): boolean {
let properties = this.getProperties();
let enabled = properties['enabled'];
return enabled !== undefined ? <boolean>enabled : true;
if (enabled === undefined) {
enabled = true;
properties['enabled'] = enabled;
this.fireEvent({
eventType: ComponentEventType.PropertiesChanged,
args: this.getProperties()
});
}
return <boolean>enabled;
}
public get valid(): boolean {

View File

@@ -10,6 +10,7 @@ import InputBoxComponent from './inputbox.component';
import DropDownComponent from './dropdown.component';
import ButtonComponent from './button.component';
import CheckBoxComponent from './checkbox.component';
import RadioButtonComponent from './radioButton.component';
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
@@ -34,3 +35,6 @@ registerComponentType(BUTTON_COMPONENT, ModelComponentTypes.Button, ButtonCompon
export const CHECKBOX_COMPONENT = 'checkbox-component';
registerComponentType(CHECKBOX_COMPONENT, ModelComponentTypes.CheckBox, CheckBoxComponent);
export const RADIOBUTTON_COMPONENT = 'radiobutton-component';
registerComponentType(RADIOBUTTON_COMPONENT, ModelComponentTypes.RadioButton, RadioButtonComponent);

View File

@@ -87,6 +87,7 @@ export default class DropDownComponent extends ComponentBase implements ICompone
if (this.value) {
this._dropdown.value = this.value;
}
this._dropdown.enabled = this.enabled;
}
// CSS-bound properties

View File

@@ -22,7 +22,7 @@ class FlexItem {
@Component({
template: `
<div *ngIf="items" class="flexContainer" [style.flexFlow]="flexFlow" [style.justifyContent]="justifyContent"
[style.alignItems]="alignItems" [style.alignContent]="alignContent">
[style.alignItems]="alignItems" [style.alignContent]="alignContent" [style.height]="height">
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.order]="getItemOrder(item)" >
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
</model-component-wrapper>
@@ -37,6 +37,7 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
private _justifyContent: string;
private _alignItems: string;
private _alignContent: string;
private _height: string;
@ViewChildren(ModelComponentWrapper) private _componentWrappers: QueryList<ModelComponentWrapper>;
@@ -70,6 +71,7 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
this._justifyContent= layout.justifyContent ? layout.justifyContent : '';
this._alignItems= layout.alignItems ? layout.alignItems : '';
this._alignContent= layout.alignContent ? layout.alignContent : '';
this._height= layout.height ? layout.height + 'px' : '';
this.layout();
}
@@ -86,6 +88,10 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
return this._alignItems;
}
public get height(): string {
return this._height;
}
public get alignContent(): string {
return this._alignContent;
}

View File

@@ -0,0 +1,119 @@
/*---------------------------------------------------------------------------------------------
* 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!./radioButton';
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 { RadioButton } from 'sql/base/browser/ui/radioButton/radioButton';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
@Component({
selector: 'radioButton',
template: `
<div #input class="modelview-radiobutton-container">
</div>
`
})
export default class RadioButtonComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
private _input: RadioButton;
@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._input = new RadioButton(this._inputContainer.nativeElement, {
label: this.label
});
this._register(this._input);
this._register(this._input.onClicked(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._input.name = this.name;
this._input.value = this.value;
this._input.label = this.label;
this._input.enabled = this.enabled;
this._input.checked = this.checked;
}
// CSS-bound properties
public get checked(): boolean {
return this.getPropertyOrDefault<sqlops.RadioButtonProperties, boolean>((props) => props.checked, false);
}
public set value(newValue: string) {
this.setPropertyFromUI<sqlops.RadioButtonProperties, string>((properties, value) => { properties.checked = value; }, newValue);
}
public get value(): string {
return this.getPropertyOrDefault<sqlops.RadioButtonProperties, string>((props) => props.value, '');
}
public getLabel(): string {
return this.label;
}
public get label(): string {
return this.getPropertyOrDefault<sqlops.RadioButtonProperties, string>((props) => props.label, '');
}
public set label(newValue: string) {
this.setPropertyFromUI<sqlops.RadioButtonProperties, string>((properties, label) => { properties.label = label; }, newValue);
}
public get name(): string {
return this.getPropertyOrDefault<sqlops.RadioButtonProperties, string>((props) => props.name, '');
}
public set name(newValue: string) {
this.setPropertyFromUI<sqlops.RadioButtonProperties, string>((properties, label) => { properties.name = label; }, newValue);
}
}

View File

@@ -0,0 +1,11 @@
.modelview-radiobutton-container {
align-items: flex-start;
}
.modelview-radiobutton-item {
align-self: flex-start ;
}
.modelview-radiobutton-title {
}

View File

@@ -21,6 +21,7 @@ declare module 'sqlops' {
card(): ComponentBuilder<CardComponent>;
inputBox(): ComponentBuilder<InputBoxComponent>;
checkBox(): ComponentBuilder<CheckBoxComponent>;
radioButton(): ComponentBuilder<RadioButtonComponent>;
button(): ComponentBuilder<ButtonComponent>;
dropDown(): ComponentBuilder<DropDownComponent>;
dashboardWidget(widgetId: string): ComponentBuilder<WidgetComponent>;
@@ -44,6 +45,22 @@ declare module 'sqlops' {
export interface FormBuilder extends ContainerBuilder<FormContainer, FormLayout, FormItemLayout> {
withFormItems(components: FormComponent[], itemLayout?: FormItemLayout): ContainerBuilder<FormContainer, FormLayout, FormItemLayout>;
/**
* Creates a collection of child components and adds them all to this container
*
* @param formComponents the definitions
* @param {*} [itemLayout] Optional layout for the child items
*/
addFormItems(formComponents: Array<FormComponent>, itemLayout?: FormItemLayout): void;
/**
* Creates a child component and adds it to this container.
*
* @param formComponent the component to be added
* @param {*} [itemLayout] Optional layout for this child item
*/
addFormItem(formComponent: FormComponent, itemLayout?: FormItemLayout): void;
}
export interface Component {
@@ -147,6 +164,8 @@ declare module 'sqlops' {
* Matches the align-content CSS property.
*/
alignContent?: string;
height? : number;
}
export interface FlexItemLayout {
@@ -236,6 +255,13 @@ declare module 'sqlops' {
label?: string;
}
export interface RadioButtonProperties {
name?: string;
label?: string;
value?: string;
checked?: boolean;
}
export interface DropDownProperties {
value?: string;
values?: string[];
@@ -256,6 +282,10 @@ declare module 'sqlops' {
onTextChanged: vscode.Event<any>;
}
export interface RadioButtonComponent extends Component, RadioButtonProperties {
onDidClick: vscode.Event<any>;
}
export interface CheckBoxComponent extends Component {
checked: boolean;
label: string;

View File

@@ -71,6 +71,7 @@ export enum ModelComponentTypes {
DropDown,
Button,
CheckBox,
RadioButton,
DashboardWidget,
DashboardWebview,
Form

View File

@@ -59,6 +59,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
return builder;
}
radioButton(): sqlops.ComponentBuilder<sqlops.RadioButtonComponent> {
let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.RadioButtonComponent> = this.getComponentBuilder(new RadioButtonWrapper(this._proxy, this._handle, id), id);
this._componentBuilders.set(id, builder);
return builder;
}
checkBox(): sqlops.ComponentBuilder<sqlops.CheckBoxComponent> {
let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.CheckBoxComponent> = this.getComponentBuilder(new CheckBoxWrapper(this._proxy, this._handle, id), id);
@@ -177,36 +184,56 @@ class ContainerBuilderImpl<T extends sqlops.Component, TLayout, TItemLayout> ext
}
}
class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sqlops.FormLayout, sqlops.FormItemLayout> {
class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sqlops.FormLayout, sqlops.FormItemLayout> implements sqlops.FormBuilder {
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
}));
return this.convertToItemConfig(item, itemLayout);
});
components.forEach(formItem => {
if (formItem.actions) {
formItem.actions.forEach(component => {
let componentWrapper = component as ComponentWrapper;
this._component.itemConfigs.push(new InternalItemConfig(componentWrapper, itemLayout));
});
}
this.addComponentActions(formItem, itemLayout);
});
return this;
}
private convertToItemConfig(formComponent: sqlops.FormComponent, itemLayout?: sqlops.FormItemLayout): InternalItemConfig {
let componentWrapper = formComponent.component as ComponentWrapper;
let actions: string[] = undefined;
if (formComponent.actions) {
actions = formComponent.actions.map(action => {
let actionComponentWrapper = action as ComponentWrapper;
return actionComponentWrapper.id;
});
}
return new InternalItemConfig(componentWrapper, Object.assign({}, itemLayout, {
title: formComponent.title,
actions: actions,
isFormComponent: true
}));
}
private addComponentActions(formComponent: sqlops.FormComponent, itemLayout?: sqlops.FormItemLayout): void {
if (formComponent.actions) {
formComponent.actions.forEach(component => {
let componentWrapper = component as ComponentWrapper;
this._component.addItem(componentWrapper, itemLayout);
});
}
}
addFormItems(formComponents: Array<sqlops.FormComponent>, itemLayout?: sqlops.FormItemLayout): void {
formComponents.forEach(formComponent => {
this.addFormItem(formComponent, itemLayout);
});
}
addFormItem(formComponent: sqlops.FormComponent, itemLayout?: sqlops.FormItemLayout): void {
let itemImpl = this.convertToItemConfig(formComponent, itemLayout);
this._component.addItem(formComponent.component as ComponentWrapper, itemImpl.config);
this.addComponentActions(formComponent, itemLayout);
}
}
class InternalItemConfig {
@@ -489,6 +516,47 @@ class CheckBoxWrapper extends ComponentWrapper implements sqlops.CheckBoxCompone
}
}
class RadioButtonWrapper extends ComponentWrapper implements sqlops.RadioButtonComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
super(proxy, handle, ModelComponentTypes.RadioButton, id);
this.properties = {};
this._emitterMap.set(ComponentEventType.onDidClick, new Emitter<any>());
}
public get name(): string {
return this.properties['name'];
}
public set name(v: string) {
this.setProperty('name', v);
}
public get label(): string {
return this.properties['label'];
}
public set label(v: string) {
this.setProperty('label', v);
}
public get value(): string {
return this.properties['value'];
}
public set value(v: string) {
this.setProperty('value', v);
}
public get checked(): boolean {
return this.properties['checked'];
}
public set checked(v: boolean) {
this.setProperty('checked', v);
}
public get onDidClick(): vscode.Event<any> {
let emitter = this._emitterMap.get(ComponentEventType.onDidClick);
return emitter && emitter.event;
}
}
class DropDownWrapper extends ComponentWrapper implements sqlops.DropDownComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {