added Declarative table to model view controls (#1593)

* added Declarative table to model view controls
This commit is contained in:
Leila Lali
2018-06-11 12:40:17 -07:00
committed by GitHub
parent 3be0c5130a
commit 4609694141
12 changed files with 380 additions and 1 deletions

View File

@@ -30,6 +30,7 @@ export class InputBox extends AngularDisposable implements OnInit, OnChanges {
@Input() type: string;
@Input() placeholder: string;
@Input() ariaLabel: string;
@Input() value: string;
@Output() onDidChange = new EventEmitter<string | number>();
@@ -49,6 +50,9 @@ export class InputBox extends AngularDisposable implements OnInit, OnChanges {
placeholder: this.placeholder,
ariaLabel: this.ariaLabel
});
if (this.value) {
this._inputbox.value = this.value;
}
this._inputbox.onDidChange(e => {
switch (this.type) {
case 'number':

View File

@@ -52,11 +52,16 @@ import { DashboardControlHostContainer } from 'sql/parts/dashboard/containers/da
import { JobsViewComponent } from 'sql/parts/jobManagement/views/jobsView.component';
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, DashboardModelViewContainer, ModelComponentWrapper];
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, DashboardModelViewContainer, ModelComponentWrapper, Checkbox,
SelectBox,
InputBox,];
/* Panel */
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';

View File

@@ -112,6 +112,14 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
this.setProperties(properties);
}
protected convertSize(size: number | string): string {
let convertedSize: string = size ? size.toString() : '100%';
if (!convertedSize.toLowerCase().endsWith('px') && !convertedSize.toLowerCase().endsWith('%')) {
convertedSize = convertedSize + 'px';
}
return convertedSize;
}
public get valid(): boolean {
return this._valid;
}

View File

@@ -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 DeclarativeTableComponent from './declarativeTable.component';
import ListBoxComponent from './listbox.component';
import ButtonComponent from './button.component';
import CheckBoxComponent from './checkbox.component';
@@ -42,6 +43,9 @@ registerComponentType(INPUTBOX_COMPONENT, ModelComponentTypes.InputBox, InputBox
export const DROPDOWN_COMPONENT = 'dropdown-component';
registerComponentType(DROPDOWN_COMPONENT, ModelComponentTypes.DropDown, DropDownComponent);
export const DECLARATIVETABLE_COMPONENT = 'declarativeTable-component';
registerComponentType(DECLARATIVETABLE_COMPONENT, ModelComponentTypes.DeclarativeTable, DeclarativeTableComponent);
export const LISTBOX_COMPONENT = 'lisbox-component';
registerComponentType(LISTBOX_COMPONENT, ModelComponentTypes.ListBox, ListBoxComponent);

View File

@@ -0,0 +1,194 @@
/*---------------------------------------------------------------------------------------------
* 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!./declarativeTable';
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 { 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 { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
import * as nls from 'vs/nls';
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
}
@Component({
selector: 'modelview-declarativeTable',
template: `
<table role=grid aria-labelledby="ID_REF" #container *ngIf="columns" class="declarative-table">
<ng-container *ngFor="let column of columns;let h = index">
<th class="declarative-table-header" tabindex="-1" role="button" aria-sort="none">{{column.displayName}}</th>
</ng-container>
<ng-container *ngIf="data">
<ng-container *ngFor="let row of data;let r = index">
<tr class="declarative-table-row">
<ng-container *ngFor="let cellData of row;let c = index">
<td class="declarative-table-cell" tabindex="-1" role="button" [style.width]="getColumnWidth(c)">
<checkbox *ngIf="isCheckBox(c)" label="" (onChange)="onCheckBoxChanged($event,r,c)" [enabled]="isControlEnabled(c)" [checked]="isChecked(r,c)"></checkbox>
<select-box *ngIf="isSelectBox(c)" [options]="GetOptions(c)" (onDidSelect)="onSelectBoxChanged($event,r,c)" [selectedOption]="GetSelectedOptionDisplayName(r,c)"></select-box>
<input-box *ngIf="isInputBox(c)" [value]="cellData" (onDidChange)="onInputBoxChanged($event,r,c)"></input-box>
<ng-container *ngIf="isLabel(c)" >{{cellData}}</ng-container>
</td>
</ng-container>
</tr>
</ng-container>
</ng-container>
</table>
`
})
export default class DeclarativeTableComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
@ViewChild('container', { read: ElementRef }) private _tableContainer: ElementRef;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
@Inject(IContextViewService) private contextViewService: IContextViewService
) {
super(changeRef);
}
ngOnInit(): void {
this.baseInit();
}
ngAfterViewInit(): void {
}
public validate(): Thenable<boolean> {
return super.validate().then(valid => {
return valid;
});
}
ngOnDestroy(): void {
this.baseDestroy();
}
private isCheckBox(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.valueType === DeclarativeDataType.boolean;
}
private isControlEnabled(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return !column.isReadOnly;
}
private isLabel(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.isReadOnly && column.valueType === DeclarativeDataType.string;
}
private isChecked(row: number, cell: number): boolean {
let cellData = this.data[row][cell];
return cellData;
}
private onInputBoxChanged(e: string, row: number, cell: number): void {
this.onCellDataChanged(e, row, cell);
}
private onCheckBoxChanged(e: boolean, row: number, cell: number): void {
this.onCellDataChanged(e, row, cell);
}
private onSelectBoxChanged(e: ISelectData, row: number, cell: number): void {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
if (column.categoryValues) {
this.onCellDataChanged(column.categoryValues[e.index].name, row, cell);
}
}
private onCellDataChanged(newValue: any, row: number, cell: number): void {
this.data[row][cell] = newValue;
this.data = this.data;
let newCellData : sqlops.TableCell = {
row: row,
column: cell,
value: newValue
};
this._onEventEmitter.fire({
eventType: ComponentEventType.onDidChange,
args: newCellData
});
}
private isSelectBox(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.valueType === DeclarativeDataType.category;
}
private isInputBox(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.valueType === DeclarativeDataType.string && !column.isReadOnly;
}
private getColumnWidth(cell: number): string {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return this.convertSize(column.width);
}
private GetOptions(cell: number): string[] {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.categoryValues ? column.categoryValues.map(x => x.displayName) : [];
}
private GetSelectedOptionDisplayName(row: number, cell: number): string {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
let cellData = this.data[row][cell];
if (cellData && column.categoryValues) {
let category = column.categoryValues.find(v => v.name === cellData);
return category.displayName;
} else {
return '';
}
}
/// 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);
}
public get data(): any[][] {
return this.getPropertyOrDefault<sqlops.DeclarativeTableProperties, any[]>((props) => props.data, []);
}
public set data(newValue: any[][]) {
this.setPropertyFromUI<sqlops.DeclarativeTableProperties, any[][]>((props, value) => props.data = value, newValue);
}
public get columns(): sqlops.DeclarativeTableColumn[] {
return this.getPropertyOrDefault<sqlops.DeclarativeTableProperties, sqlops.DeclarativeTableColumn[]>((props) => props.columns, []);
}
public set columns(newValue: sqlops.DeclarativeTableColumn[]) {
this.setPropertyFromUI<sqlops.DeclarativeTableProperties, sqlops.DeclarativeTableColumn[]>((props, value) => props.columns = value, newValue);
}
}

View File

@@ -0,0 +1,41 @@
.declarative-table {
padding: 5px 30px 0px 30px;
box-sizing: border-box;
width:100%;
border-collapse: collapse;
}
.declarative-table-row {
}
.declarative-table-header {
padding: 5px;
border: 1px solid gray;
background-color: #F5F5F5;
}
.vs-dark .declarative-table-header {
padding: 5px;
border: 1px solid gray;
background-color: #333334;
}
.hc-black .declarative-table-header {
padding: 5px;
border: 1px solid gray;
background-color: #333334;
}
.declarative-table-cell {
padding: 5px;
border: 1px solid gray;
}
[role="gridcell"]:focus,
[role="gridcell"] *:focus,
[role="grid"] [tabindex="0"]:focus {
outline: #005a9c;
outline-style: dotted;
outline-width: 3px;
}

View File

@@ -19,6 +19,9 @@ import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { Registry } from 'vs/platform/registry/common/platform';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
/* Model-backed components */
let extensionComponents = Registry.as<IComponentRegistry>(Extensions.ComponentContribution).getAllCtors();
@@ -26,6 +29,9 @@ let extensionComponents = Registry.as<IComponentRegistry>(Extensions.ComponentCo
export const DialogModule = (params, selector: string): any => {
@NgModule({
declarations: [
Checkbox,
SelectBox,
InputBox,
DialogContainer,
ModelViewContent,
ModelComponentWrapper,

View File

@@ -28,6 +28,7 @@ declare module 'sqlops' {
dropDown(): ComponentBuilder<DropDownComponent>;
listBox(): ComponentBuilder<ListBoxComponent>;
table(): ComponentBuilder<TableComponent>;
declarativeTable(): ComponentBuilder<DeclarativeTableComponent>;
dashboardWidget(widgetId: string): ComponentBuilder<DashboardWidgetComponent>;
dashboardWebview(webviewId: string): ComponentBuilder<DashboardWebviewComponent>;
formContainer(): FormBuilder;
@@ -318,6 +319,12 @@ declare module 'sqlops' {
label?: string;
}
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
}
export interface RadioButtonProperties {
name?: string;
label?: string;
@@ -335,9 +342,23 @@ declare module 'sqlops' {
editable?: boolean;
}
export interface DeclarativeTableColumn {
displayName: string;
categoryValues: CategoryValue[];
valueType: DeclarativeDataType;
isReadOnly: boolean;
width: number|string;
}
export interface DeclarativeTableProperties {
data: any[][];
columns: DeclarativeTableColumn[];
}
export interface ListBoxProperties {
selectedRow?: number;
values?: string[];
}
export interface WebViewProperties {
@@ -385,6 +406,16 @@ declare module 'sqlops' {
onValueChanged: vscode.Event<any>;
}
export interface TableCell {
row: number;
column: number;
value: any;
}
export interface DeclarativeTableComponent extends Component, DeclarativeTableProperties {
onDataChanged: vscode.Event<any>;
}
export interface ListBoxComponent extends Component, ListBoxProperties {
selectedRow?: number;
values: string[];

View File

@@ -69,6 +69,7 @@ export enum ModelComponentTypes {
Card,
InputBox,
DropDown,
DeclarativeTable,
ListBox,
Button,
CheckBox,
@@ -186,3 +187,9 @@ export enum DataProviderType {
AgentServicesProvider = 'AgentServicesProvider',
CapabilitiesProvider = 'CapabilitiesProvider'
}
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
}

View File

@@ -130,6 +130,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
return builder;
}
declarativeTable(): sqlops.ComponentBuilder<sqlops.DeclarativeTableComponent> {
let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.DeclarativeTableComponent> = this.getComponentBuilder(new DeclarativeTableWrapper(this._proxy, this._handle, id), id);
this._componentBuilders.set(id, builder);
return builder;
}
dashboardWidget(widgetId: string): sqlops.ComponentBuilder<sqlops.DashboardWidgetComponent> {
let id = this.getNextComponentId();
let builder = this.getComponentBuilder<sqlops.DashboardWidgetComponent>(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWidget, id), id);
@@ -755,6 +762,35 @@ class DropDownWrapper extends ComponentWrapper implements sqlops.DropDownCompone
}
}
class DeclarativeTableWrapper extends ComponentWrapper implements sqlops.DeclarativeTableComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
super(proxy, handle, ModelComponentTypes.DeclarativeTable, id);
this.properties = {};
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<any>());
}
public get data(): any[][] {
return this.properties['data'];
}
public set data(v: any[][]) {
this.setProperty('data', v);
}
public get columns(): sqlops.DeclarativeTableColumn[] {
return this.properties['columns'];
}
public set columns(v: sqlops.DeclarativeTableColumn[]) {
this.setProperty('columns', v);
}
public get onDataChanged(): vscode.Event<any> {
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
return emitter && emitter.event;
}
}
class ListBoxWrapper extends ComponentWrapper implements sqlops.ListBoxComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {

View File

@@ -369,6 +369,7 @@ export function createApiFactory(
serialization,
dataprotocol,
DataProviderType: sqlExtHostTypes.DataProviderType,
DeclarativeDataType: sqlExtHostTypes.DeclarativeDataType,
ServiceOptionType: sqlExtHostTypes.ServiceOptionType,
ConnectionOptionSpecialType: sqlExtHostTypes.ConnectionOptionSpecialType,
EditRowState: sqlExtHostTypes.EditRowState,