aboutsummaryrefslogtreecommitdiffstats
path: root/catalog-ui/src/app
diff options
context:
space:
mode:
authorMichael Lando <ml636r@att.com>2017-07-18 20:46:42 +0300
committerMichael Lando <ml636r@att.com>2017-07-18 20:46:42 +0300
commit39a4e0cb1b805470ad85ed4cf4fdeb69610ae98c (patch)
tree9faa499670544cce1ec5510c242ad984871ca32d /catalog-ui/src/app
parentb8e2faf476202b6ffe61bc3a9a37df1304881d40 (diff)
[SDC] rebase 1710
Change-Id: I07fced02f40a57700d9d35ed3ba498bca351fb13 Signed-off-by: Michael Lando <ml636r@att.com>
Diffstat (limited to 'catalog-ui/src/app')
-rw-r--r--catalog-ui/src/app/app.ts17
-rw-r--r--catalog-ui/src/app/directives/graphs-v2/common/style/component-instances-nodes-style.ts4
-rw-r--r--catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.directive.ts60
-rw-r--r--catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.html7
-rw-r--r--catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts8
-rw-r--r--catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts15
-rw-r--r--catalog-ui/src/app/models/components/component.ts21
-rw-r--r--catalog-ui/src/app/models/components/resource.ts23
-rw-r--r--catalog-ui/src/app/ng2/app.module.ts8
-rw-r--r--catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.less2
-rw-r--r--catalog-ui/src/app/ng2/components/properties-table/properties-table.component.less6
-rw-r--r--catalog-ui/src/app/ng2/shared/search-bar/search-bar.component.html5
-rw-r--r--catalog-ui/src/app/ng2/shared/search-bar/search-bar.component.less58
-rw-r--r--catalog-ui/src/app/ng2/shared/search-bar/search-bar.component.ts30
-rw-r--r--catalog-ui/src/app/ng2/shared/search-with-autocomplete/search-with-autocomplete.component.html6
-rw-r--r--catalog-ui/src/app/ng2/shared/search-with-autocomplete/search-with-autocomplete.component.less35
-rw-r--r--catalog-ui/src/app/ng2/shared/search-with-autocomplete/search-with-autocomplete.component.ts30
-rw-r--r--catalog-ui/src/app/view-models/shared/notification-custom-template.html14
-rw-r--r--catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts6
-rw-r--r--catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html4
-rw-r--r--catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less65
-rw-r--r--catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts18
-rw-r--r--catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html2
-rw-r--r--catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html4
-rw-r--r--catalog-ui/src/app/view-models/workspace/workspace.less3
25 files changed, 404 insertions, 47 deletions
diff --git a/catalog-ui/src/app/app.ts b/catalog-ui/src/app/app.ts
index 707d7e456e..805e5f0b5d 100644
--- a/catalog-ui/src/app/app.ts
+++ b/catalog-ui/src/app/app.ts
@@ -53,6 +53,7 @@ import {downgradeComponent} from "@angular/upgrade/static";
import {AppModule} from './ng2/app.module';
import {PropertiesAssignmentComponent} from "./ng2/pages/properties-assignment/properties-assignment.page.component";
+import { SearchWithAutoCompleteComponent } from "./ng2/shared/search-with-autocomplete/search-with-autocomplete.component";
import {Component} from "./models/components/component";
import {ComponentServiceNg2} from "./ng2/services/component-services/component.service";
import {ComponentMetadata} from "./models/component-metadata";
@@ -145,6 +146,13 @@ _.each(hostedApplications, (hostedApp)=> {
export const ng1appModule:ng.IModule = angular.module(moduleName, dependentModules);
angular.module('sdcApp').directive('propertiesAssignment', downgradeComponent({component: PropertiesAssignmentComponent}) as angular.IDirectiveFactory);
+angular.module('sdcApp').directive('ng2SearchWithAutocomplete',
+ downgradeComponent({
+ component: SearchWithAutoCompleteComponent,
+ inputs: ['searchPlaceholder', 'searchBarClass', 'autoCompleteValues'],
+ outputs: ['searchChanged', 'searchButtonClicked']
+ }) as angular.IDirectiveFactory);
+
ng1appModule.config([
'$stateProvider',
@@ -161,7 +169,7 @@ ng1appModule.config([
NotificationProvider:any):void => {
NotificationProvider.setOptions({
- delay: 10000,
+ delay: 5000,
startTop: 10,
startRight: 10,
closeOnClick: true,
@@ -170,6 +178,7 @@ ng1appModule.config([
positionX: 'right',
positionY: 'top'
});
+ NotificationProvider.options.templateUrl = 'notification-custom-template.html';
$translateProvider.useStaticFilesLoader({
prefix: pathPrefix + 'assets/languages/',
@@ -617,6 +626,7 @@ ng1appModule.run([
'LeftPaletteLoaderService',
'Sdc.Services.DataTypesService',
'AngularJSBridge',
+ '$templateCache',
($http:ng.IHttpService,
cacheService:CacheService,
cookieService:CookieService,
@@ -632,8 +642,9 @@ ng1appModule.run([
ecompHeaderService:EcompHeaderService,
LeftPaletteLoaderService:LeftPaletteLoaderService,
DataTypesService:DataTypesService,
- AngularJSBridge):void => {
-
+ AngularJSBridge,
+ $templateCache:ng.ITemplateCacheService):void => {
+ $templateCache.put('notification-custom-template.html', require('./view-models/shared/notification-custom-template.html'));
//handle cache data - version
let initAsdcVersion:Function = ():void => {
diff --git a/catalog-ui/src/app/directives/graphs-v2/common/style/component-instances-nodes-style.ts b/catalog-ui/src/app/directives/graphs-v2/common/style/component-instances-nodes-style.ts
index 0dcc93dfa7..64197594e2 100644
--- a/catalog-ui/src/app/directives/graphs-v2/common/style/component-instances-nodes-style.ts
+++ b/catalog-ui/src/app/directives/graphs-v2/common/style/component-instances-nodes-style.ts
@@ -231,7 +231,7 @@ export class ComponentInstanceNodesStyle {
public static getBasicNodeHanlde = () => {
return {
- positionX: "center",
+ positionX: "right",
positionY: "top",
offsetX: 15,
offsetY: -20,
@@ -248,7 +248,7 @@ export class ComponentInstanceNodesStyle {
public static getBasicSmallNodeHandle = () => {
return {
- positionX: "center",
+ positionX: "right",
positionY: "top",
offsetX: 3,
offsetY: -25,
diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.directive.ts b/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.directive.ts
index 651a4281fa..9aa7941272 100644
--- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.directive.ts
+++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.directive.ts
@@ -47,7 +47,8 @@ interface ICompositionGraphScope extends ng.IScope {
component:Component;
isLoading: boolean;
- isViewOnly:boolean;
+ isViewOnly: boolean;
+ withSidebar: boolean;
// Link menu - create link menu
relationMenuDirectiveObj:RelationMenuDirectiveObj;
isLinkMenuOpen:boolean;
@@ -65,6 +66,14 @@ interface ICompositionGraphScope extends ng.IScope {
//Links menus
deleteRelation(link:Cy.CollectionEdges):void;
hideRelationMenu();
+
+ //search,zoom in/out/all
+ componentInstanceNames: Array<string>; //id, name
+ zoom(zoomIn: boolean): void;
+ zoomAll(nodes?:Cy.CollectionNodes): void;
+ getAutoCompleteValues(searchTerm: string):void;
+ highlightSearchMatches(searchTerm: string): void;
+
/*//asset popover menu
assetPopoverObj:AssetPopoverObj;
assetPopoverOpen:boolean;
@@ -101,7 +110,8 @@ export class CompositionGraph implements ng.IDirective {
template = require('./composition-graph.html');
scope = {
component: '=',
- isViewOnly: '='
+ isViewOnly: '=',
+ withSidebar: '='
};
link = (scope:ICompositionGraphScope, el:JQuery) => {
@@ -147,7 +157,11 @@ export class CompositionGraph implements ng.IDirective {
this._cy = cytoscape({
container: graphEl,
style: ComponentInstanceNodesStyle.getCompositionGraphStyle(),
- zoomingEnabled: false,
+ zoomingEnabled: true,
+ maxZoom: 2.5,
+ minZoom: .1,
+ userZoomingEnabled: false,
+ userPanningEnabled: true,
selectionType: 'single',
boxSelectionEnabled: true,
autolock: isViewOnly,
@@ -270,6 +284,40 @@ export class CompositionGraph implements ng.IDirective {
this.loadGraphData(scope);
});
+ scope.zoom = (zoomIn: boolean):void => {
+ let currentZoom: number = this._cy.zoom();
+ if (zoomIn) {
+ this.GeneralGraphUtils.zoomGraphTo(this._cy, currentZoom + .1);
+ } else {
+ this.GeneralGraphUtils.zoomGraphTo(this._cy, currentZoom - .1);
+ }
+ }
+
+ //Zooms to fit all of the nodes in the collection passed in. If no nodes are passed in, will zoom to fit all nodes on graph
+ scope.zoomAll = (nodes?:Cy.CollectionNodes) => {
+ scope.withSidebar = false;
+ this._cy.animate({
+ fit: { eles: nodes, padding: 20 },
+ center: { eles: nodes }
+ }, { duration: 400 });
+ };
+
+ scope.getAutoCompleteValues = (searchTerm: string) => {
+ if (searchTerm.length > 1) { //US requirement: only display search results after 2nd letter typed.
+ let nodes: Cy.CollectionNodes = this.NodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
+ scope.componentInstanceNames = _.map(nodes, node => node.data('name'));
+ } else {
+ scope.componentInstanceNames = [];
+ }
+ };
+
+ scope.highlightSearchMatches = (searchTerm: string) => {
+ if (searchTerm === undefined) return; //dont zoom & highlight if click on Search initially (searchTerm will be undefined). However, allow highlights to be cleared after subsequent search (searchTerm will be "")
+
+ this.NodesGraphUtils.highlightMatchingNodesByName(this._cy, searchTerm);
+ let matchingNodes: Cy.CollectionNodes = this.NodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
+ scope.zoomAll(matchingNodes);
+ };
scope.createLinkFromMenu = (chosenMatch:MatchBase):void => {
scope.isLinkMenuOpen = false;
@@ -367,7 +415,7 @@ export class CompositionGraph implements ng.IDirective {
});*/
this._cy.on('handlemouseover', (event, payload) => {
- if (payload.node.grabbed() /* || this._cy.scratch('_edge_editation_highlights') === true*/) { //no need to add opacity while we are dragging and hovering othe nodes- or if opacity was already calculated for these nodes
+ if (payload.node.grabbed() || this._cy.scratch('_edge_editation_highlights') === true) { //no need to add opacity while we are dragging and hovering othe nodes- or if opacity was already calculated for these nodes
return;
}
let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
@@ -377,9 +425,9 @@ export class CompositionGraph implements ng.IDirective {
let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findByMatchingCapabilitiesToRequirements(payload.node.data().componentInstance, linkableNodes, nodesLinks);
this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy, payload.node.data());
- /*
+
this._cy.scratch()._edge_editation_highlights = true;
- scope.hideAssetPopover();*/
+ /*scope.hideAssetPopover();*/
});
this._cy.on('handlemouseout', () => {
diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.html b/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.html
index 1e69d3384a..248f19fb70 100644
--- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.html
+++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.html
@@ -20,4 +20,11 @@
</div>
+ <div class="w-sdc-search-menu" data-ng-class="{'with-sidebar': withSidebar}">
+ <ng2-search-with-autocomplete [search-placeholder]="'Type to search'" [auto-complete-values]="componentInstanceNames" (search-changed)="getAutoCompleteValues($event)" (search-button-clicked)="highlightSearchMatches($event)"
+ [search-bar-class]="'composition-search'"></ng2-search-with-autocomplete>
+ <div class="zoom-icons sprite-new canvas-fit-all" data-ng-click="zoomAll()"></div>
+ <div class="zoom-icons sprite-new zoom-plus" data-ng-click="zoom(true)"></div>
+ <div class="zoom-icons sprite-new zoom-minus" data-ng-click="zoom(false)"></div>
+ </div>
<!--<asset-popover ng-if="assetPopoverOpen" asset-popover-obj="assetPopoverObj" delete-asset="deleteNode(assetPopoverObj.nodeId)"></asset-popover>-->
diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts
index 608031430b..0ea38af825 100644
--- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts
+++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts
@@ -64,6 +64,14 @@ export class CompositionGraphGeneralUtils {
};
+ public zoomGraphTo = (cy:Cy.Instance, zoomLevel: number):void => {
+ let zy = cy.height() / 2;
+ let zx = cy.width() / 2;
+ cy.zoom({
+ level: zoomLevel,
+ renderedPosition: { x: zx, y: zy }
+ });
+ }
/**
* will return true/false if two nodes overlapping
*
diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts
index feb6ac9a54..449d551cc0 100644
--- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts
+++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts
@@ -48,6 +48,21 @@ export class CompositionGraphNodesUtils {
})
};
+
+ public highlightMatchingNodesByName = (cy: Cy.Instance, nameToMatch: string) => {
+
+ cy.batch(() => {
+ cy.nodes("[name !@^= '" + nameToMatch + "']").style({ 'background-image-opacity': 0.4 });
+ cy.nodes("[name @^= '" + nameToMatch + "']").style({ 'background-image-opacity': 1 });
+ })
+
+ }
+
+ //Returns all nodes whose name starts with searchTerm
+ public getMatchingNodesByName = (cy: Cy.Instance, nameToMatch: string): Cy.CollectionNodes => {
+ return cy.nodes("[name @^= '" + nameToMatch + "']");
+ };
+
/**
* Deletes component instances on server and then removes it from the graph as well
* @param cy
diff --git a/catalog-ui/src/app/models/components/component.ts b/catalog-ui/src/app/models/components/component.ts
index 9b2c942483..53e8f05cbe 100644
--- a/catalog-ui/src/app/models/components/component.ts
+++ b/catalog-ui/src/app/models/components/component.ts
@@ -923,16 +923,17 @@ export abstract class Component implements IComponent {
}
public toJSON = ():any => {
- this.componentService = undefined;
- this.filterTerm = undefined;
- this.iconSprite = undefined;
- this.mainCategory = undefined;
- this.subCategory = undefined;
- this.selectedInstance = undefined;
- this.showMenu = undefined;
- this.$q = undefined;
- this.selectedCategory = undefined;
- return this;
+ let temp = angular.copy(this);
+ temp.componentService = undefined;
+ temp.filterTerm = undefined;
+ temp.iconSprite = undefined;
+ temp.mainCategory = undefined;
+ temp.subCategory = undefined;
+ temp.selectedInstance = undefined;
+ temp.showMenu = undefined;
+ temp.$q = undefined;
+ temp.selectedCategory = undefined;
+ return temp;
};
}
diff --git a/catalog-ui/src/app/models/components/resource.ts b/catalog-ui/src/app/models/components/resource.ts
index 138b413028..cd839786c5 100644
--- a/catalog-ui/src/app/models/components/resource.ts
+++ b/catalog-ui/src/app/models/components/resource.ts
@@ -163,17 +163,18 @@ export class Resource extends Component {
};
public toJSON = ():any => {
- this.componentService = undefined;
- this.filterTerm = undefined;
- this.iconSprite = undefined;
- this.mainCategory = undefined;
- this.subCategory = undefined;
- this.selectedInstance = undefined;
- this.showMenu = undefined;
- this.$q = undefined;
- this.selectedCategory = undefined;
- this.importedFile = undefined;
- return this;
+ let temp = angular.copy(this);
+ temp.componentService = undefined;
+ temp.filterTerm = undefined;
+ temp.iconSprite = undefined;
+ temp.mainCategory = undefined;
+ temp.subCategory = undefined;
+ temp.selectedInstance = undefined;
+ temp.showMenu = undefined;
+ temp.$q = undefined;
+ temp.selectedCategory = undefined;
+ temp.importedFile = undefined;
+ return temp;
};
}
diff --git a/catalog-ui/src/app/ng2/app.module.ts b/catalog-ui/src/app/ng2/app.module.ts
index 09b40e920f..57adb8fd66 100644
--- a/catalog-ui/src/app/ng2/app.module.ts
+++ b/catalog-ui/src/app/ng2/app.module.ts
@@ -40,6 +40,8 @@ import {ComponentInstanceServiceNg2} from "./services/component-instance-service
import { InterceptorService } from 'ng2-interceptors';
import { XHRBackend, RequestOptions } from '@angular/http';
import {HttpInterceptor} from "./services/http.interceptor.service";
+import { SearchBarComponent } from './shared/search-bar/search-bar.component';
+import { SearchWithAutoCompleteComponent } from './shared/search-with-autocomplete/search-with-autocomplete.component';
export const upgradeAdapter = new UpgradeAdapter(forwardRef(() => AppModule));
@@ -60,7 +62,9 @@ export function interceptorFactory(xhrBackend: XHRBackend, requestOptions: Reque
@NgModule({
declarations: [
- AppComponent
+ AppComponent,
+ SearchBarComponent,
+ SearchWithAutoCompleteComponent
],
imports: [
BrowserModule,
@@ -70,7 +74,7 @@ export function interceptorFactory(xhrBackend: XHRBackend, requestOptions: Reque
PropertiesAssignmentModule
],
exports: [],
- entryComponents: [],
+ entryComponents: [SearchWithAutoCompleteComponent],
providers: [
DataTypesServiceProvider,
SharingServiceProvider,
diff --git a/catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.less b/catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.less
index 05378f0eb9..89c7287449 100644
--- a/catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.less
+++ b/catalog-ui/src/app/ng2/components/inputs-table/inputs-table.component.less
@@ -85,7 +85,7 @@
border-right:#d2d2d2 solid 1px;
}
&.col1 {
- flex: 0 0 300px;
+ flex: 1 0 200px;
max-width:300px;
display: flex;
justify-content: space-between;
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
index 3eb7e960ab..9ede84faff 100644
--- 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
@@ -91,7 +91,7 @@
border-right:#d2d2d2 solid 1px;
}
&.col1 {
- flex: 0 0 300px;
+ flex: 1 0 210px;
max-width:300px;
display: flex;
justify-content: space-between;
@@ -100,7 +100,7 @@
.property-name {
flex: 1;
display: flex;
- max-width: 270px;
+ max-width: 90%;
}
.property-description-icon {
@@ -123,7 +123,7 @@
}
&.valueCol {
- flex: 1 0 350px;
+ flex: 2 0 300px;
display: flex;
@media @smaller-screen { flex: 1 0 40%;}
}
diff --git a/catalog-ui/src/app/ng2/shared/search-bar/search-bar.component.html b/catalog-ui/src/app/ng2/shared/search-bar/search-bar.component.html
new file mode 100644
index 0000000000..36629594b0
--- /dev/null
+++ b/catalog-ui/src/app/ng2/shared/search-bar/search-bar.component.html
@@ -0,0 +1,5 @@
+<div class="search-bar-container {{class}}">
+ <input class="search-bar-input" type="text" [placeholder]="placeholder" [(ngModel)]="searchQuery" (ngModelChange)="searchQueryChange($event)"/>
+ <span class="clear-search-x" *ngIf="searchQuery" (click)="clearSearchQuery()">x</span>
+ <button class="search-bar-button" (click)="searchButtonClick()"></button>
+</div> \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/shared/search-bar/search-bar.component.less b/catalog-ui/src/app/ng2/shared/search-bar/search-bar.component.less
new file mode 100644
index 0000000000..cfeb8d3b01
--- /dev/null
+++ b/catalog-ui/src/app/ng2/shared/search-bar/search-bar.component.less
@@ -0,0 +1,58 @@
+.search-bar-container {
+ display:flex;
+ border-radius: 4px;
+ box-shadow: 0px 2px 3.88px 0.12px rgba(0, 0, 0, 0.29);
+
+ .search-bar-input {
+ border: 1px solid #cdcdcd;
+ border-radius: 4px;
+ border-right:none;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ outline:none;
+ padding:2px 50px 2px 10px;
+ color: #5a5a5a;
+ font-size: 1em;
+ font-style: italic;
+ }
+
+ .clear-search-x {
+ position:absolute;
+ right:40px;
+ top:5px;
+ padding: 0 5px;
+
+ &:hover {
+ border-radius:2px;
+ background-color: #ebebeb;
+ cursor:pointer;
+ }
+ }
+
+ .search-bar-button {
+ //background: url('../../../../assets/styles/svg/source/search-magnify.svg') no-repeat 50%;
+ background: url('../../../../assets/styles/images/sprites/sprite-global.png') no-repeat -206px -1275px;
+ background-color: rgba(234, 234, 234, 0.88);
+ width: 30px;
+ height: 30px;
+ padding: 0;
+ cursor:pointer;
+ border:solid 1px #cdcdcd;
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+
+ &:hover {
+ background-position:-126px -1275px;
+ }
+
+ &:active {
+ background-color: rgba(31, 171, 223, 0.88);
+ background-position:-45px -1275px;
+ border-left:none;
+ }
+ &:focus {
+ outline:none;
+ }
+
+ }
+}
diff --git a/catalog-ui/src/app/ng2/shared/search-bar/search-bar.component.ts b/catalog-ui/src/app/ng2/shared/search-bar/search-bar.component.ts
new file mode 100644
index 0000000000..2835d20ba4
--- /dev/null
+++ b/catalog-ui/src/app/ng2/shared/search-bar/search-bar.component.ts
@@ -0,0 +1,30 @@
+import { Component, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core';
+
+@Component({
+ selector: 'search-bar',
+ templateUrl: './search-bar.component.html',
+ styleUrls: ['./search-bar.component.less'],
+ encapsulation: ViewEncapsulation.None
+})
+export class SearchBarComponent {
+
+ @Input() placeholder: string;
+ @Input() class: string;
+ @Input() searchQuery: string;
+ @Output() searchChanged: EventEmitter<any> = new EventEmitter<any>();
+ @Output() searchButtonClicked: EventEmitter<string> = new EventEmitter<string>();
+
+ searchButtonClick = (): void => {
+ this.searchButtonClicked.emit(this.searchQuery);
+ }
+
+ searchQueryChange = ($event): void => {
+ this.searchChanged.emit($event);
+ }
+
+ private clearSearchQuery = (): void => {
+ this.searchQuery = "";
+ this.searchButtonClick();
+ }
+}
+
diff --git a/catalog-ui/src/app/ng2/shared/search-with-autocomplete/search-with-autocomplete.component.html b/catalog-ui/src/app/ng2/shared/search-with-autocomplete/search-with-autocomplete.component.html
new file mode 100644
index 0000000000..c9769ba5ae
--- /dev/null
+++ b/catalog-ui/src/app/ng2/shared/search-with-autocomplete/search-with-autocomplete.component.html
@@ -0,0 +1,6 @@
+<div class="search-with-autocomplete-container {{searchBarClass}}" [class.autocomplete-visible]="autoCompleteValues && autoCompleteValues.length" [class.active]="searchQuery && searchQuery.length">
+ <search-bar [placeholder]="searchPlaceholder" [searchQuery]="searchQuery" (searchButtonClicked)="updateSearch($event)" (searchChanged)="searchChange($event)"></search-bar>
+ <div class="autocomplete-results">
+ <div *ngFor="let item of autoCompleteValues" class="autocomplete-result-item" (click)="updateSearch(item)">{{item}}</div>
+ </div>
+</div> \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/shared/search-with-autocomplete/search-with-autocomplete.component.less b/catalog-ui/src/app/ng2/shared/search-with-autocomplete/search-with-autocomplete.component.less
new file mode 100644
index 0000000000..92b054cd85
--- /dev/null
+++ b/catalog-ui/src/app/ng2/shared/search-with-autocomplete/search-with-autocomplete.component.less
@@ -0,0 +1,35 @@
+
+.search-with-autocomplete-container{
+ &.autocomplete-visible {
+
+ .search-bar-input {
+ border-bottom-left-radius: 0;
+ }
+ .search-bar-button {
+ border-bottom-right-radius: 0;
+ }
+ .autocomplete-results {
+ border: solid 1px #d2d2d2;
+ border-top:none;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ background-color: #fff;
+ padding: 10px 20px;
+ width:100%;
+ position:absolute;
+ max-height: 200px;
+ overflow-y: scroll;
+ }
+
+ .autocomplete-result-item {
+ color:#5a5a5a;
+ padding: 5px 0;
+ cursor:pointer;
+
+ &:hover {
+ color: #999;
+ }
+ }
+ }
+}
+
diff --git a/catalog-ui/src/app/ng2/shared/search-with-autocomplete/search-with-autocomplete.component.ts b/catalog-ui/src/app/ng2/shared/search-with-autocomplete/search-with-autocomplete.component.ts
new file mode 100644
index 0000000000..ced056d1fc
--- /dev/null
+++ b/catalog-ui/src/app/ng2/shared/search-with-autocomplete/search-with-autocomplete.component.ts
@@ -0,0 +1,30 @@
+import { Component, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core';
+import { SearchBarComponent } from '../search-bar/search-bar.component';
+
+@Component({
+ selector: 'search-with-autocomplete',
+ templateUrl: './search-with-autocomplete.component.html',
+ styleUrls: ['./search-with-autocomplete.component.less'],
+ encapsulation: ViewEncapsulation.None
+})
+export class SearchWithAutoCompleteComponent {
+
+ @Input() searchPlaceholder: string;
+ @Input() searchBarClass: string;
+ @Input() searchQuery: string;
+ @Input() autoCompleteValues: Array<string>;
+ @Output() searchChanged: EventEmitter<any> = new EventEmitter<any>();
+ @Output() searchButtonClicked: EventEmitter<string> = new EventEmitter<string>();
+
+ searchChange = (searchTerm: string) => {
+ this.searchQuery = searchTerm;
+ this.searchChanged.emit(searchTerm);
+ }
+
+ updateSearch = (searchTerm: string) => {
+ this.searchQuery = searchTerm;
+ this.searchButtonClicked.emit(searchTerm);
+ this.autoCompleteValues = [];
+ }
+}
+
diff --git a/catalog-ui/src/app/view-models/shared/notification-custom-template.html b/catalog-ui/src/app/view-models/shared/notification-custom-template.html
new file mode 100644
index 0000000000..d8fdf135b9
--- /dev/null
+++ b/catalog-ui/src/app/view-models/shared/notification-custom-template.html
@@ -0,0 +1,14 @@
+<div class="ui-notification">
+ <div class="notification-container">
+ <div class="icon-container">
+ <div class="icon-circle">
+ <div class="icon sprite-new">
+ </div>
+ </div>
+ </div>
+ <div class="msg-content">
+ <h3 ng-show="title" ng-bind-html="title"></h3>
+ <div class="message" ng-bind-html="message"></div>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts
index fbd32cc967..0e5a5fcd6c 100644
--- a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts
+++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts
@@ -30,7 +30,8 @@ import {ResourceType} from "../../../../utils/constants";
export interface ICompositionViewModelScope extends IWorkspaceViewModelScope {
currentComponent:Component;
- selectedComponent:Component;
+ selectedComponent: Component;
+ componentInstanceNames: Array<string>;
isLoading:boolean;
graphApi:any;
sharingService:SharingService;
@@ -130,7 +131,6 @@ export class CompositionViewModel {
private openUpdateComponentInstanceNameModal = ():void => {
this.ModalsHandler.openUpdateComponentInstanceNameModal(this.$scope.currentComponent).then(()=> {
this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, this.$scope.currentComponent.selectedInstance);
-
});
};
@@ -225,7 +225,7 @@ export class CompositionViewModel {
this.$scope.openUpdateModal = ():void => {
this.openUpdateComponentInstanceNameModal();
};
-
+
this.$scope.deleteSelectedComponentInstance = ():void => {
let state = "deleteInstance";
let onOk = ():void => {
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html
index cef942e853..e05574e8c8 100644
--- a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html
+++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html
@@ -6,7 +6,7 @@
is-loading="isLoading"></palette>
<composition-graph component="currentComponent" data-tests-id="canvas"
- is-view-only="isViewOnly"></composition-graph>
+ is-view-only="isViewOnly" with-sidebar="displayDesignerRightSidebar"></composition-graph>
</div>
<div class="w-sdc-designer-sidebar-toggle" data-ng-class="{'active': displayDesignerRightSidebar}"
@@ -21,7 +21,7 @@
<div class="w-sdc-designer-sidebar-logo-ph">
<div class="large {{selectedComponent.iconSprite}} {{selectedComponent.icon}}">
<div ng-if="isComponentInstanceSelected()"
- data-ng-class="{'non-certified':'CERTIFIED' !== selectedComponent.lifecycleState, 'smaller-icon': selectedComponent.icon==='vl' || selectedComponent.icon==='cp'}"
+ data-ng-class="{'non-certified':'CERTIFIED' !== selectedComponent.lifecycleState}"
tooltips tooltip-side="top" tooltip-content="Not certified"></div>
</div>
</div>
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less
index 7a775bdb11..262dfd9be8 100644
--- a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less
+++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less
@@ -781,6 +781,71 @@
line-height: 18px;
}
+ //Canvas search menu
+ .w-sdc-search-menu {
+ position:absolute;
+ right: 18px;
+ top:53px;
+ transition: right 0.2s;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ margin-right:10px;
+
+ &.with-sidebar {
+ right:320px;
+ }
+
+ .search-with-autocomplete-container.composition-search {
+ margin-top: 12px;
+
+ .search-bar-input {
+ width: 250px;
+ padding:2px 50px 2px 10px;
+ transition:all 0.4s;
+ }
+ .clear-search-x {
+ top: 17px
+ }
+
+ &:not(:hover):not(.autocomplete-visible):not(.active){
+ border-radius: 0;
+ box-shadow:none;
+
+ .search-bar-input:not(:focus){
+ width: 0px;
+ padding:0;
+ border:none;
+ }
+ .clear-search-x {
+ display:none;
+ }
+ .search-bar-input:not(:focus) ~ .search-bar-button {
+ border-radius: 2px;
+ border:solid 1px #fff;
+ }
+ }
+ }
+
+ .zoom-icons {
+ border:solid 1px #fff;
+ border-radius: 2px;
+ box-shadow: 0px 2px 3.88px 0.12px rgba(0, 0, 0, 0.29);
+ background-color: rgba(234, 234, 234, 0.88);
+ background-repeat: no-repeat;
+ margin-top: 10px;
+
+ &:hover {
+ cursor:pointer;
+ }
+
+ &:active {
+ border:none;
+ background-color: rgba(31, 171, 223, 0.88);
+ }
+ }
+ }
+
// ---------------------------------------------------------------------------------------------------
// Canvas inline menu
// ---------------------------------------------------------------------------------------------------
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts
index 83e4653954..c4c63fae06 100644
--- a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts
+++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts
@@ -33,13 +33,14 @@ import {ICompositionViewModelScope} from "../../composition-view-model";
import {ArtifactsUtils, ModalsHandler, ArtifactGroupType} from "app/utils";
import {GRAPH_EVENTS} from "app/utils/constants";
import {EventListenerService} from "app/services/event-listener-service";
+import {Dictionary} from "../../../../../../utils/dictionary/dictionary";
export interface IArtifactsViewModelScope extends ICompositionViewModelScope {
artifacts:Array<ArtifactModel>;
artifactType:string;
downloadFile:IFileDownload;
isLoading:boolean;
-
+ displayDeleteButtonMap:Dictionary<string, boolean>;
getTitle():string;
addOrUpdate(artifact:ArtifactModel):void;
delete(artifact:ArtifactModel):void;
@@ -125,6 +126,10 @@ export class ResourceArtifactsViewModel {
}
}
this.$scope.artifacts = artifacts;
+ this.$scope.displayDeleteButtonMap = new Dictionary<string, boolean>();
+ _.forEach(this.$scope.artifacts, (artifact:ArtifactModel)=>{
+ this.$scope.displayDeleteButtonMap[artifact.artifactLabel] = this.displayDeleteButton(artifact);
+ });
this.$scope.isLoading = false;
};
@@ -229,6 +234,17 @@ export class ResourceArtifactsViewModel {
});
};
+ private displayDeleteButton = (artifact:ArtifactModel):boolean => {
+ if(!this.$scope.isViewMode() && artifact.esId){
+ if(this.$scope.isComponentInstanceSelected()){//is artifact of instance
+ return !this.$scope.selectedComponent.deploymentArtifacts || !this.$scope.selectedComponent.deploymentArtifacts[artifact.artifactLabel];//if the artifact is not from instance parent
+ }else{//is artifact of main component
+ return (!artifact.isHEAT() && !artifact.isThirdParty() && !this.$scope.isLicenseArtifact(artifact));
+ }
+ }
+ return false;
+};
+
private initScope = ():void => {
this.$scope.isLoading = false;
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html
index 8221c67eca..dfbd639eb4 100644
--- a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html
+++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html
@@ -36,7 +36,7 @@
<span class="i-sdc-designer-sidebar-section-content-item-artifact-details-desc-label" data-ng-show="artifact.description">Description:</span>{{artifact.description}}
</div>
</div>
- <button ng-if="!isViewMode() && artifact.esId && !isComponentInstanceSelected() && !artifact.isHEAT() && !artifact.isThirdParty() && !isLicenseArtifact(artifact)" class="i-sdc-designer-sidebar-section-content-item-button delete sprite e-sdc-small-icon-delete"
+ <button ng-if="displayDeleteButtonMap[artifact.artifactLabel]" class="i-sdc-designer-sidebar-section-content-item-button delete sprite e-sdc-small-icon-delete"
data-tests-id="delete_{{artifact.artifactDisplayName}}" data-ng-click="delete(artifact)" type="button"></button>
<button ng-if="!isViewMode() && artifact.isHEAT() && isComponentInstanceSelected() && artifact.heatParameters.length"
class="i-sdc-designer-sidebar-section-content-item-button attach sprite e-sdc-small-icon-pad"
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html
index 8607d65964..0418515789 100644
--- a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html
+++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html
@@ -23,9 +23,9 @@
<span class="i-sdc-designer-sidebar-section-content-item-label">Version:</span>
<span class="i-sdc-designer-sidebar-section-content-item-value"
- data-ng-if="!isComponentInstanceSelected() || selectedComponent.isVl()" data-tests-id="rightTab_version" data-ng-bind="selectedComponent.version"></span>
+ data-ng-if="!isComponentInstanceSelected()" data-tests-id="rightTab_version" data-ng-bind="selectedComponent.version"></span>
- <ng-form name="editForm" data-ng-if="isComponentInstanceSelected() && !selectedComponent.isVl()">
+ <ng-form name="editForm" data-ng-if="isComponentInstanceSelected()">
<select data-ng-model="editResourceVersion.changeVersion" name="changeVersion" data-tests-id="changeVersion" data-ng-disabled="$parent.isViewOnly"
class="i-sdc-designer-sidebar-section-content-item-value i-sdc-form-select"
data-ng-class="{'minor': (editResourceVersion.changeVersion)%1}"
diff --git a/catalog-ui/src/app/view-models/workspace/workspace.less b/catalog-ui/src/app/view-models/workspace/workspace.less
index b7331b5f93..d0799f4bac 100644
--- a/catalog-ui/src/app/view-models/workspace/workspace.less
+++ b/catalog-ui/src/app/view-models/workspace/workspace.less
@@ -170,6 +170,9 @@
line-height: 110px;
.f-type ._28;
}
+ &.composition .w-sdc-main-container-body-content {
+ height: calc(~'100% - @{action_nav_height}'); //composition is the only tab without a tab title. need to exclude from calculation.
+ }
.w-sdc-main-container-body-content {
height:calc(~'100% - @{action_nav_height} - @{tab_title}');
align-items: center;