Skip to content

add link.view_generator #459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 😁.

Expand Down
14 changes: 14 additions & 0 deletions docs/DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,18 @@ const graph = {
The stroke-linecap options are:- "butt"
- "round"
- "square" (optional, default `"butt"`)
- `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.
```js
viewGenerator: (props, options) => (
<CustomLink
label={props.label}
source={props.source}
target={props.target}
id={options.id}
textProps={options.textProps}
lineProps={options.lineProps}
/>),
```

### Examples

Expand Down Expand Up @@ -1438,9 +1450,11 @@ components.
},
...
}

```

```

- `linkCallbacks` **[Array][189]&lt;[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.
Expand Down
41 changes: 41 additions & 0 deletions sandbox/data/custom-link/CustomLink.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<g>
<path {...fixedLineProps} id={id} />
{label && (
<text style={{ textAnchor: "middle" }} {...textProps}>
<textPath href={`#${id}`} startOffset="50%">
{label}
</textPath>
</text>
)}
</g>
);
}

export default CustomLink;
64 changes: 64 additions & 0 deletions sandbox/data/custom-link/custom-link.config.js
Original file line number Diff line number Diff line change
@@ -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) => (
<CustomLink
label={props.label}
source={props.source}
target={props.target}
id={options.id}
textProps={options.textProps}
lineProps={options.lineProps}
/>
),
},
d3: {
gravity: -400,
linkLength: 300,
},
};
38 changes: 38 additions & 0 deletions sandbox/data/custom-link/custom-link.data.js
Original file line number Diff line number Diff line change
@@ -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",
},
],
};
1 change: 1 addition & 0 deletions src/components/graph/graph.builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/components/graph/graph.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,5 +345,6 @@ export default {
strokeDasharray: 0,
strokeDashoffset: 0,
strokeLinecap: "butt",
viewGenerator: null,
},
};
4 changes: 2 additions & 2 deletions src/components/graph/graph.renderer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ function _renderLinks(nodes, links, linksMatrix, config, linkCallbacks, highligh
highlightedLink,
transform
);

return <Link key={key} id={key} {...props} />;
const node = { source: nodes[props.source], target: nodes[props.target] };
return <Link key={key} id={key} {...props} node={node} />;
});
}

Expand Down
36 changes: 22 additions & 14 deletions src/components/link/Link.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand Down Expand Up @@ -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: {
Expand All @@ -102,17 +103,24 @@ export default class Link extends React.Component {
},
};

return (
<g>
<path {...lineProps} id={id} />
{label && (
<text style={{ textAnchor: "middle" }} {...textProps}>
<textPath href={`#${id}`} startOffset="50%">
{label}
</textPath>
</text>
)}
</g>
);
if (this.props.viewGenerator) {
return this.props.viewGenerator(
{ source: node?.source, target: node?.target, label },
{ id, lineProps, textProps }
);
} else {
return (
<g>
<path {...lineProps} id={id} />
{label && (
<text style={{ textAnchor: "middle" }} {...textProps}>
<textPath href={`#${id}`} startOffset="50%">
{label}
</textPath>
</text>
)}
</g>
);
}
}
}
2 changes: 1 addition & 1 deletion src/components/marker/Marker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
>
<path d="M0,-5L10,0L0,5" />
Expand Down
12 changes: 6 additions & 6 deletions test/graph/__snapshots__/graph.snapshot.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand All @@ -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"
Expand All @@ -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"
Expand All @@ -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"
Expand All @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions test/link/__snapshots__/link.snapshot.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ exports[`Snapshot - Link Component should match snapshot 1`] = `
/>
</g>
`;

exports[`Snapshot - Link Component should match snapshot for viewGenerator 1`] = `null`;
24 changes: 24 additions & 0 deletions test/link/link.snapshot.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,33 @@ describe("Snapshot - Link Component", () => {
);

that.tree = that.link.toJSON();

const viewGenerator = () => () => "viewGenerator";
that.linkForViewGenerator = renderer.create(
<Link
x1="2"
y1="2"
x2="4"
y2="4"
opacity="1"
stroke="red"
strokeWidth="2"
onClickLink={that.callbackMock}
strokeDasharray={0}
strokeDashoffset={0}
strokeLinecap="butt"
node={{ source: { x: 2, y: 2 }, target: { x: 4, y: 4 } }}
viewGenerator={viewGenerator}
/>
);

that.treeForViewGenerator = that.linkForViewGenerator.toJSON();
});

test("should match snapshot", () => {
expect(that.tree).toMatchSnapshot();
});
test("should match snapshot for viewGenerator", () => {
expect(that.treeForViewGenerator).toMatchSnapshot();
});
});
2 changes: 1 addition & 1 deletion test/marker/__snapshots__/marker.snapshot.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down