@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.
- package/CHANGELOG.md +25 -1
- package/README.md +8 -0
- package/lib/constants.d.ts +1 -1
- package/lib/constants.js +4 -2
- package/lib/index.d.ts +10 -20
- package/lib/index.js +10 -10
- package/lib/interaction-handlers/base.d.ts +67 -67
- package/lib/interaction-handlers/base.js +98 -104
- package/lib/interaction-handlers/box-select-interaction.d.ts +44 -44
- package/lib/interaction-handlers/box-select-interaction.js +133 -127
- package/lib/interaction-handlers/click-interaction.d.ts +111 -111
- package/lib/interaction-handlers/click-interaction.js +116 -131
- package/lib/interaction-handlers/drag-node-interaction.d.ts +40 -40
- package/lib/interaction-handlers/drag-node-interaction.js +102 -111
- package/lib/interaction-handlers/draw-interaction.d.ts +37 -37
- package/lib/interaction-handlers/draw-interaction.js +245 -278
- package/lib/interaction-handlers/hover-interaction.d.ts +34 -30
- package/lib/interaction-handlers/hover-interaction.js +83 -89
- package/lib/interaction-handlers/keyboard-interaction.d.ts +149 -97
- package/lib/interaction-handlers/keyboard-interaction.js +255 -201
- package/lib/interaction-handlers/lasso-interaction.d.ts +45 -45
- package/lib/interaction-handlers/lasso-interaction.js +174 -196
- package/lib/interaction-handlers/pan-interaction.d.ts +62 -61
- package/lib/interaction-handlers/pan-interaction.js +111 -100
- package/lib/interaction-handlers/utils.d.ts +2 -2
- package/lib/interaction-handlers/utils.js +39 -38
- package/lib/interaction-handlers/zoom-interaction.d.ts +52 -48
- package/lib/interaction-handlers/zoom-interaction.js +73 -71
- package/lib/overlay-renderer/overlay-renderer.d.ts +13 -13
- package/lib/overlay-renderer/overlay-renderer.js +96 -111
- package/package.json +19 -6
- package/lib/__tests__/box-select-interaction.test.d.ts +0 -1
- package/lib/__tests__/box-select-interaction.test.js +0 -149
- package/lib/__tests__/click-interaction.test.d.ts +0 -1
- package/lib/__tests__/click-interaction.test.js +0 -505
- package/lib/__tests__/drag-node-interaction.test.d.ts +0 -1
- package/lib/__tests__/drag-node-interaction.test.js +0 -247
- package/lib/__tests__/hover-interaction.test.d.ts +0 -1
- package/lib/__tests__/hover-interaction.test.js +0 -374
- package/lib/__tests__/keyboard-interaction.test.d.ts +0 -1
- package/lib/__tests__/keyboard-interaction.test.js +0 -322
- package/lib/__tests__/lasso-interaction.test.d.ts +0 -1
- package/lib/__tests__/lasso-interaction.test.js +0 -219
- package/lib/__tests__/pan-interaction.test.d.ts +0 -1
- package/lib/__tests__/pan-interaction.test.js +0 -223
- package/lib/__tests__/utils.test.d.ts +0 -1
- package/lib/__tests__/utils.test.js +0 -19
- package/lib/__tests__/zoom-interaction.test.d.ts +0 -1
- package/lib/__tests__/zoom-interaction.test.js +0 -185
|
@@ -1,135 +1,120 @@
|
|
|
1
|
-
import { BaseInteraction } from
|
|
2
|
-
import { isDraggingMovement } from
|
|
1
|
+
import { BaseInteraction } from "./base.js";
|
|
2
|
+
import { isDraggingMovement } from "./utils.js";
|
|
3
|
+
// src/interaction-handlers/click-interaction.ts
|
|
3
4
|
/**
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
* Marks the given ids as selected and deselects everything that is currently selected but not in that list.
|
|
6
|
+
*/
|
|
6
7
|
const buildSelectionUpdates = (idsToSelect, currentlySelected) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const keptSelected = new Set(idsToSelect);
|
|
9
|
+
return [...idsToSelect.map((id) => ({
|
|
10
|
+
id,
|
|
11
|
+
selected: true
|
|
12
|
+
})), ...currentlySelected.filter(({ id }) => !keptSelected.has(id)).map(({ id }) => ({
|
|
13
|
+
id,
|
|
14
|
+
selected: false
|
|
15
|
+
}))];
|
|
12
16
|
};
|
|
13
17
|
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const selectedNodeIds = new Set(selectedNodes.map((n) => n.id));
|
|
118
|
-
const selectedRelationshipIds = new Set(selectedRelationships.map((r) => r.id));
|
|
119
|
-
const isAlreadySelected = nodeIds.length + relationshipIds.length > 0 &&
|
|
120
|
-
nodeIds.every((id) => selectedNodeIds.has(id)) &&
|
|
121
|
-
relationshipIds.every((id) => selectedRelationshipIds.has(id));
|
|
122
|
-
const nodeIdsToSelect = isAlreadySelected ? [] : nodeIds;
|
|
123
|
-
const relationshipIdsToSelect = isAlreadySelected ? [] : relationshipIds;
|
|
124
|
-
this.nvlInstance.updateElementsInGraph(buildSelectionUpdates(nodeIdsToSelect, selectedNodes), buildSelectionUpdates(relationshipIdsToSelect, selectedRelationships));
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Removes all related event listeners from the container.
|
|
128
|
-
*/
|
|
129
|
-
destroy = () => {
|
|
130
|
-
this.removeEventListener('mousedown', this.handleMouseDown, true);
|
|
131
|
-
this.removeEventListener('click', this.handleClick, true);
|
|
132
|
-
this.removeEventListener('dblclick', this.handleDoubleClick, true);
|
|
133
|
-
this.removeEventListener('contextmenu', this.handleRightClick, true);
|
|
134
|
-
};
|
|
135
|
-
}
|
|
18
|
+
* Click interaction handler that handles click, double click and right click events on
|
|
19
|
+
* nodes, relationships, clusters, and the scene.
|
|
20
|
+
*
|
|
21
|
+
* For examples, head to the {@link https://neo4j.com/docs/nvl/current/interaction-handlers/#_clickinteraction Click Interaction documentation page}.
|
|
22
|
+
*/
|
|
23
|
+
var ClickInteraction = class extends BaseInteraction {
|
|
24
|
+
moved = false;
|
|
25
|
+
mousePosition = {
|
|
26
|
+
x: 0,
|
|
27
|
+
y: 0
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Creates a new click interaction handler.
|
|
31
|
+
* @param nvl - The NVL instance to attach the interaction handler to.
|
|
32
|
+
* @param options - Options for the click interaction handler.
|
|
33
|
+
*/
|
|
34
|
+
constructor(nvl, options = { selectOnClick: false }) {
|
|
35
|
+
super(nvl, options);
|
|
36
|
+
this.addEventListener("mousedown", this.handleMouseDown, true);
|
|
37
|
+
this.addEventListener("click", this.handleClick, true);
|
|
38
|
+
this.addEventListener("dblclick", this.handleDoubleClick, true);
|
|
39
|
+
this.addEventListener("contextmenu", this.handleRightClick, true);
|
|
40
|
+
}
|
|
41
|
+
handleMouseDown = (event) => {
|
|
42
|
+
this.mousePosition = {
|
|
43
|
+
x: event.clientX,
|
|
44
|
+
y: event.clientY
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
handleRightClick = (event) => {
|
|
48
|
+
event.preventDefault();
|
|
49
|
+
const { nvlTargets } = this.nvlInstance.getHits(event);
|
|
50
|
+
const { nodes = [], relationships = [], clusters = [] } = nvlTargets;
|
|
51
|
+
if (nodes.length === 0 && relationships.length === 0 && clusters.length === 0) {
|
|
52
|
+
this.callCallbackIfRegistered("onCanvasRightClick", event);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (nodes.length > 0) this.callCallbackIfRegistered("onNodeRightClick", nodes[0]?.data, nvlTargets, event);
|
|
56
|
+
else if (relationships.length > 0) this.callCallbackIfRegistered("onRelationshipRightClick", relationships[0]?.data, nvlTargets, event);
|
|
57
|
+
else if (clusters.length > 0) this.callCallbackIfRegistered("onClusterRightClick", clusters[0]?.data, nvlTargets, event);
|
|
58
|
+
};
|
|
59
|
+
handleDoubleClick = (event) => {
|
|
60
|
+
const { nvlTargets } = this.nvlInstance.getHits(event);
|
|
61
|
+
const { nodes = [], relationships = [], clusters = [] } = nvlTargets;
|
|
62
|
+
if (nodes.length === 0 && relationships.length === 0 && clusters.length === 0) {
|
|
63
|
+
this.callCallbackIfRegistered("onCanvasDoubleClick", event);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (nodes.length > 0) this.callCallbackIfRegistered("onNodeDoubleClick", nodes[0]?.data, nvlTargets, event);
|
|
67
|
+
else if (relationships.length > 0) this.callCallbackIfRegistered("onRelationshipDoubleClick", relationships[0]?.data, nvlTargets, event);
|
|
68
|
+
else if (clusters.length > 0) this.callCallbackIfRegistered("onClusterDoubleClick", clusters[0]?.data, nvlTargets, event);
|
|
69
|
+
};
|
|
70
|
+
handleClick = (event) => {
|
|
71
|
+
if (isDraggingMovement(event, this.mousePosition) || event.button !== 0) return;
|
|
72
|
+
const { nvlTargets } = this.nvlInstance.getHits(event);
|
|
73
|
+
const { nodes = [], relationships = [], clusters = [] } = nvlTargets;
|
|
74
|
+
if (nodes.length === 0 && relationships.length === 0 && clusters.length === 0) {
|
|
75
|
+
if (this.currentOptions.selectOnClick === true) this.nvlInstance.deselectAll();
|
|
76
|
+
this.callCallbackIfRegistered("onCanvasClick", event);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (nodes.length > 0) {
|
|
80
|
+
const node = nodes[0]?.data;
|
|
81
|
+
if (this.currentOptions.selectOnClick === true) this.setSelection(node ? [node.id] : [], []);
|
|
82
|
+
this.callCallbackIfRegistered("onNodeClick", node, nvlTargets, event);
|
|
83
|
+
} else if (relationships.length > 0) {
|
|
84
|
+
const relationship = relationships[0]?.data;
|
|
85
|
+
if (this.currentOptions.selectOnClick === true) this.setSelection([], relationship ? [relationship.id] : []);
|
|
86
|
+
this.callCallbackIfRegistered("onRelationshipClick", relationship, nvlTargets, event);
|
|
87
|
+
} else if (clusters.length > 0) {
|
|
88
|
+
const cluster = clusters[0]?.data;
|
|
89
|
+
if (this.currentOptions.selectOnClick === true) this.setSelection(cluster ? cluster.nodeIds : [], []);
|
|
90
|
+
this.callCallbackIfRegistered("onClusterClick", cluster, nvlTargets, event);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Makes the given elements the entire selection, deselecting everything else.
|
|
95
|
+
*
|
|
96
|
+
* Clicking something that is already fully selected clears the selection instead, so a repeated
|
|
97
|
+
* click toggles it off. A cluster counts as selected once all of its members are, which keeps
|
|
98
|
+
* cluster clicks behaving like node and relationship clicks.
|
|
99
|
+
*/
|
|
100
|
+
setSelection(nodeIds, relationshipIds) {
|
|
101
|
+
const selectedNodes = this.nvlInstance.getSelectedNodes();
|
|
102
|
+
const selectedRelationships = this.nvlInstance.getSelectedRelationships();
|
|
103
|
+
const selectedNodeIds = new Set(selectedNodes.map((n) => n.id));
|
|
104
|
+
const selectedRelationshipIds = new Set(selectedRelationships.map((r) => r.id));
|
|
105
|
+
const isAlreadySelected = nodeIds.length + relationshipIds.length > 0 && nodeIds.every((id) => selectedNodeIds.has(id)) && relationshipIds.every((id) => selectedRelationshipIds.has(id));
|
|
106
|
+
const nodeIdsToSelect = isAlreadySelected ? [] : nodeIds;
|
|
107
|
+
const relationshipIdsToSelect = isAlreadySelected ? [] : relationshipIds;
|
|
108
|
+
this.nvlInstance.updateElementsInGraph(buildSelectionUpdates(nodeIdsToSelect, selectedNodes), buildSelectionUpdates(relationshipIdsToSelect, selectedRelationships));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Removes all related event listeners from the container.
|
|
112
|
+
*/
|
|
113
|
+
destroy = () => {
|
|
114
|
+
this.removeEventListener("mousedown", this.handleMouseDown, true);
|
|
115
|
+
this.removeEventListener("click", this.handleClick, true);
|
|
116
|
+
this.removeEventListener("dblclick", this.handleDoubleClick, true);
|
|
117
|
+
this.removeEventListener("contextmenu", this.handleRightClick, true);
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
export { ClickInteraction };
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { BaseInteraction } from "./base.js";
|
|
2
|
+
import { NVL, Node } from "@neo4j-nvl/base";
|
|
3
3
|
/**
|
|
4
4
|
* Callbacks for the drag node interaction handler.
|
|
5
5
|
*/
|
|
6
6
|
export type DragNodeInteractionCallbacks = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Called when node(s) are dragged
|
|
9
|
+
* @param nodes - The node(s) being dragged.
|
|
10
|
+
* @param event - The original mouse event.
|
|
11
|
+
*/
|
|
12
|
+
onDrag?: ((nodes: Node[], evt: MouseEvent) => void) | boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Called once when node(s) are started to being dragged
|
|
15
|
+
* @param nodes - The node(s) being dragged.
|
|
16
|
+
* @param event - The original mouse event.
|
|
17
|
+
*/
|
|
18
|
+
onDragStart?: ((nodes: Node[], evt: MouseEvent) => void) | boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Called once when node(s) are finished being dragged
|
|
21
|
+
* @param nodes - The node(s) being dragged.
|
|
22
|
+
* @param event - The original mouse event.
|
|
23
|
+
*/
|
|
24
|
+
onDragEnd?: ((nodes: Node[], evt: MouseEvent) => void) | boolean;
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
27
|
* Interaction handler for dragging nodes,
|
|
@@ -31,23 +31,23 @@ export type DragNodeInteractionCallbacks = {
|
|
|
31
31
|
* For examples, head to the {@link https://neo4j.com/docs/nvl/current/interaction-handlers/#_dragnodeinteraction Drag Node Interaction documentation page}.
|
|
32
32
|
*/
|
|
33
33
|
export declare class DragNodeInteraction extends BaseInteraction<Record<string, never>> {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
34
|
+
private mousePosition;
|
|
35
|
+
private mouseDownNode;
|
|
36
|
+
private isDragging;
|
|
37
|
+
private isDrawing;
|
|
38
|
+
private selectedNodes;
|
|
39
|
+
private moveSelectedNodes;
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new instance of the drag node interaction handler.
|
|
42
|
+
* @param nvl - The NVL instance to attach the interaction handler to.
|
|
43
|
+
*/
|
|
44
|
+
constructor(nvl: NVL, options?: {});
|
|
45
|
+
private handleMouseDown;
|
|
46
|
+
private handleMouseMove;
|
|
47
|
+
private handleBodyMouseUp;
|
|
48
|
+
private resetState;
|
|
49
|
+
/**
|
|
50
|
+
* Removes all related event listeners from the container.
|
|
51
|
+
*/
|
|
52
|
+
destroy: () => void;
|
|
53
|
+
}
|
|
@@ -1,112 +1,103 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { BaseInteraction } from
|
|
3
|
-
import { isDraggingMovement } from
|
|
1
|
+
import "../constants.js";
|
|
2
|
+
import { BaseInteraction } from "./base.js";
|
|
3
|
+
import { isDraggingMovement } from "./utils.js";
|
|
4
|
+
// src/interaction-handlers/drag-node-interaction.ts
|
|
4
5
|
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
};
|
|
104
|
-
/**
|
|
105
|
-
* Removes all related event listeners from the container.
|
|
106
|
-
*/
|
|
107
|
-
destroy = () => {
|
|
108
|
-
this.toggleGlobalTextSelection(true, this.handleBodyMouseUp);
|
|
109
|
-
this.removeEventListener('mousedown', this.handleMouseDown);
|
|
110
|
-
this.removeEventListener('mousemove', this.handleMouseMove);
|
|
111
|
-
};
|
|
112
|
-
}
|
|
6
|
+
* Interaction handler for dragging nodes,
|
|
7
|
+
* which is achieved by clicking and moving the node.
|
|
8
|
+
* When multiple nodes are selected, they are all dragged.
|
|
9
|
+
*
|
|
10
|
+
* For examples, head to the {@link https://neo4j.com/docs/nvl/current/interaction-handlers/#_dragnodeinteraction Drag Node Interaction documentation page}.
|
|
11
|
+
*/
|
|
12
|
+
var DragNodeInteraction = class extends BaseInteraction {
|
|
13
|
+
mousePosition = {
|
|
14
|
+
x: 0,
|
|
15
|
+
y: 0
|
|
16
|
+
};
|
|
17
|
+
mouseDownNode = null;
|
|
18
|
+
isDragging = false;
|
|
19
|
+
isDrawing = false;
|
|
20
|
+
selectedNodes = [];
|
|
21
|
+
moveSelectedNodes = false;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new instance of the drag node interaction handler.
|
|
24
|
+
* @param nvl - The NVL instance to attach the interaction handler to.
|
|
25
|
+
*/
|
|
26
|
+
constructor(nvl, options = {}) {
|
|
27
|
+
super(nvl, options);
|
|
28
|
+
this.addEventListener("mousedown", this.handleMouseDown);
|
|
29
|
+
this.addEventListener("mousemove", this.handleMouseMove);
|
|
30
|
+
}
|
|
31
|
+
handleMouseDown = (event) => {
|
|
32
|
+
this.mousePosition = {
|
|
33
|
+
x: event.clientX,
|
|
34
|
+
y: event.clientY
|
|
35
|
+
};
|
|
36
|
+
this.mouseDownNode = null;
|
|
37
|
+
const hits = this.nvlInstance.getHits(event, ["node"], { hitNodeMarginWidth: 10 });
|
|
38
|
+
const hitNodes = hits.nvlTargets.nodes.filter((node) => node.insideNode);
|
|
39
|
+
if (hits.nvlTargets.nodes.filter((node) => !node.insideNode).length > 0) {
|
|
40
|
+
this.isDrawing = true;
|
|
41
|
+
this.addEventListener("mouseup", this.resetState, { once: true });
|
|
42
|
+
} else if (hitNodes.length > 0) {
|
|
43
|
+
this.mouseDownNode = hits.nvlTargets.nodes[0] ?? null;
|
|
44
|
+
this.toggleGlobalTextSelection(false, this.handleBodyMouseUp);
|
|
45
|
+
}
|
|
46
|
+
this.selectedNodes = this.nvlInstance.getSelectedNodes();
|
|
47
|
+
if (this.mouseDownNode !== null && this.selectedNodes.map((node) => node.id).includes(this.mouseDownNode.data.id)) this.moveSelectedNodes = true;
|
|
48
|
+
else this.moveSelectedNodes = false;
|
|
49
|
+
};
|
|
50
|
+
handleMouseMove = (evt) => {
|
|
51
|
+
if (this.mouseDownNode === null || evt.buttons !== 1 || this.isDrawing) return;
|
|
52
|
+
if (!isDraggingMovement(evt, this.mousePosition)) return;
|
|
53
|
+
if (!this.isDragging) {
|
|
54
|
+
if (this.moveSelectedNodes) this.callCallbackIfRegistered("onDragStart", this.selectedNodes, evt);
|
|
55
|
+
else this.callCallbackIfRegistered("onDragStart", [this.mouseDownNode.data], evt);
|
|
56
|
+
this.isDragging = true;
|
|
57
|
+
}
|
|
58
|
+
const zoom = this.nvlInstance.getScale();
|
|
59
|
+
const dx = (evt.clientX - this.mousePosition.x) / zoom * window.devicePixelRatio;
|
|
60
|
+
const dy = (evt.clientY - this.mousePosition.y) / zoom * window.devicePixelRatio;
|
|
61
|
+
if (this.moveSelectedNodes) {
|
|
62
|
+
this.nvlInstance.setNodePositions(this.selectedNodes.map((node) => ({
|
|
63
|
+
id: node.id,
|
|
64
|
+
x: node.x + dx,
|
|
65
|
+
y: node.y + dy,
|
|
66
|
+
pinned: true
|
|
67
|
+
})), true);
|
|
68
|
+
this.callCallbackIfRegistered("onDrag", this.selectedNodes, evt);
|
|
69
|
+
} else {
|
|
70
|
+
this.nvlInstance.setNodePositions([{
|
|
71
|
+
id: this.mouseDownNode.data.id,
|
|
72
|
+
x: this.mouseDownNode.targetCoordinates.x + dx,
|
|
73
|
+
y: this.mouseDownNode.targetCoordinates.y + dy,
|
|
74
|
+
pinned: true
|
|
75
|
+
}], true);
|
|
76
|
+
this.callCallbackIfRegistered("onDrag", [this.mouseDownNode.data], evt);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
handleBodyMouseUp = (evt) => {
|
|
80
|
+
this.toggleGlobalTextSelection(true, this.handleBodyMouseUp);
|
|
81
|
+
if (this.isDragging && this.mouseDownNode !== null) {
|
|
82
|
+
if (this.moveSelectedNodes) this.callCallbackIfRegistered("onDragEnd", this.selectedNodes, evt);
|
|
83
|
+
else this.callCallbackIfRegistered("onDragEnd", [this.mouseDownNode.data], evt);
|
|
84
|
+
}
|
|
85
|
+
this.resetState();
|
|
86
|
+
};
|
|
87
|
+
resetState = () => {
|
|
88
|
+
this.isDragging = false;
|
|
89
|
+
this.mouseDownNode = null;
|
|
90
|
+
this.isDrawing = false;
|
|
91
|
+
this.selectedNodes = [];
|
|
92
|
+
this.moveSelectedNodes = false;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Removes all related event listeners from the container.
|
|
96
|
+
*/
|
|
97
|
+
destroy = () => {
|
|
98
|
+
this.toggleGlobalTextSelection(true, this.handleBodyMouseUp);
|
|
99
|
+
this.removeEventListener("mousedown", this.handleMouseDown);
|
|
100
|
+
this.removeEventListener("mousemove", this.handleMouseMove);
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
export { DragNodeInteraction };
|