Commit 193ef7ddbd3b506d6492c00b5d6cfcf137d30eb7
1 parent
087cd95b
UI: Implement timewindow control on dashboard level
Showing
24 changed files
with
631 additions
and
335 deletions
@@ -74,7 +74,7 @@ export default class DataAggregator { | @@ -74,7 +74,7 @@ export default class DataAggregator { | ||
74 | }, this.aggregationTimeout, false); | 74 | }, this.aggregationTimeout, false); |
75 | } | 75 | } |
76 | 76 | ||
77 | - onData(data, update, history) { | 77 | + onData(data, update, history, apply) { |
78 | if (!this.dataReceived || this.resetPending) { | 78 | if (!this.dataReceived || this.resetPending) { |
79 | var updateIntervalScheduledTime = true; | 79 | var updateIntervalScheduledTime = true; |
80 | if (!this.dataReceived) { | 80 | if (!this.dataReceived) { |
@@ -96,18 +96,18 @@ export default class DataAggregator { | @@ -96,18 +96,18 @@ export default class DataAggregator { | ||
96 | if (updateIntervalScheduledTime) { | 96 | if (updateIntervalScheduledTime) { |
97 | this.intervalScheduledTime = currentTime(); | 97 | this.intervalScheduledTime = currentTime(); |
98 | } | 98 | } |
99 | - this.onInterval(history); | 99 | + this.onInterval(history, apply); |
100 | } else { | 100 | } else { |
101 | updateAggregatedData(this.aggregationMap, this.aggregationType === this.types.aggregation.count.value, | 101 | updateAggregatedData(this.aggregationMap, this.aggregationType === this.types.aggregation.count.value, |
102 | this.noAggregation, this.aggFunction, data.data, this.interval, this.startTs); | 102 | this.noAggregation, this.aggFunction, data.data, this.interval, this.startTs); |
103 | if (history) { | 103 | if (history) { |
104 | this.intervalScheduledTime = currentTime(); | 104 | this.intervalScheduledTime = currentTime(); |
105 | - this.onInterval(history); | 105 | + this.onInterval(history, apply); |
106 | } | 106 | } |
107 | } | 107 | } |
108 | } | 108 | } |
109 | 109 | ||
110 | - onInterval(history) { | 110 | + onInterval(history, apply) { |
111 | var now = currentTime(); | 111 | var now = currentTime(); |
112 | this.elapsed += now - this.intervalScheduledTime; | 112 | this.elapsed += now - this.intervalScheduledTime; |
113 | this.intervalScheduledTime = now; | 113 | this.intervalScheduledTime = now; |
@@ -127,7 +127,7 @@ export default class DataAggregator { | @@ -127,7 +127,7 @@ export default class DataAggregator { | ||
127 | this.data = toData(this.tsKeyNames, this.aggregationMap, this.startTs, this.endTs, this.$filter, this.limit); | 127 | this.data = toData(this.tsKeyNames, this.aggregationMap, this.startTs, this.endTs, this.$filter, this.limit); |
128 | } | 128 | } |
129 | if (this.onDataCb) { | 129 | if (this.onDataCb) { |
130 | - this.onDataCb(this.data, this.startTs, this.endTs); | 130 | + this.onDataCb(this.data, this.startTs, this.endTs, apply); |
131 | } | 131 | } |
132 | 132 | ||
133 | var self = this; | 133 | var self = this; |
@@ -197,7 +197,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -197,7 +197,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
197 | var datasourceKey = key + '_' + i; | 197 | var datasourceKey = key + '_' + i; |
198 | listener.dataUpdated(datasourceData[datasourceKey], | 198 | listener.dataUpdated(datasourceData[datasourceKey], |
199 | listener.datasourceIndex, | 199 | listener.datasourceIndex, |
200 | - dataKey.index); | 200 | + dataKey.index, false); |
201 | } | 201 | } |
202 | } | 202 | } |
203 | } else { | 203 | } else { |
@@ -205,7 +205,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -205,7 +205,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
205 | dataKey = dataKeys[key]; | 205 | dataKey = dataKeys[key]; |
206 | listener.dataUpdated(datasourceData[key], | 206 | listener.dataUpdated(datasourceData[key], |
207 | listener.datasourceIndex, | 207 | listener.datasourceIndex, |
208 | - dataKey.index); | 208 | + dataKey.index, false); |
209 | } | 209 | } |
210 | } | 210 | } |
211 | } | 211 | } |
@@ -264,7 +264,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -264,7 +264,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
264 | type: types.dataKeyType.timeseries, | 264 | type: types.dataKeyType.timeseries, |
265 | onData: function (data) { | 265 | onData: function (data) { |
266 | if (data.data) { | 266 | if (data.data) { |
267 | - onData(data.data, types.dataKeyType.timeseries); | 267 | + onData(data.data, types.dataKeyType.timeseries, null, null, true); |
268 | } | 268 | } |
269 | }, | 269 | }, |
270 | onReconnected: function() {} | 270 | onReconnected: function() {} |
@@ -287,9 +287,9 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -287,9 +287,9 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
287 | 287 | ||
288 | if (datasourceSubscription.type === types.widgetType.timeseries.value) { | 288 | if (datasourceSubscription.type === types.widgetType.timeseries.value) { |
289 | updateRealtimeSubscriptionCommand(subscriptionCommand, subsTw); | 289 | updateRealtimeSubscriptionCommand(subscriptionCommand, subsTw); |
290 | - dataAggregator = createRealtimeDataAggregator(subsTw, tsKeyNames); | 290 | + dataAggregator = createRealtimeDataAggregator(subsTw, tsKeyNames, types.dataKeyType.timeseries); |
291 | subscriber.onData = function(data) { | 291 | subscriber.onData = function(data) { |
292 | - dataAggregator.onData(data); | 292 | + dataAggregator.onData(data, false, false, true); |
293 | } | 293 | } |
294 | subscriber.onReconnected = function() { | 294 | subscriber.onReconnected = function() { |
295 | var newSubsTw = null; | 295 | var newSubsTw = null; |
@@ -308,7 +308,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -308,7 +308,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
308 | subscriber.onReconnected = function() {} | 308 | subscriber.onReconnected = function() {} |
309 | subscriber.onData = function(data) { | 309 | subscriber.onData = function(data) { |
310 | if (data.data) { | 310 | if (data.data) { |
311 | - onData(data.data, types.dataKeyType.timeseries); | 311 | + onData(data.data, types.dataKeyType.timeseries, null, null, true); |
312 | } | 312 | } |
313 | } | 313 | } |
314 | } | 314 | } |
@@ -331,7 +331,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -331,7 +331,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
331 | type: types.dataKeyType.attribute, | 331 | type: types.dataKeyType.attribute, |
332 | onData: function (data) { | 332 | onData: function (data) { |
333 | if (data.data) { | 333 | if (data.data) { |
334 | - onData(data.data, types.dataKeyType.attribute); | 334 | + onData(data.data, types.dataKeyType.attribute, null, null, true); |
335 | } | 335 | } |
336 | }, | 336 | }, |
337 | onReconnected: function() {} | 337 | onReconnected: function() {} |
@@ -351,33 +351,24 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -351,33 +351,24 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
351 | tsKeyNames.push(dataKey.name+'_'+dataKey.index); | 351 | tsKeyNames.push(dataKey.name+'_'+dataKey.index); |
352 | } | 352 | } |
353 | } | 353 | } |
354 | - dataAggregator = new DataAggregator( | ||
355 | - function (data, startTs, endTs) { | ||
356 | - onData(data, types.dataKeyType.function, startTs, endTs); | ||
357 | - }, | ||
358 | - tsKeyNames, | ||
359 | - subsTw.startTs, | ||
360 | - subsTw.aggregation.limit, | ||
361 | - subsTw.aggregation.type, | ||
362 | - subsTw.aggregation.timeWindow, | ||
363 | - subsTw.aggregation.interval, | ||
364 | - types, | ||
365 | - $timeout, | ||
366 | - $filter | ||
367 | - ); | 354 | + dataAggregator = createRealtimeDataAggregator(subsTw, tsKeyNames, types.dataKeyType.function); |
368 | } | 355 | } |
369 | if (history) { | 356 | if (history) { |
370 | - onTick(); | 357 | + onTick(false); |
371 | } else { | 358 | } else { |
372 | - timer = $timeout(onTick, 0, false); | 359 | + timer = $timeout( |
360 | + function() {onTick(true)}, | ||
361 | + 0, | ||
362 | + false | ||
363 | + ); | ||
373 | } | 364 | } |
374 | } | 365 | } |
375 | } | 366 | } |
376 | 367 | ||
377 | - function createRealtimeDataAggregator(subsTw, tsKeyNames) { | 368 | + function createRealtimeDataAggregator(subsTw, tsKeyNames, dataKeyType) { |
378 | return new DataAggregator( | 369 | return new DataAggregator( |
379 | - function(data, startTs, endTs) { | ||
380 | - onData(data, types.dataKeyType.timeseries, startTs, endTs); | 370 | + function(data, startTs, endTs, apply) { |
371 | + onData(data, dataKeyType, startTs, endTs, apply); | ||
381 | }, | 372 | }, |
382 | tsKeyNames, | 373 | tsKeyNames, |
383 | subsTw.startTs, | 374 | subsTw.startTs, |
@@ -443,7 +434,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -443,7 +434,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
443 | return data; | 434 | return data; |
444 | } | 435 | } |
445 | 436 | ||
446 | - function generateLatest(dataKey) { | 437 | + function generateLatest(dataKey, apply) { |
447 | var prevSeries; | 438 | var prevSeries; |
448 | var datasourceKeyData = datasourceData[dataKey.key].data; | 439 | var datasourceKeyData = datasourceData[dataKey.key].data; |
449 | if (datasourceKeyData.length > 0) { | 440 | if (datasourceKeyData.length > 0) { |
@@ -461,11 +452,11 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -461,11 +452,11 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
461 | var listener = listeners[i]; | 452 | var listener = listeners[i]; |
462 | listener.dataUpdated(datasourceData[dataKey.key], | 453 | listener.dataUpdated(datasourceData[dataKey.key], |
463 | listener.datasourceIndex, | 454 | listener.datasourceIndex, |
464 | - dataKey.index); | 455 | + dataKey.index, apply); |
465 | } | 456 | } |
466 | } | 457 | } |
467 | 458 | ||
468 | - function onTick() { | 459 | + function onTick(apply) { |
469 | var key; | 460 | var key; |
470 | if (datasourceSubscription.type === types.widgetType.timeseries.value) { | 461 | if (datasourceSubscription.type === types.widgetType.timeseries.value) { |
471 | var startTime; | 462 | var startTime; |
@@ -495,15 +486,15 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -495,15 +486,15 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
495 | generatedData.data[dataKey.name+'_'+dataKey.index] = data; | 486 | generatedData.data[dataKey.name+'_'+dataKey.index] = data; |
496 | } | 487 | } |
497 | } | 488 | } |
498 | - dataAggregator.onData(generatedData, true, history); | 489 | + dataAggregator.onData(generatedData, true, history, apply); |
499 | } else if (datasourceSubscription.type === types.widgetType.latest.value) { | 490 | } else if (datasourceSubscription.type === types.widgetType.latest.value) { |
500 | for (key in dataKeys) { | 491 | for (key in dataKeys) { |
501 | - generateLatest(dataKeys[key]); | 492 | + generateLatest(dataKeys[key], apply); |
502 | } | 493 | } |
503 | } | 494 | } |
504 | 495 | ||
505 | if (!history) { | 496 | if (!history) { |
506 | - timer = $timeout(onTick, frequency / 2, false); | 497 | + timer = $timeout(function() {onTick(true)}, frequency / 2, false); |
507 | } | 498 | } |
508 | } | 499 | } |
509 | 500 | ||
@@ -519,7 +510,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -519,7 +510,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
519 | } | 510 | } |
520 | } | 511 | } |
521 | 512 | ||
522 | - function onData(sourceData, type, startTs, endTs) { | 513 | + function onData(sourceData, type, startTs, endTs, apply) { |
523 | for (var keyName in sourceData) { | 514 | for (var keyName in sourceData) { |
524 | var keyData = sourceData[keyName]; | 515 | var keyData = sourceData[keyName]; |
525 | var key = keyName + '_' + type; | 516 | var key = keyName + '_' + type; |
@@ -572,7 +563,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -572,7 +563,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
572 | var listener = listeners[i2]; | 563 | var listener = listeners[i2]; |
573 | listener.dataUpdated(datasourceData[datasourceKey], | 564 | listener.dataUpdated(datasourceData[datasourceKey], |
574 | listener.datasourceIndex, | 565 | listener.datasourceIndex, |
575 | - dataKey.index); | 566 | + dataKey.index, apply); |
576 | } | 567 | } |
577 | } | 568 | } |
578 | } | 569 | } |
@@ -206,9 +206,9 @@ function TimeService($translate, types) { | @@ -206,9 +206,9 @@ function TimeService($translate, types) { | ||
206 | function defaultTimewindow() { | 206 | function defaultTimewindow() { |
207 | var currentTime = (new Date).getTime(); | 207 | var currentTime = (new Date).getTime(); |
208 | var timewindow = { | 208 | var timewindow = { |
209 | - displayValue: "", | ||
210 | - selectedTab: 0, | ||
211 | - realtime: { | 209 | + displayValue: "", |
210 | + selectedTab: 0, | ||
211 | + realtime: { | ||
212 | interval: SECOND, | 212 | interval: SECOND, |
213 | timewindowMs: MINUTE // 1 min by default | 213 | timewindowMs: MINUTE // 1 min by default |
214 | }, | 214 | }, |
@@ -52,6 +52,7 @@ function Dashboard() { | @@ -52,6 +52,7 @@ function Dashboard() { | ||
52 | bindToController: { | 52 | bindToController: { |
53 | widgets: '=', | 53 | widgets: '=', |
54 | deviceAliasList: '=', | 54 | deviceAliasList: '=', |
55 | + dashboardTimewindow: '=?', | ||
55 | columns: '=', | 56 | columns: '=', |
56 | margins: '=', | 57 | margins: '=', |
57 | isEdit: '=', | 58 | isEdit: '=', |
@@ -71,7 +72,8 @@ function Dashboard() { | @@ -71,7 +72,8 @@ function Dashboard() { | ||
71 | getStDiff: '&?', | 72 | getStDiff: '&?', |
72 | onInit: '&?', | 73 | onInit: '&?', |
73 | onInitFailed: '&?', | 74 | onInitFailed: '&?', |
74 | - dashboardStyle: '=?' | 75 | + dashboardStyle: '=?', |
76 | + dashboardClass: '=?' | ||
75 | }, | 77 | }, |
76 | controller: DashboardController, | 78 | controller: DashboardController, |
77 | controllerAs: 'vm', | 79 | controllerAs: 'vm', |
@@ -80,7 +82,7 @@ function Dashboard() { | @@ -80,7 +82,7 @@ function Dashboard() { | ||
80 | } | 82 | } |
81 | 83 | ||
82 | /*@ngInject*/ | 84 | /*@ngInject*/ |
83 | -function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $log, toast, types) { | 85 | +function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, timeService, types) { |
84 | 86 | ||
85 | var highlightedMode = false; | 87 | var highlightedMode = false; |
86 | var highlightedWidget = null; | 88 | var highlightedWidget = null; |
@@ -99,6 +101,10 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ | @@ -99,6 +101,10 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ | ||
99 | 101 | ||
100 | vm.isMobileDisabled = angular.isDefined(vm.isMobileDisabled) ? vm.isMobileDisabled : false; | 102 | vm.isMobileDisabled = angular.isDefined(vm.isMobileDisabled) ? vm.isMobileDisabled : false; |
101 | 103 | ||
104 | + if (!('dashboardTimewindow' in vm)) { | ||
105 | + vm.dashboardTimewindow = timeService.defaultTimewindow(); | ||
106 | + } | ||
107 | + | ||
102 | vm.dashboardLoading = true; | 108 | vm.dashboardLoading = true; |
103 | vm.visibleRect = { | 109 | vm.visibleRect = { |
104 | top: 0, | 110 | top: 0, |
@@ -176,6 +182,21 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ | @@ -176,6 +182,21 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ | ||
176 | vm.widgetContextMenuItems = []; | 182 | vm.widgetContextMenuItems = []; |
177 | vm.widgetContextMenuEvent = null; | 183 | vm.widgetContextMenuEvent = null; |
178 | 184 | ||
185 | + vm.dashboardTimewindowApi = { | ||
186 | + onResetTimewindow: function() { | ||
187 | + if (vm.originalDashboardTimewindow) { | ||
188 | + vm.dashboardTimewindow = angular.copy(vm.originalDashboardTimewindow); | ||
189 | + vm.originalDashboardTimewindow = null; | ||
190 | + } | ||
191 | + }, | ||
192 | + onUpdateTimewindow: function(startTimeMs, endTimeMs) { | ||
193 | + if (!vm.originalDashboardTimewindow) { | ||
194 | + vm.originalDashboardTimewindow = angular.copy(vm.dashboardTimewindow); | ||
195 | + } | ||
196 | + vm.dashboardTimewindow = timeService.toHistoryTimewindow(vm.dashboardTimewindow, startTimeMs, endTimeMs); | ||
197 | + } | ||
198 | + }; | ||
199 | + | ||
179 | //$element[0].onmousemove=function(){ | 200 | //$element[0].onmousemove=function(){ |
180 | // widgetMouseMove(); | 201 | // widgetMouseMove(); |
181 | // } | 202 | // } |
@@ -656,7 +677,12 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ | @@ -656,7 +677,12 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ | ||
656 | } | 677 | } |
657 | 678 | ||
658 | function hasTimewindow(widget) { | 679 | function hasTimewindow(widget) { |
659 | - return widget.type === types.widgetType.timeseries.value; | 680 | + if (widget.type === types.widgetType.timeseries.value) { |
681 | + return angular.isDefined(widget.config.useDashboardTimewindow) ? | ||
682 | + !widget.config.useDashboardTimewindow : false; | ||
683 | + } else { | ||
684 | + return false; | ||
685 | + } | ||
660 | } | 686 | } |
661 | 687 | ||
662 | function adoptMaxRows() { | 688 | function adoptMaxRows() { |
@@ -673,6 +699,9 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ | @@ -673,6 +699,9 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ | ||
673 | 699 | ||
674 | function dashboardLoaded() { | 700 | function dashboardLoaded() { |
675 | $timeout(function () { | 701 | $timeout(function () { |
702 | + $scope.$watch('vm.dashboardTimewindow', function () { | ||
703 | + $scope.$broadcast('dashboardTimewindowChanged', vm.dashboardTimewindow); | ||
704 | + }, true); | ||
676 | adoptMaxRows(); | 705 | adoptMaxRows(); |
677 | vm.dashboardLoading = false; | 706 | vm.dashboardLoading = false; |
678 | $timeout(function () { | 707 | $timeout(function () { |
@@ -51,7 +51,7 @@ div.tb-widget { | @@ -51,7 +51,7 @@ div.tb-widget { | ||
51 | height: 32px; | 51 | height: 32px; |
52 | min-width: 32px; | 52 | min-width: 32px; |
53 | min-height: 32px; | 53 | min-height: 32px; |
54 | - md-icon { | 54 | + md-icon, ng-md-icon { |
55 | width: 20px; | 55 | width: 20px; |
56 | height: 20px; | 56 | height: 20px; |
57 | min-width: 20px; | 57 | min-width: 20px; |
@@ -93,6 +93,7 @@ md-content.tb-dashboard-content { | @@ -93,6 +93,7 @@ md-content.tb-dashboard-content { | ||
93 | right: 0; | 93 | right: 0; |
94 | bottom: 0; | 94 | bottom: 0; |
95 | outline: none; | 95 | outline: none; |
96 | + background: none; | ||
96 | .gridster-item { | 97 | .gridster-item { |
97 | @include transition(none); | 98 | @include transition(none); |
98 | } | 99 | } |
@@ -21,7 +21,7 @@ | @@ -21,7 +21,7 @@ | ||
21 | </md-content> | 21 | </md-content> |
22 | <md-menu md-position-mode="target target" tb-mousepoint-menu> | 22 | <md-menu md-position-mode="target target" tb-mousepoint-menu> |
23 | <md-content id="gridster-parent" class="tb-dashboard-content" flex layout-wrap ng-click="" tb-contextmenu="vm.openDashboardContextMenu($event, $mdOpenMousepointMenu)"> | 23 | <md-content id="gridster-parent" class="tb-dashboard-content" flex layout-wrap ng-click="" tb-contextmenu="vm.openDashboardContextMenu($event, $mdOpenMousepointMenu)"> |
24 | - <div ng-style="vm.dashboardStyle" id="gridster-background" style="height: auto; min-height: 100%;"> | 24 | + <div ng-class="vm.dashboardClass" id="gridster-background" style="height: auto; min-height: 100%;"> |
25 | <div id="gridster-child" gridster="vm.gridsterOpts"> | 25 | <div id="gridster-child" gridster="vm.gridsterOpts"> |
26 | <ul> | 26 | <ul> |
27 | <!-- ng-click="widgetClicked($event, widget)" --> | 27 | <!-- ng-click="widgetClicked($event, widget)" --> |
@@ -30,6 +30,7 @@ | @@ -30,6 +30,7 @@ | ||
30 | <div tb-expand-fullscreen | 30 | <div tb-expand-fullscreen |
31 | fullscreen-background-style="vm.dashboardStyle" | 31 | fullscreen-background-style="vm.dashboardStyle" |
32 | expand-button-id="expand-button" | 32 | expand-button-id="expand-button" |
33 | + expand-button-size="20" | ||
33 | on-fullscreen-changed="vm.onWidgetFullscreenChanged(expanded, widget)" | 34 | on-fullscreen-changed="vm.onWidgetFullscreenChanged(expanded, widget)" |
34 | layout="column" | 35 | layout="column" |
35 | class="tb-widget" | 36 | class="tb-widget" |
@@ -45,55 +46,55 @@ | @@ -45,55 +46,55 @@ | ||
45 | color: vm.widgetColor(widget), | 46 | color: vm.widgetColor(widget), |
46 | backgroundColor: vm.widgetBackgroundColor(widget), | 47 | backgroundColor: vm.widgetBackgroundColor(widget), |
47 | padding: vm.widgetPadding(widget)}"> | 48 | padding: vm.widgetPadding(widget)}"> |
48 | - <div class="tb-widget-title" layout="column" ng-show="vm.showWidgetTitle(widget) || vm.hasTimewindow(widget)"> | 49 | + <div class="tb-widget-title" layout="column" layout-align="center start" ng-show="vm.showWidgetTitle(widget) || vm.hasTimewindow(widget)"> |
49 | <span ng-show="vm.showWidgetTitle(widget)" ng-style="vm.widgetTitleStyle(widget)" class="md-subhead">{{widget.config.title}}</span> | 50 | <span ng-show="vm.showWidgetTitle(widget)" ng-style="vm.widgetTitleStyle(widget)" class="md-subhead">{{widget.config.title}}</span> |
50 | - <tb-timewindow button-color="vm.widgetColor(widget)" aggregation ng-if="vm.hasTimewindow(widget)" ng-model="widget.config.timewindow"></tb-timewindow> | 51 | + <tb-timewindow aggregation ng-if="vm.hasTimewindow(widget)" ng-model="widget.config.timewindow"></tb-timewindow> |
51 | </div> | 52 | </div> |
52 | <div class="tb-widget-actions" layout="row" layout-align="start center"> | 53 | <div class="tb-widget-actions" layout="row" layout-align="start center"> |
53 | <md-button id="expand-button" | 54 | <md-button id="expand-button" |
54 | ng-show="!vm.isEdit && vm.enableWidgetFullscreen(widget)" | 55 | ng-show="!vm.isEdit && vm.enableWidgetFullscreen(widget)" |
55 | aria-label="{{ 'fullscreen.fullscreen' | translate }}" | 56 | aria-label="{{ 'fullscreen.fullscreen' | translate }}" |
56 | - class="md-icon-button md-primary"></md-button> | 57 | + class="md-icon-button"></md-button> |
57 | <md-button ng-show="vm.isEditActionEnabled && !vm.isWidgetExpanded" | 58 | <md-button ng-show="vm.isEditActionEnabled && !vm.isWidgetExpanded" |
58 | ng-disabled="vm.loading()" | 59 | ng-disabled="vm.loading()" |
59 | - class="md-icon-button md-primary" | 60 | + class="md-icon-button" |
60 | ng-click="vm.editWidget($event, widget)" | 61 | ng-click="vm.editWidget($event, widget)" |
61 | aria-label="{{ 'widget.edit' | translate }}"> | 62 | aria-label="{{ 'widget.edit' | translate }}"> |
62 | <md-tooltip md-direction="top"> | 63 | <md-tooltip md-direction="top"> |
63 | {{ 'widget.edit' | translate }} | 64 | {{ 'widget.edit' | translate }} |
64 | </md-tooltip> | 65 | </md-tooltip> |
65 | - <md-icon class="material-icons"> | ||
66 | - edit | ||
67 | - </md-icon> | 66 | + <ng-md-icon size="20" icon="edit"></ng-md-icon> |
68 | </md-button> | 67 | </md-button> |
69 | <md-button ng-show="vm.isExportActionEnabled && !vm.isWidgetExpanded" | 68 | <md-button ng-show="vm.isExportActionEnabled && !vm.isWidgetExpanded" |
70 | ng-disabled="vm.loading()" | 69 | ng-disabled="vm.loading()" |
71 | - class="md-icon-button md-primary" | 70 | + class="md-icon-button" |
72 | ng-click="vm.exportWidget($event, widget)" | 71 | ng-click="vm.exportWidget($event, widget)" |
73 | aria-label="{{ 'widget.export' | translate }}"> | 72 | aria-label="{{ 'widget.export' | translate }}"> |
74 | <md-tooltip md-direction="top"> | 73 | <md-tooltip md-direction="top"> |
75 | {{ 'widget.export' | translate }} | 74 | {{ 'widget.export' | translate }} |
76 | </md-tooltip> | 75 | </md-tooltip> |
77 | - <md-icon class="material-icons"> | ||
78 | - file_download | ||
79 | - </md-icon> | 76 | + <ng-md-icon size="20" icon="file_download"></ng-md-icon> |
80 | </md-button> | 77 | </md-button> |
81 | <md-button ng-show="vm.isRemoveActionEnabled && !vm.isWidgetExpanded" | 78 | <md-button ng-show="vm.isRemoveActionEnabled && !vm.isWidgetExpanded" |
82 | ng-disabled="vm.loading()" | 79 | ng-disabled="vm.loading()" |
83 | - class="md-icon-button md-primary" | 80 | + class="md-icon-button" |
84 | ng-click="vm.removeWidget($event, widget)" | 81 | ng-click="vm.removeWidget($event, widget)" |
85 | aria-label="{{ 'widget.remove' | translate }}"> | 82 | aria-label="{{ 'widget.remove' | translate }}"> |
86 | <md-tooltip md-direction="top"> | 83 | <md-tooltip md-direction="top"> |
87 | {{ 'widget.remove' | translate }} | 84 | {{ 'widget.remove' | translate }} |
88 | </md-tooltip> | 85 | </md-tooltip> |
89 | - <md-icon class="material-icons"> | ||
90 | - close | ||
91 | - </md-icon> | 86 | + <ng-md-icon size="20" icon="close"></ng-md-icon> |
92 | </md-button> | 87 | </md-button> |
93 | </div> | 88 | </div> |
94 | <div flex layout="column" class="tb-widget-content"> | 89 | <div flex layout="column" class="tb-widget-content"> |
95 | <div flex tb-widget | 90 | <div flex tb-widget |
96 | - locals="{ visibleRect: vm.visibleRect, widget: widget, deviceAliasList: vm.deviceAliasList, isEdit: vm.isEdit, stDiff: vm.stDiff }"> | 91 | + locals="{ visibleRect: vm.visibleRect, |
92 | + widget: widget, | ||
93 | + deviceAliasList: vm.deviceAliasList, | ||
94 | + isEdit: vm.isEdit, | ||
95 | + stDiff: vm.stDiff, | ||
96 | + dashboardTimewindow: vm.dashboardTimewindow, | ||
97 | + dashboardTimewindowApi: vm.dashboardTimewindowApi }"> | ||
97 | </div> | 98 | </div> |
98 | </div> | 99 | </div> |
99 | </div> | 100 | </div> |
@@ -101,11 +101,15 @@ function ExpandFullscreen($compile, $document) { | @@ -101,11 +101,15 @@ function ExpandFullscreen($compile, $document) { | ||
101 | if (attrs.expandButtonId) { | 101 | if (attrs.expandButtonId) { |
102 | expandButton = $('#' + attrs.expandButtonId, element)[0]; | 102 | expandButton = $('#' + attrs.expandButtonId, element)[0]; |
103 | } | 103 | } |
104 | + var buttonSize; | ||
105 | + if (attrs.expandButtonSize) { | ||
106 | + buttonSize = attrs.expandButtonSize; | ||
107 | + } | ||
104 | 108 | ||
105 | var html = '<md-tooltip md-direction="{{expanded ? \'bottom\' : \'top\'}}">' + | 109 | var html = '<md-tooltip md-direction="{{expanded ? \'bottom\' : \'top\'}}">' + |
106 | '{{(expanded ? \'fullscreen.exit\' : \'fullscreen.expand\') | translate}}' + | 110 | '{{(expanded ? \'fullscreen.exit\' : \'fullscreen.expand\') | translate}}' + |
107 | '</md-tooltip>' + | 111 | '</md-tooltip>' + |
108 | - '<ng-md-icon icon="{{expanded ? \'fullscreen_exit\' : \'fullscreen\'}}" ' + | 112 | + '<ng-md-icon ' + (buttonSize ? 'size="'+ buttonSize +'" ' : '') + 'icon="{{expanded ? \'fullscreen_exit\' : \'fullscreen\'}}" ' + |
109 | 'options=\'{"easing": "circ-in-out", "duration": 375, "rotation": "none"}\'>' + | 113 | 'options=\'{"easing": "circ-in-out", "duration": 375, "rotation": "none"}\'>' + |
110 | '</ng-md-icon>'; | 114 | '</ng-md-icon>'; |
111 | 115 |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<md-button class="md-raised md-primary" ng-click="openEditMode($event)"> | ||
19 | - <md-icon class="material-icons">date_range</md-icon> | 18 | +<md-button ng-disabled="disabled" class="md-raised md-primary" ng-click="openEditMode($event)"> |
19 | + <ng-md-icon icon="query_builder"></ng-md-icon> | ||
20 | <span>{{model.displayValue}}</span> | 20 | <span>{{model.displayValue}}</span> |
21 | </md-button> | 21 | </md-button> |
@@ -79,26 +79,38 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM | @@ -79,26 +79,38 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM | ||
79 | if (scope.asButton) { | 79 | if (scope.asButton) { |
80 | template = $templateCache.get(timewindowButtonTemplate); | 80 | template = $templateCache.get(timewindowButtonTemplate); |
81 | } else { | 81 | } else { |
82 | + scope.direction = scope.direction || 'left'; | ||
82 | template = $templateCache.get(timewindowTemplate); | 83 | template = $templateCache.get(timewindowTemplate); |
83 | } | 84 | } |
84 | element.html(template); | 85 | element.html(template); |
85 | 86 | ||
86 | scope.openEditMode = function (event) { | 87 | scope.openEditMode = function (event) { |
88 | + if (scope.disabled) { | ||
89 | + return; | ||
90 | + } | ||
87 | var position; | 91 | var position; |
88 | var isGtSm = $mdMedia('gt-sm'); | 92 | var isGtSm = $mdMedia('gt-sm'); |
89 | if (isGtSm) { | 93 | if (isGtSm) { |
90 | var panelHeight = 375; | 94 | var panelHeight = 375; |
95 | + var panelWidth = 417; | ||
91 | var offset = element[0].getBoundingClientRect(); | 96 | var offset = element[0].getBoundingClientRect(); |
92 | var bottomY = offset.bottom - $(window).scrollTop(); //eslint-disable-line | 97 | var bottomY = offset.bottom - $(window).scrollTop(); //eslint-disable-line |
98 | + var leftX = offset.left - $(window).scrollLeft(); //eslint-disable-line | ||
93 | var yPosition; | 99 | var yPosition; |
100 | + var xPosition; | ||
94 | if (bottomY + panelHeight > $( window ).height()) { //eslint-disable-line | 101 | if (bottomY + panelHeight > $( window ).height()) { //eslint-disable-line |
95 | yPosition = $mdPanel.yPosition.ABOVE; | 102 | yPosition = $mdPanel.yPosition.ABOVE; |
96 | } else { | 103 | } else { |
97 | yPosition = $mdPanel.yPosition.BELOW; | 104 | yPosition = $mdPanel.yPosition.BELOW; |
98 | } | 105 | } |
106 | + if (leftX + panelWidth > $( window ).width()) { //eslint-disable-line | ||
107 | + xPosition = $mdPanel.xPosition.ALIGN_END; | ||
108 | + } else { | ||
109 | + xPosition = $mdPanel.xPosition.ALIGN_START; | ||
110 | + } | ||
99 | position = $mdPanel.newPanelPosition() | 111 | position = $mdPanel.newPanelPosition() |
100 | .relativeTo(element) | 112 | .relativeTo(element) |
101 | - .addPanelPosition($mdPanel.xPosition.ALIGN_START, yPosition); | 113 | + .addPanelPosition(xPosition, yPosition); |
102 | } else { | 114 | } else { |
103 | position = $mdPanel.newPanelPosition() | 115 | position = $mdPanel.newPanelPosition() |
104 | .absolute() | 116 | .absolute() |
@@ -223,7 +235,8 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM | @@ -223,7 +235,8 @@ function Timewindow($compile, $templateCache, $filter, $mdPanel, $document, $mdM | ||
223 | require: "^ngModel", | 235 | require: "^ngModel", |
224 | scope: { | 236 | scope: { |
225 | asButton: '=asButton', | 237 | asButton: '=asButton', |
226 | - buttonColor: '=?' | 238 | + direction: '=?', |
239 | + disabled:'=ngDisabled' | ||
227 | }, | 240 | }, |
228 | link: linker | 241 | link: linker |
229 | }; | 242 | }; |
@@ -15,9 +15,23 @@ | @@ -15,9 +15,23 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<section layout='row' layout-align="start center" style="min-height: 32px;"> | ||
19 | - <span ng-click="openEditMode($event)">{{model.displayValue}}</span> | ||
20 | - <md-button class="md-icon-button tb-md-32" aria-label="{{ 'timewindow.edit' | translate }}" ng-click="openEditMode($event)"> | ||
21 | - <md-icon ng-style="{ color: buttonColor }" aria-label="{{ 'timewindow.date-range' | translate }}" class="material-icons">date_range</md-icon> | 18 | +<section layout='row' layout-align="start center" ng-style="{minHeight: '32px', padding: '0 6px'}"> |
19 | + <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)"> | ||
20 | + <md-tooltip md-direction="top"> | ||
21 | + {{ 'timewindow.edit' | translate }} | ||
22 | + </md-tooltip> | ||
23 | + <ng-md-icon aria-label="{{ 'timewindow.date-range' | translate }}" icon="query_builder"></ng-md-icon> | ||
24 | + </md-button> | ||
25 | + <span ng-click="openEditMode($event)"> | ||
26 | + <md-tooltip md-direction="top"> | ||
27 | + {{ 'timewindow.edit' | translate }} | ||
28 | + </md-tooltip> | ||
29 | + {{model.displayValue}} | ||
30 | + </span> | ||
31 | + <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)"> | ||
32 | + <md-tooltip md-direction="top"> | ||
33 | + {{ 'timewindow.edit' | translate }} | ||
34 | + </md-tooltip> | ||
35 | + <ng-md-icon aria-label="{{ 'timewindow.date-range' | translate }}" icon="query_builder"></ng-md-icon> | ||
22 | </md-button> | 36 | </md-button> |
23 | </section> | 37 | </section> |
@@ -98,6 +98,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti | @@ -98,6 +98,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti | ||
98 | }, true); | 98 | }, true); |
99 | scope.mobileOrder = ngModelCtrl.$viewValue.mobileOrder; | 99 | scope.mobileOrder = ngModelCtrl.$viewValue.mobileOrder; |
100 | scope.mobileHeight = ngModelCtrl.$viewValue.mobileHeight; | 100 | scope.mobileHeight = ngModelCtrl.$viewValue.mobileHeight; |
101 | + scope.useDashboardTimewindow = angular.isDefined(ngModelCtrl.$viewValue.useDashboardTimewindow) ? | ||
102 | + ngModelCtrl.$viewValue.useDashboardTimewindow : true; | ||
101 | scope.timewindow = ngModelCtrl.$viewValue.timewindow; | 103 | scope.timewindow = ngModelCtrl.$viewValue.timewindow; |
102 | if (scope.widgetType !== types.widgetType.rpc.value && scope.widgetType !== types.widgetType.static.value) { | 104 | if (scope.widgetType !== types.widgetType.rpc.value && scope.widgetType !== types.widgetType.static.value) { |
103 | if (scope.datasources) { | 105 | if (scope.datasources) { |
@@ -174,7 +176,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti | @@ -174,7 +176,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti | ||
174 | } | 176 | } |
175 | }; | 177 | }; |
176 | 178 | ||
177 | - scope.$watch('title + showTitle + dropShadow + enableFullscreen + backgroundColor + color + padding + titleStyle + mobileOrder + mobileHeight + intervalSec', function () { | 179 | + scope.$watch('title + showTitle + dropShadow + enableFullscreen + backgroundColor + color + ' + |
180 | + 'padding + titleStyle + mobileOrder + mobileHeight + useDashboardTimewindow', function () { | ||
178 | if (ngModelCtrl.$viewValue) { | 181 | if (ngModelCtrl.$viewValue) { |
179 | var value = ngModelCtrl.$viewValue; | 182 | var value = ngModelCtrl.$viewValue; |
180 | value.title = scope.title; | 183 | value.title = scope.title; |
@@ -191,7 +194,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti | @@ -191,7 +194,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti | ||
191 | } | 194 | } |
192 | value.mobileOrder = angular.isNumber(scope.mobileOrder) ? scope.mobileOrder : undefined; | 195 | value.mobileOrder = angular.isNumber(scope.mobileOrder) ? scope.mobileOrder : undefined; |
193 | value.mobileHeight = scope.mobileHeight; | 196 | value.mobileHeight = scope.mobileHeight; |
194 | - value.intervalSec = scope.intervalSec; | 197 | + value.useDashboardTimewindow = scope.useDashboardTimewindow; |
195 | ngModelCtrl.$setViewValue(value); | 198 | ngModelCtrl.$setViewValue(value); |
196 | scope.updateValidity(); | 199 | scope.updateValidity(); |
197 | } | 200 | } |
@@ -88,10 +88,15 @@ | @@ -88,10 +88,15 @@ | ||
88 | <input ng-model="mobileHeight" type="number"> | 88 | <input ng-model="mobileHeight" type="number"> |
89 | </md-input-container> | 89 | </md-input-container> |
90 | </div> | 90 | </div> |
91 | - <div ng-show="widgetType === types.widgetType.timeseries.value" layout="row" | ||
92 | - layout-align="center center"> | ||
93 | - <span translate style="padding-right: 8px;">widget-config.timewindow</span> | ||
94 | - <tb-timewindow as-button="true" aggregation flex ng-model="timewindow"></tb-timewindow> | 91 | + <div ng-show="widgetType === types.widgetType.timeseries.value" layout='column' layout-align="center" |
92 | + layout-gt-sm='row' layout-align-gt-sm="start center"> | ||
93 | + <md-checkbox flex aria-label="{{ 'widget-config.use-dashboard-timewindow' | translate }}" | ||
94 | + ng-model="useDashboardTimewindow">{{ 'widget-config.use-dashboard-timewindow' | translate }} | ||
95 | + </md-checkbox> | ||
96 | + <section flex layout="row" layout-align="start center" style="margin-bottom: 16px;"> | ||
97 | + <span ng-class="{'tb-disabled-label': useDashboardTimewindow}" translate style="padding-right: 8px;">widget-config.timewindow</span> | ||
98 | + <tb-timewindow ng-disabled="useDashboardTimewindow" as-button="true" aggregation flex ng-model="timewindow"></tb-timewindow> | ||
99 | + </section> | ||
95 | </div> | 100 | </div> |
96 | <v-accordion id="datasources-accordion" control="datasourcesAccordion" class="vAccordion--default" | 101 | <v-accordion id="datasources-accordion" control="datasourcesAccordion" class="vAccordion--default" |
97 | ng-show="widgetType !== types.widgetType.rpc.value && widgetType !== types.widgetType.static.value"> | 102 | ng-show="widgetType !== types.widgetType.rpc.value && widgetType !== types.widgetType.static.value"> |
@@ -20,7 +20,8 @@ import 'javascript-detect-element-resize/detect-element-resize'; | @@ -20,7 +20,8 @@ import 'javascript-detect-element-resize/detect-element-resize'; | ||
20 | 20 | ||
21 | /*@ngInject*/ | 21 | /*@ngInject*/ |
22 | export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, tbRaf, types, utils, timeService, | 22 | export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, tbRaf, types, utils, timeService, |
23 | - datasourceService, deviceService, visibleRect, isEdit, stDiff, widget, deviceAliasList, widgetType) { | 23 | + datasourceService, deviceService, visibleRect, isEdit, stDiff, dashboardTimewindow, |
24 | + dashboardTimewindowApi, widget, deviceAliasList, widgetType) { | ||
24 | 25 | ||
25 | var vm = this; | 26 | var vm = this; |
26 | 27 | ||
@@ -136,6 +137,24 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -136,6 +137,24 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
136 | $scope.widgetErrorData = utils.processWidgetException(e); | 137 | $scope.widgetErrorData = utils.processWidgetException(e); |
137 | } | 138 | } |
138 | 139 | ||
140 | + function notifyDataLoaded(apply) { | ||
141 | + if ($scope.loadingData === true) { | ||
142 | + $scope.loadingData = false; | ||
143 | + if (apply) { | ||
144 | + $scope.$digest(); | ||
145 | + } | ||
146 | + } | ||
147 | + } | ||
148 | + | ||
149 | + function notifyDataLoading(apply) { | ||
150 | + if ($scope.loadingData === false) { | ||
151 | + $scope.loadingData = true; | ||
152 | + if (apply) { | ||
153 | + $scope.$digest(); | ||
154 | + } | ||
155 | + } | ||
156 | + } | ||
157 | + | ||
139 | function onInit() { | 158 | function onInit() { |
140 | if (!widgetContext.inited) { | 159 | if (!widgetContext.inited) { |
141 | widgetContext.inited = true; | 160 | widgetContext.inited = true; |
@@ -274,7 +293,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -274,7 +293,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
274 | } | 293 | } |
275 | 294 | ||
276 | function initialize() { | 295 | function initialize() { |
277 | - if (widget.type !== types.widgetType.rpc.value) { | 296 | + if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) { |
278 | for (var i in widget.config.datasources) { | 297 | for (var i in widget.config.datasources) { |
279 | var datasource = angular.copy(widget.config.datasources[i]); | 298 | var datasource = angular.copy(widget.config.datasources[i]); |
280 | for (var a in datasource.dataKeys) { | 299 | for (var a in datasource.dataKeys) { |
@@ -287,7 +306,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -287,7 +306,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
287 | widgetContext.data.push(datasourceData); | 306 | widgetContext.data.push(datasourceData); |
288 | } | 307 | } |
289 | } | 308 | } |
290 | - } else { | 309 | + } else if (widget.type === types.widgetType.rpc.value) { |
291 | if (widget.config.targetDeviceAliasIds && widget.config.targetDeviceAliasIds.length > 0) { | 310 | if (widget.config.targetDeviceAliasIds && widget.config.targetDeviceAliasIds.length > 0) { |
292 | targetDeviceAliasId = widget.config.targetDeviceAliasIds[0]; | 311 | targetDeviceAliasId = widget.config.targetDeviceAliasIds[0]; |
293 | if (deviceAliasList[targetDeviceAliasId]) { | 312 | if (deviceAliasList[targetDeviceAliasId]) { |
@@ -354,14 +373,26 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -354,14 +373,26 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
354 | }); | 373 | }); |
355 | 374 | ||
356 | if (widget.type === types.widgetType.timeseries.value) { | 375 | if (widget.type === types.widgetType.timeseries.value) { |
357 | - $scope.$watch(function () { | ||
358 | - return widget.config.timewindow; | ||
359 | - }, function (newTimewindow, prevTimewindow) { | ||
360 | - if (!angular.equals(newTimewindow, prevTimewindow)) { | ||
361 | - unsubscribe(); | ||
362 | - subscribe(); | ||
363 | - } | ||
364 | - }); | 376 | + widgetContext.useDashboardTimewindow = angular.isDefined(widget.config.useDashboardTimewindow) |
377 | + ? widget.config.useDashboardTimewindow : true; | ||
378 | + if (widgetContext.useDashboardTimewindow) { | ||
379 | + $scope.$on('dashboardTimewindowChanged', function (event, newDashboardTimewindow) { | ||
380 | + if (!angular.equals(dashboardTimewindow, newDashboardTimewindow)) { | ||
381 | + dashboardTimewindow = newDashboardTimewindow; | ||
382 | + unsubscribe(); | ||
383 | + subscribe(); | ||
384 | + } | ||
385 | + }); | ||
386 | + } else { | ||
387 | + $scope.$watch(function () { | ||
388 | + return widgetContext.useDashboardTimewindow ? dashboardTimewindow : widget.config.timewindow; | ||
389 | + }, function (newTimewindow, prevTimewindow) { | ||
390 | + if (!angular.equals(newTimewindow, prevTimewindow)) { | ||
391 | + unsubscribe(); | ||
392 | + subscribe(); | ||
393 | + } | ||
394 | + }); | ||
395 | + } | ||
365 | } | 396 | } |
366 | subscribe(); | 397 | subscribe(); |
367 | } | 398 | } |
@@ -474,20 +505,29 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -474,20 +505,29 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
474 | }*/ | 505 | }*/ |
475 | 506 | ||
476 | function onResetTimewindow() { | 507 | function onResetTimewindow() { |
477 | - if (originalTimewindow) { | ||
478 | - widget.config.timewindow = angular.copy(originalTimewindow); | ||
479 | - originalTimewindow = null; | 508 | + if (widgetContext.useDashboardTimewindow) { |
509 | + dashboardTimewindowApi.onResetTimewindow(); | ||
510 | + } else { | ||
511 | + if (originalTimewindow) { | ||
512 | + widget.config.timewindow = angular.copy(originalTimewindow); | ||
513 | + originalTimewindow = null; | ||
514 | + } | ||
480 | } | 515 | } |
481 | } | 516 | } |
482 | 517 | ||
483 | function onUpdateTimewindow(startTimeMs, endTimeMs) { | 518 | function onUpdateTimewindow(startTimeMs, endTimeMs) { |
484 | - if (!originalTimewindow) { | ||
485 | - originalTimewindow = angular.copy(widget.config.timewindow); | 519 | + if (widgetContext.useDashboardTimewindow) { |
520 | + dashboardTimewindowApi.onUpdateTimewindow(startTimeMs, endTimeMs); | ||
521 | + } else { | ||
522 | + if (!originalTimewindow) { | ||
523 | + originalTimewindow = angular.copy(widget.config.timewindow); | ||
524 | + } | ||
525 | + widget.config.timewindow = timeService.toHistoryTimewindow(widget.config.timewindow, startTimeMs, endTimeMs); | ||
486 | } | 526 | } |
487 | - widget.config.timewindow = timeService.toHistoryTimewindow(widget.config.timewindow, startTimeMs, endTimeMs); | ||
488 | } | 527 | } |
489 | 528 | ||
490 | - function dataUpdated(sourceData, datasourceIndex, dataKeyIndex) { | 529 | + function dataUpdated(sourceData, datasourceIndex, dataKeyIndex, apply) { |
530 | + notifyDataLoaded(apply); | ||
491 | var update = true; | 531 | var update = true; |
492 | if (widget.type === types.widgetType.latest.value) { | 532 | if (widget.type === types.widgetType.latest.value) { |
493 | var prevData = widgetContext.data[datasourceIndex + dataKeyIndex].data; | 533 | var prevData = widgetContext.data[datasourceIndex + dataKeyIndex].data; |
@@ -547,16 +587,28 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -547,16 +587,28 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
547 | if (_subscriptionTimewindow) { | 587 | if (_subscriptionTimewindow) { |
548 | subscriptionTimewindow = _subscriptionTimewindow; | 588 | subscriptionTimewindow = _subscriptionTimewindow; |
549 | } else { | 589 | } else { |
550 | - subscriptionTimewindow = timeService.createSubscriptionTimewindow(widget.config.timewindow, widgetContext.timeWindow.stDiff); | 590 | + subscriptionTimewindow = |
591 | + timeService.createSubscriptionTimewindow( | ||
592 | + widgetContext.useDashboardTimewindow ? dashboardTimewindow : widget.config.timewindow, | ||
593 | + widgetContext.timeWindow.stDiff); | ||
551 | } | 594 | } |
552 | updateTimewindow(); | 595 | updateTimewindow(); |
553 | return subscriptionTimewindow; | 596 | return subscriptionTimewindow; |
554 | } | 597 | } |
555 | 598 | ||
599 | + function hasTimewindow() { | ||
600 | + if (widgetContext.useDashboardTimewindow) { | ||
601 | + return angular.isDefined(dashboardTimewindow); | ||
602 | + } else { | ||
603 | + return angular.isDefined(widget.config.timewindow); | ||
604 | + } | ||
605 | + } | ||
606 | + | ||
556 | function subscribe() { | 607 | function subscribe() { |
557 | - if (widget.type !== types.widgetType.rpc.value) { | 608 | + if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) { |
609 | + notifyDataLoading(); | ||
558 | if (widget.type === types.widgetType.timeseries.value && | 610 | if (widget.type === types.widgetType.timeseries.value && |
559 | - angular.isDefined(widget.config.timewindow)) { | 611 | + hasTimewindow()) { |
560 | updateRealtimeSubscription(); | 612 | updateRealtimeSubscription(); |
561 | if (subscriptionTimewindow.fixedWindow) { | 613 | if (subscriptionTimewindow.fixedWindow) { |
562 | onDataUpdated(); | 614 | onDataUpdated(); |
@@ -579,8 +631,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -579,8 +631,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
579 | subscriptionTimewindow: subscriptionTimewindow, | 631 | subscriptionTimewindow: subscriptionTimewindow, |
580 | datasource: datasource, | 632 | datasource: datasource, |
581 | deviceId: deviceId, | 633 | deviceId: deviceId, |
582 | - dataUpdated: function (data, datasourceIndex, dataKeyIndex) { | ||
583 | - dataUpdated(data, datasourceIndex, dataKeyIndex); | 634 | + dataUpdated: function (data, datasourceIndex, dataKeyIndex, apply) { |
635 | + dataUpdated(data, datasourceIndex, dataKeyIndex, apply); | ||
584 | }, | 636 | }, |
585 | updateRealtimeSubscription: function() { | 637 | updateRealtimeSubscription: function() { |
586 | this.subscriptionTimewindow = updateRealtimeSubscription(); | 638 | this.subscriptionTimewindow = updateRealtimeSubscription(); |
@@ -601,6 +653,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -601,6 +653,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
601 | datasourceListeners.push(listener); | 653 | datasourceListeners.push(listener); |
602 | datasourceService.subscribeToDatasource(listener); | 654 | datasourceService.subscribeToDatasource(listener); |
603 | } | 655 | } |
656 | + } else { | ||
657 | + notifyDataLoaded(); | ||
604 | } | 658 | } |
605 | } | 659 | } |
606 | 660 |
@@ -66,6 +66,8 @@ function Widget($controller, $compile, widgetService) { | @@ -66,6 +66,8 @@ function Widget($controller, $compile, widgetService) { | ||
66 | 66 | ||
67 | function loadFromWidgetInfo(widgetInfo) { | 67 | function loadFromWidgetInfo(widgetInfo) { |
68 | 68 | ||
69 | + scope.loadingData = true; | ||
70 | + | ||
69 | elem.addClass("tb-widget"); | 71 | elem.addClass("tb-widget"); |
70 | 72 | ||
71 | var widgetNamespace = "widget-type-" + (widget.isSystemType ? 'sys-' : '') | 73 | var widgetNamespace = "widget-type-" + (widget.isSystemType ? 'sys-' : '') |
@@ -73,9 +75,12 @@ function Widget($controller, $compile, widgetService) { | @@ -73,9 +75,12 @@ function Widget($controller, $compile, widgetService) { | ||
73 | + widget.typeAlias; | 75 | + widget.typeAlias; |
74 | 76 | ||
75 | elem.addClass(widgetNamespace); | 77 | elem.addClass(widgetNamespace); |
76 | - elem.html('<div class="tb-absolute-fill tb-widget-error"" ng-if="widgetErrorData">' + | 78 | + elem.html('<div class="tb-absolute-fill tb-widget-error" ng-if="widgetErrorData">' + |
77 | '<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>' + | 79 | '<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>' + |
78 | '</div>' + | 80 | '</div>' + |
81 | + '<div class="tb-absolute-fill tb-widget-loading" ng-show="loadingData" layout="column" layout-align="center center">' + | ||
82 | + '<md-progress-circular md-mode="indeterminate" class="md-accent" md-diameter="40"></md-progress-circular>' + | ||
83 | + '</div>' + | ||
79 | '<div id="container">' + widgetInfo.templateHtml + '</div>'); | 84 | '<div id="container">' + widgetInfo.templateHtml + '</div>'); |
80 | 85 | ||
81 | $compile(elem.contents())(scope); | 86 | $compile(elem.contents())(scope); |
@@ -32,6 +32,7 @@ export default function DashboardSettingsController($scope, $mdDialog, gridSetti | @@ -32,6 +32,7 @@ export default function DashboardSettingsController($scope, $mdDialog, gridSetti | ||
32 | } | 32 | } |
33 | 33 | ||
34 | vm.gridSettings.backgroundColor = vm.gridSettings.backgroundColor || 'rgba(0,0,0,0)'; | 34 | vm.gridSettings.backgroundColor = vm.gridSettings.backgroundColor || 'rgba(0,0,0,0)'; |
35 | + vm.gridSettings.titleColor = vm.gridSettings.titleColor || 'rgba(0,0,0,0.870588)'; | ||
35 | vm.gridSettings.columns = vm.gridSettings.columns || 24; | 36 | vm.gridSettings.columns = vm.gridSettings.columns || 24; |
36 | vm.gridSettings.margins = vm.gridSettings.margins || [10, 10]; | 37 | vm.gridSettings.margins = vm.gridSettings.margins || [10, 10]; |
37 | vm.hMargin = vm.gridSettings.margins[0]; | 38 | vm.hMargin = vm.gridSettings.margins[0]; |
@@ -31,10 +31,22 @@ | @@ -31,10 +31,22 @@ | ||
31 | <md-dialog-content> | 31 | <md-dialog-content> |
32 | <div class="md-dialog-content"> | 32 | <div class="md-dialog-content"> |
33 | <fieldset ng-disabled="loading"> | 33 | <fieldset ng-disabled="loading"> |
34 | - <div layout="row" layout-padding> | 34 | + <div layout="row" layout-align="start center"> |
35 | <md-checkbox flex aria-label="{{ 'dashboard.display-title' | translate }}" | 35 | <md-checkbox flex aria-label="{{ 'dashboard.display-title' | translate }}" |
36 | ng-model="vm.gridSettings.showTitle">{{ 'dashboard.display-title' | translate }} | 36 | ng-model="vm.gridSettings.showTitle">{{ 'dashboard.display-title' | translate }} |
37 | </md-checkbox> | 37 | </md-checkbox> |
38 | + <div flex | ||
39 | + ng-required="false" | ||
40 | + md-color-picker | ||
41 | + ng-model="vm.gridSettings.titleColor" | ||
42 | + label="{{ 'dashboard.title-color' | translate }}" | ||
43 | + icon="format_color_fill" | ||
44 | + default="rgba(0, 0, 0, 0.870588)" | ||
45 | + md-color-clear-button="false" | ||
46 | + open-on-input="true" | ||
47 | + md-color-generic-palette="false" | ||
48 | + md-color-history="false" | ||
49 | + ></div> | ||
38 | </div> | 50 | </div> |
39 | <md-input-container class="md-block"> | 51 | <md-input-container class="md-block"> |
40 | <label translate>dashboard.columns-count</label> | 52 | <label translate>dashboard.columns-count</label> |
@@ -23,7 +23,7 @@ import addWidgetTemplate from './add-widget.tpl.html'; | @@ -23,7 +23,7 @@ import addWidgetTemplate from './add-widget.tpl.html'; | ||
23 | 23 | ||
24 | /*@ngInject*/ | 24 | /*@ngInject*/ |
25 | export default function DashboardController(types, widgetService, userService, | 25 | export default function DashboardController(types, widgetService, userService, |
26 | - dashboardService, itembuffer, importExport, hotkeys, $window, $rootScope, | 26 | + dashboardService, timeService, itembuffer, importExport, hotkeys, $window, $rootScope, |
27 | $scope, $state, $stateParams, $mdDialog, $timeout, $document, $q, $translate, $filter) { | 27 | $scope, $state, $stateParams, $mdDialog, $timeout, $document, $q, $translate, $filter) { |
28 | 28 | ||
29 | var user = userService.getCurrentUser(); | 29 | var user = userService.getCurrentUser(); |
@@ -47,6 +47,25 @@ export default function DashboardController(types, widgetService, userService, | @@ -47,6 +47,25 @@ export default function DashboardController(types, widgetService, userService, | ||
47 | vm.widgets = []; | 47 | vm.widgets = []; |
48 | vm.dashboardInitComplete = false; | 48 | vm.dashboardInitComplete = false; |
49 | 49 | ||
50 | + vm.isToolbarOpened = false; | ||
51 | + | ||
52 | + Object.defineProperty(vm, 'toolbarOpened', { | ||
53 | + get: function() { return vm.isToolbarOpened || vm.isEdit; }, | ||
54 | + set: function() { } | ||
55 | + }); | ||
56 | + | ||
57 | + vm.openToolbar = function() { | ||
58 | + $timeout(function() { | ||
59 | + vm.isToolbarOpened = true; | ||
60 | + }); | ||
61 | + } | ||
62 | + | ||
63 | + vm.closeToolbar = function() { | ||
64 | + $timeout(function() { | ||
65 | + vm.isToolbarOpened = false; | ||
66 | + }); | ||
67 | + } | ||
68 | + | ||
50 | vm.addWidget = addWidget; | 69 | vm.addWidget = addWidget; |
51 | vm.addWidgetFromType = addWidgetFromType; | 70 | vm.addWidgetFromType = addWidgetFromType; |
52 | vm.dashboardInited = dashboardInited; | 71 | vm.dashboardInited = dashboardInited; |
@@ -154,6 +173,9 @@ export default function DashboardController(types, widgetService, userService, | @@ -154,6 +173,9 @@ export default function DashboardController(types, widgetService, userService, | ||
154 | 173 | ||
155 | if (vm.widgetEditMode) { | 174 | if (vm.widgetEditMode) { |
156 | $timeout(function () { | 175 | $timeout(function () { |
176 | + vm.dashboardConfiguration = { | ||
177 | + timewindow: timeService.defaultTimewindow() | ||
178 | + }; | ||
157 | vm.widgets = [{ | 179 | vm.widgets = [{ |
158 | isSystemType: true, | 180 | isSystemType: true, |
159 | bundleAlias: 'customWidgetBundle', | 181 | bundleAlias: 'customWidgetBundle', |
@@ -186,9 +208,12 @@ export default function DashboardController(types, widgetService, userService, | @@ -186,9 +208,12 @@ export default function DashboardController(types, widgetService, userService, | ||
186 | if (angular.isUndefined(vm.dashboard.configuration.deviceAliases)) { | 208 | if (angular.isUndefined(vm.dashboard.configuration.deviceAliases)) { |
187 | vm.dashboard.configuration.deviceAliases = {}; | 209 | vm.dashboard.configuration.deviceAliases = {}; |
188 | } | 210 | } |
189 | - //$timeout(function () { | ||
190 | - vm.widgets = vm.dashboard.configuration.widgets; | ||
191 | - //}); | 211 | + |
212 | + if (angular.isUndefined(vm.dashboard.configuration.timewindow)) { | ||
213 | + vm.dashboard.configuration.timewindow = timeService.defaultTimewindow(); | ||
214 | + } | ||
215 | + vm.dashboardConfiguration = vm.dashboard.configuration; | ||
216 | + vm.widgets = vm.dashboard.configuration.widgets; | ||
192 | deferred.resolve(); | 217 | deferred.resolve(); |
193 | }, function fail(e) { | 218 | }, function fail(e) { |
194 | deferred.reject(e); | 219 | deferred.reject(e); |
@@ -607,6 +632,7 @@ export default function DashboardController(types, widgetService, userService, | @@ -607,6 +632,7 @@ export default function DashboardController(types, widgetService, userService, | ||
607 | if (revert) { | 632 | if (revert) { |
608 | vm.dashboard = vm.prevDashboard; | 633 | vm.dashboard = vm.prevDashboard; |
609 | vm.widgets = vm.dashboard.configuration.widgets; | 634 | vm.widgets = vm.dashboard.configuration.widgets; |
635 | + vm.dashboardConfiguration = vm.dashboard.configuration; | ||
610 | } | 636 | } |
611 | } | 637 | } |
612 | } | 638 | } |
@@ -13,6 +13,8 @@ | @@ -13,6 +13,8 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
17 | +@import "~compass-sass-mixins/lib/compass"; | ||
16 | @import '../../scss/constants'; | 18 | @import '../../scss/constants'; |
17 | 19 | ||
18 | section.tb-dashboard-title { | 20 | section.tb-dashboard-title { |
@@ -53,3 +55,81 @@ tb-details-sidenav.tb-widget-details-sidenav { | @@ -53,3 +55,81 @@ tb-details-sidenav.tb-widget-details-sidenav { | ||
53 | } | 55 | } |
54 | } | 56 | } |
55 | } | 57 | } |
58 | + | ||
59 | +/*********************** | ||
60 | + * dashboard toolbar | ||
61 | + ***********************/ | ||
62 | + | ||
63 | +section.tb-dashboard-toolbar { | ||
64 | + position: absolute; | ||
65 | + top: 0px; | ||
66 | + left: -100%; | ||
67 | + z-index: 3; | ||
68 | + pointer-events: none; | ||
69 | + &.tb-dashboard-toolbar-opened { | ||
70 | + right: 0px; | ||
71 | + @include transition(right .3s cubic-bezier(.55,0,.55,.2)); | ||
72 | + } | ||
73 | + &.tb-dashboard-toolbar-closed { | ||
74 | + right: 18px; | ||
75 | + @include transition(right .3s cubic-bezier(.55,0,.55,.2) .2s); | ||
76 | + } | ||
77 | + md-fab-toolbar { | ||
78 | + &.md-is-open { | ||
79 | + md-fab-trigger { | ||
80 | + .md-button { | ||
81 | + &.md-fab { | ||
82 | + opacity: 1; | ||
83 | + @include transition(opacity .3s cubic-bezier(.55,0,.55,.2)); | ||
84 | + } | ||
85 | + } | ||
86 | + } | ||
87 | + } | ||
88 | + md-fab-trigger { | ||
89 | + .md-button { | ||
90 | + &.md-fab { | ||
91 | + line-height: 36px; | ||
92 | + width: 36px; | ||
93 | + height: 36px; | ||
94 | + margin: 4px 0 0 4px; | ||
95 | + opacity: 0.5; | ||
96 | + @include transition(opacity .3s cubic-bezier(.55,0,.55,.2) .2s); | ||
97 | + md-icon { | ||
98 | + margin: 0; | ||
99 | + line-height: 18px; | ||
100 | + height: 18px; | ||
101 | + width: 18px; | ||
102 | + min-height: 18px; | ||
103 | + min-width: 18px; | ||
104 | + } | ||
105 | + } | ||
106 | + } | ||
107 | + } | ||
108 | + .md-fab-toolbar-wrapper { | ||
109 | + height: 40px; | ||
110 | + md-toolbar { | ||
111 | + min-height: 36px; | ||
112 | + height: 36px; | ||
113 | + md-fab-actions { | ||
114 | + .close-action { | ||
115 | + margin-right: -18px; | ||
116 | + } | ||
117 | + tb-timewindow { | ||
118 | + font-size: 16px; | ||
119 | + } | ||
120 | + } | ||
121 | + } | ||
122 | + } | ||
123 | + } | ||
124 | +} | ||
125 | + | ||
126 | +.tb-dashboard-container { | ||
127 | + &.tb-dashboard-toolbar-opened { | ||
128 | + margin-top: 40px; | ||
129 | + @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2)); | ||
130 | + } | ||
131 | + &.tb-dashboard-toolbar-closed { | ||
132 | + margin-top: 0px; | ||
133 | + @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2) .2s); | ||
134 | + } | ||
135 | +} |
@@ -15,235 +15,263 @@ | @@ -15,235 +15,263 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<md-content flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode" hide-expand-button="vm.widgetEditMode || vm.iframeMode"> | ||
19 | - <!--section ng-show="!vm.isAddingWidget && !loading && !vm.widgetEditMode" layout="row" layout-wrap | ||
20 | - class="tb-header-buttons tb-top-header-buttons md-fab" ng-style="{'right': '50px'}"> | ||
21 | - <md-button ng-if="vm.isTenantAdmin()" ng-show="vm.isEdit" ng-disabled="loading" | ||
22 | - class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right" | ||
23 | - aria-label="{{ 'action.apply' | translate }}" | ||
24 | - ng-click="vm.saveDashboard()"> | ||
25 | - <md-tooltip md-direction="top"> | ||
26 | - {{ 'action.apply-changes' | translate }} | ||
27 | - </md-tooltip> | ||
28 | - <ng-md-icon icon="done"></ng-md-icon> | ||
29 | - </md-button> | ||
30 | - <md-button ng-if="vm.isTenantAdmin()" ng-disabled="loading" | ||
31 | - class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right" | ||
32 | - aria-label="{{ 'action.edit-mode' | translate }}" | ||
33 | - ng-click="vm.toggleDashboardEditMode()"> | ||
34 | - <md-tooltip md-direction="top"> | ||
35 | - {{ (vm.isEdit ? 'action.decline-changes' : 'action.enter-edit-mode') | translate }} | ||
36 | - </md-tooltip> | ||
37 | - <ng-md-icon icon="{{vm.isEdit ? 'close' : 'edit'}}" | ||
38 | - options='{"easing": "circ-in-out", "duration": 375, "rotation": "none"}'></ng-md-icon> | ||
39 | - </md-button> | ||
40 | - </section--> | ||
41 | - <section ng-show="!loading && vm.noData()" layout-align="center center" | ||
42 | - ng-class="{'tb-padded' : !vm.widgetEditMode}" | ||
43 | - style="text-transform: uppercase; display: flex; z-index: 1;" | ||
44 | - class="md-headline tb-absolute-fill"> | ||
45 | - <span translate ng-if="!vm.isEdit"> | ||
46 | - dashboard.no-widgets | ||
47 | - </span> | ||
48 | - <md-button ng-if="vm.isEdit && !vm.widgetEditMode" class="tb-add-new-widget" ng-click="vm.addWidget($event)"> | ||
49 | - <md-icon aria-label="{{ 'action.add' | translate }}" class="material-icons tb-md-96">add</md-icon> | ||
50 | - {{ 'dashboard.add-widget' | translate }} | ||
51 | - </md-button> | ||
52 | - </section> | ||
53 | - <section ng-if="!vm.widgetEditMode" class="tb-dashboard-title" layout="row" layout-align="center center"> | ||
54 | - <h3 ng-show="!vm.isEdit && vm.displayTitle()">{{ vm.dashboard.title }}</h3> | ||
55 | - <md-input-container ng-show="vm.isEdit" class="md-block" style="height: 30px;"> | ||
56 | - <label translate>dashboard.title</label> | ||
57 | - <input class="tb-dashboard-title" required name="title" ng-model="vm.dashboard.title"> | ||
58 | - </md-input-container> | ||
59 | - <md-button class="md-raised" flex="none" ng-show="vm.isEdit" ng-click="vm.openDeviceAliases($event)"> | ||
60 | - {{ 'device.aliases' | translate }} | ||
61 | - </md-button> | ||
62 | - <md-button class="md-raised" flex="none" ng-show="vm.isEdit" ng-click="vm.openDashboardSettings($event)"> | ||
63 | - {{ 'dashboard.settings' | translate }} | ||
64 | - </md-button> | ||
65 | - </section> | ||
66 | - <div class="tb-absolute-fill" | ||
67 | - ng-class="{ 'tb-padded' : !vm.widgetEditMode && (vm.isEdit || vm.displayTitle()), 'tb-shrinked' : vm.isEditingWidget }"> | ||
68 | - <tb-dashboard | ||
69 | - dashboard-style="{'background-color': vm.dashboard.configuration.gridSettings.backgroundColor, | ||
70 | - 'background-image': 'url('+vm.dashboard.configuration.gridSettings.backgroundImageUrl+')', | ||
71 | - 'background-repeat': 'no-repeat', | ||
72 | - 'background-attachment': 'scroll', | ||
73 | - 'background-size': vm.dashboard.configuration.gridSettings.backgroundSizeMode || '100%', | ||
74 | - 'background-position': '0% 0%'}" | ||
75 | - widgets="vm.widgets" | ||
76 | - columns="vm.dashboard.configuration.gridSettings.columns" | ||
77 | - margins="vm.dashboard.configuration.gridSettings.margins" | ||
78 | - device-alias-list="vm.dashboard.configuration.deviceAliases" | ||
79 | - is-edit="vm.isEdit" | ||
80 | - is-mobile="vm.forceDashboardMobileMode" | ||
81 | - is-mobile-disabled="vm.widgetEditMode" | ||
82 | - is-edit-action-enabled="vm.isEdit" | ||
83 | - is-export-action-enabled="vm.isEdit && !vm.widgetEditMode" | ||
84 | - is-remove-action-enabled="vm.isEdit && !vm.widgetEditMode" | ||
85 | - on-edit-widget="vm.editWidget(event, widget)" | ||
86 | - on-export-widget="vm.exportWidget(event, widget)" | ||
87 | - on-widget-mouse-down="vm.widgetMouseDown(event, widget)" | ||
88 | - on-widget-clicked="vm.widgetClicked(event, widget)" | ||
89 | - on-widget-context-menu="vm.widgetContextMenu(event, widget)" | ||
90 | - prepare-dashboard-context-menu="vm.prepareDashboardContextMenu()" | ||
91 | - prepare-widget-context-menu="vm.prepareWidgetContextMenu(widget)" | ||
92 | - on-remove-widget="vm.removeWidget(event, widget)" | ||
93 | - load-widgets="vm.loadDashboard()" | ||
94 | - get-st-diff="vm.getServerTimeDiff()" | ||
95 | - on-init="vm.dashboardInited(dashboard)" | ||
96 | - on-init-failed="vm.dashboardInitFailed(e)"> | ||
97 | - </tb-dashboard> | ||
98 | - </div> | ||
99 | - <tb-details-sidenav class="tb-widget-details-sidenav" | ||
100 | - header-title="vm.editingWidget.config.title" | ||
101 | - header-subtitle="{{vm.editingWidgetSubtitle}}" | ||
102 | - is-read-only="false" | ||
103 | - is-open="vm.isEditingWidget" | ||
104 | - is-always-edit="true" | ||
105 | - on-close-details="vm.onEditWidgetClosed()" | ||
106 | - on-toggle-details-edit-mode="vm.onRevertWidgetEdit(vm.widgetForm)" | ||
107 | - on-apply-details="vm.saveWidget(vm.widgetForm)" | ||
108 | - the-form="vm.widgetForm"> | ||
109 | - <details-buttons tb-help="vm.helpLinkIdForWidgetType()" help-container-id="help-container"> | ||
110 | - <div id="help-container"></div> | ||
111 | - </details-buttons> | ||
112 | - <form name="vm.widgetForm" ng-if="vm.isEditingWidget"> | ||
113 | - <tb-edit-widget | ||
114 | - dashboard="vm.dashboard" | ||
115 | - widget="vm.editingWidget" | ||
116 | - the-form="vm.widgetForm"> | ||
117 | - </tb-edit-widget> | ||
118 | - </form> | ||
119 | - </tb-details-sidenav> | ||
120 | - <tb-details-sidenav ng-if="!vm.widgetEditMode" class="tb-select-widget-sidenav" | ||
121 | - header-title="'dashboard.select-widget-title' | translate" | ||
122 | - header-height-px="120" | ||
123 | - is-read-only="true" | ||
124 | - is-open="vm.isAddingWidget" | ||
125 | - is-edit="false" | ||
126 | - on-close-details="vm.onAddWidgetClosed()"> | ||
127 | - <header-pane ng-if="vm.isAddingWidget"> | ||
128 | - <div layout="row"> | ||
129 | - <span class="tb-details-subtitle">{{ 'widgets-bundle.current' | translate }}</span> | ||
130 | - <tb-widgets-bundle-select flex-offset="5" | ||
131 | - flex | ||
132 | - ng-model="vm.widgetsBundle" | ||
133 | - tb-required="true" | ||
134 | - select-first-bundle="false"> | ||
135 | - </tb-widgets-bundle-select> | ||
136 | - </div> | ||
137 | - </header-pane> | ||
138 | - <div ng-if="vm.isAddingWidget"> | ||
139 | - <md-tabs ng-if="vm.timeseriesWidgetTypes.length > 0 || vm.latestWidgetTypes.length > 0 || | ||
140 | - vm.rpcWidgetTypes.length > 0 || vm.staticWidgetTypes.length > 0" | ||
141 | - flex | ||
142 | - class="tb-absolute-fill" md-border-bottom> | ||
143 | - <md-tab ng-if="vm.timeseriesWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.timeseries' | translate }}"> | ||
144 | - <tb-dashboard | ||
145 | - widgets="vm.timeseriesWidgetTypes" | ||
146 | - is-edit="false" | ||
147 | - is-mobile="true" | ||
148 | - is-edit-action-enabled="false" | ||
149 | - is-remove-action-enabled="false" | ||
150 | - on-widget-clicked="vm.addWidgetFromType(event, widget)"> | ||
151 | - </tb-dashboard> | ||
152 | - </md-tab> | ||
153 | - <md-tab ng-if="vm.latestWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.latest-values' | translate }}"> | ||
154 | - <tb-dashboard | ||
155 | - widgets="vm.latestWidgetTypes" | ||
156 | - is-edit="false" | ||
157 | - is-mobile="true" | ||
158 | - is-edit-action-enabled="false" | ||
159 | - is-remove-action-enabled="false" | ||
160 | - on-widget-clicked="vm.addWidgetFromType(event, widget)"> | ||
161 | - </tb-dashboard> | ||
162 | - </md-tab> | ||
163 | - <md-tab ng-if="vm.rpcWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.rpc' | translate }}"> | ||
164 | - <tb-dashboard | ||
165 | - widgets="vm.rpcWidgetTypes" | ||
166 | - is-edit="false" | ||
167 | - is-mobile="true" | ||
168 | - is-edit-action-enabled="false" | ||
169 | - is-remove-action-enabled="false" | ||
170 | - on-widget-clicked="vm.addWidgetFromType(event, widget)"> | ||
171 | - </tb-dashboard> | ||
172 | - </md-tab> | ||
173 | - <md-tab ng-if="vm.staticWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.static' | translate }}"> | ||
174 | - <tb-dashboard | ||
175 | - widgets="vm.staticWidgetTypes" | ||
176 | - is-edit="false" | ||
177 | - is-mobile="true" | ||
178 | - is-edit-action-enabled="false" | ||
179 | - is-remove-action-enabled="false" | ||
180 | - on-widget-clicked="vm.addWidgetFromType(event, widget)"> | ||
181 | - </tb-dashboard> | ||
182 | - </md-tab> | ||
183 | - </md-tabs> | ||
184 | - <span translate ng-if="vm.timeseriesWidgetTypes.length === 0 && vm.latestWidgetTypes.length === 0 && | ||
185 | - vm.rpcWidgetTypes.length === 0 && vm.staticWidgetTypes.length === 0 && vm.widgetsBundle" | ||
186 | - layout-align="center center" | ||
187 | - style="text-transform: uppercase; display: flex;" | ||
188 | - class="md-headline tb-absolute-fill">widgets-bundle.empty</span> | ||
189 | - <span translate ng-if="!vm.widgetsBundle" | ||
190 | - layout-align="center center" | ||
191 | - style="text-transform: uppercase; display: flex;" | ||
192 | - class="md-headline tb-absolute-fill">widget.select-widgets-bundle</span> | ||
193 | - </div> | ||
194 | - </tb-details-sidenav> | ||
195 | - <!-- </section> --> | ||
196 | - <section layout="row" layout-wrap class="tb-footer-buttons md-fab" layout-align="start end"> | ||
197 | - <md-fab-speed-dial ng-disabled="loading" ng-show="!vm.isAddingWidget && vm.isEdit && !vm.widgetEditMode" | ||
198 | - md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up"> | ||
199 | - <md-fab-trigger> | ||
200 | - <md-button ng-disabled="loading" | ||
201 | - class="tb-btn-footer md-accent md-hue-2 md-fab" | ||
202 | - aria-label="{{ 'dashboard.add-widget' | translate }}"> | ||
203 | - <md-tooltip md-direction="top"> | ||
204 | - {{ 'dashboard.add-widget' | translate }} | 18 | +<md-content flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode" expand-button-id="dashboard-expand-button" |
19 | + hide-expand-button="vm.widgetEditMode || vm.iframeMode" | ||
20 | + ng-style="{'background-color': vm.dashboard.configuration.gridSettings.backgroundColor, | ||
21 | + 'background-image': 'url('+vm.dashboard.configuration.gridSettings.backgroundImageUrl+')', | ||
22 | + 'background-repeat': 'no-repeat', | ||
23 | + 'background-attachment': 'scroll', | ||
24 | + 'background-size': vm.dashboard.configuration.gridSettings.backgroundSizeMode || '100%', | ||
25 | + 'background-position': '0% 0%'}"> | ||
26 | + <section class="tb-dashboard-toolbar" | ||
27 | + ng-class="{ 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }"> | ||
28 | + <md-fab-toolbar md-open="vm.toolbarOpened" | ||
29 | + md-direction="left"> | ||
30 | + <md-fab-trigger class="align-with-text"> | ||
31 | + <md-button aria-label="menu" class="md-fab md-primary" ng-click="vm.openToolbar()"> | ||
32 | + <md-tooltip ng-show="!vm.toolbarOpened" md-direction="top"> | ||
33 | + {{ 'dashboard.open-toolbar' | translate }} | ||
205 | </md-tooltip> | 34 | </md-tooltip> |
206 | - <ng-md-icon icon="add"></ng-md-icon> | 35 | + <md-icon aria-label="dashboard-toolbar" class="material-icons">more_horiz</md-icon> |
207 | </md-button> | 36 | </md-button> |
208 | </md-fab-trigger> | 37 | </md-fab-trigger> |
209 | - <md-fab-actions> | ||
210 | - <md-button ng-disabled="loading" | ||
211 | - class="tmd-accent md-hue-2 md-fab" ng-click="vm.addWidget($event)" | ||
212 | - aria-label="{{ 'action.create' | translate }}"> | ||
213 | - <md-tooltip md-direction="top"> | ||
214 | - {{ 'dashboard.create-new-widget' | translate }} | ||
215 | - </md-tooltip> | ||
216 | - <ng-md-icon icon="insert_drive_file"></ng-md-icon> | ||
217 | - </md-button> | ||
218 | - <md-button ng-disabled="loading" | ||
219 | - class="tmd-accent md-hue-2 md-fab" ng-click="vm.importWidget($event)" | ||
220 | - aria-label="{{ 'action.import' | translate }}"> | ||
221 | - <md-tooltip md-direction="top"> | ||
222 | - {{ 'dashboard.import-widget' | translate }} | ||
223 | - </md-tooltip> | ||
224 | - <ng-md-icon icon="file_upload"></ng-md-icon> | ||
225 | - </md-button> | ||
226 | - </md-fab-actions> | ||
227 | - </md-fab-speed-dial> | ||
228 | - <md-button ng-if="vm.isTenantAdmin() || vm.isSystemAdmin()" ng-show="vm.isEdit && !vm.isAddingWidget && !loading" ng-disabled="loading" | ||
229 | - class="tb-btn-footer md-accent md-hue-2 md-fab" | ||
230 | - aria-label="{{ 'action.apply' | translate }}" | ||
231 | - ng-click="vm.saveDashboard()"> | ||
232 | - <md-tooltip md-direction="top"> | ||
233 | - {{ 'action.apply-changes' | translate }} | ||
234 | - </md-tooltip> | ||
235 | - <ng-md-icon icon="done"></ng-md-icon> | ||
236 | - </md-button> | ||
237 | - <md-button ng-show="!vm.isAddingWidget && !loading" | ||
238 | - ng-if="vm.isTenantAdmin() || vm.isSystemAdmin()" ng-disabled="loading" | ||
239 | - class="tb-btn-footer md-accent md-hue-2 md-fab" | ||
240 | - aria-label="{{ 'action.edit-mode' | translate }}" | ||
241 | - ng-click="vm.toggleDashboardEditMode()"> | ||
242 | - <md-tooltip md-direction="top"> | ||
243 | - {{ (vm.isEdit ? 'action.decline-changes' : 'action.enter-edit-mode') | translate }} | ||
244 | - </md-tooltip> | ||
245 | - <ng-md-icon icon="{{vm.isEdit ? 'close' : 'edit'}}" | ||
246 | - options='{"easing": "circ-in-out", "duration": 375, "rotation": "none"}'></ng-md-icon> | ||
247 | - </md-button> | 38 | + <md-toolbar> |
39 | + <md-fab-actions class="md-toolbar-tools"> | ||
40 | + <md-button ng-show="!vm.isEdit" aria-label="close-toolbar" class="md-icon-button close-action" ng-click="vm.closeToolbar()"> | ||
41 | + <md-tooltip md-direction="top"> | ||
42 | + {{ 'dashboard.close-toolbar' | translate }} | ||
43 | + </md-tooltip> | ||
44 | + <md-icon aria-label="close-toolbar" class="material-icons">arrow_forward</md-icon> | ||
45 | + </md-button> | ||
46 | + <md-button id="dashboard-expand-button" | ||
47 | + aria-label="{{ 'fullscreen.fullscreen' | translate }}" | ||
48 | + class="md-icon-button"> | ||
49 | + </md-button> | ||
50 | + <tb-timewindow direction="left" aggregation ng-model="vm.dashboardConfiguration.timewindow"> | ||
51 | + </tb-timewindow> | ||
52 | + <md-button ng-show="vm.isEdit" aria-label="{{ 'device.aliases' | translate }}" class="md-icon-button" | ||
53 | + ng-click="vm.openDeviceAliases($event)"> | ||
54 | + <md-tooltip md-direction="top"> | ||
55 | + {{ 'device.aliases' | translate }} | ||
56 | + </md-tooltip> | ||
57 | + <md-icon aria-label="{{ 'device.aliases' | translate }}" class="material-icons">devices_other</md-icon> | ||
58 | + </md-button> | ||
59 | + <md-button ng-show="vm.isEdit" aria-label="{{ 'dashboard.settings' | translate }}" class="md-icon-button" | ||
60 | + ng-click="vm.openDashboardSettings($event)"> | ||
61 | + <md-tooltip md-direction="top"> | ||
62 | + {{ 'dashboard.settings' | translate }} | ||
63 | + </md-tooltip> | ||
64 | + <md-icon aria-label="{{ 'dashboard.settings' | translate }}" class="material-icons">settings</md-icon> | ||
65 | + </md-button> | ||
66 | + </md-fab-actions> | ||
67 | + </md-toolbar> | ||
68 | + </md-fab-toolbar> | ||
69 | + </section> | ||
70 | + <section class="tb-dashboard-container tb-absolute-fill" | ||
71 | + ng-class="{ 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }"> | ||
72 | + <section ng-show="!loading && vm.noData()" layout-align="center center" | ||
73 | + ng-class="{'tb-padded' : !vm.widgetEditMode}" | ||
74 | + style="text-transform: uppercase; display: flex; z-index: 1;" | ||
75 | + class="md-headline tb-absolute-fill"> | ||
76 | + <span translate ng-if="!vm.isEdit"> | ||
77 | + dashboard.no-widgets | ||
78 | + </span> | ||
79 | + <md-button ng-if="vm.isEdit && !vm.widgetEditMode" class="tb-add-new-widget" ng-click="vm.addWidget($event)"> | ||
80 | + <md-icon aria-label="{{ 'action.add' | translate }}" class="material-icons tb-md-96">add</md-icon> | ||
81 | + {{ 'dashboard.add-widget' | translate }} | ||
82 | + </md-button> | ||
83 | + </section> | ||
84 | + <section ng-if="!vm.widgetEditMode" class="tb-dashboard-title" layout="row" layout-align="center center" | ||
85 | + ng-style="{'color': vm.dashboard.configuration.gridSettings.titleColor}"> | ||
86 | + <h3 ng-show="!vm.isEdit && vm.displayTitle()">{{ vm.dashboard.title }}</h3> | ||
87 | + <md-input-container ng-show="vm.isEdit" class="md-block" style="height: 30px;"> | ||
88 | + <label translate ng-style="{'color': vm.dashboard.configuration.gridSettings.titleColor}">dashboard.title</label> | ||
89 | + <input class="tb-dashboard-title" ng-style="{'color': vm.dashboard.configuration.gridSettings.titleColor}" required name="title" ng-model="vm.dashboard.title"> | ||
90 | + </md-input-container> | ||
91 | + </section> | ||
92 | + <div class="tb-absolute-fill" | ||
93 | + ng-class="{ 'tb-padded' : !vm.widgetEditMode && (vm.isEdit || vm.displayTitle()), 'tb-shrinked' : vm.isEditingWidget }"> | ||
94 | + <tb-dashboard | ||
95 | + dashboard-style="{'background-color': vm.dashboard.configuration.gridSettings.backgroundColor, | ||
96 | + 'background-image': 'url('+vm.dashboard.configuration.gridSettings.backgroundImageUrl+')', | ||
97 | + 'background-repeat': 'no-repeat', | ||
98 | + 'background-attachment': 'scroll', | ||
99 | + 'background-size': vm.dashboard.configuration.gridSettings.backgroundSizeMode || '100%', | ||
100 | + 'background-position': '0% 0%'}" | ||
101 | + widgets="vm.widgets" | ||
102 | + columns="vm.dashboard.configuration.gridSettings.columns" | ||
103 | + margins="vm.dashboard.configuration.gridSettings.margins" | ||
104 | + device-alias-list="vm.dashboard.configuration.deviceAliases" | ||
105 | + dashboard-timewindow="vm.dashboardConfiguration.timewindow" | ||
106 | + is-edit="vm.isEdit" | ||
107 | + is-mobile="vm.forceDashboardMobileMode" | ||
108 | + is-mobile-disabled="vm.widgetEditMode" | ||
109 | + is-edit-action-enabled="vm.isEdit" | ||
110 | + is-export-action-enabled="vm.isEdit && !vm.widgetEditMode" | ||
111 | + is-remove-action-enabled="vm.isEdit && !vm.widgetEditMode" | ||
112 | + on-edit-widget="vm.editWidget(event, widget)" | ||
113 | + on-export-widget="vm.exportWidget(event, widget)" | ||
114 | + on-widget-mouse-down="vm.widgetMouseDown(event, widget)" | ||
115 | + on-widget-clicked="vm.widgetClicked(event, widget)" | ||
116 | + on-widget-context-menu="vm.widgetContextMenu(event, widget)" | ||
117 | + prepare-dashboard-context-menu="vm.prepareDashboardContextMenu()" | ||
118 | + prepare-widget-context-menu="vm.prepareWidgetContextMenu(widget)" | ||
119 | + on-remove-widget="vm.removeWidget(event, widget)" | ||
120 | + load-widgets="vm.loadDashboard()" | ||
121 | + get-st-diff="vm.getServerTimeDiff()" | ||
122 | + on-init="vm.dashboardInited(dashboard)" | ||
123 | + on-init-failed="vm.dashboardInitFailed(e)"> | ||
124 | + </tb-dashboard> | ||
125 | + </div> | ||
126 | + <tb-details-sidenav class="tb-widget-details-sidenav" | ||
127 | + header-title="vm.editingWidget.config.title" | ||
128 | + header-subtitle="{{vm.editingWidgetSubtitle}}" | ||
129 | + is-read-only="false" | ||
130 | + is-open="vm.isEditingWidget" | ||
131 | + is-always-edit="true" | ||
132 | + on-close-details="vm.onEditWidgetClosed()" | ||
133 | + on-toggle-details-edit-mode="vm.onRevertWidgetEdit(vm.widgetForm)" | ||
134 | + on-apply-details="vm.saveWidget(vm.widgetForm)" | ||
135 | + the-form="vm.widgetForm"> | ||
136 | + <details-buttons tb-help="vm.helpLinkIdForWidgetType()" help-container-id="help-container"> | ||
137 | + <div id="help-container"></div> | ||
138 | + </details-buttons> | ||
139 | + <form name="vm.widgetForm" ng-if="vm.isEditingWidget"> | ||
140 | + <tb-edit-widget | ||
141 | + dashboard="vm.dashboard" | ||
142 | + widget="vm.editingWidget" | ||
143 | + the-form="vm.widgetForm"> | ||
144 | + </tb-edit-widget> | ||
145 | + </form> | ||
146 | + </tb-details-sidenav> | ||
147 | + <tb-details-sidenav ng-if="!vm.widgetEditMode" class="tb-select-widget-sidenav" | ||
148 | + header-title="'dashboard.select-widget-title' | translate" | ||
149 | + header-height-px="120" | ||
150 | + is-read-only="true" | ||
151 | + is-open="vm.isAddingWidget" | ||
152 | + is-edit="false" | ||
153 | + on-close-details="vm.onAddWidgetClosed()"> | ||
154 | + <header-pane ng-if="vm.isAddingWidget"> | ||
155 | + <div layout="row"> | ||
156 | + <span class="tb-details-subtitle">{{ 'widgets-bundle.current' | translate }}</span> | ||
157 | + <tb-widgets-bundle-select flex-offset="5" | ||
158 | + flex | ||
159 | + ng-model="vm.widgetsBundle" | ||
160 | + tb-required="true" | ||
161 | + select-first-bundle="false"> | ||
162 | + </tb-widgets-bundle-select> | ||
163 | + </div> | ||
164 | + </header-pane> | ||
165 | + <div ng-if="vm.isAddingWidget"> | ||
166 | + <md-tabs ng-if="vm.timeseriesWidgetTypes.length > 0 || vm.latestWidgetTypes.length > 0 || | ||
167 | + vm.rpcWidgetTypes.length > 0 || vm.staticWidgetTypes.length > 0" | ||
168 | + flex | ||
169 | + class="tb-absolute-fill" md-border-bottom> | ||
170 | + <md-tab ng-if="vm.timeseriesWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.timeseries' | translate }}"> | ||
171 | + <tb-dashboard | ||
172 | + widgets="vm.timeseriesWidgetTypes" | ||
173 | + is-edit="false" | ||
174 | + is-mobile="true" | ||
175 | + is-edit-action-enabled="false" | ||
176 | + is-remove-action-enabled="false" | ||
177 | + on-widget-clicked="vm.addWidgetFromType(event, widget)"> | ||
178 | + </tb-dashboard> | ||
179 | + </md-tab> | ||
180 | + <md-tab ng-if="vm.latestWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.latest-values' | translate }}"> | ||
181 | + <tb-dashboard | ||
182 | + widgets="vm.latestWidgetTypes" | ||
183 | + is-edit="false" | ||
184 | + is-mobile="true" | ||
185 | + is-edit-action-enabled="false" | ||
186 | + is-remove-action-enabled="false" | ||
187 | + on-widget-clicked="vm.addWidgetFromType(event, widget)"> | ||
188 | + </tb-dashboard> | ||
189 | + </md-tab> | ||
190 | + <md-tab ng-if="vm.rpcWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.rpc' | translate }}"> | ||
191 | + <tb-dashboard | ||
192 | + widgets="vm.rpcWidgetTypes" | ||
193 | + is-edit="false" | ||
194 | + is-mobile="true" | ||
195 | + is-edit-action-enabled="false" | ||
196 | + is-remove-action-enabled="false" | ||
197 | + on-widget-clicked="vm.addWidgetFromType(event, widget)"> | ||
198 | + </tb-dashboard> | ||
199 | + </md-tab> | ||
200 | + <md-tab ng-if="vm.staticWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.static' | translate }}"> | ||
201 | + <tb-dashboard | ||
202 | + widgets="vm.staticWidgetTypes" | ||
203 | + is-edit="false" | ||
204 | + is-mobile="true" | ||
205 | + is-edit-action-enabled="false" | ||
206 | + is-remove-action-enabled="false" | ||
207 | + on-widget-clicked="vm.addWidgetFromType(event, widget)"> | ||
208 | + </tb-dashboard> | ||
209 | + </md-tab> | ||
210 | + </md-tabs> | ||
211 | + <span translate ng-if="vm.timeseriesWidgetTypes.length === 0 && vm.latestWidgetTypes.length === 0 && | ||
212 | + vm.rpcWidgetTypes.length === 0 && vm.staticWidgetTypes.length === 0 && vm.widgetsBundle" | ||
213 | + layout-align="center center" | ||
214 | + style="text-transform: uppercase; display: flex;" | ||
215 | + class="md-headline tb-absolute-fill">widgets-bundle.empty</span> | ||
216 | + <span translate ng-if="!vm.widgetsBundle" | ||
217 | + layout-align="center center" | ||
218 | + style="text-transform: uppercase; display: flex;" | ||
219 | + class="md-headline tb-absolute-fill">widget.select-widgets-bundle</span> | ||
220 | + </div> | ||
221 | + </tb-details-sidenav> | ||
222 | + <!-- </section> --> | ||
223 | + <section layout="row" layout-wrap class="tb-footer-buttons md-fab" layout-align="start end"> | ||
224 | + <md-fab-speed-dial ng-disabled="loading" ng-show="!vm.isAddingWidget && vm.isEdit && !vm.widgetEditMode" | ||
225 | + md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up"> | ||
226 | + <md-fab-trigger> | ||
227 | + <md-button ng-disabled="loading" | ||
228 | + class="tb-btn-footer md-accent md-hue-2 md-fab" | ||
229 | + aria-label="{{ 'dashboard.add-widget' | translate }}"> | ||
230 | + <md-tooltip md-direction="top"> | ||
231 | + {{ 'dashboard.add-widget' | translate }} | ||
232 | + </md-tooltip> | ||
233 | + <ng-md-icon icon="add"></ng-md-icon> | ||
234 | + </md-button> | ||
235 | + </md-fab-trigger> | ||
236 | + <md-fab-actions> | ||
237 | + <md-button ng-disabled="loading" | ||
238 | + class="tmd-accent md-hue-2 md-fab" ng-click="vm.addWidget($event)" | ||
239 | + aria-label="{{ 'action.create' | translate }}"> | ||
240 | + <md-tooltip md-direction="top"> | ||
241 | + {{ 'dashboard.create-new-widget' | translate }} | ||
242 | + </md-tooltip> | ||
243 | + <ng-md-icon icon="insert_drive_file"></ng-md-icon> | ||
244 | + </md-button> | ||
245 | + <md-button ng-disabled="loading" | ||
246 | + class="tmd-accent md-hue-2 md-fab" ng-click="vm.importWidget($event)" | ||
247 | + aria-label="{{ 'action.import' | translate }}"> | ||
248 | + <md-tooltip md-direction="top"> | ||
249 | + {{ 'dashboard.import-widget' | translate }} | ||
250 | + </md-tooltip> | ||
251 | + <ng-md-icon icon="file_upload"></ng-md-icon> | ||
252 | + </md-button> | ||
253 | + </md-fab-actions> | ||
254 | + </md-fab-speed-dial> | ||
255 | + <md-button ng-if="vm.isTenantAdmin() || vm.isSystemAdmin()" ng-show="vm.isEdit && !vm.isAddingWidget && !loading" ng-disabled="loading" | ||
256 | + class="tb-btn-footer md-accent md-hue-2 md-fab" | ||
257 | + aria-label="{{ 'action.apply' | translate }}" | ||
258 | + ng-click="vm.saveDashboard()"> | ||
259 | + <md-tooltip md-direction="top"> | ||
260 | + {{ 'action.apply-changes' | translate }} | ||
261 | + </md-tooltip> | ||
262 | + <ng-md-icon icon="done"></ng-md-icon> | ||
263 | + </md-button> | ||
264 | + <md-button ng-show="!vm.isAddingWidget && !loading" | ||
265 | + ng-if="vm.isTenantAdmin() || vm.isSystemAdmin()" ng-disabled="loading" | ||
266 | + class="tb-btn-footer md-accent md-hue-2 md-fab" | ||
267 | + aria-label="{{ 'action.edit-mode' | translate }}" | ||
268 | + ng-click="vm.toggleDashboardEditMode()"> | ||
269 | + <md-tooltip md-direction="top"> | ||
270 | + {{ (vm.isEdit ? 'action.decline-changes' : 'action.enter-edit-mode') | translate }} | ||
271 | + </md-tooltip> | ||
272 | + <ng-md-icon icon="{{vm.isEdit ? 'close' : 'edit'}}" | ||
273 | + options='{"easing": "circ-in-out", "duration": 375, "rotation": "none"}'></ng-md-icon> | ||
274 | + </md-button> | ||
275 | + </section> | ||
248 | </section> | 276 | </section> |
249 | </md-content> | 277 | </md-content> |
@@ -247,6 +247,7 @@ export default angular.module('thingsboard.locale', []) | @@ -247,6 +247,7 @@ export default angular.module('thingsboard.locale', []) | ||
247 | "min-vertical-margin-message": "Only 0 is allowed as minimum vertical margin value.", | 247 | "min-vertical-margin-message": "Only 0 is allowed as minimum vertical margin value.", |
248 | "max-vertical-margin-message": "Only 50 is allowed as maximum vertical margin value.", | 248 | "max-vertical-margin-message": "Only 50 is allowed as maximum vertical margin value.", |
249 | "display-title": "Display dashboard title", | 249 | "display-title": "Display dashboard title", |
250 | + "title-color": "Title color", | ||
250 | "import": "Import dashboard", | 251 | "import": "Import dashboard", |
251 | "export": "Export dashboard", | 252 | "export": "Export dashboard", |
252 | "export-failed-error": "Unable to export dashboard: {error}", | 253 | "export-failed-error": "Unable to export dashboard: {error}", |
@@ -258,7 +259,9 @@ export default angular.module('thingsboard.locale', []) | @@ -258,7 +259,9 @@ export default angular.module('thingsboard.locale', []) | ||
258 | "import-widget": "Import widget", | 259 | "import-widget": "Import widget", |
259 | "widget-file": "Widget file", | 260 | "widget-file": "Widget file", |
260 | "invalid-widget-file-error": "Unable to import widget: Invalid widget data structure.", | 261 | "invalid-widget-file-error": "Unable to import widget: Invalid widget data structure.", |
261 | - "widget-import-missing-aliases-title": "Select missing devices used by widget" | 262 | + "widget-import-missing-aliases-title": "Select missing devices used by widget", |
263 | + "open-toolbar": "Open dashboard toolbar", | ||
264 | + "close-toolbar": "Close toolbar" | ||
262 | }, | 265 | }, |
263 | "datakey": { | 266 | "datakey": { |
264 | "settings": "Settings", | 267 | "settings": "Settings", |
@@ -702,6 +705,7 @@ export default angular.module('thingsboard.locale', []) | @@ -702,6 +705,7 @@ export default angular.module('thingsboard.locale', []) | ||
702 | "order": "Order", | 705 | "order": "Order", |
703 | "height": "Height", | 706 | "height": "Height", |
704 | "timewindow": "Timewindow", | 707 | "timewindow": "Timewindow", |
708 | + "use-dashboard-timewindow": "Use dashboard timewindow", | ||
705 | "datasources": "Datasources", | 709 | "datasources": "Datasources", |
706 | "datasource-type": "Type", | 710 | "datasource-type": "Type", |
707 | "datasource-parameters": "Parameters", | 711 | "datasource-parameters": "Parameters", |
@@ -360,9 +360,12 @@ export default class TbFlot { | @@ -360,9 +360,12 @@ export default class TbFlot { | ||
360 | update() { | 360 | update() { |
361 | if (!this.isMouseInteraction) { | 361 | if (!this.isMouseInteraction) { |
362 | if (this.chartType === 'line' || this.chartType === 'bar') { | 362 | if (this.chartType === 'line' || this.chartType === 'bar') { |
363 | + this.options.xaxis.min = this.ctx.timeWindow.minTime; | ||
364 | + this.options.xaxis.max = this.ctx.timeWindow.maxTime; | ||
363 | this.ctx.plot.getOptions().xaxes[0].min = this.ctx.timeWindow.minTime; | 365 | this.ctx.plot.getOptions().xaxes[0].min = this.ctx.timeWindow.minTime; |
364 | this.ctx.plot.getOptions().xaxes[0].max = this.ctx.timeWindow.maxTime; | 366 | this.ctx.plot.getOptions().xaxes[0].max = this.ctx.timeWindow.maxTime; |
365 | if (this.chartType === 'bar') { | 367 | if (this.chartType === 'bar') { |
368 | + this.options.series.bars.barWidth = this.ctx.timeWindow.interval * 0.6; | ||
366 | this.ctx.plot.getOptions().series.bars.barWidth = this.ctx.timeWindow.interval * 0.6; | 369 | this.ctx.plot.getOptions().series.bars.barWidth = this.ctx.timeWindow.interval * 0.6; |
367 | } | 370 | } |
368 | this.ctx.plot.setData(this.ctx.data); | 371 | this.ctx.plot.setData(this.ctx.data); |
@@ -879,6 +882,7 @@ export default class TbFlot { | @@ -879,6 +882,7 @@ export default class TbFlot { | ||
879 | destroy() { | 882 | destroy() { |
880 | if (this.ctx.plot) { | 883 | if (this.ctx.plot) { |
881 | this.ctx.plot.destroy(); | 884 | this.ctx.plot.destroy(); |
885 | + this.ctx.plot = null; | ||
882 | } | 886 | } |
883 | } | 887 | } |
884 | 888 |
@@ -193,6 +193,12 @@ md-sidenav { | @@ -193,6 +193,12 @@ md-sidenav { | ||
193 | pointer-events: all; | 193 | pointer-events: all; |
194 | } | 194 | } |
195 | 195 | ||
196 | +.md-color-picker-input-container { | ||
197 | + md-input-container { | ||
198 | + margin-bottom: 0px; | ||
199 | + } | ||
200 | +} | ||
201 | + | ||
196 | /*********************** | 202 | /*********************** |
197 | * THINGSBOARD SPECIFIC | 203 | * THINGSBOARD SPECIFIC |
198 | ***********************/ | 204 | ***********************/ |
@@ -201,6 +207,10 @@ md-sidenav { | @@ -201,6 +207,10 @@ md-sidenav { | ||
201 | color: rgba(0,0,0,0.54); | 207 | color: rgba(0,0,0,0.54); |
202 | } | 208 | } |
203 | 209 | ||
210 | +.tb-disabled-label { | ||
211 | + color: rgba(0,0,0,0.44); | ||
212 | +} | ||
213 | + | ||
204 | label { | 214 | label { |
205 | &.tb-small { | 215 | &.tb-small { |
206 | pointer-events: none; | 216 | pointer-events: none; |