@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,113 +1,98 @@
1
+ // src/overlay-renderer/overlay-renderer.ts
1
2
  const marquee = {
2
- frameWidth: 3,
3
- frameColor: '#a9a9a9',
4
- color: '#e0e0e0',
5
- lineDash: [10, 15],
6
- opacity: 0.5
3
+ frameWidth: 3,
4
+ frameColor: "#a9a9a9",
5
+ color: "#e0e0e0",
6
+ lineDash: [10, 15],
7
+ opacity: .5
7
8
  };
8
- export class OverlayRenderer {
9
- ctx;
10
- canvas;
11
- removeResizeListener;
12
- constructor(canvasParent) {
13
- const canvas = document.createElement('canvas');
14
- canvas.style.position = 'absolute';
15
- canvas.style.top = '0';
16
- canvas.style.bottom = '0';
17
- canvas.style.left = '0';
18
- canvas.style.right = '0';
19
- // These are needed for touchpad zoom on Edge
20
- canvas.style.touchAction = 'none';
21
- canvasParent?.appendChild(canvas);
22
- const context = canvas.getContext('2d');
23
- this.ctx = context;
24
- this.canvas = canvas;
25
- const handleResize = () => {
26
- this.fixCanvasSize(canvas);
27
- };
28
- canvasParent?.addEventListener('resize', handleResize);
29
- this.removeResizeListener = () => canvasParent?.removeEventListener('resize', handleResize);
30
- this.fixCanvasSize(canvas);
31
- }
32
- fixCanvasSize(canvas) {
33
- const parent = canvas.parentElement;
34
- if (!parent) {
35
- return;
36
- }
37
- const rect = parent.getBoundingClientRect();
38
- const { width } = rect;
39
- const { height } = rect;
40
- const devicePixelRatio = window.devicePixelRatio || 1;
41
- canvas.width = width * devicePixelRatio;
42
- canvas.height = height * devicePixelRatio;
43
- canvas.style.width = `${width}px`;
44
- canvas.style.height = `${height}px`;
45
- }
46
- drawBox(startX, startY, endX, endY) {
47
- const { ctx } = this;
48
- if (ctx === null) {
49
- return;
50
- }
51
- this.clear();
52
- ctx.save();
53
- ctx.beginPath();
54
- ctx.rect(startX, startY, endX - startX, endY - startY);
55
- ctx.closePath();
56
- ctx.strokeStyle = marquee.frameColor;
57
- const devicePixelRatio = window.devicePixelRatio || 1;
58
- ctx.lineWidth = marquee.frameWidth * devicePixelRatio;
59
- ctx.fillStyle = marquee.color;
60
- ctx.globalAlpha = marquee.opacity;
61
- ctx.setLineDash(marquee.lineDash);
62
- ctx.stroke();
63
- ctx.fill();
64
- ctx.restore();
65
- }
66
- drawLasso(points, stroke, fill) {
67
- const { ctx } = this;
68
- if (ctx === null) {
69
- return;
70
- }
71
- ctx.save();
72
- this.clear();
73
- ctx.beginPath();
74
- let i = 0;
75
- for (const point of points) {
76
- const { x, y } = point;
77
- if (i === 0) {
78
- ctx.moveTo(x, y);
79
- }
80
- else {
81
- ctx.lineTo(x, y);
82
- }
83
- i += 1;
84
- }
85
- const devicePixelRatio = window.devicePixelRatio || 1;
86
- ctx.strokeStyle = marquee.frameColor;
87
- ctx.setLineDash(marquee.lineDash);
88
- ctx.lineWidth = marquee.frameWidth * devicePixelRatio;
89
- ctx.fillStyle = marquee.color;
90
- ctx.globalAlpha = marquee.opacity;
91
- if (stroke) {
92
- ctx.stroke();
93
- }
94
- if (fill) {
95
- ctx.fill();
96
- }
97
- ctx.restore();
98
- }
99
- clear() {
100
- const { ctx, canvas } = this;
101
- if (ctx === null) {
102
- return;
103
- }
104
- const rect = canvas.getBoundingClientRect();
105
- const devicePixelRatio = window.devicePixelRatio || 1;
106
- ctx.clearRect(0, 0, rect.width * devicePixelRatio, rect.height * devicePixelRatio);
107
- }
108
- destroy() {
109
- const { canvas } = this;
110
- this.removeResizeListener();
111
- canvas.remove();
112
- }
113
- }
9
+ var OverlayRenderer = class {
10
+ ctx;
11
+ canvas;
12
+ removeResizeListener;
13
+ constructor(canvasParent) {
14
+ const canvas = document.createElement("canvas");
15
+ canvas.style.position = "absolute";
16
+ canvas.style.top = "0";
17
+ canvas.style.bottom = "0";
18
+ canvas.style.left = "0";
19
+ canvas.style.right = "0";
20
+ canvas.style.touchAction = "none";
21
+ canvasParent?.appendChild(canvas);
22
+ const context = canvas.getContext("2d");
23
+ this.ctx = context;
24
+ this.canvas = canvas;
25
+ const handleResize = () => {
26
+ this.fixCanvasSize(canvas);
27
+ };
28
+ canvasParent?.addEventListener("resize", handleResize);
29
+ this.removeResizeListener = () => canvasParent?.removeEventListener("resize", handleResize);
30
+ this.fixCanvasSize(canvas);
31
+ }
32
+ fixCanvasSize(canvas) {
33
+ const parent = canvas.parentElement;
34
+ if (!parent) return;
35
+ const rect = parent.getBoundingClientRect();
36
+ const { width } = rect;
37
+ const { height } = rect;
38
+ const devicePixelRatio = window.devicePixelRatio || 1;
39
+ canvas.width = width * devicePixelRatio;
40
+ canvas.height = height * devicePixelRatio;
41
+ canvas.style.width = `${width}px`;
42
+ canvas.style.height = `${height}px`;
43
+ }
44
+ drawBox(startX, startY, endX, endY) {
45
+ const { ctx } = this;
46
+ if (ctx === null) return;
47
+ this.clear();
48
+ ctx.save();
49
+ ctx.beginPath();
50
+ ctx.rect(startX, startY, endX - startX, endY - startY);
51
+ ctx.closePath();
52
+ ctx.strokeStyle = marquee.frameColor;
53
+ const devicePixelRatio = window.devicePixelRatio || 1;
54
+ ctx.lineWidth = marquee.frameWidth * devicePixelRatio;
55
+ ctx.fillStyle = marquee.color;
56
+ ctx.globalAlpha = marquee.opacity;
57
+ ctx.setLineDash(marquee.lineDash);
58
+ ctx.stroke();
59
+ ctx.fill();
60
+ ctx.restore();
61
+ }
62
+ drawLasso(points, stroke, fill) {
63
+ const { ctx } = this;
64
+ if (ctx === null) return;
65
+ ctx.save();
66
+ this.clear();
67
+ ctx.beginPath();
68
+ let i = 0;
69
+ for (const point of points) {
70
+ const { x, y } = point;
71
+ if (i === 0) ctx.moveTo(x, y);
72
+ else ctx.lineTo(x, y);
73
+ i += 1;
74
+ }
75
+ const devicePixelRatio = window.devicePixelRatio || 1;
76
+ ctx.strokeStyle = marquee.frameColor;
77
+ ctx.setLineDash(marquee.lineDash);
78
+ ctx.lineWidth = marquee.frameWidth * devicePixelRatio;
79
+ ctx.fillStyle = marquee.color;
80
+ ctx.globalAlpha = marquee.opacity;
81
+ if (stroke) ctx.stroke();
82
+ if (fill) ctx.fill();
83
+ ctx.restore();
84
+ }
85
+ clear() {
86
+ const { ctx, canvas } = this;
87
+ if (ctx === null) return;
88
+ const rect = canvas.getBoundingClientRect();
89
+ const devicePixelRatio = window.devicePixelRatio || 1;
90
+ ctx.clearRect(0, 0, rect.width * devicePixelRatio, rect.height * devicePixelRatio);
91
+ }
92
+ destroy() {
93
+ const { canvas } = this;
94
+ this.removeResizeListener();
95
+ canvas.remove();
96
+ }
97
+ };
98
+ export { OverlayRenderer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neo4j-nvl/interaction-handlers",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "license": "SEE LICENSE IN 'LICENSE.txt'",
5
5
  "homepage": "https://neo4j.com/docs/nvl/current/",
6
6
  "description": "Interaction handlers for the Neo4j Visualization Library",
@@ -12,28 +12,41 @@
12
12
  "bugs": {
13
13
  "url": "https://community.neo4j.com/c/neo4j-graph-platform/neo4j-bloom"
14
14
  },
15
+ "type": "module",
15
16
  "main": "lib/index.js",
16
17
  "types": "lib/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./lib/index.d.ts",
21
+ "import": "./lib/index.js",
22
+ "default": "./lib/index.js"
23
+ },
24
+ "./package.json": "./package.json"
25
+ },
17
26
  "files": [
18
27
  "LICENSE.txt",
19
28
  "lib"
20
29
  ],
