Skip to content

Instantly share code, notes, and snippets.

@shawntan
Created April 17, 2023 18:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shawntan/54f40c40d7417a0ba4c5f06cba5fb8c6 to your computer and use it in GitHub Desktop.
Save shawntan/54f40c40d7417a0ba4c5f06cba5fb8c6 to your computer and use it in GitHub Desktop.
reactjs d3 tree component
<div id="react-app"></div>
let d3Tree = {};
d3Tree.create = function(el, props, state) {
let svg = d3.select(el).append('svg')
.attr('width', props.width)
.attr('height', props.height);
this.width = props.width;
this.height = props.height;
this.tree = d3.layout.tree().size([500, 250]);
this.svg = d3.select(el).select('svg');
this.nodeCounts = 0;
this.update(el, state);
};
d3Tree.update = function(el, state) {
this._drawTree(el, state.data);
};
d3Tree._drawTree = function(el, data) {
let tree = this.tree;
let svg = this.svg;
let nodes = tree.nodes(data);
let nodeCounts = this.nodeCounts;
let g = svg.selectAll('g.node')
.data(nodes, function(d) {
return d.id || (d.id = ++nodeCounts);
});
this.nodeCounts = nodeCounts;
let node = g.data(nodes);
let p = svg.selectAll('path.link');
let link = p.data(tree.links(nodes.slice(1)),
function(d) { return d.target.id; });
let linkLabel = svg.selectAll('text.link-label')
.data(tree.links(nodes.slice(1)),
function(d) { return d.target.id; });
node.enter().append('svg:g')
.attr('class', 'node')
.attr('transform', (d) => {
return `translate(${d.x},${d.y+10})`;
})
.append("svg:circle")
.attr("r", d => d.root?0:6);
node.transition().attr('transform', (d) =>`translate(${d.x},${d.y})`);
node.exit().remove();
link.enter().insert("svg:path", "g")
.attr('class', 'link')
.attr('style', d => d.root?'display:none':'display:visible')
.attr('d', function(d) {
var o = {x: d.source.x, y: d.source.y};
return d3.svg.diagonal().projection(function(d) {
return [o.x, o.y + 10];
})(d);
});
linkLabel.enter().append('text')
.attr('class', 'link-label')
.attr('text-anchor', 'middle')
.attr('x', d => (d.source.x + d.target.x) / 2)
.attr('y', d => (d.source.y + d.target.y) / 2)
.text(d => d.edgeLabel || (d.edgeLabel = d.source.children && d.source.children[0] === d.target ? '0' : '1'));
link.transition().attr('d', function(d) {
return "M" + d.source.x + "," + d.source.y
+ "L" + d.target.x + "," + d.target.y;
});
linkLabel.transition().attr('x', d => (d.source.x + d.target.x) / 2)
.attr('y', d => (d.source.y + d.target.y) / 2)
.text(d => d.source.children && d.source.children[0] === d.target ? '0' : '1');
link.exit().remove();
};
class TreeChart extends React.Component {
componentDidMount() {
var el = ReactDOM.findDOMNode(this);
d3Tree.create(el, {
width: '100%',
height: '300px'
}, this.getChartState());
}
componentDidUpdate() {
var el = ReactDOM.findDOMNode(this);
d3Tree.update(el, this.getChartState());
}
getChartState() {
return {
data: this.props.data
};
}
render() {
return (
<div className="TreeChart"></div>
);
}
}
class App extends React.Component {
static propTypes = {
data: React.PropTypes.object
};
constructor() {
super();
this.state = {
data: {
"root": true,
"children": [
{"children": [{}, {}]},
{"children": [{}, {}]},
{"children": [{}, {}]},
{"children": [
{},
{"children": [{}, {}]},
]}
]
}
};
this.addNode = this.addNode.bind(this);
this.removeNode = this.removeNode.bind(this);
}
addNode() {
//this.state.data.children = this.state.data.children||[];
//this.state.data.children.push({});
var child2 = this.state.data.children.pop();
var child1 = this.state.data.children.pop();
this.state.data.children.push({
"children": [child1, child2]
});
this.setState({
data: this.state.data
});
}
removeNode(){
this.state.data.children.pop();
this.setState({
data: this.state.data
});
}
render() {
return (
<div className="App">
<TreeChart data={this.state.data} />
<button onClick={this.addNode}>merge nodes</button>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('react-app'));
<script src="https://npmcdn.com/react@15.3.0/dist/react.min.js"></script>
<script src="https://npmcdn.com/react-dom@15.3.0/dist/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
.link {
fill: none;
stroke: black;
stroke-width: 1.5px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment