@neo4j-nvl/react 0.1.14 → 0.2.2

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/README.md CHANGED
@@ -1,3 +1 @@
1
- ### react
2
-
3
- A React wrapper that provides the NVL class as a React component.
1
+ This module exports a collection of React components that are build around the NVL class. They act as wrappers around the NVL class that provide a React interface to the NVL class.
@@ -1,73 +1,115 @@
1
- import { NamedExoticComponent } from 'react';
2
- import { NvlOptions, Node, Relationship, ExternalCallbacks, LayoutOptions, Layout } from '@neo4j-nvl/core';
1
+ import React from 'react';
2
+ import type { NvlOptions, Node, Relationship, LayoutOptions, Layout, ExternalCallbacks } from '@neo4j-nvl/core';
3
+ import NVL from '@neo4j-nvl/core';
4
+ type IncludeMethods<T> = Pick<T, {
5
+ [K in keyof T]: T[K] extends (_: unknown) => unknown ? K : never;
6
+ }[keyof T]>;
3
7
  /**
4
- * A basic React wrapper for the NVL class.
8
+ * The properties that can be passed to the {@link BasicNvlWrapper} component.
9
+ */
10
+ export interface BasicReactWrapperProps {
11
+ /** The nodes of the graph of type Node[] */
12
+ nodes: Node[];
13
+ /** The rels of the graph of type Relationship[] */
14
+ rels: Relationship[];
15
+ /** The layout, can be 'forceDirected' or 'hierarchical' */
16
+ layout?: Layout;
17
+ /** Options for the current layout */
18
+ layoutOptions?: LayoutOptions;
19
+ /** an Object containing functions for callbacks on certain actions */
20
+ nvlCallbacks?: ExternalCallbacks;
21
+ /** An object containing options for the Nvl instance */
22
+ nvlOptions?: NvlOptions;
23
+ /** A callback to handle any errors that happen during NVL initialization */
24
+ onInitializationError?: (error: unknown) => void;
25
+ /** Any events that should be passed to the NVL instance */
26
+ nvlEvents?: {
27
+ [key: string]: (event: unknown) => void;
28
+ };
29
+ }
30
+ /**
31
+ * A basic React wrapper for the {@link NVL} class.
32
+ * It takes the {@link BasicReactWrapperProps} as properties which are passed to the NVL constructor.
33
+ * Any changes in properties will be reflected in the NVL instance by calling the corresponding methods.
5
34
  *
6
35
  * @example
7
- * This is the most basic way of using the wrapper. It will create a new NVL instance with the given nodes and relationships.
8
36
  * ```tsx
9
37
  * <BasicNvlWrapper
10
- * nodes={[{ id: 0 }, { id: 1 }, { id: 2 }]}
11
- * rels={[{ from: 0, to: 1, id: 10 }, { from: 0, to: 2, id: 11 }]}
38
+ * nodes={nodes}
39
+ * rels={relationships}
40
+ * nvlOptions={options}
41
+ * nvlCallbacks={callbacks}
12
42
  * />
13
43
  * ```
14
44
  *
45
+ * would be equivalent to
46
+ *
47
+ * ```tsx
48
+ * const myNvl = new NVL(container, nodes, relationships, options, callbacks)
49
+ * ```
50
+ *
15
51
  * @example
16
- * This is a more advanced example, where the nodes and relationships are updated dynamically.
52
+ * When nodes and/or relationships are updated in the React wrapper, the NVL instance will be updated accordingly:
53
+ *
17
54
  * ```tsx
18
- * const [nodes, setNodes] = useState<Node[]>([{ id: 0 }, { id: 1 }, { id: 2 }])
19
- * const [rels, setRels] = useState<Relationship[]>([{ from: 0, to: 1, id: 10 }, { from: 0, to: 2, id: 11 }])
55
+ * const [nodes, setNodes] = useState<Node[]>([{ id: '0' }, { id: '1' }])
20
56
  *
21
- * const addNode = () => {
57
+ * const addElements = () => {
22
58
  * const newNodes = [...nodes, { id: nodes.length }]
23
59
  * setNodes(newNodes)
24
60
  * }
25
61
  *
26
- * const addRel = () => {
27
- * const newRels = [...rels, { from: 0, to: nodes.length - 1, id: rels.length }]
28
- * setRels(newRels)
29
- * }
30
- *
31
62
  * <div>
32
- * <BasicNvlWrapper
33
- * nodes={nodes}
34
- * rels={rels}
35
- * />
36
- * <button onClick={addNode}>Add Node</button>
37
- * <button onClick={addRel}>Add Relationship</button>
63
+ * <BasicNvlWrapper nodes={nodes} />
64
+ * <button onClick={addElements}>Add Graph Elements</button>
38
65
  * </div>
39
66
  * ```
40
67
  *
68
+ * would be equivalent to
69
+ *
70
+ * ```tsx
71
+ * const myNvl = new NVL(container, nodes, [])
72
+ * myButton.addEventListener('click', () => {
73
+ * myNvl.addAndUpdateElementsInGraph(newNodes, [])
74
+ * })
75
+ * ```
76
+ *
41
77
  * @example
42
- * This is an example of how to use a reference of NVL to call
43
- * NVL methods from inside the React wrapper.
78
+ * If you want to access the NVL class outside of the React wrapper you can use a
79
+ * reference of NVL to call its methods:
44
80
  * ```tsx
45
- * const nvlRef = useRef()
81
+ * const nvlRef = useRef<NVL>()
46
82
  *
47
83
  * <div>
48
84
  * <BasicNvlWrapper
49
- * nodes={[{ id: 0 }, { id: 1 }, { id: 2 }]}
50
- * rels={[{ from: 0, to: 1, id: 10 }, { from: 0, to: 2, id: 11 }]}
85
+ * nodes={[{ id: '0' }, { id: '1' }]}
86
+ * rels={[{ from: '0', to: '1', id: '10' }]}
51
87
  * ref={nvlRef}
52
88
  * />
53
89
  * <button onClick={() => nvlRef.current?.zoomToNodes([0, 1])}>Zoom to Nodes</button>
54
90
  * </div>
55
91
  * ```
92
+ *
93
+ * @example
94
+ * If you want to pass events to the NVL instance you can do so by passing them as properties to the React wrapper:
95
+ *
96
+ * ```tsx
97
+ * <BasicNvlWrapper
98
+ * nodes={nodes}
99
+ * rels={relationships}
100
+ * onClick={(event) => console.log(event)}
101
+ * />
102
+ * ```
103
+ *
104
+ * would be equivalent to
105
+ *
106
+ * ```tsx
107
+ * const myNvl = new NVL(container, nodes, relationships)
108
+ * container.addEventListener('click', (event) => console.log(event))
109
+ * ```
110
+ *
111
+ * For more about interactivity, check out the {@link NVL}.hittest method,
112
+ * the Interaction Handlers module module and the {@link InteractiveNvlWrapper}.
56
113
  */
