Commit dc47727cf5e29f657fd00ec0422da5fd4799bbb6

Authored by Igor Kulikov
1 parent 39f682ce

UI: Improve Rule Chain Editor

... ... @@ -69,14 +69,18 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
69 69 && ruleNode.getConfiguration().equals(newRuleNode.getConfiguration()));
70 70 this.ruleNode = newRuleNode;
71 71 if (restartRequired) {
72   - tbNode.destroy();
  72 + if (tbNode != null) {
  73 + tbNode.destroy();
  74 + }
73 75 start(context);
74 76 }
75 77 }
76 78
77 79 @Override
78 80 public void stop(ActorContext context) throws Exception {
79   - tbNode.destroy();
  81 + if (tbNode != null) {
  82 + tbNode.destroy();
  83 + }
80 84 context.stop(self);
81 85 }
82 86
... ...
... ... @@ -15,13 +15,13 @@
15 15 limitations under the License.
16 16
17 17 -->
18   -<div hide-xs hide-sm translate class="tb-cell" flex="30">event.event-time</div>
  18 +<div hide-xs hide-sm translate class="tb-cell" flex="25">event.event-time</div>
19 19 <div translate class="tb-cell" flex="20">event.server</div>
20   -<div translate class="tb-cell" flex="20">event.type</div>
21   -<div translate class="tb-cell" flex="20">event.entity</div>
  20 +<div translate class="tb-cell" flex="10">event.type</div>
  21 +<div translate class="tb-cell" flex="15">event.entity</div>
22 22 <div translate class="tb-cell" flex="20">event.message-id</div>
23 23 <div translate class="tb-cell" flex="20">event.message-type</div>
24   -<div translate class="tb-cell" flex="20">event.data-type</div>
25   -<div translate class="tb-cell" flex="20">event.data</div>
26   -<div translate class="tb-cell" flex="20">event.metadata</div>
27   -<div translate class="tb-cell" flex="20">event.error</div>
  24 +<div translate class="tb-cell" flex="15">event.data-type</div>
  25 +<div translate class="tb-cell" flex="10">event.data</div>
  26 +<div translate class="tb-cell" flex="10">event.metadata</div>
  27 +<div translate class="tb-cell" flex="10">event.error</div>
