thingsboard-memoizeit

Details

diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index 6a4cbcc..ad09c82 100644
--- a/ui/src/app/common/types.constant.js
+++ b/ui/src/app/common/types.constant.js
@@ -395,7 +395,7 @@ export default angular.module('thingsboard.types', [])
             },
             translate: {
                 dashboardStatePrefix: "dashboardState.state.",
-                keyLabelPrefix: "key.label."
+                customTranslationsPrefix: "custom."
             }
         }
     ).name;
diff --git a/ui/src/app/components/dashboard.directive.js b/ui/src/app/components/dashboard.directive.js
index c6b4349..f21df59 100644
--- a/ui/src/app/components/dashboard.directive.js
+++ b/ui/src/app/components/dashboard.directive.js
@@ -177,7 +177,10 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
     vm.widgetBackgroundColor = widgetBackgroundColor;
     vm.widgetPadding = widgetPadding;
     vm.showWidgetTitle = showWidgetTitle;
+    vm.showWidgetTitlePanel = showWidgetTitlePanel;
     vm.widgetTitleStyle = widgetTitleStyle;
+    vm.widgetTitle = widgetTitle;
+    vm.widgetActions = widgetActions;
     vm.dropWidgetShadow = dropWidgetShadow;
     vm.enableWidgetFullscreen = enableWidgetFullscreen;
     vm.hasTimewindow = hasTimewindow;
@@ -747,6 +750,15 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
         }
     }
 
+    function showWidgetTitlePanel(widget) {
+        var ctx = widgetContext(widget);
+        if (ctx && ctx.hideTitlePanel) {
+            return false;
+        } else {
+            return showWidgetTitle(widget) || hasTimewindow(widget);
+        }
+    }
+
     function widgetTitleStyle(widget) {
         if (angular.isDefined(widget.config.titleStyle)) {
             return widget.config.titleStyle;
@@ -755,6 +767,33 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
         }
     }
 
+    function widgetTitle(widget) {
+        var ctx = widgetContext(widget);
+        if (ctx && ctx.widgetTitle
+            && ctx.widgetTitle.length) {
+            return ctx.widgetTitle;
+        } else {
+            return widget.config.title;
+        }
+    }
+
+    function widgetActions(widget) {
+        var ctx = widgetContext(widget);
+        if (ctx && ctx.widgetActions && ctx.widgetActions.length) {
+            return ctx.widgetActions;
+        } else {
+            return [];
+        }
+    }
+
+    function widgetContext(widget) {
+        var context;
+        if (widget.$ctx) {
+            context = widget.$ctx();
+        }
+        return context;
+    }
+
     function dropWidgetShadow(widget) {
         if (angular.isDefined(widget.config.dropShadow)) {
             return widget.config.dropShadow;
diff --git a/ui/src/app/components/dashboard.tpl.html b/ui/src/app/components/dashboard.tpl.html
index 08bdd42..da069b0 100644
--- a/ui/src/app/components/dashboard.tpl.html
+++ b/ui/src/app/components/dashboard.tpl.html
@@ -37,7 +37,8 @@
 								 		class="tb-widget"
 										ng-class="{'tb-highlighted': vm.isHighlighted(widget),
 										           'tb-not-highlighted': vm.isNotHighlighted(widget),
-										           'md-whiteframe-4dp': vm.dropWidgetShadow(widget)}"
+										           'md-whiteframe-4dp': vm.dropWidgetShadow(widget),
+										           'tb-has-timewindow': vm.hasTimewindow(widget)}"
 										tb-mousedown="vm.widgetMouseDown($event, widget)"
 										ng-click="vm.widgetClicked($event, widget)"
 										tb-contextmenu="vm.openWidgetContextMenu($event, widget, $mdOpenMousepointMenu)"
@@ -45,11 +46,21 @@
             									   color: vm.widgetColor(widget),
             									   backgroundColor: vm.widgetBackgroundColor(widget),
             									   padding: vm.widgetPadding(widget)}">
