blob: cfbfc32a4356ae0e107d218437a8d444c356a81f [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;
Matteo Scandolo6a7435f2017-03-24 18:07:17 -070027 private textSize = 20;
28 private textOffset = this.textSize / 4;
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -080029
Matteo Scandolo75171782017-03-08 14:17:01 -080030 // debounced functions
Matteo Scandoloa160eef2017-03-06 17:21:26 -080031 private renderGraph;
32
Matteo Scandolo968e7f22017-03-03 11:49:18 -080033 constructor (
34 private $log: ng.ILogService,
Matteo Scandoloa160eef2017-03-06 17:21:26 -080035 private XosServiceGraphStore: IXosServiceGraphStore,
Matteo Scandolo7629cc42017-03-13 14:12:15 -070036 private XosDebouncer: IXosDebouncer,
37 private XosGraphHelpers: IXosGraphHelpers
Matteo Scandolo968e7f22017-03-03 11:49:18 -080038 ) {
39
Matteo Scandoloa160eef2017-03-06 17:21:26 -080040 }
41
42 $onInit() {
43 this.renderGraph = this.XosDebouncer.debounce(this._renderGraph, 500, this);
44
45 this.CoarseGraphSubscription = this.XosServiceGraphStore.getCoarse()
46 .subscribe(
47 (res: IXosServiceGraph) => {
Matteo Scandolo98b5f5d2017-03-17 17:09:05 -070048 this.$log.debug(`[XosCoarseTenancyGraph] Coarse Event and render`, res);
Matteo Scandolo75171782017-03-08 14:17:01 -080049
Matteo Scandoloa160eef2017-03-06 17:21:26 -080050 // id there are no data, do nothing
Matteo Scandolo265c2042017-03-20 10:15:40 -070051 if (!res) {
Matteo Scandoloa160eef2017-03-06 17:21:26 -080052 return;
53 }
Matteo Scandoloa160eef2017-03-06 17:21:26 -080054 this.graph = res;
55 this.renderGraph();
56 },
57 err => {
58 this.$log.error(`[XosCoarseTenancyGraph] Coarse Event error`, err);
59 });
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -080060
61 this.handleSvg();
Matteo Scandoloa160eef2017-03-06 17:21:26 -080062 this.setupForceLayout();
63
64 $(window).on('resize', () => {
65 this.setupForceLayout();
66 this.renderGraph();
67 });
68 }
69
70 $onDestroy() {
71 this.CoarseGraphSubscription.unsubscribe();
Matteo Scandoloa160eef2017-03-06 17:21:26 -080072 }
73
74 private _renderGraph() {
75 this.addNodeLinksToForceLayout(this.graph);
76 this.renderNodes(this.graph.nodes);
77 this.renderLinks(this.graph.links);
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -080078 }
79
80 private getSvgDimensions(): {width: number, heigth: number} {
81 return {
82 width: $('xos-coarse-tenancy-graph svg').width(),
83 heigth: $('xos-coarse-tenancy-graph svg').height()
84 };
85 }
86
87 private handleSvg() {
88 this.svg = d3.select('svg');
89
90 this.svg.append('svg:defs')
91 .selectAll('marker')
92 .data(config.markers)
93 .enter()
94 .append('svg:marker')
95 .attr('id', d => d.id)
96 .attr('viewBox', d => d.viewBox)
97 .attr('refX', d => d.refX)
98 .attr('refY', d => d.refY)
99 .attr('markerWidth', d => d.width)
100 .attr('markerHeight', d => d.height)
101 .attr('orient', 'auto')
102 .attr('class', d => `${d.id}-marker`)
103 .append('svg:path')
104 .attr('d', d => d.path);
105
106 this.linkGroup = this.svg.append('g')
107 .attr({
108 class: 'link-group'
109 });
110
111 this.nodeGroup = this.svg.append('g')
112 .attr({
113 class: 'node-group'
114 });
115 }
116
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800117 private setupForceLayout() {
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800118
119 const tick = () => {
120 this.nodeGroup.selectAll('g.node')
121 .attr({
122 transform: d => `translate(${d.x}, ${d.y})`
123 });
124
125 this.linkGroup.selectAll('line')
126 .attr({
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800127 x1: l => l.source.x || 0,
128 y1: l => l.source.y || 0,
129 x2: l => l.target.x || 0,
130 y2: l => l.target.y || 0,
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800131 });
132 };
133 const svgDim = this.getSvgDimensions();
134 this.forceLayout = d3.layout.force()
135 .size([svgDim.width, svgDim.heigth])
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800136 .linkDistance(config.force.linkDistance)
137 .charge(config.force.charge)
138 .gravity(config.force.gravity)
139 .on('tick', tick);
140 }
141
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800142 private addNodeLinksToForceLayout(data: IXosServiceGraph) {
143 this.forceLayout
144 .nodes(data.nodes)
145 .links(data.links)
146 .start();
147 }
148
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800149 private renderNodes(nodes: IXosServiceGraphNode[]) {
150 const self = this;
151 const node = this.nodeGroup
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800152 .selectAll('g.node')
153 .data(nodes, n => n.id);
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800154
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800155 const svgDim = this.getSvgDimensions();
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800156 const entering = node.enter()
157 .append('g')
158 .attr({
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700159 class: n => `node ${this.XosGraphHelpers.parseElemClasses(n.d3Class)}`,
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800160 transform: `translate(${svgDim.width / 2}, ${svgDim.heigth / 2})`
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800161 })
162 .call(this.forceLayout.drag)
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800163 .on('mousedown', () => {
164 d3.event.stopPropagation();
165 })
166 .on('mouseup', (d) => {
167 d.fixed = true;
168 });
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800169
170 entering.append('rect')
171 .attr({
172 rx: config.node.radius,
173 ry: config.node.radius
174 });
175
176 entering.append('text')
177 .attr({
Matteo Scandolo6a7435f2017-03-24 18:07:17 -0700178 'text-anchor': 'middle',
179 'transform': `translate(0,${this.textOffset})`
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800180 })
181 .text(n => n.label);
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800182 // .text(n => `${n.id} - ${n.label}`);
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800183
184 const existing = node.selectAll('rect');
185
186
187 // resize node > rect as contained text
188 existing.each(function() {
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700189 const textBBox = self.XosGraphHelpers.getSiblingTextBBox(this);
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800190 const rect = d3.select(this);
191 rect.attr({
192 width: textBBox.width + config.node.padding,
193 height: textBBox.height + config.node.padding,
194 x: textBBox.x - (config.node.padding / 2),
Matteo Scandolo6a7435f2017-03-24 18:07:17 -0700195 y: (textBBox.y + self.textOffset) - (config.node.padding / 2)
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800196 });
197 });
198 }
199
200 private renderLinks(links: IXosServiceGraphLink[]) {
201 const link = this.linkGroup
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800202 .selectAll('line')
203 .data(links, l => l.id);
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800204
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800205 const entering = link.enter();
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700206
207 // TODO read classes from graph links
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800208
209 entering.append('line')
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800210 .attr({
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700211 class: l => `link ${this.XosGraphHelpers.parseElemClasses(l.d3Class)}`,
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800212 'marker-start': 'url(#arrow)'
213 });
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800214 }
215}
216
217export const XosCoarseTenancyGraph: angular.IComponentOptions = {
218 template: require('./coarse.component.html'),
219 controllerAs: 'vm',
220 controller: XosCoarseTenancyGraphCtrl,
221};