thingsboard-memoizeit
Changes
ui/src/app/api/subscription.js 43(+43 -0)
ui/src/app/api/widget.service.js 2(+1 -1)
ui/src/app/common/types.constant.js 3(+2 -1)
ui/src/app/common/utils.service.js 23(+20 -3)
ui/src/app/components/dashboard.directive.js 10(+10 -0)
ui/src/app/components/dashboard.tpl.html 10(+10 -0)
ui/src/app/components/widget/widget.controller.js 120(+119 -1)
ui/src/app/locale/locale.constant.js 14(+12 -2)
ui/src/app/widget/lib/alarms-table-widget.js 19(+5 -14)
ui/src/app/widget/lib/entities-table-widget.js 60(+45 -15)
Details
ui/src/app/api/subscription.js 43(+43 -0)
diff --git a/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js
index 215da0f..dd93626 100644
--- a/ui/src/app/api/subscription.js
+++ b/ui/src/app/api/subscription.js
@@ -171,6 +171,48 @@ export default class Subscription {
return deferred.promise;
}
+ getFirstEntityInfo() {
+ var entityId;
+ var entityName;
+ if (this.type === this.ctx.types.widgetType.rpc.value) {
+ if (this.targetDeviceId) {
+ entityId = {
+ entityType: this.ctx.entityType.device,
+ id: this.targetDeviceId
+ }
+ entityName = this.targetDeviceName;
+ }
+ } else if (this.type == this.ctx.types.widgetType.alarm.value) {
+ if (this.alarmSource && this.alarmSource.entityType && this.alarmSource.entityId) {
+ entityId = {
+ entityType: this.alarmSource.entityType,
+ id: this.alarmSource.entityId
+ }
+ entityName = this.alarmSource.entityName;
+ }
+ } else {
+ for (var i=0;i<this.datasources.length;i++) {
+ var datasource = this.datasources[i];
+ if (datasource && datasource.entityType && datasource.entityId) {
+ entityId = {
+ entityType: datasource.entityType,
+ id: datasource.entityId
+ }
+ entityName = datasource.entityName;
+ break;
+ }
+ }
+ }
+ if (entityId) {
+ return {
+ entityId: entityId,
+ entityName: entityName
+ };
+ } else {
+ return null;
+ }
+ }
+
initAlarmSubscription() {
var deferred = this.ctx.$q.defer();
if (!this.ctx.aliasController) {
@@ -342,6 +384,7 @@ export default class Subscription {
function success(aliasInfo) {
if (aliasInfo.currentEntity && aliasInfo.currentEntity.entityType == subscription.ctx.types.entityType.device) {
subscription.targetDeviceId = aliasInfo.currentEntity.id;
+ subscription.targetDeviceName = aliasInfo.currentEntity.name;
if (subscription.targetDeviceId) {
subscription.rpcEnabled = true;
} else {
ui/src/app/api/widget.service.js 2(+1 -1)
diff --git a/ui/src/app/api/widget.service.js b/ui/src/app/api/widget.service.js
index e23e916..a58c307 100644
--- a/ui/src/app/api/widget.service.js
+++ b/ui/src/app/api/widget.service.js
@@ -626,7 +626,7 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
}
for (var actionSourceId in types.widgetActionSources) {
result.actionSources[actionSourceId] = angular.copy(types.widgetActionSources[actionSourceId]);
- result.actionSources[actionSourceId].name = $translate.instant(result.actionSources[actionSourceId].name);
+ result.actionSources[actionSourceId].name = $translate.instant(result.actionSources[actionSourceId].name) + '';
}
return result;
ui/src/app/common/types.constant.js 3(+2 -1)
diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index 43cc0cf..61fc120 100644
--- a/ui/src/app/common/types.constant.js
+++ b/ui/src/app/common/types.constant.js
@@ -400,8 +400,9 @@ export default angular.module('thingsboard.types', [])
}
},
widgetActionSources: {
- 'headerButton': {
+ headerButton: {
name: 'widget-action.header-button',
+ value: 'headerButton',
multiple: true
}
},
ui/src/app/common/utils.service.js 23(+20 -3)
diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js
index e7252e0..7c9f97d 100644
--- a/ui/src/app/common/utils.service.js
+++ b/ui/src/app/common/utils.service.js
@@ -38,7 +38,11 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
materialColors = [],
materialIcons = [];
- var commonUsedMaterialIcons = [ 'more_horiz', 'close', 'play_arrow' ];
+ var commonMaterialIcons = [ 'more_horiz', 'more_vert', 'open_in_new', 'visibility', 'play_arrow', 'arrow_back', 'arrow_downward',
+ 'arrow_forward', 'arrow_upwards', 'close', 'refresh', 'menu', 'show_chart', 'multiline_chart', 'pie_chart', 'insert_chart', 'people',
+ 'person', 'domain', 'devices_other', 'now_widgets', 'dashboards', 'map', 'pin_drop', 'my_location', 'extension', 'search',
+ 'settings', 'notifications', 'notifications_active', 'info', 'info_outline', 'warning', 'list', 'file_download', 'import_export',
+ 'share', 'add', 'edit', 'done' ];
predefinedFunctions['Sin'] = "return Math.round(1000*Math.sin(time/5000));";
predefinedFunctions['Cos'] = "return Math.round(1000*Math.cos(time/5000));";
@@ -148,7 +152,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
validateDatasources: validateDatasources,
createKey: createKey,
createLabelFromDatasource: createLabelFromDatasource,
- insertVariable: insertVariable
+ insertVariable: insertVariable,
+ customTranslation: customTranslation
}
return service;
@@ -188,7 +193,7 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
}
function getCommonMaterialIcons() {
- return commonUsedMaterialIcons;
+ return commonMaterialIcons;
}
function genMaterialColor(str) {
@@ -469,4 +474,16 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
return result;
}
+ function customTranslation(translationValue, defaultValue) {
+ var result = '';
+ var translationId = types.translate.customTranslationsPrefix + translationValue;
+ var translation = $translate.instant(translationId);
+ if (translation != translationId) {
+ result = translation + '';
+ } else {
+ result = defaultValue;
+ }
+ return result;
+ }
+
}
ui/src/app/components/dashboard.directive.js 10(+10 -0)
diff --git a/ui/src/app/components/dashboard.directive.js b/ui/src/app/components/dashboard.directive.js
index e61abcc..1a4cdba 100644
--- a/ui/src/app/components/dashboard.directive.js
+++ b/ui/src/app/components/dashboard.directive.js
@@ -187,6 +187,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
vm.showWidgetActions = showWidgetActions;
vm.widgetTitleStyle = widgetTitleStyle;
vm.widgetTitle = widgetTitle;
+ vm.customWidgetHeaderActions = customWidgetHeaderActions;
vm.widgetActions = widgetActions;
vm.dropWidgetShadow = dropWidgetShadow;
vm.enableWidgetFullscreen = enableWidgetFullscreen;
@@ -875,6 +876,15 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
}
}
+ function customWidgetHeaderActions(widget) {
+ var ctx = widgetContext(widget);
+ if (ctx && ctx.customHeaderActions && ctx.customHeaderActions.length) {
+ return ctx.customHeaderActions;
+ } else {
+ return [];
+ }
+ }
+
function widgetActions(widget) {
var ctx = widgetContext(widget);
if (ctx && ctx.widgetActions && ctx.widgetActions.length) {
ui/src/app/components/dashboard.tpl.html 10(+10 -0)
diff --git a/ui/src/app/components/dashboard.tpl.html b/ui/src/app/components/dashboard.tpl.html
index aea3dfc..5d46fea 100644
--- a/ui/src/app/components/dashboard.tpl.html
+++ b/ui/src/app/components/dashboard.tpl.html
@@ -52,6 +52,16 @@
<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" ng-show="vm.showWidgetActions(widget)" tb-mousedown="$event.stopPropagation()">
+ <md-button ng-repeat="action in vm.customWidgetHeaderActions(widget)"
+ aria-label="{{action.displayName}}"
+ ng-show="!vm.isEdit"
+ ng-click="action.onAction($event)"
+ class="md-icon-button">
+ <md-tooltip md-direction="top">
+ {{action.displayName}}
+ </md-tooltip>
+ <ng-md-icon size="20" icon="{{action.icon}}"></ng-md-icon>
+ </md-button>
<md-button ng-repeat="action in vm.widgetActions(widget)"
aria-label="{{ action.name | translate }}"
ng-show="!vm.isEdit && action.show"
diff --git a/ui/src/app/components/dashboard-autocomplete.directive.js b/ui/src/app/components/dashboard-autocomplete.directive.js
index 77e3c1c..59a56e1 100644
--- a/ui/src/app/components/dashboard-autocomplete.directive.js
+++ b/ui/src/app/components/dashboard-autocomplete.directive.js
@@ -87,23 +87,32 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u
dashboardService.getDashboardInfo(ngModelCtrl.$viewValue).then(
function success(dashboard) {
scope.dashboard = dashboard;
+ startWatchers();
},
function fail() {
scope.dashboard = null;
+ scope.updateView();
+ startWatchers();
}
);
} else {
scope.dashboard = null;
+ startWatchers();
}
}
- scope.$watch('dashboard', function () {
- scope.updateView();
- });
-
- scope.$watch('disabled', function () {
- scope.updateView();
- });
+ function startWatchers() {
+ scope.$watch('dashboard', function (newVal, prevVal) {
+ if (!angular.equals(newVal, prevVal)) {
+ scope.updateView();
+ }
+ });
+ scope.$watch('disabled', function (newVal, prevVal) {
+ if (!angular.equals(newVal, prevVal)) {
+ scope.updateView();
+ }
+ });
+ }
if (scope.selectFirstDashboard) {
var pageLink = {limit: 1, textSearch: ''};
@@ -111,6 +120,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u
var dashboards = result.data;
if (dashboards.length > 0) {
scope.dashboard = dashboards[0];
+ scope.updateView();
}
}, function fail() {
});
diff --git a/ui/src/app/components/material-icon-select.directive.js b/ui/src/app/components/material-icon-select.directive.js
index 45ab2cd..f40d1c8 100644
--- a/ui/src/app/components/material-icon-select.directive.js
+++ b/ui/src/app/components/material-icon-select.directive.js
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import './material-icon-select.scss';
+
import MaterialIconsDialogController from './material-icons-dialog.controller';
/* eslint-disable import/no-unresolved, import/default */
diff --git a/ui/src/app/components/material-icon-select.scss b/ui/src/app/components/material-icon-select.scss
new file mode 100644
index 0000000..5196e11
--- /dev/null
+++ b/ui/src/app/components/material-icon-select.scss
@@ -0,0 +1,27 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.tb-material-icon-select {
+ md-icon {
+ padding: 4px;
+ margin: 8px 4px 4px;
+ cursor: pointer;
+ border: solid 1px rgba(0,0,0,0.27);
+ }
+ md-input-container {
+ margin-bottom: 0px;
+ }
+}
\ No newline at end of file
diff --git a/ui/src/app/components/material-icon-select.tpl.html b/ui/src/app/components/material-icon-select.tpl.html
index 77ef87f..c2bee3f 100644
--- a/ui/src/app/components/material-icon-select.tpl.html
+++ b/ui/src/app/components/material-icon-select.tpl.html
@@ -15,12 +15,10 @@
limitations under the License.
-->
-<div layout="row">
+<div class="tb-material-icon-select" layout="row">
<md-icon class="material-icons" ng-click="openIconDialog($event)">{{icon}}</md-icon>
<md-input-container flex>
- <md-input-container class="md-block">
- <label translate>icon.icon</label>
- <input ng-click="openIconDialog($event)" ng-model="icon">
- </md-input-container>
+ <label translate>icon.icon</label>
+ <input ng-mousedown="openIconDialog($event)" ng-model="icon">
</md-input-container>
</div>
\ No newline at end of file
diff --git a/ui/src/app/components/widget/action/manage-widget-actions.directive.js b/ui/src/app/components/widget/action/manage-widget-actions.directive.js
index b273729..93ca06f 100644
--- a/ui/src/app/components/widget/action/manage-widget-actions.directive.js
+++ b/ui/src/app/components/widget/action/manage-widget-actions.directive.js
@@ -42,7 +42,8 @@ function ManageWidgetActions() {
scope: true,
bindToController: {
actionSources: '=',
- widgetActions: '='
+ widgetActions: '=',
+ fetchDashboardStates: '&',
},
controller: ManageWidgetActionsController,
controllerAs: 'vm',
@@ -55,7 +56,7 @@ function ManageWidgetActions() {
/*@ngInject*/
function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog, $q, $filter,
- $translate, $timeout, types) {
+ $translate, $timeout, utils, types) {
let vm = this;
@@ -165,12 +166,30 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog,
if (!isAdd) {
prevActionId = action.id;
}
+ var availableActionSources = {};
+ for (var id in vm.actionSources) {
+ var actionSource = vm.actionSources[id];
+ if (actionSource.multiple) {
+ availableActionSources[id] = actionSource;
+ } else {
+ if (!isAdd && action.actionSourceId == id) {
+ availableActionSources[id] = actionSource;
+ } else {
+ var result = $filter('filter')(vm.allActions, {actionSourceId: id});
+ if (!result || !result.length) {
+ availableActionSources[id] = actionSource;
+ }
+ }
+ }
+ }
$mdDialog.show({
controller: 'WidgetActionDialogController',
controllerAs: 'vm',
templateUrl: widgetActionDialogTemplate,
parent: angular.element($document[0].body),
- locals: {isAdd: isAdd, actionSources: vm.actionSources, action: angular.copy(action)},
+ locals: {isAdd: isAdd, fetchDashboardStates: vm.fetchDashboardStates,
+ actionSources: availableActionSources, widgetActions: vm.widgetActions,
+ action: angular.copy(action)},
skipHide: true,
fullscreen: true,
targetEvent: $event
@@ -189,7 +208,8 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog,
}
function saveAction(action, prevActionId) {
- action.actionSourceName = vm.actionSources[action.actionSourceId].name;
+ var actionSourceName = vm.actionSources[action.actionSourceId].name;
+ action.actionSourceName = utils.customTranslation(actionSourceName, actionSourceName);
action.typeName = $translate.instant(types.widgetActionTypes[action.type].name);
var actionSourceId = action.actionSourceId;
var widgetAction = angular.copy(action);
@@ -227,15 +247,10 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog,
var actionSourceActions = vm.widgetActions[actionSourceId];
for (var i=0;i<actionSourceActions.length;i++) {
var actionSourceAction = actionSourceActions[i];
- var action = {
- id: actionSourceAction.id,
- actionSourceId: actionSourceId,
- actionSourceName: actionSource.name,
- name: actionSourceAction.name,
- icon: actionSourceAction.icon,
- type: actionSourceAction.type,
- typeName: $translate.instant(types.widgetActionTypes[actionSourceAction.type].name)
- };
+ var action = angular.copy(actionSourceAction);
+ action.actionSourceId = actionSourceId;
+ action.actionSourceName = utils.customTranslation(actionSource.name, actionSource.name);
+ action.typeName = $translate.instant(types.widgetActionTypes[actionSourceAction.type].name);
vm.allActions.push(action);
}
}
diff --git a/ui/src/app/components/widget/action/widget-action-dialog.controller.js b/ui/src/app/components/widget/action/widget-action-dialog.controller.js
index 948308b..99a2f0b 100644
--- a/ui/src/app/components/widget/action/widget-action-dialog.controller.js
+++ b/ui/src/app/components/widget/action/widget-action-dialog.controller.js
@@ -15,14 +15,21 @@
*/
/*@ngInject*/
-export default function WidgetActionDialogController($scope, $mdDialog, types, utils, isAdd, actionSources, action) {
+export default function WidgetActionDialogController($scope, $mdDialog, $filter, $q, dashboardService, dashboardUtils, types, utils,
+ isAdd, fetchDashboardStates, actionSources, widgetActions, action) {
var vm = this;
vm.types = types;
vm.isAdd = isAdd;
+ vm.fetchDashboardStates = fetchDashboardStates;
vm.actionSources = actionSources;
+ vm.widgetActions = widgetActions;
+
+ vm.targetDashboardStateSearchText = '';
+
+ vm.selectedDashboardStateIds = [];
if (vm.isAdd) {
vm.action = {
@@ -32,15 +39,131 @@ export default function WidgetActionDialogController($scope, $mdDialog, types, u
vm.action = action;
}
+ vm.actionSourceName = actionSourceName;
+
+ vm.targetDashboardStateSearchTextChanged = function() {
+ }
+
+ vm.dashboardStateSearch = dashboardStateSearch;
vm.cancel = cancel;
vm.save = save;
+ $scope.$watch("vm.action.name", function(newVal, prevVal) {
+ if (!angular.equals(newVal, prevVal) && vm.action.name != null) {
+ checkActionName();
+ }
+ });
+
+ $scope.$watch("vm.action.actionSourceId", function(newVal, prevVal) {
+ if (!angular.equals(newVal, prevVal) && vm.action.actionSourceId != null) {
+ checkActionName();
+ }
+ });
+
+ $scope.$watch("vm.action.targetDashboardId", function() {
+ vm.selectedDashboardStateIds = [];
+ if (vm.action.targetDashboardId) {
+ dashboardService.getDashboard(vm.action.targetDashboardId).then(
+ function success(dashboard) {
+ dashboard = dashboardUtils.validateAndUpdateDashboard(dashboard);
+ var states = dashboard.configuration.states;
+ vm.selectedDashboardStateIds = Object.keys(states);
+ }
+ );
+ }
+ });
+
+ $scope.$watch('vm.action.type', function(newType) {
+ if (newType) {
+ switch (newType) {
+ case vm.types.widgetActionTypes.openDashboardState.value:
+ case vm.types.widgetActionTypes.updateDashboardState.value:
+ case vm.types.widgetActionTypes.openDashboard.value:
+ if (angular.isUndefined(vm.action.setEntityId)) {
+ vm.action.setEntityId = true;
+ }
+ break;
+ }
+ }
+ });
+
+ function checkActionName() {
+ var actionNameIsUnique = true;
+ if (vm.action.actionSourceId && vm.action.name) {
+ var sourceActions = vm.widgetActions[vm.action.actionSourceId];
+ if (sourceActions) {
+ var result = $filter('filter')(sourceActions, {name: vm.action.name}, true);
+ if (result && result.length && result[0].id !== vm.action.id) {
+ actionNameIsUnique = false;
+ }
+ }
+ }
+ $scope.theForm.name.$setValidity('actionNameNotUnique', actionNameIsUnique);
+ }
+
+ function actionSourceName (actionSource) {
+ if (actionSource) {
+ return utils.customTranslation(actionSource.name, actionSource.name);
+ } else {
+ return '';
+ }
+ }
+
+ function dashboardStateSearch (query) {
+ if (vm.action.type == vm.types.widgetActionTypes.openDashboard.value) {
+ var deferred = $q.defer();
+ var result = query ? vm.selectedDashboardStateIds.filter(
+ createFilterForDashboardState(query)) : vm.selectedDashboardStateIds;
+ if (result && result.length) {
+ deferred.resolve(result);
+ } else {
+ deferred.resolve([query]);
+ }
+ return deferred.promise;
+ } else {
+ return vm.fetchDashboardStates({query: query});
+ }
+ }
+
+ function createFilterForDashboardState (query) {
+ var lowercaseQuery = angular.lowercase(query);
+ return function filterFn(stateId) {
+ return (angular.lowercase(stateId).indexOf(lowercaseQuery) === 0);
+ };
+ }
+
+ function cleanupAction(action) {
+ var result = {};
+ result.id = action.id;
+ result.actionSourceId = action.actionSourceId;
+ result.name = action.name;
+ result.icon = action.icon;
+ result.type = action.type;
+ switch (action.type) {
+ case vm.types.widgetActionTypes.openDashboardState.value:
+ case vm.types.widgetActionTypes.updateDashboardState.value:
+ result.targetDashboardStateId = action.targetDashboardStateId;
+ result.openRightLayout = action.openRightLayout;
+ result.setEntityId = action.setEntityId;
+ break;
+ case vm.types.widgetActionTypes.openDashboard.value:
+ result.targetDashboardId = action.targetDashboardId;
+ result.targetDashboardStateId = action.targetDashboardStateId;
+ result.setEntityId = action.setEntityId;
+ break;
+ case vm.types.widgetActionTypes.custom.value:
+ result.customFunction = action.customFunction;
+ break;
+ }
+ return result;
+ }
+
function cancel() {
$mdDialog.cancel();
}
function save() {
$scope.theForm.$setPristine();
- $mdDialog.hide(vm.action);
+ $mdDialog.hide(cleanupAction(vm.action));
}
}
diff --git a/ui/src/app/components/widget/action/widget-action-dialog.tpl.html b/ui/src/app/components/widget/action/widget-action-dialog.tpl.html
index ccb9d41..7a20e74 100644
--- a/ui/src/app/components/widget/action/widget-action-dialog.tpl.html
+++ b/ui/src/app/components/widget/action/widget-action-dialog.tpl.html
@@ -31,12 +31,12 @@
<md-dialog-content>
<div class="md-dialog-content">
<md-content class="md-padding" layout="column">
- <fieldset ng-disabled="loading">
+ <fieldset ng-disabled="loading" layout="column">
<md-input-container class="md-block">
<label translate>widget-config.action-source</label>
<md-select name="actionSource" required aria-label="{{ 'widget-config.action-source' | translate }}" ng-model="vm.action.actionSourceId">
<md-option ng-repeat="(actionSourceId, actionSource) in vm.actionSources" ng-value="actionSourceId">
- {{actionSource.name}}
+ {{vm.actionSourceName(actionSource)}}
</md-option>
</md-select>
<div ng-messages="theForm.actionSource.$error">
@@ -48,6 +48,7 @@
<input name="name" required ng-model="vm.action.name">
<div ng-messages="theForm.name.$error">
<div ng-message="required" translate>widget-config.action-name-required</div>
+ <div ng-message="actionNameNotUnique" translate>widget-config.action-name-not-unique</div>
</div>
</md-input-container>
<tb-material-icon-select ng-model="vm.action.icon">
@@ -63,6 +64,57 @@
<div ng-message="required" translate>widget-config.action-type-required</div>
</div>
</md-input-container>
+ <div layout="column"
+ style="padding-bottom: 20px;"
+ ng-if="vm.action.type == vm.types.widgetActionTypes.openDashboard.value">
+ <div class="md-caption tb-required"
+ style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>widget-action.target-dashboard</div>
+ <tb-dashboard-autocomplete the-form="theForm"
+ tb-required="true"
+ ng-model="vm.action.targetDashboardId"
+ select-first-dashboard="false">
+ </tb-dashboard-autocomplete>
+ </div>
+ <md-autocomplete ng-if="vm.action.type == vm.types.widgetActionTypes.openDashboardState.value ||
+ vm.action.type == vm.types.widgetActionTypes.updateDashboardState.value ||
+ vm.action.type == vm.types.widgetActionTypes.openDashboard.value"
+ ng-required="vm.action.type == vm.types.widgetActionTypes.openDashboardState.value"
+ md-no-cache="true"
+ md-input-name="targetDashboardState"
+ ng-model="vm.action.targetDashboardStateId"
+ md-selected-item="vm.action.targetDashboardStateId"
+ md-search-text="vm.targetDashboardStateSearchText"
+ md-search-text-change="vm.targetDashboardStateSearchTextChanged()"
+ md-items="item in vm.dashboardStateSearch(vm.targetDashboardStateSearchText)"
+ md-item-text="item"
+ md-min-length="0"
+ md-floating-label="{{ 'widget-action.target-dashboard-state' | translate }}"
+ md-select-on-match="true">
+ <md-item-template>
+ <div>
+ <span md-highlight-text="vm.targetDashboardStateSearchText" md-highlight-flags="^i">{{item}}</span>
+ </div>
+ </md-item-template>
+ <div ng-messages="theForm.targetDashboardState.$error">
+ <div translate ng-message="required">widget-action.target-dashboard-state-required</div>
+ </div>
+ </md-autocomplete>
+ <md-checkbox ng-if="vm.action.type == vm.types.widgetActionTypes.openDashboardState.value ||
+ vm.action.type == vm.types.widgetActionTypes.updateDashboardState.value"
+ flex aria-label="{{ 'widget-action.open-right-layout' | translate }}"
+ ng-model="vm.action.openRightLayout">{{ 'widget-action.open-right-layout' | translate }}
+ </md-checkbox>
+ <md-checkbox ng-if="vm.action.type == vm.types.widgetActionTypes.openDashboardState.value ||
+ vm.action.type == vm.types.widgetActionTypes.updateDashboardState.value ||
+ vm.action.type == vm.types.widgetActionTypes.openDashboard.value"
+ flex aria-label="{{ 'widget-action.set-entity-from-widget' | translate }}"
+ ng-model="vm.action.setEntityId">{{ 'widget-action.set-entity-from-widget' | translate }}
+ </md-checkbox>
+ <tb-js-func ng-if="vm.action.type == vm.types.widgetActionTypes.custom.value"
+ ng-model="vm.action.customFunction"
+ function-args="{{ ['$event', 'widgetContext', 'entityId'] }}"
+ validation-args="{{ [] }}">
+ </tb-js-func>
</fieldset>
</md-content>
</div>
ui/src/app/components/widget/widget.controller.js 120(+119 -1)
diff --git a/ui/src/app/components/widget/widget.controller.js b/ui/src/app/components/widget/widget.controller.js
index b2877f3..113399a 100644
--- a/ui/src/app/components/widget/widget.controller.js
+++ b/ui/src/app/components/widget/widget.controller.js
@@ -20,7 +20,7 @@ import Subscription from '../../api/subscription';
/* eslint-disable angular/angularelement */
/*@ngInject*/
-export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService,
+export default function WidgetController($scope, $state, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService,
datasourceService, alarmService, entityService, deviceService, visibleRect, isEdit, isMobile, stDiff, dashboardTimewindow,
dashboardTimewindowApi, widget, aliasController, stateController, widgetInfo, widgetType) {
@@ -44,6 +44,20 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
var cafs = {};
+ var actionDescriptorsBySourceId = {};
+ if (widget.config.actions) {
+ for (var actionSourceId in widget.config.actions) {
+ var descriptors = widget.config.actions[actionSourceId];
+ var actionDescriptors = [];
+ descriptors.forEach(function(descriptor) {
+ var actionDescriptor = angular.copy(descriptor);
+ actionDescriptor.displayName = utils.customTranslation(descriptor.name, descriptor.name);
+ actionDescriptors.push(actionDescriptor);
+ });
+ actionDescriptorsBySourceId[actionSourceId] = actionDescriptors;
+ }
+ }
+
var widgetContext = {
inited: false,
$container: null,
@@ -103,9 +117,32 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
utils: {
formatValue: formatValue
},
+ actionsApi: {
+ actionDescriptorsBySourceId: actionDescriptorsBySourceId,
+ getActionDescriptors: getActionDescriptors,
+ handleWidgetAction: handleWidgetAction
+ },
stateController: stateController
};
+ widgetContext.customHeaderActions = [];
+ var headerActionsDescriptors = getActionDescriptors(types.widgetActionSources.headerButton.value);
+ for (var i=0;i<headerActionsDescriptors.length;i++) {
+ var descriptor = headerActionsDescriptors[i];
+ var headerAction = {};
+ headerAction.name = descriptor.name;
+ headerAction.displayName = descriptor.displayName;
+ headerAction.icon = descriptor.icon;
+ headerAction.descriptor = descriptor;
+ headerAction.onAction = function($event) {
+ var entityInfo = getFirstEntityInfo();
+ var entityId = entityInfo ? entityInfo.entityId : null;
+ var entityName = entityInfo ? entityInfo.entityName : null;
+ handleWidgetAction($event, this.descriptor, entityId, entityName);
+ }
+ widgetContext.customHeaderActions.push(headerAction);
+ }
+
var subscriptionContext = {
$scope: $scope,
$q: $q,
@@ -376,6 +413,87 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
return deferred.promise;
}
+ function getActionDescriptors(actionSourceId) {
+ var result = widgetContext.actionsApi.actionDescriptorsBySourceId[actionSourceId];
+ if (!result) {
+ result = [];
+ }
+ return result;
+ }
+
+ function handleWidgetAction($event, descriptor, entityId, entityName) {
+ var type = descriptor.type;
+ switch (type) {
+ case types.widgetActionTypes.openDashboardState.value:
+ case types.widgetActionTypes.updateDashboardState.value:
+ var targetDashboardStateId = descriptor.targetDashboardStateId;
+ var targetEntityId;
+ if (descriptor.setEntityId) {
+ targetEntityId = entityId;
+ }
+ var params = {};
+ if (targetEntityId) {
+ params.entityId = targetEntityId;
+ if (entityName) {
+ params.entityName = entityName;
+ }
+ }
+ if (type == types.widgetActionTypes.openDashboardState.value) {
+ widgetContext.stateController.openState(targetDashboardStateId, params, descriptor.openRightLayout);
+ } else {
+ widgetContext.stateController.updateState(targetDashboardStateId, params, descriptor.openRightLayout);
+ }
+ break;
+ case types.widgetActionTypes.openDashboard.value:
+ var targetDashboardId = descriptor.targetDashboardId;
+ targetDashboardStateId = descriptor.targetDashboardStateId;
+ targetEntityId;
+ if (descriptor.setEntityId) {
+ targetEntityId = entityId;
+ }
+ var stateObject = {};
+ stateObject.params = {};
+ if (targetEntityId) {
+ stateObject.params.entityId = targetEntityId;
+ if (entityName) {
+ stateObject.params.entityName = entityName;
+ }
+ }
+ if (targetDashboardStateId) {
+ stateObject.id = targetDashboardStateId;
+ }
+ var stateParams = {
+ dashboardId: targetDashboardId,
+ state: angular.toJson([ stateObject ])
+ }
+ $state.go('home.dashboards.dashboard', stateParams);
+ break;
+ case types.widgetActionTypes.custom.value:
+ var customFunction = descriptor.customFunction;
+ if (angular.isDefined(customFunction) && customFunction.length > 0) {
+ try {
+ var customActionFunction = new Function('$event', 'widgetContext', 'entityId', 'entityName', customFunction);
+ customActionFunction($event, widgetContext, entityId, entityName);
+ } catch (e) {
+ //
+ }
+ }
+ break;
+ }
+ }
+
+ function getFirstEntityInfo() {
+ var entityInfo;
+ for (var id in widgetContext.subscriptions) {
+ var subscription = widgetContext.subscriptions[id];
+ entityInfo = subscription.getFirstEntityInfo();
+ if (entityInfo) {
+ break;
+ }
+ }
+ return entityInfo;
+ }
+
function configureWidgetElement() {
$scope.displayLegend = angular.isDefined(widget.config.showLegend) ?
diff --git a/ui/src/app/components/widget/widget-config.directive.js b/ui/src/app/components/widget/widget-config.directive.js
index 3df9263..afdaf8d 100644
--- a/ui/src/app/components/widget/widget-config.directive.js
+++ b/ui/src/app/components/widget/widget-config.directive.js
@@ -468,6 +468,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
aliasController: '=',
functionsOnly: '=',
fetchEntityKeys: '&',
+ fetchDashboardStates: '&',
onCreateEntityAlias: '&',
theForm: '='
},
diff --git a/ui/src/app/components/widget/widget-config.tpl.html b/ui/src/app/components/widget/widget-config.tpl.html
index 2cc264c..f2ee0f1 100644
--- a/ui/src/app/components/widget/widget-config.tpl.html
+++ b/ui/src/app/components/widget/widget-config.tpl.html
@@ -277,7 +277,10 @@
</md-tab>
<md-tab label="{{ 'widget-config.actions' | translate }}">
<md-content class="md-padding" layout="column">
- <tb-manage-widget-actions action-sources="actionSources" widget-actions="actions">
+ <tb-manage-widget-actions
+ action-sources="actionSources"
+ widget-actions="actions"
+ fetch-dashboard-states="fetchDashboardStates({query: query})">
</tb-manage-widget-actions>
</md-content>
</md-tab>
diff --git a/ui/src/app/dashboard/add-widget.controller.js b/ui/src/app/dashboard/add-widget.controller.js
index fd2ada7..c15492d 100644
--- a/ui/src/app/dashboard/add-widget.controller.js
+++ b/ui/src/app/dashboard/add-widget.controller.js
@@ -36,6 +36,7 @@ export default function AddWidgetController($scope, widgetService, entityService
vm.add = add;
vm.cancel = cancel;
vm.fetchEntityKeys = fetchEntityKeys;
+ vm.fetchDashboardStates = fetchDashboardStates;
vm.createEntityAlias = createEntityAlias;
vm.widgetConfig = {
@@ -128,6 +129,26 @@ export default function AddWidgetController($scope, widgetService, entityService
return deferred.promise;
}
+ function fetchDashboardStates (query) {
+ var deferred = $q.defer();
+ var stateIds = Object.keys(vm.dashboard.configuration.states);
+ var result = query ? stateIds.filter(
+ createFilterForDashboardState(query)) : stateIds;
+ if (result && result.length) {
+ deferred.resolve(result);
+ } else {
+ deferred.resolve([query]);
+ }
+ return deferred.promise;
+ }
+
+ function createFilterForDashboardState (query) {
+ var lowercaseQuery = angular.lowercase(query);
+ return function filterFn(stateId) {
+ return (angular.lowercase(stateId).indexOf(lowercaseQuery) === 0);
+ };
+ }
+
function createEntityAlias (event, alias, allowedEntityTypes) {
var deferred = $q.defer();
diff --git a/ui/src/app/dashboard/add-widget.tpl.html b/ui/src/app/dashboard/add-widget.tpl.html
index 3920f91..4ca29f0 100644
--- a/ui/src/app/dashboard/add-widget.tpl.html
+++ b/ui/src/app/dashboard/add-widget.tpl.html
@@ -42,6 +42,7 @@
alias-controller="vm.aliasController"
functions-only="vm.functionsOnly"
fetch-entity-keys="vm.fetchEntityKeys(entityAliasId, query, type)"
+ fetch-dashboard-states="vm.fetchDashboardStates(query)"
on-create-entity-alias="vm.createEntityAlias(event, alias, allowedEntityTypes)"
the-form="theForm"></tb-widget-config>
</fieldset>
diff --git a/ui/src/app/dashboard/edit-widget.directive.js b/ui/src/app/dashboard/edit-widget.directive.js
index 5d32706..9bf1548 100644
--- a/ui/src/app/dashboard/edit-widget.directive.js
+++ b/ui/src/app/dashboard/edit-widget.directive.js
@@ -94,6 +94,26 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
return deferred.promise;
};
+ scope.fetchDashboardStates = function(query) {
+ var deferred = $q.defer();
+ var stateIds = Object.keys(scope.dashboard.configuration.states);
+ var result = query ? stateIds.filter(
+ createFilterForDashboardState(query)) : stateIds;
+ if (result && result.length) {
+ deferred.resolve(result);
+ } else {
+ deferred.resolve([query]);
+ }
+ return deferred.promise;
+ }
+
+ function createFilterForDashboardState (query) {
+ var lowercaseQuery = angular.lowercase(query);
+ return function filterFn(stateId) {
+ return (angular.lowercase(stateId).indexOf(lowercaseQuery) === 0);
+ };
+ }
+
scope.createEntityAlias = function (event, alias, allowedEntityTypes) {
var deferred = $q.defer();
diff --git a/ui/src/app/dashboard/edit-widget.tpl.html b/ui/src/app/dashboard/edit-widget.tpl.html
index c6bfd5a..1f724a8 100644
--- a/ui/src/app/dashboard/edit-widget.tpl.html
+++ b/ui/src/app/dashboard/edit-widget.tpl.html
@@ -26,6 +26,7 @@
alias-controller="aliasController"
functions-only="widgetEditMode"
fetch-entity-keys="fetchEntityKeys(entityAliasId, query, type)"
+ fetch-dashboard-states="fetchDashboardStates(query)"
on-create-entity-alias="createEntityAlias(event, alias, allowedEntityTypes)"
the-form="theForm"></tb-widget-config>
</fieldset>
diff --git a/ui/src/app/dashboard/states/default-state-controller.js b/ui/src/app/dashboard/states/default-state-controller.js
index 716d333..d39478f 100644
--- a/ui/src/app/dashboard/states/default-state-controller.js
+++ b/ui/src/app/dashboard/states/default-state-controller.js
@@ -15,7 +15,7 @@
*/
/*@ngInject*/
-export default function DefaultStateController($scope, $location, $state, $stateParams, $translate, types, dashboardUtils) {
+export default function DefaultStateController($scope, $location, $state, $stateParams, utils, types, dashboardUtils) {
var vm = this;
@@ -50,6 +50,9 @@ export default function DefaultStateController($scope, $location, $state, $state
}
function updateState(id, params, openRightLayout) {
+ if (!id) {
+ id = getStateId();
+ }
if (vm.states && vm.states[id]) {
if (!params) {
params = {};
@@ -110,15 +113,7 @@ export default function DefaultStateController($scope, $location, $state, $state
}
function getStateName(id, state) {
- var result = '';
- var translationId = types.translate.customTranslationsPrefix + state.name;
- var translation = $translate.instant(translationId);
- if (translation != translationId) {
- result = translation + '';
- } else {
- result = id;
- }
- return result;
+ return utils.customTranslation(state.name, id);
}
function parseState(stateJson) {
diff --git a/ui/src/app/dashboard/states/entity-state-controller.js b/ui/src/app/dashboard/states/entity-state-controller.js
index dd295cc..4510449 100644
--- a/ui/src/app/dashboard/states/entity-state-controller.js
+++ b/ui/src/app/dashboard/states/entity-state-controller.js
@@ -55,6 +55,9 @@ export default function EntityStateController($scope, $location, $state, $stateP
}
function updateState(id, params, openRightLayout) {
+ if (!id) {
+ id = getStateId();
+ }
if (vm.states && vm.states[id]) {
resolveEntity(params).then(
function success(entityName) {
@@ -121,17 +124,10 @@ export default function EntityStateController($scope, $location, $state, $stateP
var result = '';
if (vm.stateObject[index]) {
var stateName = vm.states[vm.stateObject[index].id].name;
- var translationId = types.translate.customTranslationsPrefix + stateName;
- var translation = $translate.instant(translationId);
- if (translation != translationId) {
- stateName = translation + '';
- }
+ stateName = utils.customTranslation(stateName, stateName);
var params = vm.stateObject[index].params;
- if (params && params.entityName) {
- result = utils.insertVariable(stateName, 'entityName', params.entityName);
- } else {
- result = stateName;
- }
+ var entityName = params && params.entityName ? params.entityName : '';
+ result = utils.insertVariable(stateName, 'entityName', entityName);
}
return result;
}
diff --git a/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js b/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
index bcf37dd..a133829 100644
--- a/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
+++ b/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
@@ -102,6 +102,7 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog,
if (vm.addToDashboardType === 0) {
dashboardService.getDashboard(vm.dashboardId).then(
function success(dashboard) {
+ dashboard = dashboardUtils.validateAndUpdateDashboard(dashboard);
selectTargetState($event, dashboard).then(
function(targetState) {
selectTargetLayout($event, dashboard, targetState).then(
ui/src/app/locale/locale.constant.js 14(+12 -2)
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index 63f20e5..4d4ebe2 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -1104,11 +1104,16 @@ export default angular.module('thingsboard.locale', [])
"export": "Export widget"
},
"widget-action": {
- "header-button": "Header button",
+ "header-button": "Widget header button",
"open-dashboard-state": "Navigate to new dashboard state",
"update-dashboard-state": "Update current dashboard state",
"open-dashboard": "Navigate to other dashboard",
- "custom": "Custom action"
+ "custom": "Custom action",
+ "target-dashboard-state": "Target dashboard state",
+ "target-dashboard-state-required": "Target dashboard state is required",
+ "set-entity-from-widget": "Set entity from widget",
+ "target-dashboard": "Target dashboard",
+ "open-right-layout": "Open right dashboard layout (mobile view)"
},
"widgets-bundle": {
"current": "Current bundle",
@@ -1174,6 +1179,7 @@ export default angular.module('thingsboard.locale', [])
"action-source-required": "Action source is required.",
"action-name": "Name",
"action-name-required": "Action name is required.",
+ "action-name-not-unique": "Another action with the same name already exists.<br/>Action name should be unique within the same action source.",
"action-icon": "Icon",
"action-type": "Type",
"action-type-required": "Action type is required.",
@@ -1205,6 +1211,10 @@ export default angular.module('thingsboard.locale', [])
"es_ES": "Spanish"
},
"custom": {
+ "widget-action": {
+ "action-cell-button": "Action cell button",
+ "row-click": "On row click"
+ }
}
}
}
ui/src/app/widget/lib/alarms-table-widget.js 19(+5 -14)
diff --git a/ui/src/app/widget/lib/alarms-table-widget.js b/ui/src/app/widget/lib/alarms-table-widget.js
index 5a65af2..7618e6a 100644
--- a/ui/src/app/widget/lib/alarms-table-widget.js
+++ b/ui/src/app/widget/lib/alarms-table-widget.js
@@ -158,13 +158,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
vm.ctx.widgetActions = [ vm.searchAction ];
if (vm.settings.alarmsTitle && vm.settings.alarmsTitle.length) {
- 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;
- }
+ vm.alarmsTitle = utils.customTranslation(vm.settings.alarmsTitle, vm.settings.alarmsTitle);
} else {
vm.alarmsTitle = $translate.instant('alarm.alarms');
}
@@ -226,6 +220,9 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
'table.md-table td.md-cell.md-checkbox-cell md-checkbox:not(.md-checked) .md-icon {\n'+
'border-color: ' + mdDarkSecondary + ';\n'+
'}\n'+
+ 'table.md-table td.md-cell.tb-action-cell button.md-icon-button md-icon {\n'+
+ 'color: ' + mdDarkSecondary + ';\n'+
+ '}\n'+
'table.md-table td.md-cell.md-placeholder {\n'+
'color: ' + mdDarkDisabled + ';\n'+
'}\n'+
@@ -539,13 +536,7 @@ 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.customTranslationsPrefix + dataKey.label;
- var translation = $translate.instant(translationId);
- if (translation != translationId) {
- dataKey.title = translation + '';
- } else {
- dataKey.title = dataKey.label;
- }
+ dataKey.title = utils.customTranslation(dataKey.label, dataKey.label);
var keySettings = dataKey.settings;
ui/src/app/widget/lib/entities-table-widget.js 60(+45 -15)
diff --git a/ui/src/app/widget/lib/entities-table-widget.js b/ui/src/app/widget/lib/entities-table-widget.js
index 4b8aa6e..733e7cb 100644
--- a/ui/src/app/widget/lib/entities-table-widget.js
+++ b/ui/src/app/widget/lib/entities-table-widget.js
@@ -65,8 +65,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
vm.currentEntity = null;
vm.displayEntityName = true;
+ vm.entityNameColumnTitle = '';
vm.displayEntityType = true;
- vm.displayActions = false; //TODO: Widget actions
+ vm.actionCellDescriptors = [];
vm.displayPagination = true;
vm.defaultPageSize = 10;
vm.defaultSortOrder = 'entityName';
@@ -92,6 +93,7 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
vm.onReorder = onReorder;
vm.onPaginate = onPaginate;
vm.onRowClick = onRowClick;
+ vm.onActionButtonClick = onActionButtonClick;
vm.isCurrent = isCurrent;
vm.cellStyle = cellStyle;
@@ -141,14 +143,10 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
vm.ctx.widgetActions = [ vm.searchAction ];
+ vm.actionCellDescriptors = vm.ctx.actionsApi.getActionDescriptors('actionCellButton');
+
if (vm.settings.entitiesTitle && vm.settings.entitiesTitle.length) {
- var translationId = types.translate.customTranslationsPrefix + vm.settings.entitiesTitle;
- var translation = $translate.instant(translationId);
- if (translation != translationId) {
- vm.entitiesTitle = translation + '';
- } else {
- vm.entitiesTitle = vm.settings.entitiesTitle;
- }
+ vm.entitiesTitle = utils.customTranslation(vm.settings.entitiesTitle, vm.settings.entitiesTitle);
} else {
vm.entitiesTitle = $translate.instant('entity.entities');
}
@@ -157,6 +155,13 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
vm.searchAction.show = angular.isDefined(vm.settings.enableSearch) ? vm.settings.enableSearch : true;
vm.displayEntityName = angular.isDefined(vm.settings.displayEntityName) ? vm.settings.displayEntityName : true;
+
+ if (vm.settings.entityNameColumnTitle && vm.settings.entityNameColumnTitle.length) {
+ vm.entityNameColumnTitle = utils.customTranslation(vm.settings.entityNameColumnTitle, vm.settings.entityNameColumnTitle);
+ } else {
+ vm.entityNameColumnTitle = $translate.instant('entity.entity-name');
+ }
+
vm.displayEntityType = angular.isDefined(vm.settings.displayEntityType) ? vm.settings.displayEntityType : true;
vm.displayPagination = angular.isDefined(vm.settings.displayPagination) ? vm.settings.displayPagination : true;
@@ -185,6 +190,8 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
//var mdDarkIcon = mdDarkSecondary;
var mdDarkDivider = defaultColor.setAlpha(0.12).toRgbString();
+ //md-icon.md-default-theme, md-icon {
+
var cssString = 'table.md-table th.md-column {\n'+
'color: ' + mdDarkSecondary + ';\n'+
'}\n'+
@@ -204,6 +211,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
'table.md-table td.md-cell.md-checkbox-cell md-checkbox:not(.md-checked) .md-icon {\n'+
'border-color: ' + mdDarkSecondary + ';\n'+
'}\n'+
+ 'table.md-table td.md-cell.tb-action-cell button.md-icon-button md-icon {\n'+
+ 'color: ' + mdDarkSecondary + ';\n'+
+ '}\n'+
'table.md-table td.md-cell.md-placeholder {\n'+
'color: ' + mdDarkDisabled + ';\n'+
'}\n'+
@@ -261,11 +271,37 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
}
function onRowClick($event, entity) {
+ if ($event) {
+ $event.stopPropagation();
+ }
if (vm.currentEntity != entity) {
vm.currentEntity = entity;
+ var descriptors = vm.ctx.actionsApi.getActionDescriptors('rowClick');
+ if (descriptors.length) {
+ var entityId;
+ var entityName;
+ if (vm.currentEntity) {
+ entityId = vm.currentEntity.id;
+ entityName = vm.currentEntity.entityName;
+ }
+ vm.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName);
+ }
}
}
+ function onActionButtonClick($event, entity, actionDescriptor) {
+ if ($event) {
+ $event.stopPropagation();
+ }
+ var entityId;
+ var entityName;
+ if (entity) {
+ entityId = entity.id;
+ entityName = entity.entityName;
+ }
+ vm.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName);
+ }
+
function isCurrent(entity) {
return (vm.currentEntity && entity && vm.currentEntity.id && entity.id) &&
(vm.currentEntity.id.id === entity.id.id);
@@ -393,13 +429,7 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
}
vm.dataKeys.push(dataKey);
- var translationId = types.translate.customTranslationsPrefix + dataKey.label;
- var translation = $translate.instant(translationId);
- if (translation != translationId) {
- dataKey.title = translation + '';
- } else {
- dataKey.title = dataKey.label;
- }
+ dataKey.title = utils.customTranslation(dataKey.label, dataKey.label);
var keySettings = dataKey.settings;
diff --git a/ui/src/app/widget/lib/entities-table-widget.tpl.html b/ui/src/app/widget/lib/entities-table-widget.tpl.html
index 247d479..162e5f7 100644
--- a/ui/src/app/widget/lib/entities-table-widget.tpl.html
+++ b/ui/src/app/widget/lib/entities-table-widget.tpl.html
@@ -41,10 +41,10 @@
<table md-table>
<thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder">
<tr md-row>
- <th md-column ng-if="vm.displayEntityName" md-order-by="entityName"><span translate>entity.entity-name</span></th>
+ <th md-column ng-if="vm.displayEntityName" md-order-by="entityName"><span>{{vm.entityNameColumnTitle}}</span></th>
<th md-column ng-if="vm.displayEntityType" md-order-by="entityType"><span translate>entity.entity-type</span></th>
<th md-column md-order-by="{{ key.name }}" ng-repeat="key in vm.dataKeys"><span>{{ key.title }}</span></th>
- <th md-column ng-if="vm.displayActions"><span> </span></th>
+ <th md-column ng-if="vm.actionCellDescriptors.length"><span> </span></th>
</tr>
</thead>
<tbody md-body>
@@ -57,14 +57,18 @@
ng-style="vm.cellStyle(entity, key)"
ng-bind-html="vm.cellContent(entity, key)">
</td>
- <td md-cell ng-if="vm.displayActions" class="tb-action-cell">
- <!--md-button class="md-icon-button" aria-label="{{ 'entity.details' | translate }}"
- ng-click="vm.openEntityDetails($event, entity)">
- <md-icon aria-label="{{ 'entity.details' | translate }}" class="material-icons">more_horiz</md-icon>
+ <td md-cell ng-if="vm.actionCellDescriptors.length" class="tb-action-cell"
+ ng-style="{minWidth: vm.actionCellDescriptors.length*36+'px',
+ maxWidth: vm.actionCellDescriptors.length*36+'px',
+ width: vm.actionCellDescriptors.length*36+'px'}">
+ <md-button class="md-icon-button" ng-repeat="actionDescriptor in vm.actionCellDescriptors"
+ aria-label="{{ actionDescriptor.displayName }}"
+ ng-click="vm.onActionButtonClick($event, entity, actionDescriptor)">
+ <md-icon aria-label="{{ actionDescriptor.displayName }}" class="material-icons">{{actionDescriptor.icon}}</md-icon>
<md-tooltip md-direction="top">
- {{ 'entity.details' | translate }}
+ {{ actionDescriptor.displayName }}
</md-tooltip>
- </md-button-->
+ </md-button>
</td>
</tr>
</tbody>