delete node and connection by keyboard 43/11043/1
authorLvbo163 <lv.bo163@zte.com.cn>
Fri, 8 Sep 2017 07:08:50 +0000 (15:08 +0800)
committerLvbo163 <lv.bo163@zte.com.cn>
Fri, 8 Sep 2017 07:08:50 +0000 (15:08 +0800)
support delete selected node or connection by 'delete' key

Issue-ID: SDC-295

Change-Id: If4506331ad054b102c2ff70138adf2fc5e739e76
Signed-off-by: Lvbo163 <lv.bo163@zte.com.cn>
sdc-workflow-designer-ui/src/app/components/canvas/canvas.component.ts
sdc-workflow-designer-ui/src/app/components/node/node.component.css
sdc-workflow-designer-ui/src/app/components/node/node.component.html
sdc-workflow-designer-ui/src/app/components/node/node.component.ts
sdc-workflow-designer-ui/src/app/services/broadcast.service.ts
sdc-workflow-designer-ui/src/app/services/jsplumb.service.ts

index f4c0e21..5016b50 100644 (file)
@@ -10,7 +10,7 @@
  *     ZTE - initial API and implementation and/or initial documentation
  */
 
-import { AfterViewInit, Component } from '@angular/core';
+import { AfterViewInit, Component, HostListener } from '@angular/core';
 
 import { BroadcastService } from '../../services/broadcast.service';
 import { JsPlumbService } from '../../services/jsplumb.service';
@@ -18,6 +18,9 @@ import { ActivatedRoute } from "@angular/router";
 import { DataAccessService } from "../../services/data-access/data-access.service";
 import { WorkflowService } from "../../services/workflow.service";
 import { Workflow } from "../../model/workflow/workflow";
+import { WorkflowProcessService } from "../../services/workflow-process.service";
+import { SequenceFlow } from "../../model/workflow/sequence-flow";
+import { WorkflowNode } from "../../model/workflow/workflow-node";
 
 /**
  * main canvas, it contains two parts: canvas and node property component
@@ -29,12 +32,17 @@ import { Workflow } from "../../model/workflow/workflow";
     templateUrl: 'canvas.component.html',
 })
 export class CanvasComponent implements AfterViewInit {
+    private currentType: string; // WorkflowNode, SequenceFlow
+    private currentWorkflowNode: WorkflowNode;
+    private currentSequenceFlow: SequenceFlow;
+
 
     constructor(private broadcastService: BroadcastService,
         private dataAccessService: DataAccessService,
         private jsPlumbService: JsPlumbService,
         private route: ActivatedRoute,
-        private workflowService: WorkflowService) {
+        private workflowService: WorkflowService,
+        private processService: WorkflowProcessService) {
     }
 
     ngOnInit(): void {
@@ -49,6 +57,9 @@ export class CanvasComponent implements AfterViewInit {
 
     public ngAfterViewInit() {
         this.jsPlumbService.buttonDroppable();
+        this.broadcastService.currentSequenceFlow$.subscribe(sequenceFlow => this.currentSequenceFlow = sequenceFlow);
+        this.broadcastService.currentWorkflowNode$.subscribe(workflowNode => this.currentWorkflowNode = workflowNode);
+        this.broadcastService.currentType$.subscribe(type => this.currentType = type);
     }
 
     public canvasClick() {
@@ -56,6 +67,16 @@ export class CanvasComponent implements AfterViewInit {
         this.broadcastService.broadcast(this.broadcastService.showSequenceFlow, false);
     }
 
+    @HostListener('window:keyup.delete', ['$event']) ondelete(event: KeyboardEvent) {
+        if (this.currentType === 'WorkflowNode') {
+            this.jsPlumbService.remove(this.currentWorkflowNode.id);
+            this.processService.deleteNode(this.currentWorkflowNode.id);
+        } else if (this.currentType === 'SequenceFlow') {
+            this.processService.deleteSequenceFlow(this.currentSequenceFlow.sourceRef, this.currentSequenceFlow.targetRef);
+            this.jsPlumbService.deleteConnect(this.currentSequenceFlow.sourceRef, this.currentSequenceFlow.targetRef);
+        }
+    }
+
 
     public getWorkflow(): Workflow {
         return this.workflowService.workflow;
index 0d01835..1498fce 100644 (file)
     z-index: 2;\r
 }\r
 \r
+\r
+.node.active {\r
+    border: 2px solid red !important;\r
+    box-shadow: 2px 2px 19px #444;\r
+    -o-box-shadow: 2px 2px 19px #444;\r
+    -webkit-box-shadow: 2px 2px 19px #444;\r
+    -moz-box-shadow: 2px 2px 19px #fff;\r
+    opacity: 0.9;\r
+}\r
+\r
 .node:hover {\r
     border: 1px solid #123456;\r
     box-shadow: 2px 2px 19px #444;\r
index 62cd6f5..d4c59d6 100644 (file)
  *     ZTE - initial API and implementation and/or initial documentation\r
  */\r
 -->\r
