diff --git a/README.md b/README.md
index dcb6b0dc1..637a4704d 100644
--- a/README.md
+++ b/README.md
@@ -15,12 +15,13 @@
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.
-| Name | Link | Source | Description |
-| :----- | :---------------------------------------------------------------------------------------- | :------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| 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. |
-| 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. |
-| 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. |
-| 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) |
+| Name | Link | Source | Description |
+| :---------- | :---------------------------------------------------------------------------------------- | :------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 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. |
+| 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. |
+| 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. |
+| 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. |
+| 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) |
Do you want to visualize your own data set on the live sandbox? Just submit a PR! You're welcome 😁.
diff --git a/docs/DOCUMENTATION.md b/docs/DOCUMENTATION.md
index 8f700d59f..4180b58aa 100644
--- a/docs/DOCUMENTATION.md
+++ b/docs/DOCUMENTATION.md
@@ -561,6 +561,18 @@ const graph = {
The stroke-linecap options are:- "butt"
- "round"
- "square" (optional, default `"butt"`)
+ - `link.viewGenerator` **[Function][190]** 🔗 🔍 function that receives parameters ( link label, source node, target node, link options ) and returns a JSX view.
+ ```js
+ viewGenerator: (props, options) => (
+ ),
+ ```
### Examples
@@ -1438,9 +1450,11 @@ components.
},
...
}
+
```
```
+
- `linkCallbacks` **[Array][189]<[Function][190]>** array of callbacks for used defined event handler for link interactions.
- `config` **[Object][188]** an object containing rd3g consumer defined configurations [config][200] for the graph.
- `highlightedNode` **[string][187]** this value contains a string that represents the some currently highlighted node.
diff --git a/sandbox/data/custom-link/CustomLink.jsx b/sandbox/data/custom-link/CustomLink.jsx
new file mode 100644
index 000000000..cb098feff
--- /dev/null
+++ b/sandbox/data/custom-link/CustomLink.jsx
@@ -0,0 +1,41 @@
+/* eslint-disable valid-jsdoc */
+import React from "react";
+
+/**
+ * @param {Object} params component props to render.
+ * @param {string} params.label path label
+ * @param {Object} params.source source node
+ * @param {Object} params.target target node
+ * @param {string} params.id path id
+ * @param {Object} params.lineProps line props
+ * @param {Object} params.textProps text props
+ */
+function CustomLink(params) {
+ const { label, source, target, id, lineProps, textProps } = params;
+ const isReverse = target.x < source.x;
+ let fixedLineProps = lineProps;
+ if (isReverse) {
+ const { markerEnd, d, ...rest } = lineProps;
+ const items = d.split(" ");
+ const [sx, sy] = items[0].replace("M", "").split(",");
+ const [tx, ty] = items[items.length - 1].split(",");
+ const sOffset = { x: source.x - sx, y: source.y - sy };
+ const tOffset = { x: target.x - tx, y: target.y - ty };
+ const reverseD = `M${target.x - tOffset.x},${target.y - tOffset.y} ${source.x - sOffset.x},${source.y - sOffset.y}`;
+ fixedLineProps = { ...rest, markerStart: markerEnd, d: reverseD };
+ }
+ return (
+
+
+ {label && (
+
+
+ {label}
+
+
+ )}
+
+ );
+}
+
+export default CustomLink;
diff --git a/sandbox/data/custom-link/custom-link.config.js b/sandbox/data/custom-link/custom-link.config.js
new file mode 100644
index 000000000..05dea76e5
--- /dev/null
+++ b/sandbox/data/custom-link/custom-link.config.js
@@ -0,0 +1,64 @@
+import React from "react";
+import CustomLink from "./CustomLink";
+
+export default {
+ automaticRearrangeAfterDropNode: false,
+ collapsible: false,
+ height: 400,
+ highlightDegree: 1,
+ highlightOpacity: 0.2,
+ linkHighlightBehavior: true,
+ maxZoom: 8,
+ minZoom: 0.1,
+ nodeHighlightBehavior: true,
+ panAndZoom: false,
+ staticGraph: false,
+ width: 800,
+ directed: true,
+ node: {
+ color: "#d3d3d3",
+ fontColor: "black",
+ fontSize: 12,
+ fontWeight: "normal",
+ highlightColor: "red",
+ highlightFontSize: 12,
+ highlightFontWeight: "bold",
+ highlightStrokeColor: "SAME",
+ highlightStrokeWidth: 1.5,
+ labelProperty: "name",
+ mouseCursor: "pointer",
+ opacity: 1,
+ renderLabel: true,
+ size: 450,
+ strokeColor: "none",
+ strokeWidth: 1.5,
+ svg: "",
+ symbolType: "circle",
+ },
+ link: {
+ color: "#d3d3d3",
+ fontColor: "blue",
+ fontSize: 10,
+ highlightColor: "blue",
+ highlightFontWeight: "bold",
+ labelProperty: link => `from ${link.source} to ${link.target}`,
+ opacity: 1,
+ renderLabel: true,
+ semanticStrokeWidth: false,
+ strokeWidth: 4,
+ viewGenerator: (props, options) => (
+
+ ),
+ },
+ d3: {
+ gravity: -400,
+ linkLength: 300,
+ },
+};
diff --git a/sandbox/data/custom-link/custom-link.data.js b/sandbox/data/custom-link/custom-link.data.js
new file mode 100644
index 000000000..81147a05b
--- /dev/null
+++ b/sandbox/data/custom-link/custom-link.data.js
@@ -0,0 +1,38 @@
+module.exports = {
+ links: [
+ {
+ source: 1,
+ target: 2,
+ },
+ {
+ source: 1,
+ target: 3,
+ },
+ {
+ source: 1,
+ target: 4,
+ },
+ {
+ source: 2,
+ target: 3,
+ },
+ ],
+ nodes: [
+ {
+ id: 1,
+ name: "Node 1",
+ },
+ {
+ id: 2,
+ name: "Node 2",
+ },
+ {
+ id: 3,
+ name: "Node 3",
+ },
+ {
+ id: 4,
+ name: "Node 4",
+ },
+ ],
+};
diff --git a/src/components/graph/graph.builder.js b/src/components/graph/graph.builder.js
index 73734e62e..5c477edff 100644
--- a/src/components/graph/graph.builder.js
+++ b/src/components/graph/graph.builder.js
@@ -161,6 +161,7 @@ function buildLinkProps(link, nodes, links, config, linkCallbacks, highlightedNo
strokeDashoffset,
strokeLinecap,
target,
+ viewGenerator: link.viewGenerator || config.link.viewGenerator,
onClickLink: linkCallbacks.onClickLink,
onMouseOutLink: linkCallbacks.onMouseOutLink,
onMouseOverLink: linkCallbacks.onMouseOverLink,
diff --git a/src/components/graph/graph.config.js b/src/components/graph/graph.config.js
index 7c9f9d219..0535fde8a 100644
--- a/src/components/graph/graph.config.js
+++ b/src/components/graph/graph.config.js
@@ -345,5 +345,6 @@ export default {
strokeDasharray: 0,
strokeDashoffset: 0,
strokeLinecap: "butt",
+ viewGenerator: null,
},
};
diff --git a/src/components/graph/graph.renderer.jsx b/src/components/graph/graph.renderer.jsx
index 6c82e6173..5cb6a06a7 100644
--- a/src/components/graph/graph.renderer.jsx
+++ b/src/components/graph/graph.renderer.jsx
@@ -51,8 +51,8 @@ function _renderLinks(nodes, links, linksMatrix, config, linkCallbacks, highligh
highlightedLink,
transform
);
-
- return ;
+ const node = { source: nodes[props.source], target: nodes[props.target] };
+ return ;
});
}
diff --git a/src/components/link/Link.jsx b/src/components/link/Link.jsx
index bf6e87ee0..9c5f5bea1 100644
--- a/src/components/link/Link.jsx
+++ b/src/components/link/Link.jsx
@@ -35,7 +35,8 @@ import React from "react";
* onClickLink={onClickLink}
* onRightClickLink={onRightClickLink}
* onMouseOverLink={onMouseOverLink}
- * onMouseOutLink={onMouseOutLink} />
+ * onMouseOutLink={onMouseOutLink}
+ * node={{source:Node,target:Node}} // for ViewGenerator />
*/
export default class Link extends React.Component {
/**
@@ -92,7 +93,7 @@ export default class Link extends React.Component {
lineProps.markerEnd = `url(#${this.props.markerId})`;
}
- const { label, id } = this.props;
+ const { label, id, node } = this.props;
const textProps = {
dy: -1,
style: {
@@ -102,17 +103,24 @@ export default class Link extends React.Component {
},
};
- return (
-
-
- {label && (
-
-
- {label}
-
-
- )}
-
- );
+ if (this.props.viewGenerator) {
+ return this.props.viewGenerator(
+ { source: node?.source, target: node?.target, label },
+ { id, lineProps, textProps }
+ );
+ } else {
+ return (
+
+
+ {label && (
+
+
+ {label}
+
+
+ )}
+
+ );
+ }
}
}
diff --git a/src/components/marker/Marker.jsx b/src/components/marker/Marker.jsx
index 82a7007ed..e21cea034 100644
--- a/src/components/marker/Marker.jsx
+++ b/src/components/marker/Marker.jsx
@@ -17,7 +17,7 @@ export default class Marker extends React.Component {
refY="0"
markerWidth={this.props.markerWidth}
markerHeight={this.props.markerHeight}
- orient="auto"
+ orient="auto-start-reverse"
fill={this.props.fill}
>
diff --git a/test/graph/__snapshots__/graph.snapshot.spec.js.snap b/test/graph/__snapshots__/graph.snapshot.spec.js.snap
index 850e2e556..892d1fc5e 100644
--- a/test/graph/__snapshots__/graph.snapshot.spec.js.snap
+++ b/test/graph/__snapshots__/graph.snapshot.spec.js.snap
@@ -21,7 +21,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
id="marker-small"
markerHeight={6}
markerWidth={6}
- orient="auto"
+ orient="auto-start-reverse"
refX={0}
refY="0"
viewBox="0 -5 10 10"
@@ -36,7 +36,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
id="marker-small-highlighted"
markerHeight={6}
markerWidth={6}
- orient="auto"
+ orient="auto-start-reverse"
refX={0}
refY="0"
viewBox="0 -5 10 10"
@@ -51,7 +51,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
id="marker-medium"
markerHeight={6}
markerWidth={6}
- orient="auto"
+ orient="auto-start-reverse"
refX={0}
refY="0"
viewBox="0 -5 10 10"
@@ -66,7 +66,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
id="marker-medium-highlighted"
markerHeight={6}
markerWidth={6}
- orient="auto"
+ orient="auto-start-reverse"
refX={0}
refY="0"
viewBox="0 -5 10 10"
@@ -81,7 +81,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
id="marker-large"
markerHeight={6}
markerWidth={6}
- orient="auto"
+ orient="auto-start-reverse"
refX={0}
refY="0"
viewBox="0 -5 10 10"
@@ -96,7 +96,7 @@ exports[`Snapshot - Graph Component should match snapshot 1`] = `
id="marker-large-highlighted"
markerHeight={6}
markerWidth={6}
- orient="auto"
+ orient="auto-start-reverse"
refX={0}
refY="0"
viewBox="0 -5 10 10"
diff --git a/test/link/__snapshots__/link.snapshot.spec.js.snap b/test/link/__snapshots__/link.snapshot.spec.js.snap
index 1b7dc3fd1..6e67c0481 100644
--- a/test/link/__snapshots__/link.snapshot.spec.js.snap
+++ b/test/link/__snapshots__/link.snapshot.spec.js.snap
@@ -22,3 +22,5 @@ exports[`Snapshot - Link Component should match snapshot 1`] = `
/>
`;
+
+exports[`Snapshot - Link Component should match snapshot for viewGenerator 1`] = `null`;
diff --git a/test/link/link.snapshot.spec.js b/test/link/link.snapshot.spec.js
index 1dc1143c3..c6893d2ea 100644
--- a/test/link/link.snapshot.spec.js
+++ b/test/link/link.snapshot.spec.js
@@ -26,9 +26,33 @@ describe("Snapshot - Link Component", () => {
);
that.tree = that.link.toJSON();
+
+ const viewGenerator = () => () => "viewGenerator";
+ that.linkForViewGenerator = renderer.create(
+
+ );
+
+ that.treeForViewGenerator = that.linkForViewGenerator.toJSON();
});
test("should match snapshot", () => {
expect(that.tree).toMatchSnapshot();
});
+ test("should match snapshot for viewGenerator", () => {
+ expect(that.treeForViewGenerator).toMatchSnapshot();
+ });
});
diff --git a/test/marker/__snapshots__/marker.snapshot.spec.js.snap b/test/marker/__snapshots__/marker.snapshot.spec.js.snap
index c20fe399b..35795b6af 100644
--- a/test/marker/__snapshots__/marker.snapshot.spec.js.snap
+++ b/test/marker/__snapshots__/marker.snapshot.spec.js.snap
@@ -5,7 +5,7 @@ exports[`Snapshot - Marker Component should match snapshot 1`] = `
className="marker"
fill="green"
id="id"
- orient="auto"
+ orient="auto-start-reverse"
refX="5"
refY="0"
viewBox="0 -5 10 10"