diff options
Diffstat (limited to 'catalog-ui/src/app/directives/graphs-v2/palette')
4 files changed, 505 insertions, 0 deletions
diff --git a/catalog-ui/src/app/directives/graphs-v2/palette/interfaces/i-dragdrop-event.d.ts b/catalog-ui/src/app/directives/graphs-v2/palette/interfaces/i-dragdrop-event.d.ts new file mode 100644 index 0000000000..26c042611c --- /dev/null +++ b/catalog-ui/src/app/directives/graphs-v2/palette/interfaces/i-dragdrop-event.d.ts @@ -0,0 +1,7 @@ +interface IDragDropEvent extends JQueryEventObject { + dataTransfer: any; + toElement: { + naturalWidth: number; + naturalHeight: number; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/directives/graphs-v2/palette/palette.directive.ts b/catalog-ui/src/app/directives/graphs-v2/palette/palette.directive.ts new file mode 100644 index 0000000000..4bfbe5270e --- /dev/null +++ b/catalog-ui/src/app/directives/graphs-v2/palette/palette.directive.ts @@ -0,0 +1,349 @@ +import { + Component, + IAppMenu, + LeftPanelModel, + NodesFactory, + LeftPaletteComponent, + CompositionCiNodeBase, + ComponentInstance +} from "app/models"; +import {CompositionGraphGeneralUtils} from "../composition-graph/utils/composition-graph-general-utils"; +import {EventListenerService} from "app/services"; +import {ResourceType, GRAPH_EVENTS, EVENTS, ComponentInstanceFactory, ModalsHandler} from "app/utils"; +import 'angular-dragdrop'; +import {LeftPaletteLoaderService} from "../../../services/components/utils/composition-left-palette-service"; + +interface IPaletteScope { + components:Array<LeftPaletteComponent>; + currentComponent:Component; + model:any; + displaySortedCategories:any; + expandedSection:string; + dragElement:JQuery; + dragbleNode:{ + event:JQueryEventObject, + components:LeftPaletteComponent, + ui:any + } + + sectionClick:(section:string)=>void; + searchComponents:(searchText:string)=>void; + onMouseOver:(displayComponent:LeftPaletteComponent)=>void; + onMouseOut:(displayComponent:LeftPaletteComponent)=>void; + dragStartCallback:(event:JQueryEventObject, ui, displayComponent:LeftPaletteComponent)=>void; + dragStopCallback:()=>void; + onDragCallback:(event:JQueryEventObject) => void; + + setElementTemplate:(e:JQueryEventObject)=>void; + + isOnDrag:boolean; + isDragable:boolean; + isLoading:boolean; + isViewOnly:boolean; +} + +export class Palette implements ng.IDirective { + constructor(private $log:ng.ILogService, + private LeftPaletteLoaderService: LeftPaletteLoaderService, + private sdcConfig, + private ComponentFactory, + private ComponentInstanceFactory:ComponentInstanceFactory, + private NodesFactory:NodesFactory, + private CompositionGraphGeneralUtils:CompositionGraphGeneralUtils, + private EventListenerService:EventListenerService, + private sdcMenu:IAppMenu, + private ModalsHandler:ModalsHandler) { + + } + + private fetchingComponentFromServer:boolean = false; + private nodeHtmlSubstitute:JQuery; + + scope = { + currentComponent: '=', + isViewOnly: '=', + isLoading: '=' + }; + restrict = 'E'; + template = require('./palette.html'); + + link = (scope:IPaletteScope, el:JQuery) => { + this.nodeHtmlSubstitute = $('<div class="node-substitute"><span></span><img /></div>'); + el.append(this.nodeHtmlSubstitute); + this.registerEventListenerForLeftPalette(scope); + // this.LeftPaletteLoaderService.loadLeftPanel(scope.currentComponent.componentType); + + this.initComponents(scope); + this.initEvents(scope); + this.initDragEvents(scope); + this._initExpandedSection(scope, ''); + el.on('$destroy', ()=> { + //remove listener of download event + this.unRegisterEventListenerForLeftPalette(scope); + }); + }; + + private registerEventListenerForLeftPalette = (scope:IPaletteScope):void => { + if (scope.currentComponent.isResource()) { + this.EventListenerService.registerObserverCallback(EVENTS.RESOURCE_LEFT_PALETTE_UPDATE_EVENT, () => { + this.updateLeftPanelDisplay(scope); + }); + } + if (scope.currentComponent.isService()) { + this.EventListenerService.registerObserverCallback(EVENTS.SERVICE_LEFT_PALETTE_UPDATE_EVENT, () => { + this.updateLeftPanelDisplay(scope); + }); + } + if (scope.currentComponent.isProduct()) { + this.EventListenerService.registerObserverCallback(EVENTS.PRODUCT_LEFT_PALETTE_UPDATE_EVENT, () => { + this.updateLeftPanelDisplay(scope); + }); + } + }; + + private unRegisterEventListenerForLeftPalette = (scope:IPaletteScope):void => { + if (scope.currentComponent.isResource()) { + this.EventListenerService.unRegisterObserver(EVENTS.RESOURCE_LEFT_PALETTE_UPDATE_EVENT); + } + if (scope.currentComponent.isService()) { + this.EventListenerService.unRegisterObserver(EVENTS.SERVICE_LEFT_PALETTE_UPDATE_EVENT); + } + if (scope.currentComponent.isProduct()) { + this.EventListenerService.unRegisterObserver(EVENTS.PRODUCT_LEFT_PALETTE_UPDATE_EVENT); + } + }; + + private leftPanelResourceFilter(resourcesNotAbstract:Array<LeftPaletteComponent>, resourceFilterTypes:Array<string>):Array<LeftPaletteComponent> { + let filterResources = _.filter(resourcesNotAbstract, (component) => { + return resourceFilterTypes.indexOf(component.getComponentSubType()) > -1; + }); + return filterResources; + } + + private initLeftPanel(leftPanelComponents:Array<LeftPaletteComponent>, resourceFilterTypes:Array<string>):LeftPanelModel { + let leftPanelModel = new LeftPanelModel(); + + if (resourceFilterTypes && resourceFilterTypes.length) { + leftPanelComponents = this.leftPanelResourceFilter(leftPanelComponents, resourceFilterTypes); + } + leftPanelModel.numberOfElements = leftPanelComponents && leftPanelComponents.length || 0; + + if (leftPanelComponents && leftPanelComponents.length) { + + let categories:any = _.groupBy(leftPanelComponents, 'mainCategory'); + for (let category in categories) + categories[category] = _.groupBy(categories[category], 'subCategory'); + + leftPanelModel.sortedCategories = categories; + } + return leftPanelModel; + } + + + private initEvents(scope:IPaletteScope) { + /** + * + * @param section + */ + scope.sectionClick = (section:string) => { + if (section === scope.expandedSection) { + scope.expandedSection = ''; + return; + } + scope.expandedSection = section; + }; + + scope.onMouseOver = (displayComponent:LeftPaletteComponent) => { + if (scope.isOnDrag) { + return; + } + scope.isOnDrag = true; + + this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, displayComponent); + this.$log.debug('palette::onMouseOver:: fired'); + // + // if (this.CompositionGraphGeneralUtils.componentRequirementsAndCapabilitiesCaching.containsKey(displayComponent.uniqueId)) { + // this.$log.debug(`palette::onMouseOver:: component id ${displayComponent.uniqueId} found in cache`); + // let cacheComponent:Component = this.CompositionGraphGeneralUtils.componentRequirementsAndCapabilitiesCaching.getValue(displayComponent.uniqueId); + // + // //TODO: Danny: fire event to highlight matching nodes + // //showMatchingNodes(cacheComponent); + // return; + // } + // + // this.$log.debug(`palette::onMouseOver:: component id ${displayComponent.uniqueId} not found in cache, initiating server get`); + // // This will bring the component from the server including requirements and capabilities + // // Check that we do not fetch many times, because only in the success we add the component to componentRequirementsAndCapabilitiesCaching + // if (this.fetchingComponentFromServer) { + // return; + // } + // + // this.fetchingComponentFromServer = true; + // this.ComponentFactory.getComponentFromServer(displayComponent.componentSubType, displayComponent.uniqueId) + // .then((component:Component) => { + // this.$log.debug(`palette::onMouseOver:: component id ${displayComponent.uniqueId} fetch success`); + // // this.LeftPaletteLoaderService.updateSpecificComponentLeftPalette(component, scope.currentComponent.componentType); + // this.CompositionGraphGeneralUtils.componentRequirementsAndCapabilitiesCaching.setValue(component.uniqueId, component); + // this.fetchingComponentFromServer = false; + // + // //TODO: Danny: fire event to highlight matching nodes + // //showMatchingNodes(component); + // }) + // .catch(() => { + // this.$log.debug('palette::onMouseOver:: component id fetch error'); + // this.fetchingComponentFromServer = false; + // }); + + + }; + + scope.onMouseOut = () => { + scope.isOnDrag = false; + this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT); + } + } + + private initComponents(scope:IPaletteScope) { + scope.searchComponents = (searchText:any):void => { + scope.displaySortedCategories = this._searchComponents(searchText, scope.model.sortedCategories); + this._initExpandedSection(scope, searchText); + }; + + scope.isDragable = scope.currentComponent.isComplex(); + this.updateLeftPanelDisplay(scope); + } + + private updateLeftPanelDisplay(scope:IPaletteScope) { + let entityType:string = scope.currentComponent.componentType.toLowerCase(); + let resourceFilterTypes:Array<string> = this.sdcConfig.resourceTypesFilter[entityType]; + scope.components = this.LeftPaletteLoaderService.getLeftPanelComponentsForDisplay(scope.currentComponent.componentType); + scope.model = this.initLeftPanel(scope.components, resourceFilterTypes); + scope.displaySortedCategories = angular.copy(scope.model.sortedCategories); + }; + + private _initExpandedSection(scope:IPaletteScope, searchText:string):void { + if (searchText == '') { + let isContainingCategory:boolean = false; + let categoryToExpand:string; + if (scope.currentComponent && scope.currentComponent.categories && scope.currentComponent.categories[0]) { + categoryToExpand = this.sdcMenu.categoriesDictionary[scope.currentComponent.categories[0].name]; + for (let category in scope.model.sortedCategories) { + if (categoryToExpand == category) { + isContainingCategory = true; + break; + } + } + } + isContainingCategory ? scope.expandedSection = categoryToExpand : scope.expandedSection = 'Generic'; + } + else { + scope.expandedSection = Object.keys(scope.displaySortedCategories).sort()[0]; + } + }; + + private initDragEvents(scope:IPaletteScope) { + scope.dragStartCallback = (event:IDragDropEvent, ui, displayComponent:LeftPaletteComponent):void => { + if (scope.isLoading || !scope.isDragable || scope.isViewOnly) { + return; + } + + let component = _.find(this.LeftPaletteLoaderService.getLeftPanelComponentsForDisplay(scope.currentComponent.componentType), (componentFullData:LeftPaletteComponent) => { + return displayComponent.uniqueId === componentFullData.uniqueId; + }); + this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_START, scope.dragElement, component); + + scope.isOnDrag = true; + + // this.graphUtils.showMatchingNodes(component, myDiagram, scope.sdcConfig.imagesPath); + // document.addEventListener('mousemove', moveOnDocument); + event.dataTransfer.component = component; + }; + + scope.dragStopCallback = () => { + scope.isOnDrag = false; + }; + + scope.onDragCallback = (event:IDragDropEvent):void => { + this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_ACTION, event); + }; + scope.setElementTemplate = (e) => { + let dragComponent:LeftPaletteComponent = _.find(this.LeftPaletteLoaderService.getLeftPanelComponentsForDisplay(scope.currentComponent.componentType), + (fullComponent:LeftPaletteComponent) => { + return (<any>angular.element(e.currentTarget).scope()).component.uniqueId === fullComponent.uniqueId; + }); + let componentInstance:ComponentInstance = this.ComponentInstanceFactory.createComponentInstanceFromComponent(dragComponent); + let node:CompositionCiNodeBase = this.NodesFactory.createNode(componentInstance); + + // myDiagram.dragFromPalette = node; + this.nodeHtmlSubstitute.find("img").attr('src', node.img); + scope.dragElement = this.nodeHtmlSubstitute.clone().show(); + + return scope.dragElement; + }; + } + + private _searchComponents = (searchText:string, categories:any):void => { + let displaySortedCategories = angular.copy(categories); + if (searchText != '') { + angular.forEach(categories, function (category:any, categoryKey) { + + angular.forEach(category, function (subcategory:Array<LeftPaletteComponent>, subcategoryKey) { + let filteredResources = []; + angular.forEach(subcategory, function (component:LeftPaletteComponent) { + + let resourceFilterTerm:string = component.searchFilterTerms; + if (resourceFilterTerm.indexOf(searchText.toLowerCase()) >= 0) { + filteredResources.push(component); + } + }); + if (filteredResources.length > 0) { + displaySortedCategories[categoryKey][subcategoryKey] = filteredResources; + } + else { + delete displaySortedCategories[categoryKey][subcategoryKey]; + } + }); + if (!(Object.keys(displaySortedCategories[categoryKey]).length > 0)) { + delete displaySortedCategories[categoryKey]; + } + + }); + } + return displaySortedCategories; + }; + + public static factory = ($log, + LeftPaletteLoaderService, + sdcConfig, + ComponentFactory, + ComponentInstanceFactory, + NodesFactory, + CompositionGraphGeneralUtils, + EventListenerService, + sdcMenu, + ModalsHandler) => { + return new Palette($log, + LeftPaletteLoaderService, + sdcConfig, + ComponentFactory, + ComponentInstanceFactory, + NodesFactory, + CompositionGraphGeneralUtils, + EventListenerService, + sdcMenu, + ModalsHandler); + }; +} + +Palette.factory.$inject = [ + '$log', + 'LeftPaletteLoaderService', + 'sdcConfig', + 'ComponentFactory', + 'ComponentInstanceFactory', + 'NodesFactory', + 'CompositionGraphGeneralUtils', + 'EventListenerService', + 'sdcMenu', + 'ModalsHandler' +]; diff --git a/catalog-ui/src/app/directives/graphs-v2/palette/palette.html b/catalog-ui/src/app/directives/graphs-v2/palette/palette.html new file mode 100644 index 0000000000..4b123e5777 --- /dev/null +++ b/catalog-ui/src/app/directives/graphs-v2/palette/palette.html @@ -0,0 +1,57 @@ +<div class="w-sdc-designer-leftbar"> + <div class="w-sdc-designer-leftbar-title">Elements <span class="w-sdc-designer-leftbar-title-count">{{model.numberOfElements}}</span> + </div> + + <div class="w-sdc-designer-leftbar-search"> + <input type="text" class="w-sdc-designer-leftbar-search-input" placeholder="Search..." + data-ng-model="searchText" data-ng-change="searchComponents(searchText)" + ng-model-options="{ debounce: 500 }" data-tests-id="searchAsset"/> + <span class="w-sdc-search-icon leftbar" data-ng-class="{'cancel':searchText, 'magnification':!searchText}" + data-ng-click="searchText=''; searchComponents('',categories)"></span> + </div> + <div class="i-sdc-designer-leftbar-section" + data-ng-repeat="(entityCategory, objCategory) in displaySortedCategories track by $index" + data-ng-class="{'expanded': expandedSection.indexOf(entityCategory) !== -1}"> + <div class="i-sdc-designer-leftbar-section-title pointer" data-ng-click="sectionClick(entityCategory)" + data-tests-id="leftbar-section-title-{{entityCategory}}"> + {{entityCategory}} + <div class="i-sdc-designer-leftbar-section-title-icon"></div> + </div> + <div class="i-sdc-designer-leftbar-section-content" + data-ng-repeat="(subCategory, components) in objCategory track by $index"> + <div class="i-sdc-designer-leftbar-section-content-subcat i-sdc-designer-leftbar-section-content-item"> + {{subCategory}} + </div> + <div class="i-sdc-designer-leftbar-section-content-item" + data-ng-class="{'default-pointer': isViewOnly}" + data-ng-mouseover="!isViewOnly && onMouseOver(component)" + data-ng-mouseleave="!isViewOnly && onMouseOut()" + data-drag="{{!isViewOnly}}" + data-jqyoui-options="{revert: 'invalid', helper:setElementTemplate, appendTo:'body', cursorAt: {left:38, top: 38}, cursor:'move'}" + jqyoui-draggable="{index:{{$index}},animate:true,onStart:'dragStartCallback(component)',onStop:'dragStopCallback()', onDrag:'onDragCallback()'}" + data-ng-repeat="component in components | orderBy: 'displayName' track by $index" + data-tests-id={{component.displayName}}> + <div class="i-sdc-designer-leftbar-section-content-item-icon-ph"> + <div class="medium {{component.iconClass}}" + data-tests-id="leftbar-section-content-item-{{component.displayName}}"> + <div class="{{component.certifiedIconClass}}" uib-tooltip="Not certified" + tooltip-class="uib-custom-tooltip" tooltip-placement="bottom" tooltip-popup-delay="700"> + </div> + </div> + </div> + <div class="i-sdc-designer-leftbar-section-content-item-info"> + <span class="i-sdc-designer-leftbar-section-content-item-info-title" + uib-tooltip="{{component.displayName}}" tooltip-class="uib-custom-tooltip" + tooltip-placement="bottom" tooltip-popup-delay="700"> + {{component.displayName}}</span> + <div class="i-sdc-designer-leftbar-section-content-item-info-text"> + V.{{component.version}} + </div> + <div class="i-sdc-designer-leftbar-section-content-item-info-text"> Type: + {{component.componentSubType}} + </div> + </div> + </div> + </div> + </div> +</div> diff --git a/catalog-ui/src/app/directives/graphs-v2/palette/palette.less b/catalog-ui/src/app/directives/graphs-v2/palette/palette.less new file mode 100644 index 0000000000..85657a43a5 --- /dev/null +++ b/catalog-ui/src/app/directives/graphs-v2/palette/palette.less @@ -0,0 +1,92 @@ +.drag-icon-border{ + border: 7px solid red; + border-radius: 500px; + -webkit-border-radius: 500px; + -moz-border-radius: 500px; + width: 53px; + height: 53px; +} + +.drag-icon-circle{ + width: 60px; + height: 60px; + -webkit-border-radius: 50%; + -moz-border-radius: 50%; + border-radius: 50%; + position: relative; + +} + + +@green-shadow: rgba(29, 154, 149, 0.3); +@red-shadow: rgba(218, 31, 61, 0.3); +.drag-icon-circle .sprite-resource-icons { + position: absolute; + top: 10px; + left: 10px; +} + +.drag-icon-circle.red { + background: @red-shadow; +} + +.drag-icon-circle.green { + background: @green-shadow; +} + + +.node-substitute { + display: none; + position: absolute; + z-index: 9999; + height: 80px; + width: 80px; + border-radius: 50%; + text-align: center; + + span { + display: inline-block; + vertical-align: middle; + height: 100%; + } + + img { + height: 40px; + width: 40px; + box-shadow: 0 0 0 10px @green-shadow; + border-radius: 50%; + + -webkit-user-drag: none; + -moz-user-drag: none; + user-drag: none; + } + &.red img { + box-shadow: 0 0 0 10px @red-shadow; + } + &.bounce img { + -moz-animation:bounceOut 0.3s linear; + -webkit-animation:bounceOut 0.3s linear; + animation:bounceOut 0.3s linear; + } +} + +@keyframes bounceOut { + 0%{ box-shadow: 0 0 0 10px @green-shadow; width: 40px; height: 40px; } + 60%{ box-shadow: 0 0 0 0px @green-shadow; width: 60px; height: 60px; } + 85%{ box-shadow: 0 0 0 0px @green-shadow; width: 75px; height: 75px; } + 100%{ box-shadow: 0 0 0 0px @green-shadow; width: 60px; height: 60px; } +} + +@-moz-keyframes bounceOut { + 0%{ box-shadow: 0 0 0 10px @green-shadow; width: 40px; height: 40px; } + 60%{ box-shadow: 0 0 0 0px @green-shadow; width: 60px; height: 60px; } + 85%{ box-shadow: 0 0 0 0px @green-shadow; width: 75px; height: 75px; } + 100%{ box-shadow: 0 0 0 0px @green-shadow; width: 60px; height: 60px; } +} + +@-webkit-keyframes bounceOut { + 0%{ box-shadow: 0 0 0 10px @green-shadow; width: 40px; height: 40px; } + 60%{ box-shadow: 0 0 0 0px @green-shadow; width: 60px; height: 60px; } + 85%{ box-shadow: 0 0 0 0px @green-shadow; width: 75px; height: 75px; } + 100%{ box-shadow: 0 0 0 0px @green-shadow; width: 60px; height: 60px; } +} |