@neo4j-nvl/react 0.2.53 → 0.2.55

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.
@@ -1,6 +1,6 @@
1
1
  import type { ExternalCallbacks, Layout, LayoutOptions, Node, NvlOptions, Relationship } from '@neo4j-nvl/core';
2
2
  import NVL from '@neo4j-nvl/core';
3
- import React from 'react';
3
+ import { type HTMLProps } from 'react';
4
4
  type IncludeMethods<T> = Pick<T, {
5
5
  [K in keyof T]: T[K] extends (_: unknown) => unknown ? K : never;
6
6
  }[keyof T]>;
@@ -109,5 +109,5 @@ export interface BasicReactWrapperProps {
109
109
  * For more about interactivity, check out the {@link NVL}.hittest method,
110
110
  * the Interaction Handlers module module and the {@link InteractiveNvlWrapper}.
111
111
  */
112
- export declare const BasicNvlWrapper: React.MemoExoticComponent<React.ForwardRefExoticComponent<Pick<BasicReactWrapperProps & React.HTMLProps<HTMLDivElement>, "max" | "required" | "type" | "data" | "key" | "default" | "high" | "low" | "id" | "media" | "height" | "width" | "start" | "open" | "name" | "min" | "color" | "content" | "translate" | "value" | "hidden" | "cite" | "form" | "label" | "slot" | "span" | "style" | "summary" | "title" | "dir" | "pattern" | "acceptCharset" | "action" | "method" | "noValidate" | "rel" | "target" | "accessKey" | "draggable" | "lang" | "className" | "prefix" | "role" | "children" | "contentEditable" | "inputMode" | "nonce" | "tabIndex" | "async" | "disabled" | "multiple" | "size" | "manifest" | "wrap" | "accept" | "allowFullScreen" | "allowTransparency" | "alt" | "as" | "autoComplete" | "autoFocus" | "autoPlay" | "capture" | "cellPadding" | "cellSpacing" | "charSet" | "challenge" | "checked" | "classID" | "cols" | "colSpan" | "controls" | "coords" | "crossOrigin" | "dateTime" | "defer" | "download" | "encType" | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | "formTarget" | "frameBorder" | "headers" | "href" | "hrefLang" | "htmlFor" | "httpEquiv" | "integrity" | "keyParams" | "keyType" | "kind" | "list" | "loop" | "marginHeight" | "marginWidth" | "maxLength" | "mediaGroup" | "minLength" | "muted" | "optimum" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "contextMenu" | "placeholder" | "spellCheck" | "radioGroup" | "about" | "datatype" | "inlist" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "playsInline" | "poster" | "preload" | "readOnly" | "reversed" | "rows" | "rowSpan" | "sandbox" | "scope" | "scoped" | "scrolling" | "seamless" | "selected" | "shape" | "sizes" | "src" | "srcDoc" | "srcLang" | "srcSet" | "step" | "useMap" | "wmode" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onResize" | "onResizeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | keyof BasicReactWrapperProps> & React.RefAttributes<IncludeMethods<NVL>>>>;
112
+ export declare const BasicNvlWrapper: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<Omit<BasicReactWrapperProps & HTMLProps<HTMLDivElement>, "ref"> & import("react").RefAttributes<IncludeMethods<NVL>>>>;
113
113
  export {};
@@ -1,6 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
1
2
  import NVL from '@neo4j-nvl/core';
2
- import React, { forwardRef, memo, useEffect, useImperativeHandle, useRef, useState } from 'react';
3
- import { getMapDifferences, getNodeAttributeDifferences } from '../utils/graphComparison';
3
+ import { forwardRef, memo, useEffect, useImperativeHandle, useRef, useState } from 'react';
4
+ import { getMapDifferences, getNodeAttributeDifferences } from '../utils/graph-comparison';
4
5
  import { useDeepCompareEffect } from '../utils/hooks';
5
6
  /**
6
7
  * A basic React wrapper for the {@link NVL} class.
@@ -91,43 +92,49 @@ export const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOpt
91
92
  useImperativeHandle(ref, () => {
92
93
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
93
94
  const nvlMethods = Object.getOwnPropertyNames(NVL.prototype);
94
- // @ts-ignore revisit better typing for reduce function
95
95
  return nvlMethods.reduce((current, method) => ({
96
96
  ...current,
97
97
  [method]: (...args) =>
98
98
  // @ts-ignore suppress the type casting error on spreading
99
- nvlRef !== undefined && nvlRef.current !== null && nvlRef.current[method](...args)
100
- }), {});
99
+ nvlRef.current !== null && nvlRef.current[method](...args)
100
+ }),
101
+ // eslint-disable-next-line max-len
102
+ // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter, @typescript-eslint/consistent-type-assertions
103
+ {});
101
104
  });
102
- const containerRef = useRef();
105
+ const containerRef = useRef(null);
103
106
  const [currentNodes, setCurrentNodes] = useState(nodes);
104
107
  const [currentRels, setCurrentRels] = useState(rels);
105
108
  useEffect(() => {
106
- let newNvl;
107
- if (nvlRef.current === null) {
108
- const combinedOptions = { ...nvlOptions, layoutOptions };
109
- if (layout !== undefined) {
110
- combinedOptions.layout = layout;
111
- }
112
- try {
113
- newNvl = new NVL(containerRef.current, currentNodes, currentRels, combinedOptions, nvlCallbacks);
114
- nvlRef.current = newNvl;
115
- setCurrentRels(rels);
116
- setCurrentNodes(nodes);
117
- }
118
- catch (e) {
119
- if (typeof onInitializationError === 'function') {
120
- onInitializationError(e);
109
+ let newNvl = null;
110
+ if (containerRef.current !== null) {
111
+ if (nvlRef.current === null) {
112
+ const combinedOptions = { ...nvlOptions, layoutOptions };
113
+ if (layout !== undefined) {
114
+ combinedOptions.layout = layout;
115
+ }
116
+ try {
117
+ newNvl = new NVL(containerRef.current, currentNodes, currentRels, combinedOptions, nvlCallbacks);
118
+ nvlRef.current = newNvl;
119
+ setCurrentRels(rels);
120
+ setCurrentNodes(nodes);
121
121
  }
122
- else {
123
- throw e;
122
+ catch (e) {
123
+ if (typeof onInitializationError === 'function') {
124
+ onInitializationError(e);
125
+ }
126
+ else {
127
+ throw e;
128
+ }
124
129
  }
125
130
  }
126
131
  }
127
132
  return () => {
128
133
  newNvl?.destroy();
134
+ nvlRef.current = null;
129
135
  };
130
- }, []);
136
+ // eslint-disable-next-line react-hooks/exhaustive-deps
137
+ }, [containerRef.current]);
131
138
  useEffect(() => {
132
139
  if (nvlRef.current === null) {
133
140
  return;
@@ -137,33 +144,33 @@ export const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOpt
137
144
  const relChanges = getMapDifferences(currentRels, rels);
138
145
  setCurrentRels(rels);
139
146
  setCurrentNodes(nodes);
140
- nvlRef.current?.addAndUpdateElementsInGraph([...nodeChanges.added, ...nodeDiff], [...relChanges.added, ...relChanges.updated]);
141
- nvlRef.current?.removeRelationshipsWithIds(relChanges.removed.map((r) => r.id));
142
- nvlRef.current?.removeNodesWithIds(nodeChanges.removed.map((n) => n.id));
143
- }, [nodes, rels]);
147
+ nvlRef.current.addAndUpdateElementsInGraph([...nodeChanges.added, ...nodeDiff], [...relChanges.added, ...relChanges.updated]);
148
+ nvlRef.current.removeRelationshipsWithIds(relChanges.removed.map((r) => r.id));
149
+ nvlRef.current.removeNodesWithIds(nodeChanges.removed.map((n) => n.id));
150
+ }, [currentNodes, currentRels, nodes, rels]);
144
151
  useEffect(() => {
145
152
  if (nvlRef.current === null || layout === undefined) {
146
153
  return;
147
154
  }
148
- nvlRef.current?.setLayout(layout);
155
+ nvlRef.current.setLayout(layout);
149
156
  }, [layout]);
150
157
  useDeepCompareEffect(() => {
151
158
  if (nvlRef.current === null || layoutOptions === undefined) {
152
159
  return;
153
160
  }
154
- nvlRef.current?.setLayoutOptions(layoutOptions);
161
+ nvlRef.current.setLayoutOptions(layoutOptions);
155
162
  }, [layoutOptions]);
156
163
  useEffect(() => {
157
164
  if (nvlRef.current === null || nvlOptions.useWebGL === undefined) {
158
165
  return;
159
166
  }
160
- nvlRef.current?.setUseWebGLRenderer(nvlOptions.useWebGL);
167
+ nvlRef.current.setUseWebGLRenderer(nvlOptions.useWebGL);
161
168
  }, [nvlOptions.useWebGL]);
162
169
  useEffect(() => {
163
170
  if (nvlRef.current === null || nvlOptions.disableWebGL === undefined) {
164
171
  return;
165
172
  }
166
- nvlRef.current?.setDisableWebGL(nvlOptions.disableWebGL);
173
+ nvlRef.current.setDisableWebGL(nvlOptions.disableWebGL);
167
174
  }, [nvlOptions.disableWebGL]);
168
- return React.createElement("div", { ref: containerRef, style: { height: '100%', outline: '0' }, ...nvlEvents });
175
+ return _jsx("div", { ref: containerRef, style: { height: '100%', outline: '0' }, ...nvlEvents });
169
176
  }));
@@ -1,5 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
1
2
  import { BoxSelectInteraction, ClickInteraction, DragNodeInteraction, DrawInteraction, HoverInteraction, LassoInteraction, PanInteraction, ZoomInteraction } from '@neo4j-nvl/interaction-handlers';
2
- import React, { forwardRef, memo, useEffect, useRef } from 'react';
3
+ import { forwardRef, memo, useEffect, useRef } from 'react';
3
4
  import { BasicNvlWrapper } from '../basic-wrapper/BasicNvlWrapper';
4
5
  import { destroyInteraction, useInteraction } from './hooks';
5
6
  const options = {
@@ -38,7 +39,8 @@ const options = {
38
39
  * ```
39
40
  */
40
41
  export const InteractiveNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOptions, mouseEventCallbacks = {}, nvlCallbacks = {}, nvlOptions = {}, interactionOptions = options }, nvlRef) => {
41
- const myNvlRef = nvlRef !== null ? nvlRef : useRef(null);
42
+ const newNvlRef = useRef(null);
43
+ const myNvlRef = nvlRef ?? newNvlRef;
42
44
  const { onHover, onNodeClick, onNodeDoubleClick, onNodeRightClick, onRelationshipClick, onRelationshipDoubleClick, onRelationshipRightClick, onCanvasClick, onCanvasRightClick, onPan, onZoom, onDrag, onDragStart, onDragEnd, onDrawEnd, onHoverNodeMargin, onBoxStarted, onBoxSelect, onLassoStarted, onLassoSelect } = mouseEventCallbacks;
43
45
  const hoverInteraction = useRef();
44
46
  const clickInteraction = useRef();
@@ -78,5 +80,5 @@ export const InteractiveNvlWrapper = memo(forwardRef(({ nodes, rels, layout, lay
78
80
  destroyInteraction(multiSelectInteraction);
79
81
  destroyInteraction(lassoInteraction);
80
82
  }, []);
81
- return (React.createElement(BasicNvlWrapper, { ref: myNvlRef, nodes: nodes, rels: rels, nvlOptions: nvlOptions, nvlCallbacks: nvlCallbacks, layout: layout, layoutOptions: layoutOptions }));
83
+ return (_jsx(BasicNvlWrapper, { ref: myNvlRef, nodes: nodes, rels: rels, nvlOptions: nvlOptions, nvlCallbacks: nvlCallbacks, layout: layout, layoutOptions: layoutOptions }));
82
84
  }));
