@neo4j-nvl/interaction-handlers 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/README.md +8 -0
  3. package/lib/constants.d.ts +1 -1
  4. package/lib/constants.js +4 -2
  5. package/lib/index.d.ts +10 -20
  6. package/lib/index.js +10 -10
  7. package/lib/interaction-handlers/base.d.ts +67 -67
  8. package/lib/interaction-handlers/base.js +98 -104
  9. package/lib/interaction-handlers/box-select-interaction.d.ts +44 -44
  10. package/lib/interaction-handlers/box-select-interaction.js +133 -127
  11. package/lib/interaction-handlers/click-interaction.d.ts +111 -111
  12. package/lib/interaction-handlers/click-interaction.js +116 -131
  13. package/lib/interaction-handlers/drag-node-interaction.d.ts +40 -40
  14. package/lib/interaction-handlers/drag-node-interaction.js +102 -111
  15. package/lib/interaction-handlers/draw-interaction.d.ts +37 -37
  16. package/lib/interaction-handlers/draw-interaction.js +245 -278
  17. package/lib/interaction-handlers/hover-interaction.d.ts +34 -30
  18. package/lib/interaction-handlers/hover-interaction.js +83 -89
  19. package/lib/interaction-handlers/keyboard-interaction.d.ts +149 -97
  20. package/lib/interaction-handlers/keyboard-interaction.js +255 -201
  21. package/lib/interaction-handlers/lasso-interaction.d.ts +45 -45
  22. package/lib/interaction-handlers/lasso-interaction.js +174 -196
  23. package/lib/interaction-handlers/pan-interaction.d.ts +62 -61
  24. package/lib/interaction-handlers/pan-interaction.js +111 -100
  25. package/lib/interaction-handlers/utils.d.ts +2 -2
  26. package/lib/interaction-handlers/utils.js +39 -38
  27. package/lib/interaction-handlers/zoom-interaction.d.ts +52 -48
  28. package/lib/interaction-handlers/zoom-interaction.js +73 -71
  29. package/lib/overlay-renderer/overlay-renderer.d.ts +13 -13
  30. package/lib/overlay-renderer/overlay-renderer.js +96 -111
  31. package/package.json +19 -6
  32. package/lib/__tests__/box-select-interaction.test.d.ts +0 -1
  33. package/lib/__tests__/box-select-interaction.test.js +0 -149
  34. package/lib/__tests__/click-interaction.test.d.ts +0 -1
  35. package/lib/__tests__/click-interaction.test.js +0 -505
  36. package/lib/__tests__/drag-node-interaction.test.d.ts +0 -1
  37. package/lib/__tests__/drag-node-interaction.test.js +0 -247
  38. package/lib/__tests__/hover-interaction.test.d.ts +0 -1
  39. package/lib/__tests__/hover-interaction.test.js +0 -374
  40. package/lib/__tests__/keyboard-interaction.test.d.ts +0 -1
  41. package/lib/__tests__/keyboard-interaction.test.js +0 -322
  42. package/lib/__tests__/lasso-interaction.test.d.ts +0 -1
  43. package/lib/__tests__/lasso-interaction.test.js +0 -219
  44. package/lib/__tests__/pan-interaction.test.d.ts +0 -1
  45. package/lib/__tests__/pan-interaction.test.js +0 -223
  46. package/lib/__tests__/utils.test.d.ts +0 -1
  47. package/lib/__tests__/utils.test.js +0 -19
  48. package/lib/__tests__/zoom-interaction.test.d.ts +0 -1
  49. package/lib/__tests__/zoom-interaction.test.js +0 -185
@@ -1,53 +1,53 @@
1
- import type { NVL, Node, Relationship } from '@neo4j-nvl/base';
2
- import { BaseInteraction } from './base';
1
+ import { BaseInteraction } from "./base.js";
2
+ import { NVL, Node, Relationship } from "@neo4j-nvl/base";
3
3
  /**
4
4
  * @internal
5
5
  * @hidden
6
6
  */
7
7
  export type DrawInteractionCallbacks = {
8
- onHoverNodeMargin?: ((hoveredNode: Node | null, evt: MouseEvent) => void) | boolean;
9
- onDrawEnded?: ((newRelationshipToAdd: Relationship | null, newTargetNodeToAdd: Node | null, event: MouseEvent) => void) | boolean;
10
- onDrawStarted?: ((event: MouseEvent) => void) | boolean;
8
+ onHoverNodeMargin?: ((hoveredNode: Node | null, evt: MouseEvent) => void) | boolean;
9
+ onDrawEnded?: ((newRelationshipToAdd: Relationship | null, newTargetNodeToAdd: Node | null, event: MouseEvent) => void) | boolean;
10
+ onDrawStarted?: ((event: MouseEvent) => void) | boolean;
11
11
  };
