diff options
Diffstat (limited to 'vid-webpack-master/src/app/drawingBoard/drawing-board-tree')
3 files changed, 449 insertions, 0 deletions
diff --git a/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.component.ts b/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.component.ts new file mode 100644 index 00000000..6b717a93 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.component.ts @@ -0,0 +1,133 @@ +import {AfterViewInit, Component, EventEmitter, OnInit, Output, ViewChild} from '@angular/core'; +import { ContextMenuService } from 'ngx-contextmenu'; +import { Constants } from '../../shared/utils/constants'; +import {ServicePlanningService} from "../../services/service-planning.service"; +import {ITreeNode} from "angular-tree-component/dist/defs/api"; +import {ITreeOptions, TreeComponent} from "angular-tree-component"; +import {VnfPopupComponent} from "../../components/vnf-popup/vnf-popup.components"; +import {DialogService} from "ng2-bootstrap-modal"; +import {ActivatedRoute} from "@angular/router"; +import {NgRedux} from "@angular-redux/store"; +import {AppState} from "../../store/reducers"; +import { MessageBoxData, ModalSize, ModalType } from '../../shared/components/messageBox/messageBox.data'; +import { MessageBoxService } from '../../shared/components/messageBox/messageBox.service'; +import { deleteVnfInstance, deleteVfModuleInstance } from '../../service.actions'; +import { isNullOrUndefined } from 'util'; +import {IframeService} from "../../shared/utils/iframe.service"; + + +@Component({ + selector: 'drawing-board-tree', + templateUrl: './drawing-board-tree.html', + styleUrls : ['./drawing-board-tree.scss'] +}) + + +export class DrawingBoardTreeComponent implements OnInit, AfterViewInit { + constructor(private _contextMenuService: ContextMenuService, + private _servicePlanningService: ServicePlanningService, + private _iframeService : IframeService, + private dialogService: DialogService, + private store: NgRedux<AppState>, + private route: ActivatedRoute) { + this.route + .queryParams + .subscribe(params => { + this.serviceModelId = params['serviceModelId']; + }); + } + + @Output() + highlightNode : EventEmitter<number> = new EventEmitter<number>(); + + @ViewChild('tree') tree: TreeComponent; + missingDataTooltip: string = Constants.Error.MISSING_VNF_DETAILS; + currentNode: ITreeNode = null; // + nodes = []; + serviceModelId: string; + options: ITreeOptions = { + nodeHeight: 45, + dropSlotHeight: 1 + }; + parentElementClassName = 'content'; + + ngOnInit(): void { + this.store.subscribe(() => {this.updateTree()}); + this.updateTree() + } + + updateTree() { + const serviceInstance = this.store.getState().service.serviceInstance[this.serviceModelId]; + this.nodes = this._servicePlanningService.convertServiceInstanceToTreeData(serviceInstance, this.serviceModelId); + } + + ngAfterViewInit():void { + // Expand drawing tree on init. + this.tree.treeModel.expandAll(); + } + + public onContextMenu($event: MouseEvent, node: ITreeNode): void { + this.currentNode = node; + node.focus(); + node.setActiveAndVisible(false); + this.selectNode(node); + this._contextMenuService.show.next({ + event: <any>$event, + item: node, + }); + $event.preventDefault(); + $event.stopPropagation(); + } + + public editItem(node: ITreeNode): void { + node = this.currentNode; + this._iframeService.addClassOpenModal(this.parentElementClassName); + this.dialogService.addDialog(VnfPopupComponent, { + serviceModelId: this.serviceModelId, + modelName: node.data.modelName, + modelType: node.data.type, + parentModelName: node.parent.data.modelName, + isNewVfModule : false + }) + } + + public deleteItem(node: ITreeNode): void { + if(this.currentNode.data.type === 'VF'){ + if(!isNullOrUndefined(this.currentNode.data.children) && this.currentNode.data.children.length === 0){ + this.store.dispatch(deleteVnfInstance(this.currentNode.data.modelName, this.serviceModelId)); + }else { + let messageBoxData : MessageBoxData = new MessageBoxData( + "Remove VNF", // modal title + "You are about to remove this VNF and all its children from this service. Are you sure you want to remove it?", + + ModalType.alert, + ModalSize.medium, + [ + {text:"Remove VNF", size:"large", callback: this.removeVnf.bind(this), closeModal:true}, + {text:"Don’t Remove", size:"medium", closeModal:true} + ]); + + MessageBoxService.openModal.next(messageBoxData); + } + }else { + this.store.dispatch(deleteVfModuleInstance(this.currentNode.data.modelName, this.serviceModelId, node.parent.data.modelName)); + } + } + + removeVnf() { + this.store.dispatch(deleteVnfInstance(this.currentNode.data.modelName, this.serviceModelId)); + } + + public selectNode(node: ITreeNode): void { + node.expand(); + this.highlightNode.emit(node.data.modelId); + } + + isDataMissing(node: ITreeNode) { + //todo: currently not showing the alert icon. will be implemented in upcoming story. + return false; + } + +} + + diff --git a/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.html b/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.html new file mode 100644 index 00000000..c4061db9 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.html @@ -0,0 +1,42 @@ +<div class="drawing-board-tree"> + <div *ngIf="nodes?.length == 0" style="text-align: center; margin-top: 50px;"> + <no-content-message-and-icon class="span-over" + data-title="Please add objects (VNFs, network, modules etc.) from the left tree to design the service instance" + subtitle="Once done, click Deploy to start instantiation" + iconPath="./assets/img/UPLOAD.svg" + iconClass="upload-icon-service-planing"></no-content-message-and-icon> + </div> + <tree-root [attr.data-tests-id]="'drawing-board-tree'" #tree [nodes]="nodes" [options]="options" id="drawing-board-tree"> + <ng-template #treeNodeTemplate let-node let-index="index"> + <div [attr.data-tests-id]="'node-'+node.data.modelId +'-' +node.data.modelName" (click)="selectNode(node)"> + <div class="model-info"> + <span> + <span class="property-name">{{node.data.type}}{{node.data.name ? ': ': ''}}<span class="auto-name">{{node.data.name? node.data.name: ''}}</span></span> + </span> + </div> + <div class="model-actions"> + <span class="icon-browse" [attr.data-tests-id]="'node-'+node.data.modelId +'-' +node.data.modelName+'-menu-btn'" (click)="onContextMenu($event, node)" > + <context-menu> + <ng-template contextMenuItem (execute)="editItem(node)"> + <div [attr.data-tests-id]="'context-menu-item'"> + <span class="icon-edit"></span> + Edit + </div> + </ng-template> + <ng-template contextMenuItem (execute)="deleteItem(node)"> + <div> + <span class="icon-trash"></span> + Remove + </div> + </ng-template> + </context-menu> + </span> + <span *ngIf="isDataMissing(node)" class="icon-alert" tooltip="{{ missingDataTooltip }}" tooltipPlacement="left" [attr.data-tests-id]="'node-'+node.data.modelId +'-' +node.data.modelName+'-alert-icon'"></span> + </div> + </div> + + </ng-template> + </tree-root> +</div> + + diff --git a/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.scss b/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.scss new file mode 100644 index 00000000..fed9ead1 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.scss @@ -0,0 +1,274 @@ +@mixin highlight($background-color, $color) { + background: none; + padding: 0; + background-color: $background-color; + border: $color 1px solid; + color: $color; +} +@mixin highlight-toggle-children { + tree-node-expander { + .toggle-children-wrapper { + span.toggle-children { + @include highlight(white, #009FDB); + border-right: none; + } + } + } +} + +@mixin highlight-tree-node-content { + tree-node-content { + > div { + .model-actions { + .icon-browse:before { + display: inline-block; + } + } + } + } +} + +drawing-board-tree { + + .toggle-children-wrapper.toggle-children-wrapper-expanded { + .toggle-children:before { + color: #009FDB; + } + } + + overflow: auto; + flex: 1; + color: #5A5A5A; + line-height: 14px; + .drawing-board-tree { + width: 100%; + } + tree-viewport { + padding: 50px 3.5% 1% 6%; + tree-node { + tree-node-drop-slot { + .node-drop-slot { + display: none; + } + } + & .tree-node-focused, + & .tree-node-active { + & > tree-node-wrapper { + .node-wrapper { + @include highlight-toggle-children; + .node-content-wrapper-focused, + .node-content-wrapper-active + { + @include highlight(#E6F6FB, #009FDB); + .property-name { + color: #009FDB; + } + .auto-name { + font-family: OpenSans-Regular !important; + } + .icon-browse:before { + color: #5A5A5A; + } + @include highlight-tree-node-content; + } + } + } + } + & .tree-node-expanded { + > tree-node-wrapper .node-wrapper { + box-shadow: 0 0px 2px rgba(90,90,90,0.24); + } + } + + .tree-node-active .tree-children { + border: 1px solid #009FDB; + padding: 20px; + } + + + + .tree-node.tree-node-active.tree-node-expanded { + border: 1px solid #009FDB; + } + + .tree-node-leaf .node-wrapper{ + margin-left: -45px; + } + + tree-node-wrapper { + .node-wrapper { + height: 45px; + &:hover { + @include highlight-toggle-children; + .node-content-wrapper { + @include highlight(#E6F6FB, #009FDB); + .property-name { + color: #009FDB; + } + .icon-browse:before { + color: #5A5A5A; + } + @include highlight-tree-node-content; + } + } + tree-node-expander { + font-family: 'icomoon' !important; + height: 100%; + .toggle-children-wrapper { + padding: 0; + display: block; + height: 100%; + span.toggle-children { + display: flex; + width: 45px; + padding: 0; + top: 0; + height: inherit; + background-image: none; + background-color: white; + border: 1px solid #D2D2D2; + border-right: none; + &:before { + content: "\e900"; + font-size: 20px; + font-weight: 600; + text-align: center; + display: inline-block; + flex: auto; + align-self: center; + } + } + } + .toggle-children-wrapper-expanded { + span.toggle-children { + transform: none; + &:before { + content: "\e930"; + } + } + } + .toggle-children-placeholder { + width:45px; + } + } + .node-content-wrapper { + padding: 0; + background: none; + box-shadow: none; + border-radius: 0; + border: 1px solid #D2D2D2; + height: 100%; + flex: 1; + tree-node-content { + > div { + height: 100%; + display: flex; + align-items: center; + justify-content: space-between; + .model-info { + flex: 1; + display: flex; + justify-content: space-between; + align-items: center; + padding-left: 8px; + > span { + flex: 1; + padding: 0 8px; + span:nth-child(2) { + display: block; + } + } + } + .model-actions { + display: flex; + align-items: center; + padding-right: 12px; + .icon-browse { + padding: 0; + width: 30px; + &:before { + content: "\e924"; + font-size: 24px; + display: none; + } + &:hover:before { + color: #009FDB; + } + &:focus:before, + &:active:before { + color: #009FDB; + } + } + + .icon-alert { + padding-left: 10px; + &:before { + content: "\e904"; + font-size: 16px; + color: #ffb81c; + } + } + } + } + } + .property-name { + font-family: OpenSans-Semibold; + font-size: 12px; + line-height: 12px; + color: #191919; + text-transform: capitalize; + } + } + + + } + } + tree-node-children { + .tree-children { + padding: 20px; + .model-info span:first-child { + flex: 1.1 !important; + } + } + } + } + } +} +.cdk-overlay-pane.ngx-contextmenu { + ul.dropdown-menu { + width: 200px; + box-shadow: none; + padding: 0; + padding-top: 10px; + margin: 0; + border: 1px solid #D2D2D2; + border-top: 3px solid #009FDB; + box-shadow: 0 0px 2px rgba(90,90,90,0.24); + border-radius: 2px; + li { + height: 40px; + a { + font-family: OpenSans-Regular; + display: flex; + align-items: center; + height: 100%; + padding: 12px; + color: #5A5A5A; + &:hover { + background: #E6F6FB; + color: #009FDB; + } + span { + padding-right: 12px; + &.icon-edit:before { + content: '\e917'; + } + &.icon-trash:before { + content: '\e937'; + } + } + } + } + } +} + |