Commit 8e90c903bb2ce29efbf9d734402af7d6bf2ecfb4
1 parent
25af06f2
TB-64: Implement widget actions table.
Showing
29 changed files
with
977 additions
and
26 deletions
... | ... | @@ -40,7 +40,7 @@ export default angular.module('thingsboard.api.widget', ['oc.lazyLoad', thingsbo |
40 | 40 | .name; |
41 | 41 | |
42 | 42 | /*@ngInject*/ |
43 | -function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, types, utils) { | |
43 | +function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $translate, types, utils) { | |
44 | 44 | |
45 | 45 | $window.$ = $; |
46 | 46 | $window.jQuery = $; |
... | ... | @@ -548,13 +548,21 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, typ |
548 | 548 | ' }\n\n' + |
549 | 549 | |
550 | 550 | ' self.typeParameters = function() {\n\n' + |
551 | - { | |
552 | - useCustomDatasources: false, | |
553 | - maxDatasources: -1 //unlimited | |
554 | - maxDataKeys: -1 //unlimited | |
555 | - } | |
551 | + return { | |
552 | + useCustomDatasources: false, | |
553 | + maxDatasources: -1 //unlimited | |
554 | + maxDataKeys: -1 //unlimited | |
555 | + }; | |
556 | 556 | ' }\n\n' + |
557 | 557 | |
558 | + ' self.actionSources = function() {\n\n' + | |
559 | + return { | |
560 | + 'headerButton': { | |
561 | + name: 'Header button', | |
562 | + multiple: true | |
563 | + } | |
564 | + }; | |
565 | + }\n\n' + | |
558 | 566 | ' self.onResize = function() {\n\n' + |
559 | 567 | |
560 | 568 | ' }\n\n' + |
... | ... | @@ -611,6 +619,16 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, typ |
611 | 619 | if (angular.isUndefined(result.typeParameters.maxDataKeys)) { |
612 | 620 | result.typeParameters.maxDataKeys = -1; |
613 | 621 | } |
622 | + if (angular.isFunction(widgetTypeInstance.actionSources)) { | |
623 | + result.actionSources = widgetTypeInstance.actionSources(); | |
624 | + } else { | |
625 | + result.actionSources = {}; | |
626 | + } | |
627 | + for (var actionSourceId in types.widgetActionSources) { | |
628 | + result.actionSources[actionSourceId] = angular.copy(types.widgetActionSources[actionSourceId]); | |
629 | + result.actionSources[actionSourceId].name = $translate.instant(result.actionSources[actionSourceId].name); | |
630 | + } | |
631 | + | |
614 | 632 | return result; |
615 | 633 | } catch (e) { |
616 | 634 | utils.processWidgetException(e); |
... | ... | @@ -650,6 +668,7 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, typ |
650 | 668 | widgetInfo.typeDataKeySettingsSchema = widgetType.dataKeySettingsSchema; |
651 | 669 | } |
652 | 670 | widgetInfo.typeParameters = widgetType.typeParameters; |
671 | + widgetInfo.actionSources = widgetType.actionSources; | |
653 | 672 | putWidgetInfoToCache(widgetInfo, bundleAlias, widgetInfo.alias, isSystem); |
654 | 673 | putWidgetTypeFunctionToCache(widgetType.widgetTypeFunction, bundleAlias, widgetInfo.alias, isSystem); |
655 | 674 | deferred.resolve(widgetInfo); | ... | ... |
... | ... | @@ -389,6 +389,30 @@ export default angular.module('thingsboard.types', []) |
389 | 389 | } |
390 | 390 | } |
391 | 391 | }, |
392 | + widgetActionSources: { | |
393 | + 'headerButton': { | |
394 | + name: 'widget-action.header-button', | |
395 | + multiple: true | |
396 | + } | |
397 | + }, | |
398 | + widgetActionTypes: { | |
399 | + openDashboardState: { | |
400 | + name: 'widget-action.open-dashboard-state', | |
401 | + value: 'openDashboardState' | |
402 | + }, | |
403 | + updateDashboardState: { | |
404 | + name: 'widget-action.update-dashboard-state', | |
405 | + value: 'updateDashboardState' | |
406 | + }, | |
407 | + openDashboard: { | |
408 | + name: 'widget-action.open-dashboard', | |
409 | + value: 'openDashboard' | |
410 | + }, | |
411 | + custom: { | |
412 | + name: 'widget-action.custom', | |
413 | + value: 'custom' | |
414 | + } | |
415 | + }, | |
392 | 416 | systemBundleAlias: { |
393 | 417 | charts: "charts", |
394 | 418 | cards: "cards" | ... | ... |
... | ... | @@ -13,6 +13,13 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
17 | +/* eslint-disable import/no-unresolved, import/default */ | |
18 | + | |
19 | +import materialIconsCodepoints from 'raw-loader!material-design-icons/iconfont/codepoints'; | |
20 | + | |
21 | +/* eslint-enable import/no-unresolved, import/default */ | |
22 | + | |
16 | 23 | import tinycolor from "tinycolor2"; |
17 | 24 | import jsonSchemaDefaults from "json-schema-defaults"; |
18 | 25 | import thingsboardTypes from "./types.constant"; |
... | ... | @@ -24,11 +31,14 @@ export default angular.module('thingsboard.utils', [thingsboardTypes]) |
24 | 31 | const varsRegex = /\$\{([^\}]*)\}/g; |
25 | 32 | |
26 | 33 | /*@ngInject*/ |
27 | -function Utils($mdColorPalette, $rootScope, $window, $translate, types) { | |
34 | +function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, types) { | |
28 | 35 | |
29 | 36 | var predefinedFunctions = {}, |
30 | 37 | predefinedFunctionsList = [], |
31 | - materialColors = []; | |
38 | + materialColors = [], | |
39 | + materialIcons = []; | |
40 | + | |
41 | + var commonUsedMaterialIcons = [ 'more_horiz', 'close', 'play_arrow' ]; | |
32 | 42 | |
33 | 43 | predefinedFunctions['Sin'] = "return Math.round(1000*Math.sin(time/5000));"; |
34 | 44 | predefinedFunctions['Cos'] = "return Math.round(1000*Math.cos(time/5000));"; |
... | ... | @@ -122,6 +132,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, types) { |
122 | 132 | getDefaultDatasourceJson: getDefaultDatasourceJson, |
123 | 133 | getDefaultAlarmDataKeys: getDefaultAlarmDataKeys, |
124 | 134 | getMaterialColor: getMaterialColor, |
135 | + getMaterialIcons: getMaterialIcons, | |
136 | + getCommonMaterialIcons: getCommonMaterialIcons, | |
125 | 137 | getPredefinedFunctionBody: getPredefinedFunctionBody, |
126 | 138 | getPredefinedFunctionsList: getPredefinedFunctionsList, |
127 | 139 | genMaterialColor: genMaterialColor, |
... | ... | @@ -154,6 +166,31 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, types) { |
154 | 166 | return materialColors[colorIndex].value; |
155 | 167 | } |
156 | 168 | |
169 | + function getMaterialIcons() { | |
170 | + var deferred = $q.defer(); | |
171 | + if (materialIcons.length) { | |
172 | + deferred.resolve(materialIcons); | |
173 | + } else { | |
174 | + $timeout(function() { | |
175 | + var codepointsArray = materialIconsCodepoints.split("\n"); | |
176 | + codepointsArray.forEach(function (codepoint) { | |
177 | + if (codepoint && codepoint.length) { | |
178 | + var values = codepoint.split(' '); | |
179 | + if (values && values.length == 2) { | |
180 | + materialIcons.push(values[0]); | |
181 | + } | |
182 | + } | |
183 | + }); | |
184 | + deferred.resolve(materialIcons); | |
185 | + }); | |
186 | + } | |
187 | + return deferred.promise; | |
188 | + } | |
189 | + | |
190 | + function getCommonMaterialIcons() { | |
191 | + return commonUsedMaterialIcons; | |
192 | + } | |
193 | + | |
157 | 194 | function genMaterialColor(str) { |
158 | 195 | var hash = Math.abs(hashCode(str)); |
159 | 196 | return getMaterialColor(hash); | ... | ... |
... | ... | @@ -20,7 +20,7 @@ import 'javascript-detect-element-resize/detect-element-resize'; |
20 | 20 | import angularGridster from 'angular-gridster'; |
21 | 21 | import thingsboardTypes from '../common/types.constant'; |
22 | 22 | import thingsboardApiWidget from '../api/widget.service'; |
23 | -import thingsboardWidget from './widget.directive'; | |
23 | +import thingsboardWidget from './widget/widget.directive'; | |
24 | 24 | import thingsboardToast from '../services/toast'; |
25 | 25 | import thingsboardTimewindow from './timewindow.directive'; |
26 | 26 | import thingsboardEvents from './tb-event-directives'; | ... | ... |
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 | +export default angular.module('thingsboard.directives.finishRender', []) | |
18 | + .directive('tbOnFinishRender', OnFinishRender) | |
19 | + .name; | |
20 | + | |
21 | +/*@ngInject*/ | |
22 | +function OnFinishRender($timeout) { | |
23 | + return { | |
24 | + restrict: 'A', | |
25 | + link: function (scope, element, attr) { | |
26 | + if (scope.$last === true) { | |
27 | + $timeout(function () { | |
28 | + scope.$emit(attr.tbOnFinishRender); | |
29 | + }); | |
30 | + } | |
31 | + } | |
32 | + }; | |
33 | +} | ... | ... |
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 | +import MaterialIconsDialogController from './material-icons-dialog.controller'; | |
18 | + | |
19 | +/* eslint-disable import/no-unresolved, import/default */ | |
20 | + | |
21 | +import materialIconSelectTemplate from './material-icon-select.tpl.html'; | |
22 | +import materialIconsDialogTemplate from './material-icons-dialog.tpl.html'; | |
23 | + | |
24 | +/* eslint-enable import/no-unresolved, import/default */ | |
25 | + | |
26 | + | |
27 | +export default angular.module('thingsboard.directives.materialIconSelect', []) | |
28 | + .controller('MaterialIconsDialogController', MaterialIconsDialogController) | |
29 | + .directive('tbMaterialIconSelect', MaterialIconSelect) | |
30 | + .name; | |
31 | + | |
32 | +/*@ngInject*/ | |
33 | +function MaterialIconSelect($compile, $templateCache, $document, $mdDialog) { | |
34 | + | |
35 | + var linker = function (scope, element, attrs, ngModelCtrl) { | |
36 | + var template = $templateCache.get(materialIconSelectTemplate); | |
37 | + element.html(template); | |
38 | + | |
39 | + scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; | |
40 | + scope.icon = null; | |
41 | + | |
42 | + scope.updateView = function () { | |
43 | + ngModelCtrl.$setViewValue(scope.icon); | |
44 | + } | |
45 | + | |
46 | + ngModelCtrl.$render = function () { | |
47 | + if (ngModelCtrl.$viewValue) { | |
48 | + scope.icon = ngModelCtrl.$viewValue; | |
49 | + } | |
50 | + if (!scope.icon || !scope.icon.length) { | |
51 | + scope.icon = 'more_horiz'; | |
52 | + } | |
53 | + } | |
54 | + | |
55 | + scope.$watch('icon', function () { | |
56 | + scope.updateView(); | |
57 | + }); | |
58 | + | |
59 | + scope.openIconDialog = function($event) { | |
60 | + if ($event) { | |
61 | + $event.stopPropagation(); | |
62 | + } | |
63 | + $mdDialog.show({ | |
64 | + controller: 'MaterialIconsDialogController', | |
65 | + controllerAs: 'vm', | |
66 | + templateUrl: materialIconsDialogTemplate, | |
67 | + parent: angular.element($document[0].body), | |
68 | + locals: {icon: scope.icon}, | |
69 | + skipHide: true, | |
70 | + fullscreen: true, | |
71 | + targetEvent: $event | |
72 | + }).then(function (icon) { | |
73 | + scope.icon = icon; | |
74 | + }); | |
75 | + } | |
76 | + | |
77 | + $compile(element.contents())(scope); | |
78 | + } | |
79 | + | |
80 | + return { | |
81 | + restrict: "E", | |
82 | + require: "^ngModel", | |
83 | + link: linker, | |
84 | + scope: { | |
85 | + tbRequired: '=?', | |
86 | + } | |
87 | + }; | |
88 | +} | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2017 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<div layout="row"> | |
19 | + <md-icon class="material-icons" ng-click="openIconDialog($event)">{{icon}}</md-icon> | |
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> | |
25 | + </md-input-container> | |
26 | +</div> | |
\ No newline at end of file | ... | ... |
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 | +import './material-icons-dialog.scss'; | |
18 | + | |
19 | +/*@ngInject*/ | |
20 | +export default function MaterialIconsDialogController($scope, $mdDialog, $timeout, utils, icon) { | |
21 | + | |
22 | + var vm = this; | |
23 | + | |
24 | + vm.selectedIcon = icon; | |
25 | + | |
26 | + vm.showAll = false; | |
27 | + vm.loadingIcons = false; | |
28 | + | |
29 | + $scope.$watch('vm.showAll', function(showAll) { | |
30 | + if (showAll) { | |
31 | + vm.loadingIcons = true; | |
32 | + $timeout(function() { | |
33 | + utils.getMaterialIcons().then( | |
34 | + function success(icons) { | |
35 | + vm.icons = icons; | |
36 | + } | |
37 | + ); | |
38 | + }); | |
39 | + } else { | |
40 | + vm.icons = utils.getCommonMaterialIcons(); | |
41 | + } | |
42 | + }); | |
43 | + | |
44 | + $scope.$on('iconsLoadFinished', function() { | |
45 | + vm.loadingIcons = false; | |
46 | + }); | |
47 | + | |
48 | + vm.cancel = cancel; | |
49 | + vm.selectIcon = selectIcon; | |
50 | + | |
51 | + function cancel() { | |
52 | + $mdDialog.cancel(); | |
53 | + } | |
54 | + | |
55 | + function selectIcon($event, icon) { | |
56 | + vm.selectedIcon = icon; | |
57 | + $mdDialog.hide(vm.selectedIcon); | |
58 | + } | |
59 | +} | ... | ... |
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-icons-dialog { | |
18 | + button.md-icon-button.tb-select-icon-button { | |
19 | + border: solid 1px orange; | |
20 | + border-radius: 0%; | |
21 | + padding: 16px; | |
22 | + height: 56px; | |
23 | + width: 56px; | |
24 | + margin: 10px; | |
25 | + } | |
26 | + .tb-icons-load { | |
27 | + top: 64px; | |
28 | + background: rgba(255,255,255,0.75); | |
29 | + z-index: 3; | |
30 | + } | |
31 | +} | |
\ No newline at end of file | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2017 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<md-dialog class="tb-material-icons-dialog" aria-label="{{'icon.material-icons' | translate }}" style="min-width: 600px;"> | |
19 | + <form> | |
20 | + <md-toolbar> | |
21 | + <div class="md-toolbar-tools"> | |
22 | + <h2>{{ 'icon.select-icon' | translate }}</h2> | |
23 | + <span flex></span> | |
24 | + <section layout="row" layout-align="start center"> | |
25 | + <md-switch ng-model="vm.showAll" | |
26 | + aria-label="{{ 'icon.show-all' | translate }}"> | |
27 | + </md-switch> | |
28 | + <label translate>icon.show-all</label> | |
29 | + </section> | |
30 | + <md-button class="md-icon-button" ng-click="vm.cancel()"> | |
31 | + <ng-md-icon icon="close" aria-label="{{ 'action.close' | translate }}"></ng-md-icon> | |
32 | + </md-button> | |
33 | + </div> | |
34 | + </md-toolbar> | |
35 | + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> | |
36 | + <span style="min-height: 5px;" flex="" ng-show="!loading"></span> | |
37 | + <div class="tb-absolute-fill tb-icons-load" ng-show="vm.loadingIcons" layout="column" layout-align="center center"> | |
38 | + <md-progress-circular md-mode="indeterminate" ng-disabled="!vm.loadingIcons" class="md-accent" md-diameter="40"></md-progress-circular> | |
39 | + </div> | |
40 | + <md-dialog-content> | |
41 | + <div class="md-dialog-content"> | |
42 | + <md-content class="md-padding" layout="column"> | |
43 | + <fieldset ng-disabled="loading"> | |
44 | + <md-button ng-class="{'md-primary md-raised': icon == vm.selectedIcon}" class="tb-select-icon-button md-icon-button" | |
45 | + ng-repeat="icon in vm.icons" ng-click="vm.selectIcon($event, icon)" tb-on-finish-render="iconsLoadFinished"> | |
46 | + <md-icon class="material-icons">{{icon}}</md-icon> | |
47 | + <md-tooltip md-direction="bottom"> | |
48 | + {{ icon }} | |
49 | + </md-tooltip> | |
50 | + </md-button> | |
51 | + </fieldset> | |
52 | + </md-content> | |
53 | + </div> | |
54 | + </md-dialog-content> | |
55 | + <md-dialog-actions layout="row"> | |
56 | + <span flex></span> | |
57 | + <md-button ng-disabled="loading" ng-click="vm.cancel()"> | |
58 | + {{ 'action.cancel' | translate }} | |
59 | + </md-button> | |
60 | + </md-dialog-actions> | |
61 | + </form> | |
62 | +</md-dialog> | ... | ... |
1 | +/** | |
2 | + * Created by igor on 6/20/17. | |
3 | + */ | |
4 | +/* | |
5 | + * Copyright © 2016-2017 The Thingsboard Authors | |
6 | + * | |
7 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
8 | + * you may not use this file except in compliance with the License. | |
9 | + * You may obtain a copy of the License at | |
10 | + * | |
11 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
12 | + * | |
13 | + * Unless required by applicable law or agreed to in writing, software | |
14 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
15 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
16 | + * See the License for the specific language governing permissions and | |
17 | + * limitations under the License. | |
18 | + */ | |
19 | + | |
20 | +import './manage-widget-actions.scss'; | |
21 | + | |
22 | +import thingsboardMaterialIconSelect from '../../material-icon-select.directive'; | |
23 | + | |
24 | +import WidgetActionDialogController from './widget-action-dialog.controller'; | |
25 | + | |
26 | +/* eslint-disable import/no-unresolved, import/default */ | |
27 | + | |
28 | +import manageWidgetActionsTemplate from './manage-widget-actions.tpl.html'; | |
29 | +import widgetActionDialogTemplate from './widget-action-dialog.tpl.html'; | |
30 | + | |
31 | +/* eslint-enable import/no-unresolved, import/default */ | |
32 | + | |
33 | +export default angular.module('thingsboard.directives.widgetActions', [thingsboardMaterialIconSelect]) | |
34 | + .controller('WidgetActionDialogController', WidgetActionDialogController) | |
35 | + .directive('tbManageWidgetActions', ManageWidgetActions) | |
36 | + .name; | |
37 | + | |
38 | +/*@ngInject*/ | |
39 | +function ManageWidgetActions() { | |
40 | + return { | |
41 | + restrict: "E", | |
42 | + scope: true, | |
43 | + bindToController: { | |
44 | + actionSources: '=', | |
45 | + widgetActions: '=' | |
46 | + }, | |
47 | + controller: ManageWidgetActionsController, | |
48 | + controllerAs: 'vm', | |
49 | + templateUrl: manageWidgetActionsTemplate | |
50 | + }; | |
51 | +} | |
52 | + | |
53 | +/* eslint-disable angular/angularelement */ | |
54 | + | |
55 | + | |
56 | +/*@ngInject*/ | |
57 | +function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog, $q, $filter, | |
58 | + $translate, $timeout, types) { | |
59 | + | |
60 | + let vm = this; | |
61 | + | |
62 | + vm.allActions = []; | |
63 | + | |
64 | + vm.actions = []; | |
65 | + vm.actionsCount = 0; | |
66 | + | |
67 | + vm.query = { | |
68 | + order: 'actionSourceName', | |
69 | + limit: 10, | |
70 | + page: 1, | |
71 | + search: null | |
72 | + }; | |
73 | + | |
74 | + vm.enterFilterMode = enterFilterMode; | |
75 | + vm.exitFilterMode = exitFilterMode; | |
76 | + vm.onReorder = onReorder; | |
77 | + vm.onPaginate = onPaginate; | |
78 | + vm.addAction = addAction; | |
79 | + vm.editAction = editAction; | |
80 | + vm.deleteAction = deleteAction; | |
81 | + | |
82 | + $timeout(function(){ | |
83 | + $scope.manageWidgetActionsForm.querySearchInput.$pristine = false; | |
84 | + }); | |
85 | + | |
86 | + $scope.$watch('vm.widgetActions', function() { | |
87 | + if (vm.widgetActions) { | |
88 | + reloadActions(); | |
89 | + } | |
90 | + }); | |
91 | + | |
92 | + $scope.$watch("vm.query.search", function(newVal, prevVal) { | |
93 | + if (!angular.equals(newVal, prevVal) && vm.query.search != null) { | |
94 | + updateActions(); | |
95 | + } | |
96 | + }); | |
97 | + | |
98 | + function enterFilterMode () { | |
99 | + vm.query.search = ''; | |
100 | + } | |
101 | + | |
102 | + function exitFilterMode () { | |
103 | + vm.query.search = null; | |
104 | + updateActions(); | |
105 | + } | |
106 | + | |
107 | + function onReorder () { | |
108 | + updateActions(); | |
109 | + } | |
110 | + | |
111 | + function onPaginate () { | |
112 | + updateActions(); | |
113 | + } | |
114 | + | |
115 | + function addAction($event) { | |
116 | + if ($event) { | |
117 | + $event.stopPropagation(); | |
118 | + } | |
119 | + openWidgetActionDialog($event, null, true); | |
120 | + } | |
121 | + | |
122 | + function editAction ($event, action) { | |
123 | + if ($event) { | |
124 | + $event.stopPropagation(); | |
125 | + } | |
126 | + openWidgetActionDialog($event, action, false); | |
127 | + } | |
128 | + | |
129 | + function deleteAction($event, action) { | |
130 | + if ($event) { | |
131 | + $event.stopPropagation(); | |
132 | + } | |
133 | + if (action) { | |
134 | + var title = $translate.instant('widget-config.delete-action-title'); | |
135 | + var content = $translate.instant('widget-config.delete-action-text', {actionName: action.name}); | |
136 | + var confirm = $mdDialog.confirm() | |
137 | + .targetEvent($event) | |
138 | + .title(title) | |
139 | + .htmlContent(content) | |
140 | + .ariaLabel(title) | |
141 | + .cancel($translate.instant('action.no')) | |
142 | + .ok($translate.instant('action.yes')); | |
143 | + | |
144 | + confirm._options.skipHide = true; | |
145 | + confirm._options.fullscreen = true; | |
146 | + | |
147 | + $mdDialog.show(confirm).then(function () { | |
148 | + var index = getActionIndex(action.id, vm.allActions); | |
149 | + if (index > -1) { | |
150 | + vm.allActions.splice(index, 1); | |
151 | + } | |
152 | + var targetActions = vm.widgetActions[action.actionSourceId]; | |
153 | + index = getActionIndex(action.id, targetActions); | |
154 | + if (index > -1) { | |
155 | + targetActions.splice(index, 1); | |
156 | + } | |
157 | + $scope.manageWidgetActionsForm.$setDirty(); | |
158 | + updateActions(); | |
159 | + }); | |
160 | + } | |
161 | + } | |
162 | + | |
163 | + function openWidgetActionDialog($event, action, isAdd) { | |
164 | + var prevActionId = null; | |
165 | + if (!isAdd) { | |
166 | + prevActionId = action.id; | |
167 | + } | |
168 | + $mdDialog.show({ | |
169 | + controller: 'WidgetActionDialogController', | |
170 | + controllerAs: 'vm', | |
171 | + templateUrl: widgetActionDialogTemplate, | |
172 | + parent: angular.element($document[0].body), | |
173 | + locals: {isAdd: isAdd, actionSources: vm.actionSources, action: angular.copy(action)}, | |
174 | + skipHide: true, | |
175 | + fullscreen: true, | |
176 | + targetEvent: $event | |
177 | + }).then(function (action) { | |
178 | + saveAction(action, prevActionId); | |
179 | + updateActions(); | |
180 | + }); | |
181 | + } | |
182 | + | |
183 | + function getActionIndex(id, actions) { | |
184 | + var result = $filter('filter')(actions, {id: id}, true); | |
185 | + if (result && result.length) { | |
186 | + return actions.indexOf(result[0]); | |
187 | + } | |
188 | + return -1; | |
189 | + } | |
190 | + | |
191 | + function saveAction(action, prevActionId) { | |
192 | + action.actionSourceName = vm.actionSources[action.actionSourceId].name; | |
193 | + action.typeName = $translate.instant(types.widgetActionTypes[action.type].name); | |
194 | + var actionSourceId = action.actionSourceId; | |
195 | + var widgetAction = angular.copy(action); | |
196 | + delete widgetAction.actionSourceId; | |
197 | + delete widgetAction.actionSourceName; | |
198 | + delete widgetAction.typeName; | |
199 | + var targetActions = vm.widgetActions[actionSourceId]; | |
200 | + if (!targetActions) { | |
201 | + targetActions = []; | |
202 | + vm.widgetActions[actionSourceId] = targetActions; | |
203 | + } | |
204 | + if (prevActionId) { | |
205 | + var index = getActionIndex(prevActionId, vm.allActions); | |
206 | + if (index > -1) { | |
207 | + vm.allActions[index] = action; | |
208 | + } | |
209 | + index = getActionIndex(prevActionId, targetActions); | |
210 | + if (index > -1) { | |
211 | + targetActions[index] = widgetAction; | |
212 | + } | |
213 | + } else { | |
214 | + vm.allActions.push(action); | |
215 | + targetActions.push(widgetAction); | |
216 | + } | |
217 | + $scope.manageWidgetActionsForm.$setDirty(); | |
218 | + } | |
219 | + | |
220 | + function reloadActions() { | |
221 | + vm.allActions = []; | |
222 | + vm.actions = []; | |
223 | + vm.actionsCount = 0; | |
224 | + | |
225 | + for (var actionSourceId in vm.widgetActions) { | |
226 | + var actionSource = vm.actionSources[actionSourceId]; | |
227 | + var actionSourceActions = vm.widgetActions[actionSourceId]; | |
228 | + for (var i=0;i<actionSourceActions.length;i++) { | |
229 | + 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 | + }; | |
239 | + vm.allActions.push(action); | |
240 | + } | |
241 | + } | |
242 | + | |
243 | + updateActions (); | |
244 | + } | |
245 | + | |
246 | + function updateActions () { | |
247 | + var result = $filter('orderBy')(vm.allActions, vm.query.order); | |
248 | + if (vm.query.search != null) { | |
249 | + result = $filter('filter')(result, {$: vm.query.search}); | |
250 | + } | |
251 | + vm.actionsCount = result.length; | |
252 | + var startIndex = vm.query.limit * (vm.query.page - 1); | |
253 | + vm.actions = result.slice(startIndex, startIndex + vm.query.limit); | |
254 | + } | |
255 | +} | |
\ No newline at end of file | ... | ... |
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 | +.tb-manage-widget-actions { | |
17 | + table.md-table { | |
18 | + tbody { | |
19 | + tr { | |
20 | + td { | |
21 | + &.tb-action-cell { | |
22 | + overflow: hidden; | |
23 | + text-overflow: ellipsis; | |
24 | + white-space: nowrap; | |
25 | + min-width: 100px; | |
26 | + max-width: 100px; | |
27 | + width: 100px; | |
28 | + } | |
29 | + } | |
30 | + } | |
31 | + } | |
32 | + } | |
33 | +} | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2017 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<div ng-form="manageWidgetActionsForm" class="tb-manage-widget-actions md-whiteframe-z1" layout="column"> | |
19 | + <md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search === null"> | |
20 | + <div class="md-toolbar-tools"> | |
21 | + <span translate>widget-config.actions</span> | |
22 | + <span flex></span> | |
23 | + <md-button class="md-icon-button" ng-click="vm.addAction($event)"> | |
24 | + <md-icon>add</md-icon> | |
25 | + <md-tooltip md-direction="top"> | |
26 | + {{ 'widget-config.add-action' | translate }} | |
27 | + </md-tooltip> | |
28 | + </md-button> | |
29 | + <md-button class="md-icon-button" ng-click="vm.enterFilterMode()"> | |
30 | + <md-icon>search</md-icon> | |
31 | + <md-tooltip md-direction="top"> | |
32 | + {{ 'action.search' | translate }} | |
33 | + </md-tooltip> | |
34 | + </md-button> | |
35 | + </div> | |
36 | + </md-toolbar> | |
37 | + <md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search != null"> | |
38 | + <div class="md-toolbar-tools"> | |
39 | + <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}"> | |
40 | + <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> | |
41 | + <md-tooltip md-direction="top"> | |
42 | + {{ 'widget-config.search-actions' | translate }} | |
43 | + </md-tooltip> | |
44 | + </md-button> | |
45 | + <md-input-container flex> | |
46 | + <label> </label> | |
47 | + <input ng-model="vm.query.search" name="querySearchInput" placeholder="{{ 'widget-config.search-actions' | translate }}"/> | |
48 | + </md-input-container> | |
49 | + <md-button class="md-icon-button" aria-label="Close" ng-click="vm.exitFilterMode()"> | |
50 | + <md-icon aria-label="Close" class="material-icons">close</md-icon> | |
51 | + <md-tooltip md-direction="top"> | |
52 | + {{ 'action.close' | translate }} | |
53 | + </md-tooltip> | |
54 | + </md-button> | |
55 | + </div> | |
56 | + </md-toolbar> | |
57 | + <md-table-container> | |
58 | + <table md-table> | |
59 | + <thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder"> | |
60 | + <tr md-row> | |
61 | + <th md-column md-order-by="actionSourceName"><span translate>widget-config.action-source</span></th> | |
62 | + <th md-column md-order-by="name"><span translate>widget-config.action-name</span></th> | |
63 | + <th md-column md-order-by="icon"><span translate>widget-config.action-icon</span></th> | |
64 | + <th md-column md-order-by="typeName"><span translate>widget-config.action-type</span></th> | |
65 | + <th md-column><span> </span></th> | |
66 | + </tr> | |
67 | + </thead> | |
68 | + <tbody md-body> | |
69 | + <tr md-row ng-repeat="action in vm.actions"> | |
70 | + <td md-cell>{{action.actionSourceName}}</td> | |
71 | + <td md-cell>{{action.name}}</td> | |
72 | + <td md-cell> | |
73 | + <md-icon aria-label="{{ 'widget-config.action-icon' | translate }}" class="material-icons">{{action.icon}}</md-icon> | |
74 | + </td> | |
75 | + <td md-cell>{{action.typeName}}</td> | |
76 | + <td md-cell class="tb-action-cell"> | |
77 | + <md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}" | |
78 | + ng-click="vm.editAction($event, action)"> | |
79 | + <md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">edit</md-icon> | |
80 | + <md-tooltip md-direction="top"> | |
81 | + {{ 'widget-config.edit-action' | translate }} | |
82 | + </md-tooltip> | |
83 | + </md-button> | |
84 | + <md-button class="md-icon-button" aria-label="{{'action.delete' | translate}}" ng-click="vm.deleteAction($event, action)"> | |
85 | + <md-icon aria-label="Delete" class="material-icons">delete</md-icon> | |
86 | + <md-tooltip md-direction="top"> | |
87 | + {{ 'widget-config.delete-action' | translate }} | |
88 | + </md-tooltip> | |
89 | + </md-button> | |
90 | + </td> | |
91 | + </tr> | |
92 | + </tbody> | |
93 | + </table> | |
94 | + </md-table-container> | |
95 | + <md-table-pagination md-limit="vm.query.limit" md-limit-options="[10, 15, 20]" | |
96 | + md-page="vm.query.page" md-total="{{vm.actionsCount}}" | |
97 | + md-on-paginate="vm.onPaginate" md-page-select> | |
98 | + </md-table-pagination> | |
99 | +</div> | ... | ... |
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 | +/*@ngInject*/ | |
18 | +export default function WidgetActionDialogController($scope, $mdDialog, types, utils, isAdd, actionSources, action) { | |
19 | + | |
20 | + var vm = this; | |
21 | + | |
22 | + vm.types = types; | |
23 | + | |
24 | + vm.isAdd = isAdd; | |
25 | + vm.actionSources = actionSources; | |
26 | + | |
27 | + if (vm.isAdd) { | |
28 | + vm.action = { | |
29 | + id: utils.guid() | |
30 | + }; | |
31 | + } else { | |
32 | + vm.action = action; | |
33 | + } | |
34 | + | |
35 | + vm.cancel = cancel; | |
36 | + vm.save = save; | |
37 | + | |
38 | + function cancel() { | |
39 | + $mdDialog.cancel(); | |
40 | + } | |
41 | + | |
42 | + function save() { | |
43 | + $scope.theForm.$setPristine(); | |
44 | + $mdDialog.hide(vm.action); | |
45 | + } | |
46 | +} | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2017 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<md-dialog class="tb-widget-action-dialog" aria-label="{{'widget-config.action' | translate }}" style="min-width: 600px;"> | |
19 | + <form name="theForm" ng-submit="vm.save()"> | |
20 | + <md-toolbar> | |
21 | + <div class="md-toolbar-tools"> | |
22 | + <h2>{{ (vm.isAdd ? 'widget-config.add-action' : 'widget-config.edit-action') | translate }}</h2> | |
23 | + <span flex></span> | |
24 | + <md-button class="md-icon-button" ng-click="vm.cancel()"> | |
25 | + <ng-md-icon icon="close" aria-label="{{ 'action.close' | translate }}"></ng-md-icon> | |
26 | + </md-button> | |
27 | + </div> | |
28 | + </md-toolbar> | |
29 | + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> | |
30 | + <span style="min-height: 5px;" flex="" ng-show="!loading"></span> | |
31 | + <md-dialog-content> | |
32 | + <div class="md-dialog-content"> | |
33 | + <md-content class="md-padding" layout="column"> | |
34 | + <fieldset ng-disabled="loading"> | |
35 | + <md-input-container class="md-block"> | |
36 | + <label translate>widget-config.action-source</label> | |
37 | + <md-select name="actionSource" required aria-label="{{ 'widget-config.action-source' | translate }}" ng-model="vm.action.actionSourceId"> | |
38 | + <md-option ng-repeat="(actionSourceId, actionSource) in vm.actionSources" ng-value="actionSourceId"> | |
39 | + {{actionSource.name}} | |
40 | + </md-option> | |
41 | + </md-select> | |
42 | + <div ng-messages="theForm.actionSource.$error"> | |
43 | + <div ng-message="required" translate>widget-config.action-source-required</div> | |
44 | + </div> | |
45 | + </md-input-container> | |
46 | + <md-input-container class="md-block"> | |
47 | + <label translate>widget-config.action-name</label> | |
48 | + <input name="name" required ng-model="vm.action.name"> | |
49 | + <div ng-messages="theForm.name.$error"> | |
50 | + <div ng-message="required" translate>widget-config.action-name-required</div> | |
51 | + </div> | |
52 | + </md-input-container> | |
53 | + <tb-material-icon-select ng-model="vm.action.icon"> | |
54 | + </tb-material-icon-select> | |
55 | + <md-input-container class="md-block"> | |
56 | + <label translate>widget-config.action-type</label> | |
57 | + <md-select name="actionType" required aria-label="{{ 'widget-config.action-type' | translate }}" ng-model="vm.action.type"> | |
58 | + <md-option ng-repeat="actionType in vm.types.widgetActionTypes" ng-value="actionType.value"> | |
59 | + {{ actionType.name | translate }} | |
60 | + </md-option> | |
61 | + </md-select> | |
62 | + <div ng-messages="theForm.actionType.$error"> | |
63 | + <div ng-message="required" translate>widget-config.action-type-required</div> | |
64 | + </div> | |
65 | + </md-input-container> | |
66 | + </fieldset> | |
67 | + </md-content> | |
68 | + </div> | |
69 | + </md-dialog-content> | |
70 | + <md-dialog-actions layout="row"> | |
71 | + <span flex></span> | |
72 | + <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" | |
73 | + class="md-raised md-primary"> | |
74 | + {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} | |
75 | + </md-button> | |
76 | + <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;"> | |
77 | + {{ 'action.cancel' | translate }} | |
78 | + </md-button> | |
79 | + </md-dialog-actions> | |
80 | + </form> | |
81 | +</md-dialog> | ... | ... |
ui/src/app/components/widget/widget-config.directive.js
renamed from
ui/src/app/components/widget-config.directive.js
... | ... | @@ -14,13 +14,14 @@ |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | 16 | import jsonSchemaDefaults from 'json-schema-defaults'; |
17 | -import thingsboardTypes from '../common/types.constant'; | |
18 | -import thingsboardUtils from '../common/utils.service'; | |
19 | -import thingsboardEntityAliasSelect from './entity-alias-select.directive'; | |
20 | -import thingsboardDatasource from './datasource.directive'; | |
21 | -import thingsboardTimewindow from './timewindow.directive'; | |
22 | -import thingsboardLegendConfig from './legend-config.directive'; | |
23 | -import thingsboardJsonForm from "./json-form.directive"; | |
17 | +import thingsboardTypes from '../../common/types.constant'; | |
18 | +import thingsboardUtils from '../../common/utils.service'; | |
19 | +import thingsboardEntityAliasSelect from '../entity-alias-select.directive'; | |
20 | +import thingsboardDatasource from '../datasource.directive'; | |
21 | +import thingsboardTimewindow from '../timewindow.directive'; | |
22 | +import thingsboardLegendConfig from '../legend-config.directive'; | |
23 | +import thingsboardJsonForm from '../json-form.directive'; | |
24 | +import thingsboardManageWidgetActions from './action/manage-widget-actions.directive'; | |
24 | 25 | import 'angular-ui-ace'; |
25 | 26 | |
26 | 27 | /* eslint-disable import/no-unresolved, import/default */ |
... | ... | @@ -38,6 +39,7 @@ export default angular.module('thingsboard.directives.widgetConfig', [thingsboar |
38 | 39 | thingsboardDatasource, |
39 | 40 | thingsboardTimewindow, |
40 | 41 | thingsboardLegendConfig, |
42 | + thingsboardManageWidgetActions, | |
41 | 43 | 'ui.ace']) |
42 | 44 | .directive('tbWidgetConfig', WidgetConfig) |
43 | 45 | .name; |
... | ... | @@ -117,6 +119,10 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout |
117 | 119 | scope.showLegend = angular.isDefined(config.showLegend) ? |
118 | 120 | config.showLegend : scope.widgetType === types.widgetType.timeseries.value; |
119 | 121 | scope.legendConfig = config.legendConfig; |
122 | + scope.actions = config.actions; | |
123 | + if (!scope.actions) { | |
124 | + scope.actions = {}; | |
125 | + } | |
120 | 126 | if (scope.widgetType !== types.widgetType.rpc.value && |
121 | 127 | scope.widgetType !== types.widgetType.alarm.value && |
122 | 128 | scope.widgetType !== types.widgetType.static.value |
... | ... | @@ -324,6 +330,19 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout |
324 | 330 | } |
325 | 331 | }); |
326 | 332 | |
333 | + scope.$watch('actions', function () { | |
334 | + if (ngModelCtrl.$viewValue && ngModelCtrl.$viewValue.config) { | |
335 | + var value = ngModelCtrl.$viewValue; | |
336 | + var config = value.config; | |
337 | + config.actions = scope.actions; | |
338 | + ngModelCtrl.$setViewValue(value); | |
339 | + scope.updateValidity(); | |
340 | + /*if (scope.theForm) { | |
341 | + scope.theForm.$setDirty(); | |
342 | + }*/ | |
343 | + } | |
344 | + }, true); | |
345 | + | |
327 | 346 | scope.addDatasource = function () { |
328 | 347 | var newDatasource; |
329 | 348 | if (scope.functionsOnly) { |
... | ... | @@ -443,6 +462,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout |
443 | 462 | isDataEnabled: '=?', |
444 | 463 | widgetType: '=', |
445 | 464 | typeParameters: '=', |
465 | + actionSources: '=', | |
446 | 466 | widgetSettingsSchema: '=', |
447 | 467 | datakeySettingsSchema: '=', |
448 | 468 | aliasController: '=', | ... | ... |
ui/src/app/components/widget/widget-config.tpl.html
renamed from
ui/src/app/components/widget-config.tpl.html
... | ... | @@ -275,4 +275,10 @@ |
275 | 275 | </ng-form> |
276 | 276 | </md-content> |
277 | 277 | </md-tab> |
278 | + <md-tab label="{{ 'widget-config.actions' | translate }}"> | |
279 | + <md-content class="md-padding" layout="column"> | |
280 | + <tb-manage-widget-actions action-sources="actionSources" widget-actions="actions"> | |
281 | + </tb-manage-widget-actions> | |
282 | + </md-content> | |
283 | + </md-tab> | |
278 | 284 | </md-tabs> | ... | ... |
ui/src/app/components/widget/widget.controller.js
renamed from
ui/src/app/components/widget.controller.js
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | */ |
16 | 16 | import $ from 'jquery'; |
17 | 17 | import 'javascript-detect-element-resize/detect-element-resize'; |
18 | -import Subscription from '../api/subscription'; | |
18 | +import Subscription from '../../api/subscription'; | |
19 | 19 | |
20 | 20 | /* eslint-disable angular/angularelement */ |
21 | 21 | ... | ... |
ui/src/app/components/widget/widget.directive.js
renamed from
ui/src/app/components/widget.directive.js
... | ... | @@ -16,9 +16,9 @@ |
16 | 16 | |
17 | 17 | import './widget.scss'; |
18 | 18 | |
19 | -import thingsboardLegend from './legend.directive'; | |
20 | -import thingsboardTypes from '../common/types.constant'; | |
21 | -import thingsboardApiDatasource from '../api/datasource.service'; | |
19 | +import thingsboardLegend from '../legend.directive'; | |
20 | +import thingsboardTypes from '../../common/types.constant'; | |
21 | +import thingsboardApiDatasource from '../../api/datasource.service'; | |
22 | 22 | |
23 | 23 | import WidgetController from './widget.controller'; |
24 | 24 | ... | ... |
ui/src/app/components/widget/widget.scss
renamed from
ui/src/app/components/widget.scss
... | ... | @@ -34,6 +34,7 @@ |
34 | 34 | <fieldset ng-disabled="loading" style="position: relative; height: 600px;"> |
35 | 35 | <tb-widget-config widget-type="vm.widget.type" |
36 | 36 | type-parameters="vm.widgetInfo.typeParameters" |
37 | + action-sources="vm.widgetInfo.actionSources" | |
37 | 38 | force-expand-datasources="true" |
38 | 39 | ng-model="vm.widgetConfig" |
39 | 40 | widget-settings-schema="vm.settingsSchema" | ... | ... |
... | ... | @@ -41,6 +41,7 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid |
41 | 41 | var settingsSchema = widgetInfo.typeSettingsSchema || widgetInfo.settingsSchema; |
42 | 42 | var dataKeySettingsSchema = widgetInfo.typeDataKeySettingsSchema || widgetInfo.dataKeySettingsSchema; |
43 | 43 | scope.typeParameters = widgetInfo.typeParameters; |
44 | + scope.actionSources = widgetInfo.actionSources; | |
44 | 45 | scope.isDataEnabled = !widgetInfo.typeParameters.useCustomDatasources; |
45 | 46 | if (!settingsSchema || settingsSchema === '') { |
46 | 47 | scope.settingsSchema = {}; | ... | ... |
... | ... | @@ -18,6 +18,7 @@ |
18 | 18 | <fieldset ng-disabled="loading"> |
19 | 19 | <tb-widget-config widget-type="widget.type" |
20 | 20 | type-parameters="typeParameters" |
21 | + action-sources="actionSources" | |
21 | 22 | ng-model="widgetConfig" |
22 | 23 | is-data-enabled="isDataEnabled" |
23 | 24 | widget-settings-schema="settingsSchema" | ... | ... |
... | ... | @@ -23,7 +23,7 @@ import thingsboardApiUser from '../api/user.service'; |
23 | 23 | import thingsboardApiDashboard from '../api/dashboard.service'; |
24 | 24 | import thingsboardApiCustomer from '../api/customer.service'; |
25 | 25 | import thingsboardDetailsSidenav from '../components/details-sidenav.directive'; |
26 | -import thingsboardWidgetConfig from '../components/widget-config.directive'; | |
26 | +import thingsboardWidgetConfig from '../components/widget/widget-config.directive'; | |
27 | 27 | import thingsboardDashboardSelect from '../components/dashboard-select.directive'; |
28 | 28 | import thingsboardRelatedEntityAutocomplete from '../components/related-entity-autocomplete.directive'; |
29 | 29 | import thingsboardDashboard from '../components/dashboard.directive'; | ... | ... |
... | ... | @@ -59,10 +59,10 @@ |
59 | 59 | <span flex></span> |
60 | 60 | <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" |
61 | 61 | class="md-raised md-primary"> |
62 | - {{ vm.isAdd ? 'Add' : 'Save' }} | |
62 | + {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} | |
63 | 63 | </md-button> |
64 | 64 | <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;"> |
65 | - Cancel | |
65 | + {{ 'action.cancel' | translate }} | |
66 | 66 | </md-button> |
67 | 67 | </md-dialog-actions> |
68 | 68 | </form> | ... | ... |
... | ... | @@ -26,6 +26,7 @@ import thingsboardApiLogin from '../api/login.service'; |
26 | 26 | import thingsboardApiUser from '../api/user.service'; |
27 | 27 | |
28 | 28 | import thingsboardNoAnimate from '../components/no-animate.directive'; |
29 | +import thingsboardOnFinishRender from '../components/finish-render.directive'; | |
29 | 30 | import thingsboardSideMenu from '../components/side-menu.directive'; |
30 | 31 | import thingsboardDashboardAutocomplete from '../components/dashboard-autocomplete.directive'; |
31 | 32 | |
... | ... | @@ -81,6 +82,7 @@ export default angular.module('thingsboard.home', [ |
81 | 82 | thingsboardApiLogin, |
82 | 83 | thingsboardApiUser, |
83 | 84 | thingsboardNoAnimate, |
85 | + thingsboardOnFinishRender, | |
84 | 86 | thingsboardSideMenu, |
85 | 87 | thingsboardDashboardAutocomplete |
86 | 88 | ]) | ... | ... |
... | ... | @@ -1103,6 +1103,13 @@ export default angular.module('thingsboard.locale', []) |
1103 | 1103 | "undo": "Undo widget changes", |
1104 | 1104 | "export": "Export widget" |
1105 | 1105 | }, |
1106 | + "widget-action": { | |
1107 | + "header-button": "Header button", | |
1108 | + "open-dashboard-state": "Navigate to new dashboard state", | |
1109 | + "update-dashboard-state": "Update current dashboard state", | |
1110 | + "open-dashboard": "Navigate to other dashboard", | |
1111 | + "custom": "Custom action" | |
1112 | + }, | |
1106 | 1113 | "widgets-bundle": { |
1107 | 1114 | "current": "Current bundle", |
1108 | 1115 | "widgets-bundles": "Widgets Bundles", |
... | ... | @@ -1158,7 +1165,22 @@ export default angular.module('thingsboard.locale', []) |
1158 | 1165 | "remove-datasource": "Remove datasource", |
1159 | 1166 | "add-datasource": "Add datasource", |
1160 | 1167 | "target-device": "Target device", |
1161 | - "alarm-source": "Alarm source" | |
1168 | + "alarm-source": "Alarm source", | |
1169 | + "actions": "Actions", | |
1170 | + "action": "Action", | |
1171 | + "add-action": "Add action", | |
1172 | + "search-actions": "Search actions", | |
1173 | + "action-source": "Action source", | |
1174 | + "action-source-required": "Action source is required.", | |
1175 | + "action-name": "Name", | |
1176 | + "action-name-required": "Action name is required.", | |
1177 | + "action-icon": "Icon", | |
1178 | + "action-type": "Type", | |
1179 | + "action-type-required": "Action type is required.", | |
1180 | + "edit-action": "Edit action", | |
1181 | + "delete-action": "Delete action", | |
1182 | + "delete-action-title": "Delete widget action", | |
1183 | + "delete-action-text": "Are you sure you want delete widget action with name '{{actionName}}'?" | |
1162 | 1184 | }, |
1163 | 1185 | "widget-type": { |
1164 | 1186 | "import": "Import widget type", |
... | ... | @@ -1168,6 +1190,12 @@ export default angular.module('thingsboard.locale', []) |
1168 | 1190 | "widget-type-file": "Widget type file", |
1169 | 1191 | "invalid-widget-type-file-error": "Unable to import widget type: Invalid widget type data structure." |
1170 | 1192 | }, |
1193 | + "icon": { | |
1194 | + "icon": "Icon", | |
1195 | + "select-icon": "Select icon", | |
1196 | + "material-icons": "Material icons", | |
1197 | + "show-all": "Show all icons" | |
1198 | + }, | |
1171 | 1199 | "language": { |
1172 | 1200 | "language": "Language", |
1173 | 1201 | "en_US": "English", | ... | ... |