thingsboard-memoizeit

UI: Improve Rule Chain Editor

3/30/2018 3:24:58 PM

Details

diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
index a08dc34..93cb5fb 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
@@ -69,14 +69,18 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
                 && ruleNode.getConfiguration().equals(newRuleNode.getConfiguration()));
         this.ruleNode = newRuleNode;
         if (restartRequired) {
-            tbNode.destroy();
+            if (tbNode != null) {
+                tbNode.destroy();
+            }
             start(context);
         }
     }
 
     @Override
     public void stop(ActorContext context) throws Exception {
-        tbNode.destroy();
+        if (tbNode != null) {
+            tbNode.destroy();
+        }
         context.stop(self);
     }
 
diff --git a/ui/src/app/event/event.scss b/ui/src/app/event/event.scss
index b3be35c..b0fc46f 100644
--- a/ui/src/app/event/event.scss
+++ b/ui/src/app/event/event.scss
@@ -24,6 +24,17 @@ md-list.tb-event-table {
     height: 48px;
     padding: 0px;
     overflow: hidden;
+    .tb-cell {
+      text-overflow: ellipsis;
+      &.tb-scroll {
+        white-space: nowrap;
+        overflow-y: hidden;
+        overflow-x: auto;
+      }
+      &.tb-nowrap {
+        white-space: nowrap;
+      }
+    }
   }
 
   .tb-row:hover {
@@ -39,13 +50,19 @@ md-list.tb-event-table {
         color: rgba(0,0,0,.54);
         font-size: 12px;
         font-weight: 700;
-        white-space: nowrap;
         background: none;
+        white-space: nowrap;
       }
   }
 
   .tb-cell {
-      padding: 0 24px;
+      &:first-child {
+        padding-left: 14px;
+      }
+      &:last-child {
+        padding-right: 14px;
+      }
+      padding: 0 6px;
       margin: auto 0;
       color: rgba(0,0,0,.87);
       font-size: 13px;
@@ -53,8 +70,8 @@ md-list.tb-event-table {
       text-align: left;
       overflow: hidden;
       .md-button {
-        padding: 0;
-        margin: 0;
+          padding: 0;
+          margin: 0;
       }
   }
 
diff --git a/ui/src/app/event/event-header-debug-rulenode.tpl.html b/ui/src/app/event/event-header-debug-rulenode.tpl.html
index b412a0c..34f4513 100644
--- a/ui/src/app/event/event-header-debug-rulenode.tpl.html
+++ b/ui/src/app/event/event-header-debug-rulenode.tpl.html
@@ -15,13 +15,13 @@
     limitations under the License.
 
 -->
-<div hide-xs hide-sm translate class="tb-cell" flex="30">event.event-time</div>
+<div hide-xs hide-sm translate class="tb-cell" flex="25">event.event-time</div>
 <div translate class="tb-cell" flex="20">event.server</div>
-<div translate class="tb-cell" flex="20">event.type</div>
-<div translate class="tb-cell" flex="20">event.entity</div>
+<div translate class="tb-cell" flex="10">event.type</div>
+<div translate class="tb-cell" flex="15">event.entity</div>
 <div translate class="tb-cell" flex="20">event.message-id</div>
 <div translate class="tb-cell" flex="20">event.message-type</div>
-<div translate class="tb-cell" flex="20">event.data-type</div>
-<div translate class="tb-cell" flex="20">event.data</div>
-<div translate class="tb-cell" flex="20">event.metadata</div>
-<div translate class="tb-cell" flex="20">event.error</div>
+<div translate class="tb-cell" flex="15">event.data-type</div>
+<div translate class="tb-cell" flex="10">event.data</div>
+<div translate class="tb-cell" flex="10">event.metadata</div>
+<div translate class="tb-cell" flex="10">event.error</div>
diff --git a/ui/src/app/event/event-row.directive.js b/ui/src/app/event/event-row.directive.js
index 4643761..b808fb8 100644
--- a/ui/src/app/event/event-row.directive.js
+++ b/ui/src/app/event/event-row.directive.js
@@ -86,6 +86,14 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
             });
         }
 
+        scope.checkTooltip = function($event) {
+            var el = $event.target;
+            var $el = angular.element(el);
+            if(el.offsetWidth < el.scrollWidth && !$el.attr('title')){
+                $el.attr('title', $el.text());
+            }
+        }
+
         $compile(element.contents())(scope);
     }
 
diff --git a/ui/src/app/event/event-row-debug-rulenode.tpl.html b/ui/src/app/event/event-row-debug-rulenode.tpl.html
index 5b96baf..bb832b1 100644
--- a/ui/src/app/event/event-row-debug-rulenode.tpl.html
+++ b/ui/src/app/event/event-row-debug-rulenode.tpl.html
@@ -15,14 +15,14 @@
     limitations under the License.
 
 -->
