Showing
13 changed files
with
318 additions
and
53 deletions
@@ -32,6 +32,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co | @@ -32,6 +32,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co | ||
32 | getRuleNodeComponents: getRuleNodeComponents, | 32 | getRuleNodeComponents: getRuleNodeComponents, |
33 | getRuleNodeComponentByClazz: getRuleNodeComponentByClazz, | 33 | getRuleNodeComponentByClazz: getRuleNodeComponentByClazz, |
34 | getRuleNodeSupportedLinks: getRuleNodeSupportedLinks, | 34 | getRuleNodeSupportedLinks: getRuleNodeSupportedLinks, |
35 | + ruleNodeAllowCustomLinks: ruleNodeAllowCustomLinks, | ||
35 | resolveTargetRuleChains: resolveTargetRuleChains, | 36 | resolveTargetRuleChains: resolveTargetRuleChains, |
36 | testScript: testScript, | 37 | testScript: testScript, |
37 | getLatestRuleNodeDebugInput: getLatestRuleNodeDebugInput | 38 | getLatestRuleNodeDebugInput: getLatestRuleNodeDebugInput |
@@ -127,21 +128,21 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co | @@ -127,21 +128,21 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co | ||
127 | 128 | ||
128 | function getRuleNodeSupportedLinks(component) { | 129 | function getRuleNodeSupportedLinks(component) { |
129 | var relationTypes = component.configurationDescriptor.nodeDefinition.relationTypes; | 130 | var relationTypes = component.configurationDescriptor.nodeDefinition.relationTypes; |
130 | - var customRelations = component.configurationDescriptor.nodeDefinition.customRelations; | ||
131 | - var linkLabels = []; | 131 | + var linkLabels = {}; |
132 | for (var i=0;i<relationTypes.length;i++) { | 132 | for (var i=0;i<relationTypes.length;i++) { |
133 | - linkLabels.push({ | ||
134 | - name: relationTypes[i], custom: false | ||
135 | - }); | ||
136 | - } | ||
137 | - if (customRelations) { | ||
138 | - linkLabels.push( | ||
139 | - { name: 'Custom', custom: true } | ||
140 | - ); | 133 | + var label = relationTypes[i]; |
134 | + linkLabels[label] = { | ||
135 | + name: label, | ||
136 | + value: label | ||
137 | + }; | ||
141 | } | 138 | } |
142 | return linkLabels; | 139 | return linkLabels; |
143 | } | 140 | } |
144 | 141 | ||
142 | + function ruleNodeAllowCustomLinks(component) { | ||
143 | + return component.configurationDescriptor.nodeDefinition.customRelations; | ||
144 | + } | ||
145 | + | ||
145 | function getRuleNodeComponents() { | 146 | function getRuleNodeComponents() { |
146 | var deferred = $q.defer(); | 147 | var deferred = $q.defer(); |
147 | if (ruleNodeComponents) { | 148 | if (ruleNodeComponents) { |
@@ -226,7 +227,10 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co | @@ -226,7 +227,10 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co | ||
226 | if (res && res.length) { | 227 | if (res && res.length) { |
227 | return res[0]; | 228 | return res[0]; |
228 | } | 229 | } |
229 | - return null; | 230 | + var unknownComponent = angular.copy(types.unknownNodeComponent); |
231 | + unknownComponent.clazz = clazz; | ||
232 | + unknownComponent.configurationDescriptor.nodeDefinition.details = "Unknown Rule Node class: " + clazz; | ||
233 | + return unknownComponent; | ||
230 | } | 234 | } |
231 | 235 | ||
232 | function resolveTargetRuleChains(ruleChainConnections) { | 236 | function resolveTargetRuleChains(ruleChainConnections) { |
@@ -510,6 +510,22 @@ export default angular.module('thingsboard.types', []) | @@ -510,6 +510,22 @@ export default angular.module('thingsboard.types', []) | ||
510 | } | 510 | } |
511 | } | 511 | } |
512 | }, | 512 | }, |
513 | + unknownNodeComponent: { | ||
514 | + type: 'UNKNOWN', | ||
515 | + name: 'unknown', | ||
516 | + clazz: 'tb.internal.Unknown', | ||
517 | + configurationDescriptor: { | ||
518 | + nodeDefinition: { | ||
519 | + description: "", | ||
520 | + details: "", | ||
521 | + inEnabled: true, | ||
522 | + outEnabled: true, | ||
523 | + relationTypes: [], | ||
524 | + customRelations: false, | ||
525 | + defaultConfiguration: {} | ||
526 | + } | ||
527 | + } | ||
528 | + }, | ||
513 | inputNodeComponent: { | 529 | inputNodeComponent: { |
514 | type: 'INPUT', | 530 | type: 'INPUT', |
515 | name: 'Input', | 531 | name: 'Input', |
@@ -565,6 +581,13 @@ export default angular.module('thingsboard.types', []) | @@ -565,6 +581,13 @@ export default angular.module('thingsboard.types', []) | ||
565 | nodeClass: "tb-input-type", | 581 | nodeClass: "tb-input-type", |
566 | icon: "input", | 582 | icon: "input", |
567 | special: true | 583 | special: true |
584 | + }, | ||
585 | + UNKNOWN: { | ||
586 | + value: "UNKNOWN", | ||
587 | + name: "rulenode.type-unknown", | ||
588 | + details: "rulenode.type-unknown-details", | ||
589 | + nodeClass: "tb-unknown-type", | ||
590 | + icon: "help_outline" | ||
568 | } | 591 | } |
569 | }, | 592 | }, |
570 | valueType: { | 593 | valueType: { |
@@ -16,8 +16,8 @@ | @@ -16,8 +16,8 @@ | ||
16 | 16 | ||
17 | --> | 17 | --> |
18 | <div flex layout="column" style="margin-top: -10px;"> | 18 | <div flex layout="column" style="margin-top: -10px;"> |
19 | - <div flex>{{vm.item.additionalInfo.description}}</div> | ||
20 | - <div flex style="text-transform: uppercase; padding-bottom: 10px;">{{vm.item.type}}</div> | ||
21 | - <div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'device.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div> | ||
22 | - <div class="tb-small" ng-show="vm.isPublic()">{{'device.public' | translate}}</div> | 19 | + <div style="text-transform: uppercase; padding-bottom: 5px;">{{vm.item.type}}</div> |
20 | + <div class="tb-card-description">{{vm.item.additionalInfo.description}}</div> | ||
21 | + <div style="padding-top: 5px;" class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'device.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div> | ||
22 | + <div style="padding-top: 5px;" class="tb-small" ng-show="vm.isPublic()">{{'device.public' | translate}}</div> | ||
23 | </div> | 23 | </div> |
@@ -1157,6 +1157,11 @@ export default angular.module('thingsboard.locale', []) | @@ -1157,6 +1157,11 @@ export default angular.module('thingsboard.locale', []) | ||
1157 | "link-label-required": "Link label is required.", | 1157 | "link-label-required": "Link label is required.", |
1158 | "custom-link-label": "Custom link label", | 1158 | "custom-link-label": "Custom link label", |
1159 | "custom-link-label-required": "Custom link label is required.", | 1159 | "custom-link-label-required": "Custom link label is required.", |
1160 | + "link-labels": "Link labels", | ||
1161 | + "link-labels-required": "Link labels is required.", | ||
1162 | + "no-link-labels-found": "No link labels found", | ||
1163 | + "no-link-label-matching": "'{{label}}' not found.", | ||
1164 | + "create-new-link-label": "Create a new one!", | ||
1160 | "type-filter": "Filter", | 1165 | "type-filter": "Filter", |
1161 | "type-filter-details": "Filter incoming messages with configured conditions", | 1166 | "type-filter-details": "Filter incoming messages with configured conditions", |
1162 | "type-enrichment": "Enrichment", | 1167 | "type-enrichment": "Enrichment", |
@@ -1171,6 +1176,8 @@ export default angular.module('thingsboard.locale', []) | @@ -1171,6 +1176,8 @@ export default angular.module('thingsboard.locale', []) | ||
1171 | "type-rule-chain-details": "Forwards incoming messages to specified Rule Chain", | 1176 | "type-rule-chain-details": "Forwards incoming messages to specified Rule Chain", |
1172 | "type-input": "Input", | 1177 | "type-input": "Input", |
1173 | "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node", | 1178 | "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node", |
1179 | + "type-unknown": "Unknown", | ||
1180 | + "type-unknown-details": "Unresolved Rule Node", | ||
1174 | "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.", | 1181 | "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.", |
1175 | "ui-resources-load-error": "Failed to load configuration ui resources.", | 1182 | "ui-resources-load-error": "Failed to load configuration ui resources.", |
1176 | "invalid-target-rulechain": "Unable to resolve target rule chain!", | 1183 | "invalid-target-rulechain": "Unable to resolve target rule chain!", |
@@ -31,7 +31,7 @@ | @@ -31,7 +31,7 @@ | ||
31 | <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> | 31 | <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
32 | <md-dialog-content> | 32 | <md-dialog-content> |
33 | <div class="md-dialog-content"> | 33 | <div class="md-dialog-content"> |
34 | - <tb-rule-node-link link="vm.link" labels="vm.labels" is-edit="true" the-form="theForm"></tb-rule-node-link> | 34 | + <tb-rule-node-link ng-model="vm.link" allowed-labels="vm.labels" is-edit="true" allow-custom="vm.allowCustomLabels"></tb-rule-node-link> |
35 | </div> | 35 | </div> |
36 | </md-dialog-content> | 36 | </md-dialog-content> |
37 | <md-dialog-actions layout="row"> | 37 | <md-dialog-actions layout="row"> |
@@ -17,7 +17,46 @@ | @@ -17,7 +17,46 @@ | ||
17 | --> | 17 | --> |
18 | <md-content class="md-padding tb-link" layout="column"> | 18 | <md-content class="md-padding tb-link" layout="column"> |
19 | <fieldset ng-disabled="$root.loading || !isEdit || isReadOnly"> | 19 | <fieldset ng-disabled="$root.loading || !isEdit || isReadOnly"> |
20 | - <md-input-container class="md-block"> | 20 | + <label translate class="tb-title no-padding" ng-class="{'tb-required': required}">rulenode.link-labels</label> |
21 | + <md-chips id="link_label_chips" | ||
22 | + ng-required="true" | ||
23 | + readonly="$root.loading || !isEdit || isReadOnly" | ||
24 | + ng-model="labels" md-autocomplete-snap | ||
25 | + md-transform-chip="transformLinkLabelChip($chip)" | ||
26 | + md-require-match="!allowCustom"> | ||
27 | + <md-autocomplete | ||
28 | + id="link_label" | ||
29 | + md-no-cache="true" | ||
30 | + md-selected-item="selectedLabel" | ||
31 | + md-search-text="labelSearchText" | ||
32 | + md-items="item in labelsSearch(labelSearchText)" | ||
33 | + md-item-text="item.name" | ||
34 | + md-min-length="0" | ||
35 | + placeholder="{{'rulenode.link-label' | translate }}" | ||
36 | + md-menu-class="tb-link-label-autocomplete"> | ||
37 | + <span md-highlight-text="labelSearchText" md-highlight-flags="^i">{{item}}</span> | ||
38 | + <md-not-found> | ||
39 | + <div class="tb-not-found"> | ||
40 | + <div class="tb-no-entries" ng-if="!labelSearchText || !labelSearchText.length"> | ||
41 | + <span translate>rulenode.no-link-labels-found</span> | ||
42 | + </div> | ||
43 | + <div ng-if="labelSearchText && labelSearchText.length"> | ||
44 | + <span translate translate-values='{ label: "{{labelSearchText | truncate:true:6:'...'}}" }'>rulenode.no-link-label-matching</span> | ||
45 | + <span ng-if="allowCustom"> | ||
46 | + <a translate ng-click="createLinkLabel($event, '#link_label_chips')">rulenode.create-new-link-label</a> | ||
47 | + </span> | ||
48 | + </div> | ||
49 | + </div> | ||
50 | + </md-not-found> | ||
51 | + </md-autocomplete> | ||
52 | + <md-chip-template> | ||
53 | + <span>{{$chip.name}}</span> | ||
54 | + </md-chip-template> | ||
55 | + </md-chips> | ||
56 | + <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert"> | ||
57 | + <div translate ng-message="linkLabels" class="tb-error-message">rulenode.link-labels-required</div> | ||
58 | + </div> | ||
59 | + <!--md-input-container class="md-block"> | ||
21 | <label translate>rulenode.link-label</label> | 60 | <label translate>rulenode.link-label</label> |
22 | <md-select ng-model="selectedLabel" ng-change="selectedLabelChanged()"> | 61 | <md-select ng-model="selectedLabel" ng-change="selectedLabelChanged()"> |
23 | <md-option ng-repeat="label in labels" ng-value="label"> | 62 | <md-option ng-repeat="label in labels" ng-value="label"> |
@@ -34,6 +73,6 @@ | @@ -34,6 +73,6 @@ | ||
34 | <div ng-messages="theForm.customLinkLabel.$error"> | 73 | <div ng-messages="theForm.customLinkLabel.$error"> |
35 | <div translate ng-message="required">rulenode.custom-link-label-required</div> | 74 | <div translate ng-message="required">rulenode.custom-link-label-required</div> |
36 | </div> | 75 | </div> |
37 | - </md-input-container> | 76 | + </md-input-container--> |
38 | </fieldset> | 77 | </fieldset> |
39 | </md-content> | 78 | </md-content> |
@@ -14,6 +14,8 @@ | @@ -14,6 +14,8 @@ | ||
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | 16 | ||
17 | +import './link.scss'; | ||
18 | + | ||
17 | /* eslint-disable import/no-unresolved, import/default */ | 19 | /* eslint-disable import/no-unresolved, import/default */ |
18 | 20 | ||
19 | import linkFieldsetTemplate from './link-fieldset.tpl.html'; | 21 | import linkFieldsetTemplate from './link-fieldset.tpl.html'; |
@@ -22,13 +24,18 @@ import linkFieldsetTemplate from './link-fieldset.tpl.html'; | @@ -22,13 +24,18 @@ import linkFieldsetTemplate from './link-fieldset.tpl.html'; | ||
22 | 24 | ||
23 | /*@ngInject*/ | 25 | /*@ngInject*/ |
24 | export default function LinkDirective($compile, $templateCache, $filter) { | 26 | export default function LinkDirective($compile, $templateCache, $filter) { |
25 | - var linker = function (scope, element) { | 27 | + var linker = function (scope, element, attrs, ngModelCtrl) { |
26 | var template = $templateCache.get(linkFieldsetTemplate); | 28 | var template = $templateCache.get(linkFieldsetTemplate); |
27 | element.html(template); | 29 | element.html(template); |
28 | 30 | ||
29 | scope.selectedLabel = null; | 31 | scope.selectedLabel = null; |
32 | + scope.labelSearchText = null; | ||
33 | + | ||
34 | + scope.ngModelCtrl = ngModelCtrl; | ||
35 | + | ||
36 | + var labelsList = []; | ||
30 | 37 | ||
31 | - scope.$watch('link', function() { | 38 | + /*scope.$watch('link', function() { |
32 | scope.selectedLabel = null; | 39 | scope.selectedLabel = null; |
33 | if (scope.link && scope.labels) { | 40 | if (scope.link && scope.labels) { |
34 | if (scope.link.label) { | 41 | if (scope.link.label) { |
@@ -53,19 +60,100 @@ export default function LinkDirective($compile, $templateCache, $filter) { | @@ -53,19 +60,100 @@ export default function LinkDirective($compile, $templateCache, $filter) { | ||
53 | scope.link.label = ""; | 60 | scope.link.label = ""; |
54 | } | 61 | } |
55 | } | 62 | } |
63 | + };*/ | ||
64 | + | ||
65 | + scope.transformLinkLabelChip = function (chip) { | ||
66 | + var res = $filter('filter')(labelsList, {name: chip}, true); | ||
67 | + var result; | ||
68 | + if (res && res.length) { | ||
69 | + result = angular.copy(res[0]); | ||
70 | + } else { | ||
71 | + result = { | ||
72 | + name: chip, | ||
73 | + value: chip | ||
74 | + }; | ||
75 | + } | ||
76 | + return result; | ||
77 | + }; | ||
78 | + | ||
79 | + scope.labelsSearch = function (searchText) { | ||
80 | + var labels = searchText ? $filter('filter')(labelsList, {name: searchText}) : labelsList; | ||
81 | + return labels.map((label) => label.name); | ||
82 | + }; | ||
83 | + | ||
84 | + scope.createLinkLabel = function (event, chipsId) { | ||
85 | + var chipsChild = angular.element(chipsId, element)[0].firstElementChild; | ||
86 | + var el = angular.element(chipsChild); | ||
87 | + var chipBuffer = el.scope().$mdChipsCtrl.getChipBuffer(); | ||
88 | + event.preventDefault(); | ||
89 | + event.stopPropagation(); | ||
90 | + el.scope().$mdChipsCtrl.appendChip(chipBuffer.trim()); | ||
91 | + el.scope().$mdChipsCtrl.resetChipBuffer(); | ||
56 | }; | 92 | }; |
57 | 93 | ||
94 | + | ||
95 | + ngModelCtrl.$render = function () { | ||
96 | + labelsList.length = 0; | ||
97 | + for (var label in scope.allowedLabels) { | ||
98 | + var linkLabel = { | ||
99 | + name: scope.allowedLabels[label].name, | ||
100 | + value: scope.allowedLabels[label].value | ||
101 | + }; | ||
102 | + labelsList.push(linkLabel); | ||
103 | + } | ||
104 | + | ||
105 | + var link = ngModelCtrl.$viewValue; | ||
106 | + var labels = []; | ||
107 | + if (link && link.labels) { | ||
108 | + for (var i = 0; i < link.labels.length; i++) { | ||
109 | + label = link.labels[i]; | ||
110 | + if (scope.allowedLabels[label]) { | ||
111 | + labels.push(angular.copy(scope.allowedLabels[label])); | ||
112 | + } else { | ||
113 | + labels.push({ | ||
114 | + name: label, | ||
115 | + value: label | ||
116 | + }); | ||
117 | + } | ||
118 | + } | ||
119 | + } | ||
120 | + scope.labels = labels; | ||
121 | + scope.$watch('labels', function (newVal, prevVal) { | ||
122 | + if (!angular.equals(newVal, prevVal)) { | ||
123 | + updateLabels(); | ||
124 | + } | ||
125 | + }, true); | ||
126 | + }; | ||
127 | + | ||
128 | + function updateLabels() { | ||
129 | + if (ngModelCtrl.$viewValue) { | ||
130 | + var labels = []; | ||
131 | + for (var i = 0; i < scope.labels.length; i++) { | ||
132 | + labels.push(scope.labels[i].value); | ||
133 | + } | ||
134 | + ngModelCtrl.$viewValue.labels = labels; | ||
135 | + ngModelCtrl.$viewValue.label = labels.join(' / '); | ||
136 | + updateValidity(); | ||
137 | + } | ||
138 | + } | ||
139 | + | ||
140 | + function updateValidity() { | ||
141 | + var valid = ngModelCtrl.$viewValue.labels && | ||
142 | + ngModelCtrl.$viewValue.labels.length ? true : false; | ||
143 | + ngModelCtrl.$setValidity('linkLabels', valid); | ||
144 | + } | ||
145 | + | ||
58 | $compile(element.contents())(scope); | 146 | $compile(element.contents())(scope); |
59 | } | 147 | } |
60 | return { | 148 | return { |
61 | restrict: "E", | 149 | restrict: "E", |
150 | + require: "^ngModel", | ||
62 | link: linker, | 151 | link: linker, |
63 | scope: { | 152 | scope: { |
64 | - link: '=', | ||
65 | - labels: '=', | 153 | + allowedLabels: '=', |
154 | + allowCustom: '=', | ||
66 | isEdit: '=', | 155 | isEdit: '=', |
67 | - isReadOnly: '=', | ||
68 | - theForm: '=' | 156 | + isReadOnly: '=' |
69 | } | 157 | } |
70 | }; | 158 | }; |
71 | } | 159 | } |
ui/src/app/rulechain/link.scss
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2018 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | + | ||
17 | +.tb-link-label-autocomplete { | ||
18 | + .tb-not-found { | ||
19 | + display: block; | ||
20 | + line-height: 1.5; | ||
21 | + height: 48px; | ||
22 | + .tb-no-entries { | ||
23 | + line-height: 48px; | ||
24 | + } | ||
25 | + } | ||
26 | + li { | ||
27 | + height: auto !important; | ||
28 | + white-space: normal !important; | ||
29 | + } | ||
30 | +} |
@@ -669,11 +669,15 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | @@ -669,11 +669,15 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | ||
669 | } | 669 | } |
670 | } else { | 670 | } else { |
671 | if (edge.label) { | 671 | if (edge.label) { |
672 | + if (!edge.labels) { | ||
673 | + edge.labels = edge.label.split(' / '); | ||
674 | + } | ||
672 | deferred.resolve(edge); | 675 | deferred.resolve(edge); |
673 | } else { | 676 | } else { |
674 | var labels = ruleChainService.getRuleNodeSupportedLinks(sourceNode.component); | 677 | var labels = ruleChainService.getRuleNodeSupportedLinks(sourceNode.component); |
678 | + var allowCustomLabels = ruleChainService.ruleNodeAllowCustomLinks(sourceNode.component); | ||
675 | vm.enableHotKeys = false; | 679 | vm.enableHotKeys = false; |
676 | - addRuleNodeLink(event, edge, labels).then( | 680 | + addRuleNodeLink(event, edge, labels, allowCustomLabels).then( |
677 | (link) => { | 681 | (link) => { |
678 | deferred.resolve(link); | 682 | deferred.resolve(link); |
679 | vm.enableHotKeys = true; | 683 | vm.enableHotKeys = true; |
@@ -713,6 +717,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | @@ -713,6 +717,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | ||
713 | vm.isEditingRuleNode = false; | 717 | vm.isEditingRuleNode = false; |
714 | vm.editingRuleNode = null; | 718 | vm.editingRuleNode = null; |
715 | vm.editingRuleNodeLinkLabels = ruleChainService.getRuleNodeSupportedLinks(sourceNode.component); | 719 | vm.editingRuleNodeLinkLabels = ruleChainService.getRuleNodeSupportedLinks(sourceNode.component); |
720 | + vm.editingRuleNodeAllowCustomLabels = ruleChainService.ruleNodeAllowCustomLinks(sourceNode.component); | ||
716 | vm.isEditingRuleNodeLink = true; | 721 | vm.isEditingRuleNodeLink = true; |
717 | vm.editingRuleNodeLinkIndex = vm.ruleChainModel.edges.indexOf(edge); | 722 | vm.editingRuleNodeLinkIndex = vm.ruleChainModel.edges.indexOf(edge); |
718 | vm.editingRuleNodeLink = angular.copy(edge); | 723 | vm.editingRuleNodeLink = angular.copy(edge); |
@@ -744,7 +749,8 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | @@ -744,7 +749,8 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | ||
744 | isInputSource: isInputSource, | 749 | isInputSource: isInputSource, |
745 | fromIndex: fromIndex, | 750 | fromIndex: fromIndex, |
746 | toIndex: toIndex, | 751 | toIndex: toIndex, |
747 | - label: edge.label | 752 | + label: edge.label, |
753 | + labels: edge.labels | ||
748 | }; | 754 | }; |
749 | connections.push(connection); | 755 | connections.push(connection); |
750 | } | 756 | } |
@@ -816,7 +822,8 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | @@ -816,7 +822,8 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | ||
816 | var edge = { | 822 | var edge = { |
817 | source: source, | 823 | source: source, |
818 | destination: destination, | 824 | destination: destination, |
819 | - label: connection.label | 825 | + label: connection.label, |
826 | + labels: connection.labels | ||
820 | }; | 827 | }; |
821 | vm.ruleChainModel.edges.push(edge); | 828 | vm.ruleChainModel.edges.push(edge); |
822 | vm.modelservice.edges.select(edge); | 829 | vm.modelservice.edges.select(edge); |
@@ -1024,6 +1031,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | @@ -1024,6 +1031,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | ||
1024 | } | 1031 | } |
1025 | 1032 | ||
1026 | if (vm.ruleChainMetaData.connections) { | 1033 | if (vm.ruleChainMetaData.connections) { |
1034 | + var edgeMap = {}; | ||
1027 | for (i = 0; i < vm.ruleChainMetaData.connections.length; i++) { | 1035 | for (i = 0; i < vm.ruleChainMetaData.connections.length; i++) { |
1028 | var connection = vm.ruleChainMetaData.connections[i]; | 1036 | var connection = vm.ruleChainMetaData.connections[i]; |
1029 | var sourceNode = nodes[connection.fromIndex]; | 1037 | var sourceNode = nodes[connection.fromIndex]; |
@@ -1032,12 +1040,23 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | @@ -1032,12 +1040,23 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | ||
1032 | var sourceConnectors = vm.modelservice.nodes.getConnectorsByType(sourceNode, flowchartConstants.rightConnectorType); | 1040 | var sourceConnectors = vm.modelservice.nodes.getConnectorsByType(sourceNode, flowchartConstants.rightConnectorType); |
1033 | var destConnectors = vm.modelservice.nodes.getConnectorsByType(destNode, flowchartConstants.leftConnectorType); | 1041 | var destConnectors = vm.modelservice.nodes.getConnectorsByType(destNode, flowchartConstants.leftConnectorType); |
1034 | if (sourceConnectors && sourceConnectors.length && destConnectors && destConnectors.length) { | 1042 | if (sourceConnectors && sourceConnectors.length && destConnectors && destConnectors.length) { |
1035 | - edge = { | ||
1036 | - source: sourceConnectors[0].id, | ||
1037 | - destination: destConnectors[0].id, | ||
1038 | - label: connection.type | ||
1039 | - }; | ||
1040 | - vm.ruleChainModel.edges.push(edge); | 1043 | + var sourceId = sourceConnectors[0].id; |
1044 | + var destId = destConnectors[0].id; | ||
1045 | + var edgeKey = sourceId + '_' + destId; | ||
1046 | + edge = edgeMap[edgeKey]; | ||
1047 | + if (!edge) { | ||
1048 | + edge = { | ||
1049 | + source: sourceId, | ||
1050 | + destination: destId, | ||
1051 | + label: connection.type, | ||
1052 | + labels: [connection.type] | ||
1053 | + }; | ||
1054 | + edgeMap[edgeKey] = edge; | ||
1055 | + vm.ruleChainModel.edges.push(edge); | ||
1056 | + } else { | ||
1057 | + edge.label += ' / ' +connection.type; | ||
1058 | + edge.labels.push(connection.type); | ||
1059 | + } | ||
1041 | } | 1060 | } |
1042 | } | 1061 | } |
1043 | } | 1062 | } |
@@ -1045,6 +1064,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | @@ -1045,6 +1064,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | ||
1045 | 1064 | ||
1046 | if (vm.ruleChainMetaData.ruleChainConnections) { | 1065 | if (vm.ruleChainMetaData.ruleChainConnections) { |
1047 | var ruleChainNodesMap = {}; | 1066 | var ruleChainNodesMap = {}; |
1067 | + var ruleChainEdgeMap = {}; | ||
1048 | for (i = 0; i < vm.ruleChainMetaData.ruleChainConnections.length; i++) { | 1068 | for (i = 0; i < vm.ruleChainMetaData.ruleChainConnections.length; i++) { |
1049 | var ruleChainConnection = vm.ruleChainMetaData.ruleChainConnections[i]; | 1069 | var ruleChainConnection = vm.ruleChainMetaData.ruleChainConnections[i]; |
1050 | var ruleChain = ruleChainsMap[ruleChainConnection.targetRuleChainId.id]; | 1070 | var ruleChain = ruleChainsMap[ruleChainConnection.targetRuleChainId.id]; |
@@ -1081,12 +1101,23 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | @@ -1081,12 +1101,23 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | ||
1081 | if (sourceNode) { | 1101 | if (sourceNode) { |
1082 | connectors = vm.modelservice.nodes.getConnectorsByType(sourceNode, flowchartConstants.rightConnectorType); | 1102 | connectors = vm.modelservice.nodes.getConnectorsByType(sourceNode, flowchartConstants.rightConnectorType); |
1083 | if (connectors && connectors.length) { | 1103 | if (connectors && connectors.length) { |
1084 | - var ruleChainEdge = { | ||
1085 | - source: connectors[0].id, | ||
1086 | - destination: ruleChainNode.connectors[0].id, | ||
1087 | - label: ruleChainConnection.type | ||
1088 | - }; | ||
1089 | - vm.ruleChainModel.edges.push(ruleChainEdge); | 1104 | + sourceId = connectors[0].id; |
1105 | + destId = ruleChainNode.connectors[0].id; | ||
1106 | + edgeKey = sourceId + '_' + destId; | ||
1107 | + var ruleChainEdge = ruleChainEdgeMap[edgeKey]; | ||
1108 | + if (!ruleChainEdge) { | ||
1109 | + ruleChainEdge = { | ||
1110 | + source: sourceId, | ||
1111 | + destination: destId, | ||
1112 | + label: ruleChainConnection.type, | ||
1113 | + labels: [ruleChainConnection.type] | ||
1114 | + }; | ||
1115 | + ruleChainEdgeMap[edgeKey] = ruleChainEdge; | ||
1116 | + vm.ruleChainModel.edges.push(ruleChainEdge); | ||
1117 | + } else { | ||
1118 | + ruleChainEdge.label += ' / ' +ruleChainConnection.type; | ||
1119 | + ruleChainEdge.labels.push(ruleChainConnection.type); | ||
1120 | + } | ||
1090 | } | 1121 | } |
1091 | } | 1122 | } |
1092 | } | 1123 | } |
@@ -1199,8 +1230,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | @@ -1199,8 +1230,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | ||
1199 | var ruleChainConnection = { | 1230 | var ruleChainConnection = { |
1200 | fromIndex: fromIndex, | 1231 | fromIndex: fromIndex, |
1201 | targetRuleChainId: {entityType: vm.types.entityType.rulechain, id: destNode.targetRuleChainId}, | 1232 | targetRuleChainId: {entityType: vm.types.entityType.rulechain, id: destNode.targetRuleChainId}, |
1202 | - additionalInfo: destNode.additionalInfo, | ||
1203 | - type: edge.label | 1233 | + additionalInfo: destNode.additionalInfo |
1204 | }; | 1234 | }; |
1205 | if (!ruleChainConnection.additionalInfo) { | 1235 | if (!ruleChainConnection.additionalInfo) { |
1206 | ruleChainConnection.additionalInfo = {}; | 1236 | ruleChainConnection.additionalInfo = {}; |
@@ -1208,15 +1238,22 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | @@ -1208,15 +1238,22 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | ||
1208 | ruleChainConnection.additionalInfo.layoutX = Math.round(destNode.x); | 1238 | ruleChainConnection.additionalInfo.layoutX = Math.round(destNode.x); |
1209 | ruleChainConnection.additionalInfo.layoutY = Math.round(destNode.y); | 1239 | ruleChainConnection.additionalInfo.layoutY = Math.round(destNode.y); |
1210 | ruleChainConnection.additionalInfo.ruleChainNodeId = destNode.id; | 1240 | ruleChainConnection.additionalInfo.ruleChainNodeId = destNode.id; |
1211 | - ruleChainMetaData.ruleChainConnections.push(ruleChainConnection); | 1241 | + for (var rcIndex=0;rcIndex<edge.labels.length;rcIndex++) { |
1242 | + var newRuleChainConnection = angular.copy(ruleChainConnection); | ||
1243 | + newRuleChainConnection.type = edge.labels[rcIndex]; | ||
1244 | + ruleChainMetaData.ruleChainConnections.push(newRuleChainConnection); | ||
1245 | + } | ||
1212 | } else { | 1246 | } else { |
1213 | var toIndex = nodes.indexOf(destNode); | 1247 | var toIndex = nodes.indexOf(destNode); |
1214 | var nodeConnection = { | 1248 | var nodeConnection = { |
1215 | fromIndex: fromIndex, | 1249 | fromIndex: fromIndex, |
1216 | - toIndex: toIndex, | ||
1217 | - type: edge.label | 1250 | + toIndex: toIndex |
1218 | }; | 1251 | }; |
1219 | - ruleChainMetaData.connections.push(nodeConnection); | 1252 | + for (var cIndex=0;cIndex<edge.labels.length;cIndex++) { |
1253 | + var newNodeConnection = angular.copy(nodeConnection); | ||
1254 | + newNodeConnection.type = edge.labels[cIndex]; | ||
1255 | + ruleChainMetaData.connections.push(newNodeConnection); | ||
1256 | + } | ||
1220 | } | 1257 | } |
1221 | } | 1258 | } |
1222 | } | 1259 | } |
@@ -1285,13 +1322,13 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | @@ -1285,13 +1322,13 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time | ||
1285 | }); | 1322 | }); |
1286 | } | 1323 | } |
1287 | 1324 | ||
1288 | - function addRuleNodeLink($event, link, labels) { | 1325 | + function addRuleNodeLink($event, link, labels, allowCustomLabels) { |
1289 | return $mdDialog.show({ | 1326 | return $mdDialog.show({ |
1290 | controller: 'AddRuleNodeLinkController', | 1327 | controller: 'AddRuleNodeLinkController', |
1291 | controllerAs: 'vm', | 1328 | controllerAs: 'vm', |
1292 | templateUrl: addRuleNodeLinkTemplate, | 1329 | templateUrl: addRuleNodeLinkTemplate, |
1293 | parent: angular.element($document[0].body), | 1330 | parent: angular.element($document[0].body), |
1294 | - locals: {link: link, labels: labels}, | 1331 | + locals: {link: link, labels: labels, allowCustomLabels: allowCustomLabels}, |
1295 | fullscreen: true, | 1332 | fullscreen: true, |
1296 | targetEvent: $event | 1333 | targetEvent: $event |
1297 | }); | 1334 | }); |
@@ -1335,13 +1372,14 @@ export function AddRuleNodeController($scope, $mdDialog, ruleNode, ruleChainId, | @@ -1335,13 +1372,14 @@ export function AddRuleNodeController($scope, $mdDialog, ruleNode, ruleChainId, | ||
1335 | } | 1372 | } |
1336 | 1373 | ||
1337 | /*@ngInject*/ | 1374 | /*@ngInject*/ |
1338 | -export function AddRuleNodeLinkController($scope, $mdDialog, link, labels, helpLinks) { | 1375 | +export function AddRuleNodeLinkController($scope, $mdDialog, link, labels, allowCustomLabels, helpLinks) { |
1339 | 1376 | ||
1340 | var vm = this; | 1377 | var vm = this; |
1341 | 1378 | ||
1342 | vm.helpLinks = helpLinks; | 1379 | vm.helpLinks = helpLinks; |
1343 | vm.link = link; | 1380 | vm.link = link; |
1344 | vm.labels = labels; | 1381 | vm.labels = labels; |
1382 | + vm.allowCustomLabels = allowCustomLabels; | ||
1345 | 1383 | ||
1346 | vm.add = add; | 1384 | vm.add = add; |
1347 | vm.cancel = cancel; | 1385 | vm.cancel = cancel; |
@@ -170,6 +170,9 @@ | @@ -170,6 +170,9 @@ | ||
170 | &.tb-rule-chain-type { | 170 | &.tb-rule-chain-type { |
171 | background-color: #d6c4f1; | 171 | background-color: #d6c4f1; |
172 | } | 172 | } |
173 | + &.tb-unknown-type { | ||
174 | + background-color: #f16c29; | ||
175 | + } | ||
173 | } | 176 | } |
174 | 177 | ||
175 | .tb-rule-node { | 178 | .tb-rule-node { |
@@ -202,6 +205,7 @@ | @@ -202,6 +205,7 @@ | ||
202 | background-color: #a3eaa9; | 205 | background-color: #a3eaa9; |
203 | user-select: none; | 206 | user-select: none; |
204 | } | 207 | } |
208 | + | ||
205 | md-icon { | 209 | md-icon { |
206 | font-size: 20px; | 210 | font-size: 20px; |
207 | width: 20px; | 211 | width: 20px; |
@@ -207,11 +207,11 @@ | @@ -207,11 +207,11 @@ | ||
207 | </details-buttons> | 207 | </details-buttons> |
208 | <form name="vm.ruleNodeLinkForm" ng-if="vm.isEditingRuleNodeLink"> | 208 | <form name="vm.ruleNodeLinkForm" ng-if="vm.isEditingRuleNodeLink"> |
209 | <tb-rule-node-link | 209 | <tb-rule-node-link |
210 | - link="vm.editingRuleNodeLink" | ||
211 | - labels="vm.editingRuleNodeLinkLabels" | 210 | + ng-model="vm.editingRuleNodeLink" |
211 | + allowed-labels="vm.editingRuleNodeLinkLabels" | ||
212 | + allow-custom="vm.editingRuleNodeAllowCustomLabels" | ||
212 | is-edit="true" | 213 | is-edit="true" |
213 | - is-read-only="false" | ||
214 | - the-form="vm.ruleNodeLinkForm"> | 214 | + is-read-only="false"> |
215 | </tb-rule-node-link> | 215 | </tb-rule-node-link> |
216 | </form> | 216 | </form> |
217 | </tb-details-sidenav> | 217 | </tb-details-sidenav> |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | @import "~compass-sass-mixins/lib/compass"; | 16 | @import "~compass-sass-mixins/lib/compass"; |
17 | @import "constants"; | 17 | @import "constants"; |
18 | @import "animations"; | 18 | @import "animations"; |
19 | +@import "mixins"; | ||
19 | @import "fonts"; | 20 | @import "fonts"; |
20 | 21 | ||
21 | /*************** | 22 | /*************** |
@@ -437,6 +438,12 @@ pre.tb-highlight { | @@ -437,6 +438,12 @@ pre.tb-highlight { | ||
437 | } | 438 | } |
438 | } | 439 | } |
439 | 440 | ||
441 | +.tb-card-description { | ||
442 | + color: rgba(0,0,0,0.54); | ||
443 | + font-size: 13px; | ||
444 | + @include line-clamp(2, 1.1); | ||
445 | +} | ||
446 | + | ||
440 | /*********************** | 447 | /*********************** |
441 | * Flow | 448 | * Flow |
442 | ***********************/ | 449 | ***********************/ |
@@ -31,4 +31,29 @@ | @@ -31,4 +31,29 @@ | ||
31 | &:-ms-input-placeholder { | 31 | &:-ms-input-placeholder { |
32 | @content; | 32 | @content; |
33 | } | 33 | } |
34 | -} | ||
34 | +} | ||
35 | + | ||
36 | +@mixin line-clamp($numLines: 1, $lineHeight: 1.412) { | ||
37 | + overflow: hidden; | ||
38 | + position: relative; | ||
39 | + line-height: $lineHeight; | ||
40 | + text-align: justify; | ||
41 | + margin-right: -1em; | ||
42 | + padding-right: 2em; | ||
43 | + max-height: ($numLines*$lineHeight)+em; | ||
44 | + &:before { | ||
45 | + content: '...'; | ||
46 | + position: absolute; | ||
47 | + right: 1em; | ||
48 | + bottom: 0; | ||
49 | + } | ||
50 | + &:after { | ||
51 | + content: ''; | ||
52 | + position: absolute; | ||
53 | + right: 1em; | ||
54 | + width: 1em; | ||
55 | + height: 1em; | ||
56 | + margin-top: 0.2em; | ||
57 | + background: white; | ||
58 | + } | ||
59 | +} |