@neo4j-nvl/react 1.0.0-b4712632 → 1.0.0-bb8a655f

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 CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  All notable changes to NVL will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
4
4
 
5
+ ## [1.1.0] - 2026-02-16
6
+
7
+ This 1.1.0 release introduces new rendering capabilities with the addition of a new SVG export method, alongside a new Circular Layout algorithm and touchpad gestures for zooming. It also includes performance improvements for the renderer and physics engine, as well as various fixes for the React wrappers and Minimap.
8
+
9
+ ### Added
10
+
11
+ - new SVG export functionality.
12
+ - new circular layout option.
13
+ - two-finger pinch gesture to zoom with touchpads.
14
+ - onCanvasDoubleClick callback to the interactive NVL React wrapper.
15
+
16
+ ### Changed
17
+
18
+ - improved minimap handling for React components.
19
+ - rendering performance improvements.
20
+ - physics layout performance and gravity improvements.
21
+
22
+ ### Fixed
23
+
24
+ - seed radius calculation in D3 layout for large graphs.
25
+ - forceDirected layout unnecessarily reheating on switch without changes.
26
+ - ensure drag operations are always correctly ended on mouse up.
27
+ - minimap positioning issues.
28
+ - background color for the 'os' theme in api documentation.
29
+
5
30
  ## [1.0.0] - 2025-09-30
6
31
  This 1.0.0 release is our first major release, with NVL's release strategy fully adopting semantic versioning going forward. Updates in this release include React 19 support for the React wrappers, a UI overhaul of the [examples app](https://neo4j.com/docs/api/nvl/current/examples.html).
7
32
 
@@ -7,6 +7,17 @@ import { BasicNvlWrapper } from '../basic-wrapper/BasicNvlWrapper';
7
7
  jest.mock('@neo4j-nvl/base');
8
8
  jest.mock('@neo4j-nvl/layout-workers');
9
9
  describe('BasicNvlWrapper', () => {
10
+ let setZoom;
11
+ let setPan;
12
+ let setZoomAndPan;
13
+ beforeEach(() => {
14
+ setZoom = jest.fn();
15
+ setPan = jest.fn();
16
+ setZoomAndPan = jest.fn();
17
+ jest.spyOn(NVL.prototype, 'setZoom').mockImplementation(setZoom);
18
+ jest.spyOn(NVL.prototype, 'setPan').mockImplementation(setPan);
19
+ jest.spyOn(NVL.prototype, 'setZoomAndPan').mockImplementation(setZoomAndPan);
20
+ });
10
21
  afterEach(() => {
11
22
  jest.clearAllMocks();
12
23
  });
@@ -37,33 +48,78 @@ describe('BasicNvlWrapper', () => {
37
48
  expect(destroy).toHaveBeenCalledTimes(1);
38
49
  });
39
50
  test('calls setZoom when zoom property is provided', () => {
40
- const setZoom = jest.fn();
41
- jest.spyOn(NVL.prototype, 'setZoom').mockImplementation(setZoom);
42
51
  render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5 }) }));
43
52
  expect(setZoom).toHaveBeenCalledWith(1.5);
53
+ expect(setZoom).toHaveBeenCalledTimes(1);
44
54
  });
45
55
  test('calls setPan when pan property is provided', () => {
46
- const setPan = jest.fn();
47
- jest.spyOn(NVL.prototype, 'setPan').mockImplementation(setPan);
48
56
  render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 10, y: 20 } }) }));
49
57
  expect(setPan).toHaveBeenCalledWith(10, 20);
58
+ expect(setPan).toHaveBeenCalledTimes(1);
50
59
  });
51
60
  test('calls setZoomAndPan when both zoom and pan properties are provided', () => {
52
- const setZoomAndPan = jest.fn();
53
- jest.spyOn(NVL.prototype, 'setZoomAndPan').mockImplementation(setZoomAndPan);
54
61
  render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
55
62
  expect(setZoomAndPan).toHaveBeenCalledWith(1.5, 10, 20);
63
+ expect(setZoomAndPan).toHaveBeenCalledTimes(1);
56
64
  });
57
65
  test('does not call zoom/pan methods when properties are undefined', () => {
58
- const setZoom = jest.fn();
59
- jest.spyOn(NVL.prototype, 'setZoom').mockImplementation(setZoom);
60
- const setPan = jest.fn();
61
- jest.spyOn(NVL.prototype, 'setPan').mockImplementation(setPan);
62
- const setZoomAndPan = jest.fn();
63
- jest.spyOn(NVL.prototype, 'setZoomAndPan').mockImplementation(setZoomAndPan);
64
66
  render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [] }) }));