-<div hide-xs hide-sm class="tb-cell" flex="30">{{event.createdTime | date :  'yyyy-MM-dd HH:mm:ss'}}</div>
+<div hide-xs hide-sm class="tb-cell" flex="25">{{event.createdTime | date :  'yyyy-MM-dd HH:mm:ss'}}</div>
 <div class="tb-cell" flex="20">{{event.body.server}}</div>
-<div class="tb-cell" flex="20">{{event.body.type}}</div>
-<div class="tb-cell" flex="20">{{event.body.entityName}}</div>
-<div class="tb-cell" flex="20">{{event.body.msgId}}</div>
-<div class="tb-cell" flex="20">{{event.body.msgType}}</div>
-<div class="tb-cell" flex="20">{{event.body.dataType}}</div>
-<div class="tb-cell" flex="20">
+<div class="tb-cell" flex="10">{{event.body.type}}</div>
+<div class="tb-cell" flex="15">{{event.body.entityName}}</div>
+<div class="tb-cell tb-nowrap" flex="20" ng-mouseenter="checkTooltip($event)">{{event.body.msgId}}</div>
+<div class="tb-cell" flex="20" ng-mouseenter="checkTooltip($event)">{{event.body.msgType}}</div>
+<div class="tb-cell" flex="15">{{event.body.dataType}}</div>
+<div class="tb-cell" flex="10">
     <md-button ng-if="event.body.data" class="md-icon-button md-primary"
                ng-click="showContent($event, event.body.data, 'event.data', event.body.dataType)"
                aria-label="{{ 'action.view' | translate }}">
@@ -35,7 +35,7 @@
         </md-icon>
     </md-button>
 </div>
-<div class="tb-cell" flex="20">
+<div class="tb-cell" flex="10">
     <md-button ng-if="event.body.metadata" class="md-icon-button md-primary"
                ng-click="showContent($event, event.body.metadata, 'event.metadata', 'JSON')"
                aria-label="{{ 'action.view' | translate }}">
@@ -48,7 +48,7 @@
         </md-icon>
     </md-button>
 </div>
-<div class="tb-cell" flex="20">
+<div class="tb-cell" flex="10">
     <md-button ng-if="event.body.error" class="md-icon-button md-primary"
                ng-click="showContent($event, event.body.error, 'event.error')"
                aria-label="{{ 'action.view' | translate }}">
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index f616fec..9c0cd12 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -43,6 +43,7 @@ export default angular.module('thingsboard.locale', [])
                     "update": "Update",
                     "remove": "Remove",
                     "search": "Search",
+                    "clear-search": "Clear search",
                     "assign": "Assign",
                     "unassign": "Unassign",
                     "share": "Share",
@@ -1188,6 +1189,7 @@ export default angular.module('thingsboard.locale', [])
                     "details": "Details",
                     "events": "Events",
                     "search": "Search nodes",
+                    "open-node-library": "Open node library",
                     "add": "Add rule node",
                     "name": "Name",
                     "name-required": "Name is required.",
diff --git a/ui/src/app/rulechain/rulechain.controller.js b/ui/src/app/rulechain/rulechain.controller.js
index 7de72c3..14cf798 100644
--- a/ui/src/app/rulechain/rulechain.controller.js
+++ b/ui/src/app/rulechain/rulechain.controller.js
@@ -37,6 +37,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
     vm.$mdExpansionPanel = $mdExpansionPanel;
     vm.types = types;
 
+    vm.isFullscreen = false;
+
     vm.editingRuleNode = null;
     vm.isEditingRuleNode = false;
 
@@ -57,6 +59,7 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
     };
 
     vm.ruleNodeTypesModel = {};
+    vm.ruleNodeTypesCanvasControl = {};
     vm.ruleChainLibraryLoaded = false;
     for (var type in types.ruleNodeType) {
         if (!types.ruleNodeType[type].special) {
@@ -67,9 +70,12 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
                 },
                 selectedObjects: []
             };
+            vm.ruleNodeTypesCanvasControl[type] = {};
         }
     }
 
+
+
     vm.selectedObjects = [];
 
     vm.modelservice = Modelfactory(vm.ruleChainModel, vm.selectedObjects);
@@ -147,6 +153,7 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
             theForm.$setPristine();
             vm.ruleChainModel.nodes[vm.editingRuleNodeIndex] = vm.editingRuleNode;
             vm.editingRuleNode = angular.copy(vm.editingRuleNode);
+            updateRuleNodesHighlight();
         }
     };
 
@@ -313,12 +320,28 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
         }
     };
 
