Rift.IO OSM R1 Initial Submission
[osm/UI.git] / skyquake / plugins / composer / src / src / libraries / graph / GraphDescriptorModel.js
1 /**
2 * Created by onvelocity on 2/11/16.
3 */
4 'use strict';
5
6 import d3 from 'd3'
7 import ColorGroups from '../ColorGroups'
8 import PathBuilder from '../graph/PathBuilder'
9 import DescriptorModelFactory from '../model/DescriptorModelFactory'
10 import DescriptorGraphSelection from './DescriptorGraphSelection'
11 import CatalogItemsActions from '../../actions/CatalogItemsActions'
12 import HighlightRecordServicePaths from './HighlightRecordServicePaths'
13 import ComposerAppActions from '../../actions/ComposerAppActions'
14
15 import '../../styles/GraphDescriptorModel.scss'
16
17 const iconTitles = {
18 'vnffgd': 'fg',
19 'constituent-vnfd': 'vnf',
20 'nsd': 'ns',
21 'vnfd': 'vnf',
22 'vld': 'vl',
23 'internal-vld': 'vl'
24 };
25
26 export default class GraphDescriptorModel {
27
28 constructor(graph, classType, props = {}) {
29 this.props = {};
30 this.graph = graph;
31 this.classType = classType;
32 this.containers = [];
33 this._dragHandler = () => {};
34 if (!classType) {
35 throw TypeError('Expect class type to be provided. Did you forget to pass in a class type, for example, DescriptorModelFactory.VirtualNetworkFunction?');
36 }
37 const defaults = {type: classType.type, className: classType.className, selector: [classType.className, classType.type, 'descriptor']};
38 this.props = Object.assign(this.props, defaults, {descriptorBorderRadius: 20, descriptorWidth: 250, descriptorHeight: 55}, props);
39 GraphDescriptorModel.buildRef(classType.type, this.graph.defs);
40 }
41
42 addContainers(containers) {
43 this.containers = containers.filter(d => d instanceof this.classType);
44 }
45
46 get dragHandler() {
47 return this._dragHandler;
48 }
49
50 set dragHandler(drag) {
51 this._dragHandler = drag;
52 }
53
54 render() {
55
56 const g = this.graph.containersGroup;
57
58 const descriptor = g.selectAll('.' + this.props.selector.join('.')).data(this.containers, DescriptorModelFactory.containerIdentity);
59
60 const path = new PathBuilder();
61
62 const descriptorPath = PathBuilder.descriptorPath(this.props.descriptorBorderRadius, this.props.descriptorHeight, this.props.descriptorWidth).toString();
63
64 // ENTER
65 const box = descriptor.enter().append('g')
66 .attr({
67 'data-uid': d => d.uid,
68 'data-key': d => d.key,
69 'class': d => {
70 return this.props.selector.join(' ');
71 }
72 }).on('cut', (container) => {
73
74 let success = false;
75
76 if (container && container.remove) {
77 success = container.remove();
78 }
79
80 if (success) {
81 CatalogItemsActions.catalogItemDescriptorChanged(container.getRoot());
82 } else {
83 d3.event.preventDefault();
84 }
85
86 d3.event.stopPropagation();
87
88 }).call(this.dragHandler);
89
90 box.append('path')
91 .attr({
92 class: d => d.type + '-descriptor-model-background-layer background-layer',
93 d: descriptorPath,
94 fill: ColorGroups.common.background,
95 stroke: ColorGroups.common.background,
96 'stroke-width': '2px'
97 });
98
99 box.append('path')
100 .attr({
101 class: d => d.type + '-descriptor-model-background background',
102 d: descriptorPath,
103 fill: d => `url(#${d.type}-descriptor-model-badge-fill)`,
104 stroke: 'transparent',
105 'stroke-width': '2px'
106 });
107
108 box.append('path')
109 .attr({
110 class: d => d.type + '-descriptor-model-border border',
111 d: descriptorPath,
112 fill: 'transparent',
113 stroke: d => ColorGroups.getColorPairForType(d.type).secondary,
114 'stroke-width': 1.25
115 }).style({opacity: 0.75});
116
117 box.append('path').attr({
118 class: d => d.type + '-icon icon',
119 d: d => d.icon.d,
120 transform: d => `translate(${d.icon.translate ? d.icon.translate : '-4, 2'}) rotate(0) scale(${24 / d.icon.width}, ${24 / d.icon.width})`,
121 fill: 'white',
122 stroke: 'white'
123 });
124
125 box.append('text')
126 .attr({
127 class:d => d.type + '-type type',
128 'font-weight': 100,
129 'font-size': '12px',
130 'text-anchor': 'middle',
131 'text-transform': 'uppercase',
132 fill: 'white',
133 stroke: 'white',
134 x: 13,
135 y: 49
136 }).text(d => iconTitles[d.type] || d.type).style({opacity: 1});
137
138 box.append('text')
139 .attr({
140 class: d => d.type + '-title title',
141 'font-weight': 100,
142 'font-size': '12px',
143 'text-anchor': 'middle',
144 x: (d) => {
145 const left = 0;
146 const widthOffset = (d.position.width / 2) + 20;
147 return left + widthOffset;
148 },
149 y: (d) => {
150 const top = 0;
151 const height = d.position.height;
152 const heightOffset = height / 2;
153 const textHeightOffset = 12 / 2;
154 return top + heightOffset + textHeightOffset;
155 }
156 });
157
158 box.each(function (d) {
159 if (DescriptorModelFactory.isForwardingGraph(d)) {
160 const box = d3.select(this);
161
162 d.rsp.forEach((rsp, i) => {
163 const colorLegend = box.append('g').attr({
164 class:d => d.type + '-rsp-color-legend color-legend',
165 transform: d => {
166 const widthOffset = d.position.width - 20 - (i * 26);
167 const heightOffset = d.position.height - 10;
168 return `translate(${widthOffset}, ${heightOffset})`;
169 }
170 });
171 colorLegend.append('ellipse').classed('colors', true).attr({
172 rx: 12,
173 ry: 7.5,
174 fill: d => d.colors && d.colors.primary,
175 stroke: d => d.colors && d.colors.secondary
176 }).on('mouseenter', function (d) {
177 HighlightRecordServicePaths.highlightPaths(rsp);
178 }).on('mouseout', function () {
179 HighlightRecordServicePaths.removeHighlighting();
180 }).on('mouseleave', function () {
181 HighlightRecordServicePaths.removeHighlighting();
182 }).on('click', function () {
183 d3.event.preventDefault();
184 ComposerAppActions.selectModel(rsp);
185 });
186 });
187
188 }
189 });
190
191
192 // ENTER & UPDATE
193 descriptor.attr({
194 transform: d => {
195 const x = d.position.left;
196 const y = d.position.top;
197 return 'translate(' + x + ', ' + y + ')';
198 }
199 });
200
201 descriptor.select('text.title')
202 .text(d => {
203 return d.title;
204 });
205
206 // EXIT
207 descriptor.exit()
208 .remove();
209
210 }
211
212 static buildRef(type, defs) {
213 if (defs.selectAll('#' + type + '-descriptor-model-badge-fill')[0].length === 0) {
214 var vld = defs.append('pattern').attr({
215 id: type + '-descriptor-model-badge-fill',
216 //patternUnits: 'userSpaceOnUse',
217 patternContentUnits: 'objectBoundingBox',
218 width: 1,
219 height: 1,
220 viewBox: '0 0 1 1',
221 preserveAspectRatio: 'none'
222 });
223 vld.append('rect').attr({
224 width: 1,
225 height: 1,
226 fill: 'white'//ensure transparent layers keep their color
227 });
228 vld.append('rect').attr({
229 width: 1,
230 height: 1,
231 fill: ColorGroups.getColorPairForType(type).secondary
232 }).style({opacity: 1});
233 vld.append('rect').attr({
234 y: 0,
235 x: 0.22,
236 width: 1,
237 height: 1,
238 fill: ColorGroups.common.background
239 });
240 }
241 }
242
243 }