... ...
... ... @@ -15,14 +15,14 @@
15 15 limitations under the License.
16 16
17 17 -->
18   -<div hide-xs hide-sm class="tb-cell" flex="30">{{event.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div>
  18 +<div hide-xs hide-sm class="tb-cell" flex="25">{{event.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div>
19 19 <div class="tb-cell" flex="20">{{event.body.server}}</div>
20   -<div class="tb-cell" flex="20">{{event.body.type}}</div>
21   -<div class="tb-cell" flex="20">{{event.body.entityName}}</div>
22   -<div class="tb-cell" flex="20">{{event.body.msgId}}</div>
23   -<div class="tb-cell" flex="20">{{event.body.msgType}}</div>
24   -<div class="tb-cell" flex="20">{{event.body.dataType}}</div>
25   -<div class="tb-cell" flex="20">
  20 +<div class="tb-cell" flex="10">{{event.body.type}}</div>
  21 +<div class="tb-cell" flex="15">{{event.body.entityName}}</div>
  22 +<div class="tb-cell tb-nowrap" flex="20" ng-mouseenter="checkTooltip($event)">{{event.body.msgId}}</div>
  23 +<div class="tb-cell" flex="20" ng-mouseenter="checkTooltip($event)">{{event.body.msgType}}</div>
  24 +<div class="tb-cell" flex="15">{{event.body.dataType}}</div>
  25 +<div class="tb-cell" flex="10">
26 26 <md-button ng-if="event.body.data" class="md-icon-button md-primary"
27 27 ng-click="showContent($event, event.body.data, 'event.data', event.body.dataType)"
28 28 aria-label="{{ 'action.view' | translate }}">
... ... @@ -35,7 +35,7 @@
35 35 </md-icon>
36 36 </md-button>
37 37 </div>
38   -<div class="tb-cell" flex="20">
  38 +<div class="tb-cell" flex="10">
39 39 <md-button ng-if="event.body.metadata" class="md-icon-button md-primary"
40 40 ng-click="showContent($event, event.body.metadata, 'event.metadata', 'JSON')"
41 41 aria-label="{{ 'action.view' | translate }}">
... ... @@ -48,7 +48,7 @@
48 48 </md-icon>
49 49 </md-button>
50 50 </div>
51   -<div class="tb-cell" flex="20">
  51 +<div class="tb-cell" flex="10">
52 52 <md-button ng-if="event.body.error" class="md-icon-button md-primary"
53 53 ng-click="showContent($event, event.body.error, 'event.error')"
54 54 aria-label="{{ 'action.view' | translate }}">
... ...
... ... @@ -86,6 +86,14 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
86 86 });
87 87 }
88 88
  89 + scope.checkTooltip = function($event) {
  90 + var el = $event.target;
  91 + var $el = angular.element(el);
  92 + if(el.offsetWidth < el.scrollWidth && !$el.attr('title')){
  93 + $el.attr('title', $el.text());
  94 + }
  95 + }
  96 +
89 97 $compile(element.contents())(scope);
90 98 }
91 99
... ...
... ... @@ -24,6 +24,17 @@ md-list.tb-event-table {
24 24 height: 48px;
25 25 padding: 0px;
26 26 overflow: hidden;
  27 + .tb-cell {
  28 + text-overflow: ellipsis;
  29 + &.tb-scroll {
  30 + white-space: nowrap;
  31 + overflow-y: hidden;
  32 + overflow-x: auto;
  33 + }
  34 + &.tb-nowrap {
  35 + white-space: nowrap;
  36 + }
  37 + }
27 38 }
28 39
29 40 .tb-row:hover {
... ... @@ -39,13 +50,19 @@ md-list.tb-event-table {
39 50 color: rgba(0,0,0,.54);
40 51 font-size: 12px;
41 52 font-weight: 700;
42   - white-space: nowrap;
43 53 background: none;
  54 + white-space: nowrap;
44 55 }
45 56 }
46 57
47 58 .tb-cell {
48   - padding: 0 24px;
  59 + &:first-child {
  60 + padding-left: 14px;
  61 + }
  62 + &:last-child {
  63 + padding-right: 14px;
  64 + }
  65 + padding: 0 6px;
49 66 margin: auto 0;
50 67 color: rgba(0,0,0,.87);
51 68 font-size: 13px;
... ... @@ -53,8 +70,8 @@ md-list.tb-event-table {
53 70 text-align: left;
54 71 overflow: hidden;
55 72 .md-button {
56   - padding: 0;
57   - margin: 0;
  73 + padding: 0;
  74 + margin: 0;
58 75 }
59 76 }
60 77
... ...
... ... @@ -43,6 +43,7 @@ export default angular.module('thingsboard.locale', [])
43 43 "update": "Update",
44 44 "remove": "Remove",
45 45 "search": "Search",
  46 + "clear-search": "Clear search",
46 47 "assign": "Assign",
47 48 "unassign": "Unassign",
48 49 "share": "Share",
... ... @@ -1188,6 +1189,7 @@ export default angular.module('thingsboard.locale', [])
1188 1189 "details": "Details",
1189 1190 "events": "Events",
1190 1191 "search": "Search nodes",
  1192 + "open-node-library": "Open node library",
1191 1193 "add": "Add rule node",
1192 1194 "name": "Name",
1193 1195 "name-required": "Name is required.",
... ...
... ... @@ -37,6 +37,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
37 37 vm.$mdExpansionPanel = $mdExpansionPanel;
38 38 vm.types = types;
39 39
  40 + vm.isFullscreen = false;
  41 +
40 42 vm.editingRuleNode = null;
41 43 vm.isEditingRuleNode = false;
42 44
... ... @@ -57,6 +59,7 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
57 59 };
58 60
59 61 vm.ruleNodeTypesModel = {};
  62 + vm.ruleNodeTypesCanvasControl = {};
60 63 vm.ruleChainLibraryLoaded = false;
61 64 for (var type in types.ruleNodeType) {
62 65 if (!types.ruleNodeType[type].special) {
... ... @@ -67,9 +70,12 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
67 70 },
68 71 selectedObjects: []
69 72 };
  73 + vm.ruleNodeTypesCanvasControl[type] = {};
70 74 }
71 75 }
72 76
  77 +
  78 +
73 79 vm.selectedObjects = [];
74 80
75 81 vm.modelservice = Modelfactory(vm.ruleChainModel, vm.selectedObjects);
... ... @@ -147,6 +153,7 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
147 153 theForm.$setPristine();
148 154 vm.ruleChainModel.nodes[vm.editingRuleNodeIndex] = vm.editingRuleNode;
149 155 vm.editingRuleNode = angular.copy(vm.editingRuleNode);
  156 + updateRuleNodesHighlight();
150 157 }
151 158 };
152 159
... ... @@ -313,12 +320,28 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
313 320 }
314 321 };
315 322
316   - loadRuleChainLibrary();
  323 + loadRuleChainLibrary(ruleNodeComponents, true);
  324 +
  325 + $scope.$watch('vm.ruleNodeSearch',
  326 + function (newVal, oldVal) {
  327 + if (!angular.equals(newVal, oldVal)) {
  328 + var res = $filter('filter')(ruleNodeComponents, {name: vm.ruleNodeSearch});
  329 + loadRuleChainLibrary(res);
  330 + }
  331 + }
  332 + );