-    loadRuleChainLibrary();
+    loadRuleChainLibrary(ruleNodeComponents, true);
+
+    $scope.$watch('vm.ruleNodeSearch',
+        function (newVal, oldVal) {
+            if (!angular.equals(newVal, oldVal)) {
+                var res = $filter('filter')(ruleNodeComponents, {name: vm.ruleNodeSearch});
+                loadRuleChainLibrary(res);
+            }
+        }
+    );
 
-    function loadRuleChainLibrary() {
+    $scope.$on('searchTextUpdated', function () {
+        updateRuleNodesHighlight();
+    });
+
+    function loadRuleChainLibrary(ruleNodeComponents, loadRuleChain) {
+        for (var componentType in vm.ruleNodeTypesModel) {
+            vm.ruleNodeTypesModel[componentType].model.nodes.length = 0;
+        }
         for (var i=0;i<ruleNodeComponents.length;i++) {
             var ruleNodeComponent = ruleNodeComponents[i];
-            var componentType = ruleNodeComponent.type;
+            componentType = ruleNodeComponent.type;
             var model = vm.ruleNodeTypesModel[componentType].model;
             var node = {
                 id: 'node-lib-' + componentType + '-' + model.nodes.length,
@@ -349,7 +372,16 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
             model.nodes.push(node);
         }
         vm.ruleChainLibraryLoaded = true;
-        prepareRuleChain();
+        if (loadRuleChain) {
+            prepareRuleChain();
+        }
+        $mdUtil.nextTick(() => {
+            for (componentType in vm.ruleNodeTypesCanvasControl) {
+                if (vm.ruleNodeTypesCanvasControl[componentType].adjustCanvasSize) {
+                    vm.ruleNodeTypesCanvasControl[componentType].adjustCanvasSize(true);
+                }
+            }
+        });
     }
 
     function prepareRuleChain() {
@@ -519,6 +551,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
 
         vm.isDirty = false;
 
+        updateRuleNodesHighlight();
+
         $mdUtil.nextTick(() => {
             vm.ruleChainWatch = $scope.$watch('vm.ruleChainModel',
                 function (newVal, oldVal) {
@@ -530,6 +564,20 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
         });
     }
 
+    function updateRuleNodesHighlight() {
+        for (var i=0;i<vm.ruleChainModel.nodes.length;i++) {
+            vm.ruleChainModel.nodes[i].highlighted = false;
+        }
+        if ($scope.searchConfig.searchText) {
+            var res = $filter('filter')(vm.ruleChainModel.nodes, {name: $scope.searchConfig.searchText});
+            if (res) {
+                for (i=0;i<res.length;i++) {
+                    res[i].highlighted = true;
+                }
+            }
+        }
+    }
+
     function saveRuleChain() {
         var ruleChainMetaData = {
             ruleChainId: vm.ruleChain.id,
@@ -642,6 +690,7 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
                 );
             }
             vm.ruleChainModel.nodes.push(ruleNode);
+            updateRuleNodesHighlight();
         }, function () {
         });
     }
diff --git a/ui/src/app/rulechain/rulechain.routes.js b/ui/src/app/rulechain/rulechain.routes.js
index f9578ef..48559d9 100644
--- a/ui/src/app/rulechain/rulechain.routes.js
+++ b/ui/src/app/rulechain/rulechain.routes.js
@@ -76,7 +76,7 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
                     }
             },
             data: {
-                searchEnabled: false,
+                searchEnabled: true,
                 pageTitle: 'rulechain.rulechain'
             },
             ncyBreadcrumb: {
diff --git a/ui/src/app/rulechain/rulechain.scss b/ui/src/app/rulechain/rulechain.scss
index 38f785a..850de3c 100644
--- a/ui/src/app/rulechain/rulechain.scss
+++ b/ui/src/app/rulechain/rulechain.scss
@@ -125,6 +125,13 @@
   color: #333;
   border: solid 1px #777;
   font-size: 12px;
+  &.tb-rule-node-highlighted {
+    box-shadow: 0 0 10px 6px #51cbee;
+    .tb-node-title {
+      text-decoration: underline;
+      font-weight: bold;
+    }
+  }
   &.tb-input-type {
     background-color: #a3eaa9;
     user-select: none;
@@ -156,7 +163,7 @@
 
   }
   .tb-node-title {
-    font-weight: 600;
+    font-weight: 500;
   }
   .tb-node-type, .tb-node-title {
     overflow: hidden;
@@ -184,6 +191,10 @@
     bottom: 0;
     background-color: #000;
     opacity: 0;
+/*    &.tb-rule-node-highlighted {
+      background-color: green;
+      opacity: 0.15;
+    }*/
   }
   &.fc-hover {
     .fc-node-overlay {
diff --git a/ui/src/app/rulechain/rulechain.tpl.html b/ui/src/app/rulechain/rulechain.tpl.html
index ddc1a90..a843b03 100644
--- a/ui/src/app/rulechain/rulechain.tpl.html
+++ b/ui/src/app/rulechain/rulechain.tpl.html
@@ -19,17 +19,17 @@
 <md-content flex tb-expand-fullscreen tb-confirm-on-exit is-dirty="vm.isDirty"
             expand-tooltip-direction="bottom" layout="column" class="tb-rulechain"
             ng-keydown="vm.keyDown($event)"
-            ng-keyup="vm.keyUp($event)">
+            ng-keyup="vm.keyUp($event)" on-fullscreen-changed="vm.isFullscreen = expanded">
     <section class="tb-rulechain-container" flex layout="column">
         <div class="tb-rulechain-layout" flex layout="row">
             <section layout="row" layout-wrap
                      class="tb-header-buttons md-fab tb-library-open">
                 <md-button ng-show="!vm.isLibraryOpen"
                            class="tb-btn-header tb-btn-open-library md-primary md-fab md-fab-top-left"
-                           aria-label="{{ 'action.apply' | translate }}"
+                           aria-label="{{ 'rulenode.open-node-library' | translate }}"
                            ng-click="vm.isLibraryOpen = true">
-                    <md-tooltip md-direction="top">
-                        {{ 'action.apply-changes' | translate }}
+                    <md-tooltip md-direction="{{vm.isFullscreen ? 'bottom' : 'top'}}">
+                        {{ 'rulenode.open-node-library' | translate }}
                     </md-tooltip>
                     <ng-md-icon icon="menu"></ng-md-icon>
                 </md-button>
@@ -43,7 +43,7 @@
                     <div class="md-toolbar-tools">
                         <md-button class="md-icon-button tb-small" aria-label="{{ 'action.search' | translate }}">
                             <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon>
-                            <md-tooltip md-direction="top">
+                            <md-tooltip md-direction="{{vm.isFullscreen ? 'bottom' : 'top'}}">
                                 {{'rulenode.search' | translate}}
                             </md-tooltip>
                         </md-button>
@@ -53,15 +53,17 @@
                                 <input ng-model="vm.ruleNodeSearch" placeholder="{{'rulenode.search' | translate}}"/>
                             </md-input-container>
                         </div>
-                        <md-button class="md-icon-button tb-small" aria-label="Close" ng-click="vm.ruleNodeSearch = ''">
+                        <md-button class="md-icon-button tb-small" aria-label="Close"
+                                   ng-show="vm.ruleNodeSearch"
+                                   ng-click="vm.ruleNodeSearch = ''">
                             <md-icon aria-label="Close" class="material-icons">close</md-icon>
-                            <md-tooltip md-direction="top">
-                                {{ 'action.close' | translate }}
+                            <md-tooltip md-direction="{{vm.isFullscreen ? 'bottom' : 'top'}}">
+                                {{ 'action.clear-search' | translate }}
                             </md-tooltip>
                         </md-button>
                         <md-button class="md-icon-button tb-small" aria-label="Close" ng-click="vm.isLibraryOpen = false">
                             <md-icon aria-label="Close" class="material-icons">chevron_left</md-icon>
-                            <md-tooltip md-direction="top">
+                            <md-tooltip md-direction="{{vm.isFullscreen ? 'bottom' : 'top'}}">
                                 {{ 'action.close' | translate }}
                             </md-tooltip>
                         </md-button>
@@ -90,6 +92,7 @@
                                            callbacks="vm.nodeLibCallbacks"
                                            node-width="170"
                                            node-height="50"
+                                           control="vm.ruleNodeTypesCanvasControl[typeId]"
                                            drop-target-id="'tb-rulchain-canvas'"></fc-canvas>
                             </md-expansion-panel-content>
                         </md-expansion-panel-expanded>
diff --git a/ui/src/app/rulechain/rulenode.tpl.html b/ui/src/app/rulechain/rulenode.tpl.html
index 55ee3d3..83ce988 100644
--- a/ui/src/app/rulechain/rulenode.tpl.html
+++ b/ui/src/app/rulechain/rulenode.tpl.html
@@ -22,8 +22,8 @@
         ng-mousedown="callbacks.mouseDown($event, node)"
         ng-mouseenter="callbacks.mouseEnter($event, node)"
         ng-mouseleave="callbacks.mouseLeave($event, node)">
-    <div class="{{flowchartConstants.nodeOverlayClass}}"></div>
-    <div class="tb-rule-node {{node.nodeClass}}">
+    <div class="{{flowchartConstants.nodeOverlayClass}}" ng-class="{'tb-rule-node-highlighted' : node.highlighted}"></div>
+    <div class="tb-rule-node {{node.nodeClass}}" ng-class="{'tb-rule-node-highlighted' : node.highlighted}">
         <md-icon aria-label="node-type-icon" flex="15"
                  class="material-icons">{{node.icon}}</md-icon>
         <div layout="column" flex="85" layout-align="center">