From ed64b5edff15e702493df21aa3230b81593e6133 Mon Sep 17 00:00:00 2001 From: Michael Lando Date: Fri, 9 Jun 2017 03:19:04 +0300 Subject: [SDC-29] catalog 1707 rebase commit. Change-Id: I43c3dc5cf44abf5da817649bc738938a3e8388c1 Signed-off-by: Michael Lando --- .../dynamic-element/dynamic-element.component.less | 3 + .../dynamic-element/dynamic-element.component.ts | 117 ++++++++++ .../dynamic-element/dynamic-element.module.ts | 34 +++ .../checkbox/ui-element-checkbox.component.html | 1 + .../checkbox/ui-element-checkbox.component.less | 2 + .../checkbox/ui-element-checkbox.component.ts | 27 +++ .../dropdown/ui-element-dropdown.component.html | 3 + .../dropdown/ui-element-dropdown.component.less | 11 + .../dropdown/ui-element-dropdown.component.ts | 33 +++ .../input/ui-element-input.component.html | 13 ++ .../input/ui-element-input.component.less | 17 ++ .../input/ui-element-input.component.ts | 22 ++ .../ui-element-popover-input.component.html | 26 +++ .../ui-element-popover-input.component.less | 36 +++ .../ui-element-popover-input.component.ts | 39 ++++ .../elements-ui/ui-element-base.component.ts | 34 +++ .../filter-properties-assignment.component.html | 26 +++ .../filter-properties-assignment.component.less | 38 +++ .../filter-properties-assignment.component.ts | 77 ++++++ .../hierarchy-display-options.ts | 12 + .../hierarchy-navigation.component.html | 13 ++ .../hierarchy-navigation.component.less | 51 ++++ .../hierarchy-navigation.component.ts | 28 +++ .../inputs-table/inputs-table.component.html | 37 +++ .../inputs-table/inputs-table.component.ts | 33 +++ .../popover/popover-content.component.html | 24 ++ .../popover/popover-content.component.less | 73 ++++++ .../popover/popover-content.component.ts | 258 +++++++++++++++++++++ .../ng2/components/popover/popover.component.ts | 159 +++++++++++++ .../app/ng2/components/popover/popover.module.ts | 27 +++ .../derived-property.component.html | 24 ++ .../derived-property.component.less | 35 +++ .../derived-property/derived-property.component.ts | 46 ++++ .../dynamic-property.component.html | 66 ++++++ .../dynamic-property.component.less | 48 ++++ .../dynamic-property/dynamic-property.component.ts | 149 ++++++++++++ .../list-property/list-property.component.html | 33 +++ .../list-property/list-property.component.less | 3 + .../list-property/list-property.component.ts | 85 +++++++ .../map-property/map-property.component.html | 38 +++ .../map-property/map-property.component.ts | 121 ++++++++++ .../properties-table.component.html | 135 +++++++++++ .../properties-table.component.less | 188 +++++++++++++++ .../properties-table/properties-table.component.ts | 93 ++++++++ .../properties-value-inner-table.component.html | 41 ++++ .../properties-value-inner-table.component.less | 71 ++++++ .../properties-value-inner-table.component.ts | 37 +++ .../tooltip/tooltip-content.component.html | 12 + .../tooltip/tooltip-content.component.less | 11 + .../tooltip/tooltip-content.component.ts | 195 ++++++++++++++++ .../ng2/components/tooltip/tooltip.component.ts | 81 +++++++ .../app/ng2/components/tooltip/tooltip.module.ts | 25 ++ 52 files changed, 2811 insertions(+) create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/dynamic-element.component.less create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/dynamic-element.component.ts create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/dynamic-element.module.ts create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/checkbox/ui-element-checkbox.component.html create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/checkbox/ui-element-checkbox.component.less create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/checkbox/ui-element-checkbox.component.ts create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/dropdown/ui-element-dropdown.component.html create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/dropdown/ui-element-dropdown.component.less create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/dropdown/ui-element-dropdown.component.ts create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/input/ui-element-input.component.html create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/input/ui-element-input.component.less create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/input/ui-element-input.component.ts create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/popover-input/ui-element-popover-input.component.html create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/popover-input/ui-element-popover-input.component.less create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/popover-input/ui-element-popover-input.component.ts create mode 100644 catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/ui-element-base.component.ts create mode 100644 catalog-ui/src/app/ng2/components/filter-properties-assignment/filter-properties-assignment.component.html create mode 100644 catalog-ui/src/app/ng2/components/filter-properties-assignment/filter-properties-assignment.component.less create mode 100644 catalog-ui/src/app/ng2/components/filter-properties-assignment/filter-properties-assignment.component.ts create mode 100644 catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-display-options.ts create mode 100644 catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-navigation.component.html create mode 100644 catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-navigation.component.less create mode 100644 catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-navigation.component.ts create mode 100644 catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.html create mode 100644 catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.ts create mode 100644 catalog-ui/src/app/ng2/components/popover/popover-content.component.html create mode 100644 catalog-ui/src/app/ng2/components/popover/popover-content.component.less create mode 100644 catalog-ui/src/app/ng2/components/popover/popover-content.component.ts create mode 100644 catalog-ui/src/app/ng2/components/popover/popover.component.ts create mode 100644 catalog-ui/src/app/ng2/components/popover/popover.module.ts create mode 100644 catalog-ui/src/app/ng2/components/properties-table/derived-property/derived-property.component.html create mode 100644 catalog-ui/src/app/ng2/components/properties-table/derived-property/derived-property.component.less create mode 100644 catalog-ui/src/app/ng2/components/properties-table/derived-property/derived-property.component.ts create mode 100644 catalog-ui/src/app/ng2/components/properties-table/dynamic-property/dynamic-property.component.html create mode 100644 catalog-ui/src/app/ng2/components/properties-table/dynamic-property/dynamic-property.component.less create mode 100644 catalog-ui/src/app/ng2/components/properties-table/dynamic-property/dynamic-property.component.ts create mode 100644 catalog-ui/src/app/ng2/components/properties-table/list-property/list-property.component.html create mode 100644 catalog-ui/src/app/ng2/components/properties-table/list-property/list-property.component.less create mode 100644 catalog-ui/src/app/ng2/components/properties-table/list-property/list-property.component.ts create mode 100644 catalog-ui/src/app/ng2/components/properties-table/map-property/map-property.component.html create mode 100644 catalog-ui/src/app/ng2/components/properties-table/map-property/map-property.component.ts create mode 100644 catalog-ui/src/app/ng2/components/properties-table/properties-table.component.html create mode 100644 catalog-ui/src/app/ng2/components/properties-table/properties-table.component.less create mode 100644 catalog-ui/src/app/ng2/components/properties-table/properties-table.component.ts create mode 100644 catalog-ui/src/app/ng2/components/properties-table/properties-value-inner-table/properties-value-inner-table.component.html create mode 100644 catalog-ui/src/app/ng2/components/properties-table/properties-value-inner-table/properties-value-inner-table.component.less create mode 100644 catalog-ui/src/app/ng2/components/properties-table/properties-value-inner-table/properties-value-inner-table.component.ts create mode 100644 catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.html create mode 100644 catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.less create mode 100644 catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.ts create mode 100644 catalog-ui/src/app/ng2/components/tooltip/tooltip.component.ts create mode 100644 catalog-ui/src/app/ng2/components/tooltip/tooltip.module.ts (limited to 'catalog-ui/src/app/ng2/components') diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/dynamic-element.component.less b/catalog-ui/src/app/ng2/components/dynamic-element/dynamic-element.component.less new file mode 100644 index 0000000000..e219d49aa4 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/dynamic-element.component.less @@ -0,0 +1,3 @@ +dynamic-element { + +} diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/dynamic-element.component.ts b/catalog-ui/src/app/ng2/components/dynamic-element/dynamic-element.component.ts new file mode 100644 index 0000000000..de5965e488 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/dynamic-element.component.ts @@ -0,0 +1,117 @@ +import { Component, Compiler, EventEmitter, ViewContainerRef, ViewChild, Input, Output, ElementRef, ComponentRef, ComponentFactory, ComponentFactoryResolver } from '@angular/core' +import { UiElementCheckBoxComponent } from './elements-ui/checkbox/ui-element-checkbox.component'; +import { UiElementDropDownComponent, DropdownValue } from './elements-ui/dropdown/ui-element-dropdown.component'; +import { UiElementInputComponent } from './elements-ui/input/ui-element-input.component'; +import {UiElementPopoverInputComponent} from "./elements-ui/popover-input/ui-element-popover-input.component"; +import {ValidationConfiguration} from "app/models"; + +@Component({ + selector: 'dynamic-element', + template: `
`, + styleUrls: ['./dynamic-element.component.less'], + entryComponents: [ + UiElementInputComponent, + UiElementDropDownComponent, + UiElementCheckBoxComponent, + UiElementPopoverInputComponent + ] +}) +export class DynamicElementComponent { + + @ViewChild('target', { read: ViewContainerRef }) target: any; + @Input() type: any; + @Input() name: string; + value:any; + + // Two way binding for value (need to write the "Change" word like this) + @Output('valueChange') emitter: EventEmitter = new EventEmitter(); + @Input('value') set setValueValue(value) { + this.value = value; + } + + cmpRef: ComponentRef; + private isViewInitialized: boolean = false; + validation = ValidationConfiguration.validation; + + constructor( + private componentFactoryResolver: ComponentFactoryResolver, + private compiler: Compiler, + private el: ElementRef) { + } + + updateComponent() { + if (!this.isViewInitialized) { + return; + } + if (this.cmpRef) { + this.cmpRef.destroy(); + } + + // Factory to create component based on type or peroperty name. + switch(this.type) { + case 'list': + case 'integer': + this.createComponent(UiElementInputComponent); + this.cmpRef.instance.pattern = this.validation.validationPatterns.integer; + break; + case 'string': + switch(this.name.toUpperCase()) { + case 'SUBNETPOOLID': + this.createComponent(UiElementPopoverInputComponent); + break; + default: + this.createComponent(UiElementInputComponent); + } + break; + case 'boolean': + //this.createComponent(UiElementCheckBoxComponent); + + this.createComponent(UiElementDropDownComponent); + + // Build drop down values + let tmp = []; + tmp.push(new DropdownValue('true','TRUE')); + tmp.push(new DropdownValue('false','FALSE')); + this.cmpRef.instance.values = tmp; + break; + default: + this.createComponent(UiElementInputComponent); + console.log("ERROR: No ui component to handle type: " + this.type); + } + + // Additional attributes in base element class + if (this.cmpRef) { + this.cmpRef.instance.name = this.name; + this.cmpRef.instance.type = this.type; + this.cmpRef.instance.value = this.value; + } + + // Subscribe to change event of of ui-element-basic and fire event to change the value + this.cmpRef.instance.baseEmitter.subscribe((value):void => { + this.emitter.emit(value) + }); + + } + + createComponent(ComponentToCreate:any):void { + let factory = this.componentFactoryResolver.resolveComponentFactory(ComponentToCreate); + this.cmpRef = this.target.createComponent(factory); + } + + ngOnChanges() { + this.updateComponent(); + } + + ngAfterContentInit() { + //console.log("DynamicElementComponent: ngAfterContentInit: type: " + this.type + " value: " + this.value); + this.isViewInitialized = true; + this.updateComponent(); + } + + ngOnDestroy() { + if (this.cmpRef) { + this.cmpRef.destroy(); + } + } + +} diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/dynamic-element.module.ts b/catalog-ui/src/app/ng2/components/dynamic-element/dynamic-element.module.ts new file mode 100644 index 0000000000..18b044bc9d --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/dynamic-element.module.ts @@ -0,0 +1,34 @@ +import { NgModule } from "@angular/core"; +import { UiElementCheckBoxComponent } from './elements-ui/checkbox/ui-element-checkbox.component'; +import { UiElementDropDownComponent } from './elements-ui/dropdown/ui-element-dropdown.component'; +import { UiElementInputComponent } from './elements-ui/input/ui-element-input.component'; +import { DynamicElementComponent } from "app/ng2/components/dynamic-element/dynamic-element.component"; +import { BrowserModule } from '@angular/platform-browser' +import { FormsModule, ReactiveFormsModule } from '@angular/forms' +import { UiElementPopoverInputComponent } from "./elements-ui/popover-input/ui-element-popover-input.component"; +import {PopoverModule} from "../popover/popover.module"; +import {TooltipModule} from "../tooltip/tooltip.module"; + +@NgModule({ + declarations: [ + DynamicElementComponent, + UiElementInputComponent, + UiElementCheckBoxComponent, + UiElementDropDownComponent, + UiElementPopoverInputComponent + ], + imports: [ + BrowserModule, + FormsModule, + PopoverModule, + ReactiveFormsModule, + TooltipModule + ], + exports: [ + DynamicElementComponent + ], + providers: [] +}) +export class DynamicElementModule { + +} diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/checkbox/ui-element-checkbox.component.html b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/checkbox/ui-element-checkbox.component.html new file mode 100644 index 0000000000..2ad3d8e94a --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/checkbox/ui-element-checkbox.component.html @@ -0,0 +1 @@ + diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/checkbox/ui-element-checkbox.component.less b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/checkbox/ui-element-checkbox.component.less new file mode 100644 index 0000000000..bed097fe5e --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/checkbox/ui-element-checkbox.component.less @@ -0,0 +1,2 @@ +/deep/ ui-element-checkbox { +} diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/checkbox/ui-element-checkbox.component.ts b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/checkbox/ui-element-checkbox.component.ts new file mode 100644 index 0000000000..152303aee7 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/checkbox/ui-element-checkbox.component.ts @@ -0,0 +1,27 @@ +import { Component, ViewChild, ElementRef, ContentChildren, Input } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser' +import { UiElementBase, UiElementBaseInterface } from './../ui-element-base.component'; + +@Component({ + selector: 'ui-element-checkbox', + templateUrl: './ui-element-checkbox.component.html', + styleUrls: ['./ui-element-checkbox.component.less'], +}) +export class UiElementCheckBoxComponent extends UiElementBase implements UiElementBaseInterface { + + constructor() { + super(); + } + + ngAfterContentInit() { + // Convert the value to boolean (instanceOf does not work, the type is undefined). + if (this.value==='true' || this.value==='false') { + this.value = this.value==='true'?true:false; + } + } + + onSave() { + this.baseEmitter.emit(this.value); + } + +} diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/dropdown/ui-element-dropdown.component.html b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/dropdown/ui-element-dropdown.component.html new file mode 100644 index 0000000000..589d00e42d --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/dropdown/ui-element-dropdown.component.html @@ -0,0 +1,3 @@ + diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/dropdown/ui-element-dropdown.component.less b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/dropdown/ui-element-dropdown.component.less new file mode 100644 index 0000000000..ea3e35140e --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/dropdown/ui-element-dropdown.component.less @@ -0,0 +1,11 @@ +@import '../../../../../../assets/styles/variables'; + +/deep/ ui-element-dropdown { + + select { + text-indent: 6px; + border: solid 1px @main_color_o; + width: 100%; + } + +} diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/dropdown/ui-element-dropdown.component.ts b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/dropdown/ui-element-dropdown.component.ts new file mode 100644 index 0000000000..208bf54983 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/dropdown/ui-element-dropdown.component.ts @@ -0,0 +1,33 @@ +import { Component, EventEmitter, Output, Input } from '@angular/core' +import { BrowserModule } from '@angular/platform-browser' +import { UiElementBase, UiElementBaseInterface } from './../ui-element-base.component'; + +export class DropdownValue { + value:string; + label:string; + + constructor(value:string,label:string) { + this.value = value; + this.label = label; + } +} + +@Component({ + selector: 'ui-element-dropdown', + templateUrl: './ui-element-dropdown.component.html', + styleUrls: ['./ui-element-dropdown.component.less'], +}) +export class UiElementDropDownComponent extends UiElementBase implements UiElementBaseInterface { + + @Input() + values: DropdownValue[]; + + constructor() { + super(); + } + + onSave() { + this.baseEmitter.emit(this.value); + } + +} diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/input/ui-element-input.component.html b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/input/ui-element-input.component.html new file mode 100644 index 0000000000..00fea65b72 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/input/ui-element-input.component.html @@ -0,0 +1,13 @@ + diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/input/ui-element-input.component.less b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/input/ui-element-input.component.less new file mode 100644 index 0000000000..d320c7ff8b --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/input/ui-element-input.component.less @@ -0,0 +1,17 @@ +@import '../../../../../../assets/styles/variables'; + +/deep/ ui-element-input { + + input { + text-indent: 6px; + border: solid 1px @main_color_o; + } + + .error { + border: solid 1px @func_color_q; + color: @func_color_q; + outline: none; + box-sizing: border-box; + } + +} diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/input/ui-element-input.component.ts b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/input/ui-element-input.component.ts new file mode 100644 index 0000000000..5a14d8f206 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/input/ui-element-input.component.ts @@ -0,0 +1,22 @@ +import { Component, ViewChild, ElementRef, ContentChildren } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser' +import { UiElementBase, UiElementBaseInterface } from './../ui-element-base.component'; + +@Component({ + selector: 'ui-element-input', + templateUrl: './ui-element-input.component.html', + styleUrls: ['./ui-element-input.component.less'], +}) +export class UiElementInputComponent extends UiElementBase implements UiElementBaseInterface { + + constructor() { + super(); + this.pattern = this.validation.validationPatterns.comment; + } + + onSave() { + if (!this.control.invalid){ + this.baseEmitter.emit(this.value); + } + } +} diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/popover-input/ui-element-popover-input.component.html b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/popover-input/ui-element-popover-input.component.html new file mode 100644 index 0000000000..5adceb49a0 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/popover-input/ui-element-popover-input.component.html @@ -0,0 +1,26 @@ +
+ + +
+ + +
+ +
+
diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/popover-input/ui-element-popover-input.component.less b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/popover-input/ui-element-popover-input.component.less new file mode 100644 index 0000000000..5be443f7b6 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/popover-input/ui-element-popover-input.component.less @@ -0,0 +1,36 @@ +@import '../../../../../../assets/styles/variables'; + +.popover-input-wrapper { + display: flex; +} + +/deep/ ui-element-popover-input { + + .popover { + max-width: 350px; + width: 350px; + } + + .edit-subnet-wrapper { + padding: 12px; + + .subnet-value { + width: 100%; + resize: none; + } + } + + input { + padding-right: 6px; + padding-left: 6px; + border: solid 1px @main_color_o; + } + + .error { + border: solid 1px @func_color_q; + color: @func_color_q; + outline: none; + box-sizing: border-box; + } + +} diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/popover-input/ui-element-popover-input.component.ts b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/popover-input/ui-element-popover-input.component.ts new file mode 100644 index 0000000000..7300417686 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/popover-input/ui-element-popover-input.component.ts @@ -0,0 +1,39 @@ +import {Component, ViewChild, ElementRef} from '@angular/core'; +import {UiElementBase, UiElementBaseInterface} from "../ui-element-base.component"; +import {ButtonsModelMap, ButtonModel} from "app/models"; +import { PopoverContentComponent } from "app/ng2/components/popover/popover-content.component" +import { PopoverComponent } from "app/ng2/components/popover/popover.component" + +@Component({ + selector: 'ui-element-popover-input', + templateUrl: './ui-element-popover-input.component.html', + styleUrls: ['./ui-element-popover-input.component.less'] +}) +export class UiElementPopoverInputComponent extends UiElementBase implements UiElementBaseInterface { + + @ViewChild('textArea') textArea: ElementRef; + @ViewChild('popoverForm') popoverContentComponent: PopoverContentComponent; + + saveButton: ButtonModel; + buttonsArray: ButtonsModelMap; + + onSave = ():void => { + if (!this.control.invalid){ + this.baseEmitter.emit(this.value); + this.popoverContentComponent.hide(); + } + } + + constructor() { + super(); + // Create Save button and insert to buttons map + this.saveButton = new ButtonModel('save', 'blue', this.onSave); + this.buttonsArray = { 'test': this.saveButton }; + + // Define the regex pattern for this controller + this.pattern = this.validation.validationPatterns.comment; + + // Disable / Enable button according to validation + //this.control.valueChanges.subscribe(data => this.saveButton.disabled = this.control.invalid); + } +} diff --git a/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/ui-element-base.component.ts b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/ui-element-base.component.ts new file mode 100644 index 0000000000..b60271f6f0 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/dynamic-element/elements-ui/ui-element-base.component.ts @@ -0,0 +1,34 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core' +import { ValidationConfiguration } from "app/models"; +import { FormControl, Validators } from '@angular/forms'; + +export interface UiElementBaseInterface { + onSave(); +} + +@Component({ + template: ``, + styles: [] +}) +export class UiElementBase { + + protected validation = ValidationConfiguration.validation; + protected control: FormControl; + + // Two way binding for value (need to write the "Change" word like this) + @Output('valueChange') baseEmitter: EventEmitter = new EventEmitter(); + @Input('value') set setValueValue(value) { + this.value = value; + } + + protected name: string; + protected type: string; + protected value: any; + protected pattern: any; + + constructor() { + //this.control = new FormControl('', [Validators.required]); + this.control = new FormControl('', []); + } + +} diff --git a/catalog-ui/src/app/ng2/components/filter-properties-assignment/filter-properties-assignment.component.html b/catalog-ui/src/app/ng2/components/filter-properties-assignment/filter-properties-assignment.component.html new file mode 100644 index 0000000000..4d2b91f3b4 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/filter-properties-assignment/filter-properties-assignment.component.html @@ -0,0 +1,26 @@ + + +
+
+ +
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
diff --git a/catalog-ui/src/app/ng2/components/filter-properties-assignment/filter-properties-assignment.component.less b/catalog-ui/src/app/ng2/components/filter-properties-assignment/filter-properties-assignment.component.less new file mode 100644 index 0000000000..c1341b8dcf --- /dev/null +++ b/catalog-ui/src/app/ng2/components/filter-properties-assignment/filter-properties-assignment.component.less @@ -0,0 +1,38 @@ +@import '../../../../assets/styles/variables'; +form{ + margin: 0 20px; + .field{ + padding:20px 0; + &:not(:last-child){ + border-bottom: solid 1px @main_color_o; + } + } + /deep/ [ng-reflect-checked="true"]{ + /deep/ .checkbox-label-content{ + color: @main_color_a; + } + } +} + +.open-filter-button{ + cursor: pointer; + width: 25px; + height: 34px; + display: inline-block; + &.open{ + z-index: 1061; + position: relative; + top: 2px; + background-color: @main_color_p; + border: solid 1px @main_color_c; + border-bottom: none; + } + .filter-icon{ + top: 8px; + position: relative; + } +} + +/deep/ .popover{ + border: solid 1px @main_color_c !important; +} diff --git a/catalog-ui/src/app/ng2/components/filter-properties-assignment/filter-properties-assignment.component.ts b/catalog-ui/src/app/ng2/components/filter-properties-assignment/filter-properties-assignment.component.ts new file mode 100644 index 0000000000..c23e08bc0d --- /dev/null +++ b/catalog-ui/src/app/ng2/components/filter-properties-assignment/filter-properties-assignment.component.ts @@ -0,0 +1,77 @@ +/** + * Created by rc2122 on 5/16/2017. + */ +import {Component, Input, Output, EventEmitter, ViewChild} from '@angular/core'; +import {ButtonModel, ButtonsModelMap, FilterPropertiesAssignmentData} from "app/models"; +import {PopoverComponent} from "../popover/popover.component"; +import * as sdcConfig from "../../../../../configurations/dev" + +@Component({ + selector: 'filter-properties-assignment', + templateUrl: './filter-properties-assignment.component.html', + styleUrls: ['./filter-properties-assignment.component.less'] +}) + +export class FilterPropertiesAssignmentComponent { + @Input() componentType: string; + @Output() searchProperties: EventEmitter = new EventEmitter(); + footerButtons:ButtonsModelMap = {}; + typesOptions:Array;//All optional types + selectedTypes:Object = {}; + allSelected:boolean = false;//if all option selected + filterData:FilterPropertiesAssignmentData = new FilterPropertiesAssignmentData(); + @ViewChild('filterPopover') filterPopover: PopoverComponent; + + ngOnInit() { + this.footerButtons['Apply'] = new ButtonModel('Apply', 'blue', this.search, this.someTypesSelectedAndThereIsPropertyName); + this.footerButtons['Close'] = new ButtonModel('Close', 'grey', this.close); + this.componentType = this.componentType.toLocaleLowerCase(); + this.typesOptions = sdcConfig.resourceTypesFilter[this.componentType]; + } + + selectAll = () => { + _.forEach(this.typesOptions, (type) => { + this.selectedTypes[type] = this.allSelected; + }); + } + + onTypeSelected = (type:string) => { + if(!this.selectedTypes[type]){ + this.allSelected = false;//unselected 'All' + } + }; + + search = () => { + console.log('search props'); + this.filterData.selectedTypes = []; + _.forEach(sdcConfig.resourceTypesFilter[this.componentType], (type) => { + if(this.selectedTypes[type]){ + this.filterData.selectedTypes.push(type); + } + }); + this.searchProperties.emit(this.filterData); + this.filterPopover.hide(); + } + + close = () => { + this.filterPopover.hide(); + } + + someTypesSelectedAndThereIsPropertyName = ():boolean => { + if( _.find(Object.keys(this.selectedTypes),(key) => { + return this.selectedTypes[key]; + }) && this.filterData.propertyName ){ + return null + } + return true; + } + + clearAll = ():void => { + this.filterData.propertyName = ""; + _.forEach(this.selectedTypes,(value, key) => { + this.selectedTypes[key] = false; + }); + this.allSelected = false; + } + +} diff --git a/catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-display-options.ts b/catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-display-options.ts new file mode 100644 index 0000000000..7045286ccd --- /dev/null +++ b/catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-display-options.ts @@ -0,0 +1,12 @@ +export class HierarchyDisplayOptions { + idProperty: string; + valueProperty: string; + childrenProperty: string; + searchText:string; + constructor(idProperty:string, valueProperty:string, childrenProperty?:string, searchText?:string) { + this.idProperty = idProperty; + this.valueProperty = valueProperty; + this.childrenProperty = childrenProperty; + this.searchText = searchText; + } +} diff --git a/catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-navigation.component.html b/catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-navigation.component.html new file mode 100644 index 0000000000..40a1c37cee --- /dev/null +++ b/catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-navigation.component.html @@ -0,0 +1,13 @@ + diff --git a/catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-navigation.component.less b/catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-navigation.component.less new file mode 100644 index 0000000000..57d51616a8 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-navigation.component.less @@ -0,0 +1,51 @@ +.navigation-wrapper { + text-align: left; +} + +.node-item { + border: 1px dotted; + border-right: none; + border-bottom: none; +} + +.node-item:last-child { + border-left: none; +} + +.node-data-wrapper { + cursor: default; + height: 30px; + line-height: 2.6em;; + position: relative; + top: -1.1em; + background-color: white; + margin-left: 0.7em; +} + +.children-node { + padding-left: 40px; +} + +.node-data { + margin-left: 10px; + margin-right: 10px; +} + +.node-data-wrapper.selected { + background-color: #e6f6fb; + + .node-data { + color: #009fdb; + } +} + +.node-data-wrapper:hover { + background-color: #eaeaea; +} + +.mark{ + background-color: yellow; +} + + + diff --git a/catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-navigation.component.ts b/catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-navigation.component.ts new file mode 100644 index 0000000000..428bbb4b04 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/hierarchy-navigtion/hierarchy-navigation.component.ts @@ -0,0 +1,28 @@ +import {Component, Input, Output, EventEmitter} from '@angular/core'; +import {HierarchyDisplayOptions} from './hierarchy-display-options'; + + +@Component({ + selector: 'hierarchy-navigation', + templateUrl: './hierarchy-navigation.component.html', + styleUrls: ['./hierarchy-navigation.component.less'] +}) + +export class HierarchyNavigationComponent { + @Input() displayData: Array; + @Input() selectedItem: any; + @Input() displayOptions: HierarchyDisplayOptions; + + @Output() updateSelected:EventEmitter = new EventEmitter(); + + onClick = ($event, item) => { + $event.stopPropagation(); + this.selectedItem = item; + this.updateSelected.emit(item); + }; + + onSelectedUpdate = ($event) => { + this.selectedItem = $event; + this.updateSelected.emit($event); + } +} diff --git a/catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.html b/catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.html new file mode 100644 index 0000000000..e7801b82cf --- /dev/null +++ b/catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.html @@ -0,0 +1,37 @@ +
+
+
Property Name
+
Type
+
ES
+
Value
+
+
+
No data to display
+
+
+ {{input.name}} + +
+
{{input.type | contentAfterLastDot}}
+
{{input.schema && input.schema.property && input.schema.property.type ? (input.schema.property.type | contentAfterLastDot) : ''}}
+
+ + +
+ +
+
+ +
+
+
+ + diff --git a/catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.ts b/catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.ts new file mode 100644 index 0000000000..ea01edf043 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.ts @@ -0,0 +1,33 @@ +/** + * Created by rc2122 on 5/4/2017. + */ +import {Component, Input, Output, EventEmitter} from "@angular/core"; +import {InputFEModel} from "app/models"; + +@Component({ + selector: 'inputs-table', + templateUrl: './inputs-table.component.html', + styleUrls: ['../properties-table/properties-table.component.less'] +}) +export class InputsTableComponent { + + @Input() inputs: Array; + + @Output() inputValueChanged: EventEmitter = new EventEmitter(); + @Output() deleteInput: EventEmitter = new EventEmitter(); + + constructor (){ + } + + onInputValueChanged = (input) => { + this.inputValueChanged.emit(input); + }; + + onDeleteInput = (input) => { + this.deleteInput.emit(input); + } + + +} + + diff --git a/catalog-ui/src/app/ng2/components/popover/popover-content.component.html b/catalog-ui/src/app/ng2/components/popover/popover-content.component.html new file mode 100644 index 0000000000..6d76f0ad06 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/popover/popover-content.component.html @@ -0,0 +1,24 @@ +
+
+
+
+ {{ title }} + +
+ + +
+ + diff --git a/catalog-ui/src/app/ng2/components/popover/popover-content.component.less b/catalog-ui/src/app/ng2/components/popover/popover-content.component.less new file mode 100644 index 0000000000..f7b62e91f7 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/popover/popover-content.component.less @@ -0,0 +1,73 @@ +@import '../../../../assets/styles/variables'; +@import '../../../../assets/styles/mixins'; +@import '../../../../assets/styles/sprite-old'; +.popover .virtual-area { + height: 11px; + width: 100%; + position: absolute; +} +.popover.top .virtual-area { + bottom: -11px; +} +.popover.bottom .virtual-area { + top: -11px; +} +.popover.left .virtual-area { + right: -11px; +} +.popover.right .virtual-area { + left: -11px; +} +.popover.hide-arrow{ + margin: 0; +} + +.popover-header{ + .m_14_m; + font-weight: bold; + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + height: 40px; + line-height: 48px; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + text-align: left; + border-bottom: solid 1px @main_color_o; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + margin: 0px 20px; + .title{ + -webkit-box-flex: 999; + -ms-flex-positive: 999; + flex-grow: 999; + } + .close-button{ + .sprite; + .sprite.x-btn-black; + cursor: pointer; + } +} + +.popover-footer{ + background-color: @tlv_color_t; + height: 40px; + clear: both; + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: end; + -ms-flex-pack: end; + justify-content: flex-end; + button{ + margin: 8px 12px 8px 6px; + } +} diff --git a/catalog-ui/src/app/ng2/components/popover/popover-content.component.ts b/catalog-ui/src/app/ng2/components/popover/popover-content.component.ts new file mode 100644 index 0000000000..c4489f59b9 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/popover/popover-content.component.ts @@ -0,0 +1,258 @@ +import {Component, Input, Output, AfterViewInit, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, EventEmitter, Renderer } from "@angular/core"; +import {ButtonsModelMap} from "app/models"; +import {PopoverComponent} from "./popover.component"; + +@Component({ + selector: "popover-content", + templateUrl:'./popover-content.component.html', + styleUrls:['popover-content.component.less'] +}) +export class PopoverContentComponent implements AfterViewInit, OnDestroy { + + @Input() public title: string; + @Input() public buttons:ButtonsModelMap; + + @Input() + content: string; + + @Input() + placement: "top"|"bottom"|"left"|"right"|"auto"|"auto top"|"auto bottom"|"auto left"|"auto right" = "bottom"; + + @Input() + animation: boolean = true; + + @Input() + closeOnClickOutside: boolean = false; + + @Input() + closeOnMouseOutside: boolean = false; + + @Input() + hideArrow: boolean = false; + + @ViewChild("popoverDiv") + popoverDiv: ElementRef; + + buttonsNames:Array; + + popover: PopoverComponent; + onCloseFromOutside = new EventEmitter(); + top: number = -10000; + left: number = -10000; + isIn: boolean = false; + displayType: string = "none"; + effectivePlacement: string; + + onDocumentMouseDown = (event: any) => { + const element = this.element.nativeElement; + if (!element || !this.popover) return; + if (element.contains(event.target) || this.popover.getElement().contains(event.target)) return; + this.hide(); + this.onCloseFromOutside.emit(undefined); + }; + + + constructor(protected element: ElementRef, + protected cdr: ChangeDetectorRef, + protected renderer: Renderer) { + } + + listenClickFunc: any; + listenMouseFunc: any; + + ngAfterViewInit(): void { + this.buttonsNames = Object.keys(this.buttons); + if (this.closeOnClickOutside) + this.listenClickFunc = this.renderer.listenGlobal("document", "mousedown", (event: any) => this.onDocumentMouseDown(event)); + if (this.closeOnMouseOutside) + this.listenMouseFunc = this.renderer.listenGlobal("document", "mouseover", (event: any) => this.onDocumentMouseDown(event)); + + this.show(); + this.cdr.detectChanges(); + } + + ngOnDestroy() { + if (this.closeOnClickOutside) + this.listenClickFunc(); + if (this.closeOnMouseOutside) + this.listenMouseFunc(); + } + + // ------------------------------------------------------------------------- + // Public Methods + // ------------------------------------------------------------------------- + + show(): void { + if (!this.popover || !this.popover.getElement()) + return; + + const p = this.positionElements(this.popover.getElement(), this.popoverDiv.nativeElement, this.placement); + this.displayType = "block"; + this.top = p.top; + this.left = p.left; + this.isIn = true; + } + + hide(): void { + this.top = -10000; + this.left = -10000; + this.isIn = true; + this.popover.hide(); + } + + hideFromPopover() { + this.top = -10000; + this.left = -10000; + this.isIn = true; + } + + // ------------------------------------------------------------------------- + // Protected Methods + // ------------------------------------------------------------------------- + + protected positionElements(hostEl: HTMLElement, targetEl: HTMLElement, positionStr: string, appendToBody: boolean = false): { top: number, left: number } { + let positionStrParts = positionStr.split("-"); + let pos0 = positionStrParts[0]; + let pos1 = positionStrParts[1] || "center"; + let hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl); + let targetElWidth = targetEl.offsetWidth; + let targetElHeight = targetEl.offsetHeight; + + this.effectivePlacement = pos0 = this.getEffectivePlacement(pos0, hostEl, targetEl); + + let shiftWidth: any = { + center: function (): number { + return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2; + }, + left: function (): number { + return hostElPos.left; + }, + right: function (): number { + return hostElPos.left + hostElPos.width - targetElWidth; + } + }; + + let shiftHeight: any = { + center: function (): number { + return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2; + }, + top: function (): number { + return hostElPos.top; + }, + bottom: function (): number { + return hostElPos.top + hostElPos.height - targetElHeight; + } + }; + + let targetElPos: { top: number, left: number }; + switch (pos0) { + case "right": + targetElPos = { + top: shiftHeight[pos1](), + left: hostElPos.left + hostElPos.width + }; + break; + + case "left": + targetElPos = { + top: shiftHeight[pos1](), + left: hostElPos.left - targetElWidth + }; + break; + + case "bottom": + targetElPos = { + top: hostElPos.top + hostElPos.height, + left: shiftWidth[pos1]() + }; + break; + + default: + targetElPos = { + top: hostElPos.top - targetElHeight, + left: shiftWidth[pos1]() + }; + break; + } + + return targetElPos; + } + + protected position(nativeEl: HTMLElement): { width: number, height: number, top: number, left: number } { + let offsetParentBCR = { top: 0, left: 0 }; + const elBCR = this.offset(nativeEl); + const offsetParentEl = this.parentOffsetEl(nativeEl); + if (offsetParentEl !== window.document) { + offsetParentBCR = this.offset(offsetParentEl); + offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop; + offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft; + } + + const boundingClientRect = nativeEl.getBoundingClientRect(); + return { + width: boundingClientRect.width || nativeEl.offsetWidth, + height: boundingClientRect.height || nativeEl.offsetHeight, + top: elBCR.top - offsetParentBCR.top, + left: elBCR.left - offsetParentBCR.left + }; + } + + protected offset(nativeEl: any): { width: number, height: number, top: number, left: number } { + const boundingClientRect = nativeEl.getBoundingClientRect(); + return { + width: boundingClientRect.width || nativeEl.offsetWidth, + height: boundingClientRect.height || nativeEl.offsetHeight, + top: boundingClientRect.top + (window.pageYOffset || window.document.documentElement.scrollTop), + left: boundingClientRect.left + (window.pageXOffset || window.document.documentElement.scrollLeft) + }; + } + + protected getStyle(nativeEl: HTMLElement, cssProp: string): string { + if ((nativeEl as any).currentStyle) // IE + return (nativeEl as any).currentStyle[cssProp]; + + if (window.getComputedStyle) + return (window.getComputedStyle as any)(nativeEl)[cssProp]; + + // finally try and get inline style + return (nativeEl.style as any)[cssProp]; + } + + protected isStaticPositioned(nativeEl: HTMLElement): boolean { + return (this.getStyle(nativeEl, "position") || "static" ) === "static"; + } + + protected parentOffsetEl(nativeEl: HTMLElement): any { + let offsetParent: any = nativeEl.offsetParent || window.document; + while (offsetParent && offsetParent !== window.document && this.isStaticPositioned(offsetParent)) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent || window.document; + } + + protected getEffectivePlacement(placement: string, hostElement: HTMLElement, targetElement: HTMLElement): string { + const placementParts = placement.split(" "); + if (placementParts[0] !== "auto") { + return placement; + } + + const hostElBoundingRect = hostElement.getBoundingClientRect(); + + const desiredPlacement = placementParts[1] || "bottom"; + + if (desiredPlacement === "top" && hostElBoundingRect.top - targetElement.offsetHeight < 0) { + return "bottom"; + } + if (desiredPlacement === "bottom" && hostElBoundingRect.bottom + targetElement.offsetHeight > window.innerHeight) { + return "top"; + } + if (desiredPlacement === "left" && hostElBoundingRect.left - targetElement.offsetWidth < 0) { + return "right"; + } + if (desiredPlacement === "right" && hostElBoundingRect.right + targetElement.offsetWidth > window.innerWidth) { + return "left"; + } + + return desiredPlacement; + } +} diff --git a/catalog-ui/src/app/ng2/components/popover/popover.component.ts b/catalog-ui/src/app/ng2/components/popover/popover.component.ts new file mode 100644 index 0000000000..a7e2881b29 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/popover/popover.component.ts @@ -0,0 +1,159 @@ +import { Directive, HostListener, ComponentRef, ViewContainerRef, ComponentFactoryResolver, ComponentFactory, Input, OnChanges, SimpleChange, Output, EventEmitter } from "@angular/core"; +import {PopoverContentComponent} from "./popover-content.component"; + +@Directive({ + selector: "[popover]", + exportAs: "popover" +}) +export class PopoverComponent implements OnChanges { + + protected PopoverComponent = PopoverContentComponent; + protected popover: ComponentRef; + protected visible: boolean; + + + constructor(protected viewContainerRef: ViewContainerRef, + protected resolver: ComponentFactoryResolver) { + } + + @Input("popover") + content: string|PopoverContentComponent; + + @Input() + popoverDisabled: boolean; + + @Input() + popoverAnimation: boolean; + + @Input() + popoverPlacement: "top"|"bottom"|"left"|"right"|"auto"|"auto top"|"auto bottom"|"auto left"|"auto right"; + + @Input() + popoverTitle: string; + + @Input() + popoverOnHover: boolean = false; + + @Input() + popoverCloseOnClickOutside: boolean; + + @Input() + popoverCloseOnMouseOutside: boolean; + + @Input() + popoverDismissTimeout: number = 0; + + @Output() + onShown = new EventEmitter(); + + @Output() + onHidden = new EventEmitter(); + + @HostListener("click") + showOrHideOnClick(): void { + if (this.popoverOnHover) return; + if (this.popoverDisabled) return; + this.toggle(); + } + + @HostListener("focusin") + @HostListener("mouseenter") + showOnHover(): void { + if (!this.popoverOnHover) return; + if (this.popoverDisabled) return; + this.show(); + } + + @HostListener("focusout") + @HostListener("mouseleave") + hideOnHover(): void { + if (this.popoverCloseOnMouseOutside) return; + if (!this.popoverOnHover) return; + if (this.popoverDisabled) return; + this.hide(); + } + + ngOnChanges(changes: {[propertyName: string]: SimpleChange}) { + if (changes["popoverDisabled"]) { + if (changes["popoverDisabled"].currentValue) { + this.hide(); + } + } + } + + toggle() { + if (!this.visible) { + this.show(); + } else { + this.hide(); + } + } + + show() { + if (this.visible) return; + + this.visible = true; + if (typeof this.content === "string") { + const factory = this.resolver.resolveComponentFactory(this.PopoverComponent); + if (!this.visible) + return; + + this.popover = this.viewContainerRef.createComponent(factory); + const popover = this.popover.instance as PopoverContentComponent; + popover.popover = this; + popover.content = this.content as string; + if (this.popoverPlacement !== undefined) + popover.placement = this.popoverPlacement; + if (this.popoverAnimation !== undefined) + popover.animation = this.popoverAnimation; + if (this.popoverTitle !== undefined) + popover.title = this.popoverTitle; + if (this.popoverCloseOnClickOutside !== undefined) + popover.closeOnClickOutside = this.popoverCloseOnClickOutside; + if (this.popoverCloseOnMouseOutside !== undefined) + popover.closeOnMouseOutside = this.popoverCloseOnMouseOutside; + + popover.onCloseFromOutside.subscribe(() => this.hide()); + if (this.popoverDismissTimeout > 0) + setTimeout(() => this.hide(), this.popoverDismissTimeout); + } else { + const popover = this.content as PopoverContentComponent; + popover.popover = this; + if (this.popoverPlacement !== undefined) + popover.placement = this.popoverPlacement; + if (this.popoverAnimation !== undefined) + popover.animation = this.popoverAnimation; + if (this.popoverTitle !== undefined) + popover.title = this.popoverTitle; + if (this.popoverCloseOnClickOutside !== undefined) + popover.closeOnClickOutside = this.popoverCloseOnClickOutside; + if (this.popoverCloseOnMouseOutside !== undefined) + popover.closeOnMouseOutside = this.popoverCloseOnMouseOutside; + + popover.onCloseFromOutside.subscribe(() => this.hide()); + if (this.popoverDismissTimeout > 0) + setTimeout(() => this.hide(), this.popoverDismissTimeout); + popover.show(); + } + + this.onShown.emit(this); + } + + hide() { + if (!this.visible) return; + + this.visible = false; + if (this.popover) + this.popover.destroy(); + + if (this.content instanceof PopoverContentComponent) + (this.content as PopoverContentComponent).hideFromPopover(); + + this.onHidden.emit(this); + } + + getElement() { + return this.viewContainerRef.element.nativeElement; + } + +} diff --git a/catalog-ui/src/app/ng2/components/popover/popover.module.ts b/catalog-ui/src/app/ng2/components/popover/popover.module.ts new file mode 100644 index 0000000000..4bd8426ce1 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/popover/popover.module.ts @@ -0,0 +1,27 @@ +/** + * Created by rc2122 on 5/17/2017. + */ +import {NgModule} from "@angular/core"; +import { CommonModule } from '@angular/common'; +import {PopoverComponent} from "./popover.component"; +import {PopoverContentComponent} from "./popover-content.component"; + +@NgModule({ + declarations: [ + PopoverComponent, + PopoverContentComponent + ], + imports: [ + // PopoverComponent, + // PopoverContentComponent + CommonModule + ], + exports: [ + PopoverComponent, + PopoverContentComponent + ], + providers: [] +}) +export class PopoverModule { + +} diff --git a/catalog-ui/src/app/ng2/components/properties-table/derived-property/derived-property.component.html b/catalog-ui/src/app/ng2/components/properties-table/derived-property/derived-property.component.html new file mode 100644 index 0000000000..6e7d4e882b --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/derived-property/derived-property.component.html @@ -0,0 +1,24 @@ + +
+
+ +
+ +
+
+
+
+
+ {{propertyObj.type | contentAfterLastDot }} + V +
+
+
+
+ {{propertyObj.name}} +
+
+
+
+
+ diff --git a/catalog-ui/src/app/ng2/components/properties-table/derived-property/derived-property.component.less b/catalog-ui/src/app/ng2/components/properties-table/derived-property/derived-property.component.less new file mode 100644 index 0000000000..3102c5ceb8 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/derived-property/derived-property.component.less @@ -0,0 +1,35 @@ +.derived-property-row { + display:flex; + flex-direction:row; + position:relative; + border-top: #d2d2d2 solid 1px; + + &:first-child { + border-top:none; + } + .table-cell { + flex: 0 0 50%; + padding:5px; + position:relative; + text-overflow: ellipsis; + white-space: nowrap; + + &:first-child { + border-right:#d2d2d2 solid 1px; + overflow:hidden; + } + span.expand-icon { + position: absolute; + right: 10px; + transition: 200ms transform ease-in-out; + } + span.expand-icon.expanded { + transform: rotate(-180deg); + } + } +} +.filtered { + /deep/ .checkbox-label-content{ + background-color: yellow; + } +} diff --git a/catalog-ui/src/app/ng2/components/properties-table/derived-property/derived-property.component.ts b/catalog-ui/src/app/ng2/components/properties-table/derived-property/derived-property.component.ts new file mode 100644 index 0000000000..16f069a685 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/derived-property/derived-property.component.ts @@ -0,0 +1,46 @@ +/** + * Created by rc2122 on 4/20/2017. + */ +import {Component, Input, Output, EventEmitter} from "@angular/core"; +import { DerivedFEProperty, DerivedPropertyType} from "app/models"; +import {PropertiesService} from "../../../services/properties.service"; + +@Component({ + selector: 'derived-property', + templateUrl: './derived-property.component.html', + styleUrls: ['./derived-property.component.less'] +}) +export class DerivedPropertyComponent { + + derivedPropertyTypes = DerivedPropertyType; //http://stackoverflow.com/questions/35835984/how-to-use-a-typescript-enum-value-in-an-angular2-ngswitch-statement + + @Input() propertyObj: DerivedFEProperty; + @Input() propertyNameSearchText: string; + @Input() expanded: boolean; + @Output() valueChanged: EventEmitter = new EventEmitter(); + @Output() expandChild: EventEmitter = new EventEmitter(); + @Output() selectProperty: EventEmitter = new EventEmitter(); + + + constructor ( private propertiesService:PropertiesService){ + } + + + propValueChanged = () => { + this.valueChanged.emit(this.propertyObj); + }; + + expandChildById = (id: string) => { + this.expandChild.emit(id); + } + + checkedChange = (isChecked:boolean) => { + this.selectProperty.emit(isChecked); + } + + addRows = (flatProperty: DerivedFEProperty): void => { + console.log("ADDING A ROW OF TYPE " + flatProperty.type); + console.log(flatProperty); + } + +} diff --git a/catalog-ui/src/app/ng2/components/properties-table/dynamic-property/dynamic-property.component.html b/catalog-ui/src/app/ng2/components/properties-table/dynamic-property/dynamic-property.component.html new file mode 100644 index 0000000000..17e4002ae5 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/dynamic-property/dynamic-property.component.html @@ -0,0 +1,66 @@ +
+ + +
+ +
+
{{property.name}}
+
+ + +
+ + +
+
+ + + +
+ {{property.type | contentAfterLastDot }} +
+
+ +
+
+
+
+ {{property.schema.property.type | contentAfterLastDot }} +
+
+ + +
+ + V +
+ +
+ + + + + + + diff --git a/catalog-ui/src/app/ng2/components/properties-table/dynamic-property/dynamic-property.component.less b/catalog-ui/src/app/ng2/components/properties-table/dynamic-property/dynamic-property.component.less new file mode 100644 index 0000000000..53cde3035a --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/dynamic-property/dynamic-property.component.less @@ -0,0 +1,48 @@ +.dynamic-property-row { + display:flex; + flex-direction:row; + position:relative; + + .table-cell { + flex: 1 0 50%; + padding:5px; + position:relative; + text-overflow: ellipsis; + white-space: nowrap; + + + &.full-width { + border-right:none; + flex: 0 0 100%; + } + + &:first-child:not(:only-child) { + border-right:#d2d2d2 solid 1px; + overflow:hidden; + } + + + } + span.delete-item-icon{ + position: absolute; + right: 25px; + top: 10px; + } + span.add-item-icon { + float:right; + } + span.expand-icon { + position: absolute; + top:6px; + right: 10px; + transition: 200ms transform ease-in-out; + } + span.expand-icon.expanded { + transform: rotate(-180deg); + } +} +.filtered { + /deep/ .checkbox-label-content{ + background-color: yellow; + } +} diff --git a/catalog-ui/src/app/ng2/components/properties-table/dynamic-property/dynamic-property.component.ts b/catalog-ui/src/app/ng2/components/properties-table/dynamic-property/dynamic-property.component.ts new file mode 100644 index 0000000000..0ca93a773f --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/dynamic-property/dynamic-property.component.ts @@ -0,0 +1,149 @@ +import {Component, Input, Output, EventEmitter} from "@angular/core"; +import { PropertyBEModel, PropertyFEModel, DerivedFEProperty, DerivedPropertyType, SchemaPropertyGroupModel, DataTypeModel } from "app/models"; +import { PROPERTY_DATA, PROPERTY_TYPES } from 'app/utils'; +import { PropertiesUtils } from "app/ng2/pages/properties-assignment/properties.utils"; +import { DataTypeService } from "../../../services/data-type.service"; +import { UUID } from "angular2-uuid"; + + +@Component({ + selector: 'dynamic-property', + templateUrl: './dynamic-property.component.html', + styleUrls: ['./dynamic-property.component.less'] +}) +export class DynamicPropertyComponent { + + derivedPropertyTypes = DerivedPropertyType; //http://stackoverflow.com/questions/35835984/how-to-use-a-typescript-enum-value-in-an-angular2-ngswitch-statement + propType: DerivedPropertyType; + propPath: string; + isPropertyFEModel: boolean; + mapOfIDsAndKeys: Map = new Map(); //used for map and list + + childrenCanBeDeclared: boolean; + @Input() canBeDeclared: boolean; + @Input() property: PropertyFEModel | DerivedFEProperty; + @Input() propChildren: Array; + @Input() expandedChildId: string; + @Input() selectedPropertyId: string; + + @Output() valueChanged: EventEmitter = new EventEmitter(); + @Output() expandChild: EventEmitter = new EventEmitter(); + @Output() checkProperty: EventEmitter = new EventEmitter(); + @Output() deleteItem: EventEmitter = new EventEmitter(); + @Output() clickOnPropertyRow: EventEmitter = new EventEmitter(); + + constructor(private propertiesUtils: PropertiesUtils, private dataTypeService: DataTypeService) { + } + + ngOnInit() { + this.isPropertyFEModel = this.property instanceof PropertyFEModel; + if (this.property instanceof PropertyFEModel) { + this.propType = this.getDerivedPropertyType(this.property.type); + this.propPath = this.property.name; + } else { + this.propType = this.property.derivedDataType; + this.propPath = this.property.propertiesName; + } + + this.childrenCanBeDeclared = this.canBeDeclared && this.propType != this.derivedPropertyTypes.MAP && this.propType != this.derivedPropertyTypes.LIST; + + if (this.propType == this.derivedPropertyTypes.LIST || this.propType == this.derivedPropertyTypes.MAP) { + this.initializeValues(); + } + + } + + initializeValues = () => { + let tempValue: any; + if (this.property.value) { + tempValue = JSON.parse(this.property.value); + if (!_.isEmpty(tempValue)) { + tempValue.forEach((element, key) => { + let newChildID: string = this.createNewChildProperty(JSON.stringify(element)); + this.mapOfIDsAndKeys[newChildID] = key; + console.log(this.mapOfIDsAndKeys); + }); + } + } + //this.pseudoChildren = []; + //this.valueObjRef = []; + //TODO: generate necessary elements for existing values here + // if (this.propType == this.derivedPropertyTypes.LIST) { + // this.valueObjRef = (this.property.value) ? JSON.parse(this.property.value) : []; + // } else if (this.propType == this.derivedPropertyTypes.MAP) { + // this.valueObjRef = (this.property.value)? JSON.parse(this.property.value) : {}; + // } + console.log(this.property.value); + } + + onClickPropertyRow = (property, event) => { + // Because DynamicPropertyComponent is recrusive second time the event is fire event.stopPropagation = undefined + event && event.stopPropagation && event.stopPropagation(); + this.clickOnPropertyRow.emit(property); + } + + deleteListOrMapItem = (itemName: string) => { + this.propChildren = this.propChildren.filter(prop => prop.propertiesName.indexOf(itemName) != 0); //remove item and children; + } + + propValueChanged = (property) => { + console.log("property value change!! Prop type: " + property.type + " New value: " + property.value); + this.valueChanged.emit(property); + }; + + expandChildById = (id: string) => { + this.expandedChildId = id; + this.expandChild.emit(id); + } + + checkedChange = (propName: string) => { + this.checkProperty.emit(propName); + } + + + + addRows = (): void => { //from within the template, when creating empty item + let childPropId = this.createNewChildProperty(); + this.expandChildById(this.propPath + "#" + childPropId); + } + + createNewChildProperty = (value?:string):string => { + let propUUID:string = UUID.UUID(); + let newProp: DerivedFEProperty; + if (this.propType == this.derivedPropertyTypes.LIST) { //for list - create new prop of schema type + newProp = new DerivedFEProperty(propUUID, this.propPath, this.property.schema.property.type, value, true); + } else { //for map - create new prop of type map, with schema, but with flag that its a child + newProp = new DerivedFEProperty(propUUID, this.propPath, this.property.type, value, true, this.property.schema); + } + + + this.propChildren = this.propChildren || []; + this.propChildren.push(newProp); + + //if it's a complex type, add children properties + if (!this.property.schema.property.isSimpleType) { + let schemaDataType: DataTypeModel = this.dataTypeService.getDataTypeByTypeName(this.property.schema.property.type); + this.dataTypeService.getDerivedDataTypeProperties(schemaDataType, this.propChildren, newProp.propertiesName); + this.propertiesUtils.assignValuesRecursively(JSON.parse(value), this.propChildren, newProp.propertiesName); + console.log(JSON.stringify(this.propChildren)); + } + + return propUUID; + } + + + + //TODO: remove this and move to somewhere central!! (or make all properties be the same type...) + getDerivedPropertyType = (type) => { + if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(type) > -1) { + return DerivedPropertyType.SIMPLE; + } else if (type == PROPERTY_TYPES.LIST) { + return DerivedPropertyType.LIST; + } else if (type == PROPERTY_TYPES.MAP) { + return DerivedPropertyType.MAP; + } else { + return DerivedPropertyType.COMPLEX; + } + } + +} diff --git a/catalog-ui/src/app/ng2/components/properties-table/list-property/list-property.component.html b/catalog-ui/src/app/ng2/components/properties-table/list-property/list-property.component.html new file mode 100644 index 0000000000..a251d33649 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/list-property/list-property.component.html @@ -0,0 +1,33 @@ +
+
+
+
+ +
+ + +
+
+
+ + +
diff --git a/catalog-ui/src/app/ng2/components/properties-table/list-property/list-property.component.less b/catalog-ui/src/app/ng2/components/properties-table/list-property/list-property.component.less new file mode 100644 index 0000000000..7c4d90a38d --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/list-property/list-property.component.less @@ -0,0 +1,3 @@ +.simple-list-item{ + position: relative; +} diff --git a/catalog-ui/src/app/ng2/components/properties-table/list-property/list-property.component.ts b/catalog-ui/src/app/ng2/components/properties-table/list-property/list-property.component.ts new file mode 100644 index 0000000000..96f8c680a2 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/list-property/list-property.component.ts @@ -0,0 +1,85 @@ +/** + * Created by rc2122 on 4/23/2017. + */ +import {Component, Input, Output, EventEmitter} from "@angular/core"; +import { PropertyFEModel} from "app/models"; +import {PropertiesService} from "app/ng2/services/properties.service"; +import { ContentAfterLastDotPipe } from "app/ng2/pipes/contentAfterLastDot.pipe"; +import {UUID} from "angular2-uuid"; +import {ComponentType} from "app/utils"; + +@Component({ + selector: 'list-property', + templateUrl: './list-property.component.html', + styleUrls: ['../properties-value-inner-table/properties-value-inner-table.component.less', './list-property.component.less'] +}) +export class ListPropertyComponent { + + @Input() property: PropertyFEModel; + @Input() selectedPropertyId: string; + @Input() propertyNameSearchText:string; + + @Output() valueChanged: EventEmitter = new EventEmitter(); + @Output() selectChildProperty: EventEmitter = new EventEmitter(); + + constructor ( private propertiesService:PropertiesService, private contentAfterLastDotPipe:ContentAfterLastDotPipe ){ + } + + propValueChanged = () => { + this.valueChanged.emit(this.property); + }; + + onChildPropertySelected = (property) => { + this.selectChildProperty.emit(property); + }; + + getNumber = (valueObjectRef: any): Array => { + let num: number = (valueObjectRef) ? valueObjectRef.length : 0; + return new Array(num); + } + + createNewChildProperty = ():void => { + let newProperty: PropertyFEModel = new PropertyFEModel(this.contentAfterLastDotPipe.transform(this.property.schema.property.type), + this.property.schema.property.type, + UUID.UUID(), + this.property, + this.property.valueObjectRef[this.property.childrenProperties.length] + ); + this.propertiesService.createPropertiesTreeForProp(newProperty); + this.property.childrenProperties.push(newProperty); + } + + addListItem = ():void => { + this.property.valueObjectRef = this.property.valueObjectRef || []; + this.property.childrenProperties = this.property.childrenProperties || []; + if (this.property.schema.property.isSimpleType){ + if( this.property.valueObjectRef.indexOf("") == -1 ) {//prevent insert multiple empty simple type items to list + this.property.valueObjectRef.push(""); + } + }else{ + this.property.valueObjectRef[this.property.childrenProperties.length] = {}; + this.property.childrenProperties = this.property.childrenProperties || []; + this.createNewChildProperty(); + this.valueChanged.emit(this.property); + } + } + + deleteListItem = (indexInList:number):void => { + this.property.valueObjectRef.splice(indexInList, 1); + if(this.property.childrenProperties){ + this.property.childrenProperties.splice(indexInList, 1); + } + if (!this.property.valueObjectRef.length) {//only when user removes all items from list - put the default + if ( this.property.defaultValue ) { + angular.copy(JSON.parse(this.property.defaultValue), this.property.valueObjectRef); + if (this.property.schema.property.isDataType){ + _.forEach(this.property.valueObjectRef, () => { + this.createNewChildProperty(); + }); + } + } + } + this.valueChanged.emit(this.property); + } + +} diff --git a/catalog-ui/src/app/ng2/components/properties-table/map-property/map-property.component.html b/catalog-ui/src/app/ng2/components/properties-table/map-property/map-property.component.html new file mode 100644 index 0000000000..e1975175a8 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/map-property/map-property.component.html @@ -0,0 +1,38 @@ +
+
+
+ + + + diff --git a/catalog-ui/src/app/ng2/components/properties-table/map-property/map-property.component.ts b/catalog-ui/src/app/ng2/components/properties-table/map-property/map-property.component.ts new file mode 100644 index 0000000000..d62d0b94e3 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/map-property/map-property.component.ts @@ -0,0 +1,121 @@ +/** + * Created by rc2122 on 4/24/2017. + */ +/** + * Created by rc2122 on 4/23/2017. + */ +import {Component, Input, Output, EventEmitter} from "@angular/core"; +import { PropertyFEModel} from "app/models"; +import { PropertiesService } from "../../../services/properties.service"; +import {ComponentType} from "app/utils"; +import {UUID} from "angular2-uuid"; + +@Component({ + selector: 'map-property', + templateUrl: './map-property.component.html', + styleUrls: ['../properties-value-inner-table/properties-value-inner-table.component.less'] +}) +export class MapPropertyComponent { + + @Input() property: PropertyFEModel; + @Input() selectedPropertyId: string; + @Input() propertyNameSearchText:string; + + @Output() valueChanged: EventEmitter = new EventEmitter(); + @Output() selectChildProperty: EventEmitter = new EventEmitter(); + + constructor ( private propertiesService:PropertiesService){ + } + + mapKeys:Array; + + ngOnInit() { + this.mapKeys = Object.keys(this.property.valueObjectRef); + } + + propValueChanged = () => { + this.valueChanged.emit(this.property); + }; + + onChildPropertySelected = (property) => { + this.selectChildProperty.emit(property); + }; + + getNumber = (num:number):Array => { + return new Array(num); + } + + createNewChildProperty = (mapKey:string):void => { + + let newProperty: PropertyFEModel = new PropertyFEModel(mapKey, + this.property.schema.property.type, + UUID.UUID(), this.property, + this.property.valueObjectRef[mapKey]); + this.propertiesService.createPropertiesTreeForProp(newProperty); + this.property.childrenProperties = this.property.childrenProperties || []; + this.property.childrenProperties.push(newProperty); + } + + //get: new key and the index of this item in the map + //This method checks if the new key isn't exist already in the map and update the object and the children array with the new key + changeKeyOfMap = (newKey:string, index:number):void => { + //let fieldName:string = "mapKey" + this.property.treeNodeId + index; + let oldKey:string = Object.keys(this.property.valueObjectRef)[index]; + let existsKeyIndex:number = Object.keys(this.property.valueObjectRef).indexOf(newKey); + if (existsKeyIndex > -1 && existsKeyIndex != index) { + //error for exists key validation + } else { + //remove error for exists key validation and if the form is valid - update the map object + let newObj = {}; + angular.forEach(this.property.valueObjectRef,function(value:any,key:string){ + if(key == oldKey){ + newObj[newKey] = value; + }else{ + newObj[key] = value; + } + }); + this.property.valueObjectRef = newObj; + this.property.parent.valueObjectRef[this.property.name] = this.property.valueObjectRef;//in order to prevent break ref + if(this.property.childrenProperties){ + this.property.childrenProperties[index].name = newKey;//update this property childrenProperties with the new key + } + } + } + + //get: index of the item in the map + //This method removes item from map. + deleteMapItem = (index:number):void=> { + delete this.property.valueObjectRef[this.mapKeys[index]]; + this.mapKeys.splice(index, 1); + if(this.property.childrenProperties){ + this.property.childrenProperties.splice(index, 1); + } + if (!this.mapKeys.length) {//only when user removes all pairs of key-value fields - put the default + if (this.property.defaultValue) { + angular.copy(JSON.parse(this.property.defaultValue), this.property.valueObjectRef); + this.mapKeys = Object.keys(this.property.valueObjectRef); + if (this.property.schema.property.isDataType){ + angular.forEach(this.property.valueObjectRef, (value, key) => { + this.createNewChildProperty(key); + }, this); + } + } + } + this.valueChanged.emit(this.property); + } + + //This method inserts new empty item to map + addMapItemFields = ():void => { + this.property.valueObjectRef = this.property.valueObjectRef || {}; + if (this.property.schema.property.isSimpleType){ + this.property.valueObjectRef[''] = null; + }else{ + if(!this.property.valueObjectRef['']){ + this.property.valueObjectRef[''] = {}; + this.createNewChildProperty(''); + } + } + this.mapKeys = Object.keys(this.property.valueObjectRef); + } +} + diff --git a/catalog-ui/src/app/ng2/components/properties-table/properties-table.component.html b/catalog-ui/src/app/ng2/components/properties-table/properties-table.component.html new file mode 100644 index 0000000000..3ab47074e7 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/properties-table.component.html @@ -0,0 +1,135 @@ + + +
+
+
Property Name
+
Type
+
ES
+
Value
+
+
+
No data to display
+ +
+
{{instanceName | contentAfterLastDot}}
+ +
+
+ + +
+
{{property.type | contentAfterLastDot}}
+
{{property.schema && property.schema.property && property.schema.property.type ? (property.schema.property.type + | contentAfterLastDot) : ''}}
+
+ + + + +
+
+ + + + +
+ +
+
+ + + + diff --git a/catalog-ui/src/app/ng2/components/properties-table/properties-table.component.less b/catalog-ui/src/app/ng2/components/properties-table/properties-table.component.less new file mode 100644 index 0000000000..de080dfdc9 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/properties-table.component.less @@ -0,0 +1,188 @@ + +@import './../../../../assets/styles/variables.less'; + +:host /deep/ input { width:100%;} + +.properties-table { + display:flex; + flex-direction:column; + flex: 1; + height:100%; + text-align:left; + + + .child-property-container { + display:flex; + flex-direction:column; + + &.table-cell { + padding:0; + } + + .child-property-row { + border-bottom: #d2d2d2 solid 1px; + &:last-child { + border-bottom:none; + } + } + } + + + derived-property, dynamic-property { + width:100%; + } + + /deep/ dynamic-property dynamic-property .dynamic-property-row { + border-top:solid #d2d2d2 1px; + } + + /deep/ dynamic-property dynamic-property:first-of-type .dynamic-property-row:not(.with-top-border) { + border-top: none; + } + + properties-value-inner-table { + width: 100%; + } + + .table-header { + font-weight:bold; + border-top: #d2d2d2 solid 1px; + background-color: #eaeaea; + + .valueCol { + justify-content: flex-start; + padding: 5px; + } + } + .table-header, .table-row { + display: flex; + flex-direction:row; + flex: 0 0 auto; + } + + .table-body { + display:flex; + flex-direction: column; + overflow-y:auto; + flex: 1; + + .no-data { + border: #d2d2d2 solid 1px; + border-top:none; + text-align: center; + height: 100%; + padding: 20px; + } + /deep/.selected{ + background-color: #e6f6fb; + color: #009fdb; + } + } + + .table-rows-header { + font-size:16px; + flex:1; + border: #d2d2d2 solid 1px; + border-top:none; + padding: 5px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + background-color: @tlv_color_v; + } + + .table-row { + &:hover { + background-color:#f8f8f8; cursor:pointer; + } + + &:last-child { + flex: 1 0 auto; + } + .selected-row { + background-color:#e6f6fb; + } + } + .table-cell { + font-size:13px; + flex:1; + border: #d2d2d2 solid 1px; + border-right:none; + border-top:none; + padding: 5px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + + &:last-child { + border-right:#d2d2d2 solid 1px; + } + &.col1 { + flex: 0 0 300px; + max-width:300px; + + .property-description-icon { + float: right; + margin-top: 4px; + } + } + &.col2 { + flex: 0 0 150px; + max-width:150px; + } + + &.col3 { + flex:0 0 120px; + max-width:120px; + } + + &.valueCol { + flex: 1 0 auto; + min-width: 350px; + display: flex; + justify-content: flex-end; + padding: 0px; + + .value-input { + flex: 1; + max-height: 24px; + border: none; + background-color: inherit; + + &:focus, &:active { + border:none; + outline:none; + } + } + + .delete-btn { + flex: 0 0 auto; + } + + .delete-button-container { + max-height: 24px; + } + + &.inner-table-container { + padding: 0px; + + .delete-button-container { + padding: 3px 5px 0 0 ; + } + } + } + + &.input-value-col { + padding: 5px; + } + + + } + + .filtered { + /deep/ .checkbox-label-content{ + background-color: yellow; + } + } + +} diff --git a/catalog-ui/src/app/ng2/components/properties-table/properties-table.component.ts b/catalog-ui/src/app/ng2/components/properties-table/properties-table.component.ts new file mode 100644 index 0000000000..d5a9b40425 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/properties-table.component.ts @@ -0,0 +1,93 @@ +import { Component, Input, Output, EventEmitter, SimpleChanges, ViewChild, ElementRef } from "@angular/core"; +import { trigger, state, style, transition, animate} from '@angular/core'; +import {PropertyFEModel, DerivedFEProperty, DerivedPropertyType, InstanceFePropertiesMap} from "app/models"; +import {PropertiesService} from "../../services/properties.service"; +// import { GroupByPipe } from 'app/ng2/pipes/groupBy.pipe'; +//import {PropertiesValueInnerTableComponent} from "./properties-table/properties-value-inner-table/properties-value-inner-table"; +import { DynamicElementComponent } from 'app/ng2/components/dynamic-element/dynamic-element.component'; +import { KeysPipe } from 'app/ng2/pipes/keys.pipe'; + +@Component({ + selector: 'properties-table', + templateUrl: './properties-table.component.html', + styleUrls: ['./properties-table.component.less'], + animations: [trigger('fadeIn', [transition(':enter', [style({ opacity: '0' }), animate('.3s ease-out', style({ opacity: '1' }))]) ])] +}) +export class PropertiesTableComponent { + + @Input() fePropertiesMap: InstanceFePropertiesMap; + @Input() selectedPropertyId: string; + @Input() displayDeleteButton: boolean; + @Input() propertyNameSearchText:string; + + @Output() valueChanged: EventEmitter = new EventEmitter(); + @Output() selectPropertyRow: EventEmitter = new EventEmitter(); + @Output() updateCheckedPropertyCount: EventEmitter = new EventEmitter(); + //@Output() selectInstanceRow: EventEmitter = new EventEmitter(); + + feInstancesNames: Array; + + constructor ( private propertiesService:PropertiesService ){ + } + + /** + * Update feInstancesNames when fePropertiesMap: InstanceFePropertiesMap change (after getting response from server) + */ + ngOnChanges(changes: SimpleChanges) { + if (changes['fePropertiesMap']) { + if (changes['fePropertiesMap'].currentValue) { + let keysPipe = new KeysPipe(); + let fiteredArr = keysPipe.transform(changes['fePropertiesMap'].currentValue,[]); + this.feInstancesNames = fiteredArr; + } + } + } + + propValueChanged = (property) => { + !property.isDeclared && this.valueChanged.emit(property); + }; + + // Click on main row (row of propertyFEModel) + onClickPropertyRow = (property:PropertyFEModel, instanceName:string, event?) => { + //event && event.stopPropagation(); + let propertyRowSelectedEvent:PropertyRowSelectedEvent = new PropertyRowSelectedEvent(property, instanceName); + this.selectPropertyRow.emit(propertyRowSelectedEvent); + }; + + // Click on inner row (row of DerivedFEProperty) + onClickPropertyInnerRow = (property:DerivedFEProperty, instanceName:string) => { + let propertyRowSelectedEvent:PropertyRowSelectedEvent = new PropertyRowSelectedEvent(property, instanceName); + this.selectPropertyRow.emit(propertyRowSelectedEvent); + } + + propertyChecked = (prop: PropertyFEModel, childPropName?: string) => { + let isChecked: boolean = (!childPropName)? prop.isSelected : prop.flattenedChildren.find(prop => prop.propertiesName == childPropName).isSelected; + + console.log(isChecked, childPropName, prop); + if (!isChecked) { + this.propertiesService.undoDisableRelatedProperties(prop, childPropName); + } else { + this.propertiesService.disableRelatedProperties(prop, childPropName); + } + this.updateCheckedPropertyCount.emit(isChecked); + } + + putDefaultValueInEmptyProperty = (property:PropertyFEModel):void => { + property.value = property.value || property.defaultValue; + } + + // clickOnInstanceRow = (instanceName:string) =>{ + // this.selectInstanceRow.emit(instanceName); + // }; + +} + +export class PropertyRowSelectedEvent { + propertyModel:PropertyFEModel | DerivedFEProperty; + instanceName:string; + constructor ( propertyModel:PropertyFEModel | DerivedFEProperty, instanceName:string ){ + this.propertyModel = propertyModel; + this.instanceName = instanceName; + } +} + diff --git a/catalog-ui/src/app/ng2/components/properties-table/properties-value-inner-table/properties-value-inner-table.component.html b/catalog-ui/src/app/ng2/components/properties-table/properties-value-inner-table/properties-value-inner-table.component.html new file mode 100644 index 0000000000..61555cac50 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/properties-value-inner-table/properties-value-inner-table.component.html @@ -0,0 +1,41 @@ + + + + diff --git a/catalog-ui/src/app/ng2/components/properties-table/properties-value-inner-table/properties-value-inner-table.component.less b/catalog-ui/src/app/ng2/components/properties-table/properties-value-inner-table/properties-value-inner-table.component.less new file mode 100644 index 0000000000..1b7f6d4cd1 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/properties-value-inner-table/properties-value-inner-table.component.less @@ -0,0 +1,71 @@ +table { width:100%;} +tr {border-bottom: #d2d2d2 solid 1px;} +tr:last-child { border-bottom:none;} +td { border:none; padding:5px;} +td:first-child { border-right:#d2d2d2 solid 1px;} + +.prop-value{ + span { + position: absolute; + top: 5px; + right: 2px; + + &.delete-span { + right:20px; + } + + &.datatype-text { + position:static; + } + + } +} + +.add-data-row { + padding:5px; + text-align:right; + border-bottom: #d2d2d2 solid 1px; + + &:last-child { + border-bottom:none; + } +} +.table-inner-row { + display:flex; + flex-direction:row; + border-bottom: #d2d2d2 solid 1px; + flex: 0 0 100%; + position:relative; + + &:last-child { + border-bottom:none; + } + + .table-cell { + flex: 0 0 50%; + padding:5px; + position:relative; + text-overflow: ellipsis; + white-space: nowrap; + + &:first-child { + border-right:#d2d2d2 solid 1px; + overflow:hidden; + } + + + } + + .table-inner-container, .inner-table-container { + flex: 0 0 100%; + } +} +/deep/ map-property, /deep/ properties-value-inner-table, /deep/ list-property{ + width:100%; +} + +.filtered { + /deep/ .checkbox-label-content{ + background-color: yellow; + } +} diff --git a/catalog-ui/src/app/ng2/components/properties-table/properties-value-inner-table/properties-value-inner-table.component.ts b/catalog-ui/src/app/ng2/components/properties-table/properties-value-inner-table/properties-value-inner-table.component.ts new file mode 100644 index 0000000000..7d0b219ffe --- /dev/null +++ b/catalog-ui/src/app/ng2/components/properties-table/properties-value-inner-table/properties-value-inner-table.component.ts @@ -0,0 +1,37 @@ +/** + * Created by rc2122 on 4/20/2017. + */ +import {Component, Input, Output, EventEmitter} from "@angular/core"; +import {PropertyFEModel} from "app/models"; +import {PropertiesService} from "../../../services/properties.service"; + +@Component({ + selector: 'properties-value-inner-table', + templateUrl: './properties-value-inner-table.component.html', + styleUrls: ['./properties-value-inner-table.component.less'] +}) +export class PropertiesValueInnerTableComponent { + + @Input() property: PropertyFEModel; + @Input() selectedPropertyId: string; + @Input() propertyNameSearchText:string; + + @Output() selectChildProperty: EventEmitter = new EventEmitter(); + @Output() valueChanged: EventEmitter = new EventEmitter(); + + constructor ( private propertiesService:PropertiesService){ + } + + + onChildPropertySelected = (property) => { + this.selectChildProperty.emit(property); + }; + + propValueChanged = () => { + this.valueChanged.emit(this.property); + }; + + putDefaultValueInEmptyChildProperty = (childProp:PropertyFEModel):void => { + this.property.valueObjectRef[childProp.name] = this.property.valueObjectRef[childProp.name] || childProp.defaultValue; + } +} diff --git a/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.html b/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.html new file mode 100644 index 0000000000..1fbf45e39f --- /dev/null +++ b/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.html @@ -0,0 +1,12 @@ + diff --git a/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.less b/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.less new file mode 100644 index 0000000000..1ff496f840 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.less @@ -0,0 +1,11 @@ +.tooltip-inner { + word-wrap: break-word; + max-width: 300px; +} + +.tooltip.bottom .tooltip-arrow { + border-bottom-color: #000 !important; +} + + + diff --git a/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.ts b/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.ts new file mode 100644 index 0000000000..6e3e8065bb --- /dev/null +++ b/catalog-ui/src/app/ng2/components/tooltip/tooltip-content.component.ts @@ -0,0 +1,195 @@ +import {Component, AfterViewInit, Input, ElementRef, ChangeDetectorRef} from "@angular/core"; + +@Component +({ + selector: "tooltip-content", + templateUrl: "./tooltip-content.component.html", + styleUrls: ["./tooltip-content.component.less"] +}) + +export class TooltipContentComponent implements AfterViewInit { + + // ------------------------------------------------------------------------- + // Inputs / Outputs + // ------------------------------------------------------------------------- + + @Input() hostElement: HTMLElement; + @Input() content: string; + @Input() placement: "top"|"bottom"|"left"|"right" = "bottom"; + @Input() animation: boolean = true; + + // ------------------------------------------------------------------------- + // Properties + // ------------------------------------------------------------------------- + + top: number = -100000; + left: number = -100000; + isIn: boolean = false; + isFade: boolean = false; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + constructor(private element: ElementRef, + private cdr: ChangeDetectorRef) { + } + + // ------------------------------------------------------------------------- + // Lifecycle callbacks + // ------------------------------------------------------------------------- + + ngAfterViewInit(): void { + this.show(); + this.cdr.detectChanges(); + } + + // ------------------------------------------------------------------------- + // Public Methods + // ------------------------------------------------------------------------- + + show(): void { + if(!this.hostElement) { + return; + } + + const position = this.positionElement(this.hostElement, this.element.nativeElement.children[0], this.placement); + this.top = position.top; + this.left = position.left; + this.isIn = true; + if (this.animation) { + this.isFade = true; + } + } + + hide(): void { + this.top = -100000; + this.left = -100000; + this.isIn = true; + if(this.animation) { + this.isFade = false; + } + } + + // ------------------------------------------------------------------------- + // Private Methods + // ------------------------------------------------------------------------- + + private positionElement(hostElem: HTMLElement, targetElem: HTMLElement, positionStr: string, appendToBody: boolean = false): {top: number, left: number} { + let positionStrParts = positionStr.split("-"); + let pos0 = positionStrParts[0]; + let pos1 = positionStrParts[1] || "center"; + let hostElemPosition = appendToBody ? this.offset(hostElem) : this.position(hostElem); + let targetElemWidth = targetElem.offsetWidth; + let targetElemHeight = targetElem.offsetHeight; + let shiftWidth: any = { + center(): number { + return hostElemPosition.left + hostElemPosition.width / 2 - targetElemWidth / 2; + }, + left(): number { + return hostElemPosition.left; + }, + right(): number { + return hostElemPosition.left + hostElemPosition.width; + } + }; + + let shiftHeight: any = { + center: function (): number { + return hostElemPosition.top + hostElemPosition.height / 2 - targetElemHeight / 2; + }, + top: function (): number { + return hostElemPosition.top; + }, + bottom: function (): number { + return hostElemPosition.top + hostElemPosition.height; + } + } + + let targetElemPosition: {top: number, left: number}; + + switch (pos0) { + case "right": + targetElemPosition = { + top: shiftHeight[pos1](), + left: shiftWidth[pos0]() + }; + break; + + case "left": + targetElemPosition = { + top: shiftHeight[pos1](), + left: hostElemPosition.left - targetElemWidth + }; + break; + + case "bottom": + targetElemPosition = { + top: shiftHeight[pos0](), + left: shiftWidth[pos1]() + }; + break; + + default: + targetElemPosition = { + top: hostElemPosition.top - targetElemHeight, + left: shiftWidth[pos1]() + }; + break; + } + + return targetElemPosition; + } + + + private position(nativeElem: HTMLElement): {width: number, height: number, top: number, left: number} { + let offsetParentCBR = {top: 0, left: 0}; + const elemBCR = this.offset(nativeElem); + const offsetParentElem = this.parentOffsetElem(nativeElem); + if(offsetParentElem !== window.document) { + offsetParentCBR = this.offset(offsetParentElem); + offsetParentCBR.top += offsetParentElem.clientTop - offsetParentElem.scrollTop; + offsetParentCBR.left += offsetParentElem.clientLeft - offsetParentElem.scrollTop; + } + + const boundingClientRect = nativeElem.getBoundingClientRect(); + + return { + width: boundingClientRect.width || nativeElem.offsetWidth, + height: boundingClientRect.height || nativeElem.offsetHeight, + top: elemBCR.top - offsetParentCBR.top, + left: elemBCR.left - offsetParentCBR.left + }; + } + + private offset(nativeElem:any): {width: number, height: number, top: number, left: number} { + const boundingClientRect = nativeElem.getBoundingClientRect(); + return { + width: boundingClientRect.width || nativeElem.offsetWidth, + height: boundingClientRect.height || nativeElem.offsetHeight, + top: boundingClientRect.top + (window.pageYOffset || window.document.documentElement.scrollTop), + left: boundingClientRect.left + (window.pageXOffset || window.document.documentElement.scrollLeft) + }; + } + + private getStyle(nativeElem: HTMLElement, cssProperty: string): string { + if(window.getComputedStyle) { + return (window.getComputedStyle(nativeElem) as any)[cssProperty]; + } + + return (nativeElem.style as any)[cssProperty]; + } + + private isStaticPositioned(nativeElem: HTMLElement): boolean { + return (this.getStyle(nativeElem, "position") || "static") === "static"; + } + + private parentOffsetElem(nativeElem: HTMLElement): any { + let offsetParent: any = nativeElem.offsetParent || window.document; + while (offsetParent && offsetParent !== window.document && this.isStaticPositioned(offsetParent)) { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || window.document; + } +} diff --git a/catalog-ui/src/app/ng2/components/tooltip/tooltip.component.ts b/catalog-ui/src/app/ng2/components/tooltip/tooltip.component.ts new file mode 100644 index 0000000000..e98b69003e --- /dev/null +++ b/catalog-ui/src/app/ng2/components/tooltip/tooltip.component.ts @@ -0,0 +1,81 @@ +import { + Directive, ComponentRef, ViewContainerRef, ComponentFactoryResolver, Input, HostListener +} from "@angular/core"; +import {TooltipContentComponent} from "./tooltip-content.component"; + +@Directive ({ + selector: "[tooltip]" +}) +export class TooltipComponent { + + // ------------------------------------------------------------------------- + // Properties + // ------------------------------------------------------------------------- + + private tooltip: ComponentRef; + private visible: boolean; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + constructor(private viewContainerRef: ViewContainerRef, + private resolver: ComponentFactoryResolver) { + } + + // ------------------------------------------------------------------------- + // Inputs / Outputs + // ------------------------------------------------------------------------- + + @Input("tooltip") content: string|TooltipContentComponent; + @Input() tooltipDisabled: boolean; + @Input() tooltipAnimation: boolean = true; + @Input() tooltipPlacement: "top"|"bottom"|"left"|"right" = "bottom"; + + // ------------------------------------------------------------------------- + // Public Methods + // ------------------------------------------------------------------------- + + @HostListener("mouseenter") + show(): void { + if(this.tooltipDisabled || this.visible || this.content === "") { + return; + } + + this.visible = true; + if (typeof this.content === "string") { + const factory = this.resolver.resolveComponentFactory(TooltipContentComponent); + if (!this.visible) { + return; + } + + this.tooltip = this.viewContainerRef.createComponent(factory); + this.tooltip.instance.hostElement = this.viewContainerRef.element.nativeElement; + this.tooltip.instance.content = this.content as string; + this.tooltip.instance.placement = this.tooltipPlacement; + this.tooltip.instance.animation = this.tooltipAnimation; + } else { + const tooltip = this.content as TooltipContentComponent; + tooltip.hostElement = this.viewContainerRef.element.nativeElement; + tooltip.placement = this.tooltipPlacement; + tooltip.animation = this.tooltipAnimation; + tooltip.show(); + } + } + + @HostListener("mouseleave") + hide(): void { + if (!this.visible) { + return; + } + + this.visible = false; + if (this.tooltip) { + this.tooltip.destroy(); + } + if (this.content instanceof TooltipContentComponent) { + (this.content as TooltipContentComponent).hide(); + } + } +} + diff --git a/catalog-ui/src/app/ng2/components/tooltip/tooltip.module.ts b/catalog-ui/src/app/ng2/components/tooltip/tooltip.module.ts new file mode 100644 index 0000000000..69976da6af --- /dev/null +++ b/catalog-ui/src/app/ng2/components/tooltip/tooltip.module.ts @@ -0,0 +1,25 @@ +import {NgModule} from "@angular/core"; +import {TooltipContentComponent} from "./tooltip-content.component"; +import {TooltipComponent} from "./tooltip.component"; +import {CommonModule} from "@angular/common"; + +@NgModule({ + declarations: [ + TooltipComponent, + TooltipContentComponent, + ], + imports: [ + CommonModule + ], + exports: [ + TooltipComponent, + TooltipContentComponent, + ], + entryComponents: [ + TooltipContentComponent + ], + providers: [] +}) +export class TooltipModule { + +} -- cgit 1.2.3-korg