65
67
  expect(setZoom).not.toHaveBeenCalled();
66
68
  expect(setPan).not.toHaveBeenCalled();
67
69
  expect(setZoomAndPan).not.toHaveBeenCalled();
68
70
  });
71
+ test('only calls setZoom when zoom changes', () => {
72
+ const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5 }) }));
73
+ expect(setZoom).toHaveBeenCalledTimes(1);
74
+ expect(setZoom).toHaveBeenCalledWith(1.5);
75
+ rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5 }) }));
76
+ expect(setZoom).toHaveBeenCalledTimes(1);
77
+ rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 2.0 }) }));
78
+ expect(setZoom).toHaveBeenCalledTimes(2);
79
+ expect(setZoom).toHaveBeenCalledWith(2.0);
80
+ expect(setPan).not.toHaveBeenCalled();
81
+ expect(setZoomAndPan).not.toHaveBeenCalled();
82
+ });
83
+ test('only calls setPan when pan changes', () => {
84
+ const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 10, y: 20 } }) }));
85
+ expect(setPan).toHaveBeenCalledTimes(1);
86
+ expect(setPan).toHaveBeenCalledWith(10, 20);
87
+ rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 10, y: 20 } }) }));
88
+ expect(setPan).toHaveBeenCalledTimes(1);
89
+ rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 30, y: 40 } }) }));
90
+ expect(setPan).toHaveBeenCalledTimes(2);
91
+ expect(setPan).toHaveBeenCalledWith(30, 40);
92
+ expect(setZoom).not.toHaveBeenCalled();
93
+ expect(setZoomAndPan).not.toHaveBeenCalled();
94
+ });
95
+ test('calls setZoomAndPan when both zoom and pan change', () => {
96
+ const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
97
+ expect(setZoomAndPan).toHaveBeenCalledTimes(1);
98
+ expect(setZoomAndPan).toHaveBeenCalledWith(1.5, 10, 20);
99
+ rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
100
+ expect(setZoomAndPan).toHaveBeenCalledTimes(1);
101
+ rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 2.0, pan: { x: 30, y: 40 } }) }));
102
+ expect(setZoomAndPan).toHaveBeenCalledTimes(2);
103
+ expect(setZoomAndPan).toHaveBeenCalledWith(2.0, 30, 40);
104
+ expect(setZoom).not.toHaveBeenCalled();
105
+ expect(setPan).not.toHaveBeenCalled();
106
+ });
107
+ test('calls appropriate methods when only zoom changes while both are set', () => {
108
+ const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
109
+ expect(setZoomAndPan).toHaveBeenCalledTimes(1);
110
+ rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 2.0, pan: { x: 10, y: 20 } }) }));
111
+ expect(setZoom).toHaveBeenCalledTimes(1);
112
+ expect(setZoom).toHaveBeenCalledWith(2.0);
113
+ expect(setZoomAndPan).toHaveBeenCalledTimes(1);
114
+ expect(setPan).not.toHaveBeenCalled();
115
+ });
116
+ test('calls appropriate methods when only pan changes while both are set', () => {
117
+ const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
118
+ expect(setZoomAndPan).toHaveBeenCalledTimes(1);
119
+ rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 30, y: 40 } }) }));
120
+ expect(setPan).toHaveBeenCalledTimes(1);
121
+ expect(setPan).toHaveBeenCalledWith(30, 40);
122
+ expect(setZoomAndPan).toHaveBeenCalledTimes(1);
123
+ expect(setZoom).not.toHaveBeenCalled();
124
+ });
69
125
  });
@@ -11,7 +11,37 @@ jest.mock('@neo4j-nvl/interaction-handlers');
11
11
  const MockedNVL = NVL;
12
12
  let mockOnInitialization;
13
13
  HoverInteraction.prototype.callbackMap = new Map();
14
+ HoverInteraction.prototype.updateCallback = jest.fn();
15
+ HoverInteraction.prototype.removeCallback = jest.fn();
16
+ HoverInteraction.prototype.destroy = jest.fn();
14
17
  PanInteraction.prototype.callbackMap = new Map();