12
12
  /**
13
13
  * Options for the draw interaction handler.
14
14
  */
15
15
  export type DrawInteractionOptions = {
16
- /** Styling for the ghost graph that is shown when drawing. */
17
- ghostGraphStyling?: {
18
- /** Styling for the ghost node. */
19
- node?: Pick<Node, 'color' | 'size'>;
20
- /** Styling for the ghost relationship. */
21
- relationship?: Pick<Relationship, 'color' | 'width'>;
22
- };
16
+ /** Styling for the ghost graph that is shown when drawing. */
17
+ ghostGraphStyling?: {
18
+ /** Styling for the ghost node. */
19
+ node?: Pick<Node, 'color' | 'size'>;
20
+ /** Styling for the ghost relationship. */
21
+ relationship?: Pick<Relationship, 'color' | 'width'>;
22
+ };
23
23
  };
24
24
  /**
25
25
  * @internal
26
26
  * @hidden
27
27
  */
28
28
  export declare class DrawInteraction extends BaseInteraction<DrawInteractionOptions> {
29
- private isMoved;
30
- private isDrawing;
31
- private isDraggingNode;
32
- private mouseDownNode;
33
- private newTempTargetNode;
34
- private newTempRegularRelationshipToNewTempTargetNode;
35
- private newTempRegularRelationshipToExistingNode;
36
- private newTempSelfReferredRelationship;
37
- private newTargetNodeToAdd;
38
- private newRelationshipToAdd;
39
- private mouseOutsideOfNvlArea;
40
- constructor(nvl: NVL, options?: DrawInteractionOptions);
41
- private cancelDrawing;
42
- private handleMouseUpGlobal;
43
- private handleMouseLeaveNvl;
44
- private handleMouseEnterNvl;
45
- private handleMouseMove;
46
- private setNewRegularRelationship;
47
- private setNewRegularRelationshipToNewTempTargetNode;
48
- private setNewRegularRelationshipToExistingNode;
49
- private setNewSelfReferredRelationship;
50
- private handleMouseDown;
51
- private handleMouseUp;
52
- destroy: () => void;
53
- }
29
+ private isMoved;
30
+ private isDrawing;
31
+ private isDraggingNode;
32
+ private mouseDownNode;
33
+ private newTempTargetNode;
34
+ private newTempRegularRelationshipToNewTempTargetNode;
35
+ private newTempRegularRelationshipToExistingNode;
36
+ private newTempSelfReferredRelationship;
37
+ private newTargetNodeToAdd;
38
+ private newRelationshipToAdd;
39
+ private mouseOutsideOfNvlArea;
40
+ constructor(nvl: NVL, options?: DrawInteractionOptions);
41
+ private cancelDrawing;
42
+ private handleMouseUpGlobal;
43
+ private handleMouseLeaveNvl;
44
+ private handleMouseEnterNvl;
45
+ private handleMouseMove;
46
+ private setNewRegularRelationship;
47
+ private setNewRegularRelationshipToNewTempTargetNode;
48
+ private setNewRegularRelationshipToExistingNode;
49
+ private setNewSelfReferredRelationship;
50
+ private handleMouseDown;
51
+ private handleMouseUp;
52
+ destroy: () => void;
53
+ }
@@ -1,281 +1,248 @@
1
- /* eslint-disable @typescript-eslint/strict-boolean-expressions */
2
- import { NODE_EDGE_WIDTH } from '../constants';
3
- import { BaseInteraction } from './base';
4
- import { generateUniqueId, getCanvasPosition, getWorldPosition } from './utils';
1
+ import "../constants.js";
2
+ import { BaseInteraction } from "./base.js";
3
+ import { generateUniqueId, getCanvasPosition, getWorldPosition } from "./utils.js";
4
+ // src/interaction-handlers/draw-interaction.ts
5
5
  const DefaultGhostGraphStyling = {
6
- node: {
7
- color: 'black',
8
- size: 25
9
- },
10
- relationship: {
11
- color: 'red',
12
- width: 1
13
- }
6
+ node: {
7
+ color: "black",
8
+ size: 25
9
+ },
10
+ relationship: {
11
+ color: "red",
12
+ width: 1
13
+ }
14
14
  };
