Feature/bdc create (#4012)

* initial checkin

* rename

* wizard pages

* target cluster radio button group

* resource strings

* existing cluster picker

* revert changes to unwanted file

* revert unwanted changes-2

* update cluster icon

* settings page

* fix group container

* hyperlink component

* address review comments

* comments part 2
This commit is contained in:
Alan Ren
2019-02-12 22:13:30 -08:00
committed by GitHub
parent dd6735ec04
commit b3a16fd0ce
36 changed files with 1061 additions and 63 deletions

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>

After

Width:  |  Height:  |  Size: 139 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>

After

Width:  |  Height:  |  Size: 139 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M11 10H5.344L11 4.414V10z"/></svg>

After

Width:  |  Height:  |  Size: 118 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M11 10H5.344L11 4.414V10z"/></svg>

After

Width:  |  Height:  |  Size: 118 B

View File

@@ -1,33 +1,49 @@
<div *ngIf="label" [class]="getClass()" (click)="onCardClick()" (mouseover)="onCardHoverChanged($event)" (mouseout)="onCardHoverChanged($event)">
<span *ngIf="hasStatus" class="card-status">
<div class="status-content" [style.backgroundColor]="statusColor"></div>
</span>
<span *ngIf="showRadioButton" class="selection-indicator-container">
<div *ngIf="showAsSelected" class="selection-indicator"></div>
</span>
<ng-container *ngIf="isVerticalButton">
<div class="card-vertical-button">
<div *ngIf="iconPath" class="iconContainer">
<div [class]="iconClass" [style.maxWidth]="iconWidth" [style.maxHeight]="iconHeight"></div>
<div *ngIf="label" [class]="getClass()" (click)="onCardClick()" (mouseover)="onCardHoverChanged($event)" (mouseout)="onCardHoverChanged($event)"
tabIndex="0">
<ng-container *ngIf="isVerticalButton || isDetailsCard">
<span *ngIf="hasStatus" class="card-status">
<div class="status-content" [style.backgroundColor]="statusColor"></div>
</span>
<span *ngIf="showRadioButton" class="selection-indicator-container">
<div *ngIf="showAsSelected" class="selection-indicator"></div>
</span>
<ng-container *ngIf="isVerticalButton">
<div class="card-vertical-button">
<div *ngIf="iconPath" class="iconContainer">
<div [class]="iconClass" [style.maxWidth]="iconWidth" [style.maxHeight]="iconHeight"></div>
</div>
<h4 class="card-label">{{label}}</h4>
</div>
<h4 class="card-label">{{label}}</h4>
</div>
</ng-container>
</ng-container>
<ng-container *ngIf="isDetailsCard">
<div class="card-content">
<h4 class="card-label">{{label}}</h4>
<p class="card-value">{{value}}</p>
<span *ngIf="actions">
<table class="model-table">
<tr *ngFor="let action of actions">
<td class="table-row">{{action.label}}</td>
<td *ngIf="action.actionTitle" class="table-row">
<a class="pointer prominent" (click)="onDidActionClick(action)">{{action.actionTitle}}</a>
</td>
</tr>
</table>
</span>
<ng-container *ngIf="isDetailsCard">
<div class="card-content">
<h4 class="card-label">{{label}}</h4>
<p class="card-value">{{value}}</p>
<span *ngIf="actions">
<table class="model-table">
<tr *ngFor="let action of actions">
<td class="table-row">{{action.label}}</td>
<td *ngIf="action.actionTitle" class="table-row">
<a class="pointer prominent" (click)="onDidActionClick(action)">{{action.actionTitle}}</a>
</td>
</tr>
</table>
</span>
</div>
</ng-container>
</ng-container>
<ng-container *ngIf="isListItemCard">
<div class="list-item-content">
<div>
<div [class]="iconClass">{{label}}</div>
<div *ngFor="let desc of descriptions">
<div class="list-item-description">{{desc}}</div>
</div>
<span *ngIf="showRadioButton" class="selection-indicator-container">
<div *ngIf="showAsSelected" class="selection-indicator"></div>
</span>
</div>
</div>
</ng-container>
</div>

View File

@@ -70,8 +70,9 @@ export default class CardComponent extends ComponentWithIconBase implements ICom
}
public getClass(): string {
return (this.selectable && this.selected || this._hasFocus) ? 'model-card selected' :
'model-card unselected';
let cardClass = this.isListItemCard ? 'model-card-list-item' : 'model-card';
return (this.selectable && this.selected || this._hasFocus) ? `${cardClass} selected` :
`${cardClass} unselected`;
}
public onCardHoverChanged(event: any) {
@@ -93,11 +94,16 @@ export default class CardComponent extends ComponentWithIconBase implements ICom
}
public get iconClass(): string {
return this._iconClass + ' icon' + ' cardIcon';
if (this.isListItemCard) {
return this._iconClass + ' icon' + ' list-item-icon';
}
else {
return this._iconClass + ' icon' + ' cardIcon';
}
}
private get selectable(): boolean {
return this.cardType === 'VerticalButton';
return this.cardType === 'VerticalButton' || this.cardType === 'ListItem';
}
// CSS-bound properties
@@ -126,11 +132,15 @@ export default class CardComponent extends ComponentWithIconBase implements ICom
return !this.cardType || this.cardType === 'Details';
}
public get isListItemCard(): boolean {
return !this.cardType || this.cardType === 'ListItem';
}
public get isVerticalButton(): boolean {
return this.cardType === 'VerticalButton';
}
public get showRadioButton():boolean{
public get showRadioButton(): boolean {
return this.selectable && (this.selected || this._hasFocus);
}
@@ -138,6 +148,9 @@ export default class CardComponent extends ComponentWithIconBase implements ICom
return this.selectable && this.selected;
}
public get descriptions(): string[] {
return this.getPropertyOrDefault<CardProperties, string[]>((props) => props.descriptions, []);
}
public get actions(): ActionDescriptor[] {
return this.getPropertyOrDefault<CardProperties, ActionDescriptor[]>((props) => props.actions, []);

View File

@@ -10,11 +10,13 @@
vertical-align: top;
}
.model-card-list-item.selected,
.model-card.selected {
border-color: rgb(0, 120, 215);
box-shadow: rgba(0, 120, 215, 0.75) 0px 0px 6px;
}
.model-card-list-item.unselected,
.model-card.unselected {
border-color: rgb(214, 214, 214);
box-shadow: none;
@@ -96,6 +98,7 @@
text-align: center;
}
.model-card-list-item .selection-indicator-container,
.model-card .selection-indicator-container {
position: absolute;
top: 5px;
@@ -110,6 +113,30 @@
border-style: solid;
}
.model-card-list-item .selection-indicator-container,
.model-card .selection-indicator-container {
position: absolute;
overflow: hidden;
width: 16px;
height: 16px;
border-radius: 50%;
background-color: white;
border-width: 1px;
border-color: rgb(0, 120, 215);
border-style: solid;
}
.model-card-list-item .selection-indicator-container {
top: 10px;
right: 10px;
}
.model-card .selection-indicator-container {
top: 5px;
right: 5px;
}
.model-card-list-item .selection-indicator,
.model-card .selection-indicator {
margin: 4px;
width: 8px;
@@ -135,4 +162,34 @@
.model-table a {
cursor: pointer;
text-decoration: underline
}
.model-card-list-item {
display: inline-block;
height: 100%;
width: 100%;
margin: 5px 0px 5px 0px;
border-width: 1px;
border-style: solid;
text-align: left;
vertical-align: top;
}
.model-card-list-item .list-item-content {
height: auto;
padding: 5px 26px 5px 5px;
min-height: 30px;
min-width: 300px;
}
.model-card-list-item .list-item-icon {
background-position: 2px 2px;
padding-left:22px;
font-size: 15px;
background-repeat: no-repeat;
background-size: 16px 16px;
}
.model-card-list-item .list-item-description {
padding-left:22px;
}

View File

@@ -25,6 +25,7 @@ import EditorComponent from './editor.component';
import DomComponent from './dom.component';
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
import HyperlinkComponent from 'sql/parts/modelComponents/hyperlink.component';
export const DIV_CONTAINER = 'div-container';
registerComponentType(DIV_CONTAINER, ModelComponentTypes.DivContainer, DivContainer);
@@ -89,3 +90,6 @@ registerComponentType(EDITOR_COMPONENT, ModelComponentTypes.Editor, EditorCompon
export const DOM_COMPONENT = 'dom-component';
registerComponentType(DOM_COMPONENT, ModelComponentTypes.Dom, DomComponent);
export const HYPERLINK_COMPONENT = 'hyperlink-component';
registerComponentType(HYPERLINK_COMPONENT, ModelComponentTypes.Hyperlink, HyperlinkComponent);

View File

@@ -20,10 +20,10 @@ import { CommonServiceInterface } from 'sql/services/common/commonServiceInterfa
@Component({
selector: 'modelview-groupContainer',
template: `
<div *ngIf="hasHeader()" class="modelview-group-header" >
<div *ngIf="hasHeader()" [class]="getHeaderClass()" (click)="changeState()">
{{_containerLayout.header}}
</div>
<div #container *ngIf="items" class="modelview-group-container" [style.width]="getContainerWidth()">
<div #container *ngIf="items" class="modelview-group-container" [style.width]="getContainerWidth()" [style.display]="getContainerDisplayStyle()">
<ng-container *ngFor="let item of items">
<div class="modelview-group-row" >
<div class="modelview-group-cell">
@@ -40,6 +40,7 @@ export default class GroupContainer extends ContainerBase<GroupLayout> implement
@Input() modelStore: IModelStore;
private _containerLayout: GroupLayout;
private _collapsed: boolean;
@ViewChild('container', { read: ElementRef }) private _container: ElementRef;
@@ -48,6 +49,7 @@ export default class GroupContainer extends ContainerBase<GroupLayout> implement
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) el: ElementRef) {
super(changeRef, el);
this._collapsed = false;
}
ngOnInit(): void {
@@ -65,6 +67,7 @@ export default class GroupContainer extends ContainerBase<GroupLayout> implement
public setLayout(layout: GroupLayout): void {
this._containerLayout = layout;
this._collapsed = !!layout.collapsed;
this.layout();
}
@@ -72,6 +75,10 @@ export default class GroupContainer extends ContainerBase<GroupLayout> implement
return this._containerLayout && this._containerLayout && this._containerLayout.header !== undefined;
}
private isCollapsible(): boolean {
return this.hasHeader() && this._containerLayout.collapsible === true;
}
private getContainerWidth(): string {
if (this._containerLayout && this._containerLayout.width) {
let width: string = this._containerLayout.width.toString();
@@ -83,4 +90,24 @@ export default class GroupContainer extends ContainerBase<GroupLayout> implement
return '100%';
}
}
private getContainerDisplayStyle(): string {
return !this.isCollapsible() || !this._collapsed ? 'block' : 'none';
}
private getHeaderClass(): string {
if (this.isCollapsible()) {
let modifier = this._collapsed ? 'collapsed' : 'expanded';
return `modelview-group-header-collapsible ${modifier}`;
} else {
return 'modelview-group-header';
}
}
private changeState(): void {
if (this.isCollapsible()) {
this._collapsed = !this._collapsed;
this._changeRef.detectChanges();
}
}
}

View File

@@ -7,14 +7,40 @@
.modelview-group-row {
display: table-row;
}
.modelview-group-header-collapsible,
.modelview-group-header {
padding-bottom: 5px;
font-size: 14px;
}
.modelview-group-header-collapsible {
padding-left: 20px;
background-position: 2px 2px;
background-size: 16px 16px;
background-repeat: no-repeat;
cursor: pointer;
}
.vs .modelview-group-header-collapsible.expanded {
background-image: url("../../media/icons/expanded.svg");
}
.vs-dark .modelview-group-header-collapsible.expanded,
.hc-black .modelview-group-header-collapsible.expanded {
background-image: url("../../media/icons/expanded_inverse.svg");
}
.vs .modelview-group-header-collapsible.collapsed {
background-image: url("../../media/icons/collapsed.svg");
}
.vs-dark .modelview-group-header-collapsible.collapsed,
.hc-black .modelview-group-header-collapsible.collapsed {
background-image: url("../../media/icons/collapsed_inverse.svg");
}
.modelview-group-cell {
padding-bottom: 5px;
display: table-cell;

View File

@@ -0,0 +1,70 @@
/*---------------------------------------------------------------------------------------------
* 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,
OnDestroy, AfterViewInit, ElementRef
} from '@angular/core';
import * as sqlops from 'sqlops';
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
@Component({
selector: 'modelview-hyperlink',
template: `<a [href]="getUrl()" target="blank">{{getLabel()}}</a>`
})
export default class HyperlinkComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
constructor(
@Inject(forwardRef(() => CommonServiceInterface)) private _commonService: CommonServiceInterface,
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) el: ElementRef) {
super(changeRef, el);
}
ngOnInit(): void {
this.baseInit();
}
ngAfterViewInit(): void {
}
ngOnDestroy(): void {
this.baseDestroy();
}
public setLayout(layout: any): void {
this.layout();
}
public set label(newValue: string) {
this.setPropertyFromUI<sqlops.HyperlinkComponentProperties, string>((properties, value) => { properties.label = value; }, newValue);
}
public get label(): string {
return this.getPropertyOrDefault<sqlops.HyperlinkComponentProperties, string>((props) => props.label, '');
}
public getLabel(): string {
return this.label;
}
public set url(newValue: string) {
this.setPropertyFromUI<sqlops.HyperlinkComponentProperties, string>((properties, value) => { properties.url = value; }, newValue);
}
public get url(): string {
return this.getPropertyOrDefault<sqlops.HyperlinkComponentProperties, string>((props) => props.url, '');
}
public getUrl(): string {
return this.url;
}
}

View File

@@ -18,7 +18,7 @@ import { CommonServiceInterface } from 'sql/services/common/commonServiceInterfa
@Component({
selector: 'modelview-text',
template: `
<p>{{getValue()}}</p>`
<p [style.width]="getWidth()">{{getValue()}}</p>`
})
export default class TextComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@Input() descriptor: IComponentDescriptor;

View File

@@ -40,6 +40,7 @@ declare module 'sqlops' {
toolbarContainer(): ToolbarBuilder;
loadingComponent(): LoadingComponentBuilder;
fileBrowserTree(): ComponentBuilder<FileBrowserTreeComponent>;
hyperlink(): ComponentBuilder<HyperlinkComponent>;
}
export interface TreeComponentDataProvider<T> extends vscode.TreeDataProvider<T> {
@@ -348,6 +349,8 @@ declare module 'sqlops' {
export interface GroupLayout {
width?: number | string;
header?: string;
collapsible?: boolean;
collapsed?: boolean;
}
export interface GroupItemLayout {
@@ -433,7 +436,8 @@ declare module 'sqlops' {
export enum CardType {
VerticalButton = 'VerticalButton',
Details = 'Details'
Details = 'Details',
ListItem = 'ListItem'
}
/**
@@ -444,6 +448,7 @@ declare module 'sqlops' {
label: string;
value?: string;
actions?: ActionDescriptor[];
descriptions?: string[];
status?: StatusIndicator;
/**
@@ -540,6 +545,11 @@ declare module 'sqlops' {
value?: string;
}
export interface HyperlinkComponentProperties extends ComponentProperties {
label: string;
url: string;
}
export interface DropDownProperties extends ComponentProperties {
value?: string | CategoryValue;
values?: string[] | CategoryValue[];
@@ -638,10 +648,15 @@ declare module 'sqlops' {
}
export interface TextComponent extends Component {
export interface TextComponent extends Component, ComponentProperties {
value: string;
}
export interface HyperlinkComponent extends Component, HyperlinkComponentProperties {
label: string;
url: string;
}
export interface InputBoxComponent extends Component, InputBoxProperties {
onTextChanged: vscode.Event<any>;
}

View File

@@ -164,7 +164,8 @@ export enum ModelComponentTypes {
TreeComponent,
FileBrowserTree,
Editor,
Dom
Dom,
Hyperlink
}
export interface IComponentShape {
@@ -262,6 +263,7 @@ export interface CardProperties {
label: string;
value?: string;
actions?: ActionDescriptor[];
descriptions?: string[];
status?: StatusIndicator;
selected?: boolean;
cardType: CardType;
@@ -300,7 +302,8 @@ export enum DeclarativeDataType {
export enum CardType {
VerticalButton = 'VerticalButton',
Details = 'Details'
Details = 'Details',
ListItem = 'ListItem'
}
export enum Orientation {
@@ -492,7 +495,7 @@ export class CellRange {
}
constructor(start: number, end: number) {
if (typeof(start) !== 'number' || typeof(start) !== 'number' || start < 0 || end < 0) {
if (typeof (start) !== 'number' || typeof (start) !== 'number' || start < 0 || end < 0) {
throw new Error('Invalid arguments');
}

View File

@@ -198,6 +198,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
return builder;
}
hyperlink(): sqlops.ComponentBuilder<sqlops.HyperlinkComponent> {
let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.HyperlinkComponent> = this.getComponentBuilder(new HyperlinkComponentWrapper(this._proxy, this._handle, id), id);
this._componentBuilders.set(id, builder);
return builder;
}
getComponentBuilder<T extends sqlops.Component>(component: ComponentWrapper, id: string): ComponentBuilderImpl<T> {
let componentBuilder: ComponentBuilderImpl<T> = new ComponentBuilderImpl<T>(component);
this._componentBuilders.set(id, componentBuilder);
@@ -1248,6 +1255,28 @@ class TreeComponentWrapper<T> extends ComponentWrapper implements sqlops.TreeCom
}
}
class HyperlinkComponentWrapper extends ComponentWrapper implements sqlops.HyperlinkComponentProperties {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
super(proxy, handle, ModelComponentTypes.Hyperlink, id);
this.properties = {};
}
public get label(): string {
return this.properties['label'];
}
public set label(v: string) {
this.setProperty('label', v);
}
public get url(): string {
return this.properties['url'];
}
public set url(v: string) {
this.setProperty('url', v);
}
}
class ModelViewImpl implements sqlops.ModelView {
public onClosedEmitter = new Emitter<any>();