From ffd70b436f496416535984d6cfe71e10757ec254 Mon Sep 17 00:00:00 2001 From: Tomasz Golabek Date: Tue, 9 Apr 2019 12:39:08 +0200 Subject: Dynamic columns in GAB table Possibility to add and remove new columns in GAB table. Currently additional columns are not stored anywhere. Change-Id: Idc204ef3da5be8f9509289461165e22d4c0068bf Issue-ID: SDC-2229 Signed-off-by: Tomasz Golabek --- catalog-ui/src/app/modules/directive-module.ts | 8 - catalog-ui/src/app/modules/view-model-module.ts | 1 - ...artifact-browser-column-provider.component.html | 35 ++++ ...artifact-browser-column-provider.component.less | 92 +++++++++ ...c-artifact-browser-column-provider.component.ts | 94 +++++++++ .../generic-artifact-browser.component.html | 73 ++++--- .../generic-artifact-browser.component.less | 38 +++- .../generic-artifact-browser.component.ts | 213 ++++++++++++--------- .../generic-artifact-browser.module.ts | 9 +- .../deployment-artifacts-view-model.ts | 7 +- 10 files changed, 437 insertions(+), 133 deletions(-) create mode 100644 catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.html create mode 100644 catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.less create mode 100644 catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.ts (limited to 'catalog-ui/src') diff --git a/catalog-ui/src/app/modules/directive-module.ts b/catalog-ui/src/app/modules/directive-module.ts index 7e664437a0..126f99ff09 100644 --- a/catalog-ui/src/app/modules/directive-module.ts +++ b/catalog-ui/src/app/modules/directive-module.ts @@ -178,7 +178,6 @@ import { TopNavComponent } from "../ng2/components/layout/top-nav/top-nav.compon import { ZoneContainerComponent } from "../ng2/components/ui/canvas-zone/zone-container.component"; import { ZoneInstanceComponent } from "../ng2/components/ui/canvas-zone/zone-instance/zone-instance.component"; import { CompositionPanelComponent } from 'app/ng2/pages/composition/panel/panel.component'; -import { CompositionPanelHeaderComponent } from 'app/ng2/pages/composition/panel/panel-header/panel-header.component'; import { PropertiesAssignmentComponent } from "../ng2/pages/properties-assignment/properties-assignment.page.component"; import { SearchWithAutoCompleteComponent } from "../ng2/components/ui/search-with-autocomplete/search-with-autocomplete.component"; import { PalettePopupPanelComponent } from "../ng2/components/ui/palette-popup-panel/palette-popup-panel.component"; @@ -190,7 +189,6 @@ import { MultilineEllipsisComponent } from "../ng2/shared/multiline-ellipsis/mul import { InterfaceOperationComponent } from '../ng2/pages/interface-operation/interface-operation.page.component'; import { PluginFrameComponent } from "../ng2/components/ui/plugin/plugin-frame.component"; import { TileComponent } from "../ng2/components/ui/tile/tile.component"; -import {GenericArtifactBrowserComponent} from "../ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component"; directiveModule.directive('menuListNg2', downgradeComponent({ @@ -204,12 +202,6 @@ directiveModule.directive('topNav', downgradeComponent({ outputs: ['searchTermChange'] }) as ng.IDirectiveFactory); -directiveModule.directive('gab', downgradeComponent({ - component: GenericArtifactBrowserComponent, - inputs: ['pathsandnames', 'artifactid', 'resourceid'], - outputs: [] -}) as ng.IDirectiveFactory); - directiveModule.directive('ng2ZoneContainer', downgradeComponent({ component: ZoneContainerComponent, inputs: ['title', 'count', 'type', 'visible', 'minimized'], diff --git a/catalog-ui/src/app/modules/view-model-module.ts b/catalog-ui/src/app/modules/view-model-module.ts index ce7d079329..c633201673 100644 --- a/catalog-ui/src/app/modules/view-model-module.ts +++ b/catalog-ui/src/app/modules/view-model-module.ts @@ -73,7 +73,6 @@ import {DeploymentViewModel} from "../view-models/workspace/tabs/deployment/depl import {ReqAndCapabilitiesViewModel} from "../view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model"; import {InputFormViewModel} from "../view-models/forms/input-form/input-form-view-modal"; import {HierarchyViewModel} from "../view-models/tabs/hierarchy/hierarchy-view-model"; -import {downgradeComponent} from "@angular/upgrade/static"; import {ConformanceLevelModalViewModel} from "../view-models/modals/conformance-level-modal/conformance-level-modal-view-model"; import {PluginsTabViewModel} from "../view-models/plugins/plugins-tab-view-model"; import {PluginsContextViewModel} from "../view-models/workspace/tabs/plugins/plugins-context-view-model"; diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.html b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.html new file mode 100644 index 0000000000..d5fa96a4a3 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.html @@ -0,0 +1,35 @@ + + +
+
+ + + + + + + + +
+
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.less b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.less new file mode 100644 index 0000000000..07241a8aff --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.less @@ -0,0 +1,92 @@ +.adaptive_placeholder(@height, @radius, @margin: 0em, @border: 1px) { + @borders: (@border * 2); + box-sizing: border-box; + width: 80%; + height: ~"calc(@{height} + @{borders})"; + margin: @margin; + padding: @margin; + border: @border solid #00bafa; + border-radius: @radius; + background: #fff; + resize: none; + outline: none; + &[required] { + &:focus { + border-color: #00bafa; + + label { + &[placeholder] { + &:before { + margin: 0; + color: #00bafa; + } + } + } + } + &:focus, + &:valid { + + label { + &[placeholder] { + &:before { + transform: translate(0, (-1em)) scale(.9, .9); + } + } + } + } + &:invalid { + + label { + &[placeholder] { + &[alt] { + &:before { + content: attr(alt); + } + } + } + } + } + + label { + &[placeholder] { + display: block; + pointer-events: none; + line-height: @margin * 1.25; + margin-top: ~"calc(-1em - @{borders})"; + margin-bottom: ~"calc((@{height} - @{margin}) + @{borders})"; + &:before { + content: attr(placeholder); + display: inline-block; + margin: 0 ~"calc(@{margin} + @{borders})"; + padding: 0 2px; + color: #00bafa; + font-size: small; + white-space: nowrap; + transition: .3s ease-in-out; + + background-image: linear-gradient(to bottom, #fff, #fff); + background-size: 100% 5px; + background-repeat: no-repeat; + background-position: center; + } + } + } + } +} + +.gab-column-provider-form { + margin: 5px +} + +.gab-column-provider-input { + @height: 2em; + &[type="text"] { + .adaptive_placeholder(@height, (@height / 2)); + } +} + +.gab-column-provider-input.ng-valid[required], .gab-column-provider-input.ng-valid.required { + border-left: 5px solid #42bb48; + border-right: 5px solid #42bb48; +} + +.gab-column-provider-input.ng-invalid:not(form) { + border-left: 5px solid #ff0511; + border-right: 5px solid #ff0511; +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.ts b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.ts new file mode 100644 index 0000000000..570f428b04 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.ts @@ -0,0 +1,94 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2019 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation} from "@angular/core"; +import {PathsAndNamesDefinition} from "../../../../models/paths-and-names"; + +@Component({ + selector: 'gab-column-provider', + templateUrl: './generic-artifact-browser-column-provider.component.html', + styleUrls: ['./generic-artifact-browser-column-provider.component.less'], + encapsulation: ViewEncapsulation.None +}) +export class GenericArtifactBrowserColumnProviderComponent { + @Input() + pathsAndNames: PathsAndNamesDefinition[]; + + @Output() + onCancel = new EventEmitter(); + @Output() + onSave = new EventEmitter(); + + @ViewChild('generalForm') generalForm; + name: string; + path: string; + + constructor() {} + + checkNameDuplications(event) { + const tmp = event.target.value; + if (tmp && !this.columnsContainsName(tmp)) { + this.name = tmp; + this.generalForm.form.controls['name'].setErrors(null); + } else { + this.generalForm.form.controls['name'].setErrors({incorrect: true}); + } + } + + checkPathDuplications(event) { + const tmp = event.target.value; + if (tmp && !this.columnsContainsPath(tmp)) { + this.path = tmp; + this.generalForm.form.controls['path'].setErrors(null); + } else { + this.generalForm.form.controls['path'].setErrors({incorrect: true}); + } + } + + cancelAddingNewColumn() { + this.onCancel.emit(); + } + + saveNewColumn() { + this.onSave.emit(); + } + + addColumn() { + this.updateColumnFilter(this.name, this.path); + this.saveNewColumn(); + } + + private updateColumnFilter = (name: string, prop: string): void => { + this.pathsAndNames.push(new PathsAndNamesDefinition(prop, name)); + this.generalForm.form.controls['name'].setValue(""); + this.generalForm.form.controls['path'].setValue(""); + } + + private columnsContainsName = (name: string): boolean => { + const columnDefinitions = this.pathsAndNames.filter(column => column.friendlyName.toLowerCase() === name.toLowerCase()); + return columnDefinitions.length > 0; + } + + private columnsContainsPath = (path: string): boolean => { + const columnDefinitions = this.pathsAndNames.filter(column => column.path.toLowerCase() === path.toLowerCase()); + return columnDefinitions.length > 0; + } + +} diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.html b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.html index 1abd68db50..ad1ebcb094 100644 --- a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.html +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.html @@ -15,36 +15,51 @@ -->
- - - + - + + + + + + + + + +
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.less b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.less index 57da199ef2..b237a123b3 100644 --- a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.less +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.less @@ -18,7 +18,9 @@ border-bottom: 6px solid white; .selectable(); - + :last-of-type { + background-color: white; + } span { display: inline-block; line-height: 15pt; @@ -29,6 +31,31 @@ } } +.datatable-column-span { + height: 10px; +} + +.add-btn-div { + width: 45px; +} + +.datattable-input-filter{ + height: 23px; + margin: 2px auto; + border: none; + border-bottom: solid 3px #009fdb; + border-radius: 15px; + width: 75% !important; + padding: 0 15px 0 15px !important; + color: #009fdb; + outline: none; +} + +.datattable-input-filter::placeholder { + color: #009fdb; + font-style: italic; +} + .datatable-row-group { padding-bottom: 6px; padding-top: 6px; @@ -45,8 +72,10 @@ } } -.datatable-header span.datatable-header-cell-label { - .break-all-words(); +.datatable-header { + span.datatable-header-cell-label { + .break-all-words(); + } } .datatable-input-filter{ @@ -68,6 +97,9 @@ font-weight: bold; font-size: 14px; text-align: center; + div { + display: inline-block; + } } .break-all-words { diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.ts b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.ts index 695d782a15..7e704e1927 100644 --- a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.ts @@ -25,49 +25,85 @@ import {PathsAndNamesDefinition} from "../../../../models/paths-and-names"; const COLUMN_PREFIX: string = 'col'; @Component({ - selector: 'gab', - templateUrl: './generic-artifact-browser.component.html', - styleUrls:['./generic-artifact-browser.component.less'], - encapsulation: ViewEncapsulation.None + selector: 'gab', + templateUrl: './generic-artifact-browser.component.html', + styleUrls: ['./generic-artifact-browser.component.less'], + encapsulation: ViewEncapsulation.None }) export class GenericArtifactBrowserComponent { - @Input() - pathsandnames: PathsAndNamesDefinition[]; - @Input() - artifactid: string; - @Input() - resourceid: string; - - columns: ColumnDefinition[]; - rows: any[]; - originRows: any[]; - selectedRows: any[]; - isLoading: boolean; - ready: boolean; - columnsFilters: Map; - - constructor(private gabService: GabService) { - } + @Input() + pathsandnames: PathsAndNamesDefinition[]; + @Input() + artifactid: string; + @Input() + resourceid: string; + + columns: ColumnDefinition[]; + originColumns: ColumnDefinition[]; + rows: any[]; + originRows: any[]; + isLoading: boolean; + ready: boolean; + columnsFilters: Map; + addNewColumn: boolean; + + constructor(private gabService: GabService) { + } - ngOnInit() { - this.ready = false; - this.isLoading = true; - this.columns = []; - this.columnsFilters = new Map(); - let paths: string[] = this.pathsandnames.map(item => item.path); - this.gabService.getArtifact(this.artifactid, this.resourceid, paths) - .subscribe( - response => { - let typedServerResponse:IServerResponse = response.json(); - this.normalizeDataForNgxDatatable(typedServerResponse.data); - }, - err => console.log(err), - () => { - this.ready = true; - this.isLoading = false; - } - ); - } + ngOnInit() { + this.ready = false; + this.isLoading = true; + this.columns = []; + this.loadArtifacts(); + } + + loadArtifacts() { + this.addNewColumn = false; + this.columnsFilters = new Map(); + let paths: string[] = this.pathsandnames.map(item => item.path); + this.gabService.getArtifact(this.artifactid, this.resourceid, paths) + .subscribe( + response => { + let typedServerResponse: IServerResponse = response.json() as IServerResponse; + this.normalizeDataForNgxDatatable(typedServerResponse.data); + }, + () => { + this.ready = false; + this.isLoading = false; + }, + () => { + this.ready = true; + this.isLoading = false; + } + ); + } + + refresh() { + this.loadArtifacts(); + } + + canBeDeleted(name: string){ + return this.originColumns.filter(function(column){ + return column.name === name; + }).length === 0; + } + + deleteColumn(col: ColumnDefinition) { + this.pathsandnames = this.pathsandnames.filter(function(pathandname){ + return pathandname.friendlyName != col.name; + }); + this.columns = this.columns.filter(function(column){ + return column != col; + }) + } + + hideAddNewColumn() { + this.addNewColumn = false; + } + + showAddNewColumn() { + this.addNewColumn = true; + } updateColumnFilter(event, column) { const val = event.target.value.toLowerCase(); @@ -83,68 +119,73 @@ export class GenericArtifactBrowserComponent { } private updateSingleColumnFilter(value, column, rows) { - return rows.filter(function(obj) { + return rows.filter(function (obj) { const row = obj[column]; return row !== undefined && row.toLowerCase().indexOf(value) !== -1 || !value; }); } - private normalizeDataForNgxDatatable(data: [{ [key: string]: string }]) { - let result: NormalizationResult = this.getNormalizationResult(data, this.pathsandnames); - this.rows = result.rows; - this.originRows = result.rows; - this.columns = result.columns; + private normalizeDataForNgxDatatable(data: [{ [key: string]: string }]) { + let result: NormalizationResult = this.getNormalizationResult(data, this.pathsandnames); + this.rows = result.rows; + this.originRows = result.rows; + this.columns = result.columns; + if (!this.originColumns){ + this.originColumns = [...result.columns]; } + } - private getNormalizationResult(data: [{ [key: string]: string }], - pathsAndNames: PathsAndNamesDefinition[]): NormalizationResult { - //Prepare column names and column data property names - let mappingsPathToProp = new Map(); - let columnsDefinitions = this.normalizeColumns(pathsAndNames, mappingsPathToProp); + private getNormalizationResult(data: [{ [key: string]: string }], + pathsAndNames: PathsAndNamesDefinition[]): NormalizationResult { + //Prepare column names and column data property names + let mappingsPathToProp = new Map(); + let columnsDefinitions = this.normalizeColumns(pathsAndNames, mappingsPathToProp); - //Convert rows from { "string": "string" } to { prop : "string" } format - //This is required by NgxDatatable component - let arrayOfRows = this.normalizeRows(data, mappingsPathToProp); + //Convert rows from { "string": "string" } to { prop : "string" } format + //This is required by NgxDatatable component + let arrayOfRows = this.normalizeRows(data, mappingsPathToProp); - return new NormalizationResult(arrayOfRows, columnsDefinitions); - } + return new NormalizationResult(arrayOfRows, columnsDefinitions); + } - private normalizeColumns(pathsAndNames: PathsAndNamesDefinition[], mappingsPathToProp: Map) { - let columnsDefinitions: ColumnDefinition[] = []; - let index: number = 1; - - pathsAndNames.forEach(function (col) { - let columnDataPropertyName: string = COLUMN_PREFIX + index; - mappingsPathToProp.set(col.path, columnDataPropertyName); - let cell: ColumnDefinition = new ColumnDefinition(col.friendlyName, columnDataPropertyName); - columnsDefinitions.push(cell); - index += 1; - }); - return columnsDefinitions; - } + private normalizeColumns(pathsAndNames: PathsAndNamesDefinition[], mappingsPathToProp: Map) { + let columnsDefinitions: ColumnDefinition[] = []; + let index: number = 1; - private normalizeRows(data: [{ [key: string]: string }], mappingsPathToProp: Map) { - let arrayOfRows = []; - data.forEach(function (col) { - let row = {}; - for (let key in col) { - if (col.hasOwnProperty(key)) { - let columnNameAsProp = mappingsPathToProp.get(key); - row[columnNameAsProp] = col[key]; - } - } - arrayOfRows.push(row); - }); - - return arrayOfRows; - } + pathsAndNames.forEach(function (col) { + let columnDataPropertyName: string = COLUMN_PREFIX + index; + mappingsPathToProp.set(col.path, columnDataPropertyName); + let cell: ColumnDefinition = new ColumnDefinition(col.friendlyName, columnDataPropertyName); + columnsDefinitions.push(cell); + index += 1; + }); + return columnsDefinitions; + } + + private normalizeRows(data: [{ [key: string]: string }], mappingsPathToProp: Map) { + let arrayOfRows = []; + data.forEach(function (col) { + let row = {}; + for (let key in col) { + if (col.hasOwnProperty(key)) { + let columnNameAsProp = mappingsPathToProp.get(key); + row[columnNameAsProp] = col[key]; + } + } + arrayOfRows.push(row); + }); + + return arrayOfRows; + } } class NormalizationResult { - constructor(public rows: any[], public columns: ColumnDefinition[]) {} + constructor(public rows: any[], public columns: ColumnDefinition[]) { + } } export class ColumnDefinition { - constructor(public name: string, public prop: string) {} + constructor(public name: string, public prop: string) { + } } diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.module.ts b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.module.ts index afb20472f1..e3ccf3027b 100644 --- a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.module.ts +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.module.ts @@ -22,17 +22,22 @@ import {GenericArtifactBrowserComponent} from "./generic-artifact-browser.compon import {NgxDatatableModule} from "@swimlane/ngx-datatable"; import {GabService} from "../../../services/gab.service"; import {BrowserModule} from "@angular/platform-browser"; +import {GenericArtifactBrowserColumnProviderComponent} from "./generic-artifact-browser-column-provider.component"; +import {FormsModule} from "@angular/forms"; @NgModule({ declarations: [ - GenericArtifactBrowserComponent + GenericArtifactBrowserComponent, + GenericArtifactBrowserColumnProviderComponent ], imports: [ BrowserModule, + FormsModule, NgxDatatableModule ], entryComponents: [ //need to add anything that will be dynamically created - GenericArtifactBrowserComponent + GenericArtifactBrowserComponent, + GenericArtifactBrowserColumnProviderComponent ], exports: [], providers: [GabService] diff --git a/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts index 5e78301287..5ada3d0a69 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts @@ -289,12 +289,11 @@ export class DeploymentArtifactsViewModel { title: title, type: 'custom', buttons: [{ - id: 'okButton', - text: 'OK', + id: 'closeGABButton', + text: 'Close', size: "'x-small'", closeModal: true - }, - {text: "Cancel", size: "'x-small'", closeModal: true}] + }] }; const uiConfiguration: any = this.cacheService.get('UIConfiguration'); -- cgit 1.2.3-korg