X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=skyquake%2Fframework%2Fwidgets%2Ftopology%2FtopologyL2Graph.jsx;fp=skyquake%2Fframework%2Fwidgets%2Ftopology%2FtopologyL2Graph.jsx;h=fe4360bd83f1412672fd86156ec050bb1f6d474b;hb=e29efc315df33d546237e270470916e26df391d6;hp=0000000000000000000000000000000000000000;hpb=9c5e457509ba5a1822c316635c6308874e61b4b9;p=osm%2FUI.git diff --git a/skyquake/framework/widgets/topology/topologyL2Graph.jsx b/skyquake/framework/widgets/topology/topologyL2Graph.jsx new file mode 100644 index 000000000..fe4360bd8 --- /dev/null +++ b/skyquake/framework/widgets/topology/topologyL2Graph.jsx @@ -0,0 +1,253 @@ + +/* + * + * 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 ( +
+
) + } +} + +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 + } +}