thingsboard-aplcache
Changes
ui/src/app/api/subscription.js 8(+8 -0)
ui/src/app/widget/lib/alarms-table-widget.js 190(+176 -14)
Details
ui/src/app/api/subscription.js 8(+8 -0)
diff --git a/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js
index 350b1ef..a66c4b1 100644
--- a/ui/src/app/api/subscription.js
+++ b/ui/src/app/api/subscription.js
@@ -267,6 +267,14 @@ export default class Subscription {
} else {
this.startWatchingTimewindow();
}
+ registration = this.ctx.$scope.$watch(function () {
+ return subscription.alarmSearchStatus;
+ }, function (newAlarmSearchStatus, prevAlarmSearchStatus) {
+ if (!angular.equals(newAlarmSearchStatus, prevAlarmSearchStatus)) {
+ subscription.update();
+ }
+ }, true);
+ this.registrations.push(registration);
}
initDataSubscription() {
diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json
index 6321d63..2caf013 100644
--- a/ui/src/app/locale/locale.constant-en_US.json
+++ b/ui/src/app/locale/locale.constant-en_US.json
@@ -133,8 +133,13 @@
"min-polling-interval-message": "At least 1 sec polling interval is allowed.",
"aknowledge-alarms-title": "Acknowledge { count, plural, 1 {1 alarm} other {# alarms} }",
"aknowledge-alarms-text": "Are you sure you want to acknowledge { count, plural, 1 {1 alarm} other {# alarms} }?",
+ "aknowledge-alarm-title": "Acknowledge Alarm",
+ "aknowledge-alarm-text": "Are you sure you want to acknowledge Alarm?",
"clear-alarms-title": "Clear { count, plural, 1 {1 alarm} other {# alarms} }",
- "clear-alarms-text": "Are you sure you want to clear { count, plural, 1 {1 alarm} other {# alarms} }?"
+ "clear-alarms-text": "Are you sure you want to clear { count, plural, 1 {1 alarm} other {# alarms} }?",
+ "clear-alarm-title": "Clear Alarm",
+ "clear-alarm-text": "Are you sure you want to clear Alarm?",
+ "alarm-status-filter": "Alarm Status Filter"
},
"alias": {
"add": "Add alias",
@@ -748,7 +753,8 @@
"entity-name": "Entity name",
"details": "Entity details",
"no-entities-prompt": "No entities found",
- "no-data": "No data to display"
+ "no-data": "No data to display",
+ "columns-to-display": "Columns to Display"
},
"event": {
"event-type": "Event type",
ui/src/app/widget/lib/alarms-table-widget.js 190(+176 -14)
diff --git a/ui/src/app/widget/lib/alarms-table-widget.js b/ui/src/app/widget/lib/alarms-table-widget.js
index 0696a7b..6ae17e0 100644
--- a/ui/src/app/widget/lib/alarms-table-widget.js
+++ b/ui/src/app/widget/lib/alarms-table-widget.js
@@ -14,11 +14,15 @@
* limitations under the License.
*/
import './alarms-table-widget.scss';
+import './display-columns-panel.scss';
+import './alarm-status-filter-panel.scss';
/* eslint-disable import/no-unresolved, import/default */
import alarmsTableWidgetTemplate from './alarms-table-widget.tpl.html';
import alarmDetailsDialogTemplate from '../../alarm/alarm-details-dialog.tpl.html';
+import displayColumnsPanelTemplate from './display-columns-panel.tpl.html';
+import alarmStatusFilterPanelTemplate from './alarm-status-filter-panel.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
@@ -45,7 +49,7 @@ function AlarmsTableWidget() {
}
/*@ngInject*/
-function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDialog, $document, $translate, $q, $timeout, alarmService, utils, types) {
+function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDialog, $mdPanel, $document, $translate, $q, $timeout, alarmService, utils, types) {
var vm = this;
vm.stylesInfo = {};
@@ -60,6 +64,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
vm.selectedAlarms = []
vm.alarmSource = null;
+ vm.alarmSearchStatus = null;
vm.allAlarms = [];
vm.currentAlarm = null;
@@ -95,14 +100,20 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
vm.onPaginate = onPaginate;
vm.onRowClick = onRowClick;
vm.onActionButtonClick = onActionButtonClick;
+ vm.actionEnabled = actionEnabled;
vm.isCurrent = isCurrent;
vm.openAlarmDetails = openAlarmDetails;
vm.ackAlarms = ackAlarms;
+ vm.ackAlarm = ackAlarm;
vm.clearAlarms = clearAlarms;
+ vm.clearAlarm = clearAlarm;
vm.cellStyle = cellStyle;
vm.cellContent = cellContent;
+ vm.editAlarmStatusFilter = editAlarmStatusFilter;
+ vm.editColumnsToDisplay = editColumnsToDisplay;
+
$scope.$watch('vm.ctx', function() {
if (vm.ctx) {
vm.settings = vm.ctx.settings;
@@ -158,7 +169,41 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
vm.ctx.widgetActions = [ vm.searchAction ];
- vm.actionCellDescriptors = vm.ctx.actionsApi.getActionDescriptors('actionCellButton');
+ 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;
+
+ if (vm.displayDetails) {
+ vm.actionCellDescriptors.push(
+ {
+ displayName: $translate.instant('alarm.details'),
+ icon: 'more_horiz',
+ details: true
+ }
+ );
+ }
+
+ if (vm.allowAcknowledgment) {
+ vm.actionCellDescriptors.push(
+ {
+ displayName: $translate.instant('alarm.acknowledge'),
+ icon: 'done',
+ acknowledge: true
+ }
+ );
+ }
+
+ if (vm.allowClear) {
+ vm.actionCellDescriptors.push(
+ {
+ displayName: $translate.instant('alarm.clear'),
+ icon: 'clear',
+ clear: true
+ }
+ );
+ }
+
+ vm.actionCellDescriptors = vm.actionCellDescriptors.concat(vm.ctx.actionsApi.getActionDescriptors('actionCellButton'));
if (vm.settings.alarmsTitle && vm.settings.alarmsTitle.length) {
vm.alarmsTitle = utils.customTranslation(vm.settings.alarmsTitle, vm.settings.alarmsTitle);
@@ -170,9 +215,6 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
vm.enableSelection = angular.isDefined(vm.settings.enableSelection) ? vm.settings.enableSelection : 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;
if (!vm.allowAcknowledgment && !vm.allowClear) {
vm.enableSelection = false;
}
@@ -305,16 +347,35 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
}
function onActionButtonClick($event, alarm, actionDescriptor) {
- if ($event) {
- $event.stopPropagation();
+ if (actionDescriptor.details) {
+ vm.openAlarmDetails($event, alarm);
+ } else if (actionDescriptor.acknowledge) {
+ vm.ackAlarm($event, alarm);
+ } else if (actionDescriptor.clear) {
+ vm.clearAlarm($event, alarm);
+ } else {
+ if ($event) {
+ $event.stopPropagation();
+ }
+ var entityId;
+ var entityName;
+ if (alarm && alarm.originator) {
+ entityId = alarm.originator;
+ entityName = alarm.originatorName;
+ }
+ vm.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName, {alarm: alarm});
}
- var entityId;
- var entityName;
- if (alarm && alarm.originator) {
- entityId = alarm.originator;
- entityName = alarm.originatorName;
+ }
+
+ function actionEnabled(alarm, actionDescriptor) {
+ if (actionDescriptor.acknowledge) {
+ return (alarm.status == types.alarmStatus.activeUnack ||
+ alarm.status == types.alarmStatus.clearedUnack);
+ } else if (actionDescriptor.clear) {
+ return (alarm.status == types.alarmStatus.activeAck ||
+ alarm.status == types.alarmStatus.activeUnack);
}
- vm.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName, { alarm: alarm });
+ return true;
}
function isCurrent(alarm) {
@@ -387,6 +448,25 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
}
}
+ function ackAlarm($event, alarm) {
+ if ($event) {
+ $event.stopPropagation();
+ }
+ var confirm = $mdDialog.confirm()
+ .targetEvent($event)
+ .title($translate.instant('alarm.aknowledge-alarm-title'))
+ .htmlContent($translate.instant('alarm.aknowledge-alarm-text'))
+ .ariaLabel($translate.instant('alarm.acknowledge'))
+ .cancel($translate.instant('action.no'))
+ .ok($translate.instant('action.yes'));
+ $mdDialog.show(confirm).then(function () {
+ alarmService.ackAlarm(alarm.id.id).then(function () {
+ vm.selectedAlarms = [];
+ vm.subscription.update();
+ });
+ });
+ }
+
function clearAlarms($event) {
if ($event) {
$event.stopPropagation();
@@ -420,6 +500,24 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
}
}
+ function clearAlarm($event, alarm) {
+ if ($event) {
+ $event.stopPropagation();
+ }
+ var confirm = $mdDialog.confirm()
+ .targetEvent($event)
+ .title($translate.instant('alarm.clear-alarm-title'))
+ .htmlContent($translate.instant('alarm.clear-alarm-text'))
+ .ariaLabel($translate.instant('alarm.clear'))
+ .cancel($translate.instant('action.no'))
+ .ok($translate.instant('action.yes'));
+ $mdDialog.show(confirm).then(function () {
+ alarmService.clearAlarm(alarm.id.id).then(function () {
+ vm.selectedAlarms = [];
+ vm.subscription.update();
+ });
+ });
+ }
function updateAlarms(preserveSelections) {
if (!preserveSelections) {
@@ -558,6 +656,54 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
}
}
+ function editAlarmStatusFilter($event) {
+ var element = angular.element($event.target);
+ var position = $mdPanel.newPanelPosition()
+ .relativeTo(element)
+ .addPanelPosition($mdPanel.xPosition.ALIGN_END, $mdPanel.yPosition.BELOW);
+ var config = {
+ attachTo: angular.element($document[0].body),
+ controller: AlarmStatusFilterPanelController,
+ controllerAs: 'vm',
+ templateUrl: alarmStatusFilterPanelTemplate,
+ panelClass: 'tb-alarm-status-filter-panel',
+ position: position,
+ fullscreen: false,
+ locals: {
+ 'subscription': vm.subscription
+ },
+ openFrom: $event,
+ clickOutsideToClose: true,
+ escapeToClose: true,
+ focusOnOpen: false
+ };
+ $mdPanel.open(config);
+ }
+
+ function editColumnsToDisplay($event) {
+ var element = angular.element($event.target);
+ var position = $mdPanel.newPanelPosition()
+ .relativeTo(element)
+ .addPanelPosition($mdPanel.xPosition.ALIGN_END, $mdPanel.yPosition.BELOW);
+ var config = {
+ attachTo: angular.element($document[0].body),
+ controller: DisplayColumnsPanelController,
+ controllerAs: 'vm',
+ templateUrl: displayColumnsPanelTemplate,
+ panelClass: 'tb-display-columns-panel',
+ position: position,
+ fullscreen: false,
+ locals: {
+ 'columns': vm.alarmSource.dataKeys
+ },
+ openFrom: $event,
+ clickOutsideToClose: true,
+ escapeToClose: true,
+ focusOnOpen: false
+ };
+ $mdPanel.open(config);
+ }
+
function updateAlarmSource() {
vm.ctx.widgetTitle = utils.createLabelFromDatasource(vm.alarmSource, vm.alarmsTitle);
@@ -570,6 +716,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
var dataKey = vm.alarmSource.dataKeys[d];
dataKey.title = utils.customTranslation(dataKey.label, dataKey.label);
+ dataKey.display = true;
var keySettings = dataKey.settings;
@@ -618,4 +765,19 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
}
}
-}
\ No newline at end of file
+}
+
+/*@ngInject*/
+function DisplayColumnsPanelController(columns) { //eslint-disable-line
+
+ var vm = this;
+ vm.columns = columns;
+}
+
+/*@ngInject*/
+function AlarmStatusFilterPanelController(subscription, types) { //eslint-disable-line
+
+ var vm = this;
+ vm.types = types;
+ vm.subscription = subscription;
+}
diff --git a/ui/src/app/widget/lib/alarms-table-widget.scss b/ui/src/app/widget/lib/alarms-table-widget.scss
index 2922996..a031523 100644
--- a/ui/src/app/widget/lib/alarms-table-widget.scss
+++ b/ui/src/app/widget/lib/alarms-table-widget.scss
@@ -44,6 +44,27 @@
&.tb-data-table {
table.md-table,
table.md-table.md-row-select {
+ th.md-column {
+ &.tb-action-cell {
+ .md-button {
+ /* stylelint-disable-next-line selector-max-class */
+ &.md-icon-button {
+ width: 36px;
+ height: 36px;
+ padding: 6px;
+ margin: 0;
+ /* stylelint-disable-next-line selector-max-class */
+ md-icon {
+ width: 24px;
+ height: 24px;
+ font-size: 24px !important;
+ line-height: 24px !important;
+ }
+ }
+ }
+ }
+ }
+
tbody {
tr {
td {
@@ -51,6 +72,15 @@
width: 36px;
min-width: 36px;
max-width: 36px;
+
+ .md-button[disabled] {
+ &.md-icon-button {
+ /* stylelint-disable-next-line selector-max-class */
+ md-icon {
+ color: rgba(0, 0, 0, .38);
+ }
+ }
+ }
}
}
}
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 8480058..39843e1 100644
--- a/ui/src/app/widget/lib/alarms-table-widget.tpl.html
+++ b/ui/src/app/widget/lib/alarms-table-widget.tpl.html
@@ -62,33 +62,45 @@
<table md-table md-row-select="vm.enableSelection" multiple="" ng-model="vm.selectedAlarms">
<thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder">
<tr md-row>
- <th md-column md-order-by="{{ key.name }}" ng-repeat="key in vm.alarmSource.dataKeys"><span>{{ key.title }}</span></th>
- <th md-column ng-if="vm.displayDetails"><span> </span></th>
- <th md-column ng-if="vm.actionCellDescriptors.length"><span> </span></th>
+ <th ng-if="key.display" md-column md-order-by="{{ key.name }}" ng-repeat="key in vm.alarmSource.dataKeys"><span>{{ key.title }}</span></th>
+ <th md-column class="tb-action-cell" layout="row" layout-align="end center">
+ <md-button class="md-icon-button"
+ aria-label="{{'alarm.alarm-status-filter' | translate}}"
+ ng-click="vm.editAlarmStatusFilter($event)">
+ <md-icon aria-label="{{'alarm.alarm-status-filter' | translate}}"
+ class="material-icons">filter_list
+ </md-icon>
+ <md-tooltip md-direction="top">
+ {{'alarm.alarm-status-filter' | translate}}
+ </md-tooltip>
+ </md-button>
+ <md-button class="md-icon-button"
+ aria-label="{{'entity.columns-to-display' | translate}}"
+ ng-click="vm.editColumnsToDisplay($event)">
+ <md-icon aria-label="{{'entity.columns-to-display' | translate}}"
+ class="material-icons">view_column
+ </md-icon>
+ <md-tooltip md-direction="top">
+ {{'entity.columns-to-display' | translate}}
+ </md-tooltip>
+ </md-button>
+ </th>
</tr>
</thead>
<tbody md-body>
<tr ng-show="vm.alarms.length" md-row md-select="alarm"
md-select-id="id.id" md-auto-select="false" ng-repeat="alarm in vm.alarms"
ng-click="vm.onRowClick($event, alarm)" ng-class="{'tb-current': vm.isCurrent(alarm)}">
- <td md-cell flex ng-repeat="key in vm.alarmSource.dataKeys"
+ <td ng-if="key.display" md-cell flex ng-repeat="key in vm.alarmSource.dataKeys"
ng-style="vm.cellStyle(alarm, key)"
ng-bind-html="vm.cellContent(alarm, key)">
</td>
- <td md-cell ng-if="vm.displayDetails" class="tb-action-cell">
- <md-button class="md-icon-button" aria-label="{{ 'alarm.details' | translate }}"
- ng-click="vm.openAlarmDetails($event, alarm)" ng-disabled="$root.loading">
- <md-icon aria-label="{{ 'alarm.details' | translate }}" class="material-icons">more_horiz</md-icon>
- <md-tooltip md-direction="top">
- {{ 'alarm.details' | translate }}
- </md-tooltip>
- </md-button>
- </td>
- <td md-cell ng-if="vm.actionCellDescriptors.length" class="tb-action-cell"
+ <td md-cell 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"
+ ng-disabled="!vm.actionEnabled(alarm, actionDescriptor)"
aria-label="{{ actionDescriptor.displayName }}"
ng-click="vm.onActionButtonClick($event, alarm, actionDescriptor)" ng-disabled="$root.loading">
<md-icon aria-label="{{ actionDescriptor.displayName }}" class="material-icons">{{actionDescriptor.icon}}</md-icon>
diff --git a/ui/src/app/widget/lib/alarm-status-filter-panel.scss b/ui/src/app/widget/lib/alarm-status-filter-panel.scss
new file mode 100644
index 0000000..4bf6eee
--- /dev/null
+++ b/ui/src/app/widget/lib/alarm-status-filter-panel.scss
@@ -0,0 +1,31 @@
+/**
+ * Copyright © 2016-2018 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-alarm-status-filter-panel {
+ min-width: 300px;
+ overflow: hidden;
+ background: #fff;
+ border-radius: 4px;
+ box-shadow:
+ 0 7px 8px -4px rgba(0, 0, 0, .2),
+ 0 13px 19px 2px rgba(0, 0, 0, .14),
+ 0 5px 24px 4px rgba(0, 0, 0, .12);
+
+ md-content {
+ overflow: hidden;
+ background-color: #fff;
+ }
+}
diff --git a/ui/src/app/widget/lib/alarm-status-filter-panel.tpl.html b/ui/src/app/widget/lib/alarm-status-filter-panel.tpl.html
new file mode 100644
index 0000000..45e8414
--- /dev/null
+++ b/ui/src/app/widget/lib/alarm-status-filter-panel.tpl.html
@@ -0,0 +1,28 @@
+<!--
+
+ Copyright © 2016-2018 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.
+
+-->
+
+<md-content style="height: 100%" flex layout="column" class="md-padding">
+ <label class="tb-title" translate>alarm.alarm-status-filter</label>
+ <md-radio-group ng-model="vm.subscription.alarmSearchStatus" class="md-primary">
+ <md-radio-button ng-value="searchStatus"
+ aria-label="{{ ('alarm.search-status.' + searchStatus) | translate }}"
+ class="md-primary md-align-top-left md-radio-interactive" ng-repeat="searchStatus in vm.types.alarmSearchStatus">
+ {{ ('alarm.search-status.' + searchStatus) | translate }}
+ </md-radio-button>
+ </md-radio-group>
+</md-content>
diff --git a/ui/src/app/widget/lib/display-columns-panel.scss b/ui/src/app/widget/lib/display-columns-panel.scss
new file mode 100644
index 0000000..2e518cb
--- /dev/null
+++ b/ui/src/app/widget/lib/display-columns-panel.scss
@@ -0,0 +1,31 @@
+/**
+ * Copyright © 2016-2018 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-display-columns-panel {
+ min-width: 300px;
+ overflow: hidden;
+ background: #fff;
+ border-radius: 4px;
+ box-shadow:
+ 0 7px 8px -4px rgba(0, 0, 0, .2),
+ 0 13px 19px 2px rgba(0, 0, 0, .14),
+ 0 5px 24px 4px rgba(0, 0, 0, .12);
+
+ md-content {
+ overflow: hidden;
+ background-color: #fff;
+ }
+}
diff --git a/ui/src/app/widget/lib/display-columns-panel.tpl.html b/ui/src/app/widget/lib/display-columns-panel.tpl.html
new file mode 100644
index 0000000..42e2471
--- /dev/null
+++ b/ui/src/app/widget/lib/display-columns-panel.tpl.html
@@ -0,0 +1,24 @@
+<!--
+
+ Copyright © 2016-2018 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.
+
+-->
+
+<md-content style="height: 100%" flex layout="column" class="md-padding">
+ <label class="tb-title" translate>entity.columns-to-display</label>
+ <md-checkbox aria-label="{{ 'entity.columns-to-display' | translate }}" ng-repeat="column in vm.columns"
+ ng-model="column.display">{{ column.title }}
+ </md-checkbox>
+</md-content>
diff --git a/ui/src/app/widget/lib/entities-table-widget.js b/ui/src/app/widget/lib/entities-table-widget.js
index d0b629d..620f313 100644
--- a/ui/src/app/widget/lib/entities-table-widget.js
+++ b/ui/src/app/widget/lib/entities-table-widget.js
@@ -14,11 +14,13 @@
* limitations under the License.
*/
import './entities-table-widget.scss';
+import './display-columns-panel.scss';
/* eslint-disable import/no-unresolved, import/default */
import entitiesTableWidgetTemplate from './entities-table-widget.tpl.html';
//import entityDetailsDialogTemplate from './entitiy-details-dialog.tpl.html';
+import displayColumnsPanelTemplate from './display-columns-panel.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
@@ -45,7 +47,7 @@ function EntitiesTableWidget() {
}
/*@ngInject*/
-function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $translate, $timeout, utils, types) {
+function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $mdPanel, $document, $translate, $timeout, utils, types) {
var vm = this;
vm.stylesInfo = {};
@@ -98,6 +100,8 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
vm.cellStyle = cellStyle;
vm.cellContent = cellContent;
+ vm.editColumnsToDisplay = editColumnsToDisplay;
+
$scope.$watch('vm.ctx', function() {
if (vm.ctx && vm.ctx.defaultSubscription) {
vm.settings = vm.ctx.settings;
@@ -414,12 +418,37 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
}
}
+ function editColumnsToDisplay($event) {
+ var element = angular.element($event.target);
+ var position = $mdPanel.newPanelPosition()
+ .relativeTo(element)
+ .addPanelPosition($mdPanel.xPosition.ALIGN_END, $mdPanel.yPosition.BELOW);
+ var config = {
+ attachTo: angular.element($document[0].body),
+ controller: DisplayColumnsPanelController,
+ controllerAs: 'vm',
+ templateUrl: displayColumnsPanelTemplate,
+ panelClass: 'tb-display-columns-panel',
+ position: position,
+ fullscreen: false,
+ locals: {
+ 'columns': vm.columns
+ },
+ openFrom: $event,
+ clickOutsideToClose: true,
+ escapeToClose: true,
+ focusOnOpen: false
+ };
+ $mdPanel.open(config);
+ }
+
function updateDatasources() {
vm.stylesInfo = {};
vm.contentsInfo = {};
vm.columnWidth = {};
vm.dataKeys = [];
+ vm.columns = [];
vm.allEntities = [];
var datasource;
@@ -429,6 +458,42 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
vm.ctx.widgetTitle = utils.createLabelFromDatasource(datasource, vm.entitiesTitle);
+ if (vm.displayEntityName) {
+ vm.columns.push(
+ {
+ name: 'entityName',
+ label: 'entityName',
+ title: vm.entityNameColumnTitle,
+ display: true
+ }
+ );
+ vm.contentsInfo['entityName'] = {
+ useCellContentFunction: false
+ };
+ vm.stylesInfo['entityName'] = {
+ useCellStyleFunction: false
+ };
+ vm.columnWidth['entityName'] = '0px';
+ }
+
+ if (vm.displayEntityType) {
+ vm.columns.push(
+ {
+ name: 'entityType',
+ label: 'entityType',
+ title: $translate.instant('entity.entity-type'),
+ display: true
+ }
+ );
+ vm.contentsInfo['entityType'] = {
+ useCellContentFunction: false
+ };
+ vm.stylesInfo['entityType'] = {
+ useCellStyleFunction: false
+ };
+ vm.columnWidth['entityType'] = '0px';
+ }
+
for (var d = 0; d < datasource.dataKeys.length; d++ ) {
dataKey = angular.copy(datasource.dataKeys[d]);
if (dataKey.type == types.dataKeyType.function) {
@@ -482,6 +547,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
var columnWidth = angular.isDefined(keySettings.columnWidth) ? keySettings.columnWidth : '0px';
vm.columnWidth[dataKey.label] = columnWidth;
+
+ dataKey.display = true;
+ vm.columns.push(dataKey);
}
for (var i=0;i<vm.datasources.length;i++) {
@@ -511,4 +579,11 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
}
-}
\ No newline at end of file
+}
+
+/*@ngInject*/
+function DisplayColumnsPanelController(columns) { //eslint-disable-line
+
+ var vm = this;
+ vm.columns = columns;
+}
diff --git a/ui/src/app/widget/lib/entities-table-widget.scss b/ui/src/app/widget/lib/entities-table-widget.scss
index 85648d6..d745529 100644
--- a/ui/src/app/widget/lib/entities-table-widget.scss
+++ b/ui/src/app/widget/lib/entities-table-widget.scss
@@ -44,6 +44,27 @@
&.tb-data-table {
table.md-table,
table.md-table.md-row-select {
+ th.md-column {
+ &.tb-action-cell {
+ .md-button {
+ /* stylelint-disable-next-line selector-max-class */
+ &.md-icon-button {
+ width: 36px;
+ height: 36px;
+ padding: 6px;
+ margin: 0;
+ /* stylelint-disable-next-line selector-max-class */
+ md-icon {
+ width: 24px;
+ height: 24px;
+ font-size: 24px !important;
+ line-height: 24px !important;
+ }
+ }
+ }
+ }
+ }
+
tbody {
tr {
td {
@@ -51,6 +72,15 @@
width: 36px;
min-width: 36px;
max-width: 36px;
+
+ .md-button[disabled] {
+ &.md-icon-button {
+ /* stylelint-disable-next-line selector-max-class */
+ md-icon {
+ color: rgba(0, 0, 0, .38);
+ }
+ }
+ }
}
}
}
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 474d536..66932a0 100644
--- a/ui/src/app/widget/lib/entities-table-widget.tpl.html
+++ b/ui/src/app/widget/lib/entities-table-widget.tpl.html
@@ -41,23 +41,30 @@
<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>{{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.actionCellDescriptors.length"><span> </span></th>
+ <th ng-if="column.display" md-column md-order-by="{{ column.name }}" ng-repeat="column in vm.columns"><span>{{ column.title }}</span></th>
+ <th md-column class="tb-action-cell" layout="row" layout-align="end center">
+ <md-button class="md-icon-button"
+ aria-label="{{'entity.columns-to-display' | translate}}"
+ ng-click="vm.editColumnsToDisplay($event)">
+ <md-icon aria-label="{{'entity.columns-to-display' | translate}}"
+ class="material-icons">view_column
+ </md-icon>
+ <md-tooltip md-direction="top">
+ {{'entity.columns-to-display' | translate}}
+ </md-tooltip>
+ </md-button>
+ </th>
</tr>
</thead>
<tbody md-body>
<tr ng-show="vm.entities.length" md-row md-select="entity"
md-select-id="id.id" md-auto-select="false" ng-repeat="entity in vm.entities"
ng-click="vm.onRowClick($event, entity)" ng-class="{'tb-current': vm.isCurrent(entity)}">
- <td md-cell flex ng-if="vm.displayEntityName">{{entity.entityName}}</td>
- <td md-cell flex ng-if="vm.displayEntityType">{{entity.entityType}}</td>
- <td md-cell flex ng-repeat="key in vm.dataKeys"
- ng-style="vm.cellStyle(entity, key)"
- ng-bind-html="vm.cellContent(entity, key)">
+ <td ng-if="column.display" md-cell flex ng-repeat="column in vm.columns"
+ ng-style="vm.cellStyle(entity, column)"
+ ng-bind-html="vm.cellContent(entity, column)">
</td>
- <td md-cell ng-if="vm.actionCellDescriptors.length" class="tb-action-cell"
+ <td md-cell class="tb-action-cell"
ng-style="{minWidth: vm.actionCellDescriptors.length*36+'px',
maxWidth: vm.actionCellDescriptors.length*36+'px',
width: vm.actionCellDescriptors.length*36+'px'}">