@@ -1,5 +1,5 @@
1
1
  import type NVL from '@neo4j-nvl/core';
2
2
  import type { MutableRefObject } from 'react';
3
3
  import type { InteractionOptions, MouseEvent, MouseInteraction, MouseInteractionModule } from './types';
4
- export declare const destroyInteraction: (interactionRef: MutableRefObject<MouseInteraction>) => void;
5
- export declare const useInteraction: (Interaction: MouseInteractionModule, interactionRef: MutableRefObject<MouseInteraction>, callback: boolean | ((...args: unknown[]) => void), eventName: MouseEvent, nvlRef: MutableRefObject<NVL>, interactionOptions: InteractionOptions) => void;
4
+ export declare const destroyInteraction: (interactionRef: MutableRefObject<MouseInteraction | null>) => void;
5
+ export declare const useInteraction: (Interaction: MouseInteractionModule, interactionRef: MutableRefObject<MouseInteraction>, callback: boolean | ((...args: unknown[]) => void), eventName: MouseEvent, nvlRef: MutableRefObject<NVL | null>, interactionOptions: InteractionOptions) => void;
@@ -6,21 +6,19 @@ export const destroyInteraction = (interactionRef) => {
6
6
  };
7
7
  export const useInteraction = (Interaction, interactionRef, callback, eventName, nvlRef, interactionOptions) => {
8
8
  useEffect(() => {
9
- setTimeout(() => {
10
- if ((callback === true || typeof callback === 'function') && !isNil(nvlRef.current)) {
11
- if (isNil(interactionRef.current)) {
12
- interactionRef.current = new Interaction(nvlRef.current, interactionOptions);
13
- }
14
- if (typeof callback === 'function') {
15
- interactionRef.current.updateCallback(eventName, callback);
16
- }
17
- else {
18
- interactionRef.current.removeCallback(eventName);
19
- }
9
+ if ((callback === true || typeof callback === 'function') && !isNil(nvlRef.current)) {
10
+ if (isNil(interactionRef.current)) {
11
+ interactionRef.current = new Interaction(nvlRef.current, interactionOptions);
20
12
  }
21
- else if (callback === false) {
22
- destroyInteraction(interactionRef);
13
+ if (typeof callback === 'function') {
14
+ interactionRef.current.updateCallback(eventName, callback);
23
15
  }
24
- });
25
- }, [callback]);
16
+ else if (!isNil(interactionRef.current.callbackMap[eventName])) {
17
+ interactionRef.current.removeCallback(eventName);
18
+ }
19
+ }
20
+ else if (callback === false) {
21
+ destroyInteraction(interactionRef);
22
+ }
23
+ }, [Interaction, callback, eventName, interactionOptions, interactionRef, nvlRef]);
26
24
  };