-<div (dblclick)="showProperties($event)" class="node {{node.type}}" id="{{node.id}}" [style.top]="node.position.top + 'px'"\r
+<div (dblclick)="showProperties($event)" class="node {{node.type}}" id="{{node.id}}"\r
+    (click)="onSelected()"\r
+    [class.active]="active"\r
+    [style.top]="node.position.top + 'px'"\r
     [style.left]="node.position.left + 'px'">\r
     <div class="name">\r
         {{getDisplayName()}}\r
index c6a9596..7a48e5d 100644 (file)
  *     ZTE - initial API and implementation and/or initial documentation\r
  */\r
 \r
-import { Component, AfterViewInit, Input } from '@angular/core';\r
+import { Component, AfterViewInit, Input, OnDestroy } from '@angular/core';\r
 \r
 import { JsPlumbService } from '../../services/jsplumb.service';\r
 import { BroadcastService } from "../../services/broadcast.service";\r
 import { WorkflowNode } from "../../model/workflow/workflow-node";\r
+import { Subscription } from "rxjs/Subscription";\r
 \r
 /**\r
  * workflow node component\r
@@ -24,11 +25,15 @@ import { WorkflowNode } from "../../model/workflow/workflow-node";
     styleUrls: ['./node.component.css'],\r
     templateUrl: 'node.component.html',\r
 })\r
-export class NodeComponent implements AfterViewInit {\r
+export class NodeComponent implements AfterViewInit, OnDestroy {\r
 \r
     @Input() public node: WorkflowNode;\r
     @Input() public last: boolean;\r
 \r
+    public active = false;\r
+    private currentTypeSubscription: Subscription;\r
+    private currentWorkflowSubscription: Subscription;\r
+\r
     constructor(private broadcastService: BroadcastService,\r
         private jsPlumbService: JsPlumbService) {\r
 \r
@@ -38,6 +43,25 @@ export class NodeComponent implements AfterViewInit {
         if(this.last) {\r
             this.jsPlumbService.initNode('.node');\r
         }\r
+\r
+        this.currentTypeSubscription = this.broadcastService.currentType$.subscribe(type => {\r
+            if (type === 'SequenceFlow') {\r
+                this.active = false;\r
+            }\r
+        });\r
+\r
+        this.currentWorkflowSubscription = this.broadcastService.currentWorkflowNode$.subscribe(activeNode => {\r
+            if (activeNode.id === this.node.id) {\r
+                this.active = true;\r
+            } else {\r
+                this.active = false;\r
+            }\r
+        });\r
+    }\r
+\r
+    public ngOnDestroy() {\r
+        this.currentTypeSubscription.unsubscribe();\r
+        this.currentWorkflowSubscription.unsubscribe();\r
     }\r
 \r
     public showProperties() {\r
@@ -53,4 +77,9 @@ export class NodeComponent implements AfterViewInit {
         }\r
     }\r
 \r
+    public onSelected() {\r
+        this.broadcastService.broadcast(this.broadcastService.currentWorkflowNode, this.node);\r
+        this.broadcastService.broadcast(this.broadcastService.currentType, 'WorkflowNode');\r
+    }\r
+\r
 }\r
index 7726eae..ec182b3 100644 (file)
@@ -44,6 +44,13 @@ export class BroadcastService {
     public sequenceFlow = new Subject<SequenceFlow>();
     public sequenceFlow$ = this.sequenceFlow.asObservable();
 
+    public currentSequenceFlow = new Subject<SequenceFlow>();
+    public currentSequenceFlow$ = this.currentSequenceFlow.asObservable();
+    public currentWorkflowNode = new Subject<WorkflowNode>();
+    public currentWorkflowNode$ = this.currentWorkflowNode.asObservable();
+    public currentType = new Subject<string>();
+    public currentType$ = this.currentType.asObservable();
+
     /**
      * broadcast datas
      * this method will catch the exceptions for the broadcast
index e4a9f38..06cb2b0 100644 (file)
@@ -14,6 +14,7 @@ import { Injectable } from '@angular/core';
 import * as jsp from 'jsplumb';\r
 import { WorkflowProcessService } from "./workflow-process.service";\r
 import { BroadcastService } from "./broadcast.service";\r
+import { Subscription } from 'rxjs/Subscription';\r
 \r
 /**\r
  * JsPlumbService\r
@@ -22,6 +23,7 @@ import { BroadcastService } from "./broadcast.service";
 @Injectable()\r
 export class JsPlumbService {\r
     public jsplumbInstance;\r
+    public subscriptionMap = new Map<string, Subscription>();\r
 \r
     constructor(private processService: WorkflowProcessService, private broadcastService: BroadcastService) {\r
         this.initJsPlumbInstance();\r
@@ -61,10 +63,13 @@ export class JsPlumbService {
         this.jsplumbInstance.bind('connection', info => {\r
             this.processService.addSequenceFlow(info.connection.sourceId, info.connection.targetId);\r
 \r
-            // info.connection.bind('click', connection => {\r
-            //     this.jsplumbInstance.select({ connections: [connection] }).delete();\r
-            //     this.processService.deleteSequenceFlow(connection.sourceId, connection.targetId);\r
-            // });\r
+            this.subscribe4Connection(info.connection);\r
+\r
+            info.connection.bind('click', connection => {\r
+                const sequenceFlow = this.processService.getSequenceFlow(connection.sourceId, connection.targetId);\r
+                this.broadcastService.broadcast(this.broadcastService.currentSequenceFlow, sequenceFlow);\r
+                this.broadcastService.broadcast(this.broadcastService.currentType, 'SequenceFlow');\r
+            });\r
 \r
             info.connection.bind('dblclick', connection => {\r
                 const sequenceFlow = this.processService.getSequenceFlow(connection.sourceId, connection.targetId);\r
@@ -75,14 +80,48 @@ export class JsPlumbService {
 \r
     }\r
 \r
+    private subscribe4Connection(connection: any) {\r
+        const pre = connection.sourceId + connection.targetId;\r
+        let sequenceFlowSubscription = this.subscriptionMap.get(pre + 'sequenceFlowSubscription');\r
+        if (sequenceFlowSubscription && !sequenceFlowSubscription.closed) {\r
+            sequenceFlowSubscription.unsubscribe();\r
+        }\r
+\r
+        sequenceFlowSubscription = this.broadcastService.currentSequenceFlow$.subscribe(currentSequenceFlow => {\r
+            if (currentSequenceFlow.sourceRef === connection.sourceId\r
+                && currentSequenceFlow.targetRef === connection.targetId) {\r
+                connection.setPaintStyle({ stroke: 'red' });\r
+            } else {\r
+                connection.setPaintStyle({ stroke: 'black' });\r
+            }\r
+        });\r
+\r
+        this.subscriptionMap.set(pre + 'sequenceFlowSubscription', sequenceFlowSubscription);\r
+\r
+        let typeSubscription = this.subscriptionMap.get(pre + 'typeSubscription');\r
+        if (typeSubscription && !typeSubscription.closed) {\r
+            typeSubscription.unsubscribe();\r
+        }\r
+        typeSubscription = this.broadcastService.currentType$.subscribe(type => {\r
+            if (type === 'WorkflowNode') {\r
+                connection.setPaintStyle({ stroke: 'black' });\r
+            }\r
+        });\r
+        this.subscriptionMap.set(pre + 'typeSubscription', typeSubscription);\r
+    }\r
+\r
+    private unsubscription4Connection(connectionSelection: any) {\r
+        connectionSelection.each(connection => {\r
+            const pre = connection.sourceId + connection.targetId;\r
+            this.subscriptionMap.get(pre + 'sequenceFlowSubscription').unsubscribe();\r
+            this.subscriptionMap.get(pre + 'typeSubscription').unsubscribe();\r
+        });\r
+    }\r
+\r
     public initNode(selectorString: string) {\r
         const selector = this.jsplumbInstance.getSelector(selectorString);\r
 \r
         this.jsplumbInstance.draggable(selector, {\r
-            // stop(event) {\r
-            //     node.position.left = event.pos[0];\r
-            //     node.position.top = event.pos[1];\r
-            // },\r
         });\r
 \r
         this.jsplumbInstance.makeTarget(selector, {\r
@@ -109,10 +148,17 @@ export class JsPlumbService {
     public deleteConnect(sourceId: string, targetId: string) {\r
         const sourceNode = this.processService.getNodeById(sourceId);\r
         const connectionSelection = this.jsplumbInstance.select({ source: sourceId, target: targetId });\r
+        this.unsubscription4Connection(connectionSelection);\r
         connectionSelection.delete();\r
     }\r
 \r
     public remove(nodeId: string) {\r
+        // unsubscription4Connection\r
+        const connectionsAsSource = this.jsplumbInstance.select({ source: nodeId });\r
+        this.unsubscription4Connection(connectionsAsSource);\r
+        const connectionsAsTarget = this.jsplumbInstance.select({ target: nodeId });\r
+        this.unsubscription4Connection(connectionsAsTarget);\r
+\r
         this.jsplumbInstance.remove(nodeId);\r
     }\r
 \r