57
- declare const BasicNvlWrapper: NamedExoticComponent<{
58
- /** The nodes of the graph of type Node[] */
59
- nodes: Node[];
60
- /** The rels of the graph of type Relationship[] */
61
- rels: Relationship[];
62
- /** The layout, can be 'forceDirected' or 'hierarchical' */
63
- layout?: Layout;
64
- /** Options for the current layout */
65
- layoutOptions?: LayoutOptions;
66
- /** an Object containing functions for callbacks on certain actions */
67
- nvlCallbacks?: ExternalCallbacks;
68
- /** An object containing options for the NVL instance */
69
- nvlOptions?: NvlOptions;
70
- /** A callback to handle any errors that happen during NVL initialization */
71
- onInitializationError?: (error: unknown) => void;
72
- }>;
73
- export { BasicNvlWrapper };
114
+ export declare const BasicNvlWrapper: React.MemoExoticComponent<React.ForwardRefExoticComponent<BasicReactWrapperProps & React.RefAttributes<IncludeMethods<NVL>>>>;
115
+ export {};
@@ -1,72 +1,119 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
1
12
  import React, { useEffect, useState, useRef, forwardRef, useImperativeHandle, memo } from 'react';
2
13
  import NVL from '@neo4j-nvl/core';
3
14
  import { getMapDifferences, getNodeAttributeDifferences } from '../utils/graphComparison';
4
15
  import { useDeepCompareEffect } from '../utils/hooks';
