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;
59 boardGraph: joint.dia.Graph;
60 boardPaper: joint.dia.Paper;
62 paletteGraph: joint.dia.Graph;
63 palettePaper: joint.dia.Paper;
64 private ngUnsubscribe = new Subject();
65 private opt = { tx: 100, ty: 100 };
67 constructor(private designerStore: DesignerStore,
68 private functionStore: FunctionsStore,
69 private graphUtil: GraphUtil,
70 private graphGenerator: GraphGenerator,
71 private route: ActivatedRoute,
72 private designerService: DesignerService) {
73 this.controllerSideBar = true;
74 this.attributesSideBar = false;
75 this.showAction = false;
76 this.functionAttributeSidebar = true;
79 private _toggleSidebar1() {
80 this.controllerSideBar = !this.controllerSideBar;
82 private _toggleSidebar2() {
83 this.attributesSideBar = !this.attributesSideBar;
85 // private _toggleSidebar3() {
86 // this.functionAttributeSidebar = !this.functionAttributeSidebar;
91 * - There is a board (main paper) that will the action and function selected from the palette
92 * itmes in this board will be used to create tosca workflow and node templates
93 * - There is also palette , whis contains all the possible functions and actions
94 * that can be dragged into the board
95 * - There is also a fly paper , which is temporarliy paper created on the fly
96 * when items is dragged from the palette- and it's deleted when the item is dropped over
98 * for more info about the drag and drop algorithem used please visit the following link:
99 * https://stackoverflow.com/a/36932973/1340034
103 this.customActionName = this.route.snapshot.paramMap.get('actionName');
104 if (this.customActionName !== '') {
105 this.showAction = true;
107 this.initializeBoard();
108 this.initializePalette();
109 this.stencilPaperEventListeners();
110 const id = this.route.snapshot.paramMap.get('id');
111 this.designerService.getPagedPackages(id).subscribe(
112 (bluePrintDetailModels) => {
113 if (bluePrintDetailModels) {
114 this.viewedPackage = bluePrintDetailModels[0];
118 * the code to retrieve from server is commented
120 this.functionStore.state$
122 distinctUntilChanged((a: any, b: any) => JSON.stringify(a) === JSON.stringify(b)),
123 takeUntil(this.ngUnsubscribe))
124 .subscribe(state => {
126 if (state.serverFunctions) {
127 console.log('inside subscriotn on functions store -->', state.serverFunctions);
129 // this.viewedFunctions = state.functions;
130 const list = state.serverFunctions;
132 const cells = this.graphUtil.buildPaletteGraphFromList(list);
133 this.paletteGraph.resetCells(cells);
136 cells.forEach(cell => {
137 cell.translate(5, (cell.attributes.size.height + 5) * idx++);
142 this.designerStore.state$
144 distinctUntilChanged((a: any, b: any) => JSON.stringify(a) === JSON.stringify(b)),
145 takeUntil(this.ngUnsubscribe))
146 .subscribe(state => {
147 if (state.sourceContent) {
148 console.log('inside desinger.component---> ', state);
149 // generate graph from store objects if exist
150 const topologtTemplate = JSON.parse(state.sourceContent);
151 console.log(topologtTemplate);
152 delete state.sourceContent;
153 this.graphGenerator.populate(topologtTemplate, this.boardGraph);
155 console.log('all cells', this.boardGraph.getCells());
157 * auto arrange elements in graph
158 * https://resources.jointjs.com/docs/jointjs/v3.1/joint.html#layout.DirectedGraph
160 joint.layout.DirectedGraph.layout( this.boardGraph.getCells(), {
163 setLinkVertices: false,
166 clusterPadding: { top: 100, left: 30, right: 10, bottom: 100 },
173 this.functionStore.retrieveFuntions();
177 initializePalette() {
178 if (!this.paletteGraph) {
179 this.paletteGraph = new joint.dia.Graph();
180 this.palettePaper = new joint.dia.Paper({
181 el: $('#palette-paper'),
182 model: this.paletteGraph,
184 height: $('#palette-paper').height(),
186 // color: 'rgba(0, 255, 0, 0.3)'
189 // elements in paletter need to be fixed, please refer to flying paper concept
195 if (!this.boardGraph) {
196 console.log('initializeBoard...');
197 this.boardGraph = new joint.dia.Graph();
198 this.boardPaper = new joint.dia.Paper({
199 el: $('#board-paper'),
200 model: this.boardGraph,
206 // color: 'rgba(0, 255, 0, 0.3)'
208 cellViewNamespace: joint.shapes
211 this.boardPaper.on('all', element => {
212 // console.log(element);
215 this.boardPaper.on('link:pointerdown', link => {
219 this.boardPaper.on('element:pointerdown', element => {
220 // this.modelSelected.emit(element.model.get('model'));
223 this.boardPaper.on('blank:pointerclick', () => {
224 // this.selectedModel = undefined;
227 this.boardGraph.on('change:position', (cell) => {
229 const parentId = cell.get('parent');
235 const parent = this.boardGraph.getCell(parentId);
237 const parentBbox = parent.getBBox();
238 const cellBbox = cell.getBBox();
239 if (parentBbox.containsPoint(cellBbox.origin()) &&
240 parentBbox.containsPoint(cellBbox.topRight()) &&
241 parentBbox.containsPoint(cellBbox.corner()) &&
242 parentBbox.containsPoint(cellBbox.bottomLeft())) {
244 // All the four corners of the child are inside
249 // Revert the child position.
250 cell.set('position', cell.previous('position'));
253 console.log('done initializing Board...');
256 insertCustomActionIntoBoard() {
257 console.log('saving action to store action workflow....');
258 const actionName = this.graphUtil.generateNewActionName();
259 this.graphUtil.createCustomActionWithName(actionName, this.boardGraph);
260 this.designerStore.addDeclarativeWorkFlow(actionName);
263 stencilPaperEventListeners() {
264 this.palettePaper.on('cell:pointerdown', (draggedCell, pointerDownEvent, x, y) => {
268 style="position:fixed;z-index:100;opacity:.7;pointer-event:none;background-color: transparent !important;"></div>`
270 const flyGraph = new joint.dia.Graph();
271 const flyPaper = new joint.dia.Paper({
276 const flyShape = draggedCell.model.clone();
277 const pos = draggedCell.model.position();
283 flyShape.position(0, 0);
284 flyGraph.addCell(flyShape);
285 $('#flyPaper').offset({
286 left: pointerDownEvent.pageX - offset.x,
287 top: pointerDownEvent.pageY - offset.y
289 $('body').on('mousemove.fly', mouseMoveEvent => {
290 $('#flyPaper').offset({
291 left: mouseMoveEvent.pageX - offset.x,
292 top: mouseMoveEvent.pageY - offset.y
296 $('body').on('mouseup.fly', mouseupEvent => {
297 const mouseupX = mouseupEvent.pageX;
298 const mouseupY = mouseupEvent.pageY;
299 const target = this.boardPaper.$el.offset();
300 // Dropped over paper ?
301 if (mouseupX > target.left &&
302 mouseupX < target.left + this.boardPaper.$el.width() &&
303 mouseupY > target.top && y < target.top + this.boardPaper.$el.height()) {
304 const functionType = this.graphUtil.getFunctionTypeFromPaletteFunction(flyShape);
305 // step name is CDS realted terminology, please refer to tosca types
306 const stepName = functionType;
307 const functionElementForBoard = this.graphUtil.dropFunctionOverActionWithPosition(
308 stepName, functionType,
313 const parentCell = this.graphUtil.getParent(functionElementForBoard, this.boardPaper);
316 parentCell.model.attributes.type === ActionElementTypeName &&
317 this.graphUtil.canEmpedMoreChildern(parentCell.model, this.boardGraph)) {
319 if (this.graphUtil.isEmptyParent(parentCell.model)) {
320 // first function in action
321 const actionName = parentCell.model.attributes.attrs['#label'].text;
322 this.designerStore.addStepToDeclarativeWorkFlow(actionName, stepName, functionType);
323 if (functionType === 'dg-generic') {
324 this.designerStore.addDgGenericNodeTemplate(stepName);
326 this.designerStore.addNodeTemplate(stepName, functionType);
329 // second action means there was a dg-generic node before
330 this.designerStore.addNodeTemplate(stepName, functionType);
331 // this will fail if multiple dg-generic were added
332 // TODO prevent multi functions of the same type inside the same action
333 const dgGenericNode = this.graphUtil.getDgGenericChild(parentCell.model, this.boardGraph)[0];
334 const dgGenericNodeName = this.graphUtil.getFunctionNameFromBoardFunction(dgGenericNode);
335 this.designerStore.addDgGenericDependency(dgGenericNodeName, stepName);
339 // Prevent recursive embedding.
341 parentCell.model.get('parent') !== functionElementForBoard.id) {
342 parentCell.model.embed(functionElementForBoard);
345 console.log('function dropped outside action or not allowed, rolling back...');
346 alert('function dropped outside action or not allowed, rolling back...');
347 functionElementForBoard.remove();
350 $('body').off('mousemove.fly').off('mouseup.fly');
351 // flyShape.remove();
352 $('#flyPaper').remove();
355 console.log('done stencilPaperEventListeners()...');
359 this.ngUnsubscribe.next();
360 this.ngUnsubscribe.complete();