Skip to content

Commit b8a75fe

Browse files
committed
add link/viewGenerator
1 parent a1bd350 commit b8a75fe

File tree

14 files changed

+224
-30
lines changed

14 files changed

+224
-30
lines changed

README.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515

1616
You can also load different data sets and configurations via URL query parameter. Below is a table with all the data sets available in the live sandbox for you to interactively explore different kinds of integrations with the library.
1717

18-
| Name | Link | Source | Description |
19-
| :----- | :---------------------------------------------------------------------------------------- | :------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
20-
| small | [demo](https://danielcaldas.github.io/react-d3-graph/sandbox/index.html?data=small) | `sandbox/data/small` | This is a good example to get you started. It has only 4 nodes. It's good to discuss over integration details and it's also good to report issues that you might found in the library. It's much easier to debug over a tiny graph. |
21-
| custom | [demo](https://danielcaldas.github.io/react-d3-graph/sandbox/index.html?data=custom-node) | `sandbox/data/custom-node` | In this example you'll be able to see the power of the feature [node.viewGenerator](https://danielcaldas.github.io/react-d3-graph/docs/#node-view-generator) to create highly customizable nodes for you graph that go beyond the simple shapes that come out of the box with the library. |
22-
| marvel | [demo](https://danielcaldas.github.io/react-d3-graph/sandbox/index.html?data=marvel) | `sandbox/data/marvel` | In this thematic example you can see how several features such as: [nodeHighlightBehavior](https://danielcaldas.github.io/react-d3-graph/docs/#node-highlight-behavior), [custom SVGs for nodes](https://danielcaldas.github.io/react-d3-graph/docs/#node-svg), [collapsible](https://danielcaldas.github.io/react-d3-graph/docs/#collapsible) etc. come together on top of a directed graph that displays some characters from the Marvel Universe. |
23-
| static | [demo](https://danielcaldas.github.io/react-d3-graph/sandbox/index.html?data=static) | `sandbox/data/static` | If your goal is not to have nodes dancing around with the default [d3 forces](https://danielcaldas.github.io/react-d3-graph/docs/#config-d3) that the library provides, you can opt by making your nodes static and positioned them always in the same _(x, y)_ coordinates. To achieve this you can make use of [staticGraphWithDragAndDrop](https://danielcaldas.github.io/react-d3-graph/docs/#static-graph-with-drag-and-drop) or [staticGraph](https://danielcaldas.github.io/react-d3-graph/docs/#static-graph) |
18+
| Name | Link | Source | Description |
19+
| :---------- | :---------------------------------------------------------------------------------------- | :------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
20+
| small | [demo](https://danielcaldas.github.io/react-d3-graph/sandbox/index.html?data=small) | `sandbox/data/small` | This is a good example to get you started. It has only 4 nodes. It's good to discuss over integration details and it's also good to report issues that you might found in the library. It's much easier to debug over a tiny graph. |
21+
| custom_node | [demo](https://danielcaldas.github.io/react-d3-graph/sandbox/index.html?data=custom-node) | `sandbox/data/custom-node` | In this example you'll be able to see the power of the feature [node.viewGenerator](https://danielcaldas.github.io/react-d3-graph/docs/#node-view-generator) to create highly customizable nodes for you graph that go beyond the simple shapes that come out of the box with the library. |
22+
| custom_link | [demo](https://danielcaldas.github.io/react-d3-graph/sandbox/index.html?data=custom-link) | `sandbox/data/custom-link` | In this example you'll be able to see the power of the feature [link.viewGenerator](https://danielcaldas.github.io/react-d3-graph/docs/#link-view-generator) to create highly customizable links for you graph. |
23+
| marvel | [demo](https://danielcaldas.github.io/react-d3-graph/sandbox/index.html?data=marvel) | `sandbox/data/marvel` | In this thematic example you can see how several features such as: [nodeHighlightBehavior](https://danielcaldas.github.io/react-d3-graph/docs/#node-highlight-behavior), [custom SVGs for nodes](https://danielcaldas.github.io/react-d3-graph/docs/#node-svg), [collapsible](https://danielcaldas.github.io/react-d3-graph/docs/#collapsible) etc. come together on top of a directed graph that displays some characters from the Marvel Universe. |
24+
| static | [demo](https://danielcaldas.github.io/react-d3-graph/sandbox/index.html?data=static) | `sandbox/data/static` | If your goal is not to have nodes dancing around with the default [d3 forces](https://danielcaldas.github.io/react-d3-graph/docs/#config-d3) that the library provides, you can opt by making your nodes static and positioned them always in the same _(x, y)_ coordinates. To achieve this you can make use of [staticGraphWithDragAndDrop](https://danielcaldas.github.io/react-d3-graph/docs/#static-graph-with-drag-and-drop) or [staticGraph](https://danielcaldas.github.io/react-d3-graph/docs/#static-graph) |
2425

2526
Do you want to visualize your own data set on the live sandbox? Just submit a PR! You're welcome 😁.
2627

docs/DOCUMENTATION.md

+14
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,18 @@ const graph = {
561561
The stroke-linecap options are:- "butt"
562562
- "round"
563563
- "square" (optional, default `"butt"`)
564+
- `link.viewGenerator` **[Function][190]** <a id="link-view-generator" href="#link-view-generator">🔗</a> 🔍 function that receives parameters ( link label, source node, target node, link options ) and returns a JSX view.
565+
```js
566+
viewGenerator: (props, options) => (
567+
<CustomLink
568+
label={props.label}
569+
source={props.source}
570+
target={props.target}
571+
id={options.id}
572+
textProps={options.textProps}
573+
lineProps={options.lineProps}
574+
/>),
575+
```
564576

565577
### Examples
566578

@@ -1438,9 +1450,11 @@ components.
14381450
},
14391451
...
14401452
}
1453+
14411454
```
14421455
14431456
```
1457+
14441458
- `linkCallbacks` **[Array][189]&lt;[Function][190]>** array of callbacks for used defined event handler for link interactions.
14451459
- `config` **[Object][188]** an object containing rd3g consumer defined configurations [config][200] for the graph.
14461460
- `highlightedNode` **[string][187]** this value contains a string that represents the some currently highlighted node.
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/* eslint-disable valid-jsdoc */
2+
import React from "react";
3+
4+
/**
5+
* @param {Object} params component props to render.
6+
* @param {string} params.label path label
7+
* @param {Object} params.source source node
8+
* @param {Object} params.target target node
9+
* @param {string} params.id path id
10+
* @param {Object} params.lineProps line props
11+
* @param {Object} params.textProps text props
12+
*/
13+
function CustomLink(params) {
14+
const { label, source, target, id, lineProps, textProps } = params;
15+
const isReverse = target.x < source.x;
16+
let fixedLineProps = lineProps;
17+
if (isReverse) {
18+
const { markerEnd, d, ...rest } = lineProps;
19+
const items = d.split(" ");
20+
const [sx, sy] = items[0].replace("M", "").split(",");
21+
const [tx, ty] = items[items.length - 1].split(",");
22+
const sOffset = { x: source.x - sx, y: source.y - sy };
23+
const tOffset = { x: target.x - tx, y: target.y - ty };
24+
const reverseD = `M${target.x - tOffset.x},${target.y - tOffset.y} ${source.x - sOffset.x},${source.y - sOffset.y}`;
25+
fixedLineProps = { ...rest, markerStart: markerEnd, d: reverseD };
26+
}
27+
return (
28+
<g>
29+
<path {...fixedLineProps} id={id} />
30+
{label && (
31+
<text style={{ textAnchor: "middle" }} {...textProps}>
32+
<textPath href={`#${id}`} startOffset="50%">
33+
{label}
34+
</textPath>
35+
</text>
36+
)}
37+
</g>
38+
);
39+
}
40+
41+
export default CustomLink;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from "react";
2+
import CustomLink from "./CustomLink";
3+
4+
export default {
5+
automaticRearrangeAfterDropNode: false,
6+
collapsible: false,
7+
height: 400,
8+
highlightDegree: 1,
9+
highlightOpacity: 0.2,
10+
linkHighlightBehavior: true,
11+
maxZoom: 8,
12+
minZoom: 0.1,
13+
nodeHighlightBehavior: true,
14+
panAndZoom: false,
15+
staticGraph: false,
16+
width: 800,
17+
directed: true,
18+
node: {
19+
color: "#d3d3d3",
20+
fontColor: "black",
21+
fontSize: 12,
22+
fontWeight: "normal",
23+
highlightColor: "red",
24+
highlightFontSize: 12,
25+
highlightFontWeight: "bold",
26+
highlightStrokeColor: "SAME",
27+
highlightStrokeWidth: 1.5,
28+
labelProperty: "name",
29+
mouseCursor: "pointer",
30+
opacity: 1,
31+
renderLabel: true,
32+
size: 450,
33+
strokeColor: "none",
34+
strokeWidth: 1.5,
35+
svg: "",
36+
symbolType: "circle",
37+
},
38+
link: {
39+
color: "#d3d3d3",
40+
fontColor: "blue",
41+
fontSize: 10,
42+
highlightColor: "blue",
43+
highlightFontWeight: "bold",
44+
labelProperty: link => `from ${link.source} to ${link.target}`,
45+
opacity: 1,
46+
renderLabel: true,
47+
semanticStrokeWidth: false,
48+
strokeWidth: 4,
49+
viewGenerator: (props, options) => (
50+
<CustomLink
51+
label={props.label}
52+
source={props.source}
53+
target={props.target}
54+
id={options.id}
55+
textProps={options.textProps}
56+
lineProps={options.lineProps}
57+
/>
58+
),
59+
},
60+
d3: {
61+
gravity: -400,
62+
linkLength: 300,
63+
},
64+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module.exports = {
2+
links: [
3+
{
4+
source: 1,
5+
target: 2,
6+
},
7+
{
8+
source: 1,
9+
target: 3,
10+
},
11+
{
12+
source: 1,
13+
target: 4,
14+
},
15+
{
16+
source: 2,
17+
target: 3,
18+
},
19+
],
20+
nodes: [
21+
{
22+
id: 1,
23+
name: "Node 1",
24+
},
25+
{
26+
id: 2,
27+
name: "Node 2",
28+
},
29+
{
30+
id: 3,
31+
name: "Node 3",
32+
},
33+
{
34+
id: 4,
35+
name: "Node 4",
36+
},
37+
],
38+
};

src/components/graph/graph.builder.js

+1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ function buildLinkProps(link, nodes, links, config, linkCallbacks, highlightedNo
161161
strokeDashoffset,
162162
strokeLinecap,
163163
target,
164+
viewGenerator: link.viewGenerator || config.link.viewGenerator,
164165
onClickLink: linkCallbacks.onClickLink,
165166
onMouseOutLink: linkCallbacks.onMouseOutLink,
166167
onMouseOverLink: linkCallbacks.onMouseOverLink,

src/components/graph/graph.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -345,5 +345,6 @@ export default {
345345
strokeDasharray: 0,
346346
strokeDashoffset: 0,
347347
strokeLinecap: "butt",
348+
viewGenerator: null,
348349
},
349350
};

src/components/graph/graph.renderer.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ function _renderLinks(nodes, links, linksMatrix, config, linkCallbacks, highligh
5151
highlightedLink,
5252
transform
5353
);
54-
55-
return <Link key={key} id={key} {...props} />;
54+
const node = { source: nodes[props.source], target: nodes[props.target] };
55+
return <Link key={key} id={key} {...props} node={node} />;
5656
});
5757
}
5858

src/components/link/Link.jsx

+22-14
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ import React from "react";
3535
* onClickLink={onClickLink}
3636
* onRightClickLink={onRightClickLink}
3737
* onMouseOverLink={onMouseOverLink}
38-
* onMouseOutLink={onMouseOutLink} />
38+
* onMouseOutLink={onMouseOutLink}
39+
* node={{source:Node,target:Node}} // for ViewGenerator />
3940
*/
4041
export default class Link extends React.Component {
4142
/**
@@ -92,7 +93,7 @@ export default class Link extends React.Component {
9293
lineProps.markerEnd = `url(#${this.props.markerId})`;
9394
}
9495

95-
const { label, id } = this.props;
96+
const { label, id, node } = this.props;
9697
const textProps = {
9798
dy: -1,
9899
style: {
@@ -102,17 +103,24 @@ export default class Link extends React.Component {
102103
},
103104
};
104105

105-
return (
106-
<g>
107-
<path {...lineProps} id={id} />
108-
{label && (
109-
<text style={{ textAnchor: "middle" }} {...textProps}>
110-
<textPath href={`#${id}`} startOffset="50%">
111-
{label}
112-
</textPath>
113-
</text>
114-
)}
115-
</g>
116-
);
106+
if (this.props.viewGenerator) {
107+
return this.props.viewGenerator(
108+
{ source: node?.source, target: node?.target, label },
109+
{ id, lineProps, textProps }
110+
);
111+
} else {
112+
return (
113+
<g>
114+
<path {...lineProps} id={id} />
115+
{label && (
116+
<text style={{ textAnchor: "middle" }} {...textProps}>
117+
<textPath href={`#${id}`} startOffset="50%">
118+
{label}
119+
</textPath>
120+
</text>
121+
)}
122+
</g>
123+
);
124+
}
117125
}
118126
}

src/components/marker/Marker.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export default class Marker extends React.Component {
1717
refY="0"
1818
markerWidth={this.props.markerWidth}
1919
markerHeight={this.props.markerHeight}
20-
orient="auto"
20+
orient="auto-start-reverse"
2121
fill={this.props.fill}
2222
>
2323
<path d="M0,-5L10,0L0,5" />

test/graph/__snapshots__/graph.snapshot.spec.js.snap

+6-6
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
2121
id="marker-small"
2222
markerHeight={6}
2323
markerWidth={6}
24-
orient="auto"
24+
orient="auto-start-reverse"
2525
refX={0}
2626
refY="0"
2727
viewBox="0 -5 10 10"
@@ -36,7 +36,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
3636
id="marker-small-highlighted"
3737
markerHeight={6}
3838
markerWidth={6}
39-
orient="auto"
39+
orient="auto-start-reverse"
4040
refX={0}
4141
refY="0"
4242
viewBox="0 -5 10 10"
@@ -51,7 +51,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
5151
id="marker-medium"
5252
markerHeight={6}
5353
markerWidth={6}
54-
orient="auto"
54+
orient="auto-start-reverse"
5555
refX={0}
5656
refY="0"
5757
viewBox="0 -5 10 10"
@@ -66,7 +66,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
6666
id="marker-medium-highlighted"
6767
markerHeight={6}
6868
markerWidth={6}
69-
orient="auto"
69+
orient="auto-start-reverse"
7070
refX={0}
7171
refY="0"
7272
viewBox="0 -5 10 10"
@@ -81,7 +81,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
8181
id="marker-large"
8282
markerHeight={6}
8383
markerWidth={6}
84-
orient="auto"
84+
orient="auto-start-reverse"
8585
refX={0}
8686
refY="0"
8787
viewBox="0 -5 10 10"
@@ -96,7 +96,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
9696
id="marker-large-highlighted"
9797
markerHeight={6}
9898
markerWidth={6}
99-
orient="auto"
99+
orient="auto-start-reverse"
100100
refX={0}
101101
refY="0"
102102
viewBox="0 -5 10 10"

test/link/__snapshots__/link.snapshot.spec.js.snap

+2
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ exports[`Snapshot - Link Component should match snapshot 1`] = `
2222
/>
2323
</g>
2424
`;
25+
26+
exports[`Snapshot - Link Component should match snapshot for viewGenerator 1`] = `null`;

test/link/link.snapshot.spec.js

+24
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,33 @@ describe("Snapshot - Link Component", () => {
2626
);
2727

2828
that.tree = that.link.toJSON();
29+
30+
const viewGenerator = () => () => "viewGenerator";
31+
that.linkForViewGenerator = renderer.create(
32+
<Link
33+
x1="2"
34+
y1="2"
35+
x2="4"
36+
y2="4"
37+
opacity="1"
38+
stroke="red"
39+
strokeWidth="2"
40+
onClickLink={that.callbackMock}
41+
strokeDasharray={0}
42+
strokeDashoffset={0}
43+
strokeLinecap="butt"
44+
node={{ source: { x: 2, y: 2 }, target: { x: 4, y: 4 } }}
45+
viewGenerator={viewGenerator}
46+
/>
47+
);
48+
49+
that.treeForViewGenerator = that.linkForViewGenerator.toJSON();
2950
});
3051

3152
test("should match snapshot", () => {
3253
expect(that.tree).toMatchSnapshot();
3354
});
55+
test("should match snapshot for viewGenerator", () => {
56+
expect(that.treeForViewGenerator).toMatchSnapshot();
57+
});
3458
});

test/marker/__snapshots__/marker.snapshot.spec.js.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ exports[`Snapshot - Marker Component should match snapshot 1`] = `
55
className="marker"
66
fill="green"
77
id="id"
8-
orient="auto"
8+
orient="auto-start-reverse"
99
refX="5"
1010
refY="0"
1111
viewBox="0 -5 10 10"

0 commit comments

Comments
 (0)