Showing
27 changed files
with
595 additions
and
95 deletions
... | ... | @@ -171,6 +171,48 @@ export default class Subscription { |
171 | 171 | return deferred.promise; |
172 | 172 | } |
173 | 173 | |
174 | + getFirstEntityInfo() { | |
175 | + var entityId; | |
176 | + var entityName; | |
177 | + if (this.type === this.ctx.types.widgetType.rpc.value) { | |
178 | + if (this.targetDeviceId) { | |
179 | + entityId = { | |
180 | + entityType: this.ctx.entityType.device, | |
181 | + id: this.targetDeviceId | |
182 | + } | |
183 | + entityName = this.targetDeviceName; | |
184 | + } | |
185 | + } else if (this.type == this.ctx.types.widgetType.alarm.value) { | |
186 | + if (this.alarmSource && this.alarmSource.entityType && this.alarmSource.entityId) { | |
187 | + entityId = { | |
188 | + entityType: this.alarmSource.entityType, | |
189 | + id: this.alarmSource.entityId | |
190 | + } | |
191 | + entityName = this.alarmSource.entityName; | |
192 | + } | |
193 | + } else { | |
194 | + for (var i=0;i<this.datasources.length;i++) { | |
195 | + var datasource = this.datasources[i]; | |
196 | + if (datasource && datasource.entityType && datasource.entityId) { | |
197 | + entityId = { | |
198 | + entityType: datasource.entityType, | |
199 | + id: datasource.entityId | |
200 | + } | |
201 | + entityName = datasource.entityName; | |
202 | + break; | |
203 | + } | |
204 | + } | |
205 | + } | |
206 | + if (entityId) { | |
207 | + return { | |
208 | + entityId: entityId, | |
209 | + entityName: entityName | |
210 | + }; | |
211 | + } else { | |
212 | + return null; | |
213 | + } | |
214 | + } | |
215 | + | |
174 | 216 | initAlarmSubscription() { |
175 | 217 | var deferred = this.ctx.$q.defer(); |
176 | 218 | if (!this.ctx.aliasController) { |
... | ... | @@ -342,6 +384,7 @@ export default class Subscription { |
342 | 384 | function success(aliasInfo) { |
343 | 385 | if (aliasInfo.currentEntity && aliasInfo.currentEntity.entityType == subscription.ctx.types.entityType.device) { |
344 | 386 | subscription.targetDeviceId = aliasInfo.currentEntity.id; |
387 | + subscription.targetDeviceName = aliasInfo.currentEntity.name; | |
345 | 388 | if (subscription.targetDeviceId) { |
346 | 389 | subscription.rpcEnabled = true; |
347 | 390 | } else { | ... | ... |
... | ... | @@ -626,7 +626,7 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr |
626 | 626 | } |
627 | 627 | for (var actionSourceId in types.widgetActionSources) { |
628 | 628 | result.actionSources[actionSourceId] = angular.copy(types.widgetActionSources[actionSourceId]); |
629 | - result.actionSources[actionSourceId].name = $translate.instant(result.actionSources[actionSourceId].name); | |
629 | + result.actionSources[actionSourceId].name = $translate.instant(result.actionSources[actionSourceId].name) + ''; | |
630 | 630 | } |
631 | 631 | |
632 | 632 | return result; | ... | ... |
... | ... | @@ -400,8 +400,9 @@ export default angular.module('thingsboard.types', []) |
400 | 400 | } |
401 | 401 | }, |
402 | 402 | widgetActionSources: { |
403 | - 'headerButton': { | |
403 | + headerButton: { | |
404 | 404 | name: 'widget-action.header-button', |
405 | + value: 'headerButton', | |
405 | 406 | multiple: true |
406 | 407 | } |
407 | 408 | }, | ... | ... |
... | ... | @@ -38,7 +38,11 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t |
38 | 38 | materialColors = [], |
39 | 39 | materialIcons = []; |
40 | 40 | |
41 | - var commonUsedMaterialIcons = [ 'more_horiz', 'close', 'play_arrow' ]; | |
41 | + var commonMaterialIcons = [ 'more_horiz', 'more_vert', 'open_in_new', 'visibility', 'play_arrow', 'arrow_back', 'arrow_downward', | |
42 | + 'arrow_forward', 'arrow_upwards', 'close', 'refresh', 'menu', 'show_chart', 'multiline_chart', 'pie_chart', 'insert_chart', 'people', | |
43 | + 'person', 'domain', 'devices_other', 'now_widgets', 'dashboards', 'map', 'pin_drop', 'my_location', 'extension', 'search', | |
44 | + 'settings', 'notifications', 'notifications_active', 'info', 'info_outline', 'warning', 'list', 'file_download', 'import_export', | |
45 | + 'share', 'add', 'edit', 'done' ]; | |
42 | 46 | |
43 | 47 | predefinedFunctions['Sin'] = "return Math.round(1000*Math.sin(time/5000));"; |
44 | 48 | predefinedFunctions['Cos'] = "return Math.round(1000*Math.cos(time/5000));"; |
... | ... | @@ -148,7 +152,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t |
148 | 152 | validateDatasources: validateDatasources, |
149 | 153 | createKey: createKey, |
150 | 154 | createLabelFromDatasource: createLabelFromDatasource, |
151 | - insertVariable: insertVariable | |
155 | + insertVariable: insertVariable, | |
156 | + customTranslation: customTranslation | |
152 | 157 | } |
153 | 158 | |
154 | 159 | return service; |
... | ... | @@ -188,7 +193,7 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t |
188 | 193 | } |
189 | 194 | |
190 | 195 | function getCommonMaterialIcons() { |
191 | - return commonUsedMaterialIcons; | |
196 | + return commonMaterialIcons; | |
192 | 197 | } |
193 | 198 | |
194 | 199 | function genMaterialColor(str) { |
... | ... | @@ -469,4 +474,16 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t |
469 | 474 | return result; |
470 | 475 | } |
471 | 476 | |
477 | + function customTranslation(translationValue, defaultValue) { | |
478 | + var result = ''; | |
479 | + var translationId = types.translate.customTranslationsPrefix + translationValue; | |
480 | + var translation = $translate.instant(translationId); | |
481 | + if (translation != translationId) { | |
482 | + result = translation + ''; | |
483 | + } else { | |
484 | + result = defaultValue; | |
485 | + } | |
486 | + return result; | |
487 | + } | |
488 | + | |
472 | 489 | } | ... | ... |
... | ... | @@ -87,23 +87,32 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u |
87 | 87 | dashboardService.getDashboardInfo(ngModelCtrl.$viewValue).then( |
88 | 88 | function success(dashboard) { |
89 | 89 | scope.dashboard = dashboard; |
90 | + startWatchers(); | |
90 | 91 | }, |
91 | 92 | function fail() { |
92 | 93 | scope.dashboard = null; |
94 | + scope.updateView(); | |
95 | + startWatchers(); | |
93 | 96 | } |
94 | 97 | ); |
95 | 98 | } else { |
96 | 99 | scope.dashboard = null; |
100 | + startWatchers(); | |
97 | 101 | } |
98 | 102 | } |
99 | 103 | |
100 | - scope.$watch('dashboard', function () { | |
101 | - scope.updateView(); | |
102 | - }); | |
103 | - | |
104 | - scope.$watch('disabled', function () { | |
105 | - scope.updateView(); | |
106 | - }); | |
104 | + function startWatchers() { | |
105 | + scope.$watch('dashboard', function (newVal, prevVal) { | |
106 | + if (!angular.equals(newVal, prevVal)) { | |
107 | + scope.updateView(); | |
108 | + } | |
109 | + }); | |
110 | + scope.$watch('disabled', function (newVal, prevVal) { | |
111 | + if (!angular.equals(newVal, prevVal)) { | |
112 | + scope.updateView(); | |
113 | + } | |
114 | + }); | |
115 | + } | |
107 | 116 | |
108 | 117 | if (scope.selectFirstDashboard) { |
109 | 118 | var pageLink = {limit: 1, textSearch: ''}; |
... | ... | @@ -111,6 +120,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u |
111 | 120 | var dashboards = result.data; |
112 | 121 | if (dashboards.length > 0) { |
113 | 122 | scope.dashboard = dashboards[0]; |
123 | + scope.updateView(); | |
114 | 124 | } |
115 | 125 | }, function fail() { |
116 | 126 | }); | ... | ... |
... | ... | @@ -187,6 +187,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
187 | 187 | vm.showWidgetActions = showWidgetActions; |
188 | 188 | vm.widgetTitleStyle = widgetTitleStyle; |
189 | 189 | vm.widgetTitle = widgetTitle; |
190 | + vm.customWidgetHeaderActions = customWidgetHeaderActions; | |
190 | 191 | vm.widgetActions = widgetActions; |
191 | 192 | vm.dropWidgetShadow = dropWidgetShadow; |
192 | 193 | vm.enableWidgetFullscreen = enableWidgetFullscreen; |
... | ... | @@ -875,6 +876,15 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
875 | 876 | } |
876 | 877 | } |
877 | 878 | |
879 | + function customWidgetHeaderActions(widget) { | |
880 | + var ctx = widgetContext(widget); | |
881 | + if (ctx && ctx.customHeaderActions && ctx.customHeaderActions.length) { | |
882 | + return ctx.customHeaderActions; | |
883 | + } else { | |
884 | + return []; | |
885 | + } | |
886 | + } | |
887 | + | |
878 | 888 | function widgetActions(widget) { |
879 | 889 | var ctx = widgetContext(widget); |
880 | 890 | if (ctx && ctx.widgetActions && ctx.widgetActions.length) { | ... | ... |
... | ... | @@ -52,6 +52,16 @@ |
52 | 52 | <tb-timewindow aggregation="{{vm.hasAggregation(widget)}}" ng-if="vm.hasTimewindow(widget)" ng-model="widget.config.timewindow"></tb-timewindow> |
53 | 53 | </div> |
54 | 54 | <div class="tb-widget-actions" layout="row" layout-align="start center" ng-show="vm.showWidgetActions(widget)" tb-mousedown="$event.stopPropagation()"> |
55 | + <md-button ng-repeat="action in vm.customWidgetHeaderActions(widget)" | |
56 | + aria-label="{{action.displayName}}" | |
57 | + ng-show="!vm.isEdit" | |
58 | + ng-click="action.onAction($event)" | |
59 | + class="md-icon-button"> | |
60 | + <md-tooltip md-direction="top"> | |
61 | + {{action.displayName}} | |
62 | + </md-tooltip> | |
63 | + <ng-md-icon size="20" icon="{{action.icon}}"></ng-md-icon> | |
64 | + </md-button> | |
55 | 65 | <md-button ng-repeat="action in vm.widgetActions(widget)" |
56 | 66 | aria-label="{{ action.name | translate }}" |
57 | 67 | ng-show="!vm.isEdit && action.show" | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2017 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-material-icon-select { | |
18 | + md-icon { | |
19 | + padding: 4px; | |
20 | + margin: 8px 4px 4px; | |
21 | + cursor: pointer; | |
22 | + border: solid 1px rgba(0,0,0,0.27); | |
23 | + } | |
24 | + md-input-container { | |
25 | + margin-bottom: 0px; | |
26 | + } | |
27 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -15,12 +15,10 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<div layout="row"> | |
18 | +<div class="tb-material-icon-select" layout="row"> | |
19 | 19 | <md-icon class="material-icons" ng-click="openIconDialog($event)">{{icon}}</md-icon> |
20 | 20 | <md-input-container flex> |
21 | - <md-input-container class="md-block"> | |
22 | - <label translate>icon.icon</label> | |
23 | - <input ng-click="openIconDialog($event)" ng-model="icon"> | |
24 | - </md-input-container> | |
21 | + <label translate>icon.icon</label> | |
22 | + <input ng-mousedown="openIconDialog($event)" ng-model="icon"> | |
25 | 23 | </md-input-container> |
26 | 24 | </div> |
\ No newline at end of file | ... | ... |
... | ... | @@ -42,7 +42,8 @@ function ManageWidgetActions() { |
42 | 42 | scope: true, |
43 | 43 | bindToController: { |
44 | 44 | actionSources: '=', |
45 | - widgetActions: '=' | |
45 | + widgetActions: '=', | |
46 | + fetchDashboardStates: '&', | |
46 | 47 | }, |
47 | 48 | controller: ManageWidgetActionsController, |
48 | 49 | controllerAs: 'vm', |
... | ... | @@ -55,7 +56,7 @@ function ManageWidgetActions() { |
55 | 56 | |
56 | 57 | /*@ngInject*/ |
57 | 58 | function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog, $q, $filter, |
58 | - $translate, $timeout, types) { | |
59 | + $translate, $timeout, utils, types) { | |
59 | 60 | |
60 | 61 | let vm = this; |
61 | 62 | |
... | ... | @@ -165,12 +166,30 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog, |
165 | 166 | if (!isAdd) { |
166 | 167 | prevActionId = action.id; |
167 | 168 | } |
169 | + var availableActionSources = {}; | |
170 | + for (var id in vm.actionSources) { | |
171 | + var actionSource = vm.actionSources[id]; | |
172 | + if (actionSource.multiple) { | |
173 | + availableActionSources[id] = actionSource; | |
174 | + } else { | |
175 | + if (!isAdd && action.actionSourceId == id) { | |
176 | + availableActionSources[id] = actionSource; | |
177 | + } else { | |
178 | + var result = $filter('filter')(vm.allActions, {actionSourceId: id}); | |
179 | + if (!result || !result.length) { | |
180 | + availableActionSources[id] = actionSource; | |
181 | + } | |
182 | + } | |
183 | + } | |
184 | + } | |
168 | 185 | $mdDialog.show({ |
169 | 186 | controller: 'WidgetActionDialogController', |
170 | 187 | controllerAs: 'vm', |
171 | 188 | templateUrl: widgetActionDialogTemplate, |
172 | 189 | parent: angular.element($document[0].body), |
173 | - locals: {isAdd: isAdd, actionSources: vm.actionSources, action: angular.copy(action)}, | |
190 | + locals: {isAdd: isAdd, fetchDashboardStates: vm.fetchDashboardStates, | |
191 | + actionSources: availableActionSources, widgetActions: vm.widgetActions, | |
192 | + action: angular.copy(action)}, | |
174 | 193 | skipHide: true, |
175 | 194 | fullscreen: true, |
176 | 195 | targetEvent: $event |
... | ... | @@ -189,7 +208,8 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog, |
189 | 208 | } |
190 | 209 | |
191 | 210 | function saveAction(action, prevActionId) { |
192 | - action.actionSourceName = vm.actionSources[action.actionSourceId].name; | |
211 | + var actionSourceName = vm.actionSources[action.actionSourceId].name; | |
212 | + action.actionSourceName = utils.customTranslation(actionSourceName, actionSourceName); | |
193 | 213 | action.typeName = $translate.instant(types.widgetActionTypes[action.type].name); |
194 | 214 | var actionSourceId = action.actionSourceId; |
195 | 215 | var widgetAction = angular.copy(action); |
... | ... | @@ -227,15 +247,10 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog, |
227 | 247 | var actionSourceActions = vm.widgetActions[actionSourceId]; |
228 | 248 | for (var i=0;i<actionSourceActions.length;i++) { |
229 | 249 | var actionSourceAction = actionSourceActions[i]; |
230 | - var action = { | |
231 | - id: actionSourceAction.id, | |
232 | - actionSourceId: actionSourceId, | |
233 | - actionSourceName: actionSource.name, | |
234 | - name: actionSourceAction.name, | |
235 | - icon: actionSourceAction.icon, | |
236 | - type: actionSourceAction.type, | |
237 | - typeName: $translate.instant(types.widgetActionTypes[actionSourceAction.type].name) | |
238 | - }; | |
250 | + var action = angular.copy(actionSourceAction); | |
251 | + action.actionSourceId = actionSourceId; | |
252 | + action.actionSourceName = utils.customTranslation(actionSource.name, actionSource.name); | |
253 | + action.typeName = $translate.instant(types.widgetActionTypes[actionSourceAction.type].name); | |
239 | 254 | vm.allActions.push(action); |
240 | 255 | } |
241 | 256 | } | ... | ... |
... | ... | @@ -15,14 +15,21 @@ |
15 | 15 | */ |
16 | 16 | |
17 | 17 | /*@ngInject*/ |
18 | -export default function WidgetActionDialogController($scope, $mdDialog, types, utils, isAdd, actionSources, action) { | |
18 | +export default function WidgetActionDialogController($scope, $mdDialog, $filter, $q, dashboardService, dashboardUtils, types, utils, | |
19 | + isAdd, fetchDashboardStates, actionSources, widgetActions, action) { | |
19 | 20 | |
20 | 21 | var vm = this; |
21 | 22 | |
22 | 23 | vm.types = types; |
23 | 24 | |
24 | 25 | vm.isAdd = isAdd; |
26 | + vm.fetchDashboardStates = fetchDashboardStates; | |
25 | 27 | vm.actionSources = actionSources; |
28 | + vm.widgetActions = widgetActions; | |
29 | + | |
30 | + vm.targetDashboardStateSearchText = ''; | |
31 | + | |
32 | + vm.selectedDashboardStateIds = []; | |
26 | 33 | |
27 | 34 | if (vm.isAdd) { |
28 | 35 | vm.action = { |
... | ... | @@ -32,15 +39,131 @@ export default function WidgetActionDialogController($scope, $mdDialog, types, u |
32 | 39 | vm.action = action; |
33 | 40 | } |
34 | 41 | |
42 | + vm.actionSourceName = actionSourceName; | |
43 | + | |
44 | + vm.targetDashboardStateSearchTextChanged = function() { | |
45 | + } | |
46 | + | |
47 | + vm.dashboardStateSearch = dashboardStateSearch; | |
35 | 48 | vm.cancel = cancel; |
36 | 49 | vm.save = save; |
37 | 50 | |
51 | + $scope.$watch("vm.action.name", function(newVal, prevVal) { | |
52 | + if (!angular.equals(newVal, prevVal) && vm.action.name != null) { | |
53 | + checkActionName(); | |
54 | + } | |
55 | + }); | |
56 | + | |
57 | + $scope.$watch("vm.action.actionSourceId", function(newVal, prevVal) { | |
58 | + if (!angular.equals(newVal, prevVal) && vm.action.actionSourceId != null) { | |
59 | + checkActionName(); | |
60 | + } | |
61 | + }); | |
62 | + | |
63 | + $scope.$watch("vm.action.targetDashboardId", function() { | |
64 | + vm.selectedDashboardStateIds = []; | |
65 | + if (vm.action.targetDashboardId) { | |
66 | + dashboardService.getDashboard(vm.action.targetDashboardId).then( | |
67 | + function success(dashboard) { | |
68 | + dashboard = dashboardUtils.validateAndUpdateDashboard(dashboard); | |
69 | + var states = dashboard.configuration.states; | |
70 | + vm.selectedDashboardStateIds = Object.keys(states); | |
71 | + } | |
72 | + ); | |
73 | + } | |
74 | + }); | |
75 | + | |
76 | + $scope.$watch('vm.action.type', function(newType) { | |
77 | + if (newType) { | |
78 | + switch (newType) { | |
79 | + case vm.types.widgetActionTypes.openDashboardState.value: | |
80 | + case vm.types.widgetActionTypes.updateDashboardState.value: | |
81 | + case vm.types.widgetActionTypes.openDashboard.value: | |
82 | + if (angular.isUndefined(vm.action.setEntityId)) { | |
83 | + vm.action.setEntityId = true; | |
84 | + } | |
85 | + break; | |
86 | + } | |
87 | + } | |
88 | + }); | |
89 | + | |
90 | + function checkActionName() { | |
91 | + var actionNameIsUnique = true; | |
92 | + if (vm.action.actionSourceId && vm.action.name) { | |
93 | + var sourceActions = vm.widgetActions[vm.action.actionSourceId]; | |
94 | + if (sourceActions) { | |
95 | + var result = $filter('filter')(sourceActions, {name: vm.action.name}, true); | |
96 | + if (result && result.length && result[0].id !== vm.action.id) { | |
97 | + actionNameIsUnique = false; | |
98 | + } | |
99 | + } | |
100 | + } | |
101 | + $scope.theForm.name.$setValidity('actionNameNotUnique', actionNameIsUnique); | |
102 | + } | |
103 | + | |
104 | + function actionSourceName (actionSource) { | |
105 | + if (actionSource) { | |
106 | + return utils.customTranslation(actionSource.name, actionSource.name); | |
107 | + } else { | |
108 | + return ''; | |
109 | + } | |
110 | + } | |
111 | + | |
112 | + function dashboardStateSearch (query) { | |
113 | + if (vm.action.type == vm.types.widgetActionTypes.openDashboard.value) { | |
114 | + var deferred = $q.defer(); | |
115 | + var result = query ? vm.selectedDashboardStateIds.filter( | |
116 | + createFilterForDashboardState(query)) : vm.selectedDashboardStateIds; | |
117 | + if (result && result.length) { | |
118 | + deferred.resolve(result); | |
119 | + } else { | |
120 | + deferred.resolve([query]); | |
121 | + } | |
122 | + return deferred.promise; | |
123 | + } else { | |
124 | + return vm.fetchDashboardStates({query: query}); | |
125 | + } | |
126 | + } | |
127 | + | |
128 | + function createFilterForDashboardState (query) { | |
129 | + var lowercaseQuery = angular.lowercase(query); | |
130 | + return function filterFn(stateId) { | |
131 | + return (angular.lowercase(stateId).indexOf(lowercaseQuery) === 0); | |
132 | + }; | |
133 | + } | |
134 | + | |
135 | + function cleanupAction(action) { | |
136 | + var result = {}; | |
137 | + result.id = action.id; | |
138 | + result.actionSourceId = action.actionSourceId; | |
139 | + result.name = action.name; | |
140 | + result.icon = action.icon; | |
141 | + result.type = action.type; | |
142 | + switch (action.type) { | |
143 | + case vm.types.widgetActionTypes.openDashboardState.value: | |
144 | + case vm.types.widgetActionTypes.updateDashboardState.value: | |
145 | + result.targetDashboardStateId = action.targetDashboardStateId; | |
146 | + result.openRightLayout = action.openRightLayout; | |
147 | + result.setEntityId = action.setEntityId; | |
148 | + break; | |
149 | + case vm.types.widgetActionTypes.openDashboard.value: | |
150 | + result.targetDashboardId = action.targetDashboardId; | |
151 | + result.targetDashboardStateId = action.targetDashboardStateId; | |
152 | + result.setEntityId = action.setEntityId; | |
153 | + break; | |
154 | + case vm.types.widgetActionTypes.custom.value: | |
155 | + result.customFunction = action.customFunction; | |
156 | + break; | |
157 | + } | |
158 | + return result; | |
159 | + } | |
160 | + | |
38 | 161 | function cancel() { |
39 | 162 | $mdDialog.cancel(); |
40 | 163 | } |
41 | 164 | |
42 | 165 | function save() { |
43 | 166 | $scope.theForm.$setPristine(); |
44 | - $mdDialog.hide(vm.action); | |
167 | + $mdDialog.hide(cleanupAction(vm.action)); | |
45 | 168 | } |
46 | 169 | } | ... | ... |
... | ... | @@ -31,12 +31,12 @@ |
31 | 31 | <md-dialog-content> |
32 | 32 | <div class="md-dialog-content"> |
33 | 33 | <md-content class="md-padding" layout="column"> |
34 | - <fieldset ng-disabled="loading"> | |
34 | + <fieldset ng-disabled="loading" layout="column"> | |
35 | 35 | <md-input-container class="md-block"> |
36 | 36 | <label translate>widget-config.action-source</label> |
37 | 37 | <md-select name="actionSource" required aria-label="{{ 'widget-config.action-source' | translate }}" ng-model="vm.action.actionSourceId"> |
38 | 38 | <md-option ng-repeat="(actionSourceId, actionSource) in vm.actionSources" ng-value="actionSourceId"> |
39 | - {{actionSource.name}} | |
39 | + {{vm.actionSourceName(actionSource)}} | |
40 | 40 | </md-option> |
41 | 41 | </md-select> |
42 | 42 | <div ng-messages="theForm.actionSource.$error"> |
... | ... | @@ -48,6 +48,7 @@ |
48 | 48 | <input name="name" required ng-model="vm.action.name"> |
49 | 49 | <div ng-messages="theForm.name.$error"> |
50 | 50 | <div ng-message="required" translate>widget-config.action-name-required</div> |
51 | + <div ng-message="actionNameNotUnique" translate>widget-config.action-name-not-unique</div> | |
51 | 52 | </div> |
52 | 53 | </md-input-container> |
53 | 54 | <tb-material-icon-select ng-model="vm.action.icon"> |
... | ... | @@ -63,6 +64,57 @@ |
63 | 64 | <div ng-message="required" translate>widget-config.action-type-required</div> |
64 | 65 | </div> |
65 | 66 | </md-input-container> |
67 | + <div layout="column" | |
68 | + style="padding-bottom: 20px;" | |
69 | + ng-if="vm.action.type == vm.types.widgetActionTypes.openDashboard.value"> | |
70 | + <div class="md-caption tb-required" | |
71 | + style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>widget-action.target-dashboard</div> | |
72 | + <tb-dashboard-autocomplete the-form="theForm" | |
73 | + tb-required="true" | |
74 | + ng-model="vm.action.targetDashboardId" | |
75 | + select-first-dashboard="false"> | |
76 | + </tb-dashboard-autocomplete> | |
77 | + </div> | |
78 | + <md-autocomplete ng-if="vm.action.type == vm.types.widgetActionTypes.openDashboardState.value || | |
79 | + vm.action.type == vm.types.widgetActionTypes.updateDashboardState.value || | |
80 | + vm.action.type == vm.types.widgetActionTypes.openDashboard.value" | |
81 | + ng-required="vm.action.type == vm.types.widgetActionTypes.openDashboardState.value" | |
82 | + md-no-cache="true" | |
83 | + md-input-name="targetDashboardState" | |
84 | + ng-model="vm.action.targetDashboardStateId" | |
85 | + md-selected-item="vm.action.targetDashboardStateId" | |
86 | + md-search-text="vm.targetDashboardStateSearchText" | |
87 | + md-search-text-change="vm.targetDashboardStateSearchTextChanged()" | |
88 | + md-items="item in vm.dashboardStateSearch(vm.targetDashboardStateSearchText)" | |
89 | + md-item-text="item" | |
90 | + md-min-length="0" | |
91 | + md-floating-label="{{ 'widget-action.target-dashboard-state' | translate }}" | |
92 | + md-select-on-match="true"> | |
93 | + <md-item-template> | |
94 | + <div> | |
95 | + <span md-highlight-text="vm.targetDashboardStateSearchText" md-highlight-flags="^i">{{item}}</span> | |
96 | + </div> | |
97 | + </md-item-template> | |
98 | + <div ng-messages="theForm.targetDashboardState.$error"> | |
99 | + <div translate ng-message="required">widget-action.target-dashboard-state-required</div> | |
100 | + </div> | |
101 | + </md-autocomplete> | |
102 | + <md-checkbox ng-if="vm.action.type == vm.types.widgetActionTypes.openDashboardState.value || | |
103 | + vm.action.type == vm.types.widgetActionTypes.updateDashboardState.value" | |
104 | + flex aria-label="{{ 'widget-action.open-right-layout' | translate }}" | |
105 | + ng-model="vm.action.openRightLayout">{{ 'widget-action.open-right-layout' | translate }} | |
106 | + </md-checkbox> | |
107 | + <md-checkbox ng-if="vm.action.type == vm.types.widgetActionTypes.openDashboardState.value || | |
108 | + vm.action.type == vm.types.widgetActionTypes.updateDashboardState.value || | |
109 | + vm.action.type == vm.types.widgetActionTypes.openDashboard.value" | |
110 | + flex aria-label="{{ 'widget-action.set-entity-from-widget' | translate }}" | |
111 | + ng-model="vm.action.setEntityId">{{ 'widget-action.set-entity-from-widget' | translate }} | |
112 | + </md-checkbox> | |
113 | + <tb-js-func ng-if="vm.action.type == vm.types.widgetActionTypes.custom.value" | |
114 | + ng-model="vm.action.customFunction" | |
115 | + function-args="{{ ['$event', 'widgetContext', 'entityId'] }}" | |
116 | + validation-args="{{ [] }}"> | |
117 | + </tb-js-func> | |
66 | 118 | </fieldset> |
67 | 119 | </md-content> |
68 | 120 | </div> | ... | ... |
... | ... | @@ -468,6 +468,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout |
468 | 468 | aliasController: '=', |
469 | 469 | functionsOnly: '=', |
470 | 470 | fetchEntityKeys: '&', |
471 | + fetchDashboardStates: '&', | |
471 | 472 | onCreateEntityAlias: '&', |
472 | 473 | theForm: '=' |
473 | 474 | }, | ... | ... |
... | ... | @@ -277,7 +277,10 @@ |
277 | 277 | </md-tab> |
278 | 278 | <md-tab label="{{ 'widget-config.actions' | translate }}"> |
279 | 279 | <md-content class="md-padding" layout="column"> |
280 | - <tb-manage-widget-actions action-sources="actionSources" widget-actions="actions"> | |
280 | + <tb-manage-widget-actions | |
281 | + action-sources="actionSources" | |
282 | + widget-actions="actions" | |
283 | + fetch-dashboard-states="fetchDashboardStates({query: query})"> | |
281 | 284 | </tb-manage-widget-actions> |
282 | 285 | </md-content> |
283 | 286 | </md-tab> | ... | ... |
... | ... | @@ -20,7 +20,7 @@ import Subscription from '../../api/subscription'; |
20 | 20 | /* eslint-disable angular/angularelement */ |
21 | 21 | |
22 | 22 | /*@ngInject*/ |
23 | -export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService, | |
23 | +export default function WidgetController($scope, $state, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService, | |
24 | 24 | datasourceService, alarmService, entityService, deviceService, visibleRect, isEdit, isMobile, stDiff, dashboardTimewindow, |
25 | 25 | dashboardTimewindowApi, widget, aliasController, stateController, widgetInfo, widgetType) { |
26 | 26 | |
... | ... | @@ -44,6 +44,20 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
44 | 44 | |
45 | 45 | var cafs = {}; |
46 | 46 | |
47 | + var actionDescriptorsBySourceId = {}; | |
48 | + if (widget.config.actions) { | |
49 | + for (var actionSourceId in widget.config.actions) { | |
50 | + var descriptors = widget.config.actions[actionSourceId]; | |
51 | + var actionDescriptors = []; | |
52 | + descriptors.forEach(function(descriptor) { | |
53 | + var actionDescriptor = angular.copy(descriptor); | |
54 | + actionDescriptor.displayName = utils.customTranslation(descriptor.name, descriptor.name); | |
55 | + actionDescriptors.push(actionDescriptor); | |
56 | + }); | |
57 | + actionDescriptorsBySourceId[actionSourceId] = actionDescriptors; | |
58 | + } | |
59 | + } | |
60 | + | |
47 | 61 | var widgetContext = { |
48 | 62 | inited: false, |
49 | 63 | $container: null, |
... | ... | @@ -103,9 +117,32 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
103 | 117 | utils: { |
104 | 118 | formatValue: formatValue |
105 | 119 | }, |
120 | + actionsApi: { | |
121 | + actionDescriptorsBySourceId: actionDescriptorsBySourceId, | |
122 | + getActionDescriptors: getActionDescriptors, | |
123 | + handleWidgetAction: handleWidgetAction | |
124 | + }, | |
106 | 125 | stateController: stateController |
107 | 126 | }; |
108 | 127 | |
128 | + widgetContext.customHeaderActions = []; | |
129 | + var headerActionsDescriptors = getActionDescriptors(types.widgetActionSources.headerButton.value); | |
130 | + for (var i=0;i<headerActionsDescriptors.length;i++) { | |
131 | + var descriptor = headerActionsDescriptors[i]; | |
132 | + var headerAction = {}; | |
133 | + headerAction.name = descriptor.name; | |
134 | + headerAction.displayName = descriptor.displayName; | |
135 | + headerAction.icon = descriptor.icon; | |
136 | + headerAction.descriptor = descriptor; | |
137 | + headerAction.onAction = function($event) { | |
138 | + var entityInfo = getFirstEntityInfo(); | |
139 | + var entityId = entityInfo ? entityInfo.entityId : null; | |
140 | + var entityName = entityInfo ? entityInfo.entityName : null; | |
141 | + handleWidgetAction($event, this.descriptor, entityId, entityName); | |
142 | + } | |
143 | + widgetContext.customHeaderActions.push(headerAction); | |
144 | + } | |
145 | + | |
109 | 146 | var subscriptionContext = { |
110 | 147 | $scope: $scope, |
111 | 148 | $q: $q, |
... | ... | @@ -376,6 +413,87 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
376 | 413 | return deferred.promise; |
377 | 414 | } |
378 | 415 | |
416 | + function getActionDescriptors(actionSourceId) { | |
417 | + var result = widgetContext.actionsApi.actionDescriptorsBySourceId[actionSourceId]; | |
418 | + if (!result) { | |
419 | + result = []; | |
420 | + } | |
421 | + return result; | |
422 | + } | |
423 | + | |
424 | + function handleWidgetAction($event, descriptor, entityId, entityName) { | |
425 | + var type = descriptor.type; | |
426 | + switch (type) { | |
427 | + case types.widgetActionTypes.openDashboardState.value: | |
428 | + case types.widgetActionTypes.updateDashboardState.value: | |
429 | + var targetDashboardStateId = descriptor.targetDashboardStateId; | |
430 | + var targetEntityId; | |
431 | + if (descriptor.setEntityId) { | |
432 | + targetEntityId = entityId; | |
433 | + } | |
434 | + var params = {}; | |
435 | + if (targetEntityId) { | |
436 | + params.entityId = targetEntityId; | |
437 | + if (entityName) { | |
438 | + params.entityName = entityName; | |
439 | + } | |
440 | + } | |
441 | + if (type == types.widgetActionTypes.openDashboardState.value) { | |
442 | + widgetContext.stateController.openState(targetDashboardStateId, params, descriptor.openRightLayout); | |
443 | + } else { | |
444 | + widgetContext.stateController.updateState(targetDashboardStateId, params, descriptor.openRightLayout); | |
445 | + } | |
446 | + break; | |
447 | + case types.widgetActionTypes.openDashboard.value: | |
448 | + var targetDashboardId = descriptor.targetDashboardId; | |
449 | + targetDashboardStateId = descriptor.targetDashboardStateId; | |
450 | + targetEntityId; | |
451 | + if (descriptor.setEntityId) { | |
452 | + targetEntityId = entityId; | |
453 | + } | |
454 | + var stateObject = {}; | |
455 | + stateObject.params = {}; | |
456 | + if (targetEntityId) { | |
457 | + stateObject.params.entityId = targetEntityId; | |
458 | + if (entityName) { | |
459 | + stateObject.params.entityName = entityName; | |
460 | + } | |
461 | + } | |
462 | + if (targetDashboardStateId) { | |
463 | + stateObject.id = targetDashboardStateId; | |
464 | + } | |
465 | + var stateParams = { | |
466 | + dashboardId: targetDashboardId, | |
467 | + state: angular.toJson([ stateObject ]) | |
468 | + } | |
469 | + $state.go('home.dashboards.dashboard', stateParams); | |
470 | + break; | |
471 | + case types.widgetActionTypes.custom.value: | |
472 | + var customFunction = descriptor.customFunction; | |
473 | + if (angular.isDefined(customFunction) && customFunction.length > 0) { | |
474 | + try { | |
475 | + var customActionFunction = new Function('$event', 'widgetContext', 'entityId', 'entityName', customFunction); | |
476 | + customActionFunction($event, widgetContext, entityId, entityName); | |
477 | + } catch (e) { | |
478 | + // | |
479 | + } | |
480 | + } | |
481 | + break; | |
482 | + } | |
483 | + } | |
484 | + | |
485 | + function getFirstEntityInfo() { | |
486 | + var entityInfo; | |
487 | + for (var id in widgetContext.subscriptions) { | |
488 | + var subscription = widgetContext.subscriptions[id]; | |
489 | + entityInfo = subscription.getFirstEntityInfo(); | |
490 | + if (entityInfo) { | |
491 | + break; | |
492 | + } | |
493 | + } | |
494 | + return entityInfo; | |
495 | + } | |
496 | + | |
379 | 497 | function configureWidgetElement() { |
380 | 498 | |
381 | 499 | $scope.displayLegend = angular.isDefined(widget.config.showLegend) ? | ... | ... |
... | ... | @@ -36,6 +36,7 @@ export default function AddWidgetController($scope, widgetService, entityService |
36 | 36 | vm.add = add; |
37 | 37 | vm.cancel = cancel; |
38 | 38 | vm.fetchEntityKeys = fetchEntityKeys; |
39 | + vm.fetchDashboardStates = fetchDashboardStates; | |
39 | 40 | vm.createEntityAlias = createEntityAlias; |
40 | 41 | |
41 | 42 | vm.widgetConfig = { |
... | ... | @@ -128,6 +129,26 @@ export default function AddWidgetController($scope, widgetService, entityService |
128 | 129 | return deferred.promise; |
129 | 130 | } |
130 | 131 | |
132 | + function fetchDashboardStates (query) { | |
133 | + var deferred = $q.defer(); | |
134 | + var stateIds = Object.keys(vm.dashboard.configuration.states); | |
135 | + var result = query ? stateIds.filter( | |
136 | + createFilterForDashboardState(query)) : stateIds; | |
137 | + if (result && result.length) { | |
138 | + deferred.resolve(result); | |
139 | + } else { | |
140 | + deferred.resolve([query]); | |
141 | + } | |
142 | + return deferred.promise; | |
143 | + } | |
144 | + | |
145 | + function createFilterForDashboardState (query) { | |
146 | + var lowercaseQuery = angular.lowercase(query); | |
147 | + return function filterFn(stateId) { | |
148 | + return (angular.lowercase(stateId).indexOf(lowercaseQuery) === 0); | |
149 | + }; | |
150 | + } | |
151 | + | |
131 | 152 | function createEntityAlias (event, alias, allowedEntityTypes) { |
132 | 153 | |
133 | 154 | var deferred = $q.defer(); | ... | ... |
... | ... | @@ -42,6 +42,7 @@ |
42 | 42 | alias-controller="vm.aliasController" |
43 | 43 | functions-only="vm.functionsOnly" |
44 | 44 | fetch-entity-keys="vm.fetchEntityKeys(entityAliasId, query, type)" |
45 | + fetch-dashboard-states="vm.fetchDashboardStates(query)" | |
45 | 46 | on-create-entity-alias="vm.createEntityAlias(event, alias, allowedEntityTypes)" |
46 | 47 | the-form="theForm"></tb-widget-config> |
47 | 48 | </fieldset> | ... | ... |
... | ... | @@ -94,6 +94,26 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid |
94 | 94 | return deferred.promise; |
95 | 95 | }; |
96 | 96 | |
97 | + scope.fetchDashboardStates = function(query) { | |
98 | + var deferred = $q.defer(); | |
99 | + var stateIds = Object.keys(scope.dashboard.configuration.states); | |
100 | + var result = query ? stateIds.filter( | |
101 | + createFilterForDashboardState(query)) : stateIds; | |
102 | + if (result && result.length) { | |
103 | + deferred.resolve(result); | |
104 | + } else { | |
105 | + deferred.resolve([query]); | |
106 | + } | |
107 | + return deferred.promise; | |
108 | + } | |
109 | + | |
110 | + function createFilterForDashboardState (query) { | |
111 | + var lowercaseQuery = angular.lowercase(query); | |
112 | + return function filterFn(stateId) { | |
113 | + return (angular.lowercase(stateId).indexOf(lowercaseQuery) === 0); | |
114 | + }; | |
115 | + } | |
116 | + | |
97 | 117 | scope.createEntityAlias = function (event, alias, allowedEntityTypes) { |
98 | 118 | |
99 | 119 | var deferred = $q.defer(); | ... | ... |
... | ... | @@ -26,6 +26,7 @@ |
26 | 26 | alias-controller="aliasController" |
27 | 27 | functions-only="widgetEditMode" |
28 | 28 | fetch-entity-keys="fetchEntityKeys(entityAliasId, query, type)" |
29 | + fetch-dashboard-states="fetchDashboardStates(query)" | |
29 | 30 | on-create-entity-alias="createEntityAlias(event, alias, allowedEntityTypes)" |
30 | 31 | the-form="theForm"></tb-widget-config> |
31 | 32 | </fieldset> | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | */ |
16 | 16 | |
17 | 17 | /*@ngInject*/ |
18 | -export default function DefaultStateController($scope, $location, $state, $stateParams, $translate, types, dashboardUtils) { | |
18 | +export default function DefaultStateController($scope, $location, $state, $stateParams, utils, types, dashboardUtils) { | |
19 | 19 | |
20 | 20 | var vm = this; |
21 | 21 | |
... | ... | @@ -50,6 +50,9 @@ export default function DefaultStateController($scope, $location, $state, $state |
50 | 50 | } |
51 | 51 | |
52 | 52 | function updateState(id, params, openRightLayout) { |
53 | + if (!id) { | |
54 | + id = getStateId(); | |
55 | + } | |
53 | 56 | if (vm.states && vm.states[id]) { |
54 | 57 | if (!params) { |
55 | 58 | params = {}; |
... | ... | @@ -110,15 +113,7 @@ export default function DefaultStateController($scope, $location, $state, $state |
110 | 113 | } |
111 | 114 | |
112 | 115 | function getStateName(id, state) { |
113 | - var result = ''; | |
114 | - var translationId = types.translate.customTranslationsPrefix + state.name; | |
115 | - var translation = $translate.instant(translationId); | |
116 | - if (translation != translationId) { | |
117 | - result = translation + ''; | |
118 | - } else { | |
119 | - result = id; | |
120 | - } | |
121 | - return result; | |
116 | + return utils.customTranslation(state.name, id); | |
122 | 117 | } |
123 | 118 | |
124 | 119 | function parseState(stateJson) { | ... | ... |
... | ... | @@ -55,6 +55,9 @@ export default function EntityStateController($scope, $location, $state, $stateP |
55 | 55 | } |
56 | 56 | |
57 | 57 | function updateState(id, params, openRightLayout) { |
58 | + if (!id) { | |
59 | + id = getStateId(); | |
60 | + } | |
58 | 61 | if (vm.states && vm.states[id]) { |
59 | 62 | resolveEntity(params).then( |
60 | 63 | function success(entityName) { |
... | ... | @@ -121,17 +124,10 @@ export default function EntityStateController($scope, $location, $state, $stateP |
121 | 124 | var result = ''; |
122 | 125 | if (vm.stateObject[index]) { |
123 | 126 | var stateName = vm.states[vm.stateObject[index].id].name; |
124 | - var translationId = types.translate.customTranslationsPrefix + stateName; | |
125 | - var translation = $translate.instant(translationId); | |
126 | - if (translation != translationId) { | |
127 | - stateName = translation + ''; | |
128 | - } | |
127 | + stateName = utils.customTranslation(stateName, stateName); | |
129 | 128 | var params = vm.stateObject[index].params; |
130 | - if (params && params.entityName) { | |
131 | - result = utils.insertVariable(stateName, 'entityName', params.entityName); | |
132 | - } else { | |
133 | - result = stateName; | |
134 | - } | |
129 | + var entityName = params && params.entityName ? params.entityName : ''; | |
130 | + result = utils.insertVariable(stateName, 'entityName', entityName); | |
135 | 131 | } |
136 | 132 | return result; |
137 | 133 | } | ... | ... |
... | ... | @@ -102,6 +102,7 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog, |
102 | 102 | if (vm.addToDashboardType === 0) { |
103 | 103 | dashboardService.getDashboard(vm.dashboardId).then( |
104 | 104 | function success(dashboard) { |
105 | + dashboard = dashboardUtils.validateAndUpdateDashboard(dashboard); | |
105 | 106 | selectTargetState($event, dashboard).then( |
106 | 107 | function(targetState) { |
107 | 108 | selectTargetLayout($event, dashboard, targetState).then( | ... | ... |
... | ... | @@ -1104,11 +1104,16 @@ export default angular.module('thingsboard.locale', []) |
1104 | 1104 | "export": "Export widget" |
1105 | 1105 | }, |
1106 | 1106 | "widget-action": { |
1107 | - "header-button": "Header button", | |
1107 | + "header-button": "Widget header button", | |
1108 | 1108 | "open-dashboard-state": "Navigate to new dashboard state", |
1109 | 1109 | "update-dashboard-state": "Update current dashboard state", |
1110 | 1110 | "open-dashboard": "Navigate to other dashboard", |
1111 | - "custom": "Custom action" | |
1111 | + "custom": "Custom action", | |
1112 | + "target-dashboard-state": "Target dashboard state", | |
1113 | + "target-dashboard-state-required": "Target dashboard state is required", | |
1114 | + "set-entity-from-widget": "Set entity from widget", | |
1115 | + "target-dashboard": "Target dashboard", | |
1116 | + "open-right-layout": "Open right dashboard layout (mobile view)" | |
1112 | 1117 | }, |
1113 | 1118 | "widgets-bundle": { |
1114 | 1119 | "current": "Current bundle", |
... | ... | @@ -1174,6 +1179,7 @@ export default angular.module('thingsboard.locale', []) |
1174 | 1179 | "action-source-required": "Action source is required.", |
1175 | 1180 | "action-name": "Name", |
1176 | 1181 | "action-name-required": "Action name is required.", |
1182 | + "action-name-not-unique": "Another action with the same name already exists.<br/>Action name should be unique within the same action source.", | |
1177 | 1183 | "action-icon": "Icon", |
1178 | 1184 | "action-type": "Type", |
1179 | 1185 | "action-type-required": "Action type is required.", |
... | ... | @@ -1205,6 +1211,10 @@ export default angular.module('thingsboard.locale', []) |
1205 | 1211 | "es_ES": "Spanish" |
1206 | 1212 | }, |
1207 | 1213 | "custom": { |
1214 | + "widget-action": { | |
1215 | + "action-cell-button": "Action cell button", | |
1216 | + "row-click": "On row click" | |
1217 | + } | |
1208 | 1218 | } |
1209 | 1219 | } |
1210 | 1220 | } | ... | ... |
... | ... | @@ -158,13 +158,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia |
158 | 158 | vm.ctx.widgetActions = [ vm.searchAction ]; |
159 | 159 | |
160 | 160 | if (vm.settings.alarmsTitle && vm.settings.alarmsTitle.length) { |
161 | - var translationId = types.translate.customTranslationsPrefix + vm.settings.alarmsTitle; | |
162 | - var translation = $translate.instant(translationId); | |
163 | - if (translation != translationId) { | |
164 | - vm.alarmsTitle = translation + ''; | |
165 | - } else { | |
166 | - vm.alarmsTitle = vm.settings.alarmsTitle; | |
167 | - } | |
161 | + vm.alarmsTitle = utils.customTranslation(vm.settings.alarmsTitle, vm.settings.alarmsTitle); | |
168 | 162 | } else { |
169 | 163 | vm.alarmsTitle = $translate.instant('alarm.alarms'); |
170 | 164 | } |
... | ... | @@ -226,6 +220,9 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia |
226 | 220 | 'table.md-table td.md-cell.md-checkbox-cell md-checkbox:not(.md-checked) .md-icon {\n'+ |
227 | 221 | 'border-color: ' + mdDarkSecondary + ';\n'+ |
228 | 222 | '}\n'+ |
223 | + 'table.md-table td.md-cell.tb-action-cell button.md-icon-button md-icon {\n'+ | |
224 | + 'color: ' + mdDarkSecondary + ';\n'+ | |
225 | + '}\n'+ | |
229 | 226 | 'table.md-table td.md-cell.md-placeholder {\n'+ |
230 | 227 | 'color: ' + mdDarkDisabled + ';\n'+ |
231 | 228 | '}\n'+ |
... | ... | @@ -539,13 +536,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia |
539 | 536 | for (var d = 0; d < vm.alarmSource.dataKeys.length; d++ ) { |
540 | 537 | var dataKey = vm.alarmSource.dataKeys[d]; |
541 | 538 | |
542 | - var translationId = types.translate.customTranslationsPrefix + dataKey.label; | |
543 | - var translation = $translate.instant(translationId); | |
544 | - if (translation != translationId) { | |
545 | - dataKey.title = translation + ''; | |
546 | - } else { | |
547 | - dataKey.title = dataKey.label; | |
548 | - } | |
539 | + dataKey.title = utils.customTranslation(dataKey.label, dataKey.label); | |
549 | 540 | |
550 | 541 | var keySettings = dataKey.settings; |
551 | 542 | ... | ... |
... | ... | @@ -65,8 +65,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra |
65 | 65 | vm.currentEntity = null; |
66 | 66 | |
67 | 67 | vm.displayEntityName = true; |
68 | + vm.entityNameColumnTitle = ''; | |
68 | 69 | vm.displayEntityType = true; |
69 | - vm.displayActions = false; //TODO: Widget actions | |
70 | + vm.actionCellDescriptors = []; | |
70 | 71 | vm.displayPagination = true; |
71 | 72 | vm.defaultPageSize = 10; |
72 | 73 | vm.defaultSortOrder = 'entityName'; |
... | ... | @@ -92,6 +93,7 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra |
92 | 93 | vm.onReorder = onReorder; |
93 | 94 | vm.onPaginate = onPaginate; |
94 | 95 | vm.onRowClick = onRowClick; |
96 | + vm.onActionButtonClick = onActionButtonClick; | |
95 | 97 | vm.isCurrent = isCurrent; |
96 | 98 | |
97 | 99 | vm.cellStyle = cellStyle; |
... | ... | @@ -141,14 +143,10 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra |
141 | 143 | |
142 | 144 | vm.ctx.widgetActions = [ vm.searchAction ]; |
143 | 145 | |
146 | + vm.actionCellDescriptors = vm.ctx.actionsApi.getActionDescriptors('actionCellButton'); | |
147 | + | |
144 | 148 | if (vm.settings.entitiesTitle && vm.settings.entitiesTitle.length) { |
145 | - var translationId = types.translate.customTranslationsPrefix + vm.settings.entitiesTitle; | |
146 | - var translation = $translate.instant(translationId); | |
147 | - if (translation != translationId) { | |
148 | - vm.entitiesTitle = translation + ''; | |
149 | - } else { | |
150 | - vm.entitiesTitle = vm.settings.entitiesTitle; | |
151 | - } | |
149 | + vm.entitiesTitle = utils.customTranslation(vm.settings.entitiesTitle, vm.settings.entitiesTitle); | |
152 | 150 | } else { |
153 | 151 | vm.entitiesTitle = $translate.instant('entity.entities'); |
154 | 152 | } |
... | ... | @@ -157,6 +155,13 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra |
157 | 155 | |
158 | 156 | vm.searchAction.show = angular.isDefined(vm.settings.enableSearch) ? vm.settings.enableSearch : true; |
159 | 157 | vm.displayEntityName = angular.isDefined(vm.settings.displayEntityName) ? vm.settings.displayEntityName : true; |
158 | + | |
159 | + if (vm.settings.entityNameColumnTitle && vm.settings.entityNameColumnTitle.length) { | |
160 | + vm.entityNameColumnTitle = utils.customTranslation(vm.settings.entityNameColumnTitle, vm.settings.entityNameColumnTitle); | |
161 | + } else { | |
162 | + vm.entityNameColumnTitle = $translate.instant('entity.entity-name'); | |
163 | + } | |
164 | + | |
160 | 165 | vm.displayEntityType = angular.isDefined(vm.settings.displayEntityType) ? vm.settings.displayEntityType : true; |
161 | 166 | vm.displayPagination = angular.isDefined(vm.settings.displayPagination) ? vm.settings.displayPagination : true; |
162 | 167 | |
... | ... | @@ -185,6 +190,8 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra |
185 | 190 | //var mdDarkIcon = mdDarkSecondary; |
186 | 191 | var mdDarkDivider = defaultColor.setAlpha(0.12).toRgbString(); |
187 | 192 | |
193 | + //md-icon.md-default-theme, md-icon { | |
194 | + | |
188 | 195 | var cssString = 'table.md-table th.md-column {\n'+ |
189 | 196 | 'color: ' + mdDarkSecondary + ';\n'+ |
190 | 197 | '}\n'+ |
... | ... | @@ -204,6 +211,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra |
204 | 211 | 'table.md-table td.md-cell.md-checkbox-cell md-checkbox:not(.md-checked) .md-icon {\n'+ |
205 | 212 | 'border-color: ' + mdDarkSecondary + ';\n'+ |
206 | 213 | '}\n'+ |
214 | + 'table.md-table td.md-cell.tb-action-cell button.md-icon-button md-icon {\n'+ | |
215 | + 'color: ' + mdDarkSecondary + ';\n'+ | |
216 | + '}\n'+ | |
207 | 217 | 'table.md-table td.md-cell.md-placeholder {\n'+ |
208 | 218 | 'color: ' + mdDarkDisabled + ';\n'+ |
209 | 219 | '}\n'+ |
... | ... | @@ -261,11 +271,37 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra |
261 | 271 | } |
262 | 272 | |
263 | 273 | function onRowClick($event, entity) { |
274 | + if ($event) { | |
275 | + $event.stopPropagation(); | |
276 | + } | |
264 | 277 | if (vm.currentEntity != entity) { |
265 | 278 | vm.currentEntity = entity; |
279 | + var descriptors = vm.ctx.actionsApi.getActionDescriptors('rowClick'); | |
280 | + if (descriptors.length) { | |
281 | + var entityId; | |
282 | + var entityName; | |
283 | + if (vm.currentEntity) { | |
284 | + entityId = vm.currentEntity.id; | |
285 | + entityName = vm.currentEntity.entityName; | |
286 | + } | |
287 | + vm.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName); | |
288 | + } | |
266 | 289 | } |
267 | 290 | } |
268 | 291 | |
292 | + function onActionButtonClick($event, entity, actionDescriptor) { | |
293 | + if ($event) { | |
294 | + $event.stopPropagation(); | |
295 | + } | |
296 | + var entityId; | |
297 | + var entityName; | |
298 | + if (entity) { | |
299 | + entityId = entity.id; | |
300 | + entityName = entity.entityName; | |
301 | + } | |
302 | + vm.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName); | |
303 | + } | |
304 | + | |
269 | 305 | function isCurrent(entity) { |
270 | 306 | return (vm.currentEntity && entity && vm.currentEntity.id && entity.id) && |
271 | 307 | (vm.currentEntity.id.id === entity.id.id); |
... | ... | @@ -393,13 +429,7 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra |
393 | 429 | } |
394 | 430 | vm.dataKeys.push(dataKey); |
395 | 431 | |
396 | - var translationId = types.translate.customTranslationsPrefix + dataKey.label; | |
397 | - var translation = $translate.instant(translationId); | |
398 | - if (translation != translationId) { | |
399 | - dataKey.title = translation + ''; | |
400 | - } else { | |
401 | - dataKey.title = dataKey.label; | |
402 | - } | |
432 | + dataKey.title = utils.customTranslation(dataKey.label, dataKey.label); | |
403 | 433 | |
404 | 434 | var keySettings = dataKey.settings; |
405 | 435 | ... | ... |
... | ... | @@ -41,10 +41,10 @@ |
41 | 41 | <table md-table> |
42 | 42 | <thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder"> |
43 | 43 | <tr md-row> |
44 | - <th md-column ng-if="vm.displayEntityName" md-order-by="entityName"><span translate>entity.entity-name</span></th> | |
44 | + <th md-column ng-if="vm.displayEntityName" md-order-by="entityName"><span>{{vm.entityNameColumnTitle}}</span></th> | |
45 | 45 | <th md-column ng-if="vm.displayEntityType" md-order-by="entityType"><span translate>entity.entity-type</span></th> |
46 | 46 | <th md-column md-order-by="{{ key.name }}" ng-repeat="key in vm.dataKeys"><span>{{ key.title }}</span></th> |
47 | - <th md-column ng-if="vm.displayActions"><span> </span></th> | |
47 | + <th md-column ng-if="vm.actionCellDescriptors.length"><span> </span></th> | |
48 | 48 | </tr> |
49 | 49 | </thead> |
50 | 50 | <tbody md-body> |
... | ... | @@ -57,14 +57,18 @@ |
57 | 57 | ng-style="vm.cellStyle(entity, key)" |
58 | 58 | ng-bind-html="vm.cellContent(entity, key)"> |
59 | 59 | </td> |
60 | - <td md-cell ng-if="vm.displayActions" class="tb-action-cell"> | |
61 | - <!--md-button class="md-icon-button" aria-label="{{ 'entity.details' | translate }}" | |
62 | - ng-click="vm.openEntityDetails($event, entity)"> | |
63 | - <md-icon aria-label="{{ 'entity.details' | translate }}" class="material-icons">more_horiz</md-icon> | |
60 | + <td md-cell ng-if="vm.actionCellDescriptors.length" class="tb-action-cell" | |
61 | + ng-style="{minWidth: vm.actionCellDescriptors.length*36+'px', | |
62 | + maxWidth: vm.actionCellDescriptors.length*36+'px', | |
63 | + width: vm.actionCellDescriptors.length*36+'px'}"> | |
64 | + <md-button class="md-icon-button" ng-repeat="actionDescriptor in vm.actionCellDescriptors" | |
65 | + aria-label="{{ actionDescriptor.displayName }}" | |
66 | + ng-click="vm.onActionButtonClick($event, entity, actionDescriptor)"> | |
67 | + <md-icon aria-label="{{ actionDescriptor.displayName }}" class="material-icons">{{actionDescriptor.icon}}</md-icon> | |
64 | 68 | <md-tooltip md-direction="top"> |
65 | - {{ 'entity.details' | translate }} | |
69 | + {{ actionDescriptor.displayName }} | |
66 | 70 | </md-tooltip> |
67 | - </md-button--> | |
71 | + </md-button> | |
68 | 72 | </td> |
69 | 73 | </tr> |
70 | 74 | </tbody> | ... | ... |