Added slices and instances to tree layout
diff --git a/gui/ngXosViews/serviceTopology/src/js/d3.js b/gui/ngXosViews/serviceTopology/src/js/d3.js
index 52127a4..9452e5f 100644
--- a/gui/ngXosViews/serviceTopology/src/js/d3.js
+++ b/gui/ngXosViews/serviceTopology/src/js/d3.js
@@ -5,10 +5,214 @@
.factory('d3', function($window){
return $window.d3;
})
- .service('TreeLayout', function(){
- this.updateTree = (source) => {
+ .service('TreeLayout', function($window, lodash, ServiceRelation, serviceTopologyConfig, Slice, Instances){
- }
+ var _svg, _layout, _source;
+
+ var i = 0;
+
+ // given a canvas, a layout and a data source, draw a tree layout
+ const updateTree = (svg, layout, source) => {
+
+ //cache data
+ _svg = svg;
+ _layout = layout;
+ _source = source;
+
+ const maxDepth = ServiceRelation.depthOf(source);
+
+ const diagonal = d3.svg.diagonal()
+ .projection(d => [d.y, d.x]);
+
+ // Compute the new tree layout.
+ var nodes = layout.nodes(source).reverse(),
+ links = layout.links(nodes);
+
+ // Normalize for fixed-depth.
+ nodes.forEach(function(d) {
+ // position the child node horizontally
+ d.y = d.depth * (($window.innerWidth - (serviceTopologyConfig.widthMargin * 2)) / maxDepth);
+ console.log(d.id);
+ if(d.type == 'slice'){
+ console.info('slice found!', d);
+ }
+ });
+
+ // Update the nodes…
+ var node = svg.selectAll('g.node')
+ .data(nodes, function(d) { return d.id || (d.id = ++i); });
+
+ // Enter any new nodes at the parent's previous position.
+ var nodeEnter = node.enter().append('g')
+ .attr({
+ class: d => `node ${d.type}`,
+ transform: d => `translate(${source.y0},${source.x0})` // this is the starting position
+ });
+
+ nodeEnter.append('circle')
+ .attr('r', 1e-6)
+ .style('fill', d => d._children ? 'lightsteelblue' : '#fff')
+ .on('click', serviceClick);
+
+ nodeEnter.append('text')
+ .attr('x', function(d) { return d.children || d._children ? -13 : 13; })
+ .attr('transform', function(d) {
+ if((d.children || d._children) && d.parent || d._parent){
+ return 'rotate(30)';
+ }
+ })
+ .attr('dy', '.35em')
+ .attr('text-anchor', function(d) { return d.children || d._children ? 'end' : 'start'; })
+ .text(function(d) { return d.name; })
+ .style('fill-opacity', 1e-6);
+
+ // Transition nodes to their new position.
+ var nodeUpdate = node.transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr('transform', function(d) {
+ return 'translate(' + d.y + ',' + d.x + ')';
+ });
+
+ nodeUpdate.select('circle')
+ .attr('r', d => d.selected ? 15 : 10)
+ .style('fill', function(d) { return d._children ? 'lightsteelblue' : '#fff'; });
+
+ nodeUpdate.select('text')
+ .style('fill-opacity', 1);
+
+ // Transition exiting nodes to the parent's new position.
+ var nodeExit = node.exit().transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr('transform', function(d) { return 'translate(' + source.y + ',' + source.x + ')'; })
+ .remove();
+
+ nodeExit.select('circle')
+ .attr('r', 1e-6);
+
+ nodeExit.select('text')
+ .style('fill-opacity', 1e-6);
+
+ // Update the links…
+ var link = svg.selectAll('path.link')
+ .data(links, function(d) { return d.target.id; });
+
+ // Enter any new links at the parent's previous position.
+ link.enter().insert('path', 'g')
+ .attr('class', 'link')
+ .attr('d', function(d) {
+ var o = {x: source.x0, y: source.y0};
+ return diagonal({source: o, target: o});
+ });
+
+ // Transition links to their new position.
+ link.transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr('d', diagonal);
+
+ // Transition exiting nodes to the parent's new position.
+ link.exit().transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr('d', function(d) {
+ var o = {x: source.x, y: source.y};
+ return diagonal({source: o, target: o});
+ })
+ .remove();
+
+ // Stash the old positions for transition.
+ nodes.forEach(function(d) {
+ d.x0 = d.x;
+ d.y0 = d.y;
+ });
+ };
+
+ const serviceClick = function(d) {
+
+ // empty panel
+ //_this.slices = [];
+ //_this.instances = [];
+
+ // reset all the nodes to default radius
+ var nodes = d3.selectAll('circle')
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr('r', 10);
+
+ // remove slices details
+ d3.selectAll('rect.slice-detail')
+ .remove();
+ d3.selectAll('text.slice-name')
+ .remove();
+
+ var selectedNode = d3.select(this);
+
+ selectedNode
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr('r', 15);
+
+ if(!d.service){
+ return;
+ }
+
+ //_this.selectedService = {
+ // id: d.id,
+ // name: d.name
+ //};
+
+ ServiceRelation.getServiceInterfaces(d.service.id)
+ .then(interfaceTree => {
+
+ const isDetailed = lodash.find(d.children, {type: 'slice'});
+ if(isDetailed){
+ d.selected = false;
+ lodash.remove(d.children, {type: 'slice'});
+ }
+ else {
+ d.selected = true;
+
+ d.children = d.children.concat(interfaceTree);
+ }
+
+ updateTree(_svg, _layout, _source);
+ // draw a rect with slice names
+ //const parentNode = d3.select(this.parentNode);
+ //parentNode
+ // .append('rect')
+ // .style('opacity', 0)
+ // .attr({
+ // width: 150,
+ // height: 50,
+ // y: 35,
+ // x: -75,
+ // class: 'slice-detail'
+ // })
+ // .transition()
+ // .duration(serviceTopologyConfig.duration)
+ // .style('opacity', 1);
+ // TODO attach a click listener to draw instances and networks
+
+ //parentNode
+ // .append('text')
+ // .style('opacity', 0)
+ // .attr({
+ // y: 65,
+ // x: -60,
+ // class: 'slice-name'
+ // })
+ // .text(() => {
+ // if(slices[0]){
+ // return slices[0].humanReadableName;
+ // }
+ //
+ // return '';
+ // })
+ // .transition()
+ // .duration(serviceTopologyConfig.duration)
+ // .style('opacity', 1);
+ });
+ };
+
+ this.updateTree = updateTree;
});
}());
\ No newline at end of file