@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,135 +1,120 @@
1
- import { BaseInteraction } from './base';
2
- import { isDraggingMovement } from './utils';
1
+ import { BaseInteraction } from "./base.js";
2
+ import { isDraggingMovement } from "./utils.js";
3
+ // src/interaction-handlers/click-interaction.ts
3
4
  /**
4
- * Marks the given ids as selected and deselects everything that is currently selected but not in that list.
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
- const keptSelected = new Set(idsToSelect);
8
- return [
9
- ...idsToSelect.map((id) => ({ id, selected: true })),
10
- ...currentlySelected.filter(({ id }) => !keptSelected.has(id)).map(({ id }) => ({ id, selected: false }))
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
- * Click interaction handler that handles click, double click and right click events on
15
- * nodes, relationships, clusters, and the scene.
16
- *
17
- * For examples, head to the {@link https://neo4j.com/docs/nvl/current/interaction-handlers/#_clickinteraction Click Interaction documentation page}.
18
- */
19
- export class ClickInteraction extends BaseInteraction {
20
- moved = false;
21
- mousePosition = { x: 0, y: 0 };
22
- /**
23
- * Creates a new click interaction handler.
24
- * @param nvl - The NVL instance to attach the interaction handler to.
25
- * @param options - Options for the click interaction handler.
26
- */
27
- constructor(nvl, options = { selectOnClick: false }) {
28
- super(nvl, options);
29
- this.addEventListener('mousedown', this.handleMouseDown, true);
30
- this.addEventListener('click', this.handleClick, true);
31
- this.addEventListener('dblclick', this.handleDoubleClick, true);
32
- this.addEventListener('contextmenu', this.handleRightClick, true);
33
- }
34
- handleMouseDown = (event) => {
35
- this.mousePosition = { x: event.clientX, y: event.clientY };
36
- };
37
- handleRightClick = (event) => {
38
- event.preventDefault();
39
- const { nvlTargets } = this.nvlInstance.getHits(event);
40
- const { nodes = [], relationships = [], clusters = [] } = nvlTargets;
41
- if (nodes.length === 0 && relationships.length === 0 && clusters.length === 0) {
42
- this.callCallbackIfRegistered('onCanvasRightClick', event);
43
- return;
44
- }
45
- if (nodes.length > 0) {
46
- this.callCallbackIfRegistered('onNodeRightClick', nodes[0]?.data, nvlTargets, event);
47
- }
48
- else if (relationships.length > 0) {
49
- this.callCallbackIfRegistered('onRelationshipRightClick', relationships[0]?.data, nvlTargets, event);
50
- }
51
- else if (clusters.length > 0) {
52
- this.callCallbackIfRegistered('onClusterRightClick', clusters[0]?.data, nvlTargets, event);
53
- }
54
- };
55
- handleDoubleClick = (event) => {
56
- const { nvlTargets } = this.nvlInstance.getHits(event);
57
- const { nodes = [], relationships = [], clusters = [] } = nvlTargets;
58
- if (nodes.length === 0 && relationships.length === 0 && clusters.length === 0) {
59
- this.callCallbackIfRegistered('onCanvasDoubleClick', event);
60
- return;
61
- }
62
- if (nodes.length > 0) {
63
- this.callCallbackIfRegistered('onNodeDoubleClick', nodes[0]?.data, nvlTargets, event);
64
- }
65
- else if (relationships.length > 0) {
66
- this.callCallbackIfRegistered('onRelationshipDoubleClick', relationships[0]?.data, nvlTargets, event);
67
- }
68
- else if (clusters.length > 0) {
69
- this.callCallbackIfRegistered('onClusterDoubleClick', clusters[0]?.data, nvlTargets, event);
70
- }
71
- };
72
- handleClick = (event) => {
73
- if (isDraggingMovement(event, this.mousePosition) || event.button !== 0) {
74
- return;
75
- }
76
- const { nvlTargets } = this.nvlInstance.getHits(event);
77
- const { nodes = [], relationships = [], clusters = [] } = nvlTargets;
78
- if (nodes.length === 0 && relationships.length === 0 && clusters.length === 0) {
79
- if (this.currentOptions.selectOnClick === true) {
80
- this.nvlInstance.deselectAll();
81
- }
82
- this.callCallbackIfRegistered('onCanvasClick', event);
83
- return;
84
- }
85
- if (nodes.length > 0) {
86
- const node = nodes[0]?.data;
87
- if (this.currentOptions.selectOnClick === true) {
88
- this.setSelection(node ? [node.id] : [], []);
89
- }
90
- this.callCallbackIfRegistered('onNodeClick', node, nvlTargets, event);
91
- }
92
- else if (relationships.length > 0) {
93
- const relationship = relationships[0]?.data;
94
- if (this.currentOptions.selectOnClick === true) {
95
- this.setSelection([], relationship ? [relationship.id] : []);
96
- }
97
- this.callCallbackIfRegistered('onRelationshipClick', relationship, nvlTargets, event);
98
- }
99
- else if (clusters.length > 0) {
100
- const cluster = clusters[0]?.data;
101
- if (this.currentOptions.selectOnClick === true) {
102
- this.setSelection(cluster ? cluster.nodeIds : [], []);
103
- }
104
- this.callCallbackIfRegistered('onClusterClick', cluster, nvlTargets, event);
105
- }
106
- };
107
- /**
108
- * Makes the given elements the entire selection, deselecting everything else.
109
- *
110
- * Clicking something that is already fully selected clears the selection instead, so a repeated
111
- * click toggles it off. A cluster counts as selected once all of its members are, which keeps
112
- * cluster clicks behaving like node and relationship clicks.
113
- */
114
- setSelection(nodeIds, relationshipIds) {
115
- const selectedNodes = this.nvlInstance.getSelectedNodes();
116
- const selectedRelationships = this.nvlInstance.getSelectedRelationships();
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 type { NVL, Node } from '@neo4j-nvl/base';
2
- import { BaseInteraction } from './base';
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
- * 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;
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
- 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
- }
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 { NODE_EDGE_WIDTH } from '../constants';
2
- import { BaseInteraction } from './base';
3
- import { isDraggingMovement } from './utils';
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
- * Interaction handler for dragging nodes,
6
- * which is achieved by clicking and moving the node.
7
- * When multiple nodes are selected, they are all dragged.
8
- *
9
- * For examples, head to the {@link https://neo4j.com/docs/nvl/current/interaction-handlers/#_dragnodeinteraction Drag Node Interaction documentation page}.
10
- */
11
- export class DragNodeInteraction extends BaseInteraction {
12
- mousePosition = { x: 0, y: 0 };
13
- mouseDownNode = null;
14
- isDragging = false;
15
- isDrawing = false;
16
- selectedNodes = [];
17
- moveSelectedNodes = false;
18
- /**
19
- * Creates a new instance of the drag node interaction handler.
20
- * @param nvl - The NVL instance to attach the interaction handler to.
21
- */
22
- constructor(nvl, options = {}) {
23
- super(nvl, options);
24
- this.addEventListener('mousedown', this.handleMouseDown);
25
- this.addEventListener('mousemove', this.handleMouseMove);
26
- }
27
- handleMouseDown = (event) => {
28
- this.mousePosition = { x: event.clientX, y: event.clientY };
29
- this.mouseDownNode = null;
30
- const hits = this.nvlInstance.getHits(event, ['node'], { hitNodeMarginWidth: NODE_EDGE_WIDTH });
31
- const hitNodes = hits.nvlTargets.nodes.filter((node) => node.insideNode);
32
- const hitNodeEdges = hits.nvlTargets.nodes.filter((node) => !node.insideNode);
33
- if (hitNodeEdges.length > 0) {
34
- this.isDrawing = true;
35
- this.addEventListener('mouseup', this.resetState, { once: true });
36
- }
37
- else if (hitNodes.length > 0) {
38
- this.mouseDownNode = hits.nvlTargets.nodes[0] ?? null;
39
- this.toggleGlobalTextSelection(false, this.handleBodyMouseUp);
40
- }
41
- this.selectedNodes = this.nvlInstance.getSelectedNodes();
42
- if (this.mouseDownNode !== null && this.selectedNodes.map((node) => node.id).includes(this.mouseDownNode.data.id)) {
43
- this.moveSelectedNodes = true;
44
- }
45
- else {
46
- this.moveSelectedNodes = false;
47
- }
48
- };
49
- handleMouseMove = (evt) => {
50
- // Avoid conflicts on moving node position in drawing process.
51
- if (this.mouseDownNode === null || evt.buttons !== 1 || this.isDrawing) {
52
- return;
53
- }
54
- if (!isDraggingMovement(evt, this.mousePosition)) {
55
- return;
56
- }
57
- if (!this.isDragging) {
58
- if (this.moveSelectedNodes) {
59
- this.callCallbackIfRegistered('onDragStart', this.selectedNodes, evt);
60
- }
61
- else {
62
- this.callCallbackIfRegistered('onDragStart', [this.mouseDownNode.data], evt);
63
- }
64
- this.isDragging = true;
65
- }
66
- const zoom = this.nvlInstance.getScale();
67
- const dx = ((evt.clientX - this.mousePosition.x) / zoom) * window.devicePixelRatio;
68
- const dy = ((evt.clientY - this.mousePosition.y) / zoom) * window.devicePixelRatio;
69
- if (this.moveSelectedNodes) {
70
- this.nvlInstance.setNodePositions(this.selectedNodes.map((node) => ({ id: node.id, x: node.x + dx, y: node.y + dy, pinned: true })), true);
71
- this.callCallbackIfRegistered('onDrag', this.selectedNodes, evt);
72
- }
73
- else {
74
- this.nvlInstance.setNodePositions([
75
- {
76
- id: this.mouseDownNode.data.id,
77
- x: this.mouseDownNode.targetCoordinates.x + dx,
78
- y: this.mouseDownNode.targetCoordinates.y + dy,
79
- pinned: true
80
- }
81
- ], true);
82
- this.callCallbackIfRegistered('onDrag', [this.mouseDownNode.data], evt);
83
- }
84
- };
85
- handleBodyMouseUp = (evt) => {
86
- this.toggleGlobalTextSelection(true, this.handleBodyMouseUp);
87
- if (this.isDragging && this.mouseDownNode !== null) {
88
- if (this.moveSelectedNodes) {
89
- this.callCallbackIfRegistered('onDragEnd', this.selectedNodes, evt);
90
- }
91
- else {
92
- this.callCallbackIfRegistered('onDragEnd', [this.mouseDownNode.data], evt);
93
- }
94
- }
95
- this.resetState();
96
- };
97
- resetState = () => {
98
- this.isDragging = false;
99
- this.mouseDownNode = null;
100
- this.isDrawing = false;
101
- this.selectedNodes = [];
102
- this.moveSelectedNodes = false;
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 };