5
16
  /**
6
- * A basic React wrapper for the NVL class.
17
+ * A basic React wrapper for the {@link NVL} class.
18
+ * It takes the {@link BasicReactWrapperProps} as properties which are passed to the NVL constructor.
19
+ * Any changes in properties will be reflected in the NVL instance by calling the corresponding methods.
7
20
  *
8
21
  * @example
9
- * This is the most basic way of using the wrapper. It will create a new NVL instance with the given nodes and relationships.
10
22
  * ```tsx
11
23
  * <BasicNvlWrapper
12
- * nodes={[{ id: 0 }, { id: 1 }, { id: 2 }]}
13
- * rels={[{ from: 0, to: 1, id: 10 }, { from: 0, to: 2, id: 11 }]}
24
+ * nodes={nodes}
25
+ * rels={relationships}
26
+ * nvlOptions={options}
27
+ * nvlCallbacks={callbacks}
14
28
  * />
15
29
  * ```
16
30
  *
31
+ * would be equivalent to
32
+ *
33
+ * ```tsx
34
+ * const myNvl = new NVL(container, nodes, relationships, options, callbacks)
35
+ * ```
36
+ *
17
37
  * @example
18
- * This is a more advanced example, where the nodes and relationships are updated dynamically.
38
+ * When nodes and/or relationships are updated in the React wrapper, the NVL instance will be updated accordingly:
39
+ *
19
40
  * ```tsx
20
- * const [nodes, setNodes] = useState<Node[]>([{ id: 0 }, { id: 1 }, { id: 2 }])
21
- * const [rels, setRels] = useState<Relationship[]>([{ from: 0, to: 1, id: 10 }, { from: 0, to: 2, id: 11 }])
41
+ * const [nodes, setNodes] = useState<Node[]>([{ id: '0' }, { id: '1' }])
22
42
  *
23
- * const addNode = () => {
43
+ * const addElements = () => {
24
44
  * const newNodes = [...nodes, { id: nodes.length }]
25
45
  * setNodes(newNodes)
26
46
  * }
27
47
  *
28
- * const addRel = () => {
29
- * const newRels = [...rels, { from: 0, to: nodes.length - 1, id: rels.length }]
30
- * setRels(newRels)
31
- * }
32
- *
33
48
  * <div>
34
- * <BasicNvlWrapper
35
- * nodes={nodes}
36
- * rels={rels}
37
- * />
38
- * <button onClick={addNode}>Add Node</button>
39
- * <button onClick={addRel}>Add Relationship</button>
49
+ * <BasicNvlWrapper nodes={nodes} />
50
+ * <button onClick={addElements}>Add Graph Elements</button>
40
51
  * </div>
41
52
  * ```
42
53
  *
54
+ * would be equivalent to
55
+ *
56
+ * ```tsx
57
+ * const myNvl = new NVL(container, nodes, [])
58
+ * myButton.addEventListener('click', () => {
59
+ * myNvl.addAndUpdateElementsInGraph(newNodes, [])
60
+ * })
61
+ * ```
62
+ *
43
63
  * @example
44
- * This is an example of how to use a reference of NVL to call
45
- * NVL methods from inside the React wrapper.
64
+ * If you want to access the NVL class outside of the React wrapper you can use a
65
+ * reference of NVL to call its methods:
46
66
  * ```tsx
47
- * const nvlRef = useRef()
67
+ * const nvlRef = useRef<NVL>()
48
68
  *
49
69
  * <div>
50
70
  * <BasicNvlWrapper
51
- * nodes={[{ id: 0 }, { id: 1 }, { id: 2 }]}
52
- * rels={[{ from: 0, to: 1, id: 10 }, { from: 0, to: 2, id: 11 }]}
71
+ * nodes={[{ id: '0' }, { id: '1' }]}
72
+ * rels={[{ from: '0', to: '1', id: '10' }]}
53
73
  * ref={nvlRef}
54
74
  * />
55
75
  * <button onClick={() => nvlRef.current?.zoomToNodes([0, 1])}>Zoom to Nodes</button>
56
76
  * </div>
57
77
  * ```
78
+ *
79
+ * @example
80
+ * If you want to pass events to the NVL instance you can do so by passing them as properties to the React wrapper:
81
+ *
82
+ * ```tsx
83
+ * <BasicNvlWrapper
84
+ * nodes={nodes}
85
+ * rels={relationships}
86
+ * onClick={(event) => console.log(event)}
87
+ * />
88
+ * ```
89
+ *
90
+ * would be equivalent to
91
+ *
92
+ * ```tsx
93
+ * const myNvl = new NVL(container, nodes, relationships)
94
+ * container.addEventListener('click', (event) => console.log(event))
95
+ * ```
96
+ *
97
+ * For more about interactivity, check out the {@link NVL}.hittest method,
98
+ * the Interaction Handlers module module and the {@link InteractiveNvlWrapper}.
58
99
  */