18
+ PanInteraction.prototype.updateCallback = jest.fn();
19
+ PanInteraction.prototype.removeCallback = jest.fn();
20
+ PanInteraction.prototype.destroy = jest.fn();
21
+ ClickInteraction.prototype.callbackMap = new Map();
22
+ ClickInteraction.prototype.updateCallback = jest.fn();
23
+ ClickInteraction.prototype.removeCallback = jest.fn();
24
+ ClickInteraction.prototype.destroy = jest.fn();
25
+ ZoomInteraction.prototype.callbackMap = new Map();
26
+ ZoomInteraction.prototype.updateCallback = jest.fn();
27
+ ZoomInteraction.prototype.removeCallback = jest.fn();
28
+ ZoomInteraction.prototype.destroy = jest.fn();
29
+ DragNodeInteraction.prototype.callbackMap = new Map();
30
+ DragNodeInteraction.prototype.updateCallback = jest.fn();
31
+ DragNodeInteraction.prototype.removeCallback = jest.fn();
32
+ DragNodeInteraction.prototype.destroy = jest.fn();
33
+ DrawInteraction.prototype.callbackMap = new Map();
34
+ DrawInteraction.prototype.updateCallback = jest.fn();
35
+ DrawInteraction.prototype.removeCallback = jest.fn();
36
+ DrawInteraction.prototype.destroy = jest.fn();
37
+ BoxSelectInteraction.prototype.callbackMap = new Map();
38
+ BoxSelectInteraction.prototype.updateCallback = jest.fn();
39
+ BoxSelectInteraction.prototype.removeCallback = jest.fn();
40
+ BoxSelectInteraction.prototype.destroy = jest.fn();
41
+ LassoInteraction.prototype.callbackMap = new Map();
42
+ LassoInteraction.prototype.updateCallback = jest.fn();
43
+ LassoInteraction.prototype.removeCallback = jest.fn();
44
+ LassoInteraction.prototype.destroy = jest.fn();
15
45
  let mockDestroy;
16
46
  let destroyHoverInteraction;
17
47
  let destroyPanInteraction;
@@ -34,6 +64,12 @@ describe('InteractiveNvlWrapper', () => {
34
64
  jest.spyOn(NVL.prototype, 'destroy').mockImplementation(mockDestroy);
35
65
  jest.spyOn(HoverInteraction.prototype, 'destroy').mockImplementation(destroyHoverInteraction);
36
66
  jest.spyOn(PanInteraction.prototype, 'destroy').mockImplementation(destroyPanInteraction);
67
+ jest.spyOn(ClickInteraction.prototype, 'destroy').mockImplementation(jest.fn());
68
+ jest.spyOn(ZoomInteraction.prototype, 'destroy').mockImplementation(jest.fn());
69
+ jest.spyOn(DragNodeInteraction.prototype, 'destroy').mockImplementation(jest.fn());
70
+ jest.spyOn(DrawInteraction.prototype, 'destroy').mockImplementation(jest.fn());
71
+ jest.spyOn(BoxSelectInteraction.prototype, 'destroy').mockImplementation(jest.fn());
72
+ jest.spyOn(LassoInteraction.prototype, 'destroy').mockImplementation(jest.fn());
37
73
  });
