Showing
13 changed files
with
318 additions
and
53 deletions
... | ... | @@ -32,6 +32,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co |
32 | 32 | getRuleNodeComponents: getRuleNodeComponents, |
33 | 33 | getRuleNodeComponentByClazz: getRuleNodeComponentByClazz, |
34 | 34 | getRuleNodeSupportedLinks: getRuleNodeSupportedLinks, |
35 | + ruleNodeAllowCustomLinks: ruleNodeAllowCustomLinks, | |
35 | 36 | resolveTargetRuleChains: resolveTargetRuleChains, |
36 | 37 | testScript: testScript, |
37 | 38 | getLatestRuleNodeDebugInput: getLatestRuleNodeDebugInput |
... | ... | @@ -127,21 +128,21 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co |
127 | 128 | |
128 | 129 | function getRuleNodeSupportedLinks(component) { |
129 | 130 | var relationTypes = component.configurationDescriptor.nodeDefinition.relationTypes; |
130 | - var customRelations = component.configurationDescriptor.nodeDefinition.customRelations; | |
131 | - var linkLabels = []; | |
131 | + var linkLabels = {}; | |
132 | 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 | 139 | return linkLabels; |
143 | 140 | } |
144 | 141 | |
142 | + function ruleNodeAllowCustomLinks(component) { | |
143 | + return component.configurationDescriptor.nodeDefinition.customRelations; | |
144 | + } | |
145 | + | |
145 | 146 | function getRuleNodeComponents() { |
146 | 147 | var deferred = $q.defer(); |
147 | 148 | if (ruleNodeComponents) { |
... | ... | @@ -226,7 +227,10 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co |
226 | 227 | if (res && res.length) { |
227 | 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 | 236 | function resolveTargetRuleChains(ruleChainConnections) { | ... | ... |
... | ... | @@ -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 | 529 | inputNodeComponent: { |
514 | 530 | type: 'INPUT', |
515 | 531 | name: 'Input', |
... | ... | @@ -565,6 +581,13 @@ export default angular.module('thingsboard.types', []) |
565 | 581 | nodeClass: "tb-input-type", |
566 | 582 | icon: "input", |
567 | 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 | 593 | valueType: { | ... | ... |
... | ... | @@ -16,8 +16,8 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 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 | 23 | </div> | ... | ... |
... | ... | @@ -1157,6 +1157,11 @@ export default angular.module('thingsboard.locale', []) |
1157 | 1157 | "link-label-required": "Link label is required.", |
1158 | 1158 | "custom-link-label": "Custom link label", |
1159 | 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 | 1165 | "type-filter": "Filter", |
1161 | 1166 | "type-filter-details": "Filter incoming messages with configured conditions", |
1162 | 1167 | "type-enrichment": "Enrichment", |
... | ... | @@ -1171,6 +1176,8 @@ export default angular.module('thingsboard.locale', []) |
1171 | 1176 | "type-rule-chain-details": "Forwards incoming messages to specified Rule Chain", |
1172 | 1177 | "type-input": "Input", |
1173 | 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 | 1181 | "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.", |
1175 | 1182 | "ui-resources-load-error": "Failed to load configuration ui resources.", |
1176 | 1183 | "invalid-target-rulechain": "Unable to resolve target rule chain!", | ... | ... |
... | ... | @@ -31,7 +31,7 @@ |
31 | 31 | <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
32 | 32 | <md-dialog-content> |
33 | 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 | 35 | </div> |
36 | 36 | </md-dialog-content> |
37 | 37 | <md-dialog-actions layout="row"> | ... | ... |
... | ... | @@ -17,7 +17,46 @@ |
17 | 17 | --> |
18 | 18 | <md-content class="md-padding tb-link" layout="column"> |
19 | 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 | 60 | <label translate>rulenode.link-label</label> |
22 | 61 | <md-select ng-model="selectedLabel" ng-change="selectedLabelChanged()"> |
23 | 62 | <md-option ng-repeat="label in labels" ng-value="label"> |
... | ... | @@ -34,6 +73,6 @@ |
34 | 73 | <div ng-messages="theForm.customLinkLabel.$error"> |
35 | 74 | <div translate ng-message="required">rulenode.custom-link-label-required</div> |
36 | 75 | </div> |
37 | - </md-input-container> | |
76 | + </md-input-container--> | |
38 | 77 | </fieldset> |
39 | 78 | </md-content> | ... | ... |
... | ... | @@ -14,6 +14,8 @@ |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | 16 | |
17 | +import './link.scss'; | |
18 | + | |
17 | 19 | /* eslint-disable import/no-unresolved, import/default */ |
18 | 20 | |
19 | 21 | import linkFieldsetTemplate from './link-fieldset.tpl.html'; |
... | ... | @@ -22,13 +24,18 @@ import linkFieldsetTemplate from './link-fieldset.tpl.html'; |
22 | 24 | |
23 | 25 | /*@ngInject*/ |
24 | 26 | export default function LinkDirective($compile, $templateCache, $filter) { |
25 | - var linker = function (scope, element) { | |
27 | + var linker = function (scope, element, attrs, ngModelCtrl) { | |
26 | 28 | var template = $templateCache.get(linkFieldsetTemplate); |
27 | 29 | element.html(template); |
28 | 30 | |
29 | 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 | 39 | scope.selectedLabel = null; |
33 | 40 | if (scope.link && scope.labels) { |
34 | 41 | if (scope.link.label) { |
... | ... | @@ -53,19 +60,100 @@ export default function LinkDirective($compile, $templateCache, $filter) { |
53 | 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 | 146 | $compile(element.contents())(scope); |
59 | 147 | } |
60 | 148 | return { |
61 | 149 | restrict: "E", |
150 | + require: "^ngModel", | |
62 | 151 | link: linker, |
63 | 152 | scope: { |
64 | - link: '=', | |
65 | - labels: '=', | |
153 | + allowedLabels: '=', | |
154 | + allowCustom: '=', | |
66 | 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 | 669 | } |
670 | 670 | } else { |
671 | 671 | if (edge.label) { |
672 | + if (!edge.labels) { | |
673 | + edge.labels = edge.label.split(' / '); | |
674 | + } | |
672 | 675 | deferred.resolve(edge); |
673 | 676 | } else { |
674 | 677 | var labels = ruleChainService.getRuleNodeSupportedLinks(sourceNode.component); |
678 | + var allowCustomLabels = ruleChainService.ruleNodeAllowCustomLinks(sourceNode.component); | |
675 | 679 | vm.enableHotKeys = false; |
676 | - addRuleNodeLink(event, edge, labels).then( | |
680 | + addRuleNodeLink(event, edge, labels, allowCustomLabels).then( | |
677 | 681 | (link) => { |
678 | 682 | deferred.resolve(link); |
679 | 683 | vm.enableHotKeys = true; |
... | ... | @@ -713,6 +717,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time |
713 | 717 | vm.isEditingRuleNode = false; |
714 | 718 | vm.editingRuleNode = null; |
715 | 719 | vm.editingRuleNodeLinkLabels = ruleChainService.getRuleNodeSupportedLinks(sourceNode.component); |
720 | + vm.editingRuleNodeAllowCustomLabels = ruleChainService.ruleNodeAllowCustomLinks(sourceNode.component); | |
716 | 721 | vm.isEditingRuleNodeLink = true; |
717 | 722 | vm.editingRuleNodeLinkIndex = vm.ruleChainModel.edges.indexOf(edge); |
718 | 723 | vm.editingRuleNodeLink = angular.copy(edge); |
... | ... | @@ -744,7 +749,8 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time |
744 | 749 | isInputSource: isInputSource, |
745 | 750 | fromIndex: fromIndex, |
746 | 751 | toIndex: toIndex, |
747 | - label: edge.label | |
752 | + label: edge.label, | |
753 | + labels: edge.labels | |
748 | 754 | }; |
749 | 755 | connections.push(connection); |
750 | 756 | } |
... | ... | @@ -816,7 +822,8 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time |
816 | 822 | var edge = { |
817 | 823 | source: source, |
818 | 824 | destination: destination, |
819 | - label: connection.label | |
825 | + label: connection.label, | |
826 | + labels: connection.labels | |
820 | 827 | }; |
821 | 828 | vm.ruleChainModel.edges.push(edge); |
822 | 829 | vm.modelservice.edges.select(edge); |
... | ... | @@ -1024,6 +1031,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time |
1024 | 1031 | } |
1025 | 1032 | |
1026 | 1033 | if (vm.ruleChainMetaData.connections) { |
1034 | + var edgeMap = {}; | |
1027 | 1035 | for (i = 0; i < vm.ruleChainMetaData.connections.length; i++) { |
1028 | 1036 | var connection = vm.ruleChainMetaData.connections[i]; |
1029 | 1037 | var sourceNode = nodes[connection.fromIndex]; |
... | ... | @@ -1032,12 +1040,23 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time |
1032 | 1040 | var sourceConnectors = vm.modelservice.nodes.getConnectorsByType(sourceNode, flowchartConstants.rightConnectorType); |
1033 | 1041 | var destConnectors = vm.modelservice.nodes.getConnectorsByType(destNode, flowchartConstants.leftConnectorType); |
1034 | 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 | 1064 | |
1046 | 1065 | if (vm.ruleChainMetaData.ruleChainConnections) { |
1047 | 1066 | var ruleChainNodesMap = {}; |
1067 | + var ruleChainEdgeMap = {}; | |
1048 | 1068 | for (i = 0; i < vm.ruleChainMetaData.ruleChainConnections.length; i++) { |
1049 | 1069 | var ruleChainConnection = vm.ruleChainMetaData.ruleChainConnections[i]; |
1050 | 1070 | var ruleChain = ruleChainsMap[ruleChainConnection.targetRuleChainId.id]; |
... | ... | @@ -1081,12 +1101,23 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time |
1081 | 1101 | if (sourceNode) { |
1082 | 1102 | connectors = vm.modelservice.nodes.getConnectorsByType(sourceNode, flowchartConstants.rightConnectorType); |
1083 | 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 | 1230 | var ruleChainConnection = { |
1200 | 1231 | fromIndex: fromIndex, |
1201 | 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 | 1235 | if (!ruleChainConnection.additionalInfo) { |
1206 | 1236 | ruleChainConnection.additionalInfo = {}; |
... | ... | @@ -1208,15 +1238,22 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time |
1208 | 1238 | ruleChainConnection.additionalInfo.layoutX = Math.round(destNode.x); |
1209 | 1239 | ruleChainConnection.additionalInfo.layoutY = Math.round(destNode.y); |
1210 | 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 | 1246 | } else { |
1213 | 1247 | var toIndex = nodes.indexOf(destNode); |
1214 | 1248 | var nodeConnection = { |
1215 | 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 | 1322 | }); |
1286 | 1323 | } |
1287 | 1324 | |
1288 | - function addRuleNodeLink($event, link, labels) { | |
1325 | + function addRuleNodeLink($event, link, labels, allowCustomLabels) { | |
1289 | 1326 | return $mdDialog.show({ |
1290 | 1327 | controller: 'AddRuleNodeLinkController', |
1291 | 1328 | controllerAs: 'vm', |
1292 | 1329 | templateUrl: addRuleNodeLinkTemplate, |
1293 | 1330 | parent: angular.element($document[0].body), |
1294 | - locals: {link: link, labels: labels}, | |
1331 | + locals: {link: link, labels: labels, allowCustomLabels: allowCustomLabels}, | |
1295 | 1332 | fullscreen: true, |
1296 | 1333 | targetEvent: $event |
1297 | 1334 | }); |
... | ... | @@ -1335,13 +1372,14 @@ export function AddRuleNodeController($scope, $mdDialog, ruleNode, ruleChainId, |
1335 | 1372 | } |
1336 | 1373 | |
1337 | 1374 | /*@ngInject*/ |
1338 | -export function AddRuleNodeLinkController($scope, $mdDialog, link, labels, helpLinks) { | |
1375 | +export function AddRuleNodeLinkController($scope, $mdDialog, link, labels, allowCustomLabels, helpLinks) { | |
1339 | 1376 | |
1340 | 1377 | var vm = this; |
1341 | 1378 | |
1342 | 1379 | vm.helpLinks = helpLinks; |
1343 | 1380 | vm.link = link; |
1344 | 1381 | vm.labels = labels; |
1382 | + vm.allowCustomLabels = allowCustomLabels; | |
1345 | 1383 | |
1346 | 1384 | vm.add = add; |
1347 | 1385 | vm.cancel = cancel; | ... | ... |
... | ... | @@ -170,6 +170,9 @@ |
170 | 170 | &.tb-rule-chain-type { |
171 | 171 | background-color: #d6c4f1; |
172 | 172 | } |
173 | + &.tb-unknown-type { | |
174 | + background-color: #f16c29; | |
175 | + } | |
173 | 176 | } |
174 | 177 | |
175 | 178 | .tb-rule-node { |
... | ... | @@ -202,6 +205,7 @@ |
202 | 205 | background-color: #a3eaa9; |
203 | 206 | user-select: none; |
204 | 207 | } |
208 | + | |
205 | 209 | md-icon { |
206 | 210 | font-size: 20px; |
207 | 211 | width: 20px; | ... | ... |
... | ... | @@ -207,11 +207,11 @@ |
207 | 207 | </details-buttons> |
208 | 208 | <form name="vm.ruleNodeLinkForm" ng-if="vm.isEditingRuleNodeLink"> |
209 | 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 | 213 | is-edit="true" |
213 | - is-read-only="false" | |
214 | - the-form="vm.ruleNodeLinkForm"> | |
214 | + is-read-only="false"> | |
215 | 215 | </tb-rule-node-link> |
216 | 216 | </form> |
217 | 217 | </tb-details-sidenav> | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | @import "~compass-sass-mixins/lib/compass"; |
17 | 17 | @import "constants"; |
18 | 18 | @import "animations"; |
19 | +@import "mixins"; | |
19 | 20 | @import "fonts"; |
20 | 21 | |
21 | 22 | /*************** |
... | ... | @@ -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 | 448 | * Flow |
442 | 449 | ***********************/ | ... | ... |
... | ... | @@ -31,4 +31,29 @@ |
31 | 31 | &:-ms-input-placeholder { |
32 | 32 | @content; |
33 | 33 | } |
34 | -} | |
\ No newline at end of file | ||
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 | +} | ... | ... |