59
- const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOptions, nvlCallbacks = {}, nvlOptions, onInitializationError }, ref) => {
100
+ export const BasicNvlWrapper = memo(forwardRef((_a, ref) => {
101
+ var { nodes, rels, layout, layoutOptions, nvlCallbacks = {}, nvlOptions = {}, onInitializationError } = _a, nvlEvents = __rest(_a, ["nodes", "rels", "layout", "layoutOptions", "nvlCallbacks", "nvlOptions", "onInitializationError"]);
60
102
  useImperativeHandle(ref, () => {
61
103
  const nvlMethods = Object.getOwnPropertyNames(NVL.prototype);
62
- return nvlMethods.reduce((current, method) => (Object.assign(Object.assign({}, current), { [method]: (...args) => nvl && nvl[method](...args) })), {});
104
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
105
+ // @ts-ignore revisit better typing for reduce function
106
+ return nvlMethods.reduce((current, method) => (Object.assign(Object.assign({}, current), {
107
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
108
+ // @ts-ignore suppress the type casting error on spreading
109
+ [method]: (...args) => nvlRef && nvlRef.current && nvlRef.current[method](...args) })), {});
63
110
  });
64
111
  const containerRef = useRef();
65
- const [nvl, setNvl] = useState();
112
+ const nvlRef = useRef();
66
113
  const [currentNodes, setCurrentNodes] = useState(nodes);
67
114
  const [currentRels, setCurrentRels] = useState(rels);
68
115
  useEffect(() => {
69
- if (!nvl) {
116
+ if (!nvlRef.current) {
70
117
  const combinedOptions = Object.assign(Object.assign({}, nvlOptions), { layoutOptions });
71
118
  if (layout) {
72
119
  combinedOptions.layout = layout;
@@ -74,7 +121,7 @@ const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOptions, n
74
121
  let newNvl;
75
122
  try {
76
123
  newNvl = new NVL(containerRef.current, currentNodes, currentRels, combinedOptions, nvlCallbacks);
77
- setNvl(newNvl);
124
+ nvlRef.current = newNvl;
78
125
  setCurrentRels(rels);
79
126
  setCurrentNodes(nodes);
80
127
  }
@@ -92,29 +139,29 @@ const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOptions, n
92
139
  }
93
140
  }, []);
94
141
  useEffect(() => {
95
- if (!nvl)
142
+ if (!nvlRef.current) {
96
143
  return;
144
+ }
97
145
  const nodeChanges = getMapDifferences(currentNodes, nodes);
98
146
  const nodeDiff = getNodeAttributeDifferences(currentNodes, nodes);
99
147
  const relChanges = getMapDifferences(currentRels, rels);
100
148
  setCurrentRels(rels);
101
149
  setCurrentNodes(nodes);
102
- nvl.updateGraph([...nodeChanges.added, ...nodeDiff], [...relChanges.added, ...relChanges.updated]);
103
- nvl.removeRelationshipsWithIds(relChanges.removed.map((r) => r.id));
104
- nvl.removeNodesWithIds(nodeChanges.removed.map((n) => n.id));
150
+ nvlRef.current && nvlRef.current.addAndUpdateElementsInGraph([...nodeChanges.added, ...nodeDiff], [...relChanges.added, ...relChanges.updated]);
151
+ nvlRef.current && nvlRef.current.removeRelationshipsWithIds(relChanges.removed.map((r) => r.id));
152
+ nvlRef.current && nvlRef.current.removeNodesWithIds(nodeChanges.removed.map((n) => n.id));
105
153
  }, [nodes, rels]);
106
154
  useEffect(() => {
107
- nvl === null || nvl === void 0 ? void 0 : nvl.setLayout(layout);
155
+ nvlRef.current && nvlRef.current.setLayout(layout);
108
156
  }, [layout]);
109
157
  useDeepCompareEffect(() => {
110
- nvl === null || nvl === void 0 ? void 0 : nvl.setLayoutOptions(layoutOptions);
158
+ nvlRef.current && nvlRef.current.setLayoutOptions(layoutOptions);
111
159
  }, [layoutOptions]);
112
160
  useEffect(() => {
113
- nvl === null || nvl === void 0 ? void 0 : nvl.setUseWebGLRenderer(nvlOptions === null || nvlOptions === void 0 ? void 0 : nvlOptions.useWebGL);
114
- }, [nvlOptions === null || nvlOptions === void 0 ? void 0 : nvlOptions.useWebGL]);
161
+ nvlRef.current && nvlRef.current.setUseWebGLRenderer(nvlOptions.useWebGL);
162
+ }, [nvlOptions.useWebGL]);
115
163
  useEffect(() => {
116
- nvl === null || nvl === void 0 ? void 0 : nvl.setDisableWebGL(nvlOptions === null || nvlOptions === void 0 ? void 0 : nvlOptions.disableWebGL);
117
- }, [nvlOptions === null || nvlOptions === void 0 ? void 0 : nvlOptions.disableWebGL]);
118
- return React.createElement("div", { ref: containerRef, style: { height: '100%', outline: '0' } });
164
+ nvlRef.current && nvlRef.current.setDisableWebGL(nvlOptions.disableWebGL);
165
+ }, [nvlOptions.disableWebGL]);
166
+ return React.createElement("div", Object.assign({ ref: containerRef, style: { height: '100%', outline: '0' } }, nvlEvents));
119
167
  }));
120
- export { BasicNvlWrapper };
package/lib/index.d.ts CHANGED
@@ -1,2 +1,6 @@
1
1
  import { BasicNvlWrapper } from './basic-wrapper/BasicNvlWrapper';
2
- export { BasicNvlWrapper };
2
+ import type { BasicReactWrapperProps } from './basic-wrapper/BasicNvlWrapper';
3
+ import { InteractiveNvlWrapper } from './interactive-nvl-wrapper/InteractiveNvlWrapper';
4
+ import type { MouseEventCallbacks, InteractiveNvlWrapperProps, MouseEvent, InteractionOptions } from './interactive-nvl-wrapper/InteractiveNvlWrapper';
5
+ export { BasicNvlWrapper, InteractiveNvlWrapper };
6
+ export type { MouseEventCallbacks, MouseEvent, BasicReactWrapperProps, InteractionOptions, InteractiveNvlWrapperProps };
package/lib/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  import { BasicNvlWrapper } from './basic-wrapper/BasicNvlWrapper';
2
- export { BasicNvlWrapper };
2
+ import { InteractiveNvlWrapper } from './interactive-nvl-wrapper/InteractiveNvlWrapper';
3
+ export { BasicNvlWrapper, InteractiveNvlWrapper };
@@ -0,0 +1,56 @@
1
+ import type NVL from '@neo4j-nvl/core';
2
+ import type { ClickInteractionOptions, HoverInteractionOptions, MultiSelectInteractionOptions } from '@neo4j-nvl/interaction-handlers';
3
+ import React from 'react';
4
+ import type { BasicReactWrapperProps } from '../basic-wrapper/BasicNvlWrapper';
5
+ /**
6
+ * The events that can be passed to the {@link MouseEventCallbacks} object
7
+ * to turn on/off certain events for the {@link InteractiveNvlWrapper} component.
8
+ */
9
+ export type MouseEvent = 'onHover' | 'onNodeClick' | 'onNodeRightClick' | 'onNodeDoubleClick' | 'onRelationshipClick' | 'onRelationshipDoubleClick' | 'onRelationshipRightClick' | 'onCanvasClick' | 'onCanvasDoubleClick' | 'onCanvasRightClick' | 'onDrag' | 'onDraw' | 'onMultiSelect' | 'onPan' | 'onZoom';
10
+ /**
11
+ * An object to turn on/off certain events and provide callbacks for all {@link MouseEvent}s
12
+ * for the {@link InteractiveNvlWrapper} component.
13
+ */
14
+ export type MouseEventCallbacks<T extends string = MouseEvent> = {
15
+ [K in T]?: ((...args: unknown[]) => void) | boolean;
16
+ };
17
+ export type InteractionOptions = ClickInteractionOptions & MultiSelectInteractionOptions & HoverInteractionOptions;
18
+ /**
19
+ * The properties that can be passed to the {@link InteractiveNvlWrapper} component.
20
+ */
21
+ export interface InteractiveNvlWrapperProps extends BasicReactWrapperProps {
22
+ /** {@link MouseEventCallbacks} containing functions for callbacks on certain actions */
23
+ mouseEventCallbacks?: MouseEventCallbacks;
24
+ /** {@link InteractionOptions} for the underlying interaction handlers */
25
+ interactionOptions?: InteractionOptions;
26
+ }
27
+ /**
28
+ * This is a wrapper component that provides a basic set of interactions for the NVL.
29
+ * It is an extension of the {@link BasicNvlWrapper} component and incorporates the
30
+ * {@link InteractionHandlers} module's decorators to provide a set of interaction events.
31
+ *
32
+ * Events can be turned on and off by passing a callback function or a boolean value to the
33
+ * `mouseEventCallbacks` prop. The callback function will be called with the event's arguments
34
+ * when the event is triggered. If a boolean value is passed, the event will be turned on or off
35
+ * accordingly.
36
+ *
37
+ * @example
38
+ * ```tsx
39
+ * const [multiSelect, setMultiSelect] = useState(false)
40
+ * <>
41
+ * <button onClick={() => setMultiSelect(!multiSelect)}>
42
+ * {multiSelect ? 'Disable' : 'Enable'} multi-select
43
+ * </button>
44
+ * <InteractiveNvlWrapper
45
+ * nodes={nodes}
46
+ * rels={relationships}
47
+ * mouseEventCallbacks={{
48
+ * onHover: (element) => console.log(element),
49
+ * onNodeClick: (node) => console.log(node),
50
+ * onMultiSelect: multiSelect
51
+ * }}
52
+ * />
53
+ * </>
54
+ * ```
55
+ */
56
+ export declare const InteractiveNvlWrapper: React.MemoExoticComponent<React.ForwardRefExoticComponent<InteractiveNvlWrapperProps & React.RefAttributes<NVL>>>;
@@ -0,0 +1,104 @@
1
+ import { ClickInteraction, DragNodeInteraction, DrawInteraction, HoverInteraction, MultiSelectInteraction, PanInteraction, ZoomInteraction } from '@neo4j-nvl/interaction-handlers';
2
+ import React, { forwardRef, memo, useEffect, useRef } from 'react';
3
+ import { BasicNvlWrapper } from '../basic-wrapper/BasicNvlWrapper';
4
+ const options = {
5
+ selectOnClick: false,
6
+ drawShadowOnHover: true,
7
+ selectOnRelease: false
8
+ };
9
+ const destroyInteraction = (interactionRef) => {
10
+ interactionRef.current && interactionRef.current.destroy();
11
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
12
+ // @ts-ignore
13
+ interactionRef.current = null;
14
+ };
15
+ const useInteractionEffect = (interaction, interactionRef, callback, eventName, nvlRef, interactionOptions) => {
16
+ useEffect(() => {
17
+ setTimeout(() => {
18
+ if (callback) {
19
+ if (!interactionRef.current) {
20
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
21
+ // @ts-ignore
22
+ interactionRef.current = new interaction(nvlRef.current, interactionOptions);
23
+ }
24
+ if (typeof callback === 'function') {
25
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
26
+ // @ts-ignore
27
+ interactionRef.current.updateCallback(eventName, callback);
28
+ }
29
+ else {
30
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
31
+ // @ts-ignore
32
+ interactionRef.current.removeCallback(eventName);
33
+ }
34
+ }
35
+ else if (callback === false) {
36
+ destroyInteraction(interactionRef);
37
+ }
38
+ });
39
+ }, [callback]);
40
+ };
41
+ /**
42
+ * This is a wrapper component that provides a basic set of interactions for the NVL.
43
+ * It is an extension of the {@link BasicNvlWrapper} component and incorporates the
44
+ * {@link InteractionHandlers} module's decorators to provide a set of interaction events.
45
+ *
46
+ * Events can be turned on and off by passing a callback function or a boolean value to the
47
+ * `mouseEventCallbacks` prop. The callback function will be called with the event's arguments
48
+ * when the event is triggered. If a boolean value is passed, the event will be turned on or off
49
+ * accordingly.
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * const [multiSelect, setMultiSelect] = useState(false)
54
+ * <>
55
+ * <button onClick={() => setMultiSelect(!multiSelect)}>
56
+ * {multiSelect ? 'Disable' : 'Enable'} multi-select
57
+ * </button>
58
+ * <InteractiveNvlWrapper
59
+ * nodes={nodes}
60
+ * rels={relationships}
61
+ * mouseEventCallbacks={{
62
+ * onHover: (element) => console.log(element),
63
+ * onNodeClick: (node) => console.log(node),
64
+ * onMultiSelect: multiSelect
65
+ * }}
66
+ * />
67
+ * </>
68
+ * ```
69
+ */
70
+ export const InteractiveNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOptions, mouseEventCallbacks = {}, nvlCallbacks, nvlOptions = {}, interactionOptions = options }, nvlRef) => {
71
+ const myNvlRef = nvlRef !== null ? nvlRef : useRef(null);
72
+ const { onHover, onNodeClick, onNodeDoubleClick, onNodeRightClick, onRelationshipClick, onRelationshipDoubleClick, onRelationshipRightClick, onCanvasClick, onCanvasRightClick, onPan, onZoom, onDrag, onDraw, onMultiSelect } = mouseEventCallbacks;
73
+ const hoverInteraction = useRef();
74
+ const clickInteraction = useRef();
75
+ const panInteraction = useRef();
76
+ const zoomInteraction = useRef();
77
+ const dragNodeInteraction = useRef();
78
+ const drawInteraction = useRef();
79
+ const multiSelectInteraction = useRef();
80
+ useInteractionEffect(HoverInteraction, hoverInteraction, onHover, 'onHover', myNvlRef, interactionOptions);
81
+ useInteractionEffect(ClickInteraction, clickInteraction, onNodeClick, 'onNodeClick', myNvlRef, interactionOptions);
82
+ useInteractionEffect(ClickInteraction, clickInteraction, onNodeDoubleClick, 'onNodeDoubleClick', myNvlRef, interactionOptions);
83
+ useInteractionEffect(ClickInteraction, clickInteraction, onNodeRightClick, 'onNodeRightClick', myNvlRef, interactionOptions);
84
+ useInteractionEffect(ClickInteraction, clickInteraction, onRelationshipClick, 'onRelationshipClick', myNvlRef, interactionOptions);
85
+ useInteractionEffect(ClickInteraction, clickInteraction, onRelationshipDoubleClick, 'onRelationshipDoubleClick', myNvlRef, interactionOptions);
86
+ useInteractionEffect(ClickInteraction, clickInteraction, onRelationshipRightClick, 'onRelationshipRightClick', myNvlRef, interactionOptions);
87
+ useInteractionEffect(ClickInteraction, clickInteraction, onCanvasClick, 'onCanvasClick', myNvlRef, interactionOptions);
88
+ useInteractionEffect(ClickInteraction, clickInteraction, onCanvasRightClick, 'onCanvasRightClick', myNvlRef, interactionOptions);
89
+ useInteractionEffect(PanInteraction, panInteraction, onPan, 'onPan', myNvlRef, interactionOptions);
90
+ useInteractionEffect(ZoomInteraction, zoomInteraction, onZoom, 'onZoom', myNvlRef, interactionOptions);
91
+ useInteractionEffect(DragNodeInteraction, dragNodeInteraction, onDrag, 'onDrag', myNvlRef, interactionOptions);
92
+ useInteractionEffect(DrawInteraction, drawInteraction, onDraw, 'onDraw', myNvlRef, interactionOptions);
93
+ useInteractionEffect(MultiSelectInteraction, multiSelectInteraction, onMultiSelect, 'onMultiSelect', myNvlRef, interactionOptions);
94
+ useEffect(() => () => {
95
+ destroyInteraction(hoverInteraction);
96
+ destroyInteraction(clickInteraction);
97
+ destroyInteraction(panInteraction);
98
+ destroyInteraction(zoomInteraction);
99
+ destroyInteraction(dragNodeInteraction);
100
+ destroyInteraction(drawInteraction);
101
+ destroyInteraction(multiSelectInteraction);
102
+ }, []);
103
+ return (React.createElement(BasicNvlWrapper, { ref: myNvlRef, nodes: nodes, rels: rels, nvlOptions: nvlOptions, nvlCallbacks: nvlCallbacks, layout: layout, layoutOptions: layoutOptions }));
104
+ }));
@@ -1,14 +1,8 @@
1
1
  import { Node, Relationship } from '@neo4j-nvl/core';