15
15
  /**
16
- * @internal
17
- * @hidden
18
- */
19
- export class DrawInteraction extends BaseInteraction {
20
- isMoved = false;
21
- isDrawing = false;
22
- isDraggingNode = false;
23
- mouseDownNode = undefined;
24
- newTempTargetNode = null;
25
- newTempRegularRelationshipToNewTempTargetNode = null;
26
- newTempRegularRelationshipToExistingNode = null;
27
- newTempSelfReferredRelationship = null;
28
- newTargetNodeToAdd = null;
29
- newRelationshipToAdd = null;
30
- mouseOutsideOfNvlArea = false;
31
- constructor(nvl, options = {}) {
32
- super(nvl, options);
33
- this.nvlInstance.setLayout('free');
34
- this.addEventListener('mousemove', this.handleMouseMove, true);
35
- this.addEventListener('mousedown', this.handleMouseDown, true);
36
- this.addEventListener('mouseup', this.handleMouseUp, true);
37
- this.containerInstance?.addEventListener('mouseleave', this.handleMouseLeaveNvl);
38
- this.containerInstance?.addEventListener('mouseenter', this.handleMouseEnterNvl);
39
- document.addEventListener('mouseup', this.handleMouseUpGlobal, true);
40
- }
41
- cancelDrawing = () => {
42
- this.nvlInstance.removeRelationshipsWithIds([
43
- this.newTempRegularRelationshipToNewTempTargetNode?.id,
44
- this.newTempRegularRelationshipToExistingNode?.id,
45
- this.newTempSelfReferredRelationship?.id
46
- ].filter((id) => Boolean(id)));
47
- this.nvlInstance.removeNodesWithIds(this.newTempTargetNode?.id ? [this.newTempTargetNode?.id] : []);
48
- this.newTempTargetNode = null;
49
- this.newTempRegularRelationshipToNewTempTargetNode = null;
50
- this.newTempRegularRelationshipToExistingNode = null;
51
- this.newTempSelfReferredRelationship = null;
52
- this.isMoved = false;
53
- this.isDrawing = false;
54
- this.isDraggingNode = false;
55
- };
56
- handleMouseUpGlobal = (event) => {
57
- // If mouse up outside of nvl area while drawing, cancel drawing.
58
- if (this.isDrawing && this.mouseOutsideOfNvlArea) {
59
- this.cancelDrawing();
60
- }
61
- };
62
- handleMouseLeaveNvl = () => {
63
- this.mouseOutsideOfNvlArea = true;
64
- };
65
- handleMouseEnterNvl = () => {
66
- this.mouseOutsideOfNvlArea = false;
67
- };
68
- handleMouseMove = (event) => {
69
- this.isMoved = true;
70
- if (this.isDrawing) {
71
- const canvasPos = getCanvasPosition(this.containerInstance, event);
72
- const pos = getWorldPosition(this.nvlInstance, canvasPos);
73
- const hits = this.nvlInstance.getHits(event, ['node']);
74
- const [hitNode] = hits.nvlTargets.nodes.filter((n) => n.data.id !== this.newTempTargetNode?.id);
75
- const targetNode = hitNode
76
- ? {
77
- id: hitNode.data.id,
78
- x: hitNode.targetCoordinates.x,
79
- y: hitNode.targetCoordinates.y,
80
- size: hitNode.data.size
81
- }
82
- : undefined;
83
- // ArrowBundler has race-condition to update the dataset,
84
- // if we remove the node with the same ID after mouse releases,
85
- // the same node will be removed then be added again, which will cause the issue.
86
- // So always make sure the new node id to be added to the dataset after mouse releasing is a new id.
87
- const newTargetNodeId = generateUniqueId(13);
88
- const newTargetNode = targetNode
89
- ? null
90
- : {
91
- id: newTargetNodeId,
92
- size: this.currentOptions.ghostGraphStyling?.node?.size ?? DefaultGhostGraphStyling.node.size,
93
- selected: false,
94
- x: pos.x,
95
- y: pos.y
96
- };
97
- const newRelationshipId = generateUniqueId(13);
98
- const relationship = this.mouseDownNode?.data
99
- ? {
100
- id: newRelationshipId,
101
- from: this.mouseDownNode.data.id,
102
- to: targetNode ? targetNode.id : newTargetNodeId
103
- }
104
- : null;
105
- let { x, y } = pos;
106
- let size = this.currentOptions.ghostGraphStyling?.node?.size ?? DefaultGhostGraphStyling.node.size;
107
- if (hitNode) {
108
- x = hitNode.targetCoordinates.x;
109
- y = hitNode.targetCoordinates.y;
110
- size = hitNode.data.size ?? size;
111
- if (hitNode.data.id === this.mouseDownNode?.data.id && !this.newTempSelfReferredRelationship) {
112
- this.nvlInstance.removeRelationshipsWithIds([
113
- this.newTempRegularRelationshipToNewTempTargetNode?.id,
114
- this.newTempRegularRelationshipToExistingNode?.id
115
- ].filter((id) => Boolean(id)));
116
- this.newTempRegularRelationshipToNewTempTargetNode = null;
117
- this.newTempRegularRelationshipToExistingNode = null;
118
- this.setNewSelfReferredRelationship();
119
- if (this.newTempSelfReferredRelationship) {
120
- this.nvlInstance.addElementsToGraph([], [this.newTempSelfReferredRelationship]);
121
- }
122
- }
123
- else if (hitNode.data.id !== this.mouseDownNode?.data.id && !this.newTempRegularRelationshipToExistingNode) {
124
- this.nvlInstance.removeRelationshipsWithIds([this.newTempSelfReferredRelationship?.id, this.newTempRegularRelationshipToNewTempTargetNode?.id].filter((id) => Boolean(id)));
125
- this.newTempSelfReferredRelationship = null;
126
- this.newTempRegularRelationshipToNewTempTargetNode = null;
127
- this.setNewRegularRelationshipToExistingNode(hitNode.data.id);
128
- if (this.newTempRegularRelationshipToExistingNode) {
129
- this.nvlInstance.addElementsToGraph([], [this.newTempRegularRelationshipToExistingNode]);
130
- }
131
- }
132
- }
133
- else if (!this.newTempRegularRelationshipToNewTempTargetNode) {
134
- this.nvlInstance.removeRelationshipsWithIds([this.newTempSelfReferredRelationship?.id, this.newTempRegularRelationshipToExistingNode?.id].filter((id) => Boolean(id)));
135
- this.newTempSelfReferredRelationship = null;
136
- this.newTempRegularRelationshipToExistingNode = null;
137
- this.setNewRegularRelationshipToNewTempTargetNode();
138
- this.nvlInstance.addElementsToGraph([], this.newTempRegularRelationshipToNewTempTargetNode ? [this.newTempRegularRelationshipToNewTempTargetNode] : []);
139
- }
140
- if (this.newTempTargetNode) {
141
- this.nvlInstance.setNodePositions([{ id: this.newTempTargetNode.id, x, y }]);
142
- this.nvlInstance.updateElementsInGraph([{ id: this.newTempTargetNode.id, x, y, size }], []);
143
- }
144
- this.newRelationshipToAdd = relationship;
145
- this.newTargetNodeToAdd = newTargetNode;
146
- }
147
- else if (!this.isDraggingNode) {
148
- this.newRelationshipToAdd = null;
149
- this.newTargetNodeToAdd = null;
150
- const hits = this.nvlInstance.getHits(event, ['node'], { hitNodeMarginWidth: NODE_EDGE_WIDTH });
151
- const hitNodeEdges = hits.nvlTargets.nodes.filter((node) => !node.insideNode);
152
- if (hitNodeEdges.length > 0) {
153
- const [node] = hitNodeEdges;
154
- this.callCallbackIfRegistered('onHoverNodeMargin', node?.data);
155
- }
156
- else {
157
- this.callCallbackIfRegistered('onHoverNodeMargin', null);
158
- }
159
- }
160
- };
161
- setNewRegularRelationship(targetId) {
162
- if (!this.mouseDownNode) {
163
- return null;
164
- }
165
- return {
166
- id: generateUniqueId(13),
167
- from: this.mouseDownNode.data.id,
168
- to: targetId,
169
- color: this.currentOptions.ghostGraphStyling?.relationship?.color ?? DefaultGhostGraphStyling.relationship.color,
170
- width: this.currentOptions.ghostGraphStyling?.relationship?.width ?? DefaultGhostGraphStyling.relationship.width
171
- };
172
- }
173
- setNewRegularRelationshipToNewTempTargetNode() {
174
- if (!this.mouseDownNode || !this.newTempTargetNode) {
175
- return;
176
- }
177
- this.newTempRegularRelationshipToNewTempTargetNode = this.setNewRegularRelationship(this.newTempTargetNode.id);
178
- }
179
- setNewRegularRelationshipToExistingNode(targetId) {
180
- if (!this.mouseDownNode) {
181
- return;
182
- }
183
- this.newTempRegularRelationshipToExistingNode = this.setNewRegularRelationship(targetId);
184
- }
185
- setNewSelfReferredRelationship() {
186
- if (!this.mouseDownNode) {
187
- return;
188
- }
189
- this.newTempSelfReferredRelationship = {
190
- id: generateUniqueId(13),
191
- from: this.mouseDownNode.data.id,
192
- to: this.mouseDownNode.data.id,
193
- color: this.currentOptions.ghostGraphStyling?.relationship?.color ?? DefaultGhostGraphStyling.relationship.color,
194
- width: this.currentOptions.ghostGraphStyling?.relationship?.width ?? DefaultGhostGraphStyling.relationship.width
195
- };
196
- }
197
- handleMouseDown = (event) => {
198
- this.callCallbackIfRegistered('onHoverNodeMargin', null);
199
- this.isMoved = false;
200
- this.newRelationshipToAdd = null;
201
- this.newTargetNodeToAdd = null;
202
- const hits = this.nvlInstance.getHits(event, ['node'], { hitNodeMarginWidth: NODE_EDGE_WIDTH });
203
- const hitNodes = hits.nvlTargets.nodes.filter((node) => node.insideNode);
204
- const hitNodeEdges = hits.nvlTargets.nodes.filter((node) => !node.insideNode);
205
- const startDragging = hitNodes.length > 0;
206
- const startDrawing = hitNodeEdges.length > 0;
207
- if (startDragging || startDrawing) {
208
- // Prevent default behavior to avoid interactions with other elements outside of nvl, like text selection,
209
- // but still assign focus to nvl container on click
210
- event.preventDefault();
211
- this.containerInstance?.focus();
212
- }
213
- if (startDragging) {
214
- this.isDraggingNode = true;
215
- this.isDrawing = false;
216
- }
217
- else if (startDrawing) {
218
- this.isDrawing = true;
219
- this.isDraggingNode = false;
220
- this.mouseDownNode = hitNodeEdges[0];
221
- const canvasPos = getCanvasPosition(this.containerInstance, event);
222
- const pos = getWorldPosition(this.nvlInstance, canvasPos);
223
- const color = this.currentOptions.ghostGraphStyling?.node?.color ?? DefaultGhostGraphStyling.node.color;
224
- const html = document.createElement('div');
225
- html.style.width = '110%';
226
- html.style.height = '110%';
227
- html.style.position = 'absolute';
228
- html.style.left = '-5%';
229
- html.style.top = '-5%';
230
- html.style.borderRadius = '50%';
231
- html.style.backgroundColor = color;
232
- this.newTempTargetNode = {
233
- id: generateUniqueId(13),
234
- size: this.currentOptions.ghostGraphStyling?.node?.size ?? DefaultGhostGraphStyling.node.size,
235
- selected: false,
236
- x: pos.x,
237
- y: pos.y,
238
- html
239
- };
240
- this.setNewRegularRelationshipToNewTempTargetNode();
241
- this.nvlInstance.addAndUpdateElementsInGraph([this.newTempTargetNode], this.newTempRegularRelationshipToNewTempTargetNode ? [this.newTempRegularRelationshipToNewTempTargetNode] : []);
242
- this.callCallbackIfRegistered('onDrawStarted', event);
243
- }
244
- else {
245
- this.mouseDownNode = undefined;
246
- this.isDrawing = false;
247
- this.isDraggingNode = false;
248
- }
249
- };
250
- handleMouseUp = (event) => {
251
- this.nvlInstance.removeRelationshipsWithIds([
252
- this.newTempRegularRelationshipToNewTempTargetNode?.id,
253
- this.newTempRegularRelationshipToExistingNode?.id,
254
- this.newTempSelfReferredRelationship?.id
255
- ].filter((id) => Boolean(id)));
256
- this.nvlInstance.removeNodesWithIds(this.newTempTargetNode?.id ? [this.newTempTargetNode?.id] : []);
257
- if (this.isDrawing && this.isMoved) {
258
- if (this.newTargetNodeToAdd) {
259
- this.nvlInstance.setNodePositions([this.newTargetNodeToAdd]);
260
- }
261
- // Avoid relationship glitch.
262
- this.nvlInstance.addAndUpdateElementsInGraph(this.newTargetNodeToAdd ? [{ id: this.newTargetNodeToAdd.id }] : [], this.newRelationshipToAdd ? [this.newRelationshipToAdd] : []);
263
- this.callCallbackIfRegistered('onDrawEnded', this.newRelationshipToAdd, this.newTargetNodeToAdd, event);
264
- }
265
- this.newTempTargetNode = null;
266
- this.newTempRegularRelationshipToNewTempTargetNode = null;
267
- this.newTempRegularRelationshipToExistingNode = null;
268
- this.newTempSelfReferredRelationship = null;
269
- this.isMoved = false;
270
- this.isDrawing = false;
271
- this.isDraggingNode = false;
272
- };
273
- destroy = () => {
274
- this.removeEventListener('mousemove', this.handleMouseMove, true);
275
- this.removeEventListener('mousedown', this.handleMouseDown, true);
276
- this.removeEventListener('mouseup', this.handleMouseUp, true);
277
- this.containerInstance?.removeEventListener('mouseleave', this.handleMouseLeaveNvl);
278
- this.containerInstance?.removeEventListener('mouseenter', this.handleMouseEnterNvl);
279
- document.removeEventListener('mouseup', this.handleMouseUpGlobal, true);
280
- };
281
- }
16
+ * @internal
17
+ * @hidden
18
+ */
19
+ var DrawInteraction = class extends BaseInteraction {
20
+ isMoved = false;
21
+ isDrawing = false;
22
+ isDraggingNode = false;
23
+ mouseDownNode = void 0;
24
+ newTempTargetNode = null;
25
+ newTempRegularRelationshipToNewTempTargetNode = null;
26
+ newTempRegularRelationshipToExistingNode = null;
27
+ newTempSelfReferredRelationship = null;
28
+ newTargetNodeToAdd = null;
29
+ newRelationshipToAdd = null;
30
+ mouseOutsideOfNvlArea = false;
31
+ constructor(nvl, options = {}) {
32
+ super(nvl, options);
33
+ this.nvlInstance.setLayout("free");
34
+ this.addEventListener("mousemove", this.handleMouseMove, true);
35
+ this.addEventListener("mousedown", this.handleMouseDown, true);
36
+ this.addEventListener("mouseup", this.handleMouseUp, true);
37
+ this.containerInstance?.addEventListener("mouseleave", this.handleMouseLeaveNvl);
38
+ this.containerInstance?.addEventListener("mouseenter", this.handleMouseEnterNvl);
39
+ document.addEventListener("mouseup", this.handleMouseUpGlobal, true);
40
+ }
41
+ cancelDrawing = () => {
42
+ this.nvlInstance.removeRelationshipsWithIds([
43
+ this.newTempRegularRelationshipToNewTempTargetNode?.id,
44
+ this.newTempRegularRelationshipToExistingNode?.id,
45
+ this.newTempSelfReferredRelationship?.id
46
+ ].filter((id) => Boolean(id)));
47
+ this.nvlInstance.removeNodesWithIds(this.newTempTargetNode?.id ? [this.newTempTargetNode?.id] : []);
48
+ this.newTempTargetNode = null;
49
+ this.newTempRegularRelationshipToNewTempTargetNode = null;
50
+ this.newTempRegularRelationshipToExistingNode = null;
51
+ this.newTempSelfReferredRelationship = null;
52
+ this.isMoved = false;
53
+ this.isDrawing = false;
54
+ this.isDraggingNode = false;
55
+ };
56
+ handleMouseUpGlobal = (event) => {
57
+ if (this.isDrawing && this.mouseOutsideOfNvlArea) this.cancelDrawing();
58
+ };
59
+ handleMouseLeaveNvl = () => {
60
+ this.mouseOutsideOfNvlArea = true;
61
+ };
62
+ handleMouseEnterNvl = () => {
63
+ this.mouseOutsideOfNvlArea = false;
64
+ };
65
+ handleMouseMove = (event) => {
66
+ this.isMoved = true;
67
+ if (this.isDrawing) {
68
+ const canvasPos = getCanvasPosition(this.containerInstance, event);
69
+ const pos = getWorldPosition(this.nvlInstance, canvasPos);
70
+ const [hitNode] = this.nvlInstance.getHits(event, ["node"]).nvlTargets.nodes.filter((n) => n.data.id !== this.newTempTargetNode?.id);
71
+ const targetNode = hitNode ? {
72
+ id: hitNode.data.id,
73
+ x: hitNode.targetCoordinates.x,
74
+ y: hitNode.targetCoordinates.y,
75
+ size: hitNode.data.size
76
+ } : void 0;
77
+ const newTargetNodeId = generateUniqueId(13);
78
+ const newTargetNode = targetNode ? null : {
79
+ id: newTargetNodeId,
80
+ size: this.currentOptions.ghostGraphStyling?.node?.size ?? DefaultGhostGraphStyling.node.size,
81
+ selected: false,
82
+ x: pos.x,
83
+ y: pos.y
84
+ };
85
+ const newRelationshipId = generateUniqueId(13);
86
+ const relationship = this.mouseDownNode?.data ? {
87
+ id: newRelationshipId,
88
+ from: this.mouseDownNode.data.id,
89
+ to: targetNode ? targetNode.id : newTargetNodeId
90
+ } : null;
91
+ let { x, y } = pos;
92
+ let size = this.currentOptions.ghostGraphStyling?.node?.size ?? DefaultGhostGraphStyling.node.size;
93
+ if (hitNode) {
94
+ x = hitNode.targetCoordinates.x;
95
+ y = hitNode.targetCoordinates.y;
96
+ size = hitNode.data.size ?? size;
97
+ if (hitNode.data.id === this.mouseDownNode?.data.id && !this.newTempSelfReferredRelationship) {
98
+ this.nvlInstance.removeRelationshipsWithIds([this.newTempRegularRelationshipToNewTempTargetNode?.id, this.newTempRegularRelationshipToExistingNode?.id].filter((id) => Boolean(id)));
99
+ this.newTempRegularRelationshipToNewTempTargetNode = null;
100
+ this.newTempRegularRelationshipToExistingNode = null;
101
+ this.setNewSelfReferredRelationship();
102
+ if (this.newTempSelfReferredRelationship) this.nvlInstance.addElementsToGraph([], [this.newTempSelfReferredRelationship]);
103
+ } else if (hitNode.data.id !== this.mouseDownNode?.data.id && !this.newTempRegularRelationshipToExistingNode) {
104
+ this.nvlInstance.removeRelationshipsWithIds([this.newTempSelfReferredRelationship?.id, this.newTempRegularRelationshipToNewTempTargetNode?.id].filter((id) => Boolean(id)));
105
+ this.newTempSelfReferredRelationship = null;
106
+ this.newTempRegularRelationshipToNewTempTargetNode = null;
107
+ this.setNewRegularRelationshipToExistingNode(hitNode.data.id);
108
+ if (this.newTempRegularRelationshipToExistingNode) this.nvlInstance.addElementsToGraph([], [this.newTempRegularRelationshipToExistingNode]);
109
+ }
110
+ } else if (!this.newTempRegularRelationshipToNewTempTargetNode) {
111
+ this.nvlInstance.removeRelationshipsWithIds([this.newTempSelfReferredRelationship?.id, this.newTempRegularRelationshipToExistingNode?.id].filter((id) => Boolean(id)));
112
+ this.newTempSelfReferredRelationship = null;
113
+ this.newTempRegularRelationshipToExistingNode = null;
114
+ this.setNewRegularRelationshipToNewTempTargetNode();
115
+ this.nvlInstance.addElementsToGraph([], this.newTempRegularRelationshipToNewTempTargetNode ? [this.newTempRegularRelationshipToNewTempTargetNode] : []);
116
+ }
117
+ if (this.newTempTargetNode) {
118
+ this.nvlInstance.setNodePositions([{
119
+ id: this.newTempTargetNode.id,
120
+ x,
121
+ y
122
+ }]);
123
+ this.nvlInstance.updateElementsInGraph([{
124
+ id: this.newTempTargetNode.id,
125
+ x,
126
+ y,
127
+ size
128
+ }], []);
129
+ }
130
+ this.newRelationshipToAdd = relationship;
131
+ this.newTargetNodeToAdd = newTargetNode;
132
+ } else if (!this.isDraggingNode) {
133
+ this.newRelationshipToAdd = null;
134
+ this.newTargetNodeToAdd = null;
135
+ const hitNodeEdges = this.nvlInstance.getHits(event, ["node"], { hitNodeMarginWidth: 10 }).nvlTargets.nodes.filter((node) => !node.insideNode);
136
+ if (hitNodeEdges.length > 0) {
137
+ const [node] = hitNodeEdges;
138
+ this.callCallbackIfRegistered("onHoverNodeMargin", node?.data);
139
+ } else this.callCallbackIfRegistered("onHoverNodeMargin", null);
140
+ }
141
+ };
142
+ setNewRegularRelationship(targetId) {
143
+ if (!this.mouseDownNode) return null;
144
+ return {
145
+ id: generateUniqueId(13),
146
+ from: this.mouseDownNode.data.id,
147
+ to: targetId,
148
+ color: this.currentOptions.ghostGraphStyling?.relationship?.color ?? DefaultGhostGraphStyling.relationship.color,
149
+ width: this.currentOptions.ghostGraphStyling?.relationship?.width ?? DefaultGhostGraphStyling.relationship.width
150
+ };
151
+ }
152
+ setNewRegularRelationshipToNewTempTargetNode() {
153
+ if (!this.mouseDownNode || !this.newTempTargetNode) return;
154
+ this.newTempRegularRelationshipToNewTempTargetNode = this.setNewRegularRelationship(this.newTempTargetNode.id);
155
+ }
156
+ setNewRegularRelationshipToExistingNode(targetId) {
157
+ if (!this.mouseDownNode) return;
158
+ this.newTempRegularRelationshipToExistingNode = this.setNewRegularRelationship(targetId);
159
+ }
160
+ setNewSelfReferredRelationship() {
161
+ if (!this.mouseDownNode) return;
162
+ this.newTempSelfReferredRelationship = {
163
+ id: generateUniqueId(13),
164
+ from: this.mouseDownNode.data.id,
165
+ to: this.mouseDownNode.data.id,
166
+ color: this.currentOptions.ghostGraphStyling?.relationship?.color ?? DefaultGhostGraphStyling.relationship.color,
167
+ width: this.currentOptions.ghostGraphStyling?.relationship?.width ?? DefaultGhostGraphStyling.relationship.width
168
+ };
169
+ }
170
+ handleMouseDown = (event) => {
171
+ this.callCallbackIfRegistered("onHoverNodeMargin", null);
172
+ this.isMoved = false;
173
+ this.newRelationshipToAdd = null;
174
+ this.newTargetNodeToAdd = null;
175
+ const hits = this.nvlInstance.getHits(event, ["node"], { hitNodeMarginWidth: 10 });
176
+ const hitNodes = hits.nvlTargets.nodes.filter((node) => node.insideNode);
177
+ const hitNodeEdges = hits.nvlTargets.nodes.filter((node) => !node.insideNode);
178
+ const startDragging = hitNodes.length > 0;
179
+ const startDrawing = hitNodeEdges.length > 0;
180
+ if (startDragging || startDrawing) {
181
+ event.preventDefault();
182
+ this.containerInstance?.focus();
183
+ }
184
+ if (startDragging) {
185
+ this.isDraggingNode = true;
186
+ this.isDrawing = false;
187
+ } else if (startDrawing) {
188
+ this.isDrawing = true;
189
+ this.isDraggingNode = false;
190
+ this.mouseDownNode = hitNodeEdges[0];
191
+ const canvasPos = getCanvasPosition(this.containerInstance, event);
192
+ const pos = getWorldPosition(this.nvlInstance, canvasPos);
193
+ const color = this.currentOptions.ghostGraphStyling?.node?.color ?? DefaultGhostGraphStyling.node.color;
194
+ const html = document.createElement("div");
195
+ html.style.width = "110%";
196
+ html.style.height = "110%";
197
+ html.style.position = "absolute";
198
+ html.style.left = "-5%";
199
+ html.style.top = "-5%";
200
+ html.style.borderRadius = "50%";
201
+ html.style.backgroundColor = color;
202
+ this.newTempTargetNode = {
203
+ id: generateUniqueId(13),
204
+ size: this.currentOptions.ghostGraphStyling?.node?.size ?? DefaultGhostGraphStyling.node.size,
205
+ selected: false,
206
+ x: pos.x,
207
+ y: pos.y,
208
+ html
209
+ };
210
+ this.setNewRegularRelationshipToNewTempTargetNode();
211
+ this.nvlInstance.addAndUpdateElementsInGraph([this.newTempTargetNode], this.newTempRegularRelationshipToNewTempTargetNode ? [this.newTempRegularRelationshipToNewTempTargetNode] : []);
212
+ this.callCallbackIfRegistered("onDrawStarted", event);
213
+ } else {
214
+ this.mouseDownNode = void 0;
215
+ this.isDrawing = false;
216
+ this.isDraggingNode = false;
217
+ }
218
+ };
219
+ handleMouseUp = (event) => {
220
+ this.nvlInstance.removeRelationshipsWithIds([
221
+ this.newTempRegularRelationshipToNewTempTargetNode?.id,
222
+ this.newTempRegularRelationshipToExistingNode?.id,
223
+ this.newTempSelfReferredRelationship?.id
224
+ ].filter((id) => Boolean(id)));
225
+ this.nvlInstance.removeNodesWithIds(this.newTempTargetNode?.id ? [this.newTempTargetNode?.id] : []);
226
+ if (this.isDrawing && this.isMoved) {
227
+ if (this.newTargetNodeToAdd) this.nvlInstance.setNodePositions([this.newTargetNodeToAdd]);
228
+ this.nvlInstance.addAndUpdateElementsInGraph(this.newTargetNodeToAdd ? [{ id: this.newTargetNodeToAdd.id }] : [], this.newRelationshipToAdd ? [this.newRelationshipToAdd] : []);
229
+ this.callCallbackIfRegistered("onDrawEnded", this.newRelationshipToAdd, this.newTargetNodeToAdd, event);
230
+ }
231
+ this.newTempTargetNode = null;
232
+ this.newTempRegularRelationshipToNewTempTargetNode = null;
233
+ this.newTempRegularRelationshipToExistingNode = null;
234
+ this.newTempSelfReferredRelationship = null;
235
+ this.isMoved = false;
236
+ this.isDrawing = false;
237
+ this.isDraggingNode = false;
238
+ };
239
+ destroy = () => {
240
+ this.removeEventListener("mousemove", this.handleMouseMove, true);
241
+ this.removeEventListener("mousedown", this.handleMouseDown, true);
242
+ this.removeEventListener("mouseup", this.handleMouseUp, true);
243
+ this.containerInstance?.removeEventListener("mouseleave", this.handleMouseLeaveNvl);
244
+ this.containerInstance?.removeEventListener("mouseenter", this.handleMouseEnterNvl);
245
+ document.removeEventListener("mouseup", this.handleMouseUpGlobal, true);
246
+ };
247
+ };
248
+ export { DrawInteraction };