@neo4j-nvl/react 2.0.0-d21c8bbe → 2.0.0-ec6fdba7
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 +23 -0
- package/lib/basic-wrapper/BasicNvlWrapper.d.ts +30 -31
- package/lib/basic-wrapper/BasicNvlWrapper.js +116 -143
- package/lib/index.d.ts +5 -8
- package/lib/index.js +3 -3
- package/lib/interactive-nvl-wrapper/InteractionHandlers.d.ts +8 -8
- package/lib/interactive-nvl-wrapper/InteractionHandlers.js +62 -60
- package/lib/interactive-nvl-wrapper/InteractiveNvlWrapper.d.ts +4 -4
- package/lib/interactive-nvl-wrapper/InteractiveNvlWrapper.js +54 -42
- package/lib/interactive-nvl-wrapper/hooks.d.ts +4 -4
- package/lib/interactive-nvl-wrapper/hooks.js +24 -27
- package/lib/interactive-nvl-wrapper/types.d.ts +9 -9
- package/lib/interactive-nvl-wrapper/types.js +0 -1
- package/lib/static-picture-wrapper/StaticPictureWrapper.d.ts +15 -15
- package/lib/static-picture-wrapper/StaticPictureWrapper.js +71 -55
- package/lib/utils/constants.d.ts +1 -1
- package/lib/utils/constants.js +4 -2
- package/lib/utils/graph-comparison.d.ts +5 -5
- package/lib/utils/graph-comparison.js +53 -68
- package/lib/utils/hooks.d.ts +2 -2
- package/lib/utils/hooks.js +10 -11
- package/package.json +19 -8
- package/lib/__tests__/BasicNvlWrapper.test.d.ts +0 -1
- package/lib/__tests__/BasicNvlWrapper.test.js +0 -159
- package/lib/__tests__/InteractiveNvlWrapper.test.d.ts +0 -1
- package/lib/__tests__/InteractiveNvlWrapper.test.js +0 -396
- package/lib/__tests__/StaticPictureWrapper.test.d.ts +0 -1
- package/lib/__tests__/StaticPictureWrapper.test.js +0 -85
- package/lib/__tests__/index.test.d.ts +0 -1
- package/lib/__tests__/index.test.js +0 -8
|
@@ -1,46 +1,58 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { INTERACTIVE_WRAPPER_ID } from "../utils/constants.js";
|
|
2
|
+
import { BasicNvlWrapper } from "../basic-wrapper/BasicNvlWrapper.js";
|
|
3
|
+
import { InteractionHandlers } from "./InteractionHandlers.js";
|
|
4
|
+
import { forwardRef, memo, useCallback, useRef, useState } from "react";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
// src/interactive-nvl-wrapper/InteractiveNvlWrapper.tsx
|
|
6
7
|
const options = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
selectOnClick: false,
|
|
9
|
+
drawShadowOnHover: true,
|
|
10
|
+
selectOnRelease: false,
|
|
11
|
+
excludeNodeMargin: true
|
|
11
12
|
};
|
|
12
13
|
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
14
|
+
* The interactive React wrapper component contains a collection of interaction handlers by default
|
|
15
|
+
* and provides a variety of common functionality and callbacks.
|
|
16
|
+
* It is an extension of the {@link BasicNvlWrapper} component and incorporates the
|
|
17
|
+
* @neo4j-nvl/interaction-handlers module's decorators to provide a set of interaction events.
|
|
18
|
+
*
|
|
19
|
+
* The mouseEventCallbacks property takes an object where various callbacks can be defined
|
|
20
|
+
* and behavior can be toggled on and off.
|
|
21
|
+
*
|
|
22
|
+
* For examples, head to the {@link https://neo4j.com/docs/nvl/current/react-wrappers/#_interactive_react_wrapper Interactive React wrapper documentation page}.
|
|
23
|
+
*/
|
|
24
|
+
const InteractiveNvlWrapper = memo(forwardRef(({ nodes, rels, onInitializationError, mouseEventCallbacks = {}, keyboardEventCallbacks = {}, nvlCallbacks = {}, nvlOptions = {}, interactionOptions = options, ...nvlEvents }, nvlRef) => {
|
|
25
|
+
const newNvlRef = useRef(null);
|
|
26
|
+
const myNvlRef = nvlRef ?? newNvlRef;
|
|
27
|
+
const [isNvlInitialized, setIsNvlInitialized] = useState(false);
|
|
28
|
+
const handleInitialization = useCallback(() => {
|
|
29
|
+
setIsNvlInitialized(true);
|
|
30
|
+
}, []);
|
|
31
|
+
const handleInitializationError = useCallback((error) => {
|
|
32
|
+
setIsNvlInitialized(false);
|
|
33
|
+
if (onInitializationError) onInitializationError(error);
|
|
34
|
+
}, [onInitializationError]);
|
|
35
|
+
const setupInteractions = isNvlInitialized && myNvlRef.current !== null;
|
|
36
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(BasicNvlWrapper, {
|
|
37
|
+
ref: myNvlRef,
|
|
38
|
+
nodes,
|
|
39
|
+
id: INTERACTIVE_WRAPPER_ID,
|
|
40
|
+
rels,
|
|
41
|
+
nvlOptions,
|
|
42
|
+
nvlCallbacks: {
|
|
43
|
+
...nvlCallbacks,
|
|
44
|
+
onInitialization: () => {
|
|
45
|
+
if (nvlCallbacks.onInitialization !== void 0) nvlCallbacks.onInitialization();
|
|
46
|
+
handleInitialization();
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
onInitializationError: handleInitializationError,
|
|
50
|
+
...nvlEvents
|
|
51
|
+
}), setupInteractions && /* @__PURE__ */ jsx(InteractionHandlers, {
|
|
52
|
+
nvlRef: myNvlRef,
|
|
53
|
+
mouseEventCallbacks,
|
|
54
|
+
keyboardEventCallbacks,
|
|
55
|
+
interactionOptions
|
|
56
|
+
})] });
|
|
46
57
|
}));
|
|
58
|
+
export { InteractiveNvlWrapper };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import { Interaction, InteractionModule, InteractionOptions, KeyboardEvent, MouseEvent } from "./types.js";
|
|
2
|
+
import NVL from "@neo4j-nvl/base";
|
|
3
|
+
import { MutableRefObject } from "react";
|
|
4
4
|
export declare const destroyInteraction: (interactionRef: MutableRefObject<Interaction | null>) => void;
|
|
5
|
-
export declare const useInteraction: (Interaction: InteractionModule, interactionRef: MutableRefObject<Interaction | null>, callback: ((...args: unknown[]) => void) | boolean | undefined, eventName: MouseEvent | KeyboardEvent, nvlRef: MutableRefObject<NVL | null>, interactionOptions: InteractionOptions) => void;
|
|
5
|
+
export declare const useInteraction: (Interaction: InteractionModule, interactionRef: MutableRefObject<Interaction | null>, callback: ((...args: unknown[]) => void) | boolean | undefined, eventName: MouseEvent | KeyboardEvent, nvlRef: MutableRefObject<NVL | null>, interactionOptions: InteractionOptions) => void;
|
|
@@ -1,29 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { isNil } from "lodash-es";
|
|
3
|
+
// src/interactive-nvl-wrapper/hooks.ts
|
|
4
|
+
const destroyInteraction = (interactionRef) => {
|
|
5
|
+
interactionRef.current?.destroy();
|
|
6
|
+
interactionRef.current = null;
|
|
6
7
|
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
else if (callback === false) {
|
|
26
|
-
destroyInteraction(interactionRef);
|
|
27
|
-
}
|
|
28
|
-
}, [Interaction, callback, eventName, interactionOptions, interactionRef, nvlRef]);
|
|
8
|
+
const useInteraction = (Interaction, interactionRef, callback, eventName, nvlRef, interactionOptions) => {
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const nvl = nvlRef.current;
|
|
11
|
+
if (isNil(nvl) || isNil(nvl.getContainer())) return;
|
|
12
|
+
if (callback === true || typeof callback === "function") {
|
|
13
|
+
if (isNil(interactionRef.current)) interactionRef.current = new Interaction(nvl, interactionOptions);
|
|
14
|
+
if (typeof callback === "function") interactionRef.current.updateCallback(eventName, callback);
|
|
15
|
+
else if (!isNil(interactionRef.current.callbackMap[eventName])) interactionRef.current.removeCallback(eventName);
|
|
16
|
+
} else if (callback === false) destroyInteraction(interactionRef);
|
|
17
|
+
}, [
|
|
18
|
+
Interaction,
|
|
19
|
+
callback,
|
|
20
|
+
eventName,
|
|
21
|
+
interactionOptions,
|
|
22
|
+
interactionRef,
|
|
23
|
+
nvlRef
|
|
24
|
+
]);
|
|
29
25
|
};
|
|
26
|
+
export { destroyInteraction, useInteraction };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { BasicReactWrapperProps } from "../basic-wrapper/BasicNvlWrapper.js";
|
|
2
|
+
import { BoxSelectInteraction, BoxSelectInteractionCallbacks, BoxSelectInteractionOptions, ClickInteraction, ClickInteractionCallbacks, ClickInteractionOptions, DragNodeInteraction, DragNodeInteractionCallbacks, DrawInteraction, DrawInteractionCallbacks, DrawInteractionOptions, HoverInteraction, HoverInteractionCallbacks, HoverInteractionOptions, KeyboardInteraction, KeyboardInteractionCallbacks, KeyboardInteractionOptions, LassoInteraction, LassoInteractionCallbacks, LassoInteractionOptions, PanInteraction, PanInteractionCallbacks, PanInteractionOptions, ZoomInteraction, ZoomInteractionCallbacks, ZoomInteractionOptions } from "@neo4j-nvl/interaction-handlers";
|
|
3
3
|
export type InteractionModule = typeof HoverInteraction | typeof ClickInteraction | typeof PanInteraction | typeof ZoomInteraction | typeof DragNodeInteraction | typeof DrawInteraction | typeof BoxSelectInteraction | typeof LassoInteraction | typeof KeyboardInteraction;
|
|
4
4
|
export type Interaction = HoverInteraction | ClickInteraction | PanInteraction | ZoomInteraction | DragNodeInteraction | DrawInteraction | BoxSelectInteraction | LassoInteraction | KeyboardInteraction;
|
|
5
5
|
/**
|
|
@@ -27,10 +27,10 @@ export type KeyboardEvent = keyof KeyboardEventCallbacks;
|
|
|
27
27
|
* The properties that can be passed to the {@link InteractiveNvlWrapper} component.
|
|
28
28
|
*/
|
|
29
29
|
export interface InteractiveNvlWrapperProps extends BasicReactWrapperProps {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
30
|
+
/** {@link MouseEventCallbacks} containing functions for callbacks on certain actions */
|
|
31
|
+
mouseEventCallbacks?: MouseEventCallbacks;
|
|
32
|
+
/** {@link KeyboardEventCallbacks} containing functions for callbacks on keyboard actions */
|
|
33
|
+
keyboardEventCallbacks?: KeyboardEventCallbacks;
|
|
34
|
+
/** {@link InteractionOptions} for the underlying interaction handlers */
|
|
35
|
+
interactionOptions?: InteractionOptions;
|
|
36
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Node, NvlOptions, Relationship } from "@neo4j-nvl/base";
|
|
2
2
|
/**
|
|
3
3
|
* The properties that can be passed to the StaticPictureWrapper component.
|
|
4
4
|
*/
|
|
5
5
|
export type StaticPictureWrapperProps = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
6
|
+
/** The nodes to be displayed in the graph. */
|
|
7
|
+
nodes: Node[];
|
|
8
|
+
/** The relationships to be displayed in the graph. */
|
|
9
|
+
rels: Relationship[];
|
|
10
|
+
/** Options to customize the NVL instance. */
|
|
11
|
+
nvlOptions?: NvlOptions;
|
|
12
|
+
/** The width of the static picture in pixels. */
|
|
13
|
+
width?: number;
|
|
14
|
+
/** The height of the static picture in pixels. */
|
|
15
|
+
height?: number;
|
|
16
|
+
/** The format of the static picture: 'png' or 'svg'.
|
|
17
|
+
* Defaults to 'png'. */
|
|
18
|
+
format?: 'png' | 'svg';
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
21
21
|
* A React component that creates a static picture of a graph using NVL.
|
|
@@ -36,4 +36,4 @@ export type StaticPictureWrapperProps = {
|
|
|
36
36
|
* />
|
|
37
37
|
* ```
|
|
38
38
|
*/
|
|
39
|
-
export declare const StaticPictureWrapper: ({ nodes, rels, nvlOptions, width, height, format }: StaticPictureWrapperProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
39
|
+
export declare const StaticPictureWrapper: ({ nodes, rels, nvlOptions, width, height, format }: StaticPictureWrapperProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -1,57 +1,73 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import NVL from "@neo4j-nvl/base";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
// src/static-picture-wrapper/StaticPictureWrapper.tsx
|
|
4
5
|
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
6
|
+
* A React component that creates a static picture of a graph using NVL.
|
|
7
|
+
* This component is useful for generating static visualizations of graphs without requiring user interaction.
|
|
8
|
+
* The component automatically fits all nodes in the viewport before capturing the image
|
|
9
|
+
* NVL is automatically destroyed after capturing the image to free up resources.
|
|
10
|
+
*
|
|
11
|
+
* @param props - The component properties
|
|
12
|
+
* @returns An img element displaying the graph visualization, or null during rendering
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* <StaticPictureWrapper
|
|
17
|
+
* nodes={nodes}
|
|
18
|
+
* rels={relationships}
|
|
19
|
+
* width={500}
|
|
20
|
+
* height={500}
|
|
21
|
+
* />
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
const StaticPictureWrapper = ({ nodes, rels, nvlOptions = {}, width = 500, height = 500, format = "png" }) => {
|
|
25
|
+
const [imgSrc, setImgSrc] = useState();
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const div = document.createElement("div");
|
|
28
|
+
div.style.width = `${width}px`;
|
|
29
|
+
div.style.height = `${height}px`;
|
|
30
|
+
const myNvl = new NVL(div, nodes, rels, nvlOptions, {
|
|
31
|
+
onLayoutDone: () => {
|
|
32
|
+
myNvl.fit(myNvl.getNodes().map((n) => n.id));
|
|
33
|
+
},
|
|
34
|
+
onZoomTransitionDone: () => {
|
|
35
|
+
const fetchDataUrl = async () => {
|
|
36
|
+
try {
|
|
37
|
+
const dataUrl = format === "svg" ? await myNvl.getSvgDataUrl() : myNvl.getImageDataUrl();
|
|
38
|
+
setImgSrc(dataUrl);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
console.error("Failed to generate image data URL", err);
|
|
41
|
+
} finally {
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
myNvl.destroy();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
fetchDataUrl();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return () => {
|
|
51
|
+
myNvl?.destroy();
|
|
52
|
+
};
|
|
53
|
+
}, [
|
|
54
|
+
nodes,
|
|
55
|
+
rels,
|
|
56
|
+
width,
|
|
57
|
+
height,
|
|
58
|
+
format
|
|
59
|
+
]);
|
|
60
|
+
return imgSrc !== void 0 ? /* @__PURE__ */ jsx("img", {
|
|
61
|
+
src: imgSrc,
|
|
62
|
+
width,
|
|
63
|
+
height,
|
|
64
|
+
style: {
|
|
65
|
+
display: "block",
|
|
66
|
+
width,
|
|
67
|
+
height,
|
|
68
|
+
objectFit: "contain"
|
|
69
|
+
},
|
|
70
|
+
alt: "Graph"
|
|
71
|
+
}) : null;
|
|
57
72
|
};
|
|
73
|
+
export { StaticPictureWrapper };
|
package/lib/utils/constants.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const BASIC_WRAPPER_ID = "NVL_basic-wrapper";
|
|
2
|
-
export declare const INTERACTIVE_WRAPPER_ID = "NVL_interactive-wrapper";
|
|
2
|
+
export declare const INTERACTIVE_WRAPPER_ID = "NVL_interactive-wrapper";
|
package/lib/utils/constants.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// src/utils/constants.ts
|
|
2
|
+
const BASIC_WRAPPER_ID = "NVL_basic-wrapper";
|
|
3
|
+
const INTERACTIVE_WRAPPER_ID = "NVL_interactive-wrapper";
|
|
4
|
+
export { BASIC_WRAPPER_ID, INTERACTIVE_WRAPPER_ID };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Node, Relationship } from "@neo4j-nvl/base";
|
|
2
2
|
declare const getMapDifferences: <T extends Node | Relationship>(prevGraphElements: T[], newGraphElements: T[]) => {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
added: T[];
|
|
4
|
+
removed: T[];
|
|
5
|
+
updated: T[];
|
|
6
6
|
};
|
|
7
7
|
declare const getNodeAttributeDifferences: (prevNodes: Node[], newNodes: Node[]) => Node[];
|
|
8
|
-
export {
|
|
8
|
+
export { getMapDifferences, getNodeAttributeDifferences };
|
|
@@ -1,72 +1,57 @@
|
|
|
1
|
-
import { isEqual, isNil, keyBy, keys, sortBy, transform } from
|
|
1
|
+
import { isEqual, isNil, keyBy, keys, sortBy, transform } from "lodash-es";
|
|
2
|
+
// src/utils/graph-comparison.ts
|
|
2
3
|
const getMapDifferences = (prevGraphElements, newGraphElements) => {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
added.push(currId);
|
|
48
|
-
j += 1;
|
|
49
|
-
}
|
|
50
|
-
return {
|
|
51
|
-
added: added.map((id) => currentMap[id]).filter((n) => !isNil(n)),
|
|
52
|
-
removed: removed.map((id) => prevMap[id]).filter((n) => !isNil(n)),
|
|
53
|
-
updated: updated.map((id) => currentMap[id]).filter((n) => !isNil(n))
|
|
54
|
-
};
|
|
4
|
+
const prevMap = keyBy(prevGraphElements, "id");
|
|
5
|
+
const currentMap = keyBy(newGraphElements, "id");
|
|
6
|
+
const prevIds = sortBy(keys(prevMap));
|
|
7
|
+
const currentIds = sortBy(keys(currentMap));
|
|
8
|
+
const added = [];
|
|
9
|
+
const removed = [];
|
|
10
|
+
const updated = [];
|
|
11
|
+
let i = 0;
|
|
12
|
+
let j = 0;
|
|
13
|
+
while (i < prevIds.length && j < currentIds.length) {
|
|
14
|
+
const prevId = prevIds[i];
|
|
15
|
+
const currId = currentIds[j];
|
|
16
|
+
if (prevId === void 0 || currId === void 0) continue;
|
|
17
|
+
if (prevId === currId) {
|
|
18
|
+
if (!isEqual(prevMap[prevId], currentMap[currId])) updated.push(currId);
|
|
19
|
+
i += 1;
|
|
20
|
+
j += 1;
|
|
21
|
+
} else if (prevId < currId) {
|
|
22
|
+
removed.push(prevId);
|
|
23
|
+
i += 1;
|
|
24
|
+
} else {
|
|
25
|
+
added.push(currId);
|
|
26
|
+
j += 1;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
while (i < prevIds.length) {
|
|
30
|
+
const prevId = prevIds[i];
|
|
31
|
+
if (prevId === void 0) continue;
|
|
32
|
+
removed.push(prevId);
|
|
33
|
+
i += 1;
|
|
34
|
+
}
|
|
35
|
+
while (j < currentIds.length) {
|
|
36
|
+
const currId = currentIds[j];
|
|
37
|
+
if (currId === void 0) continue;
|
|
38
|
+
added.push(currId);
|
|
39
|
+
j += 1;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
added: added.map((id) => currentMap[id]).filter((n) => !isNil(n)),
|
|
43
|
+
removed: removed.map((id) => prevMap[id]).filter((n) => !isNil(n)),
|
|
44
|
+
updated: updated.map((id) => currentMap[id]).filter((n) => !isNil(n))
|
|
45
|
+
};
|
|
55
46
|
};
|
|
56
47
|
const getNodeAttributeDifferences = (prevNodes, newNodes) => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (key === 'id' || value !== previousNode[key]) {
|
|
66
|
-
Object.assign(result, { [key]: value });
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
})
|
|
70
|
-
.filter((n) => n !== null && Object.keys(n).length > 1);
|
|
48
|
+
const prevNodeMap = keyBy(prevNodes, "id");
|
|
49
|
+
return newNodes.map((nodeToUpdate) => {
|
|
50
|
+
const previousNode = prevNodeMap[nodeToUpdate.id];
|
|
51
|
+
if (previousNode === void 0) return null;
|
|
52
|
+
return transform(nodeToUpdate, (result, value, key) => {
|
|
53
|
+
if (key === "id" || value !== previousNode[key]) Object.assign(result, { [key]: value });
|
|
54
|
+
});
|
|
55
|
+
}).filter((n) => n !== null && Object.keys(n).length > 1);
|
|
71
56
|
};
|
|
72
|
-
export {
|
|
57
|
+
export { getMapDifferences, getNodeAttributeDifferences };
|
package/lib/utils/hooks.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
export declare const useDeepCompareEffect: (callback: EffectCallback, dependencies: unknown[]) => void;
|
|
1
|
+
import { EffectCallback } from "react";
|
|
2
|
+
export declare const useDeepCompareEffect: (callback: EffectCallback, dependencies: unknown[]) => void;
|
package/lib/utils/hooks.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { isEqual } from "lodash-es";
|
|
3
|
+
// src/utils/hooks.ts
|
|
3
4
|
const deepCompareEquals = (a, b) => {
|
|
4
|
-
|
|
5
|
+
return isEqual(a, b);
|
|
5
6
|
};
|
|
6
7
|
const useDeepCompareMemoize = (value) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
return ref.current;
|
|
8
|
+
const ref = useRef();
|
|
9
|
+
if (!deepCompareEquals(value, ref.current)) ref.current = value;
|
|
10
|
+
return ref.current;
|
|
12
11
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
useEffect(callback, dependencies.map(useDeepCompareMemoize));
|
|
12
|
+
const useDeepCompareEffect = (callback, dependencies) => {
|
|
13
|
+
useEffect(callback, dependencies.map(useDeepCompareMemoize));
|
|
16
14
|
};
|
|
15
|
+
export { useDeepCompareEffect };
|
package/package.json
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neo4j-nvl/react",
|
|
3
|
-
"version": "2.0.0-
|
|
3
|
+
"version": "2.0.0-ec6fdba7",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./lib/index.d.ts",
|
|
10
|
+
"import": "./lib/index.js",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
5
15
|
"homepage": "https://neo4j.com/docs/nvl/current/",
|
|
6
16
|
"license": "SEE LICENSE IN 'LICENSE.txt'",
|
|
7
17
|
"description": "React wrappers for the Neo4j Visualization Library",
|
|
@@ -15,8 +25,8 @@
|
|
|
15
25
|
"url": "https://community.neo4j.com/c/neo4j-graph-platform/neo4j-bloom"
|
|
16
26
|
},
|
|
17
27
|
"scripts": {
|
|
18
|
-
"build": "yarn global:tsc",
|
|
19
|
-
"watch": "
|
|
28
|
+
"build": "yarn global:tsc --noEmit && tsdown",
|
|
29
|
+
"watch": "tsdown --watch",
|
|
20
30
|
"test": " yarn global:jest",
|
|
21
31
|
"prepack": "cp ../../LICENSE.txt ./ && cp ../../CHANGELOG.md ./",
|
|
22
32
|
"postpack": "rm LICENSE.txt && rm CHANGELOG.md",
|
|
@@ -33,15 +43,16 @@
|
|
|
33
43
|
"@testing-library/dom": "10.4.1",
|
|
34
44
|
"@testing-library/jest-dom": "5.17.0",
|
|
35
45
|
"@testing-library/react": "16.3.0",
|
|
36
|
-
"@types/lodash": "4.17.
|
|
46
|
+
"@types/lodash-es": "4.17.12",
|
|
37
47
|
"@types/react": "18.3.27",
|
|
38
48
|
"react": "19.2.1",
|
|
39
|
-
"react-dom": "19.2.1"
|
|
49
|
+
"react-dom": "19.2.1",
|
|
50
|
+
"tsdown": "0.23.1"
|
|
40
51
|
},
|
|
41
52
|
"dependencies": {
|
|
42
|
-
"@neo4j-nvl/base": "2.0.0-
|
|
43
|
-
"@neo4j-nvl/interaction-handlers": "2.0.0-
|
|
44
|
-
"lodash": "4.18.1"
|
|
53
|
+
"@neo4j-nvl/base": "2.0.0-ec6fdba7",
|
|
54
|
+
"@neo4j-nvl/interaction-handlers": "2.0.0-ec6fdba7",
|
|
55
|
+
"lodash-es": "4.18.1"
|
|
45
56
|
},
|
|
46
57
|
"peerDependencies": {
|
|
47
58
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import '@testing-library/jest-dom';
|