20300: Merge branch 'main' into 20300-rails7
[arvados.git] / services / workbench2 / src / lib / cwl-svg / plugins / edge-hover / edge-hover.ts
1 import {PluginBase} from "../plugin-base";
2
3 export class SVGEdgeHoverPlugin extends PluginBase {
4
5     private boundEdgeEnterFunction = this.onEdgeEnter.bind(this);
6
7     private modelListener: { dispose: Function } = {
8         dispose: () => void 0
9     };
10
11     afterRender(): void {
12         this.attachEdgeHoverBehavior();
13     }
14
15     destroy(): void {
16         this.detachEdgeHoverBehavior();
17         this.modelListener.dispose();
18     }
19
20     private attachEdgeHoverBehavior() {
21
22         this.detachEdgeHoverBehavior();
23         this.workflow.workflow.addEventListener("mouseenter", this.boundEdgeEnterFunction, true);
24     }
25
26     private detachEdgeHoverBehavior() {
27         this.workflow.workflow.removeEventListener("mouseenter", this.boundEdgeEnterFunction, true);
28     }
29
30     private onEdgeEnter(ev: MouseEvent) {
31
32
33         // Ignore if we did not enter an edge
34         if (!(ev.target! as Element).classList.contains("edge")) return;
35
36         const target = ev.target as SVGGElement;
37         let tipEl: SVGGElement;
38
39         const onMouseMove = (ev: MouseEvent) => {
40             const coords = this.workflow.transformScreenCTMtoCanvas(ev.clientX, ev.clientY);
41             tipEl.setAttribute("x", String(coords.x));
42             tipEl.setAttribute("y", String(coords.y - 16));
43         };
44
45         const onMouseLeave = (ev: MouseEvent) => {
46             tipEl.remove();
47             target.removeEventListener("mousemove", onMouseMove);
48             target.removeEventListener("mouseleave", onMouseLeave)
49         };
50
51         this.modelListener = this.workflow.model.on("connection.remove", (source, destination) => {
52             if (!tipEl) return;
53             const [tipS, tipD] = tipEl.getAttribute("data-source-destination")!.split("$!$");
54             if (tipS === source.connectionId && tipD === destination.connectionId) {
55                 tipEl.remove();
56             }
57         });
58
59         const sourceNode    = target.getAttribute("data-source-node");
60         const destNode      = target.getAttribute("data-destination-node");
61         const sourcePort    = target.getAttribute("data-source-port");
62         const destPort      = target.getAttribute("data-destination-port");
63         const sourceConnect = target.getAttribute("data-source-connection");
64         const destConnect   = target.getAttribute("data-destination-connection");
65
66         const sourceLabel = sourceNode === sourcePort ? sourceNode : `${sourceNode} (${sourcePort})`;
67         const destLabel   = destNode === destPort ? destNode : `${destNode} (${destPort})`;
68
69         const coords = this.workflow.transformScreenCTMtoCanvas(ev.clientX, ev.clientY);
70
71         const ns = "http://www.w3.org/2000/svg";
72         tipEl    = document.createElementNS(ns, "text");
73         tipEl.classList.add("label");
74         tipEl.classList.add("label-edge");
75         tipEl.setAttribute("x", String(coords.x));
76         tipEl.setAttribute("y", String(coords.y));
77         tipEl.setAttribute("data-source-destination", sourceConnect + "$!$" + destConnect);
78         tipEl.innerHTML = sourceLabel + " → " + destLabel;
79
80         this.workflow.workflow.appendChild(tipEl);
81
82         target.addEventListener("mousemove", onMouseMove);
83         target.addEventListener("mouseleave", onMouseLeave);
84
85     }
86
87 }