-
Notifications
You must be signed in to change notification settings - Fork 833
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes #34.
- Loading branch information
Showing
6 changed files
with
286 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
# Sankey | ||
|
||
Note: This component is in alpha. | ||
|
||
### Usage | ||
|
||
```jsx | ||
import {Sankey} from 'react-vis'; | ||
|
||
const nodes = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; | ||
const links = [ | ||
{source: 0, target: 1, value: 10}, | ||
{source: 0, target: 2, value: 20}, | ||
{source: 1, target: 2, value: 20} | ||
]; | ||
|
||
<Sankey | ||
nodes={nodes} | ||
links={links} | ||
width={200} | ||
height={200} | ||
/> | ||
``` | ||
|
||
### Api | ||
|
||
##### width (required, pixels) | ||
##### height (required, pixels) | ||
##### nodes (required) | ||
|
||
An array of objects matching the following shape: | ||
|
||
``` | ||
{ | ||
color: String, | ||
opacity: Number, | ||
key: String | ||
} | ||
``` | ||
|
||
All these fields are optional. | ||
|
||
##### links (required) | ||
|
||
An array of objects matching the following shape, where both `source` and `target` | ||
are the indexes of the nodes they intent to represent, and `value` that would | ||
match the height of the path link. | ||
|
||
``` | ||
{ | ||
// required | ||
source: Number, | ||
target: Number, | ||
value: Number, | ||
// optional | ||
color: String, | ||
opacity: Number, | ||
key: String | ||
} | ||
``` | ||
|
||
##### margin (pixels) | ||
|
||
The margin that will be applied to each side of the Sankey. | ||
|
||
Defaults to `20`. | ||
|
||
##### nodeWidth (pixels) | ||
|
||
Width of the nodes. | ||
|
||
Defaults to `10`. | ||
|
||
##### nodePadding (pixels) | ||
|
||
Padding between each node. | ||
|
||
Defaults to `10`. | ||
|
||
##### align | ||
|
||
The alignment used for the sankey ([example](http://bl.ocks.org/vasturiano/b0b14f2e58fdeb0da61e62d51c649908)). | ||
Can be `justify`, `center`, `left`, `right`. | ||
|
||
Defaults to `justify`. | ||
|
||
##### layout | ||
|
||
The number of passes the sankey algorithm will do in order to arrange positioning. | ||
|
||
Defaults to `50`. | ||
|
||
##### hasVoronoi | ||
|
||
Determine if the node selection will be done using a voronoi or not. Although less | ||
precise, it can help providing a better interactive experience to the user. | ||
|
||
Defaults to `false`. | ||
|
||
##### onClick | ||
|
||
Callback when clicking a node, or the voronoi assigned to this node, pass the node. | ||
|
||
##### onHover | ||
|
||
Callback when hovering a node, or the voronoi assigned to this node, pass the node. | ||
|
||
##### onBlur | ||
|
||
Callback when bluring a node, or the voronoi assigned to this node, pass the node. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import React from 'react'; | ||
|
||
import Sankey from 'sankey'; | ||
|
||
const nodes = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; | ||
const links = [ | ||
{source: 0, target: 1, value: 10}, | ||
{source: 0, target: 2, value: 20}, | ||
{source: 1, target: 2, value: 20} | ||
]; | ||
|
||
export default function BasicSankeyExample() { | ||
return ( | ||
<Sankey | ||
nodes={nodes} | ||
links={links} | ||
width={200} | ||
height={200} | ||
/> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import React, {PropTypes, Component} from 'react'; | ||
import {voronoi} from 'd3-voronoi'; | ||
import {sankey} from 'd3-sankey-align'; | ||
|
||
import {DISCRETE_COLOR_RANGE} from 'theme'; | ||
|
||
const NOOP = f => f; | ||
|
||
const DEFAULT_LINK_COLOR = DISCRETE_COLOR_RANGE[1]; | ||
const DEFAULT_LINK_OPACITY = 0.7; | ||
const DEFAULT_NODE_COLOR = DISCRETE_COLOR_RANGE[0]; | ||
const DEFAULT_NODE_OPACITY = 1; | ||
|
||
class Sankey extends Component { | ||
|
||
static defaultProps = { | ||
align: 'justify', | ||
className: '', | ||
hasVoronoi: false, | ||
layout: 50, | ||
margin: 20, | ||
nodePadding: 10, | ||
nodeWidth: 10, | ||
onBlur: NOOP, | ||
onClick: NOOP, | ||
onHover: NOOP | ||
} | ||
|
||
static propTypes = { | ||
align: PropTypes.oneOf(['justify', 'left', 'right', 'center']), | ||
className: PropTypes.string, | ||
hasVoronoi: PropTypes.bool, | ||
height: PropTypes.number.isRequired, | ||
layout: PropTypes.number, | ||
links: PropTypes.arrayOf(PropTypes.shape({ | ||
source: PropTypes.number.isRequired, | ||
target: PropTypes.number.isRequired | ||
})).isRequired, | ||
margin: PropTypes.number, | ||
nodePadding: PropTypes.number, | ||
nodes: PropTypes.arrayOf(PropTypes.object).isRequired, | ||
nodeWidth: PropTypes.number, | ||
onBlur: PropTypes.func, | ||
onClick: PropTypes.func, | ||
onHover: PropTypes.func, | ||
width: PropTypes.number.isRequired | ||
} | ||
|
||
render() { | ||
|
||
const { | ||
align, | ||
className, | ||
hasVoronoi, | ||
height, | ||
layout, | ||
links, | ||
margin, | ||
nodePadding, | ||
nodes, | ||
nodeWidth, | ||
onBlur, | ||
onClick, | ||
onHover, | ||
width | ||
} = this.props; | ||
|
||
const sankeyInstance = sankey() | ||
.size([width, height]) | ||
.nodeWidth(nodeWidth) | ||
.nodePadding(nodePadding) | ||
.nodes(nodes) | ||
.links(links) | ||
.align(align) | ||
.layout(layout); | ||
|
||
const nWidth = sankeyInstance.nodeWidth(); | ||
const path = sankeyInstance.link(); | ||
|
||
// Create a voronoi with each node center points | ||
const voronoiInstance = hasVoronoi ? | ||
voronoi() | ||
.x(d => d.x + d.dx / 2) | ||
.y(d => d.y + d.dy / 2) | ||
.extent([[-margin, -margin], [width + margin, height + margin]]) : | ||
null; | ||
|
||
return ( | ||
<svg height={height + margin} width={width + margin} className={`rv-sankey ${className}`}> | ||
<g transform={`translate(${margin / 2}, ${margin / 2})`}> | ||
|
||
{links.map((link, i) => ( | ||
<path | ||
d={path(link)} | ||
className="rv-sankey__link" | ||
opacity={Number.isFinite(link.opacity) ? link.opacity : DEFAULT_LINK_OPACITY} | ||
stroke={link.color || DEFAULT_LINK_COLOR} | ||
strokeWidth={Math.max(1, link.dy)} | ||
fill="none" | ||
key={link.id || link.key || `link-${i}`} /> | ||
))} | ||
|
||
{nodes.map((node, i) => ( | ||
<g | ||
transform={`translate(${node.x}, ${node.y})`} | ||
className="rv-sankey__node" | ||
opacity={Number.isFinite(node.opacity) ? node.opacity : DEFAULT_NODE_OPACITY} | ||
key={node.id || node.key || `node-${i}`}> | ||
<rect | ||
onClick={() => onClick(node)} | ||
onMouseOver={() => onHover(node)} | ||
onMouseOut={() => onBlur(node)} | ||
fill={node.color || DEFAULT_NODE_COLOR} | ||
height={node.dy} | ||
width={nWidth} /> | ||
</g> | ||
))} | ||
|
||
{hasVoronoi && ( | ||
<g className="rv-sankey__voronoi"> | ||
{voronoiInstance.polygons(nodes).map((d, i) => ( | ||
<path | ||
d={`M${d.join('L')}Z`} | ||
onClick={() => onClick(d.data)} | ||
onMouseOver={() => onHover(d.data)} | ||
onMouseOut={() => onBlur(d.data)} | ||
fill="none" | ||
style={{pointerEvents: 'all'}} | ||
key={i} /> | ||
))} | ||
</g> | ||
)} | ||
|
||
</g> | ||
</svg> | ||
); | ||
} | ||
|
||
} | ||
|
||
export default Sankey; |