38
74
  afterEach(() => {
39
75
  jest.clearAllMocks();
@@ -75,6 +111,148 @@ describe('InteractiveNvlWrapper', () => {
75
111
  expect(BoxSelectInteraction).not.toHaveBeenCalled();
76
112
  expect(LassoInteraction).not.toHaveBeenCalled();
77
113
  });
114
+ test.each([
115
+ {
116
+ name: 'HoverInteraction',
117
+ InteractionClass: HoverInteraction,
118
+ callback: { onHover: jest.fn() },
119
+ options: { drawShadowOnHover: true }
120
+ },
121
+ {
122
+ name: 'ClickInteraction with onCanvasClick',
123
+ InteractionClass: ClickInteraction,
124
+ callback: { onCanvasClick: jest.fn() },
125
+ options: { selectOnClick: false }
126
+ },
127
+ {
128
+ name: 'ClickInteraction with onCanvasDoubleClick',
129
+ InteractionClass: ClickInteraction,
130
+ callback: { onCanvasDoubleClick: jest.fn() },
131
+ options: { selectOnClick: false }
132
+ },
133
+ {
134
+ name: 'ClickInteraction with onCanvasRightClick',
135
+ InteractionClass: ClickInteraction,
136
+ callback: { onCanvasRightClick: jest.fn() },
137
+ options: { selectOnClick: false }
138
+ },
139
+ {
140
+ name: 'ClickInteraction with onNodeClick',
141
+ InteractionClass: ClickInteraction,
142
+ callback: { onNodeClick: jest.fn() },
143
+ options: { selectOnClick: true }
144
+ },
145
+ {
146
+ name: 'ClickInteraction with onNodeDoubleClick',
147
+ InteractionClass: ClickInteraction,
148
+ callback: { onNodeDoubleClick: jest.fn() },
149
+ options: { selectOnClick: false }
150
+ },
151
+ {
152
+ name: 'ClickInteraction with onNodeRightClick',
153
+ InteractionClass: ClickInteraction,
154
+ callback: { onNodeRightClick: jest.fn() },
155
+ options: { selectOnClick: false }
156
+ },
157
+ {
158
+ name: 'ClickInteraction with onRelationshipClick',
159
+ InteractionClass: ClickInteraction,
160
+ callback: { onRelationshipClick: jest.fn() },
161
+ options: { selectOnClick: false }
162
+ },
163
+ {
164
+ name: 'ClickInteraction with onRelationshipDoubleClick',
165
+ InteractionClass: ClickInteraction,
166
+ callback: { onRelationshipDoubleClick: jest.fn() },
167
+ options: { selectOnClick: false }
168
+ },
169
+ {
170
+ name: 'ClickInteraction with onRelationshipRightClick',
171
+ InteractionClass: ClickInteraction,
172
+ callback: { onRelationshipRightClick: jest.fn() },
173
+ options: { selectOnClick: false }
174
+ },
175
+ {
176
+ name: 'PanInteraction',
177
+ InteractionClass: PanInteraction,
178
+ callback: { onPan: jest.fn() },
179
+ options: {}
180
+ },
181
+ {
182
+ name: 'ZoomInteraction',
183
+ InteractionClass: ZoomInteraction,
184
+ callback: { onZoom: jest.fn() },
185
+ options: {}
186
+ },
187
+ {
188
+ name: 'DragNodeInteraction with onDrag',
189
+ InteractionClass: DragNodeInteraction,
190
+ callback: { onDrag: jest.fn() },
191
+ options: { selectOnRelease: false }
192
+ },
193
+ {
194
+ name: 'DragNodeInteraction with onDragStart',
195
+ InteractionClass: DragNodeInteraction,
196
+ callback: { onDragStart: jest.fn() },
197
+ options: {}
198
+ },
199
+ {
200
+ name: 'DragNodeInteraction with onDragEnd',
201
+ InteractionClass: DragNodeInteraction,
202
+ callback: { onDragEnd: jest.fn() },
203
+ options: {}
204
+ },
205
+ {
206
+ name: 'DrawInteraction with onHoverNodeMargin',
207
+ InteractionClass: DrawInteraction,
208
+ callback: { onHoverNodeMargin: jest.fn() },
209
+ options: { excludeNodeMargin: false }
210
+ },
211
+ {
212
+ name: 'DrawInteraction with onDrawStarted',
213
+ InteractionClass: DrawInteraction,
214
+ callback: { onDrawStarted: jest.fn() },
215
+ options: {}
216
+ },
217
+ {
218
+ name: 'DrawInteraction with onDrawEnded',
219
+ InteractionClass: DrawInteraction,
220
+ callback: { onDrawEnded: jest.fn() },
221
+ options: {}
222
+ },
223
+ {
224
+ name: 'BoxSelectInteraction with onBoxStarted',
225
+ InteractionClass: BoxSelectInteraction,
226
+ callback: { onBoxStarted: jest.fn() },
227
+ options: {}
228
+ },
229
+ {
230
+ name: 'BoxSelectInteraction with onBoxSelect',
231
+ InteractionClass: BoxSelectInteraction,
232
+ callback: { onBoxSelect: jest.fn() },
233
+ options: {}
234
+ },
235
+ {
236
+ name: 'LassoInteraction with onLassoStarted',
237
+ InteractionClass: LassoInteraction,
238
+ callback: { onLassoStarted: jest.fn() },
239
+ options: {}
240
+ },
241
+ {
242
+ name: 'LassoInteraction with onLassoSelect',
243
+ InteractionClass: LassoInteraction,
244
+ callback: { onLassoSelect: jest.fn() },
245
+ options: {}
246
+ }
247
+ ])('initialises $name when its callback is provided', ({ InteractionClass, callback, options }) => {
248
+ const myNvlRef = createRef();
249
+ render(_jsx(InteractiveNvlWrapper, { nodes: [], rels: [], ref: myNvlRef, mouseEventCallbacks: callback, interactionOptions: options }));
250
+ act(() => {
251
+ mockOnInitialization?.();
252
+ });
253
+ expect(InteractionClass).toHaveBeenCalledWith(myNvlRef.current, options);
254
+ expect(InteractionClass).toHaveBeenCalledTimes(1);
255
+ });
78
256
  test('does not attempt to initialize interaction handlers if NVL container is not available', () => {
79
257
  const myNvlRef = createRef();
80
258
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -45,4 +45,4 @@ export interface BasicReactWrapperProps {
45
45
  *
46
46
  * For examples, head to the {@link https://neo4j.com/docs/nvl/current/react-wrappers/#_basic_react_wrapper Basic React wrapper documentation page}.
47
47
  */
48
- export declare const BasicNvlWrapper: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<Omit<BasicReactWrapperProps & HTMLProps<HTMLDivElement>, "ref"> & import("react").RefAttributes<Partial<Pick<NVL, "performance" | "restart" | "destroy" | "addAndUpdateElementsInGraph" | "getSelectedNodes" | "getSelectedRelationships" | "removeNodesWithIds" | "removeRelationshipsWithIds" | "getNodes" | "getRelationships" | "getNodeById" | "getRelationshipById" | "getPositionById" | "getCurrentOptions" | "deselectAll" | "fit" | "resetZoom" | "setRenderer" | "setDisableWebGL" | "pinNode" | "unPinNode" | "setLayout" | "setLayoutOptions" | "getNodesOnScreen" | "getNodePositions" | "setNodePositions" | "isLayoutMoving" | "saveToFile" | "getImageDataUrl" | "saveFullGraphToLargeFile" | "setZoom" | "getScale" | "getPan" | "getHits" | "getContainer">>>>>;
48
+ export declare const BasicNvlWrapper: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<Omit<BasicReactWrapperProps & HTMLProps<HTMLDivElement>, "ref"> & import("react").RefAttributes<Partial<Pick<NVL, "restart" | "destroy" | "addAndUpdateElementsInGraph" | "getSelectedNodes" | "getSelectedRelationships" | "removeNodesWithIds" | "removeRelationshipsWithIds" | "getNodes" | "getRelationships" | "getNodeById" | "getRelationshipById" | "getPositionById" | "getCurrentOptions" | "deselectAll" | "fit" | "resetZoom" | "setRenderer" | "setDisableWebGL" | "pinNode" | "unPinNode" | "setLayout" | "setLayoutOptions" | "getNodesOnScreen" | "getNodePositions" | "setNodePositions" | "isLayoutMoving" | "saveToFile" | "saveToSvg" | "getImageDataUrl" | "saveFullGraphToLargeFile" | "getZoomLimits" | "setZoom" | "getScale" | "getPan" | "getHits" | "getContainer">>>>>;
@@ -14,6 +14,8 @@ import { useDeepCompareEffect } from '../utils/hooks';
14
14
  */
15
15
  export const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOptions, nvlCallbacks = {}, nvlOptions = {}, positions = [], zoom, pan, onInitializationError, ...nvlEvents }, ref) => {
16
16
  const nvlRef = useRef(null);
17
+ const prevZoomRef = useRef(undefined);
18
+ const prevPanRef = useRef(undefined);
17
19
  useImperativeHandle(ref, () => {
18
20
  const nvlMethods = Object.getOwnPropertyNames(NVL.prototype);
19
21
  return nvlMethods.reduce((current, method) => ({
@@ -127,15 +129,21 @@ export const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOpt
127
129
  if (nvlRef.current === null) {
128
130
  return;
129
131
  }
130
- if (zoom !== undefined && pan !== undefined) {
132
+ const currentZoom = prevZoomRef.current;
133
+ const currentPan = prevPanRef.current;
134
+ const zoomHasChanged = zoom !== undefined && zoom !== currentZoom;
135
+ const panHasChanged = pan !== undefined && (pan.x !== currentPan?.x || pan.y !== currentPan.y);
136
+ if (zoomHasChanged && panHasChanged) {
131
137
  nvlRef.current.setZoomAndPan(zoom, pan.x, pan.y);
132
138
  }
133
- else if (zoom !== undefined) {
139
+ else if (zoomHasChanged) {
134
140
  nvlRef.current.setZoom(zoom);
135
141
  }
136
- else if (pan !== undefined) {
142
+ else if (panHasChanged) {
137
143
  nvlRef.current.setPan(pan.x, pan.y);
138
144
  }
145
+ prevZoomRef.current = zoom;
146
+ prevPanRef.current = pan;
139
147
  }, [zoom, pan]);
140
148
  return _jsx("div", { id: BASIC_WRAPPER_ID, ref: containerRef, style: { height: '100%', outline: '0' }, ...nvlEvents });
141
149
  }));
@@ -18,9 +18,11 @@ export const InteractionHandlers = ({ nvlRef, mouseEventCallbacks, interactionOp
18
18
  useInteraction(ClickInteraction, clickInteraction, mouseEventCallbacks.onRelationshipDoubleClick, 'onRelationshipDoubleClick', nvlRef, interactionOptions);
19
19
  useInteraction(ClickInteraction, clickInteraction, mouseEventCallbacks.onRelationshipRightClick, 'onRelationshipRightClick', nvlRef, interactionOptions);
20
20
  useInteraction(ClickInteraction, clickInteraction, mouseEventCallbacks.onCanvasClick, 'onCanvasClick', nvlRef, interactionOptions);
21
+ useInteraction(ClickInteraction, clickInteraction, mouseEventCallbacks.onCanvasDoubleClick, 'onCanvasDoubleClick', nvlRef, interactionOptions);
21
22
  useInteraction(ClickInteraction, clickInteraction, mouseEventCallbacks.onCanvasRightClick, 'onCanvasRightClick', nvlRef, interactionOptions);
22
23
  useInteraction(PanInteraction, panInteraction, mouseEventCallbacks.onPan, 'onPan', nvlRef, interactionOptions);
23
24
  useInteraction(ZoomInteraction, zoomInteraction, mouseEventCallbacks.onZoom, 'onZoom', nvlRef, interactionOptions);
25
+ useInteraction(ZoomInteraction, zoomInteraction, mouseEventCallbacks.onZoomAndPan, 'onZoomAndPan', nvlRef, interactionOptions);
24
26
  useInteraction(DragNodeInteraction, dragNodeInteraction, mouseEventCallbacks.onDrag, 'onDrag', nvlRef, interactionOptions);
25
27
  useInteraction(DragNodeInteraction, dragNodeInteraction, mouseEventCallbacks.onDragStart, 'onDragStart', nvlRef, interactionOptions);
26
28
  useInteraction(DragNodeInteraction, dragNodeInteraction, mouseEventCallbacks.onDragEnd, 'onDragEnd', nvlRef, interactionOptions);
@@ -2,4 +2,4 @@ import type NVL from '@neo4j-nvl/base';
2
2
  import type { MutableRefObject } from 'react';
3
3
  import type { InteractionOptions, MouseEvent, MouseInteraction, MouseInteractionModule } from './types';
4
4
  export declare const destroyInteraction: (interactionRef: MutableRefObject<MouseInteraction | null>) => void;
5
- export declare const useInteraction: (Interaction: MouseInteractionModule, interactionRef: MutableRefObject<MouseInteraction | null>, callback: boolean | ((...args: unknown[]) => void) | undefined, eventName: MouseEvent, nvlRef: MutableRefObject<NVL | null>, interactionOptions: InteractionOptions) => void;
5
+ export declare const useInteraction: (Interaction: MouseInteractionModule, interactionRef: MutableRefObject<MouseInteraction | null>, callback: ((...args: unknown[]) => void) | boolean | undefined, eventName: MouseEvent, nvlRef: MutableRefObject<NVL | null>, interactionOptions: InteractionOptions) => void;
@@ -1,4 +1,4 @@
1
- import type { BoxSelectInteraction, BoxSelectInteractionCallbacks, BoxSelectInteractionOptions, ClickInteraction, ClickInteractionCallbacks, ClickInteractionOptions, DragNodeInteraction, DragNodeInteractionCallbacks, DrawInteraction, DrawInteractionCallbacks, DrawInteractionOptions, HoverInteraction, HoverInteractionCallbacks, HoverInteractionOptions, LassoInteraction, LassoInteractionCallbacks, LassoInteractionOptions, PanInteraction, PanInteractionCallbacks, PanInteractionOptions, ZoomInteraction, ZoomInteractionCallbacks } from '@neo4j-nvl/interaction-handlers';
1
+ import type { BoxSelectInteraction, BoxSelectInteractionCallbacks, BoxSelectInteractionOptions, ClickInteraction, ClickInteractionCallbacks, ClickInteractionOptions, DragNodeInteraction, DragNodeInteractionCallbacks, DrawInteraction, DrawInteractionCallbacks, DrawInteractionOptions, HoverInteraction, HoverInteractionCallbacks, HoverInteractionOptions, LassoInteraction, LassoInteractionCallbacks, LassoInteractionOptions, PanInteraction, PanInteractionCallbacks, PanInteractionOptions, ZoomInteraction, ZoomInteractionCallbacks, ZoomInteractionOptions } from '@neo4j-nvl/interaction-handlers';
2
2
  import type { BasicReactWrapperProps } from '../basic-wrapper/BasicNvlWrapper';
3
3
  export type MouseInteractionModule = typeof HoverInteraction | typeof ClickInteraction | typeof PanInteraction | typeof ZoomInteraction | typeof DragNodeInteraction | typeof DrawInteraction | typeof BoxSelectInteraction | typeof LassoInteraction;
4
4
  export type MouseInteraction = HoverInteraction | ClickInteraction | PanInteraction | ZoomInteraction | DragNodeInteraction | DrawInteraction | BoxSelectInteraction | LassoInteraction;
@@ -11,7 +11,7 @@ export type MouseEventCallbacks = ClickInteractionCallbacks & DragNodeInteractio
11
11
  * Collection of interaction options that can be used with
12
12
  * the {@link InteractiveNvlWrapper} component.
13
13
  */
14
- export type InteractionOptions = ClickInteractionOptions & BoxSelectInteractionOptions & HoverInteractionOptions & PanInteractionOptions & LassoInteractionOptions & DrawInteractionOptions;
14
+ export type InteractionOptions = ClickInteractionOptions & BoxSelectInteractionOptions & HoverInteractionOptions & PanInteractionOptions & ZoomInteractionOptions & LassoInteractionOptions & DrawInteractionOptions;
15
15
  /**
16
16
  * The events that can be passed to the {@link MouseEventCallbacks} object
17
17
  * to turn on/off certain events for the {@link InteractiveNvlWrapper} component.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neo4j-nvl/react",
3
- "version": "1.0.0-b4712632",
3
+ "version": "1.0.0-bb8a655f",
4
4
  "main": "lib/index.js",
5
5
  "homepage": "https://neo4j.com/docs/nvl/current/",
6
6
  "license": "SEE LICENSE IN 'LICENSE.txt'",
@@ -27,22 +27,22 @@
27
27
  "lib"
28
28
  ],
29
29
  "devDependencies": {
30
- "@testing-library/dom": "^10.4.0",
31
- "@testing-library/jest-dom": "^5.16.5",
32
- "@testing-library/react": "^16.3.0",
33
- "@types/lodash": "4.14.202",
34
- "@types/react": "^18.2.58",
35
- "react": "^19.1.1",
36
- "react-dom": "^19.1.1"
30
+ "@testing-library/dom": "10.4.1",
31
+ "@testing-library/jest-dom": "5.17.0",
32
+ "@testing-library/react": "16.3.0",
33
+ "@types/lodash": "4.17.21",
34
+ "@types/react": "18.3.27",
35
+ "react": "19.2.1",
36
+ "react-dom": "19.2.1"
37
37
  },
38
38
  "dependencies": {
39
- "@neo4j-nvl/base": "1.0.0-b4712632",
40
- "@neo4j-nvl/interaction-handlers": "1.0.0-b4712632",
41
- "lodash": "4.17.21"
39
+ "@neo4j-nvl/base": "1.0.0-bb8a655f",
40
+ "@neo4j-nvl/interaction-handlers": "1.0.0-bb8a655f",
41
+ "lodash": "4.17.23"
42
42
  },
43
43
  "peerDependencies": {
44
- "react": "^18.0.0 || ^19.0.0",
45
- "react-dom": "^18.0.0 || ^19.0.0"
44
+ "react": "18.0.0 || ^19.0.0",
45
+ "react-dom": "18.0.0 || ^19.0.0"
46
46
  },
47
47
  "stableVersion": "1.0.0"
48
48
  }