317 333
318   - function loadRuleChainLibrary() {
  334 + $scope.$on('searchTextUpdated', function () {
  335 + updateRuleNodesHighlight();
  336 + });
  337 +
  338 + function loadRuleChainLibrary(ruleNodeComponents, loadRuleChain) {
  339 + for (var componentType in vm.ruleNodeTypesModel) {
  340 + vm.ruleNodeTypesModel[componentType].model.nodes.length = 0;
  341 + }
319 342 for (var i=0;i<ruleNodeComponents.length;i++) {
320 343 var ruleNodeComponent = ruleNodeComponents[i];
321   - var componentType = ruleNodeComponent.type;
  344 + componentType = ruleNodeComponent.type;
322 345 var model = vm.ruleNodeTypesModel[componentType].model;
323 346 var node = {
324 347 id: 'node-lib-' + componentType + '-' + model.nodes.length,
... ... @@ -349,7 +372,16 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
349 372 model.nodes.push(node);
350 373 }
351 374 vm.ruleChainLibraryLoaded = true;
352   - prepareRuleChain();
  375 + if (loadRuleChain) {
  376 + prepareRuleChain();
  377 + }
  378 + $mdUtil.nextTick(() => {
  379 + for (componentType in vm.ruleNodeTypesCanvasControl) {
  380 + if (vm.ruleNodeTypesCanvasControl[componentType].adjustCanvasSize) {
  381 + vm.ruleNodeTypesCanvasControl[componentType].adjustCanvasSize(true);
  382 + }
  383 + }
  384 + });
353 385 }
354 386
355 387 function prepareRuleChain() {
... ... @@ -519,6 +551,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
519 551
520 552 vm.isDirty = false;
521 553
  554 + updateRuleNodesHighlight();
  555 +
522 556 $mdUtil.nextTick(() => {
523 557 vm.ruleChainWatch = $scope.$watch('vm.ruleChainModel',
524 558 function (newVal, oldVal) {
... ... @@ -530,6 +564,20 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
530 564 });
531 565 }
532 566
  567 + function updateRuleNodesHighlight() {
  568 + for (var i=0;i<vm.ruleChainModel.nodes.length;i++) {
  569 + vm.ruleChainModel.nodes[i].highlighted = false;
  570 + }
  571 + if ($scope.searchConfig.searchText) {
  572 + var res = $filter('filter')(vm.ruleChainModel.nodes, {name: $scope.searchConfig.searchText});
  573 + if (res) {
  574 + for (i=0;i<res.length;i++) {
  575 + res[i].highlighted = true;
  576 + }
  577 + }
  578 + }
  579 + }
  580 +
533 581 function saveRuleChain() {
534 582 var ruleChainMetaData = {
535 583 ruleChainId: vm.ruleChain.id,
... ... @@ -642,6 +690,7 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
642 690 );
643 691 }
644 692 vm.ruleChainModel.nodes.push(ruleNode);
  693 + updateRuleNodesHighlight();
645 694 }, function () {
646 695 });
647 696 }
... ...
... ... @@ -76,7 +76,7 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
76 76 }
77 77 },
78 78 data: {
79   - searchEnabled: false,
  79 + searchEnabled: true,
80 80 pageTitle: 'rulechain.rulechain'
81 81 },
82 82 ncyBreadcrumb: {
... ...
... ... @@ -125,6 +125,13 @@
125 125 color: #333;
126 126 border: solid 1px #777;
127 127 font-size: 12px;
  128 + &.tb-rule-node-highlighted {
  129 + box-shadow: 0 0 10px 6px #51cbee;
  130 + .tb-node-title {
  131 + text-decoration: underline;
  132 + font-weight: bold;
  133 + }
  134 + }
128 135 &.tb-input-type {
129 136 background-color: #a3eaa9;
130 137 user-select: none;
... ... @@ -156,7 +163,7 @@
156 163
157 164 }
158 165 .tb-node-title {
159   - font-weight: 600;
  166 + font-weight: 500;
160 167 }
161 168 .tb-node-type, .tb-node-title {
162 169 overflow: hidden;
... ... @@ -184,6 +191,10 @@
184 191 bottom: 0;
185 192 background-color: #000;
186 193 opacity: 0;
  194 +/* &.tb-rule-node-highlighted {
  195 + background-color: green;
  196 + opacity: 0.15;
  197 + }*/
187 198 }
188 199 &.fc-hover {
189 200 .fc-node-overlay {
... ...
... ... @@ -19,17 +19,17 @@
19 19 <md-content flex tb-expand-fullscreen tb-confirm-on-exit is-dirty="vm.isDirty"
20 20 expand-tooltip-direction="bottom" layout="column" class="tb-rulechain"
21 21 ng-keydown="vm.keyDown($event)"
22   - ng-keyup="vm.keyUp($event)">
  22 + ng-keyup="vm.keyUp($event)" on-fullscreen-changed="vm.isFullscreen = expanded">
23 23 <section class="tb-rulechain-container" flex layout="column">
24 24 <div class="tb-rulechain-layout" flex layout="row">
25 25 <section layout="row" layout-wrap
26 26 class="tb-header-buttons md-fab tb-library-open">
27 27 <md-button ng-show="!vm.isLibraryOpen"
28 28 class="tb-btn-header tb-btn-open-library md-primary md-fab md-fab-top-left"
29   - aria-label="{{ 'action.apply' | translate }}"
  29 + aria-label="{{ 'rulenode.open-node-library' | translate }}"
30 30 ng-click="vm.isLibraryOpen = true">
31   - <md-tooltip md-direction="top">
32   - {{ 'action.apply-changes' | translate }}
  31 + <md-tooltip md-direction="{{vm.isFullscreen ? 'bottom' : 'top'}}">
  32 + {{ 'rulenode.open-node-library' | translate }}
33 33 </md-tooltip>
34 34 <ng-md-icon icon="menu"></ng-md-icon>
35 35 </md-button>
... ... @@ -43,7 +43,7 @@
43 43 <div class="md-toolbar-tools">
44 44 <md-button class="md-icon-button tb-small" aria-label="{{ 'action.search' | translate }}">
45 45 <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon>
46   - <md-tooltip md-direction="top">
  46 + <md-tooltip md-direction="{{vm.isFullscreen ? 'bottom' : 'top'}}">
47 47 {{'rulenode.search' | translate}}
48 48 </md-tooltip>
49 49 </md-button>
... ... @@ -53,15 +53,17 @@
53 53 <input ng-model="vm.ruleNodeSearch" placeholder="{{'rulenode.search' | translate}}"/>
54 54 </md-input-container>
55 55 </div>
56   - <md-button class="md-icon-button tb-small" aria-label="Close" ng-click="vm.ruleNodeSearch = ''">
  56 + <md-button class="md-icon-button tb-small" aria-label="Close"
  57 + ng-show="vm.ruleNodeSearch"
  58 + ng-click="vm.ruleNodeSearch = ''">
57 59 <md-icon aria-label="Close" class="material-icons">close</md-icon>
58   - <md-tooltip md-direction="top">
59   - {{ 'action.close' | translate }}
  60 + <md-tooltip md-direction="{{vm.isFullscreen ? 'bottom' : 'top'}}">
  61 + {{ 'action.clear-search' | translate }}
60 62 </md-tooltip>
61 63 </md-button>
62 64 <md-button class="md-icon-button tb-small" aria-label="Close" ng-click="vm.isLibraryOpen = false">
63 65 <md-icon aria-label="Close" class="material-icons">chevron_left</md-icon>
64   - <md-tooltip md-direction="top">
  66 + <md-tooltip md-direction="{{vm.isFullscreen ? 'bottom' : 'top'}}">
65 67 {{ 'action.close' | translate }}
66 68 </md-tooltip>
67 69 </md-button>
... ... @@ -90,6 +92,7 @@
90 92 callbacks="vm.nodeLibCallbacks"
91 93 node-width="170"
92 94 node-height="50"
  95 + control="vm.ruleNodeTypesCanvasControl[typeId]"
93 96 drop-target-id="'tb-rulchain-canvas'"></fc-canvas>
94 97 </md-expansion-panel-content>
95 98 </md-expansion-panel-expanded>
... ...
... ... @@ -22,8 +22,8 @@
22 22 ng-mousedown="callbacks.mouseDown($event, node)"
23 23 ng-mouseenter="callbacks.mouseEnter($event, node)"
24 24 ng-mouseleave="callbacks.mouseLeave($event, node)">
25   - <div class="{{flowchartConstants.nodeOverlayClass}}"></div>
26   - <div class="tb-rule-node {{node.nodeClass}}">
  25 + <div class="{{flowchartConstants.nodeOverlayClass}}" ng-class="{'tb-rule-node-highlighted' : node.highlighted}"></div>
  26 + <div class="tb-rule-node {{node.nodeClass}}" ng-class="{'tb-rule-node-highlighted' : node.highlighted}">
27 27 <md-icon aria-label="node-type-icon" flex="15"
28 28 class="material-icons">{{node.icon}}</md-icon>
29 29 <div layout="column" flex="85" layout-align="center">
... ...