blob: fb04e2d985fbe1ca0cfc605b69a71d7a1bffdec9 [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
49 if (!res.nodes || res.nodes.length === 0 || !res.links || res.links.length === 0) {
50 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) {
Matteo Scandolo98b5f5d2017-03-17 17:09:05 -0700141 if (!data.nodes || !data.links) {
142 return;
143 }
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800144 this.forceLayout
145 .nodes(data.nodes)
146 .links(data.links)
147 .start();
148 }
149
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800150 private renderNodes(nodes: IXosServiceGraphNode[]) {
151 const self = this;
152 const node = this.nodeGroup
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800153 .selectAll('g.node')
154 .data(nodes, n => n.id);
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800155
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800156 const svgDim = this.getSvgDimensions();
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800157 const entering = node.enter()
158 .append('g')
159 .attr({
Matteo Scandolo7629cc42017-03-13 14:12:15 -0700160 class: n => `node ${this.XosGraphHelpers.parseElemClasses(n.d3Class)}`,
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800161 transform: `translate(${svgDim.width / 2}, ${svgDim.heigth / 2})`
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800162 })
163 .call(this.forceLayout.drag)
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800164 .on('mousedown', () => {
165 d3.event.stopPropagation();
166 })
167 .on('mouseup', (d) => {
168 d.fixed = true;
169 });
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800170
171 entering.append('rect')
172 .attr({
173 rx: config.node.radius,
174 ry: config.node.radius
175 });
176
177 entering.append('text')
178 .attr({
179 'text-anchor': 'middle'
180 })
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),
195 y: textBBox.y - (config.node.padding / 2)
196 });
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};