|
| 1 | +/* eslint-disable no-underscore-dangle */ |
| 2 | +/* eslint-disable no-param-reassign */ |
| 3 | +/* eslint-disable no-use-before-define */ |
| 4 | +/* eslint-disable react/no-string-refs */ |
| 5 | +import React, { Component } from 'react'; |
| 6 | +import PropTypes from 'prop-types'; |
| 7 | +import * as d3 from 'd3'; |
| 8 | +import '../d3tooltip.js' |
| 9 | + |
| 10 | +let root = {}; |
| 11 | +let duration = 750; |
| 12 | +class Chart extends Component { |
| 13 | + constructor(props) { |
| 14 | + super(props); |
| 15 | + this.chartRef = React.createRef(); |
| 16 | + this.maked3Tree = this.maked3Tree.bind(this); |
| 17 | + this.removed3Tree = this.removed3Tree.bind(this); |
| 18 | + } |
| 19 | + componentDidMount() { |
| 20 | + const { snapshot, hierarchy } = this.props; |
| 21 | + console.log('initial props', this.props) |
| 22 | + root = JSON.parse(JSON.stringify(hierarchy)); |
| 23 | + this.maked3Tree(); |
| 24 | + } |
| 25 | + |
| 26 | + componentDidUpdate() { |
| 27 | + const { snapshot, hierarchy } = this.props; |
| 28 | + console.log('updated props', this.props) |
| 29 | + root = JSON.parse(JSON.stringify(hierarchy)); |
| 30 | + this.maked3Tree(); |
| 31 | + } |
| 32 | + |
| 33 | + removed3Tree() { |
| 34 | + const { current } = this.chartRef; |
| 35 | + while (current.hasChildNodes()) { |
| 36 | + current.removeChild(current.lastChild); |
| 37 | + } |
| 38 | + } |
| 39 | + |
| 40 | + maked3Tree() { |
| 41 | + this.removed3Tree(); |
| 42 | + let width = 900; |
| 43 | + let height = 1000; |
| 44 | + let chartContainer = d3.select(this.chartRef.current) |
| 45 | + .append('svg') // chartContainer is now pointing to svg |
| 46 | + .attr('width', width) |
| 47 | + .attr('height', height); |
| 48 | + |
| 49 | + let g = chartContainer |
| 50 | + .append("g") |
| 51 | + // this is changing where the graph is located physically |
| 52 | + .attr("transform", `translate(${width / 2 + 4}, ${height / 2 + 2})`); |
| 53 | + |
| 54 | + // if we consider the container for our radial node graph as a box encapsulating, half of this container width is essentially the radius of our radial node graph |
| 55 | + let radius = width / 2; |
| 56 | + |
| 57 | + // d3.hierarchy constructs a root node from the specified hierarchical data (our object titled dataset), which must be an object representing the root node |
| 58 | + let hierarchy = d3.hierarchy(root); |
| 59 | + |
| 60 | + let tree = d3.tree() |
| 61 | + // this assigns width of tree to be 2pi |
| 62 | + .size([2 * Math.PI, radius]) |
| 63 | + .separation(function (a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth }); |
| 64 | + |
| 65 | + let d3root = tree(hierarchy); |
| 66 | + |
| 67 | + g.selectAll('.link') |
| 68 | + // root.links() gets an array of all the links, where each element is an object containing a source property, which represents the link's source node, and a target property, which represents the link's target node. |
| 69 | + .data(d3root.links()) |
| 70 | + .enter() |
| 71 | + .append('path') |
| 72 | + .attr('class', 'link') |
| 73 | + .attr("d", d3.linkRadial() |
| 74 | + .angle(d => d.x) |
| 75 | + .radius(d => d.y)); |
| 76 | + |
| 77 | + let node = g.selectAll(".node") |
| 78 | + // root.descendants gets an array of of all nodes |
| 79 | + .data(d3root.descendants()) |
| 80 | + .enter() |
| 81 | + .append("g") |
| 82 | + // assigning class to the node based on whether node has children or not |
| 83 | + .attr("class", function (d) { |
| 84 | + return "node" + (d.children ? " node--internal" : " node--leaf") |
| 85 | + }) |
| 86 | + .attr("transform", function (d) { |
| 87 | + return "translate(" + reinfeldTidierAlgo(d.x, d.y) + ")"; |
| 88 | + }); |
| 89 | + |
| 90 | + node.append("circle") |
| 91 | + .attr("r", 15) |
| 92 | + |
| 93 | + // creating a d3.tip method where the html has a function that returns the data we passed into tip.show from line 120 |
| 94 | + let tip = d3.tip() |
| 95 | + .attr("class", "d3-tip") |
| 96 | + .html(function (d) { return "State Snapshot: " + d; }) |
| 97 | + // invoking tooltip for nodes |
| 98 | + node.call(tip) |
| 99 | + |
| 100 | + node |
| 101 | + .append("text") |
| 102 | + // adjusts the y coordinates for the node text |
| 103 | + .attr("dy", "0.276em") |
| 104 | + .attr("x", function (d) { |
| 105 | + // this positions how far the text is from leaf nodes (ones without children) |
| 106 | + // negative number before the colon moves the text of rightside nodes, positive number moves the text for the leftside nodes |
| 107 | + return d.x < Math.PI === !d.children ? -5 : 9; |
| 108 | + }) |
| 109 | + .attr("text-anchor", function (d) { return d.x < Math.PI === !d.children ? "start" : "end"; }) |
| 110 | + // this arranges the angle of the text |
| 111 | + .attr("transform", function (d) { return "rotate(" + (d.x < Math.PI ? d.x - Math.PI / 2 : d.x + Math.PI / 2) * 1 / Math.PI + ")"; }) |
| 112 | + .text(function (d) { |
| 113 | + return d.data.index; |
| 114 | + }); |
| 115 | + |
| 116 | + //applying tooltip on mouseover and removes it when mouse cursor moves away |
| 117 | + node |
| 118 | + .on('mouseover', function (d) { |
| 119 | + // without JSON.stringify, data will display as object Object |
| 120 | + tip.show(JSON.stringify(d.data.stateSnaphot)) |
| 121 | + }) |
| 122 | + .on('mouseout', tip.hide) |
| 123 | + |
| 124 | + function reinfeldTidierAlgo(x, y) { |
| 125 | + return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)]; |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + render() { |
| 130 | + return <div ref={this.chartRef} className="d3Container" />; |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +// Chart.propTypes = { |
| 135 | +// snapshot: PropTypes.arrayOf(PropTypes.object).isRequired, |
| 136 | +// }; |
| 137 | + |
| 138 | +export default Chart; |
0 commit comments