2
- interface NodeChanges {
3
- added: Node[];
4
- removed: Node[];
5
- updated: Node[];
6
- }
7
- interface RelChanges {
8
- added: Relationship[];
9
- removed: Relationship[];
10
- updated: Relationship[];
11
- }
12
- declare const getMapDifferences: (prevGraphElements: Node[] | Relationship[], newGraphElements: Node[] | Relationship[]) => any;
13
- declare const getNodeAttributeDifferences: (prevNodes: Node[], newNodes: Node[]) => any[];
14
- export { getNodeAttributeDifferences, getMapDifferences, NodeChanges, RelChanges };
2
+ declare const getMapDifferences: <T extends Node | Relationship>(prevGraphElements: T[], newGraphElements: T[]) => {
3
+ added: T[];
4
+ removed: T[];
5
+ updated: T[];
6
+ };
7
+ declare const getNodeAttributeDifferences: (prevNodes: Node[], newNodes: Node[]) => Node[];
8
+ export { getNodeAttributeDifferences, getMapDifferences, };
@@ -46,14 +46,14 @@ const getNodeAttributeDifferences = (prevNodes, newNodes) => {
46
46
  .map(nodeToUpdate => {
47
47
  const previousNode = prevNodeMap[nodeToUpdate.id];
48
48
  if (!previousNode) {
49
- return nodeToUpdate;
49
+ return null;
50
50
  }
51
51
  return transform(nodeToUpdate, (result, value, key) => {
52
52
  if (key === 'id' || value !== previousNode[key]) {
53
- result[key] = value;
53
+ Object.assign(result, { [key]: value });
54
54
  }
55
55
  });
56
56
  })
57
- .filter(n => Object.keys(n).length > 1);
57
+ .filter(n => n && Object.keys(n).length > 1);
58
58
  };
59
- export { getNodeAttributeDifferences, getMapDifferences };
59
+ export { getNodeAttributeDifferences, getMapDifferences, };
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@neo4j-nvl/react",
3
- "version": "0.1.14",
3
+ "version": "0.2.2",
4
4
  "main": "lib/index.js",
5
+ "license": "SEE LICENSE IN 'Neo4j Early Access Agreement - Visualization Library.pdf'",
5
6
  "scripts": {
6
7
  "prebuild": "rm -rf lib/",
7
8
  "build": "tsc",
8
- "test": "jest"
9
+ "test": "jest",
10
+ "prepack": "cp ../../Neo4j\\ Early\\ Access\\ Agreement\\ -\\ Visualization\\ Library.pdf ./",
11
+ "postpack": "rm Neo4j\\ Early\\ Access\\ Agreement\\ -\\ Visualization\\ Library.pdf"
9
12
  },
10
13
  "files": [
14
+ "Neo4j Early Access Agreement - Visualization Library.pdf",
11
15
  "lib"
12
16
  ],
13
17
  "engines": {
@@ -24,10 +28,10 @@
24
28
  "@testing-library/react": "^13.4.0",
25
29
  "@types/lodash": "^4.14.184",
26
30
  "@types/react": "^18.0.18",
27
- "babel-eslint": "^10.1.0",
28
- "typedoc": "^0.23.15"
31
+ "babel-eslint": "^10.1.0"
29
32
  },
30
33
  "dependencies": {
34
+ "lodash": "^4.17.21",
31
35
  "react": "^18.2.0",
32
36
  "react-dom": "^18.2.0"
33
37
  }