2 ============LICENSE_START==========================================
3 ===================================================================
4 Copyright (C) 2019 Orange. All rights reserved.
5 ===================================================================
6 Modification Copyright (c) 2020 IBM
7 ===================================================================
9 Unless otherwise specified, all software contained herein is licensed
10 under the Apache License, Version 2.0 (the License);
11 you may not use this software except in compliance with the License.
12 You may obtain a copy of the License at
14 http://www.apache.org/licenses/LICENSE-2.0
16 Unless required by applicable law or agreed to in writing, software
17 distributed under the License is distributed on an "AS IS" BASIS,
18 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 See the License for the specific language governing permissions and
20 limitations under the License.
21 ============LICENSE_END============================================
24 import dagre from 'dagre';
25 import graphlib from 'graphlib';
26 import { Component, OnInit, ViewEncapsulation, OnDestroy } from '@angular/core';
27 import * as joint from 'jointjs';
28 import './jointjs/elements/palette.function.element';
29 import './jointjs/elements/action.element';
30 import './jointjs/elements/board.function.element';
31 import { DesignerStore } from './designer.store';
32 import { ActionElementTypeName } from 'src/app/common/constants/app-constants';
33 import { GraphUtil } from './graph.util';
34 import { GraphGenerator } from './graph.generator.util';
35 import { FunctionsStore } from './functions.store';
36 import { Subject, empty } from 'rxjs';
37 import { takeUntil } from 'rxjs/operators';
38 import { distinctUntilChanged } from 'rxjs/operators';
39 import { BluePrintDetailModel } from '../model/BluePrint.detail.model';
40 import { ActivatedRoute } from '@angular/router';
41 import { DesignerService } from './designer.service';
42 import { isDefined } from '@angular/compiler/src/util';
45 selector: 'app-designer',
46 templateUrl: './designer.component.html',
47 styleUrls: ['./designer.component.css'],
48 encapsulation: ViewEncapsulation.None
50 export class DesignerComponent implements OnInit, OnDestroy {
52 private controllerSideBar: boolean;
53 private attributesSideBar: boolean;
54 functionAttributeSidebar: boolean;
55 viewedPackage: BluePrintDetailModel = new BluePrintDetailModel();
56 customActionName: string;
60 boardGraph: joint.dia.Graph;
61 boardPaper: joint.dia.Paper;
63 paletteGraph: joint.dia.Graph;
64 palettePaper: joint.dia.Paper;
65 private ngUnsubscribe = new Subject();
66 private opt = { tx: 100, ty: 100 };
68 constructor(private designerStore: DesignerStore,
69 private functionStore: FunctionsStore,
70 private graphUtil: GraphUtil,
71 private graphGenerator: GraphGenerator,
72 private route: ActivatedRoute,
73 private designerService: DesignerService) {
74 this.controllerSideBar = true;
75 this.attributesSideBar = false;
76 this.showAction = false;
77 this.functionAttributeSidebar = false;
80 private _toggleSidebar1() {
81 this.controllerSideBar = !this.controllerSideBar;
82 if (this.controllerSideBar === false) {
85 if (this.controllerSideBar === true) {
89 private _toggleSidebar2() {
90 this.attributesSideBar = !this.attributesSideBar;
92 // private _toggleSidebar3() {
93 // this.functionAttributeSidebar = !this.functionAttributeSidebar;
98 * - There is a board (main paper) that will the action and function selected from the palette
99 * itmes in this board will be used to create tosca workflow and node templates
100 * - There is also palette , whis contains all the possible functions and actions
101 * that can be dragged into the board
102 * - There is also a fly paper , which is temporarliy paper created on the fly
103 * when items is dragged from the palette- and it's deleted when the item is dropped over
105 * for more info about the drag and drop algorithem used please visit the following link:
106 * https://stackoverflow.com/a/36932973/1340034
110 this.customActionName = this.route.snapshot.paramMap.get('actionName');
111 if (this.customActionName !== '') {
112 this.showAction = true;
114 this.initializeBoard();
115 this.initializePalette();
116 this.stencilPaperEventListeners();
117 const id = this.route.snapshot.paramMap.get('id');
118 this.designerService.getPagedPackages(id).subscribe(
119 (bluePrintDetailModels) => {
120 if (bluePrintDetailModels) {
121 this.viewedPackage = bluePrintDetailModels[0];
125 * the code to retrieve from server is commented
127 this.functionStore.state$
129 distinctUntilChanged((a: any, b: any) => JSON.stringify(a) === JSON.stringify(b)),
130 takeUntil(this.ngUnsubscribe))
131 .subscribe(state => {
133 if (state.serverFunctions) {
134 console.log('inside subscriotn on functions store -->', state.serverFunctions);
136 // this.viewedFunctions = state.functions;
137 const list = state.serverFunctions;
139 const cells = this.graphUtil.buildPaletteGraphFromList(list);
140 this.paletteGraph.resetCells(cells);
143 cells.forEach(cell => {
144 cell.translate(5, (cell.attributes.size.height + 5) * idx++);
149 this.designerStore.state$
151 distinctUntilChanged((a: any, b: any) => JSON.stringify(a) === JSON.stringify(b)),
152 takeUntil(this.ngUnsubscribe))
153 .subscribe(state => {
154 if (state.sourceContent) {
155 console.log('inside desinger.component---> ', state);
156 // generate graph from store objects if exist
157 const topologtTemplate = JSON.parse(state.sourceContent);
158 console.log(topologtTemplate);
159 delete state.sourceContent;
160 this.graphGenerator.populate(topologtTemplate, this.boardGraph);
162 console.log('all cells', this.boardGraph.getCells());
164 * auto arrange elements in graph
165 * https://resources.jointjs.com/docs/jointjs/v3.1/joint.html#layout.DirectedGraph
167 joint.layout.DirectedGraph.layout( this.boardGraph.getCells(), {
170 setLinkVertices: false,
173 clusterPadding: { top: 100, left: 30, right: 10, bottom: 100 },
180 this.functionStore.retrieveFuntions();
184 initializePalette() {
185 if (!this.paletteGraph) {
186 this.paletteGraph = new joint.dia.Graph();
187 this.palettePaper = new joint.dia.Paper({
188 el: $('#palette-paper'),
189 model: this.paletteGraph,
191 height: $('#palette-paper').height(),
193 // color: 'rgba(0, 255, 0, 0.3)'
196 // elements in paletter need to be fixed, please refer to flying paper concept
202 if (!this.boardGraph) {
203 console.log('initializeBoard...');
204 this.boardGraph = new joint.dia.Graph();
205 this.boardPaper = new joint.dia.Paper({
206 el: $('#board-paper'),
207 model: this.boardGraph,
213 // color: 'rgba(0, 255, 0, 0.3)'
215 cellViewNamespace: joint.shapes
218 this.boardPaper.on('all', element => {
219 // console.log(element);
222 this.boardPaper.on('link:pointerdown', link => {
226 this.boardPaper.on('element:pointerdown', element => {
227 // this.modelSelected.emit(element.model.get('model'));
230 this.boardPaper.on('blank:pointerclick', () => {
231 // this.selectedModel = undefined;
234 this.boardGraph.on('change:position', (cell) => {
236 const parentId = cell.get('parent');
242 const parent = this.boardGraph.getCell(parentId);
244 const parentBbox = parent.getBBox();
245 const cellBbox = cell.getBBox();
246 if (parentBbox.containsPoint(cellBbox.origin()) &&
247 parentBbox.containsPoint(cellBbox.topRight()) &&
248 parentBbox.containsPoint(cellBbox.corner()) &&
249 parentBbox.containsPoint(cellBbox.bottomLeft())) {
251 // All the four corners of the child are inside
256 // Revert the child position.
257 cell.set('position', cell.previous('position'));
260 console.log('done initializing Board...');
263 insertCustomActionIntoBoard() {
264 console.log('saving action to store action workflow....');
265 const actionName = this.graphUtil.generateNewActionName();
266 this.graphUtil.createCustomActionWithName(actionName, this.boardGraph);
267 this.designerStore.addDeclarativeWorkFlow(actionName);
270 stencilPaperEventListeners() {
271 this.palettePaper.on('cell:pointerdown', (draggedCell, pointerDownEvent, x, y) => {
275 style="position:fixed;z-index:100;opacity:.7;pointer-event:none;background-color: transparent !important;"></div>`
277 const flyGraph = new joint.dia.Graph();
278 const flyPaper = new joint.dia.Paper({
283 const flyShape = draggedCell.model.clone();
284 const pos = draggedCell.model.position();
290 flyShape.position(0, 0);
291 flyGraph.addCell(flyShape);
292 $('#flyPaper').offset({
293 left: pointerDownEvent.pageX - offset.x,
294 top: pointerDownEvent.pageY - offset.y
296 $('body').on('mousemove.fly', mouseMoveEvent => {
297 $('#flyPaper').offset({
298 left: mouseMoveEvent.pageX - offset.x,
299 top: mouseMoveEvent.pageY - offset.y
303 $('body').on('mouseup.fly', mouseupEvent => {
304 const mouseupX = mouseupEvent.pageX;
305 const mouseupY = mouseupEvent.pageY;
306 const target = this.boardPaper.$el.offset();
307 // Dropped over paper ?
308 if (mouseupX > target.left &&
309 mouseupX < target.left + this.boardPaper.$el.width() &&
310 mouseupY > target.top && y < target.top + this.boardPaper.$el.height()) {
311 const functionType = this.graphUtil.getFunctionTypeFromPaletteFunction(flyShape);
312 // step name is CDS realted terminology, please refer to tosca types
313 const stepName = functionType;
314 const functionElementForBoard = this.graphUtil.dropFunctionOverActionWithPosition(
315 stepName, functionType,
320 const parentCell = this.graphUtil.getParent(functionElementForBoard, this.boardPaper);
323 parentCell.model.attributes.type === ActionElementTypeName &&
324 this.graphUtil.canEmpedMoreChildern(parentCell.model, this.boardGraph)) {
326 if (this.graphUtil.isEmptyParent(parentCell.model)) {
327 // first function in action
328 const actionName = parentCell.model.attributes.attrs['#label'].text;
329 this.designerStore.addStepToDeclarativeWorkFlow(actionName, stepName, functionType);
330 if (functionType === 'dg-generic') {
331 this.designerStore.addDgGenericNodeTemplate(stepName);
333 this.designerStore.addNodeTemplate(stepName, functionType);
336 // second action means there was a dg-generic node before
337 this.designerStore.addNodeTemplate(stepName, functionType);
338 // this will fail if multiple dg-generic were added
339 // TODO prevent multi functions of the same type inside the same action
340 const dgGenericNode = this.graphUtil.getDgGenericChild(parentCell.model, this.boardGraph)[0];
341 const dgGenericNodeName = this.graphUtil.getFunctionNameFromBoardFunction(dgGenericNode);
342 this.designerStore.addDgGenericDependency(dgGenericNodeName, stepName);
346 // Prevent recursive embedding.
348 parentCell.model.get('parent') !== functionElementForBoard.id) {
349 parentCell.model.embed(functionElementForBoard);
352 console.log('function dropped outside action or not allowed, rolling back...');
353 alert('function dropped outside action or not allowed, rolling back...');
354 functionElementForBoard.remove();
357 $('body').off('mousemove.fly').off('mouseup.fly');
358 // flyShape.remove();
359 $('#flyPaper').remove();
362 console.log('done stencilPaperEventListeners()...');
366 this.ngUnsubscribe.next();
367 this.ngUnsubscribe.complete();