@neo4j-nvl/react 0.3.3-f8d06686 → 0.3.4
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,6 +1,6 @@
|
|
|
1
1
|
# Neo4j Visualization Library
|
|
2
2
|
|
|
3
|
-
Welcome to
|
|
3
|
+
Welcome to the Neo4j Visualization Library, NVL for short. NVL is a collection of libraries that can be used to build custom graph visualizations like those used in [Neo4j Bloom and Explore(powered by Bloom)](https://neo4j.com/product/bloom/). NVL is written in TypeScript and can be used in any JavaScript project. This package contains React components that make it easier to use NVL within a React application. If you want to use NVL with a framework other than React or want to build your own React wrapper, you can do so by using the [NVL base library](https://www.npmjs.com/package/@neo4j-nvl/base).
|
|
4
4
|
|
|
5
5
|
## Consuming the library
|
|
6
6
|
|
|
@@ -30,7 +30,7 @@ const addElements = () => {
|
|
|
30
30
|
setNodes(newNodes)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
;<div>
|
|
34
34
|
<BasicNvlWrapper nodes={nodes} />
|
|
35
35
|
<button onClick={addElements}>Add Graph Elements</button>
|
|
36
36
|
</div>
|
|
@@ -80,5 +80,13 @@ You can also find more instructions and examples on how to use the NVL React wra
|
|
|
80
80
|
|
|
81
81
|
### Product analytics
|
|
82
82
|
|
|
83
|
-
In order to improve the library we are collecting some information about the usage of NVL. If you prefer to disable this behavior
|
|
84
|
-
For example:
|
|
83
|
+
In order to improve the library we are collecting some information about the usage of NVL. If you prefer to disable this behavior setting `disableTelemetry` to `true` in the `nvlOptions` property.
|
|
84
|
+
For example:
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
<BasicNvlWrapper
|
|
88
|
+
nvlOptions={{ disableTelemetry: true }}
|
|
89
|
+
nodes={[{ id: '0' }, { id: '1' }]}
|
|
90
|
+
rels={[{ from: '0', to: '1', id: '10' }]}
|
|
91
|
+
/>
|
|
92
|
+
```
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ExternalCallbacks, Layout, LayoutOptions, Node, NvlOptions, Relationship } from '@neo4j-nvl/base';
|
|
2
2
|
import NVL from '@neo4j-nvl/base';
|
|
3
3
|
import { type HTMLProps } from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* @hidden
|
|
6
|
+
*/
|
|
4
7
|
type IncludeMethods<T> = Pick<T, {
|
|
5
8
|
[K in keyof T]: T[K] extends (_: unknown) => unknown ? K : never;
|
|
6
9
|
}[keyof T]>;
|
|
@@ -24,98 +27,12 @@ export interface BasicReactWrapperProps {
|
|
|
24
27
|
onInitializationError?: (error: unknown) => void;
|
|
25
28
|
}
|
|
26
29
|
/**
|
|
27
|
-
* A basic React wrapper for the {@link NVL} class.
|
|
28
|
-
* It takes the {@link BasicReactWrapperProps} as properties which are passed to the NVL constructor.
|
|
29
|
-
* Any changes in properties will be reflected in the NVL instance by calling the corresponding methods.
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```tsx
|
|
33
|
-
* <BasicNvlWrapper
|
|
34
|
-
* nodes={nodes}
|
|
35
|
-
* rels={relationships}
|
|
36
|
-
* nvlOptions={options}
|
|
37
|
-
* nvlCallbacks={callbacks}
|
|
38
|
-
* />
|
|
39
|
-
* ```
|
|
40
|
-
*
|
|
41
|
-
* would be equivalent to
|
|
42
|
-
*
|
|
43
|
-
* ```tsx
|
|
44
|
-
* const myNvl = new NVL(container, nodes, relationships, options, callbacks)
|
|
45
|
-
* ```
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* When nodes and/or relationships are updated in the React wrapper, the NVL instance will be updated accordingly:
|
|
49
|
-
*
|
|
50
|
-
* ```tsx
|
|
51
|
-
* const NvlComponent = () => {
|
|
52
|
-
* const [nodes, setNodes] = useState<Node[]>([{ id: '0' }, { id: '1' }])
|
|
53
|
-
*
|
|
54
|
-
* const addElements = () => {
|
|
55
|
-
* const newNodes = [...nodes, { id: nodes.length.toString() }]
|
|
56
|
-
* setNodes(newNodes)
|
|
57
|
-
* }
|
|
58
30
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* </div>
|
|
63
|
-
* }
|
|
64
|
-
* ```
|
|
65
|
-
*
|
|
66
|
-
* would be equivalent to
|
|
67
|
-
*
|
|
68
|
-
* ```tsx
|
|
69
|
-
* const container = document.createElement('div')
|
|
70
|
-
* const myButton = document.createElement('button')
|
|
71
|
-
* const nodes = [{ id: '0' }, { id: '1' }]
|
|
72
|
-
* const myNvl = new NVL(container, nodes, [])
|
|
73
|
-
* myButton.addEventListener('click', () => {
|
|
74
|
-
* const newNodes = [...nodes, { id: nodes.length.toString() }]
|
|
75
|
-
* myNvl.addAndUpdateElementsInGraph(newNodes, [])
|
|
76
|
-
* })
|
|
77
|
-
* ```
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* If you want to access the NVL class outside of the React wrapper you can use a
|
|
81
|
-
* reference of NVL to call its methods:
|
|
82
|
-
* ```tsx
|
|
83
|
-
* const NvlComponent = () => {
|
|
84
|
-
* const nvlRef = useRef<NVL>()
|
|
85
|
-
*
|
|
86
|
-
* return <div>
|
|
87
|
-
* <BasicNvlWrapper
|
|
88
|
-
* nodes={[{ id: '0' }, { id: '1' }]}
|
|
89
|
-
* rels={[{ from: '0', to: '1', id: '10' }]}
|
|
90
|
-
* ref={nvlRef}
|
|
91
|
-
* />
|
|
92
|
-
* <button onClick={() => nvlRef.current?.fit([0, 1])}>Zoom to Nodes</button>
|
|
93
|
-
* </div>
|
|
94
|
-
* }
|
|
95
|
-
* ```
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* If you want to pass events to the NVL instance you can do so by passing them as properties to the React wrapper:
|
|
99
|
-
*
|
|
100
|
-
* ```tsx
|
|
101
|
-
* <BasicNvlWrapper
|
|
102
|
-
* nodes={nodes}
|
|
103
|
-
* rels={relationships}
|
|
104
|
-
* onClick={(event) => console.log(event)}
|
|
105
|
-
* />
|
|
106
|
-
* ```
|
|
107
|
-
*
|
|
108
|
-
* would be equivalent to
|
|
109
|
-
*
|
|
110
|
-
* ```tsx
|
|
111
|
-
* const container = document.createElement('div')
|
|
112
|
-
* const nodes = [{ id: '0' }, { id: '1' }]
|
|
113
|
-
* const myNvl = new NVL(container, nodes, [])
|
|
114
|
-
* container.addEventListener('click', (event) => console.log(event))
|
|
115
|
-
* ```
|
|
31
|
+
* A basic React wrapper that wraps the base {@link NVL} library within a React component.
|
|
32
|
+
* It takes the class' arguments 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.
|
|
116
34
|
*
|
|
117
|
-
* For
|
|
118
|
-
* the Interaction Handlers module and the {@link InteractiveNvlWrapper}.
|
|
35
|
+
* For examples, head to the {@link https://neo4j.com/docs/nvl/current/react-wrappers/#_basic_react_wrapper Basic React wrapper documentation page}.
|
|
119
36
|
*/
|
|
120
37
|
export declare const BasicNvlWrapper: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<Omit<BasicReactWrapperProps & HTMLProps<HTMLDivElement>, "ref"> & import("react").RefAttributes<IncludeMethods<NVL>>>>;
|
|
121
38
|
export {};
|
|
@@ -5,98 +5,12 @@ import { BASIC_WRAPPER_ID } from '../utils/constants';
|
|
|
5
5
|
import { getMapDifferences, getNodeAttributeDifferences } from '../utils/graph-comparison';
|
|
6
6
|
import { useDeepCompareEffect } from '../utils/hooks';
|
|
7
7
|
/**
|
|
8
|
-
* A basic React wrapper for the {@link NVL} class.
|
|
9
|
-
* It takes the {@link BasicReactWrapperProps} as properties which are passed to the NVL constructor.
|
|
10
|
-
* Any changes in properties will be reflected in the NVL instance by calling the corresponding methods.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```tsx
|
|
14
|
-
* <BasicNvlWrapper
|
|
15
|
-
* nodes={nodes}
|
|
16
|
-
* rels={relationships}
|
|
17
|
-
* nvlOptions={options}
|
|
18
|
-
* nvlCallbacks={callbacks}
|
|
19
|
-
* />
|
|
20
|
-
* ```
|
|
21
|
-
*
|
|
22
|
-
* would be equivalent to
|
|
23
|
-
*
|
|
24
|
-
* ```tsx
|
|
25
|
-
* const myNvl = new NVL(container, nodes, relationships, options, callbacks)
|
|
26
|
-
* ```
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* When nodes and/or relationships are updated in the React wrapper, the NVL instance will be updated accordingly:
|
|
30
|
-
*
|
|
31
|
-
* ```tsx
|
|
32
|
-
* const NvlComponent = () => {
|
|
33
|
-
* const [nodes, setNodes] = useState<Node[]>([{ id: '0' }, { id: '1' }])
|
|
34
|
-
*
|
|
35
|
-
* const addElements = () => {
|
|
36
|
-
* const newNodes = [...nodes, { id: nodes.length.toString() }]
|
|
37
|
-
* setNodes(newNodes)
|
|
38
|
-
* }
|
|
39
|
-
*
|
|
40
|
-
* return <div>
|
|
41
|
-
* <BasicNvlWrapper nodes={nodes} rels={[]} />
|
|
42
|
-
* <button onClick={addElements}>Add Graph Elements</button>
|
|
43
|
-
* </div>
|
|
44
|
-
* }
|
|
45
|
-
* ```
|
|
46
8
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* const container = document.createElement('div')
|
|
51
|
-
* const myButton = document.createElement('button')
|
|
52
|
-
* const nodes = [{ id: '0' }, { id: '1' }]
|
|
53
|
-
* const myNvl = new NVL(container, nodes, [])
|
|
54
|
-
* myButton.addEventListener('click', () => {
|
|
55
|
-
* const newNodes = [...nodes, { id: nodes.length.toString() }]
|
|
56
|
-
* myNvl.addAndUpdateElementsInGraph(newNodes, [])
|
|
57
|
-
* })
|
|
58
|
-
* ```
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* If you want to access the NVL class outside of the React wrapper you can use a
|
|
62
|
-
* reference of NVL to call its methods:
|
|
63
|
-
* ```tsx
|
|
64
|
-
* const NvlComponent = () => {
|
|
65
|
-
* const nvlRef = useRef<NVL>()
|
|
66
|
-
*
|
|
67
|
-
* return <div>
|
|
68
|
-
* <BasicNvlWrapper
|
|
69
|
-
* nodes={[{ id: '0' }, { id: '1' }]}
|
|
70
|
-
* rels={[{ from: '0', to: '1', id: '10' }]}
|
|
71
|
-
* ref={nvlRef}
|
|
72
|
-
* />
|
|
73
|
-
* <button onClick={() => nvlRef.current?.fit([0, 1])}>Zoom to Nodes</button>
|
|
74
|
-
* </div>
|
|
75
|
-
* }
|
|
76
|
-
* ```
|
|
77
|
-
*
|
|
78
|
-
* @example
|
|
79
|
-
* If you want to pass events to the NVL instance you can do so by passing them as properties to the React wrapper:
|
|
80
|
-
*
|
|
81
|
-
* ```tsx
|
|
82
|
-
* <BasicNvlWrapper
|
|
83
|
-
* nodes={nodes}
|
|
84
|
-
* rels={relationships}
|
|
85
|
-
* onClick={(event) => console.log(event)}
|
|
86
|
-
* />
|
|
87
|
-
* ```
|
|
88
|
-
*
|
|
89
|
-
* would be equivalent to
|
|
90
|
-
*
|
|
91
|
-
* ```tsx
|
|
92
|
-
* const container = document.createElement('div')
|
|
93
|
-
* const nodes = [{ id: '0' }, { id: '1' }]
|
|
94
|
-
* const myNvl = new NVL(container, nodes, [])
|
|
95
|
-
* container.addEventListener('click', (event) => console.log(event))
|
|
96
|
-
* ```
|
|
9
|
+
* A basic React wrapper that wraps the base {@link NVL} library within a React component.
|
|
10
|
+
* It takes the class' arguments as properties, which are passed to the NVL constructor.
|
|
11
|
+
* Any changes in properties will be reflected in the NVL instance by calling the corresponding methods.
|
|
97
12
|
*
|
|
98
|
-
* For
|
|
99
|
-
* the Interaction Handlers module and the {@link InteractiveNvlWrapper}.
|
|
13
|
+
* For examples, head to the {@link https://neo4j.com/docs/nvl/current/react-wrappers/#_basic_react_wrapper Basic React wrapper documentation page}.
|
|
100
14
|
*/
|
|
101
15
|
export const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOptions, nvlCallbacks = {}, nvlOptions = {}, onInitializationError, ...nvlEvents }, ref) => {
|
|
102
16
|
const nvlRef = useRef(null);
|
|
@@ -3,34 +3,14 @@ import type { HTMLProps } from 'react';
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import type { InteractiveNvlWrapperProps } from './types';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* The interactive React wrapper component contains a collection of interaction handlers by default
|
|
7
|
+
* and provides a variety of common functionality and callbacks.
|
|
7
8
|
* It is an extension of the {@link BasicNvlWrapper} component and incorporates the
|
|
8
|
-
*
|
|
9
|
+
* @neo4j-nvl/interaction-handlers module's decorators to provide a set of interaction events.
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* when the event is triggered. If a boolean value is passed, the event will be turned on or off
|
|
13
|
-
* accordingly.
|
|
11
|
+
* The mouseEventCallbacks property takes an object where various callbacks can be defined
|
|
12
|
+
* and behavior can be toggled on and off.
|
|
14
13
|
*
|
|
15
|
-
* @
|
|
16
|
-
* ```tsx
|
|
17
|
-
* const InteractiveNvlComponent = () => {
|
|
18
|
-
* const [boxSelect, setBoxSelect] = useState(false)
|
|
19
|
-
* return <>
|
|
20
|
-
* <button onClick={() => setBoxSelect(!boxSelect)}>
|
|
21
|
-
* {boxSelect ? 'Disable' : 'Enable'} box-select
|
|
22
|
-
* </button>
|
|
23
|
-
* <InteractiveNvlWrapper
|
|
24
|
-
* nodes={nodes}
|
|
25
|
-
* rels={relationships}
|
|
26
|
-
* mouseEventCallbacks={{
|
|
27
|
-
* onHover: (element) => console.log(element),
|
|
28
|
-
* onNodeClick: (node) => console.log(node),
|
|
29
|
-
* onBoxSelect: boxSelect
|
|
30
|
-
* }}
|
|
31
|
-
* />
|
|
32
|
-
* </>
|
|
33
|
-
* }
|
|
34
|
-
* ```
|
|
14
|
+
* For examples, head to the {@link https://neo4j.com/docs/nvl/current/react-wrappers/#_interactive_reactive_wrapperr Interactive React wrapper documentation page}.
|
|
35
15
|
*/
|
|
36
16
|
export declare const InteractiveNvlWrapper: React.MemoExoticComponent<React.ForwardRefExoticComponent<Omit<InteractiveNvlWrapperProps & HTMLProps<HTMLDivElement>, "ref"> & React.RefAttributes<NVL>>>;
|
|
@@ -11,40 +11,20 @@ const options = {
|
|
|
11
11
|
excludeNodeMargin: true
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* The interactive React wrapper component contains a collection of interaction handlers by default
|
|
15
|
+
* and provides a variety of common functionality and callbacks.
|
|
15
16
|
* It is an extension of the {@link BasicNvlWrapper} component and incorporates the
|
|
16
|
-
*
|
|
17
|
+
* @neo4j-nvl/interaction-handlers module's decorators to provide a set of interaction events.
|
|
17
18
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* when the event is triggered. If a boolean value is passed, the event will be turned on or off
|
|
21
|
-
* accordingly.
|
|
19
|
+
* The mouseEventCallbacks property takes an object where various callbacks can be defined
|
|
20
|
+
* and behavior can be toggled on and off.
|
|
22
21
|
*
|
|
23
|
-
* @
|
|
24
|
-
* ```tsx
|
|
25
|
-
* const InteractiveNvlComponent = () => {
|
|
26
|
-
* const [boxSelect, setBoxSelect] = useState(false)
|
|
27
|
-
* return <>
|
|
28
|
-
* <button onClick={() => setBoxSelect(!boxSelect)}>
|
|
29
|
-
* {boxSelect ? 'Disable' : 'Enable'} box-select
|
|
30
|
-
* </button>
|
|
31
|
-
* <InteractiveNvlWrapper
|
|
32
|
-
* nodes={nodes}
|
|
33
|
-
* rels={relationships}
|
|
34
|
-
* mouseEventCallbacks={{
|
|
35
|
-
* onHover: (element) => console.log(element),
|
|
36
|
-
* onNodeClick: (node) => console.log(node),
|
|
37
|
-
* onBoxSelect: boxSelect
|
|
38
|
-
* }}
|
|
39
|
-
* />
|
|
40
|
-
* </>
|
|
41
|
-
* }
|
|
42
|
-
* ```
|
|
22
|
+
* For examples, head to the {@link https://neo4j.com/docs/nvl/current/react-wrappers/#_interactive_reactive_wrapperr Interactive React wrapper documentation page}.
|
|
43
23
|
*/
|
|
44
24
|
export const InteractiveNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOptions, onInitializationError, mouseEventCallbacks = {}, nvlCallbacks = {}, nvlOptions = {}, interactionOptions = options, ...nvlEvents }, nvlRef) => {
|
|
45
25
|
const newNvlRef = useRef(null);
|
|
46
26
|
const myNvlRef = nvlRef ?? newNvlRef;
|
|
47
|
-
const { onHover, onNodeClick, onNodeDoubleClick, onNodeRightClick, onRelationshipClick, onRelationshipDoubleClick, onRelationshipRightClick, onCanvasClick, onCanvasRightClick, onPan, onZoom, onDrag, onDragStart, onDragEnd,
|
|
27
|
+
const { onHover, onNodeClick, onNodeDoubleClick, onNodeRightClick, onRelationshipClick, onRelationshipDoubleClick, onRelationshipRightClick, onCanvasClick, onCanvasRightClick, onPan, onZoom, onDrag, onDragStart, onDragEnd, onDrawStarted, onDrawEnded, onHoverNodeMargin, onBoxStarted, onBoxSelect, onLassoStarted, onLassoSelect } = mouseEventCallbacks;
|
|
48
28
|
const hoverInteraction = useRef(null);
|
|
49
29
|
const clickInteraction = useRef(null);
|
|
50
30
|
const panInteraction = useRef(null);
|
|
@@ -68,7 +48,8 @@ export const InteractiveNvlWrapper = memo(forwardRef(({ nodes, rels, layout, lay
|
|
|
68
48
|
useInteraction(DragNodeInteraction, dragNodeInteraction, onDragStart, 'onDragStart', myNvlRef, interactionOptions);
|
|
69
49
|
useInteraction(DragNodeInteraction, dragNodeInteraction, onDragEnd, 'onDragEnd', myNvlRef, interactionOptions);
|
|
70
50
|
useInteraction(DrawInteraction, drawInteraction, onHoverNodeMargin, 'onHoverNodeMargin', myNvlRef, interactionOptions);
|
|
71
|
-
useInteraction(DrawInteraction, drawInteraction,
|
|
51
|
+
useInteraction(DrawInteraction, drawInteraction, onDrawStarted, 'onDrawStarted', myNvlRef, interactionOptions);
|
|
52
|
+
useInteraction(DrawInteraction, drawInteraction, onDrawEnded, 'onDrawEnded', myNvlRef, interactionOptions);
|
|
72
53
|
useInteraction(BoxSelectInteraction, multiSelectInteraction, onBoxStarted, 'onBoxStarted', myNvlRef, interactionOptions);
|
|
73
54
|
useInteraction(BoxSelectInteraction, multiSelectInteraction, onBoxSelect, 'onBoxSelect', myNvlRef, interactionOptions);
|
|
74
55
|
useInteraction(LassoInteraction, lassoInteraction, onLassoStarted, 'onLassoStarted', myNvlRef, interactionOptions);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neo4j-nvl/react",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"homepage": "https://neo4j.com/docs/nvl/current/",
|
|
6
6
|
"license": "SEE LICENSE IN 'LICENSE.txt'",
|
|
@@ -26,12 +26,6 @@
|
|
|
26
26
|
"LICENSE.txt",
|
|
27
27
|
"lib"
|
|
28
28
|
],
|
|
29
|
-
"typedoc": {
|
|
30
|
-
"entryPoint": "./src/index.ts",
|
|
31
|
-
"readmeFile": "./README.md",
|
|
32
|
-
"displayName": "React",
|
|
33
|
-
"tsconfig": "./tsconfig.json"
|
|
34
|
-
},
|
|
35
29
|
"devDependencies": {
|
|
36
30
|
"@testing-library/jest-dom": "^5.16.5",
|
|
37
31
|
"@testing-library/react": "^13.4.0",
|
|
@@ -43,8 +37,8 @@
|
|
|
43
37
|
"typescript": "^5.4.5"
|
|
44
38
|
},
|
|
45
39
|
"dependencies": {
|
|
46
|
-
"@neo4j-nvl/base": "0.3.
|
|
47
|
-
"@neo4j-nvl/interaction-handlers": "0.3.
|
|
40
|
+
"@neo4j-nvl/base": "0.3.4",
|
|
41
|
+
"@neo4j-nvl/interaction-handlers": "0.3.4",
|
|
48
42
|
"lodash": "4.17.21",
|
|
49
43
|
"react": "^18.2.0",
|
|
50
44
|
"react-dom": "^18.2.0"
|
|
@@ -53,6 +47,5 @@
|
|
|
53
47
|
"eslint": "*",
|
|
54
48
|
"jest": "*",
|
|
55
49
|
"typescript": "*"
|
|
56
|
-
}
|
|
57
|
-
"stableVersion": "0.3.3"
|
|
50
|
+
}
|
|
58
51
|
}
|