diff options
Diffstat (limited to 'catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts')
-rw-r--r-- | catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts | 266 |
1 files changed, 266 insertions, 0 deletions
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 new file mode 100644 index 0000000000..96afc8a4ea --- /dev/null +++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts @@ -0,0 +1,266 @@ +import {Component, NodesFactory, ComponentInstance, CompositionCiNodeVl,IAppMenu,AssetPopoverObj} from "app/models"; +import {EventListenerService, LoaderService} from "app/services"; +import {GRAPH_EVENTS,ModalsHandler,GraphUIObjects} from "app/utils"; +import {CompositionGraphGeneralUtils} from "./composition-graph-general-utils"; +import {CommonGraphUtils} from "../../common/common-graph-utils"; +/** + * Created by obarda on 11/9/2016. + */ +export class CompositionGraphNodesUtils { + constructor(private NodesFactory:NodesFactory, private $log:ng.ILogService, + private GeneralGraphUtils:CompositionGraphGeneralUtils, + private commonGraphUtils:CommonGraphUtils, + private eventListenerService:EventListenerService, + private loaderService:LoaderService /*, + private sdcMenu: IAppMenu, + private ModalsHandler: ModalsHandler*/) { + + } + + /** + * Returns component instances for all nodes passed in + * @param nodes - Cy nodes + * @returns {any[]} + */ + public getAllNodesData(nodes:Cy.CollectionNodes) { + return _.map(nodes, (node:Cy.CollectionFirstNode)=> { + return node.data(); + }) + }; + + /** + * Deletes component instances on server and then removes it from the graph as well + * @param cy + * @param component + * @param nodeToDelete + */ + public deleteNode(cy:Cy.Instance, component:Component, nodeToDelete:Cy.CollectionNodes):void { + + this.loaderService.showLoader('composition-graph'); + let onSuccess:(response:ComponentInstance) => void = (response:ComponentInstance) => { + console.info('onSuccess', response); + + //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); + } + + //update UI + cy.remove(nodeToDelete); + + }; + + let onFailed:(response:any) => void = (response:any) => { + console.info('onFailed', response); + }; + + + this.GeneralGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback( + () => component.deleteComponentInstance(nodeToDelete.data().componentInstance.uniqueId).then(onSuccess, onFailed), + () => this.loaderService.hideLoader('composition-graph') + ); + + }; + +/* + public confirmDeleteNode = (nodeId:string, cy:Cy.Instance, component:Component) => { + let node:Cy.CollectionNodes = cy.getElementById(nodeId); + let onOk = ():void => { + this.deleteNode(cy, component, node); + }; + + let componentInstance:ComponentInstance = node.data().componentInstance; + let state = "deleteInstance"; + let title:string = this.sdcMenu.alertMessages[state].title; + let message:string = this.sdcMenu.alertMessages[state].message.format([componentInstance.name]); + + this.ModalsHandler.openAlertModal(title, message).then(onOk); + };*/ + /** + * Finds all VLs connected to a single node + * @param node + * @returns {Array<Cy.CollectionFirstNode>} + */ + public getConnectedVlToNode = (node:Cy.CollectionNodes):Array<Cy.CollectionFirstNode> => { + let connectedVls:Array<Cy.CollectionFirstNode> = new Array<Cy.CollectionFirstNode>(); + _.forEach(node.connectedEdges().connectedNodes(), (node:Cy.CollectionFirstNode) => { + if (node.data() instanceof CompositionCiNodeVl) { + connectedVls.push(node); + } + }); + return connectedVls; + }; + + + /** + * Delete all VLs that have only two connected nodes (this function is called when deleting a node) + * @param connectedVls + */ + public handleConnectedVlsToDelete = (connectedVls:Array<Cy.CollectionFirstNode>) => { + _.forEach(connectedVls, (vlToDelete:Cy.CollectionNodes) => { + + if (vlToDelete.connectedEdges().length === 2) { // if vl connected only to 2 nodes need to delete the vl + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, vlToDelete.data().componentInstance); + } + }); + }; + + + /** + * This function is called when moving a node in or out of UCPE. + * Deletes all connected VLs that have less than 2 valid connections remaining after the move + * Returns the collection of vls that are in the process of deletion (async) to prevent duplicate calls while deletion is in progress + * @param component + * @param cy + * @param node - node that was moved in/out of ucpe + */ + public deleteNodeVLsUponMoveToOrFromUCPE = (component:Component, cy:Cy.Instance, node:Cy.CollectionNodes):Cy.CollectionNodes => { + if (node.data() instanceof CompositionCiNodeVl) { + return; + } + + let connectedVLsToDelete:Cy.CollectionNodes = cy.collection(); + _.forEach(node.neighborhood('node'), (connectedNode) => { + + //Find all neighboring nodes that are VLs + if (connectedNode.data() instanceof CompositionCiNodeVl) { + + //check VL's neighbors to see if it has 2 or more nodes whose location is compatible with VL (regardless of whether VL is in or out of UCPE) + let compatibleNodeCount = 0; + let vlNeighborhood = connectedNode.neighborhood('node'); + _.forEach(vlNeighborhood, (vlNeighborNode)=> { + if (this.commonGraphUtils.nodeLocationsCompatible(cy, connectedNode, vlNeighborNode)) { + compatibleNodeCount++; + } + }); + + if (compatibleNodeCount < 2) { + connectedVLsToDelete = connectedVLsToDelete.add(connectedNode); + } + } + }); + + connectedVLsToDelete.each((i, vlToDelete:Cy.CollectionNodes)=> { + this.deleteNode(cy, component, vlToDelete); + }); + return connectedVLsToDelete; + }; + + /** + * This function will update nodes position. if the new position is into or out of ucpe, the node will trigger the ucpe events + * @param cy + * @param component + * @param nodesMoved - the node/multiple nodes now moved by the user + */ + public onNodesPositionChanged = (cy:Cy.Instance, component:Component, nodesMoved:Cy.CollectionNodes):void => { + + if (nodesMoved.length === 0) { + return; + } + + let isValidMove:boolean = this.GeneralGraphUtils.isGroupValidDrop(cy, nodesMoved); + if (isValidMove) { + + this.$log.debug(`composition-graph::ValidDrop:: updating node position`); + let instancesToUpdateInNonBlockingAction:Array<ComponentInstance> = new Array<ComponentInstance>(); + + _.each(nodesMoved, (node:Cy.CollectionFirstNode)=> { //update all nodes new position + + if (node.data().isUcpePart && !node.data().isUcpe) { + return; + }//No need to update UCPE-CPs + + //update position + let newPosition:Cy.Position = this.commonGraphUtils.getNodePosition(node); + node.data().componentInstance.updatePosition(newPosition.x, newPosition.y); + + //check if node moved to or from UCPE + let ucpe = this.commonGraphUtils.isInUcpe(node.cy(), node.boundingbox()); + if (node.data().isInsideGroup || ucpe.length) { + this.handleUcpeChildMove(node, ucpe, instancesToUpdateInNonBlockingAction); + } else { + instancesToUpdateInNonBlockingAction.push(node.data().componentInstance); + } + + }); + + if (instancesToUpdateInNonBlockingAction.length > 0) { + this.GeneralGraphUtils.pushMultipleUpdateComponentInstancesRequestToQueue(false, instancesToUpdateInNonBlockingAction, component); + } + } else { + this.$log.debug(`composition-graph::notValidDrop:: node return to latest position`); + //reset nodes position + nodesMoved.positions((i, node) => { + return { + x: +node.data().componentInstance.posX, + y: +node.data().componentInstance.posY + }; + }) + } + + this.GeneralGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(() => { + }, () => { + this.loaderService.hideLoader('composition-graph'); + }); + + }; + + /** + * Checks whether the node has been added or removed from UCPE and triggers appropriate events + * @param node - node moved + * @param ucpeContainer - UCPE container that the node has been moved to. When moving a node out of ucpe, param will be empty + * @param instancesToUpdateInNonBlockingAction + */ + public handleUcpeChildMove(node:Cy.CollectionFirstNode, ucpeContainer:Cy.CollectionElements, instancesToUpdateInNonBlockingAction:Array<ComponentInstance>) { + + if (node.data().isInsideGroup) { + if (ucpeContainer.length) { //moving node within UCPE. Simply update position + this.commonGraphUtils.updateUcpeChildPosition(<Cy.CollectionNodes>node, ucpeContainer); + instancesToUpdateInNonBlockingAction.push(node.data().componentInstance); + } else { //removing node from UCPE. Notify observers + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, node, ucpeContainer); + } + } else if (!node.data().isInsideGroup && ucpeContainer.length && !node.data().isUcpePart) { //adding node to UCPE + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_INSERT_NODE_TO_UCPE, node, ucpeContainer, true); + } + } + /** + * Gets the position for the asset popover menu + * Then, check if right edge of menu would overlap horizontal screen edge (palette offset + canvas width - right panel) + * Then, check if bottom edge of menu would overlap the vertical end of the canvas. + * @param cy + * @param node + * @returns {Cy.Position} + + public createAssetPopover = (cy: Cy.Instance, node:Cy.CollectionFirstNode, isViewOnly:boolean):AssetPopoverObj => { + + let menuOffset:Cy.Position = { x: node.renderedWidth() / 2, y: -(node.renderedWidth() / 2) };// getNodePositionWithOffset returns central point of node. First add node.renderedWidth()/2 to get its to border. + let menuPosition:Cy.Position = this.commonGraphUtils.getNodePositionWithOffset(node, menuOffset); + let menuSide:string = 'right'; + + if(menuPosition.x + GraphUIObjects.COMPOSITION_NODE_MENU_WIDTH >= cy.width() + GraphUIObjects.DIAGRAM_PALETTE_WIDTH_OFFSET - GraphUIObjects.COMPOSITION_RIGHT_PANEL_OFFSET){ + menuPosition.x -= menuOffset.x * 2 + GraphUIObjects.COMPOSITION_NODE_MENU_WIDTH; //menu position already includes offset to the right. Therefore, subtract double offset so we have same distance from node for menu on left + menuSide = 'left'; + } + + if(menuPosition.y + GraphUIObjects.COMPOSITION_NODE_MENU_HEIGHT >= cy.height()){ + menuPosition.y = menuPosition.y - GraphUIObjects.COMPOSITION_NODE_MENU_HEIGHT - menuOffset.y * 2; + } + + return new AssetPopoverObj(node.data().id, node.data().name, menuPosition, menuSide, isViewOnly); + }; + */ + + } + + + CompositionGraphNodesUtils.$inject = ['NodesFactory', '$log', 'CompositionGraphGeneralUtils', 'CommonGraphUtils', 'EventListenerService', 'LoaderService' /*, 'sdcMenu', 'ModalsHandler'*/] + |