blob: ed5d100f234ba7df169f9ffdce2502eb4128a58c [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';
Matteo Scandoloa160eef2017-03-06 17:21:26 -08007import {IXosDebouncer} from '../../../core/services/helpers/debounce.helper';
8import {Subscription} from 'rxjs';
Matteo Scandolo7629cc42017-03-13 14:12:15 -07009import {IXosGraphHelpers} from '../../services/d3-helpers/graph.helpers';
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -080010
Matteo Scandolo968e7f22017-03-03 11:49:18 -080011class XosCoarseTenancyGraphCtrl {
12
Matteo Scandoloa160eef2017-03-06 17:21:26 -080013 static $inject = [
14 '$log',
15 'XosServiceGraphStore',
Matteo Scandolo7629cc42017-03-13 14:12:15 -070016 'XosDebouncer',
17 'XosGraphHelpers'
Matteo Scandoloa160eef2017-03-06 17:21:26 -080018 ];
Matteo Scandolo968e7f22017-03-03 11:49:18 -080019
20 public graph: IXosServiceGraph;
21
Matteo Scandoloa160eef2017-03-06 17:21:26 -080022 private CoarseGraphSubscription: Subscription;
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -080023 private svg;
24 private forceLayout;
25 private linkGroup;
26 private nodeGroup;
27
Matteo Scandolo75171782017-03-08 14:17:01 -080028 // debounced functions
Matteo Scandoloa160eef2017-03-06 17:21:26 -080029 private renderGraph;
30
Matteo Scandolo968e7f22017-03-03 11:49:18 -080031 constructor (
32 private $log: ng.ILogService,
Matteo Scandoloa160eef2017-03-06 17:21:26 -080033 private XosServiceGraphStore: IXosServiceGraphStore,
Matteo Scandolo7629cc42017-03-13 14:12:15 -070034 private XosDebouncer: IXosDebouncer,
35 private XosGraphHelpers: IXosGraphHelpers
Matteo Scandolo968e7f22017-03-03 11:49:18 -080036 ) {
37
Matteo Scandoloa160eef2017-03-06 17:21:26 -080038 }
39
40 $onInit() {
41 this.renderGraph = this.XosDebouncer.debounce(this._renderGraph, 500, this);
42
43 this.CoarseGraphSubscription = this.XosServiceGraphStore.getCoarse()
44 .subscribe(
45 (res: IXosServiceGraph) => {
Matteo Scandolo98b5f5d2017-03-17 17:09:05 -070046 this.$log.debug(`[XosCoarseTenancyGraph] Coarse Event and render`, res);
Matteo Scandolo75171782017-03-08 14:17:01 -080047
Matteo Scandoloa160eef2017-03-06 17:21:26 -080048 // id there are no data, do nothing
Matteo Scandolo265c2042017-03-20 10:15:40 -070049 if (!res) {
Matteo Scandoloa160eef2017-03-06 17:21:26 -080050 return;
51 }
Matteo Scandoloa160eef2017-03-06 17:21:26 -080052 this.graph = res;
53 this.renderGraph();
54 },
55 err => {
56 this.$log.error(`[XosCoarseTenancyGraph] Coarse Event error`, err);
57 });
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -080058
59 this.handleSvg();
Matteo Scandoloa160eef2017-03-06 17:21:26 -080060 this.setupForceLayout();
61
62 $(window).on('resize', () => {
63 this.setupForceLayout();
64 this.renderGraph();
65 });
66 }
67
68 $onDestroy() {
69 this.CoarseGraphSubscription.unsubscribe();
Matteo Scandoloa160eef2017-03-06 17:21:26 -080070 }
71
72 private _renderGraph() {
73 this.addNodeLinksToForceLayout(this.graph);
74 this.renderNodes(this.graph.nodes);
75 this.renderLinks(this.graph.links);
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -080076 }
77
78 private getSvgDimensions(): {width: number, heigth: number} {
79 return {
80 width: $('xos-coarse-tenancy-graph svg').width(),
81 heigth: $('xos-coarse-tenancy-graph svg').height()
82 };
83 }
84
85 private handleSvg() {
86 this.svg = d3.select('svg');
87
88 this.svg.append('svg:defs')
89 .selectAll('marker')
90 .data(config.markers)
91 .enter()
92 .append('svg:marker')
93 .attr('id', d => d.id)
94 .attr('viewBox', d => d.viewBox)
95 .attr('refX', d => d.refX)
96 .attr('refY', d => d.refY)
97 .attr('markerWidth', d => d.width)
98 .attr('markerHeight', d => d.height)
99 .attr('orient', 'auto')
100 .attr('class', d => `${d.id}-marker`)
101 .append('svg:path')
102 .attr('d', d => d.path);
103
104 this.linkGroup = this.svg.append('g')
105 .attr({
106 class: 'link-group'
107 });
108
109 this.nodeGroup = this.svg.append('g')
110 .attr({
111 class: 'node-group'
112 });
113 }
114
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800115 private setupForceLayout() {
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800116
117 const tick = () => {
118 this.nodeGroup.selectAll('g.node')
119 .attr({
120 transform: d => `translate(${d.x}, ${d.y})`
121 });
122
123 this.linkGroup.selectAll('line')
124 .attr({
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800125 x1: l => l.source.x || 0,
126 y1: l => l.source.y || 0,
127 x2: l => l.target.x || 0,
128 y2: l => l.target.y || 0,
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800129 });
130 };
131 const svgDim = this.getSvgDimensions();
132 this.forceLayout = d3.layout.force()
133 .size([svgDim.width, svgDim.heigth])
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800134 .linkDistance(config.force.linkDistance)
135 .charge(config.force.charge)
136 .gravity(config.force.gravity)
137 .on('tick', tick);
138 }
139
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800140 private addNodeLinksToForceLayout(data: IXosServiceGraph) {
141 this.forceLayout
142 .nodes(data.nodes)
143 .links(data.links)
144 .start();
145 }
146
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800147 private renderNodes(nodes: IXosServiceGraphNode[]) {
148 const self = this;
149 const node = this.nodeGroup
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800150 .selectAll('g.node')
151 .data(nodes, n => n.id);
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800152
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800153 const svgDim = this.getSvgDimensions();
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800154 const entering = node.enter()
155 .append('g')
156 .attr({
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700157 class: n => `node ${this.XosGraphHelpers.parseElemClasses(n.d3Class)}`,
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800158 transform: `translate(${svgDim.width / 2}, ${svgDim.heigth / 2})`
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800159 })
160 .call(this.forceLayout.drag)
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800161 .on('mousedown', () => {
162 d3.event.stopPropagation();
163 })
164 .on('mouseup', (d) => {
165 d.fixed = true;
166 });
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800167
168 entering.append('rect')
169 .attr({
170 rx: config.node.radius,
171 ry: config.node.radius
172 });
173
174 entering.append('text')
175 .attr({
176 'text-anchor': 'middle'
177 })
178 .text(n => n.label);
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800179 // .text(n => `${n.id} - ${n.label}`);
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800180
181 const existing = node.selectAll('rect');
182
183
184 // resize node > rect as contained text
185 existing.each(function() {
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700186 const textBBox = self.XosGraphHelpers.getSiblingTextBBox(this);
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800187 const rect = d3.select(this);
188 rect.attr({
189 width: textBBox.width + config.node.padding,
190 height: textBBox.height + config.node.padding,
191 x: textBBox.x - (config.node.padding / 2),
192 y: textBBox.y - (config.node.padding / 2)
193 });
194 });
195 }
196
197 private renderLinks(links: IXosServiceGraphLink[]) {
198 const link = this.linkGroup
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800199 .selectAll('line')
200 .data(links, l => l.id);
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800201
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800202 const entering = link.enter();
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700203
204 // TODO read classes from graph links
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800205
206 entering.append('line')
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800207 .attr({
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700208 class: l => `link ${this.XosGraphHelpers.parseElemClasses(l.d3Class)}`,
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800209 'marker-start': 'url(#arrow)'
210 });
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800211 }
212}
213
214export const XosCoarseTenancyGraph: angular.IComponentOptions = {
215 template: require('./coarse.component.html'),
216 controllerAs: 'vm',
217 controller: XosCoarseTenancyGraphCtrl,
218};