@@ -1,4 +1,4 @@
1
- import { isEqual, keyBy, keys, sortBy, transform } from 'lodash';
1
+ import { isEqual, isNil, keyBy, keys, sortBy, transform } from 'lodash';
2
2
  const getMapDifferences = (prevGraphElements, newGraphElements) => {
3
3
  const prevMap = keyBy(prevGraphElements, 'id');
4
4
  const currentMap = keyBy(newGraphElements, 'id');
@@ -12,6 +12,9 @@ const getMapDifferences = (prevGraphElements, newGraphElements) => {
12
12
  while (i < prevIds.length && j < currentIds.length) {
13
13
  const prevId = prevIds[i];
14
14
  const currId = currentIds[j];
15
+ if (prevId === undefined || currId === undefined) {
16
+ continue;
17
+ }
15
18
  if (prevId === currId) {
16
19
  if (!isEqual(prevMap[prevId], currentMap[currId])) {
17
20
  updated.push(currId);
@@ -29,17 +32,25 @@ const getMapDifferences = (prevGraphElements, newGraphElements) => {
29
32
  }
30
33
  }
31
34
  while (i < prevIds.length) {
32
- removed.push(prevIds[i]);
35
+ const prevId = prevIds[i];
36
+ if (prevId === undefined) {
37
+ continue;
38
+ }
39
+ removed.push(prevId);
33
40
  i += 1;
34
41
  }
35
42
  while (j < currentIds.length) {
36
- added.push(currentIds[j]);
43
+ const currId = currentIds[j];
44
+ if (currId === undefined) {
45
+ continue;
46
+ }
47
+ added.push(currId);
37
48
  j += 1;
38
49
  }
39
50
  return {
40
- added: added.map((id) => currentMap[id]),
41
- removed: removed.map((id) => prevMap[id]),
42
- updated: updated.map((id) => currentMap[id])
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))
43
54
  };
44
55
  };
45
56
  const getNodeAttributeDifferences = (prevNodes, newNodes) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neo4j-nvl/react",
3
- "version": "0.2.53",
3
+ "version": "0.2.55",
4
4
  "main": "lib/index.js",
5
5
  "license": "SEE LICENSE IN 'Neo4j Early Access Agreement - Visualization Library.pdf'",
6
6
  "scripts": {
@@ -27,7 +27,7 @@
27
27
  "@testing-library/jest-dom": "^5.16.5",
28
28
  "@testing-library/react": "^13.4.0",
29
29
  "@types/lodash": "4.14.202",
30
- "@types/react": "^18.0.18",
30
+ "@types/react": "18.2.58",
31
31
  "babel-eslint": "^10.1.0"
32
32
  },
33
33
  "dependencies": {