blob: fe4360bd83f1412672fd86156ec050bb1f6d474b [file] [log] [blame]
/*
*
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import React from 'react';
import d3 from 'd3';
import DashboardCard from '../dashboard_card/dashboard_card.jsx';
export default class TopologyL2Graph extends React.Component {
constructor(props) {
super(props);
this.data = props.data;
this.selectedID = 0;
this.nodeCount = 0;
this.network_coding = {}
this.nodeEvent = props.nodeEvent || null;
}
componentDidMount(){
var weight = 400;
var handler = this;
this.force = d3.layout.force()
.size([this.props.width, this.props.height])
.charge(-weight)
.linkDistance(weight)
.on("tick", this.tick.bind(this));
this.drag = this.force.drag()
.on("dragstart", function(d) {
handler.dragstart(d, handler);
});
}
componentWillUnmount() {
d3.select('svg').remove();
}
componentWillReceiveProps(props) {
if(!this.svg) {
// NOTE: We may need to revisit how D3 accesses DOM elements
this.svg = d3.select(document.querySelector('#topologyL2')).append("svg")
.attr("width", this.props.width)
.attr("height", this.props.height)
.classed("topology", true);
}
if (props.data.links.length > 0) {
this.network_coding = this.create_group_coding(props.data.network_ids.sort());
this.update(props.data);
}
}
create_group_coding(group_ids) {
var group_coding = {};
group_ids.forEach(function(element, index, array) {
group_coding[element] = index+1;
});
return group_coding;
}
getNetworkCoding(network_id) {
var group = this.network_coding[network_id];
if (group != undefined) {
return group;
} else {
return 0;
}
}
drawLegend(graph) {
// Hack to prevent multiple legends being displayed
this.svg.selectAll(".legend").remove();
var showBox = false;
var svg = this.svg;
var item_count = (graph.network_ids) ? graph.network_ids.length : 0;
var pos = {
anchorX: 5,
anchorY: 5,
height: 40,
width: 200,
items_y: 35,
items_x: 7,
item_height: 25
};
pos.height += item_count * pos.item_height;
var legend_translate = "translate("+pos.anchorX+","+pos.anchorY+")";
var legend = svg.append("g")
.attr("class", "legend")
.attr("transform", legend_translate);
var legend_box = (showBox) ? legend.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("height", pos.height)
.attr("width", pos.width)
.style("stroke", "black")
.style("fill", "none") : null;
legend.append("text")
.attr("x", 5)
.attr("y", 15)
.text("Network color mapping:");
legend.selectAll("g").data(graph.network_ids)
.enter()
.append("g")
.each(function(d, i) {
var colors = ["green", "orange", "red" ];
var g = d3.select(this);
var group_number = i+1;
g.attr('class', "node-group-" + group_number);
g.append("circle")
.attr("cx", pos.items_x + 3)
.attr("cy", pos.items_y + i * pos.item_height)
.attr("r", 6);
g.append("text")
.attr("x", pos.items_x + 25)
.attr("y", pos.items_y + (i * pos.item_height + 4))
.attr("height", 20)
.attr("width", 80)
.text(d);
})
}
update(graph) {
var svg = this.svg;
var handler = this;
this.force
.nodes(graph.nodes)
.links(graph.links)
.start();
this.link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link");
this.gnodes = svg.selectAll('g.gnode')
.data(graph.nodes)
.enter()
.append('g')
.classed('gnode', true)
.attr('data-network', function(d) { return d.network; })
.attr('class', function(d) {
return d3.select(this).attr('class') + ' node-group-'+ handler.getNetworkCoding(d.network);
});
this.node = this.gnodes.append("circle")
.attr("class", "node")
.attr("r", this.props.radius)
.on("dblclick", function(d) {
handler.dblclick(d, handler)
})
.call(this.drag)
.on('click', function(d) {
handler.click.call(this, d, handler)
});
var labels = this.gnodes.append("text")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("font-size", "12")
.attr("y", "-10")
.text(function(d) { return d.name; });
this.drawLegend(graph);
}
tick = () => {
this.link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
this.gnodes.attr("transform", function(d) {
return 'translate(' + [d.x, d.y] + ')';
});
}
click(d, topo) {
console.log("TopologyL2Graph.click called");
// 'This' is the svg circle element
var gnode = d3.select(this.parentNode);
topo.svg.selectAll("text").transition()
.duration(topo.props.nodeText.transitionTime)
.attr("font-size", topo.props.nodeText.size)
.attr("fill", topo.props.nodeText.color)
// Set focus node text properties
d3.select(this.parentNode).selectAll('text').transition()
.duration(topo.props.nodeText.transitionTime)
.attr("font-size", topo.props.nodeText.focus.size)
.attr("fill", topo.props.nodeText.focus.color);
// Perform detail view
topo.selectedID = d.id;
if (topo.nodeEvent) {
topo.nodeEvent(d.id);
}
// set record view as listener
}
dblclick(d, topo) {
this.d3.select(this).classed("fixed", d.fixed = false);
}
dragstart(d) {
//d3.select(this).classed("fixed", d.fixed = true);
}
render() {
return ( <DashboardCard showHeader={true} title="Topology L2 Graph">
<div id="topologyL2"></div>
</DashboardCard>)
}
}
TopologyL2Graph.defaultProps = {
width: 700,
height: 500,
maxLabel: 150,
duration: 500,
radius: 6,
data: {
nodes: [],
links: [],
network_ids: []
},
nodeText: {
size: 12,
color: 'black',
focus: {
size: 14,
color: 'blue'
},
transitionTime: 250
}
}