@neo4j-nvl/react 0.1.14 → 0.2.1
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/Neo4j Early Access Agreement - Visualization Library.pdf +0 -0
- package/README.md +1 -3
- package/lib/basic-wrapper/BasicNvlWrapper.d.ts +85 -43
- package/lib/basic-wrapper/BasicNvlWrapper.js +83 -36
- package/lib/index.d.ts +5 -1
- package/lib/index.js +2 -1
- package/lib/interactive-nvl-wrapper/InteractiveNvlWrapper.d.ts +56 -0
- package/lib/interactive-nvl-wrapper/InteractiveNvlWrapper.js +104 -0
- package/lib/utils/graphComparison.d.ts +7 -13
- package/lib/utils/graphComparison.js +4 -4
- package/package.json +8 -4
|
Binary file
|
package/README.md
CHANGED
|
@@ -1,73 +1,115 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { NvlOptions, Node, Relationship,
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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 }
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
43
|
-
*
|
|
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 }
|
|
50
|
-
* rels={[{ from: 0, to: 1, id: 10 }
|
|
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:
|
|
58
|
-
|
|
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,65 +1,112 @@
|
|
|
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
|
-
*
|
|
13
|
-
*
|
|
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
|
-
*
|
|
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 }
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
45
|
-
*
|
|
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 }
|
|
52
|
-
* rels={[{ from: 0, to: 1, id: 10 }
|
|
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((
|
|
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
|
-
|
|
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) => nvl && nvl[method](...args) })), {});
|
|
63
110
|
});
|
|
64
111
|
const containerRef = useRef();
|
|
65
112
|
const [nvl, setNvl] = useState();
|
|
@@ -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 (!nvl) {
|
|
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.
|
|
150
|
+
nvl.addAndUpdateElementsInGraph([...nodeChanges.added, ...nodeDiff], [...relChanges.added, ...relChanges.updated]);
|
|
103
151
|
nvl.removeRelationshipsWithIds(relChanges.removed.map((r) => r.id));
|
|
104
152
|
nvl.removeNodesWithIds(nodeChanges.removed.map((n) => n.id));
|
|
105
153
|
}, [nodes, rels]);
|
|
106
154
|
useEffect(() => {
|
|
107
|
-
nvl
|
|
155
|
+
nvl && nvl.setLayout(layout);
|
|
108
156
|
}, [layout]);
|
|
109
157
|
useDeepCompareEffect(() => {
|
|
110
|
-
nvl
|
|
158
|
+
nvl && nvl.setLayoutOptions(layoutOptions);
|
|
111
159
|
}, [layoutOptions]);
|
|
112
160
|
useEffect(() => {
|
|
113
|
-
nvl
|
|
114
|
-
}, [nvlOptions
|
|
161
|
+
nvl && nvl.setUseWebGLRenderer(nvlOptions.useWebGL);
|
|
162
|
+
}, [nvlOptions.useWebGL]);
|
|
115
163
|
useEffect(() => {
|
|
116
|
-
nvl
|
|
117
|
-
}, [nvlOptions
|
|
118
|
-
return React.createElement("div", { ref: containerRef, style: { height: '100%', outline: '0' } });
|
|
164
|
+
nvl && nvl.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
|
-
|
|
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
|
@@ -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
|
-
|
|
3
|
-
added:
|
|
4
|
-
removed:
|
|
5
|
-
updated:
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
49
|
+
return null;
|
|
50
50
|
}
|
|
51
51
|
return transform(nodeToUpdate, (result, value, key) => {
|
|
52
52
|
if (key === 'id' || value !== previousNode[key]) {
|
|
53
|
-
result[key]
|
|
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
|
|
3
|
+
"version": "0.2.1",
|
|
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
|
}
|