30
+ "skills": [
31
+ "npm:@neo4j-nvl/base"
32
+ ],
21
33
  "scripts": {
22
- "build": "yarn global:tsc",
23
- "watch": "yarn global:tsc -w",
34
+ "build": "yarn global:tsc --noEmit && tsdown",
35
+ "watch": "tsdown --watch",
24
36
  "test": "yarn global:jest",
25
37
  "prepack": "cp ../../LICENSE.txt ./ && cp ../../CHANGELOG.md ./",
26
38
  "postpack": "rm LICENSE.txt && rm CHANGELOG.md",
27
39
  "eslint": "yarn global:eslint ./src/"
28
40
  },
29
41
  "dependencies": {
30
- "@neo4j-nvl/base": "2.0.0",
42
+ "@neo4j-nvl/base": "2.1.0",
31
43
  "concaveman": "1.2.1",
32
- "lodash": "4.18.1"
44
+ "lodash-es": "4.18.1"
33
45
  },
34
46
  "devDependencies": {
35
47
  "@testing-library/jest-dom": "5.17.0",
36
48
  "@types/concaveman": "1.1.6",
37
- "@types/lodash": "4.17.21"
49
+ "@types/lodash-es": "4.17.12",
50
+ "tsdown": "0.23.1"
38
51
  }
