thingsboard-aplcache
Changes
ui/src/app/api/data-aggregator.js 10(+5 -5)
ui/src/app/api/datasource.service.js 59(+25 -34)
ui/src/app/api/time.service.js 6(+3 -3)
ui/src/app/components/dashboard.directive.js 35(+32 -3)
ui/src/app/components/dashboard.scss 3(+2 -1)
ui/src/app/components/dashboard.tpl.html 35(+18 -17)
ui/src/app/components/timewindow.tpl.html 22(+18 -4)
ui/src/app/components/widget.controller.js 100(+77 -23)
ui/src/app/components/widget.scss 4(+4 -0)
ui/src/app/dashboard/dashboard.controller.js 34(+30 -4)
ui/src/app/dashboard/dashboard.scss 80(+80 -0)
ui/src/app/dashboard/dashboard.tpl.html 482(+255 -227)
ui/src/app/locale/locale.constant.js 6(+5 -1)
ui/src/app/widget/lib/flot-widget.js 4(+4 -0)
ui/src/scss/main.scss 10(+10 -0)
Details
ui/src/app/api/data-aggregator.js 10(+5 -5)
diff --git a/ui/src/app/api/data-aggregator.js b/ui/src/app/api/data-aggregator.js
index 31baff1..76d3b1c 100644
--- a/ui/src/app/api/data-aggregator.js
+++ b/ui/src/app/api/data-aggregator.js
@@ -74,7 +74,7 @@ export default class DataAggregator {
}, this.aggregationTimeout, false);
}
- onData(data, update, history) {
+ onData(data, update, history, apply) {
if (!this.dataReceived || this.resetPending) {
var updateIntervalScheduledTime = true;
if (!this.dataReceived) {
@@ -96,18 +96,18 @@ export default class DataAggregator {
if (updateIntervalScheduledTime) {
this.intervalScheduledTime = currentTime();
}
- this.onInterval(history);
+ this.onInterval(history, apply);
} else {
updateAggregatedData(this.aggregationMap, this.aggregationType === this.types.aggregation.count.value,
this.noAggregation, this.aggFunction, data.data, this.interval, this.startTs);
if (history) {
this.intervalScheduledTime = currentTime();
- this.onInterval(history);
+ this.onInterval(history, apply);
}
}
}
- onInterval(history) {
+ onInterval(history, apply) {
var now = currentTime();
this.elapsed += now - this.intervalScheduledTime;
this.intervalScheduledTime = now;
@@ -127,7 +127,7 @@ export default class DataAggregator {
this.data = toData(this.tsKeyNames, this.aggregationMap, this.startTs, this.endTs, this.$filter, this.limit);
}
if (this.onDataCb) {
- this.onDataCb(this.data, this.startTs, this.endTs);
+ this.onDataCb(this.data, this.startTs, this.endTs, apply);
}
var self = this;
ui/src/app/api/datasource.service.js 59(+25 -34)
diff --git a/ui/src/app/api/datasource.service.js b/ui/src/app/api/datasource.service.js
index 7d2c1be..5458ada 100644
--- a/ui/src/app/api/datasource.service.js
+++ b/ui/src/app/api/datasource.service.js
@@ -197,7 +197,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
var datasourceKey = key + '_' + i;
listener.dataUpdated(datasourceData[datasourceKey],
listener.datasourceIndex,
- dataKey.index);
+ dataKey.index, false);
}
}
} else {
@@ -205,7 +205,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
dataKey = dataKeys[key];
listener.dataUpdated(datasourceData[key],
listener.datasourceIndex,
- dataKey.index);
+ dataKey.index, false);
}
}
}
@@ -264,7 +264,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
type: types.dataKeyType.timeseries,
onData: function (data) {
if (data.data) {
- onData(data.data, types.dataKeyType.timeseries);
+ onData(data.data, types.dataKeyType.timeseries, null, null, true);
}
},
onReconnected: function() {}
@@ -287,9 +287,9 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
if (datasourceSubscription.type === types.widgetType.timeseries.value) {
updateRealtimeSubscriptionCommand(subscriptionCommand, subsTw);
- dataAggregator = createRealtimeDataAggregator(subsTw, tsKeyNames);
+ dataAggregator = createRealtimeDataAggregator(subsTw, tsKeyNames, types.dataKeyType.timeseries);
subscriber.onData = function(data) {
- dataAggregator.onData(data);
+ dataAggregator.onData(data, false, false, true);
}
subscriber.onReconnected = function() {
var newSubsTw = null;
@@ -308,7 +308,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
subscriber.onReconnected = function() {}
subscriber.onData = function(data) {
if (data.data) {
- onData(data.data, types.dataKeyType.timeseries);
+ onData(data.data, types.dataKeyType.timeseries, null, null, true);
}
}
}
@@ -331,7 +331,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
type: types.dataKeyType.attribute,
onData: function (data) {
if (data.data) {
- onData(data.data, types.dataKeyType.attribute);
+ onData(data.data, types.dataKeyType.attribute, null, null, true);
}
},
onReconnected: function() {}
@@ -351,33 +351,24 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
tsKeyNames.push(dataKey.name+'_'+dataKey.index);
}
}
- dataAggregator = new DataAggregator(
- function (data, startTs, endTs) {
- onData(data, types.dataKeyType.function, startTs, endTs);
- },
- tsKeyNames,
- subsTw.startTs,
- subsTw.aggregation.limit,
- subsTw.aggregation.type,
- subsTw.aggregation.timeWindow,
- subsTw.aggregation.interval,
- types,
- $timeout,
- $filter
- );
+ dataAggregator = createRealtimeDataAggregator(subsTw, tsKeyNames, types.dataKeyType.function);
}
if (history) {
- onTick();
+ onTick(false);
} else {
- timer = $timeout(onTick, 0, false);
+ timer = $timeout(
+ function() {onTick(true)},
+ 0,
+ false
+ );
}
}
}
- function createRealtimeDataAggregator(subsTw, tsKeyNames) {
+ function createRealtimeDataAggregator(subsTw, tsKeyNames, dataKeyType) {
return new DataAggregator(
- function(data, startTs, endTs) {
- onData(data, types.dataKeyType.timeseries, startTs, endTs);
+ function(data, startTs, endTs, apply) {
+ onData(data, dataKeyType, startTs, endTs, apply);
},
tsKeyNames,
subsTw.startTs,
@@ -443,7 +434,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
return data;
}
- function generateLatest(dataKey) {
+ function generateLatest(dataKey, apply) {
var prevSeries;
var datasourceKeyData = datasourceData[dataKey.key].data;
if (datasourceKeyData.length > 0) {
@@ -461,11 +452,11 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
var listener = listeners[i];
listener.dataUpdated(datasourceData[dataKey.key],
listener.datasourceIndex,
- dataKey.index);
+ dataKey.index, apply);
}
}
- function onTick() {
+ function onTick(apply) {
var key;
if (datasourceSubscription.type === types.widgetType.timeseries.value) {
var startTime;
@@ -495,15 +486,15 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
generatedData.data[dataKey.name+'_'+dataKey.index] = data;
}
}
- dataAggregator.onData(generatedData, true, history);
+ dataAggregator.onData(generatedData, true, history, apply);
} else if (datasourceSubscription.type === types.widgetType.latest.value) {
for (key in dataKeys) {
- generateLatest(dataKeys[key]);
+ generateLatest(dataKeys[key], apply);
}
}
if (!history) {
- timer = $timeout(onTick, frequency / 2, false);
+ timer = $timeout(function() {onTick(true)}, frequency / 2, false);
}
}
@@ -519,7 +510,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
}
}
- function onData(sourceData, type, startTs, endTs) {
+ function onData(sourceData, type, startTs, endTs, apply) {
for (var keyName in sourceData) {
var keyData = sourceData[keyName];
var key = keyName + '_' + type;
@@ -572,7 +563,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
var listener = listeners[i2];
listener.dataUpdated(datasourceData[datasourceKey],
listener.datasourceIndex,
- dataKey.index);
+ dataKey.index, apply);
}
}
}
ui/src/app/api/time.service.js 6(+3 -3)
diff --git a/ui/src/app/api/time.service.js b/ui/src/app/api/time.service.js
index a4bb571..14f4c63 100644
--- a/ui/src/app/api/time.service.js
+++ b/ui/src/app/api/time.service.js
@@ -206,9 +206,9 @@ function TimeService($translate, types) {
function defaultTimewindow() {
var currentTime = (new Date).getTime();
var timewindow = {
- displayValue: "",
- selectedTab: 0,
- realtime: {
+ displayValue: "",
+ selectedTab: 0,
+ realtime: {
interval: SECOND,
timewindowMs: MINUTE // 1 min by default
},
ui/src/app/components/dashboard.directive.js 35(+32 -3)
diff --git a/ui/src/app/components/dashboard.directive.js b/ui/src/app/components/dashboard.directive.js
index 3889339..1141add 100644
--- a/ui/src/app/components/dashboard.directive.js
+++ b/ui/src/app/components/dashboard.directive.js
@@ -52,6 +52,7 @@ function Dashboard() {
bindToController: {
widgets: '=',
deviceAliasList: '=',
+ dashboardTimewindow: '=?',
columns: '=',
margins: '=',
isEdit: '=',
@@ -71,7 +72,8 @@ function Dashboard() {
getStDiff: '&?',
onInit: '&?',
onInitFailed: '&?',
- dashboardStyle: '=?'
+ dashboardStyle: '=?',
+ dashboardClass: '=?'
},
controller: DashboardController,
controllerAs: 'vm',
@@ -80,7 +82,7 @@ function Dashboard() {
}
/*@ngInject*/
-function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $log, toast, types) {
+function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, timeService, types) {
var highlightedMode = false;
var highlightedWidget = null;
@@ -99,6 +101,10 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
vm.isMobileDisabled = angular.isDefined(vm.isMobileDisabled) ? vm.isMobileDisabled : false;
+ if (!('dashboardTimewindow' in vm)) {
+ vm.dashboardTimewindow = timeService.defaultTimewindow();
+ }
+
vm.dashboardLoading = true;
vm.visibleRect = {
top: 0,
@@ -176,6 +182,21 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
vm.widgetContextMenuItems = [];
vm.widgetContextMenuEvent = null;
+ vm.dashboardTimewindowApi = {
+ onResetTimewindow: function() {
+ if (vm.originalDashboardTimewindow) {
+ vm.dashboardTimewindow = angular.copy(vm.originalDashboardTimewindow);
+ vm.originalDashboardTimewindow = null;
+ }
+ },
+ onUpdateTimewindow: function(startTimeMs, endTimeMs) {
+ if (!vm.originalDashboardTimewindow) {
+ vm.originalDashboardTimewindow = angular.copy(vm.dashboardTimewindow);
+ }
+ vm.dashboardTimewindow = timeService.toHistoryTimewindow(vm.dashboardTimewindow, startTimeMs, endTimeMs);
+ }
+ };
+
//$element[0].onmousemove=function(){
// widgetMouseMove();
// }
@@ -656,7 +677,12 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
}
function hasTimewindow(widget) {
- return widget.type === types.widgetType.timeseries.value;
+ if (widget.type === types.widgetType.timeseries.value) {
+ return angular.isDefined(widget.config.useDashboardTimewindow) ?
+ !widget.config.useDashboardTimewindow : false;
+ } else {
+ return false;
+ }
}
function adoptMaxRows() {
@@ -673,6 +699,9 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
function dashboardLoaded() {
$timeout(function () {
+ $scope.$watch('vm.dashboardTimewindow', function () {
+ $scope.$broadcast('dashboardTimewindowChanged', vm.dashboardTimewindow);
+ }, true);
adoptMaxRows();
vm.dashboardLoading = false;
$timeout(function () {
ui/src/app/components/dashboard.scss 3(+2 -1)
diff --git a/ui/src/app/components/dashboard.scss b/ui/src/app/components/dashboard.scss
index 4c4af47..cf56c15 100644
--- a/ui/src/app/components/dashboard.scss
+++ b/ui/src/app/components/dashboard.scss
@@ -51,7 +51,7 @@ div.tb-widget {
height: 32px;
min-width: 32px;
min-height: 32px;
- md-icon {
+ md-icon, ng-md-icon {
width: 20px;
height: 20px;
min-width: 20px;
@@ -93,6 +93,7 @@ md-content.tb-dashboard-content {
right: 0;
bottom: 0;
outline: none;
+ background: none;
.gridster-item {
@include transition(none);
}
ui/src/app/components/dashboard.tpl.html 35(+18 -17)
diff --git a/ui/src/app/components/dashboard.tpl.html b/ui/src/app/components/dashboard.tpl.html
index dc46e35..114460d 100644
--- a/ui/src/app/components/dashboard.tpl.html
+++ b/ui/src/app/components/dashboard.tpl.html
@@ -21,7 +21,7 @@
</md-content>
<md-menu md-position-mode="target target" tb-mousepoint-menu>
<md-content id="gridster-parent" class="tb-dashboard-content" flex layout-wrap ng-click="" tb-contextmenu="vm.openDashboardContextMenu($event, $mdOpenMousepointMenu)">
- <div ng-style="vm.dashboardStyle" id="gridster-background" style="height: auto; min-height: 100%;">
+ <div ng-class="vm.dashboardClass" id="gridster-background" style="height: auto; min-height: 100%;">
<div id="gridster-child" gridster="vm.gridsterOpts">
<ul>
<!-- ng-click="widgetClicked($event, widget)" -->
@@ -30,6 +30,7 @@
<div tb-expand-fullscreen
fullscreen-background-style="vm.dashboardStyle"
expand-button-id="expand-button"
+ expand-button-size="20"
on-fullscreen-changed="vm.onWidgetFullscreenChanged(expanded, widget)"
layout="column"
class="tb-widget"
@@ -45,55 +46,55 @@
color: vm.widgetColor(widget),
backgroundColor: vm.widgetBackgroundColor(widget),
padding: vm.widgetPadding(widget)}">
- <div class="tb-widget-title" layout="column" ng-show="vm.showWidgetTitle(widget) || vm.hasTimewindow(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>
- <tb-timewindow button-color="vm.widgetColor(widget)" aggregation ng-if="vm.hasTimewindow(widget)" ng-model="widget.config.timewindow"></tb-timewindow>
+ <tb-timewindow aggregation ng-if="vm.hasTimewindow(widget)" ng-model="widget.config.timewindow"></tb-timewindow>
</div>
<div class="tb-widget-actions" layout="row" layout-align="start center">
<md-button id="expand-button"
ng-show="!vm.isEdit && vm.enableWidgetFullscreen(widget)"
aria-label="{{ 'fullscreen.fullscreen' | translate }}"
- class="md-icon-button md-primary"></md-button>
+ class="md-icon-button"></md-button>
<md-button ng-show="vm.isEditActionEnabled && !vm.isWidgetExpanded"
ng-disabled="vm.loading()"
- class="md-icon-button md-primary"
+ class="md-icon-button"
ng-click="vm.editWidget($event, widget)"
aria-label="{{ 'widget.edit' | translate }}">
<md-tooltip md-direction="top">
{{ 'widget.edit' | translate }}
</md-tooltip>
- <md-icon class="material-icons">
- edit
- </md-icon>
+ <ng-md-icon size="20" icon="edit"></ng-md-icon>
</md-button>
<md-button ng-show="vm.isExportActionEnabled && !vm.isWidgetExpanded"
ng-disabled="vm.loading()"
- class="md-icon-button md-primary"
+ class="md-icon-button"
ng-click="vm.exportWidget($event, widget)"
aria-label="{{ 'widget.export' | translate }}">
<md-tooltip md-direction="top">
{{ 'widget.export' | translate }}
</md-tooltip>
- <md-icon class="material-icons">
- file_download
- </md-icon>
+ <ng-md-icon size="20" icon="file_download"></ng-md-icon>
</md-button>
<md-button ng-show="vm.isRemoveActionEnabled && !vm.isWidgetExpanded"
ng-disabled="vm.loading()"
- class="md-icon-button md-primary"
+ class="md-icon-button"
ng-click="vm.removeWidget($event, widget)"
aria-label="{{ 'widget.remove' | translate }}">
<md-tooltip md-direction="top">
{{ 'widget.remove' | translate }}
</md-tooltip>
- <md-icon class="material-icons">
- close
- </md-icon>
+ <ng-md-icon size="20" icon="close"></ng-md-icon>
</md-button>
</div>
<div flex layout="column" class="tb-widget-content">
<div flex tb-widget
- locals="{ visibleRect: vm.visibleRect, widget: widget, deviceAliasList: vm.deviceAliasList, isEdit: vm.isEdit, stDiff: vm.stDiff }">
+ locals="{ visibleRect: vm.visibleRect,
+ widget: widget,
+ deviceAliasList: vm.deviceAliasList,
+ isEdit: vm.isEdit,
+ stDiff: vm.stDiff,
+ dashboardTimewindow: vm.dashboardTimewindow,
+ dashboardTimewindowApi: vm.dashboardTimewindowApi }">
</div>
</div>
</div>
diff --git a/ui/src/app/components/expand-fullscreen.directive.js b/ui/src/app/components/expand-fullscreen.directive.js
index 16bd713..f74aa91 100644
--- a/ui/src/app/components/expand-fullscreen.directive.js
+++ b/ui/src/app/components/expand-fullscreen.directive.js
@@ -101,11 +101,15 @@ function ExpandFullscreen($compile, $document) {
if (attrs.expandButtonId) {
expandButton = $('#' + attrs.expandButtonId, element)[0];
}
+ var buttonSize;
+ if (attrs.expandButtonSize) {
+ buttonSize = attrs.expandButtonSize;
+ }
var html = '<md-tooltip md-direction="{{expanded ? \'bottom\' : \'top\'}}">' +
'{{(expanded ? \'fullscreen.exit\' : \'fullscreen.expand\') | translate}}' +
'</md-tooltip>' +
- '<ng-md-icon icon="{{expanded ? \'fullscreen_exit\' : \'fullscreen\'}}" ' +
+ '<ng-md-icon ' + (buttonSize ? 'size="'+ buttonSize +'" ' : '') + 'icon="{{expanded ? \'fullscreen_exit\' : \'fullscreen\'}}" ' +
'options=\'{"easing": "circ-in-out", "duration": 375, "rotation": "none"}\'>' +
'</ng-md-icon>';
diff --git a/ui/src/app/components/timewindow.directive.js b/ui/src/app/components/timewindow.directive.js
index 14fff62..580b21b 100644
--- a/ui/src/app/components/timewindow.directive.js
+++ b/ui/src/app/components/timewindow.directive.js
@@ -79,26 +79,38 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM
if (scope.asButton) {
template = $templateCache.get(timewindowButtonTemplate);
} else {
+ scope.direction = scope.direction || 'left';
template = $templateCache.get(timewindowTemplate);
}
element.html(template);
scope.openEditMode = function (event) {
+ if (scope.disabled) {
+ return;
+ }
var position;
var isGtSm = $mdMedia('gt-sm');
if (isGtSm) {
var panelHeight = 375;
+ var panelWidth = 417;
var offset = element[0].getBoundingClientRect();
var bottomY = offset.bottom - $(window).scrollTop(); //eslint-disable-line
+ var leftX = offset.left - $(window).scrollLeft(); //eslint-disable-line
var yPosition;
+ var xPosition;
if (bottomY + panelHeight > $( window ).height()) { //eslint-disable-line
yPosition = $mdPanel.yPosition.ABOVE;
} else {
yPosition = $mdPanel.yPosition.BELOW;
}
+ if (leftX + panelWidth > $( window ).width()) { //eslint-disable-line
+ xPosition = $mdPanel.xPosition.ALIGN_END;
+ } else {
+ xPosition = $mdPanel.xPosition.ALIGN_START;
+ }
position = $mdPanel.newPanelPosition()
.relativeTo(element)
- .addPanelPosition($mdPanel.xPosition.ALIGN_START, yPosition);
+ .addPanelPosition(xPosition, yPosition);
} else {
position = $mdPanel.newPanelPosition()
.absolute()
@@ -223,7 +235,8 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM
require: "^ngModel",
scope: {
asButton: '=asButton',
- buttonColor: '=?'
+ direction: '=?',
+ disabled:'=ngDisabled'
},
link: linker
};
diff --git a/ui/src/app/components/timewindow.scss b/ui/src/app/components/timewindow.scss
index 0c85d52..9ea7c34 100644
--- a/ui/src/app/components/timewindow.scss
+++ b/ui/src/app/components/timewindow.scss
@@ -57,3 +57,10 @@
}
}
}
+
+tb-timewindow {
+ span {
+ pointer-events: all;
+ cursor: pointer;
+ }
+}
ui/src/app/components/timewindow.tpl.html 22(+18 -4)
diff --git a/ui/src/app/components/timewindow.tpl.html b/ui/src/app/components/timewindow.tpl.html
index 0de81e2..2778975 100644
--- a/ui/src/app/components/timewindow.tpl.html
+++ b/ui/src/app/components/timewindow.tpl.html
@@ -15,9 +15,23 @@
limitations under the License.
-->
-<section layout='row' layout-align="start center" style="min-height: 32px;">
- <span ng-click="openEditMode($event)">{{model.displayValue}}</span>
- <md-button class="md-icon-button tb-md-32" aria-label="{{ 'timewindow.edit' | translate }}" ng-click="openEditMode($event)">
- <md-icon ng-style="{ color: buttonColor }" aria-label="{{ 'timewindow.date-range' | translate }}" class="material-icons">date_range</md-icon>
+<section layout='row' layout-align="start center" ng-style="{minHeight: '32px', padding: '0 6px'}">
+ <md-button ng-if="direction === 'left'" ng-disabled="disabled" class="md-icon-button tb-md-32" aria-label="{{ 'timewindow.edit' | translate }}" ng-click="openEditMode($event)">
+ <md-tooltip md-direction="top">
+ {{ 'timewindow.edit' | translate }}
+ </md-tooltip>
+ <ng-md-icon aria-label="{{ 'timewindow.date-range' | translate }}" icon="query_builder"></ng-md-icon>
+ </md-button>
+ <span ng-click="openEditMode($event)">
+ <md-tooltip md-direction="top">
+ {{ 'timewindow.edit' | translate }}
+ </md-tooltip>
+ {{model.displayValue}}
+ </span>
+ <md-button ng-if="direction === 'right'" ng-disabled="disabled" class="md-icon-button tb-md-32" aria-label="{{ 'timewindow.edit' | translate }}" ng-click="openEditMode($event)">
+ <md-tooltip md-direction="top">
+ {{ 'timewindow.edit' | translate }}
+ </md-tooltip>
+ <ng-md-icon aria-label="{{ 'timewindow.date-range' | translate }}" icon="query_builder"></ng-md-icon>
</md-button>
</section>
\ No newline at end of file
diff --git a/ui/src/app/components/timewindow-button.tpl.html b/ui/src/app/components/timewindow-button.tpl.html
index 11d8382..cea776c 100644
--- a/ui/src/app/components/timewindow-button.tpl.html
+++ b/ui/src/app/components/timewindow-button.tpl.html
@@ -15,7 +15,7 @@
limitations under the License.
-->
-<md-button class="md-raised md-primary" ng-click="openEditMode($event)">
- <md-icon class="material-icons">date_range</md-icon>
+<md-button ng-disabled="disabled" class="md-raised md-primary" ng-click="openEditMode($event)">
+ <ng-md-icon icon="query_builder"></ng-md-icon>
<span>{{model.displayValue}}</span>
</md-button>
\ No newline at end of file
ui/src/app/components/widget.controller.js 100(+77 -23)
diff --git a/ui/src/app/components/widget.controller.js b/ui/src/app/components/widget.controller.js
index a2056e0..c0617bc 100644
--- a/ui/src/app/components/widget.controller.js
+++ b/ui/src/app/components/widget.controller.js
@@ -20,7 +20,8 @@ import 'javascript-detect-element-resize/detect-element-resize';
/*@ngInject*/
export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, tbRaf, types, utils, timeService,
- datasourceService, deviceService, visibleRect, isEdit, stDiff, widget, deviceAliasList, widgetType) {
+ datasourceService, deviceService, visibleRect, isEdit, stDiff, dashboardTimewindow,
+ dashboardTimewindowApi, widget, deviceAliasList, widgetType) {
var vm = this;
@@ -136,6 +137,24 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
$scope.widgetErrorData = utils.processWidgetException(e);
}
+ function notifyDataLoaded(apply) {
+ if ($scope.loadingData === true) {
+ $scope.loadingData = false;
+ if (apply) {
+ $scope.$digest();
+ }
+ }
+ }
+
+ function notifyDataLoading(apply) {
+ if ($scope.loadingData === false) {
+ $scope.loadingData = true;
+ if (apply) {
+ $scope.$digest();
+ }
+ }
+ }
+
function onInit() {
if (!widgetContext.inited) {
widgetContext.inited = true;
@@ -274,7 +293,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
}
function initialize() {
- if (widget.type !== types.widgetType.rpc.value) {
+ if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) {
for (var i in widget.config.datasources) {
var datasource = angular.copy(widget.config.datasources[i]);
for (var a in datasource.dataKeys) {
@@ -287,7 +306,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
widgetContext.data.push(datasourceData);
}
}
- } else {
+ } else if (widget.type === types.widgetType.rpc.value) {
if (widget.config.targetDeviceAliasIds && widget.config.targetDeviceAliasIds.length > 0) {
targetDeviceAliasId = widget.config.targetDeviceAliasIds[0];
if (deviceAliasList[targetDeviceAliasId]) {
@@ -354,14 +373,26 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
});
if (widget.type === types.widgetType.timeseries.value) {
- $scope.$watch(function () {
- return widget.config.timewindow;
- }, function (newTimewindow, prevTimewindow) {
- if (!angular.equals(newTimewindow, prevTimewindow)) {
- unsubscribe();
- subscribe();
- }
- });
+ widgetContext.useDashboardTimewindow = angular.isDefined(widget.config.useDashboardTimewindow)
+ ? widget.config.useDashboardTimewindow : true;
+ if (widgetContext.useDashboardTimewindow) {
+ $scope.$on('dashboardTimewindowChanged', function (event, newDashboardTimewindow) {
+ if (!angular.equals(dashboardTimewindow, newDashboardTimewindow)) {
+ dashboardTimewindow = newDashboardTimewindow;
+ unsubscribe();
+ subscribe();
+ }
+ });
+ } else {
+ $scope.$watch(function () {
+ return widgetContext.useDashboardTimewindow ? dashboardTimewindow : widget.config.timewindow;
+ }, function (newTimewindow, prevTimewindow) {
+ if (!angular.equals(newTimewindow, prevTimewindow)) {
+ unsubscribe();
+ subscribe();
+ }
+ });
+ }
}
subscribe();
}
@@ -474,20 +505,29 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
}*/
function onResetTimewindow() {
- if (originalTimewindow) {
- widget.config.timewindow = angular.copy(originalTimewindow);
- originalTimewindow = null;
+ if (widgetContext.useDashboardTimewindow) {
+ dashboardTimewindowApi.onResetTimewindow();
+ } else {
+ if (originalTimewindow) {
+ widget.config.timewindow = angular.copy(originalTimewindow);
+ originalTimewindow = null;
+ }
}
}
function onUpdateTimewindow(startTimeMs, endTimeMs) {
- if (!originalTimewindow) {
- originalTimewindow = angular.copy(widget.config.timewindow);
+ if (widgetContext.useDashboardTimewindow) {
+ dashboardTimewindowApi.onUpdateTimewindow(startTimeMs, endTimeMs);
+ } else {
+ if (!originalTimewindow) {
+ originalTimewindow = angular.copy(widget.config.timewindow);
+ }
+ widget.config.timewindow = timeService.toHistoryTimewindow(widget.config.timewindow, startTimeMs, endTimeMs);
}
- widget.config.timewindow = timeService.toHistoryTimewindow(widget.config.timewindow, startTimeMs, endTimeMs);
}
- function dataUpdated(sourceData, datasourceIndex, dataKeyIndex) {
+ function dataUpdated(sourceData, datasourceIndex, dataKeyIndex, apply) {
+ notifyDataLoaded(apply);
var update = true;
if (widget.type === types.widgetType.latest.value) {
var prevData = widgetContext.data[datasourceIndex + dataKeyIndex].data;
@@ -547,16 +587,28 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
if (_subscriptionTimewindow) {
subscriptionTimewindow = _subscriptionTimewindow;
} else {
- subscriptionTimewindow = timeService.createSubscriptionTimewindow(widget.config.timewindow, widgetContext.timeWindow.stDiff);
+ subscriptionTimewindow =
+ timeService.createSubscriptionTimewindow(
+ widgetContext.useDashboardTimewindow ? dashboardTimewindow : widget.config.timewindow,
+ widgetContext.timeWindow.stDiff);
}
updateTimewindow();
return subscriptionTimewindow;
}
+ function hasTimewindow() {
+ if (widgetContext.useDashboardTimewindow) {
+ return angular.isDefined(dashboardTimewindow);
+ } else {
+ return angular.isDefined(widget.config.timewindow);
+ }
+ }
+
function subscribe() {
- if (widget.type !== types.widgetType.rpc.value) {
+ if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) {
+ notifyDataLoading();
if (widget.type === types.widgetType.timeseries.value &&
- angular.isDefined(widget.config.timewindow)) {
+ hasTimewindow()) {
updateRealtimeSubscription();
if (subscriptionTimewindow.fixedWindow) {
onDataUpdated();
@@ -579,8 +631,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
subscriptionTimewindow: subscriptionTimewindow,
datasource: datasource,
deviceId: deviceId,
- dataUpdated: function (data, datasourceIndex, dataKeyIndex) {
- dataUpdated(data, datasourceIndex, dataKeyIndex);
+ dataUpdated: function (data, datasourceIndex, dataKeyIndex, apply) {
+ dataUpdated(data, datasourceIndex, dataKeyIndex, apply);
},
updateRealtimeSubscription: function() {
this.subscriptionTimewindow = updateRealtimeSubscription();
@@ -601,6 +653,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
datasourceListeners.push(listener);
datasourceService.subscribeToDatasource(listener);
}
+ } else {
+ notifyDataLoaded();
}
}
diff --git a/ui/src/app/components/widget.directive.js b/ui/src/app/components/widget.directive.js
index cd1d16b..6d3a330 100644
--- a/ui/src/app/components/widget.directive.js
+++ b/ui/src/app/components/widget.directive.js
@@ -66,6 +66,8 @@ function Widget($controller, $compile, widgetService) {
function loadFromWidgetInfo(widgetInfo) {
+ scope.loadingData = true;
+
elem.addClass("tb-widget");
var widgetNamespace = "widget-type-" + (widget.isSystemType ? 'sys-' : '')
@@ -73,9 +75,12 @@ function Widget($controller, $compile, widgetService) {
+ widget.typeAlias;
elem.addClass(widgetNamespace);
- elem.html('<div class="tb-absolute-fill tb-widget-error"" ng-if="widgetErrorData">' +
+ elem.html('<div class="tb-absolute-fill tb-widget-error" ng-if="widgetErrorData">' +
'<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>' +
'</div>' +
+ '<div class="tb-absolute-fill tb-widget-loading" ng-show="loadingData" layout="column" layout-align="center center">' +
+ '<md-progress-circular md-mode="indeterminate" class="md-accent" md-diameter="40"></md-progress-circular>' +
+ '</div>' +
'<div id="container">' + widgetInfo.templateHtml + '</div>');
$compile(elem.contents())(scope);
ui/src/app/components/widget.scss 4(+4 -0)
diff --git a/ui/src/app/components/widget.scss b/ui/src/app/components/widget.scss
index bad71b9..ddeddcc 100644
--- a/ui/src/app/components/widget.scss
+++ b/ui/src/app/components/widget.scss
@@ -23,4 +23,8 @@
color: red;
}
}
+ .tb-widget-loading {
+ background: rgba(255,255,255,0.15);
+ z-index: 3;
+ }
}
diff --git a/ui/src/app/components/widget-config.directive.js b/ui/src/app/components/widget-config.directive.js
index c2c5743..639395a 100644
--- a/ui/src/app/components/widget-config.directive.js
+++ b/ui/src/app/components/widget-config.directive.js
@@ -98,6 +98,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti
}, true);
scope.mobileOrder = ngModelCtrl.$viewValue.mobileOrder;
scope.mobileHeight = ngModelCtrl.$viewValue.mobileHeight;
+ scope.useDashboardTimewindow = angular.isDefined(ngModelCtrl.$viewValue.useDashboardTimewindow) ?
+ ngModelCtrl.$viewValue.useDashboardTimewindow : true;
scope.timewindow = ngModelCtrl.$viewValue.timewindow;
if (scope.widgetType !== types.widgetType.rpc.value && scope.widgetType !== types.widgetType.static.value) {
if (scope.datasources) {
@@ -174,7 +176,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti
}
};
- scope.$watch('title + showTitle + dropShadow + enableFullscreen + backgroundColor + color + padding + titleStyle + mobileOrder + mobileHeight + intervalSec', function () {
+ scope.$watch('title + showTitle + dropShadow + enableFullscreen + backgroundColor + color + ' +
+ 'padding + titleStyle + mobileOrder + mobileHeight + useDashboardTimewindow', function () {
if (ngModelCtrl.$viewValue) {
var value = ngModelCtrl.$viewValue;
value.title = scope.title;
@@ -191,7 +194,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti
}
value.mobileOrder = angular.isNumber(scope.mobileOrder) ? scope.mobileOrder : undefined;
value.mobileHeight = scope.mobileHeight;
- value.intervalSec = scope.intervalSec;
+ value.useDashboardTimewindow = scope.useDashboardTimewindow;
ngModelCtrl.$setViewValue(value);
scope.updateValidity();
}
diff --git a/ui/src/app/components/widget-config.tpl.html b/ui/src/app/components/widget-config.tpl.html
index 23c64fc..88bfc2d 100644
--- a/ui/src/app/components/widget-config.tpl.html
+++ b/ui/src/app/components/widget-config.tpl.html
@@ -88,10 +88,15 @@
<input ng-model="mobileHeight" type="number">
</md-input-container>
</div>
- <div ng-show="widgetType === types.widgetType.timeseries.value" layout="row"
- layout-align="center center">
- <span translate style="padding-right: 8px;">widget-config.timewindow</span>
- <tb-timewindow as-button="true" aggregation flex ng-model="timewindow"></tb-timewindow>
+ <div ng-show="widgetType === types.widgetType.timeseries.value" layout='column' layout-align="center"
+ layout-gt-sm='row' layout-align-gt-sm="start center">
+ <md-checkbox flex aria-label="{{ 'widget-config.use-dashboard-timewindow' | translate }}"
+ ng-model="useDashboardTimewindow">{{ 'widget-config.use-dashboard-timewindow' | translate }}
+ </md-checkbox>
+ <section flex layout="row" layout-align="start center" style="margin-bottom: 16px;">
+ <span ng-class="{'tb-disabled-label': useDashboardTimewindow}" translate style="padding-right: 8px;">widget-config.timewindow</span>
+ <tb-timewindow ng-disabled="useDashboardTimewindow" as-button="true" aggregation flex ng-model="timewindow"></tb-timewindow>
+ </section>
</div>
<v-accordion id="datasources-accordion" control="datasourcesAccordion" class="vAccordion--default"
ng-show="widgetType !== types.widgetType.rpc.value && widgetType !== types.widgetType.static.value">
ui/src/app/dashboard/dashboard.controller.js 34(+30 -4)
diff --git a/ui/src/app/dashboard/dashboard.controller.js b/ui/src/app/dashboard/dashboard.controller.js
index c2a9cf3..167b5ae 100644
--- a/ui/src/app/dashboard/dashboard.controller.js
+++ b/ui/src/app/dashboard/dashboard.controller.js
@@ -23,7 +23,7 @@ import addWidgetTemplate from './add-widget.tpl.html';
/*@ngInject*/
export default function DashboardController(types, widgetService, userService,
- dashboardService, itembuffer, importExport, hotkeys, $window, $rootScope,
+ dashboardService, timeService, itembuffer, importExport, hotkeys, $window, $rootScope,
$scope, $state, $stateParams, $mdDialog, $timeout, $document, $q, $translate, $filter) {
var user = userService.getCurrentUser();
@@ -47,6 +47,25 @@ export default function DashboardController(types, widgetService, userService,
vm.widgets = [];
vm.dashboardInitComplete = false;
+ vm.isToolbarOpened = false;
+
+ Object.defineProperty(vm, 'toolbarOpened', {
+ get: function() { return vm.isToolbarOpened || vm.isEdit; },
+ set: function() { }
+ });
+
+ vm.openToolbar = function() {
+ $timeout(function() {
+ vm.isToolbarOpened = true;
+ });
+ }
+
+ vm.closeToolbar = function() {
+ $timeout(function() {
+ vm.isToolbarOpened = false;
+ });
+ }
+
vm.addWidget = addWidget;
vm.addWidgetFromType = addWidgetFromType;
vm.dashboardInited = dashboardInited;
@@ -154,6 +173,9 @@ export default function DashboardController(types, widgetService, userService,
if (vm.widgetEditMode) {
$timeout(function () {
+ vm.dashboardConfiguration = {
+ timewindow: timeService.defaultTimewindow()
+ };
vm.widgets = [{
isSystemType: true,
bundleAlias: 'customWidgetBundle',
@@ -186,9 +208,12 @@ export default function DashboardController(types, widgetService, userService,
if (angular.isUndefined(vm.dashboard.configuration.deviceAliases)) {
vm.dashboard.configuration.deviceAliases = {};
}
- //$timeout(function () {
- vm.widgets = vm.dashboard.configuration.widgets;
- //});
+
+ if (angular.isUndefined(vm.dashboard.configuration.timewindow)) {
+ vm.dashboard.configuration.timewindow = timeService.defaultTimewindow();
+ }
+ vm.dashboardConfiguration = vm.dashboard.configuration;
+ vm.widgets = vm.dashboard.configuration.widgets;
deferred.resolve();
}, function fail(e) {
deferred.reject(e);
@@ -607,6 +632,7 @@ export default function DashboardController(types, widgetService, userService,
if (revert) {
vm.dashboard = vm.prevDashboard;
vm.widgets = vm.dashboard.configuration.widgets;
+ vm.dashboardConfiguration = vm.dashboard.configuration;
}
}
}
ui/src/app/dashboard/dashboard.scss 80(+80 -0)
diff --git a/ui/src/app/dashboard/dashboard.scss b/ui/src/app/dashboard/dashboard.scss
index 64aa4ec..2d3b2fa 100644
--- a/ui/src/app/dashboard/dashboard.scss
+++ b/ui/src/app/dashboard/dashboard.scss
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+@import "~compass-sass-mixins/lib/compass";
@import '../../scss/constants';
section.tb-dashboard-title {
@@ -53,3 +55,81 @@ tb-details-sidenav.tb-widget-details-sidenav {
}
}
}
+
+/***********************
+ * dashboard toolbar
+ ***********************/
+
+section.tb-dashboard-toolbar {
+ position: absolute;
+ top: 0px;
+ left: -100%;
+ z-index: 3;
+ pointer-events: none;
+ &.tb-dashboard-toolbar-opened {
+ right: 0px;
+ @include transition(right .3s cubic-bezier(.55,0,.55,.2));
+ }
+ &.tb-dashboard-toolbar-closed {
+ right: 18px;
+ @include transition(right .3s cubic-bezier(.55,0,.55,.2) .2s);
+ }
+ md-fab-toolbar {
+ &.md-is-open {
+ md-fab-trigger {
+ .md-button {
+ &.md-fab {
+ opacity: 1;
+ @include transition(opacity .3s cubic-bezier(.55,0,.55,.2));
+ }
+ }
+ }
+ }
+ md-fab-trigger {
+ .md-button {
+ &.md-fab {
+ line-height: 36px;
+ width: 36px;
+ height: 36px;
+ margin: 4px 0 0 4px;
+ opacity: 0.5;
+ @include transition(opacity .3s cubic-bezier(.55,0,.55,.2) .2s);
+ md-icon {
+ margin: 0;
+ line-height: 18px;
+ height: 18px;
+ width: 18px;
+ min-height: 18px;
+ min-width: 18px;
+ }
+ }
+ }
+ }
+ .md-fab-toolbar-wrapper {
+ height: 40px;
+ md-toolbar {
+ min-height: 36px;
+ height: 36px;
+ md-fab-actions {
+ .close-action {
+ margin-right: -18px;
+ }
+ tb-timewindow {
+ font-size: 16px;
+ }
+ }
+ }
+ }
+ }
+}
+
+.tb-dashboard-container {
+ &.tb-dashboard-toolbar-opened {
+ margin-top: 40px;
+ @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2));
+ }
+ &.tb-dashboard-toolbar-closed {
+ margin-top: 0px;
+ @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2) .2s);
+ }
+}
ui/src/app/dashboard/dashboard.tpl.html 482(+255 -227)
diff --git a/ui/src/app/dashboard/dashboard.tpl.html b/ui/src/app/dashboard/dashboard.tpl.html
index 298d365..bf0b3ac 100644
--- a/ui/src/app/dashboard/dashboard.tpl.html
+++ b/ui/src/app/dashboard/dashboard.tpl.html
@@ -15,235 +15,263 @@
limitations under the License.
-->
-<md-content flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode" hide-expand-button="vm.widgetEditMode || vm.iframeMode">
- <!--section ng-show="!vm.isAddingWidget && !loading && !vm.widgetEditMode" layout="row" layout-wrap
- class="tb-header-buttons tb-top-header-buttons md-fab" ng-style="{'right': '50px'}">
- <md-button ng-if="vm.isTenantAdmin()" ng-show="vm.isEdit" ng-disabled="loading"
- class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right"
- aria-label="{{ 'action.apply' | translate }}"
- ng-click="vm.saveDashboard()">
- <md-tooltip md-direction="top">
- {{ 'action.apply-changes' | translate }}
- </md-tooltip>
- <ng-md-icon icon="done"></ng-md-icon>
- </md-button>
- <md-button ng-if="vm.isTenantAdmin()" ng-disabled="loading"
- class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right"
- aria-label="{{ 'action.edit-mode' | translate }}"
- ng-click="vm.toggleDashboardEditMode()">
- <md-tooltip md-direction="top">
- {{ (vm.isEdit ? 'action.decline-changes' : 'action.enter-edit-mode') | translate }}
- </md-tooltip>
- <ng-md-icon icon="{{vm.isEdit ? 'close' : 'edit'}}"
- options='{"easing": "circ-in-out", "duration": 375, "rotation": "none"}'></ng-md-icon>
- </md-button>
- </section-->
- <section ng-show="!loading && vm.noData()" layout-align="center center"
- ng-class="{'tb-padded' : !vm.widgetEditMode}"
- style="text-transform: uppercase; display: flex; z-index: 1;"
- class="md-headline tb-absolute-fill">
- <span translate ng-if="!vm.isEdit">
- dashboard.no-widgets
- </span>
- <md-button ng-if="vm.isEdit && !vm.widgetEditMode" class="tb-add-new-widget" ng-click="vm.addWidget($event)">
- <md-icon aria-label="{{ 'action.add' | translate }}" class="material-icons tb-md-96">add</md-icon>
- {{ 'dashboard.add-widget' | translate }}
- </md-button>
- </section>
- <section ng-if="!vm.widgetEditMode" class="tb-dashboard-title" layout="row" layout-align="center center">
- <h3 ng-show="!vm.isEdit && vm.displayTitle()">{{ vm.dashboard.title }}</h3>
- <md-input-container ng-show="vm.isEdit" class="md-block" style="height: 30px;">
- <label translate>dashboard.title</label>
- <input class="tb-dashboard-title" required name="title" ng-model="vm.dashboard.title">
- </md-input-container>
- <md-button class="md-raised" flex="none" ng-show="vm.isEdit" ng-click="vm.openDeviceAliases($event)">
- {{ 'device.aliases' | translate }}
- </md-button>
- <md-button class="md-raised" flex="none" ng-show="vm.isEdit" ng-click="vm.openDashboardSettings($event)">
- {{ 'dashboard.settings' | translate }}
- </md-button>
- </section>
- <div class="tb-absolute-fill"
- ng-class="{ 'tb-padded' : !vm.widgetEditMode && (vm.isEdit || vm.displayTitle()), 'tb-shrinked' : vm.isEditingWidget }">
- <tb-dashboard
- dashboard-style="{'background-color': vm.dashboard.configuration.gridSettings.backgroundColor,
- 'background-image': 'url('+vm.dashboard.configuration.gridSettings.backgroundImageUrl+')',
- 'background-repeat': 'no-repeat',
- 'background-attachment': 'scroll',
- 'background-size': vm.dashboard.configuration.gridSettings.backgroundSizeMode || '100%',
- 'background-position': '0% 0%'}"
- widgets="vm.widgets"
- columns="vm.dashboard.configuration.gridSettings.columns"
- margins="vm.dashboard.configuration.gridSettings.margins"
- device-alias-list="vm.dashboard.configuration.deviceAliases"
- is-edit="vm.isEdit"
- is-mobile="vm.forceDashboardMobileMode"
- is-mobile-disabled="vm.widgetEditMode"
- is-edit-action-enabled="vm.isEdit"
- is-export-action-enabled="vm.isEdit && !vm.widgetEditMode"
- is-remove-action-enabled="vm.isEdit && !vm.widgetEditMode"
- on-edit-widget="vm.editWidget(event, widget)"
- on-export-widget="vm.exportWidget(event, widget)"
- on-widget-mouse-down="vm.widgetMouseDown(event, widget)"
- on-widget-clicked="vm.widgetClicked(event, widget)"
- on-widget-context-menu="vm.widgetContextMenu(event, widget)"
- prepare-dashboard-context-menu="vm.prepareDashboardContextMenu()"
- prepare-widget-context-menu="vm.prepareWidgetContextMenu(widget)"
- on-remove-widget="vm.removeWidget(event, widget)"
- load-widgets="vm.loadDashboard()"
- get-st-diff="vm.getServerTimeDiff()"
- on-init="vm.dashboardInited(dashboard)"
- on-init-failed="vm.dashboardInitFailed(e)">
- </tb-dashboard>
- </div>
- <tb-details-sidenav class="tb-widget-details-sidenav"
- header-title="vm.editingWidget.config.title"
- header-subtitle="{{vm.editingWidgetSubtitle}}"
- is-read-only="false"
- is-open="vm.isEditingWidget"
- is-always-edit="true"
- on-close-details="vm.onEditWidgetClosed()"
- on-toggle-details-edit-mode="vm.onRevertWidgetEdit(vm.widgetForm)"
- on-apply-details="vm.saveWidget(vm.widgetForm)"
- the-form="vm.widgetForm">
- <details-buttons tb-help="vm.helpLinkIdForWidgetType()" help-container-id="help-container">
- <div id="help-container"></div>
- </details-buttons>
- <form name="vm.widgetForm" ng-if="vm.isEditingWidget">
- <tb-edit-widget
- dashboard="vm.dashboard"
- widget="vm.editingWidget"
- the-form="vm.widgetForm">
- </tb-edit-widget>
- </form>
- </tb-details-sidenav>
- <tb-details-sidenav ng-if="!vm.widgetEditMode" class="tb-select-widget-sidenav"
- header-title="'dashboard.select-widget-title' | translate"
- header-height-px="120"
- is-read-only="true"
- is-open="vm.isAddingWidget"
- is-edit="false"
- on-close-details="vm.onAddWidgetClosed()">
- <header-pane ng-if="vm.isAddingWidget">
- <div layout="row">
- <span class="tb-details-subtitle">{{ 'widgets-bundle.current' | translate }}</span>
- <tb-widgets-bundle-select flex-offset="5"
- flex
- ng-model="vm.widgetsBundle"
- tb-required="true"
- select-first-bundle="false">
- </tb-widgets-bundle-select>
- </div>
- </header-pane>
- <div ng-if="vm.isAddingWidget">
- <md-tabs ng-if="vm.timeseriesWidgetTypes.length > 0 || vm.latestWidgetTypes.length > 0 ||
- vm.rpcWidgetTypes.length > 0 || vm.staticWidgetTypes.length > 0"
- flex
- class="tb-absolute-fill" md-border-bottom>
- <md-tab ng-if="vm.timeseriesWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.timeseries' | translate }}">
- <tb-dashboard
- widgets="vm.timeseriesWidgetTypes"
- is-edit="false"
- is-mobile="true"
- is-edit-action-enabled="false"
- is-remove-action-enabled="false"
- on-widget-clicked="vm.addWidgetFromType(event, widget)">
- </tb-dashboard>
- </md-tab>
- <md-tab ng-if="vm.latestWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.latest-values' | translate }}">
- <tb-dashboard
- widgets="vm.latestWidgetTypes"
- is-edit="false"
- is-mobile="true"
- is-edit-action-enabled="false"
- is-remove-action-enabled="false"
- on-widget-clicked="vm.addWidgetFromType(event, widget)">
- </tb-dashboard>
- </md-tab>
- <md-tab ng-if="vm.rpcWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.rpc' | translate }}">
- <tb-dashboard
- widgets="vm.rpcWidgetTypes"
- is-edit="false"
- is-mobile="true"
- is-edit-action-enabled="false"
- is-remove-action-enabled="false"
- on-widget-clicked="vm.addWidgetFromType(event, widget)">
- </tb-dashboard>
- </md-tab>
- <md-tab ng-if="vm.staticWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.static' | translate }}">
- <tb-dashboard
- widgets="vm.staticWidgetTypes"
- is-edit="false"
- is-mobile="true"
- is-edit-action-enabled="false"
- is-remove-action-enabled="false"
- on-widget-clicked="vm.addWidgetFromType(event, widget)">
- </tb-dashboard>
- </md-tab>
- </md-tabs>
- <span translate ng-if="vm.timeseriesWidgetTypes.length === 0 && vm.latestWidgetTypes.length === 0 &&
- vm.rpcWidgetTypes.length === 0 && vm.staticWidgetTypes.length === 0 && vm.widgetsBundle"
- layout-align="center center"
- style="text-transform: uppercase; display: flex;"
- class="md-headline tb-absolute-fill">widgets-bundle.empty</span>
- <span translate ng-if="!vm.widgetsBundle"
- layout-align="center center"
- style="text-transform: uppercase; display: flex;"
- class="md-headline tb-absolute-fill">widget.select-widgets-bundle</span>
- </div>
- </tb-details-sidenav>
- <!-- </section> -->
- <section layout="row" layout-wrap class="tb-footer-buttons md-fab" layout-align="start end">
- <md-fab-speed-dial ng-disabled="loading" ng-show="!vm.isAddingWidget && vm.isEdit && !vm.widgetEditMode"
- md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up">
- <md-fab-trigger>
- <md-button ng-disabled="loading"
- class="tb-btn-footer md-accent md-hue-2 md-fab"
- aria-label="{{ 'dashboard.add-widget' | translate }}">
- <md-tooltip md-direction="top">
- {{ 'dashboard.add-widget' | translate }}
+<md-content flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode" expand-button-id="dashboard-expand-button"
+ hide-expand-button="vm.widgetEditMode || vm.iframeMode"
+ ng-style="{'background-color': vm.dashboard.configuration.gridSettings.backgroundColor,
+ 'background-image': 'url('+vm.dashboard.configuration.gridSettings.backgroundImageUrl+')',
+ 'background-repeat': 'no-repeat',
+ 'background-attachment': 'scroll',
+ 'background-size': vm.dashboard.configuration.gridSettings.backgroundSizeMode || '100%',
+ 'background-position': '0% 0%'}">
+ <section class="tb-dashboard-toolbar"
+ ng-class="{ 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }">
+ <md-fab-toolbar md-open="vm.toolbarOpened"
+ md-direction="left">
+ <md-fab-trigger class="align-with-text">
+ <md-button aria-label="menu" class="md-fab md-primary" ng-click="vm.openToolbar()">
+ <md-tooltip ng-show="!vm.toolbarOpened" md-direction="top">
+ {{ 'dashboard.open-toolbar' | translate }}
</md-tooltip>
- <ng-md-icon icon="add"></ng-md-icon>
+ <md-icon aria-label="dashboard-toolbar" class="material-icons">more_horiz</md-icon>
</md-button>
</md-fab-trigger>
- <md-fab-actions>
- <md-button ng-disabled="loading"
- class="tmd-accent md-hue-2 md-fab" ng-click="vm.addWidget($event)"
- aria-label="{{ 'action.create' | translate }}">
- <md-tooltip md-direction="top">
- {{ 'dashboard.create-new-widget' | translate }}
- </md-tooltip>
- <ng-md-icon icon="insert_drive_file"></ng-md-icon>
- </md-button>
- <md-button ng-disabled="loading"
- class="tmd-accent md-hue-2 md-fab" ng-click="vm.importWidget($event)"
- aria-label="{{ 'action.import' | translate }}">
- <md-tooltip md-direction="top">
- {{ 'dashboard.import-widget' | translate }}
- </md-tooltip>
- <ng-md-icon icon="file_upload"></ng-md-icon>
- </md-button>
- </md-fab-actions>
- </md-fab-speed-dial>
- <md-button ng-if="vm.isTenantAdmin() || vm.isSystemAdmin()" ng-show="vm.isEdit && !vm.isAddingWidget && !loading" ng-disabled="loading"
- class="tb-btn-footer md-accent md-hue-2 md-fab"
- aria-label="{{ 'action.apply' | translate }}"
- ng-click="vm.saveDashboard()">
- <md-tooltip md-direction="top">
- {{ 'action.apply-changes' | translate }}
- </md-tooltip>
- <ng-md-icon icon="done"></ng-md-icon>
- </md-button>
- <md-button ng-show="!vm.isAddingWidget && !loading"
- ng-if="vm.isTenantAdmin() || vm.isSystemAdmin()" ng-disabled="loading"
- class="tb-btn-footer md-accent md-hue-2 md-fab"
- aria-label="{{ 'action.edit-mode' | translate }}"
- ng-click="vm.toggleDashboardEditMode()">
- <md-tooltip md-direction="top">
- {{ (vm.isEdit ? 'action.decline-changes' : 'action.enter-edit-mode') | translate }}
- </md-tooltip>
- <ng-md-icon icon="{{vm.isEdit ? 'close' : 'edit'}}"
- options='{"easing": "circ-in-out", "duration": 375, "rotation": "none"}'></ng-md-icon>
- </md-button>
+ <md-toolbar>
+ <md-fab-actions class="md-toolbar-tools">
+ <md-button ng-show="!vm.isEdit" aria-label="close-toolbar" class="md-icon-button close-action" ng-click="vm.closeToolbar()">
+ <md-tooltip md-direction="top">
+ {{ 'dashboard.close-toolbar' | translate }}
+ </md-tooltip>
+ <md-icon aria-label="close-toolbar" class="material-icons">arrow_forward</md-icon>
+ </md-button>
+ <md-button id="dashboard-expand-button"
+ aria-label="{{ 'fullscreen.fullscreen' | translate }}"
+ class="md-icon-button">
+ </md-button>
+ <tb-timewindow direction="left" aggregation ng-model="vm.dashboardConfiguration.timewindow">
+ </tb-timewindow>
+ <md-button ng-show="vm.isEdit" aria-label="{{ 'device.aliases' | translate }}" class="md-icon-button"
+ ng-click="vm.openDeviceAliases($event)">
+ <md-tooltip md-direction="top">
+ {{ 'device.aliases' | translate }}
+ </md-tooltip>
+ <md-icon aria-label="{{ 'device.aliases' | translate }}" class="material-icons">devices_other</md-icon>
+ </md-button>
+ <md-button ng-show="vm.isEdit" aria-label="{{ 'dashboard.settings' | translate }}" class="md-icon-button"
+ ng-click="vm.openDashboardSettings($event)">
+ <md-tooltip md-direction="top">
+ {{ 'dashboard.settings' | translate }}
+ </md-tooltip>
+ <md-icon aria-label="{{ 'dashboard.settings' | translate }}" class="material-icons">settings</md-icon>
+ </md-button>
+ </md-fab-actions>
+ </md-toolbar>
+ </md-fab-toolbar>
+ </section>
+ <section class="tb-dashboard-container tb-absolute-fill"
+ ng-class="{ 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }">
+ <section ng-show="!loading && vm.noData()" layout-align="center center"
+ ng-class="{'tb-padded' : !vm.widgetEditMode}"
+ style="text-transform: uppercase; display: flex; z-index: 1;"
+ class="md-headline tb-absolute-fill">
+ <span translate ng-if="!vm.isEdit">
+ dashboard.no-widgets
+ </span>
+ <md-button ng-if="vm.isEdit && !vm.widgetEditMode" class="tb-add-new-widget" ng-click="vm.addWidget($event)">
+ <md-icon aria-label="{{ 'action.add' | translate }}" class="material-icons tb-md-96">add</md-icon>
+ {{ 'dashboard.add-widget' | translate }}
+ </md-button>
+ </section>
+ <section ng-if="!vm.widgetEditMode" class="tb-dashboard-title" layout="row" layout-align="center center"
+ ng-style="{'color': vm.dashboard.configuration.gridSettings.titleColor}">
+ <h3 ng-show="!vm.isEdit && vm.displayTitle()">{{ vm.dashboard.title }}</h3>
+ <md-input-container ng-show="vm.isEdit" class="md-block" style="height: 30px;">
+ <label translate ng-style="{'color': vm.dashboard.configuration.gridSettings.titleColor}">dashboard.title</label>
+ <input class="tb-dashboard-title" ng-style="{'color': vm.dashboard.configuration.gridSettings.titleColor}" required name="title" ng-model="vm.dashboard.title">
+ </md-input-container>
+ </section>
+ <div class="tb-absolute-fill"
+ ng-class="{ 'tb-padded' : !vm.widgetEditMode && (vm.isEdit || vm.displayTitle()), 'tb-shrinked' : vm.isEditingWidget }">
+ <tb-dashboard
+ dashboard-style="{'background-color': vm.dashboard.configuration.gridSettings.backgroundColor,
+ 'background-image': 'url('+vm.dashboard.configuration.gridSettings.backgroundImageUrl+')',
+ 'background-repeat': 'no-repeat',
+ 'background-attachment': 'scroll',
+ 'background-size': vm.dashboard.configuration.gridSettings.backgroundSizeMode || '100%',
+ 'background-position': '0% 0%'}"
+ widgets="vm.widgets"
+ columns="vm.dashboard.configuration.gridSettings.columns"
+ margins="vm.dashboard.configuration.gridSettings.margins"
+ device-alias-list="vm.dashboard.configuration.deviceAliases"
+ dashboard-timewindow="vm.dashboardConfiguration.timewindow"
+ is-edit="vm.isEdit"
+ is-mobile="vm.forceDashboardMobileMode"
+ is-mobile-disabled="vm.widgetEditMode"
+ is-edit-action-enabled="vm.isEdit"
+ is-export-action-enabled="vm.isEdit && !vm.widgetEditMode"
+ is-remove-action-enabled="vm.isEdit && !vm.widgetEditMode"
+ on-edit-widget="vm.editWidget(event, widget)"
+ on-export-widget="vm.exportWidget(event, widget)"
+ on-widget-mouse-down="vm.widgetMouseDown(event, widget)"
+ on-widget-clicked="vm.widgetClicked(event, widget)"
+ on-widget-context-menu="vm.widgetContextMenu(event, widget)"
+ prepare-dashboard-context-menu="vm.prepareDashboardContextMenu()"
+ prepare-widget-context-menu="vm.prepareWidgetContextMenu(widget)"
+ on-remove-widget="vm.removeWidget(event, widget)"
+ load-widgets="vm.loadDashboard()"
+ get-st-diff="vm.getServerTimeDiff()"
+ on-init="vm.dashboardInited(dashboard)"
+ on-init-failed="vm.dashboardInitFailed(e)">
+ </tb-dashboard>
+ </div>
+ <tb-details-sidenav class="tb-widget-details-sidenav"
+ header-title="vm.editingWidget.config.title"
+ header-subtitle="{{vm.editingWidgetSubtitle}}"
+ is-read-only="false"
+ is-open="vm.isEditingWidget"
+ is-always-edit="true"
+ on-close-details="vm.onEditWidgetClosed()"
+ on-toggle-details-edit-mode="vm.onRevertWidgetEdit(vm.widgetForm)"
+ on-apply-details="vm.saveWidget(vm.widgetForm)"
+ the-form="vm.widgetForm">
+ <details-buttons tb-help="vm.helpLinkIdForWidgetType()" help-container-id="help-container">
+ <div id="help-container"></div>
+ </details-buttons>
+ <form name="vm.widgetForm" ng-if="vm.isEditingWidget">
+ <tb-edit-widget
+ dashboard="vm.dashboard"
+ widget="vm.editingWidget"
+ the-form="vm.widgetForm">
+ </tb-edit-widget>
+ </form>
+ </tb-details-sidenav>
+ <tb-details-sidenav ng-if="!vm.widgetEditMode" class="tb-select-widget-sidenav"
+ header-title="'dashboard.select-widget-title' | translate"
+ header-height-px="120"
+ is-read-only="true"
+ is-open="vm.isAddingWidget"
+ is-edit="false"
+ on-close-details="vm.onAddWidgetClosed()">
+ <header-pane ng-if="vm.isAddingWidget">
+ <div layout="row">
+ <span class="tb-details-subtitle">{{ 'widgets-bundle.current' | translate }}</span>
+ <tb-widgets-bundle-select flex-offset="5"
+ flex
+ ng-model="vm.widgetsBundle"
+ tb-required="true"
+ select-first-bundle="false">
+ </tb-widgets-bundle-select>
+ </div>
+ </header-pane>
+ <div ng-if="vm.isAddingWidget">
+ <md-tabs ng-if="vm.timeseriesWidgetTypes.length > 0 || vm.latestWidgetTypes.length > 0 ||
+ vm.rpcWidgetTypes.length > 0 || vm.staticWidgetTypes.length > 0"
+ flex
+ class="tb-absolute-fill" md-border-bottom>
+ <md-tab ng-if="vm.timeseriesWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.timeseries' | translate }}">
+ <tb-dashboard
+ widgets="vm.timeseriesWidgetTypes"
+ is-edit="false"
+ is-mobile="true"
+ is-edit-action-enabled="false"
+ is-remove-action-enabled="false"
+ on-widget-clicked="vm.addWidgetFromType(event, widget)">
+ </tb-dashboard>
+ </md-tab>
+ <md-tab ng-if="vm.latestWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.latest-values' | translate }}">
+ <tb-dashboard
+ widgets="vm.latestWidgetTypes"
+ is-edit="false"
+ is-mobile="true"
+ is-edit-action-enabled="false"
+ is-remove-action-enabled="false"
+ on-widget-clicked="vm.addWidgetFromType(event, widget)">
+ </tb-dashboard>
+ </md-tab>
+ <md-tab ng-if="vm.rpcWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.rpc' | translate }}">
+ <tb-dashboard
+ widgets="vm.rpcWidgetTypes"
+ is-edit="false"
+ is-mobile="true"
+ is-edit-action-enabled="false"
+ is-remove-action-enabled="false"
+ on-widget-clicked="vm.addWidgetFromType(event, widget)">
+ </tb-dashboard>
+ </md-tab>
+ <md-tab ng-if="vm.staticWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.static' | translate }}">
+ <tb-dashboard
+ widgets="vm.staticWidgetTypes"
+ is-edit="false"
+ is-mobile="true"
+ is-edit-action-enabled="false"
+ is-remove-action-enabled="false"
+ on-widget-clicked="vm.addWidgetFromType(event, widget)">
+ </tb-dashboard>
+ </md-tab>
+ </md-tabs>
+ <span translate ng-if="vm.timeseriesWidgetTypes.length === 0 && vm.latestWidgetTypes.length === 0 &&
+ vm.rpcWidgetTypes.length === 0 && vm.staticWidgetTypes.length === 0 && vm.widgetsBundle"
+ layout-align="center center"
+ style="text-transform: uppercase; display: flex;"
+ class="md-headline tb-absolute-fill">widgets-bundle.empty</span>
+ <span translate ng-if="!vm.widgetsBundle"
+ layout-align="center center"
+ style="text-transform: uppercase; display: flex;"
+ class="md-headline tb-absolute-fill">widget.select-widgets-bundle</span>
+ </div>
+ </tb-details-sidenav>
+ <!-- </section> -->
+ <section layout="row" layout-wrap class="tb-footer-buttons md-fab" layout-align="start end">
+ <md-fab-speed-dial ng-disabled="loading" ng-show="!vm.isAddingWidget && vm.isEdit && !vm.widgetEditMode"
+ md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up">
+ <md-fab-trigger>
+ <md-button ng-disabled="loading"
+ class="tb-btn-footer md-accent md-hue-2 md-fab"
+ aria-label="{{ 'dashboard.add-widget' | translate }}">
+ <md-tooltip md-direction="top">
+ {{ 'dashboard.add-widget' | translate }}
+ </md-tooltip>
+ <ng-md-icon icon="add"></ng-md-icon>
+ </md-button>
+ </md-fab-trigger>
+ <md-fab-actions>
+ <md-button ng-disabled="loading"
+ class="tmd-accent md-hue-2 md-fab" ng-click="vm.addWidget($event)"
+ aria-label="{{ 'action.create' | translate }}">
+ <md-tooltip md-direction="top">
+ {{ 'dashboard.create-new-widget' | translate }}
+ </md-tooltip>
+ <ng-md-icon icon="insert_drive_file"></ng-md-icon>
+ </md-button>
+ <md-button ng-disabled="loading"
+ class="tmd-accent md-hue-2 md-fab" ng-click="vm.importWidget($event)"
+ aria-label="{{ 'action.import' | translate }}">
+ <md-tooltip md-direction="top">
+ {{ 'dashboard.import-widget' | translate }}
+ </md-tooltip>
+ <ng-md-icon icon="file_upload"></ng-md-icon>
+ </md-button>
+ </md-fab-actions>
+ </md-fab-speed-dial>
+ <md-button ng-if="vm.isTenantAdmin() || vm.isSystemAdmin()" ng-show="vm.isEdit && !vm.isAddingWidget && !loading" ng-disabled="loading"
+ class="tb-btn-footer md-accent md-hue-2 md-fab"
+ aria-label="{{ 'action.apply' | translate }}"
+ ng-click="vm.saveDashboard()">
+ <md-tooltip md-direction="top">
+ {{ 'action.apply-changes' | translate }}
+ </md-tooltip>
+ <ng-md-icon icon="done"></ng-md-icon>
+ </md-button>
+ <md-button ng-show="!vm.isAddingWidget && !loading"
+ ng-if="vm.isTenantAdmin() || vm.isSystemAdmin()" ng-disabled="loading"
+ class="tb-btn-footer md-accent md-hue-2 md-fab"
+ aria-label="{{ 'action.edit-mode' | translate }}"
+ ng-click="vm.toggleDashboardEditMode()">
+ <md-tooltip md-direction="top">
+ {{ (vm.isEdit ? 'action.decline-changes' : 'action.enter-edit-mode') | translate }}
+ </md-tooltip>
+ <ng-md-icon icon="{{vm.isEdit ? 'close' : 'edit'}}"
+ options='{"easing": "circ-in-out", "duration": 375, "rotation": "none"}'></ng-md-icon>
+ </md-button>
+ </section>
</section>
</md-content>
diff --git a/ui/src/app/dashboard/dashboard-settings.controller.js b/ui/src/app/dashboard/dashboard-settings.controller.js
index 91885d3..aac6da3 100644
--- a/ui/src/app/dashboard/dashboard-settings.controller.js
+++ b/ui/src/app/dashboard/dashboard-settings.controller.js
@@ -32,6 +32,7 @@ export default function DashboardSettingsController($scope, $mdDialog, gridSetti
}
vm.gridSettings.backgroundColor = vm.gridSettings.backgroundColor || 'rgba(0,0,0,0)';
+ vm.gridSettings.titleColor = vm.gridSettings.titleColor || 'rgba(0,0,0,0.870588)';
vm.gridSettings.columns = vm.gridSettings.columns || 24;
vm.gridSettings.margins = vm.gridSettings.margins || [10, 10];
vm.hMargin = vm.gridSettings.margins[0];
diff --git a/ui/src/app/dashboard/dashboard-settings.tpl.html b/ui/src/app/dashboard/dashboard-settings.tpl.html
index 9a1f5d4..38cb530 100644
--- a/ui/src/app/dashboard/dashboard-settings.tpl.html
+++ b/ui/src/app/dashboard/dashboard-settings.tpl.html
@@ -31,10 +31,22 @@
<md-dialog-content>
<div class="md-dialog-content">
<fieldset ng-disabled="loading">
- <div layout="row" layout-padding>
+ <div layout="row" layout-align="start center">
<md-checkbox flex aria-label="{{ 'dashboard.display-title' | translate }}"
ng-model="vm.gridSettings.showTitle">{{ 'dashboard.display-title' | translate }}
</md-checkbox>
+ <div flex
+ ng-required="false"
+ md-color-picker
+ ng-model="vm.gridSettings.titleColor"
+ label="{{ 'dashboard.title-color' | translate }}"
+ icon="format_color_fill"
+ default="rgba(0, 0, 0, 0.870588)"
+ md-color-clear-button="false"
+ open-on-input="true"
+ md-color-generic-palette="false"
+ md-color-history="false"
+ ></div>
</div>
<md-input-container class="md-block">
<label translate>dashboard.columns-count</label>
ui/src/app/locale/locale.constant.js 6(+5 -1)
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index 05411d7..635ddec 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -247,6 +247,7 @@ export default angular.module('thingsboard.locale', [])
"min-vertical-margin-message": "Only 0 is allowed as minimum vertical margin value.",
"max-vertical-margin-message": "Only 50 is allowed as maximum vertical margin value.",
"display-title": "Display dashboard title",
+ "title-color": "Title color",
"import": "Import dashboard",
"export": "Export dashboard",
"export-failed-error": "Unable to export dashboard: {error}",
@@ -258,7 +259,9 @@ export default angular.module('thingsboard.locale', [])
"import-widget": "Import widget",
"widget-file": "Widget file",
"invalid-widget-file-error": "Unable to import widget: Invalid widget data structure.",
- "widget-import-missing-aliases-title": "Select missing devices used by widget"
+ "widget-import-missing-aliases-title": "Select missing devices used by widget",
+ "open-toolbar": "Open dashboard toolbar",
+ "close-toolbar": "Close toolbar"
},
"datakey": {
"settings": "Settings",
@@ -702,6 +705,7 @@ export default angular.module('thingsboard.locale', [])
"order": "Order",
"height": "Height",
"timewindow": "Timewindow",
+ "use-dashboard-timewindow": "Use dashboard timewindow",
"datasources": "Datasources",
"datasource-type": "Type",
"datasource-parameters": "Parameters",
ui/src/app/widget/lib/flot-widget.js 4(+4 -0)
diff --git a/ui/src/app/widget/lib/flot-widget.js b/ui/src/app/widget/lib/flot-widget.js
index 2888b0f..9c34405 100644
--- a/ui/src/app/widget/lib/flot-widget.js
+++ b/ui/src/app/widget/lib/flot-widget.js
@@ -360,9 +360,12 @@ export default class TbFlot {
update() {
if (!this.isMouseInteraction) {
if (this.chartType === 'line' || this.chartType === 'bar') {
+ this.options.xaxis.min = this.ctx.timeWindow.minTime;
+ this.options.xaxis.max = this.ctx.timeWindow.maxTime;
this.ctx.plot.getOptions().xaxes[0].min = this.ctx.timeWindow.minTime;
this.ctx.plot.getOptions().xaxes[0].max = this.ctx.timeWindow.maxTime;
if (this.chartType === 'bar') {
+ this.options.series.bars.barWidth = this.ctx.timeWindow.interval * 0.6;
this.ctx.plot.getOptions().series.bars.barWidth = this.ctx.timeWindow.interval * 0.6;
}
this.ctx.plot.setData(this.ctx.data);
@@ -879,6 +882,7 @@ export default class TbFlot {
destroy() {
if (this.ctx.plot) {
this.ctx.plot.destroy();
+ this.ctx.plot = null;
}
}
ui/src/scss/main.scss 10(+10 -0)
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index 31c256d..8ffac23 100644
--- a/ui/src/scss/main.scss
+++ b/ui/src/scss/main.scss
@@ -193,6 +193,12 @@ md-sidenav {
pointer-events: all;
}
+.md-color-picker-input-container {
+ md-input-container {
+ margin-bottom: 0px;
+ }
+}
+
/***********************
* THINGSBOARD SPECIFIC
***********************/
@@ -201,6 +207,10 @@ md-sidenav {
color: rgba(0,0,0,0.54);
}
+.tb-disabled-label {
+ color: rgba(0,0,0,0.44);
+}
+
label {
&.tb-small {
pointer-events: none;