-								<div class="tb-widget-title" layout="column" layout-align="center start" ng-show="vm.showWidgetTitle(widget) || vm.hasTimewindow(widget)">
-									<span ng-show="vm.showWidgetTitle(widget)" ng-style="vm.widgetTitleStyle(widget)" class="md-subhead">{{widget.config.title}}</span>
+								<div class="tb-widget-title" layout="column" layout-align="center start" ng-show="vm.showWidgetTitlePanel(widget)">
+									<span ng-show="vm.showWidgetTitle(widget)" ng-style="vm.widgetTitleStyle(widget)" class="md-subhead">{{vm.widgetTitle(widget)}}</span>
 									<tb-timewindow aggregation="{{vm.hasAggregation(widget)}}" ng-if="vm.hasTimewindow(widget)" ng-model="widget.config.timewindow"></tb-timewindow>
 								</div>
-								<div class="tb-widget-actions" layout="row" layout-align="start center" tb-mousedown="$event.stopPropagation()">
+								<div class="tb-widget-actions" layout="row" layout-align="start center" ng-show="vm.showWidgetTitlePanel(widget)" tb-mousedown="$event.stopPropagation()">
+									<md-button ng-repeat="action in vm.widgetActions(widget)"
+											   aria-label="{{ action.name | translate }}"
+											   ng-show="!vm.isEdit && action.show"
+											   ng-click="action.onAction($event)"
+											   class="md-icon-button">
+											   <md-tooltip md-direction="top">
+												   {{ action.name | translate }}
+											   </md-tooltip>
+											   <ng-md-icon size="20" icon="{{action.icon}}"></ng-md-icon>
+									</md-button>
 									<md-button id="expand-button"
 											   ng-show="!vm.isEdit && vm.enableWidgetFullscreen(widget)"
 											   aria-label="{{ 'fullscreen.fullscreen' | translate }}"
diff --git a/ui/src/app/components/widget.controller.js b/ui/src/app/components/widget.controller.js
index 85f8e78..63ad0ed 100644
--- a/ui/src/app/components/widget.controller.js
+++ b/ui/src/app/components/widget.controller.js
@@ -48,6 +48,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
         $containerParent: null,
         width: 0,
         height: 0,
+        hideTitlePanel: false,
         isEdit: isEdit,
         isMobile: false,
         widgetConfig: widget.config,
@@ -121,6 +122,10 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
         aliasController: aliasController
     };
 
+    widget.$ctx = function() {
+        return widgetContext;
+    }
+
     var widgetTypeInstance;
 
     vm.useCustomDatasources = false;
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index ee0f0c2..f35434c 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -1167,6 +1167,11 @@ export default angular.module('thingsboard.locale', [])
                     "zh_CN": "Chinese",
                     "ru_RU": "Russian",
                     "es_ES": "Spanish"
+                },
+                "custom": {
+                    "alarms": {
+                        "title": "Super ${entityName}"
+                    }
                 }
             }
         }
