blob: c595cf12d5e09be1e55fc4e1b6e11e10ec964ef8 [file] [log] [blame]
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -08001import './coarse.component.scss';
2import * as d3 from 'd3';
3import * as $ from 'jquery';
Matteo Scandolo968e7f22017-03-03 11:49:18 -08004import {IXosServiceGraphStore} from '../../services/graph.store';
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -08005import {IXosServiceGraph, IXosServiceGraphNode, IXosServiceGraphLink} from '../../interfaces';
6import {XosServiceGraphConfig as config} from '../../graph.config';
7
Matteo Scandolo968e7f22017-03-03 11:49:18 -08008class XosCoarseTenancyGraphCtrl {
9
10 static $inject = ['$log', 'XosServiceGraphStore'];
11
12 public graph: IXosServiceGraph;
13
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -080014 private svg;
15 private forceLayout;
16 private linkGroup;
17 private nodeGroup;
18
Matteo Scandolo968e7f22017-03-03 11:49:18 -080019 constructor (
20 private $log: ng.ILogService,
21 private XosServiceGraphStore: IXosServiceGraphStore
22 ) {
23
24 this.XosServiceGraphStore.getCoarse()
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -080025 .subscribe((res: IXosServiceGraph) => {
26 // id there are no data, do nothing
27 if (!res.nodes || res.nodes.length === 0 || !res.links || res.links.length === 0) {
28 return;
29 }
Matteo Scandolo968e7f22017-03-03 11:49:18 -080030 this.graph = res;
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -080031 this.setupForceLayout(res);
32 this.renderNodes(res.nodes);
33 this.renderLinks(res.links);
34 this.forceLayout.start();
Matteo Scandolo968e7f22017-03-03 11:49:18 -080035 });
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -080036
37 this.handleSvg();
38 }
39
40 private getSvgDimensions(): {width: number, heigth: number} {
41 return {
42 width: $('xos-coarse-tenancy-graph svg').width(),
43 heigth: $('xos-coarse-tenancy-graph svg').height()
44 };
45 }
46
47 private handleSvg() {
48 this.svg = d3.select('svg');
49
50 this.svg.append('svg:defs')
51 .selectAll('marker')
52 .data(config.markers)
53 .enter()
54 .append('svg:marker')
55 .attr('id', d => d.id)
56 .attr('viewBox', d => d.viewBox)
57 .attr('refX', d => d.refX)
58 .attr('refY', d => d.refY)
59 .attr('markerWidth', d => d.width)
60 .attr('markerHeight', d => d.height)
61 .attr('orient', 'auto')
62 .attr('class', d => `${d.id}-marker`)
63 .append('svg:path')
64 .attr('d', d => d.path);
65
66 this.linkGroup = this.svg.append('g')
67 .attr({
68 class: 'link-group'
69 });
70
71 this.nodeGroup = this.svg.append('g')
72 .attr({
73 class: 'node-group'
74 });
75 }
76
77 private setupForceLayout(data: IXosServiceGraph) {
78
79 const tick = () => {
80 this.nodeGroup.selectAll('g.node')
81 .attr({
82 transform: d => `translate(${d.x}, ${d.y})`
83 });
84
85 this.linkGroup.selectAll('line')
86 .attr({
87 x1: l => l.source.x,
88 y1: l => l.source.y,
89 x2: l => l.target.x,
90 y2: l => l.target.y,
91 });
92 };
93 const svgDim = this.getSvgDimensions();
94 this.forceLayout = d3.layout.force()
95 .size([svgDim.width, svgDim.heigth])
96 .nodes(data.nodes)
97 .links(data.links)
98 .linkDistance(config.force.linkDistance)
99 .charge(config.force.charge)
100 .gravity(config.force.gravity)
101 .on('tick', tick);
102 }
103
104 private getSiblingTextBBox(contex: any /* D3 this */) {
105 return d3.select(contex.parentNode).select('text').node().getBBox();
106 }
107
108 private renderNodes(nodes: IXosServiceGraphNode[]) {
109 const self = this;
110 const node = this.nodeGroup
111 .selectAll('rect')
112 .data(nodes);
113
114 const entering = node.enter()
115 .append('g')
116 .attr({
117 class: 'node',
118 })
119 .call(this.forceLayout.drag)
120 .on('mousedown', () => { d3.event.stopPropagation(); })
121 .on('mouseup', (d) => { d.fixed = true; });
122
123 entering.append('rect')
124 .attr({
125 rx: config.node.radius,
126 ry: config.node.radius
127 });
128
129 entering.append('text')
130 .attr({
131 'text-anchor': 'middle'
132 })
133 .text(n => n.label);
134
135 const existing = node.selectAll('rect');
136
137
138 // resize node > rect as contained text
139 existing.each(function() {
140 const textBBox = self.getSiblingTextBBox(this);
141 const rect = d3.select(this);
142 rect.attr({
143 width: textBBox.width + config.node.padding,
144 height: textBBox.height + config.node.padding,
145 x: textBBox.x - (config.node.padding / 2),
146 y: textBBox.y - (config.node.padding / 2)
147 });
148 });
149 }
150
151 private renderLinks(links: IXosServiceGraphLink[]) {
152 const link = this.linkGroup
153 .selectAll('rect')
154 .data(links);
155
156 const entering = link.enter()
157 .append('g')
158 .attr({
159 class: 'link',
160 });
161
162 entering.append('line')
163 .attr('marker-start', 'url(#arrow)');
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800164 }
165}
166
167export const XosCoarseTenancyGraph: angular.IComponentOptions = {
168 template: require('./coarse.component.html'),
169 controllerAs: 'vm',
170 controller: XosCoarseTenancyGraphCtrl,
171};