summaryrefslogtreecommitdiffstats
path: root/catalog-ui/src/app/directives/graphs-v2
diff options
context:
space:
mode:
authorAnjali walsatwar <anjali.walsatwar@huawei.com>2018-09-25 14:16:47 +0530
committerAnjali Walsatwar <anjali.walsatwar@huawei.com>2018-12-28 09:30:56 +0000
commitc3f4278b607e853959273f4fbb5c43b9ecb0c49c (patch)
treea0b103aedd2e1a353e829056683f2079bbeee159 /catalog-ui/src/app/directives/graphs-v2
parent8eaa66c35b6dc428491f5064c68f4f6bebf38086 (diff)
Keyboard Shortcut for copy&Paste and delete
Issue-ID: SDC-1694 Change-Id: I6da5532520e289774c1e028cb7c31db0c5f79489 Signed-off-by: Anjali walsatwar <anjali.walsatwar@huawei.com>
Diffstat (limited to 'catalog-ui/src/app/directives/graphs-v2')
-rw-r--r--catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.directive.ts172
-rw-r--r--catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.html3
-rw-r--r--catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.less4
-rw-r--r--catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts40
-rw-r--r--catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts85
5 files changed, 298 insertions, 6 deletions
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 804e772ac4..a6af9a8110 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
@@ -35,7 +35,7 @@ import {
NodesFactory,
Point
} from "app/models";
-import { ComponentInstanceFactory, ComponentFactory, GRAPH_EVENTS, GraphColors } from "app/utils";
+import { ComponentInstanceFactory, ComponentFactory, GRAPH_EVENTS, GraphColors, GraphUIObjects, ModalsHandler } from "app/utils";
import { EventListenerService, LoaderService } from "app/services";
import { CompositionGraphLinkUtils } from "./utils/composition-graph-links-utils";
import { CompositionGraphGeneralUtils } from "./utils/composition-graph-general-utils";
@@ -127,6 +127,11 @@ export interface ICompositionGraphScope extends ng.IScope {
deletePathsOnCy(): void;
drawPathOnCy(data: ForwardingPath): void;
selectedPathId: string;
+
+ copyComponentInstance(): void;
+ pasteComponentInstance(event: IDragDropEvent): void;
+
+ origComponentId: string;
}
export class CompositionGraph implements ng.IDirective {
@@ -134,6 +139,7 @@ export class CompositionGraph implements ng.IDirective {
private _currentlyCLickedNodePosition: Cy.Position;
private dragElement: JQuery;
private dragComponent: ComponentInstance;
+ private cyBackgroundClickEvent: any;
constructor(private $q: ng.IQService,
private $log: ng.ILogService,
@@ -154,7 +160,8 @@ export class CompositionGraph implements ng.IDirective {
private ModalServiceNg2: ModalService,
private ConnectionWizardServiceNg2: ConnectionWizardService,
private ComponentInstanceServiceNg2: ComponentInstanceServiceNg2,
- private servicePathGraphUtils: ServicePathGraphUtils) {
+ private servicePathGraphUtils: ServicePathGraphUtils,
+ private ModalsHandler: ModalsHandler) {
}
@@ -531,8 +538,135 @@ export class CompositionGraph implements ng.IDirective {
this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, true, link);
}
};
+
+
+ document.onkeydown = (event) => {
+ let isModalExist = document.getElementsByTagName('body')[0].classList.contains('modal-open');
+
+ if (!scope.isViewOnly && !isModalExist) {
+
+ switch (event.keyCode) {
+
+ case 46: //delete
+ scope.deleteSelectedElements();
+ break;
+
+ case 67: //ctrl+c : copy componentInstance
+ if (event.ctrlKey && scope.component.isService()) {
+ scope.copyComponentInstance();
+ }
+ break;
+ case 86: // ctrl+v: paste componentInstance
+ if (event.ctrlKey && scope.component.isService()) {
+ let hidePasteObj = $(".w-canvas-content-paste");
+ hidePasteObj.click();
+ }
+ break;
+
+ }
+ }
+ };
+
+ scope.deleteSelectedElements = (): void => {
+ if (this._cy) {
+ let nodesToDelete = this._cy.$('node:selected');
+ let edgesSelected = this._cy.$('edge:selected');
+ if (nodesToDelete.length + edgesSelected.length <= 0) {
+ return;
+ }
+ let componentInstancetobeDele;
+ let title: string = "Delete Confirmation";
+ let message: string = "Are you sure you would like to delete selected elements?";
+ if (nodesToDelete.size() == 1 && edgesSelected.size() == 0) {
+
+ componentInstancetobeDele = nodesToDelete[0].data().componentInstance;
+ let showName = nodesToDelete[0].data().componentInstance.name;
+ message = "Are you sure you would like to delete" + " " + showName + "?";
+ }
+
+ let onOk = (): void => {
+ if (nodesToDelete.size() == 1 && edgesSelected.size() == 0) {
+ this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, componentInstancetobeDele);
+ }
+ else {
+ this.NodesGraphUtils.batchDeleteNodes(this._cy, scope.component, nodesToDelete);
+ }
+
+ };
+
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ }
+
+ };
+
+ scope.copyComponentInstance = (): void => {
+ scope.origComponentId = scope.component.uniqueId;
+ if (scope.component.selectedInstance) {
+ scope.origSelectedInstance = scope.component.selectedInstance;
+ }
+ };
+
+
+ scope.pasteComponentInstance = (event: IDragDropEvent): void => {
+ event.clientX = event.clientX ? event.clientX : this.cyBackgroundClickEvent ? this.cyBackgroundClickEvent.originalEvent.clientX : null;
+ event.clientY = event.clientY ? event.clientY : this.cyBackgroundClickEvent ? this.cyBackgroundClickEvent.originalEvent.clientY : null;
+
+ if (event.clientX == null || event.clientY == null) {
+ return;
+ }
+ let offsetPosition = {
+ x: event.clientX - GraphUIObjects.DIAGRAM_PALETTE_WIDTH_OFFSET,
+ y: event.clientY - GraphUIObjects.DIAGRAM_HEADER_OFFSET
+ };
+
+ let mousePosition = this.commonGraphUtils.HTMLCoordsToCytoscapeCoords(this._cy.extent(), offsetPosition);
+ let newPositionX = mousePosition.x;
+ let newPositionY = mousePosition.y;
+ let origSelectedInstance = scope.origSelectedInstance;
+
+ let copyComponentInstance = `{
+ "posX":"${newPositionX}",
+ "posY":"${newPositionY}",
+ "name":"${origSelectedInstance.componentName}",
+ "componentVersion":"${origSelectedInstance.componentVersion}",
+ "originType":"${origSelectedInstance.originType}",
+ "icon":"${origSelectedInstance.icon}",
+ "componentUid":"${origSelectedInstance.componentUid}"
+ }`;
+
+ this.isComponentPasteValid(scope, this._cy, event, offsetPosition, origSelectedInstance);
+
+
+ let onSuccess = (response): void => {
+ let success = (component: Component) => {
+ scope.component.componentInstances = component.componentInstances;
+ if (component.isService() && component.componentInstances.length > 0) {
+ _.each(component.componentInstances, (instance) => {
+ if (instance.uniqueId == response.componentInstance.uniqueId) {
+ let compositionGraphNode: CompositionCiNodeBase = this.NodesFactory.createNode(instance);
+ this.commonGraphUtils.addComponentInstanceNodeToGraph(this._cy, compositionGraphNode);
+ }
+ });
+ }
+ scope.isLoading = false;
+ };
+ scope.component.getComponent().then(success);
+ };
+ let onFailed = (error: any): void => {
+ console.log(error);
+ scope.isLoading = false;
+ };
+
+ if (scope.pasteValid) {
+ scope.isLoading = true;
+ scope.component.pasteMenuComponentInstance(origSelectedInstance.uniqueId, copyComponentInstance).then(onSuccess, onFailed);
+ }
+ };
+
}
+
+
private registerCytoscapeGraphEvents(scope: ICompositionGraphScope) {
this._cy.on('addedgemouseup', (event, data) => {
@@ -633,6 +767,7 @@ export class CompositionGraph implements ng.IDirective {
}
this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
}
+ this.cyBackgroundClickEvent = event;
scope.hideRelationMenu();
}
@@ -752,7 +887,6 @@ export class CompositionGraph implements ng.IDirective {
});
}
-
private initDropZone(scope: ICompositionGraphScope) {
if (scope.isViewOnly) {
@@ -866,7 +1000,30 @@ export class CompositionGraph implements ng.IDirective {
}
};
};
+ private isComponentPasteValid(scope: ICompositionGraphScope, cy: Cy.Instance, event: IDragDropEvent, offsetPosition: Cy.Position, origSelectedInstance: ComponentInstance) {
+ let bbox = this._getNodeBBox(cy, event, origSelectedInstance, offsetPosition);
+ if (this.GeneralGraphUtils.isPaletteDropValid(cy, bbox, origSelectedInstance)) {
+ scope.pasteValid = true;
+ } else {
+ scope.pasteValid = false;
+ }
+ }
+
+ private _getNodeBBox(cy: Cy.Instance, event: IDragDropEvent, origSelectedInstance: ComponentInstance, position?: Cy.Position) {
+ let bbox = <Cy.BoundingBox>{};
+ if (!position) {
+ position = this.commonGraphUtils.getCytoscapeNodePosition(cy, event);
+ }
+ let cushionWidth: number = 40;
+ let cushionHeight: number = 40;
+
+ bbox.x1 = position.x - cushionWidth / 2;
+ bbox.y1 = position.y - cushionHeight / 2;
+ bbox.x2 = position.x + cushionWidth / 2;
+ bbox.y2 = position.y + cushionHeight / 2;
+ return bbox;
+ }
public static factory = ($q,
$log,
@@ -887,7 +1044,8 @@ export class CompositionGraph implements ng.IDirective {
ModalService,
ConnectionWizardService,
ComponentInstanceServiceNg2,
- ServicePathGraphUtils) => {
+ ServicePathGraphUtils,
+ ModalsHandler) => {
return new CompositionGraph(
$q,
$log,
@@ -908,7 +1066,8 @@ export class CompositionGraph implements ng.IDirective {
ModalService,
ConnectionWizardService,
ComponentInstanceServiceNg2,
- ServicePathGraphUtils);
+ ServicePathGraphUtils,
+ ModalsHandler);
}
}
@@ -932,5 +1091,6 @@ CompositionGraph.factory.$inject = [
'ModalServiceNg2',
'ConnectionWizardServiceNg2',
'ComponentInstanceServiceNg2',
- 'ServicePathGraphUtils'
+ 'ServicePathGraphUtils',
+ 'ModalsHandler'
];
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 b473f44628..e264381a99 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
@@ -26,6 +26,9 @@
create-relation="createLinkFromMenu" cancel="cancelRelationMenu()"></relation-menu> -->
<menu-list-ng2 [props]="canvasMenuProps"></menu-list-ng2>
+<div id="cyMenu" class="hidePaste">
+ <div class="w-canvas-content-paste" data-ng-click="pasteComponentInstance($event)">paste</div>
+</div>
<div class="w-sdc-search-menu" data-ng-class="{'with-sidebar': withSidebar}">
diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.less b/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.less
index 7124a4b5a6..a310cd447e 100644
--- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.less
+++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.less
@@ -16,6 +16,10 @@ composition-graph {
background-color:rgb(248, 248, 248);
}
+ .hidePaste {
+ display:none;
+ }
+
.sdc-canvas-zones__wrapper {
position: absolute;
bottom: 10px;
diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts
index 705367c5f7..6c83810312 100644
--- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts
+++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts
@@ -105,6 +105,46 @@ export class CompositionGraphLinkUtils {
this.createLink(linkObg, cy, component);
};
+ public batchDeleteEdges(cy: Cy.Instance, component: Component, edgesToDelete: Cy.CollectionEdges, alreadyDeleteNodeIds?: Array<string>): void {
+ let toDeleteLinks: Array<RelationshipModel> = new Array<RelationshipModel>();
+ if (alreadyDeleteNodeIds && alreadyDeleteNodeIds.length > 0) {
+ edgesToDelete.each((i: number, link: Cy.CollectionEdges) => {
+ if (alreadyDeleteNodeIds.indexOf(link.data().source) < 0 && alreadyDeleteNodeIds.indexOf(link.data().target) < 0) {
+ toDeleteLinks.push(link.data().relation);
+ }
+ });
+ }
+ else {
+ edgesToDelete.each((i: number, link: Cy.CollectionEdges) => {
+ toDeleteLinks.push(link.data().relation);
+ });
+ }
+ this.loaderService.showLoader('composition-graph');
+ let onSuccessDeleteRelations = (response: Array<RelationshipModel>) => {
+ console.info('onSuccessDeleteRelations response is ', response);
+ //remove tempSimplePortNodes
+ if (alreadyDeleteNodeIds && alreadyDeleteNodeIds.length > 0) {
+ edgesToDelete.each((i: number, link: Cy.CollectionEdges) => {
+ if (alreadyDeleteNodeIds.indexOf(link.data().source) < 0 && alreadyDeleteNodeIds.indexOf(link.data().target) < 0) {
+ cy.remove(edgesToDelete);
+ }
+ });
+ }
+ else {
+ edgesToDelete.each((i: number, link: Cy.CollectionEdges) => {
+ cy.remove(edgesToDelete);
+ });
+ }
+ };
+
+ if (toDeleteLinks.length > 0) {
+ this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(
+ () => component.batchDeleteRelation(toDeleteLinks).then(onSuccessDeleteRelations),
+ () => this.loaderService.hideLoader('composition-graph'));
+
+ }
+ };
+
public createLinkFromMenu = (cy:Cy.Instance, chosenMatch:Match, component:Component):void => {
if (chosenMatch) {
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 c6c732b7df..7d08d7f9b5 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
@@ -122,6 +122,91 @@ export class CompositionGraphNodesUtils {
};
/**
+ * Batch delete component instances on server and then removes them from the graph as well
+ * @param cy
+ * @param component
+ * @param nodesToDelete
+ */
+ public batchDeleteNodes(cy:Cy.Instance, component:Component, nodesToDelete:Cy.CollectionNodes):Array<string> {
+ let nodesToDeleteIds:Array<string> = new Array<string>();
+ if(nodesToDelete && nodesToDelete.size() > 0){
+ nodesToDelete.each((i:number, node:Cy.CollectionNodes) => {
+ nodesToDeleteIds.push(node.data('id'));
+ });
+ this.loaderService.showLoader('composition-graph');
+ let componentInstances:Array<ComponentInstance> = component.componentInstances;
+ let onSuccess:(response:any) => void = (deleteFailedIds:any) => {
+ this.removeDeletedNodesOnGraph(cy, nodesToDelete, deleteFailedIds, componentInstances);
+ };
+
+ let onFailed:(response:any) => void = (response:any) => {
+ console.error('batchDeleteNodes failed error is', response);
+ };
+
+ this.GeneralGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(
+ () => component.batchDeleteComponentInstance(nodesToDeleteIds).then(onSuccess, onFailed),
+ () => this.loaderService.hideLoader('composition-graph')
+ );
+ }
+
+ return nodesToDeleteIds;
+ };
+
+ private deleteNodeSuccess(cy:Cy.Instance, component:Component, nodeToDelete:Cy.CollectionNodes):void{
+ //if node to delete is a UCPE, remove all children (except UCPE-CPs) and remove their "hostedOn" links
+ if (nodeToDelete.data().isUcpe) {
+ _.each(cy.nodes('[?isInsideGroup]'), (node)=> {
+ this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, node, nodeToDelete);
+ });
+ }
+
+ //check whether the node is connected to any VLs that only have one other connection. If so, delete that VL as well
+ if (!(nodeToDelete.data() instanceof CompositionCiNodeVl)) {
+ let connectedVls:Array<Cy.CollectionFirstNode> = this.getConnectedVlToNode(nodeToDelete);
+ this.handleConnectedVlsToDelete(connectedVls);
+ }
+
+ // check whether there is a service path going through this node, and if so clean it from the graph.
+ let nodeId = nodeToDelete.data().id;
+ let connectedPathLinks = cy.collection(`[type="${CompositionCiServicePathLink.LINK_TYPE}"][source="${nodeId}"], [type="${CompositionCiServicePathLink.LINK_TYPE}"][target="${nodeId}"]`);
+ _.forEach(connectedPathLinks, (link, key) => {
+ cy.remove(`[pathId="${link.data().pathId}"]`);
+ });
+
+ // update service path list
+ this.serviceService.getComponentCompositionData(component).subscribe((response:ServiceGenericResponse) => {
+ (<Service>component).forwardingPaths = response.forwardingPaths;
+ });
+
+ this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE_SUCCESS, nodeId);
+
+ //update UI
+ cy.remove(nodeToDelete);
+ }
+
+ private removeDeletedNodesOnGraph(cy:Cy.Instance, nodesToDelete:Cy.CollectionNodes, deleteFailedIds:Array<string>, componentInstances:Array<ComponentInstance>):void {
+ nodesToDelete.each((j:number, nodeToDelete:Cy.CollectionNodes) => {
+ if(deleteFailedIds.indexOf(nodeToDelete.data('id')) < 0) {
+ //if node to delete is a UCPE, remove all children (except UCPE-CPs) and remove their "hostedOn" links
+ if (nodeToDelete.data().isUcpe) {
+ _.each(cy.nodes('[?isInsideGroup]'), (node)=> {
+ this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, node , nodeToDelete);
+ });
+ }
+
+ //check whether the node is connected to any VLs that only have one other connection. If so, delete that VL as well
+ if(!(nodeToDelete.data() instanceof CompositionCiNodeVl)) {
+ let connectedVls:Array<Cy.CollectionFirstNode> = this.getConnectedVlToNode(nodeToDelete);
+ this.handleConnectedVlsToDelete(connectedVls);
+ }
+
+
+ cy.remove(nodeToDelete);
+ }
+ });
+ }
+
+ /**
* Finds all VLs connected to a single node
* @param node
* @returns {Array<Cy.CollectionFirstNode>}