diff --git a/ui/src/app/locale/translate-handler.js b/ui/src/app/locale/translate-handler.js
index a0723cf..20af258 100644
--- a/ui/src/app/locale/translate-handler.js
+++ b/ui/src/app/locale/translate-handler.js
@@ -19,7 +19,7 @@ export default function ThingsboardMissingTranslateHandler($log, types) {
 
     return function (translationId) {
         if (translationId && !translationId.startsWith(types.translate.dashboardStatePrefix) &&
-                             !translationId.startsWith(types.translate.keyLabelPrefix)) {
+                             !translationId.startsWith(types.translate.customTranslationsPrefix)) {
             $log.warn('Translation for ' + translationId + ' doesn\'t exist');
         }
     };
diff --git a/ui/src/app/widget/lib/alarms-table-widget.js b/ui/src/app/widget/lib/alarms-table-widget.js
index 9a06100..ebe6b03 100644
--- a/ui/src/app/widget/lib/alarms-table-widget.js
+++ b/ui/src/app/widget/lib/alarms-table-widget.js
@@ -37,8 +37,7 @@ function AlarmsTableWidget() {
         scope: true,
         bindToController: {
             tableId: '=',
-            config: '=',
-            subscription: '='
+            ctx: '='
         },
         controller: AlarmsTableWidgetController,
         controllerAs: 'vm',
@@ -66,9 +65,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
 
     vm.currentAlarm = null;
 
-    vm.alarmsTitle = $translate.instant('alarm.alarms');
     vm.enableSelection = true;
-    vm.enableSearch = true;
     vm.displayDetails = true;
     vm.allowAcknowledgment = true;
     vm.allowClear = true;
@@ -83,6 +80,15 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
         search: null
     };
 
+    vm.searchAction = {
+        name: 'action.search',
+        show: true,
+        onAction: function() {
+            vm.enterFilterMode();
+        },
+        icon: 'search'
+    };
+
     vm.enterFilterMode = enterFilterMode;
     vm.exitFilterMode = exitFilterMode;
     vm.onReorder = onReorder;
@@ -96,11 +102,14 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
     vm.cellStyle = cellStyle;
     vm.cellContent = cellContent;
 
-    $scope.$watch('vm.config', function() {
-        if (vm.config) {
-            vm.settings = vm.config.settings;
-            vm.widgetConfig = vm.config.widgetConfig;
+    $scope.$watch('vm.ctx', function() {
+        if (vm.ctx) {
+            vm.settings = vm.ctx.settings;
+            vm.widgetConfig = vm.ctx.widgetConfig;
+            vm.subscription = vm.ctx.defaultSubscription;
+            vm.alarmSource = vm.subscription.alarmSource;
             initializeConfig();
+            updateAlarmSource();
         }
     });
 
@@ -110,13 +119,6 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
         }
     });
 
-    $scope.$watch('vm.subscription', function() {
-        if (vm.subscription) {
-            vm.alarmSource = vm.subscription.alarmSource;
-            updateAlarmSource();
-        }
-    });
-
     $scope.$on('alarms-table-data-updated', function(event, tableId) {
         if (vm.tableId == tableId) {
             if (vm.subscription) {
@@ -140,13 +142,37 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
         }
     });
 
+    $scope.$watch('vm.selectedAlarms.length', function (newLength) {
+        var selectionMode = newLength ? true : false;
+        if (vm.ctx) {
+            if (selectionMode) {
+                vm.ctx.hideTitlePanel = true;
+            } else if (vm.query.search == null) {
+                vm.ctx.hideTitlePanel = false;
+            }
+        }
+    });
+
     function initializeConfig() {
 
+        vm.ctx.widgetActions = [ vm.searchAction ];
+
         if (vm.settings.alarmsTitle && vm.settings.alarmsTitle.length) {
-            vm.alarmsTitle = vm.settings.alarmsTitle;
+            var translationId = types.translate.customTranslationsPrefix + vm.settings.alarmsTitle;
+            var translation = $translate.instant(translationId);
+            if (translation != translationId) {
+                vm.alarmsTitle = translation + '';
+            } else {
+                vm.alarmsTitle = vm.settings.alarmsTitle;
+            }
+        } else {
+            vm.alarmsTitle = $translate.instant('alarm.alarms');
         }
+
+        vm.ctx.widgetTitle = vm.alarmsTitle;
+
         vm.enableSelection = angular.isDefined(vm.settings.enableSelection) ? vm.settings.enableSelection : true;
-        vm.enableSearch = angular.isDefined(vm.settings.enableSearch) ? vm.settings.enableSearch : true;
+        vm.searchAction.show = angular.isDefined(vm.settings.enableSearch) ? vm.settings.enableSearch : true;
         vm.displayDetails = angular.isDefined(vm.settings.displayDetails) ? vm.settings.displayDetails : true;
         vm.allowAcknowledgment = angular.isDefined(vm.settings.allowAcknowledgment) ? vm.settings.allowAcknowledgment : true;
         vm.allowClear = angular.isDefined(vm.settings.allowClear) ? vm.settings.allowClear : true;
@@ -233,11 +259,13 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
 
     function enterFilterMode () {
         vm.query.search = '';
+        vm.ctx.hideTitlePanel = true;
     }
 
     function exitFilterMode () {
         vm.query.search = null;
         updateAlarms();
+        vm.ctx.hideTitlePanel = false;
     }
 
     function onReorder () {
@@ -496,9 +524,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
 
     function updateAlarmSource() {
 
-        if (vm.settings.alarmsTitle && vm.settings.alarmsTitle.length) {
-            vm.alarmsTitle = utils.createLabelFromDatasource(vm.alarmSource, vm.settings.alarmsTitle);
-        }
+        vm.ctx.widgetTitle = utils.createLabelFromDatasource(vm.alarmSource, vm.alarmsTitle);
 
         vm.stylesInfo = {};
         vm.contentsInfo = {};
@@ -507,10 +533,10 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
         for (var d = 0; d < vm.alarmSource.dataKeys.length; d++ ) {
             var dataKey = vm.alarmSource.dataKeys[d];
 
-            var translationId = types.translate.keyLabelPrefix + dataKey.label;
+            var translationId = types.translate.customTranslationsPrefix + dataKey.label;
             var translation = $translate.instant(translationId);
             if (translation != translationId) {
-                dataKey.title = translation;
+                dataKey.title = translation + '';
             } else {
                 dataKey.title = dataKey.label;
             }
diff --git a/ui/src/app/widget/lib/alarms-table-widget.scss b/ui/src/app/widget/lib/alarms-table-widget.scss
index ac5addd..600be46 100644
--- a/ui/src/app/widget/lib/alarms-table-widget.scss
+++ b/ui/src/app/widget/lib/alarms-table-widget.scss
@@ -14,8 +14,32 @@
  * limitations under the License.
  */
 
+.tb-has-timewindow {
+  .tb-alarms-table {
+    md-toolbar {
+      min-height: 60px;
+      max-height: 60px;
+      &.md-table-toolbar {
+        .md-toolbar-tools {
+          max-height: 60px;
+        }
+      }
+    }
+  }
+}
+
 .tb-alarms-table {
-  margin-top: 15px;
+
+  md-toolbar {
+    min-height: 39px;
+    max-height: 39px;
+    &.md-table-toolbar {
+      .md-toolbar-tools {
+        max-height: 39px;
+      }
+    }
+  }
+
   &.tb-data-table {
     table.md-table, table.md-table.md-row-select {
       tbody {
diff --git a/ui/src/app/widget/lib/alarms-table-widget.tpl.html b/ui/src/app/widget/lib/alarms-table-widget.tpl.html
index cfb92ca..942b2af 100644
--- a/ui/src/app/widget/lib/alarms-table-widget.tpl.html
+++ b/ui/src/app/widget/lib/alarms-table-widget.tpl.html
@@ -17,19 +17,6 @@
 -->
 <div class="tb-absolute-fill tb-alarms-table tb-data-table" layout="column">
     <div ng-show="vm.showData" flex class="tb-absolute-fill" layout="column">
-        <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedAlarms.length
-                                                                 && vm.query.search === null">
-            <div class="md-toolbar-tools">
-                <span>{{ vm.alarmsTitle }}</span>
-                <span flex></span>
-                <md-button ng-if="vm.enableSearch" class="md-icon-button" ng-click="vm.enterFilterMode()">
-                    <md-icon>search</md-icon>
-                    <md-tooltip md-direction="top">
-                        {{ 'action.search' | translate }}
-                    </md-tooltip>
-                </md-button>
-            </div>
-        </md-toolbar>
         <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedAlarms.length &&
                                                                   vm.query.search != null">
             <div class="md-toolbar-tools">