39
52
  }
@@ -1 +0,0 @@
1
- import '@testing-library/jest-dom';
@@ -1,149 +0,0 @@
1
- import NVL from '@neo4j-nvl/base';
2
- import '@testing-library/jest-dom';
3
- import { BoxSelectInteraction } from '../interaction-handlers/box-select-interaction';
4
- jest.mock('@neo4j-nvl/layout-workers');
5
- const testNodes = [
6
- { id: '0', x: 25, y: 25 },
7
- { id: '1', x: 200, y: 200 }
8
- ];
9
- const testRels = [{ id: '10', from: '0', to: '1' }];
10
- const createMouseEvent = (type, x, y) => {
11
- return new MouseEvent(type, { button: 0, buttons: 1, clientX: x, clientY: y });
12
- };
13
- describe('BoxSelectInteraction', () => {
14
- let boxSelectInteraction;
15
- let myNVL;
16
- const startCallback = jest.fn();
17
- const selectCallback = jest.fn();
18
- const boxSelectCallbackMock = jest.fn();
19
- beforeEach(() => {
20
- myNVL = new NVL(document.createElement('div'), testNodes, testRels, {
21
- disableWebGL: true,
22
- initialZoom: 1,
23
- layout: 'free'
24
- });
25
- boxSelectInteraction = new BoxSelectInteraction(myNVL);
26
- });
27
- afterEach(() => {
28
- boxSelectInteraction.destroy();
29
- myNVL.destroy();
30
- startCallback.mockReset();
31
- selectCallback.mockReset();
32
- boxSelectCallbackMock.mockReset();
33
- });
34
- test('can select relationships by selecting both connected nodes', () => {
35
- boxSelectInteraction.updateCallback('onBoxStarted', startCallback);
36
- boxSelectInteraction.updateCallback('onBoxSelect', selectCallback);
37
- expect(myNVL.getSelectedNodes()).toHaveLength(0);
38
- // Box that contains both nodes
39
- const gesture = [
40
- createMouseEvent('mousedown', 0, 0),
41
- createMouseEvent('mousemove', 250, 250),
42
- createMouseEvent('mouseup', 250, 250)
43
- ];
44
- const container = myNVL.getContainer();
45
- gesture.forEach((e) => container.dispatchEvent(e));
46
- return new Promise((resolve) => {
47
- expect(startCallback).toHaveBeenCalledTimes(1);
48
- expect(selectCallback).toHaveBeenCalledTimes(1);
49
- expect(selectCallback).toHaveBeenCalledWith({ nodes: [testNodes[0], testNodes[1]], rels: [testRels[0]] }, gesture[2]);
50
- resolve();
51
- });
52
- });
53
- test('will not start when node is below pointer when dragging', () => {
54
- boxSelectInteraction.updateCallback('onBoxStarted', startCallback);
55
- boxSelectInteraction.updateCallback('onBoxSelect', selectCallback);
56
- expect(myNVL.getSelectedNodes()).toHaveLength(0);
57
- const gesture = [
58
- createMouseEvent('mousedown', 25, 25),
59
- createMouseEvent('mousemove', 50, 50),
60
- createMouseEvent('mousemove', 200, 200),
61
- createMouseEvent('mouseup', 200, 200)
62
- ];
63
- const container = myNVL.getContainer();
64
- gesture.forEach((e) => container.dispatchEvent(e));
65
- return new Promise((resolve) => {
66
- expect(startCallback).not.toHaveBeenCalled();
67
- expect(selectCallback).not.toHaveBeenCalled();
68
- resolve();
69
- });
70
- });
71
- test('can start box select when only a relationship is under the pointer', () => {
72
- boxSelectInteraction.updateCallback('onBoxStarted', startCallback);
73
- boxSelectInteraction.updateCallback('onBoxSelect', selectCallback);
74
- const gesture = [
75
- createMouseEvent('mousedown', 100, 100),
76
- createMouseEvent('mousemove', 150, 100),
77
- createMouseEvent('mouseup', 150, 100)
78
- ];
79
- const container = myNVL.getContainer();
80
- gesture.forEach((e) => container.dispatchEvent(e));
81
- return new Promise((resolve) => {
82
- expect(startCallback).toHaveBeenCalledTimes(1);
83
- expect(selectCallback).toHaveBeenCalledTimes(1);
84
- resolve();
85
- });
86
- });
87
- test('does not fire callbacks when clicking directly on a relationship without dragging', () => {
88
- boxSelectInteraction.updateCallback('onBoxStarted', startCallback);
89
- boxSelectInteraction.updateCallback('onBoxSelect', selectCallback);
90
- const container = myNVL.getContainer();
91
- container.dispatchEvent(createMouseEvent('mousedown', 112, 112));
92
- container.dispatchEvent(createMouseEvent('mouseup', 112, 112));
93
- expect(startCallback).not.toHaveBeenCalled();
94
- expect(selectCallback).not.toHaveBeenCalled();
95
- });
96
- test('box selection should disable text selection until mouse release', () => {
97
- const container = myNVL.getContainer();
98
- boxSelectInteraction.updateCallback('onBoxSelect', boxSelectCallbackMock);
99
- const gesture = [createMouseEvent('mousedown', 0, 0), createMouseEvent('mousemove', 150, 150)];
100
- gesture.forEach((e) => container.dispatchEvent(e));
101
- expect(document.body.style.getPropertyValue('user-select')).toBe('none');
102
- const bodyMouseUpEvent = createMouseEvent('mouseup', 150, 150);
103
- document.body.dispatchEvent(bodyMouseUpEvent);
104
- expect(document.body.style.getPropertyValue('user-select')).toBe('');
105
- });
106
- test('can automatically select nodes when selectOnRelease is true', () => {
107
- boxSelectInteraction.destroy();
108
- boxSelectInteraction = new BoxSelectInteraction(myNVL, { selectOnRelease: true });
109
- boxSelectInteraction.updateCallback('onBoxSelect', selectCallback);
110
- const gesture = [
111
- createMouseEvent('mousedown', 0, 0),
112
- createMouseEvent('mousemove', 50, 50),
113
- createMouseEvent('mouseup', 50, 50)
114
- ];
115
- const container = myNVL.getContainer();
116
- gesture.forEach((e) => container.dispatchEvent(e));
117
- return new Promise((resolve) => {
118
- expect(selectCallback).toHaveBeenCalledTimes(1);
119
- const selectedNodes = myNVL.getSelectedNodes();
120
- expect(selectedNodes).toHaveLength(1);
121
- expect(selectedNodes[0]?.id).toBe('0');
122
- resolve();
123
- });
124
- });
125
- test('box selection should work after leaving and re-entering the app area', () => {
126
- const container = myNVL.getContainer();
127
- boxSelectInteraction.updateCallback('onBoxStarted', startCallback);
128
- boxSelectInteraction.updateCallback('onBoxSelect', selectCallback);
129
- const exitGesture = [
130
- createMouseEvent('mousedown', 0, 0),
131
- createMouseEvent('mousemove', 50, 50),
132
- createMouseEvent('mouseleave', 99, 99)
133
- ];
134
- exitGesture.forEach((e) => container.dispatchEvent(e));
135
- const moveOutsideEvent = createMouseEvent('mousemove', 100, 100);
136
- document.body.dispatchEvent(moveOutsideEvent);
137
- const reEnterGesture = [createMouseEvent('mousemove', 150, 150), createMouseEvent('mouseup', 50, 50)];
138
- reEnterGesture.forEach((e) => container.dispatchEvent(e));
139
- return new Promise((resolve) => {
140
- expect(startCallback).toHaveBeenCalledTimes(1);
141
- expect(selectCallback).toHaveBeenCalledTimes(1);
142
- expect(selectCallback).toHaveBeenCalledWith({
143
- nodes: [testNodes[0]],
144
- rels: []
145
- }, reEnterGesture[1]);
146
- resolve();
147
- });
148
- });
149
- });
@@ -1 +0,0 @@
1
- import '@testing-library/jest-dom';