Commit f93dc9058f625b3dbdd401d9e10c46e1aca6b785
1 parent
13a6c9a1
UI: Dashboard layout improvements.
Showing
9 changed files
with
129 additions
and
28 deletions
... | ... | @@ -215,6 +215,12 @@ function DashboardUtils(types, utils, timeService) { |
215 | 215 | row: widget.row, |
216 | 216 | col: widget.col, |
217 | 217 | }; |
218 | + if (angular.isDefined(widget.config.mobileHeight)) { | |
219 | + mainLayout.widgets[id].mobileHeight = widget.config.mobileHeight; | |
220 | + } | |
221 | + if (angular.isDefined(widget.config.mobileOrder)) { | |
222 | + mainLayout.widgets[id].mobileOrder = widget.config.mobileOrder; | |
223 | + } | |
218 | 224 | } |
219 | 225 | } else { |
220 | 226 | var states = dashboard.configuration.states; | ... | ... |
... | ... | @@ -60,6 +60,8 @@ function Dashboard() { |
60 | 60 | margins: '=', |
61 | 61 | isEdit: '=', |
62 | 62 | autofillHeight: '=', |
63 | + mobileAutofillHeight: '=?', | |
64 | + mobileRowHeight: '=?', | |
63 | 65 | isMobile: '=', |
64 | 66 | isMobileDisabled: '=?', |
65 | 67 | isEditActionEnabled: '=', |
... | ... | @@ -124,8 +126,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
124 | 126 | maxRows: 100, |
125 | 127 | columns: vm.columns ? vm.columns : 24, |
126 | 128 | margins: vm.margins ? vm.margins : [10, 10], |
127 | - minSizeX: 2, | |
128 | - minSizeY: 2, | |
129 | + minSizeX: 1, | |
130 | + minSizeY: 1, | |
129 | 131 | defaultSizeX: 8, |
130 | 132 | defaultSizeY: 6, |
131 | 133 | resizable: { |
... | ... | @@ -170,6 +172,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
170 | 172 | |
171 | 173 | vm.onWidgetFullscreenChanged = onWidgetFullscreenChanged; |
172 | 174 | |
175 | + vm.isAutofillHeight = autofillHeight; | |
176 | + | |
173 | 177 | vm.widgetMouseDown = widgetMouseDown; |
174 | 178 | vm.widgetClicked = widgetClicked; |
175 | 179 | |
... | ... | @@ -177,9 +181,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
177 | 181 | vm.widgetSizeY = widgetSizeY; |
178 | 182 | vm.widgetRow = widgetRow; |
179 | 183 | vm.widgetCol = widgetCol; |
180 | - vm.widgetColor = widgetColor; | |
181 | - vm.widgetBackgroundColor = widgetBackgroundColor; | |
182 | - vm.widgetPadding = widgetPadding; | |
184 | + vm.widgetStyle = widgetStyle; | |
183 | 185 | vm.showWidgetTitle = showWidgetTitle; |
184 | 186 | vm.hasWidgetTitleTemplate = hasWidgetTitleTemplate; |
185 | 187 | vm.widgetTitleTemplate = widgetTitleTemplate; |
... | ... | @@ -236,7 +238,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
236 | 238 | }); |
237 | 239 | |
238 | 240 | function onGirdsterParentResize() { |
239 | - if (gridsterParent.height() && vm.autofillHeight) { | |
241 | + if (gridsterParent.height() && autofillHeight()) { | |
240 | 242 | updateMobileOpts(); |
241 | 243 | } |
242 | 244 | } |
... | ... | @@ -279,7 +281,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
279 | 281 | delete vm.widgetLayoutInfo[widgetId]; |
280 | 282 | } |
281 | 283 | } |
282 | - if (vm.autofillHeight) { | |
284 | + if (autofillHeight()) { | |
283 | 285 | updateMobileOpts(); |
284 | 286 | } |
285 | 287 | }); |
... | ... | @@ -295,15 +297,13 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
295 | 297 | |
296 | 298 | function updateMobileOpts() { |
297 | 299 | var isMobileDisabled = vm.isMobileDisabled === true; |
298 | - var isMobile = vm.isMobile === true && !isMobileDisabled || vm.autofillHeight; | |
300 | + var isMobile = vm.isMobile === true && !isMobileDisabled; | |
299 | 301 | var mobileBreakPoint = isMobileDisabled ? 0 : (isMobile ? 20000 : 960); |
300 | 302 | |
301 | 303 | if (!isMobile && !isMobileDisabled) { |
302 | 304 | isMobile = !$mdMedia('gt-sm'); |
303 | 305 | } |
304 | 306 | |
305 | - var rowHeight = detectRowSize(isMobile); | |
306 | - | |
307 | 307 | if (vm.gridsterOpts.isMobile != isMobile) { |
308 | 308 | vm.gridsterOpts.isMobile = isMobile; |
309 | 309 | vm.gridsterOpts.mobileModeEnabled = isMobile; |
... | ... | @@ -311,6 +311,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
311 | 311 | if (vm.gridsterOpts.mobileBreakPoint != mobileBreakPoint) { |
312 | 312 | vm.gridsterOpts.mobileBreakPoint = mobileBreakPoint; |
313 | 313 | } |
314 | + var rowHeight = detectRowSize(isMobile); | |
314 | 315 | if (vm.gridsterOpts.rowHeight != rowHeight) { |
315 | 316 | vm.gridsterOpts.rowHeight = rowHeight; |
316 | 317 | } |
... | ... | @@ -339,6 +340,14 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
339 | 340 | updateMobileOpts(); |
340 | 341 | }); |
341 | 342 | |
343 | + $scope.$watch('vm.mobileAutofillHeight', function () { | |
344 | + updateMobileOpts(); | |
345 | + }); | |
346 | + | |
347 | + $scope.$watch('vm.mobileRowHeight', function () { | |
348 | + updateMobileOpts(); | |
349 | + }); | |
350 | + | |
342 | 351 | $scope.$watch('vm.isMobileDisabled', function () { |
343 | 352 | updateMobileOpts(); |
344 | 353 | }); |
... | ... | @@ -408,32 +417,49 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
408 | 417 | } |
409 | 418 | }); |
410 | 419 | |
420 | + function autofillHeight() { | |
421 | + if (vm.gridsterOpts.isMobile) { | |
422 | + return angular.isDefined(vm.mobileAutofillHeight) ? vm.mobileAutofillHeight : false; | |
423 | + } else { | |
424 | + return angular.isDefined(vm.autofillHeight) ? vm.autofillHeight : false; | |
425 | + } | |
426 | + } | |
427 | + | |
411 | 428 | function detectRowSize(isMobile) { |
412 | - var rowHeight = isMobile ? 70 : 'match'; | |
413 | - if (vm.autofillHeight) { | |
429 | + var rowHeight; | |
430 | + if (autofillHeight()) { | |
414 | 431 | var viewportHeight = gridsterParent.height(); |
415 | 432 | var totalRows = 0; |
416 | 433 | for (var i = 0; i < vm.widgets.length; i++) { |
417 | 434 | var w = vm.widgets[i]; |
418 | 435 | var sizeY = widgetSizeY(w); |
419 | - totalRows += sizeY; | |
436 | + if (isMobile) { | |
437 | + totalRows += sizeY; | |
438 | + } else { | |
439 | + var row = widgetRow(w); | |
440 | + var bottom = row + sizeY; | |
441 | + totalRows = Math.max(totalRows, bottom); | |
442 | + } | |
420 | 443 | } |
421 | 444 | rowHeight = (viewportHeight - vm.gridsterOpts.margins[1]*(vm.widgets.length+1) + vm.gridsterOpts.margins[0]*vm.widgets.length) / totalRows; |
445 | + } else if (isMobile) { | |
446 | + rowHeight = angular.isDefined(vm.mobileRowHeight) ? vm.mobileRowHeight : 70; | |
447 | + } else { | |
448 | + rowHeight = 'match'; | |
422 | 449 | } |
423 | 450 | return rowHeight; |
424 | 451 | } |
425 | 452 | |
426 | 453 | function widgetOrder(widget) { |
427 | 454 | var order; |
428 | - if (vm.widgetLayouts && vm.widgetLayouts[widget.id]) { | |
429 | - if (angular.isDefined(vm.widgetLayouts[widget.id].mobileOrder) | |
455 | + var hasLayout = vm.widgetLayouts && vm.widgetLayouts[widget.id]; | |
456 | + if (hasLayout && angular.isDefined(vm.widgetLayouts[widget.id].mobileOrder) | |
430 | 457 | && vm.widgetLayouts[widget.id].mobileOrder >= 0) { |
431 | - order = vm.widgetLayouts[widget.id].mobileOrder; | |
432 | - } else { | |
433 | - order = vm.widgetLayouts[widget.id].row; | |
434 | - } | |
458 | + order = vm.widgetLayouts[widget.id].mobileOrder; | |
435 | 459 | } else if (angular.isDefined(widget.config.mobileOrder) && widget.config.mobileOrder >= 0) { |
436 | 460 | order = widget.config.mobileOrder; |
461 | + } else if (hasLayout) { | |
462 | + order = vm.widgetLayouts[widget.id].row; | |
437 | 463 | } else { |
438 | 464 | order = widget.row; |
439 | 465 | } |
... | ... | @@ -722,7 +748,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
722 | 748 | } |
723 | 749 | |
724 | 750 | function widgetSizeY(widget) { |
725 | - if (vm.gridsterOpts.isMobile && !vm.autofillHeight) { | |
751 | + if (vm.gridsterOpts.isMobile && !vm.mobileAutofillHeight) { | |
726 | 752 | var mobileHeight; |
727 | 753 | if (vm.widgetLayouts && vm.widgetLayouts[widget.id]) { |
728 | 754 | mobileHeight = vm.widgetLayouts[widget.id].mobileHeight; |
... | ... | @@ -790,6 +816,18 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
790 | 816 | } |
791 | 817 | } |
792 | 818 | |
819 | + function widgetStyle(widget) { | |
820 | + var style = {cursor: 'pointer', | |
821 | + color: widgetColor(widget), | |
822 | + backgroundColor: widgetBackgroundColor(widget), | |
823 | + padding: widgetPadding(widget), | |
824 | + margin: widgetMargin(widget)}; | |
825 | + if (angular.isDefined(widget.config.widgetStyle)) { | |
826 | + Object.assign(style, widget.config.widgetStyle); | |
827 | + } | |
828 | + return style; | |
829 | + } | |
830 | + | |
793 | 831 | function widgetColor(widget) { |
794 | 832 | if (widget.config.color) { |
795 | 833 | return widget.config.color; |
... | ... | @@ -814,6 +852,14 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ |
814 | 852 | } |
815 | 853 | } |
816 | 854 | |
855 | + function widgetMargin(widget) { | |
856 | + if (widget.config.margin) { | |
857 | + return widget.config.margin; | |
858 | + } else { | |
859 | + return '0px'; | |
860 | + } | |
861 | + } | |
862 | + | |
817 | 863 | function showWidgetTitle(widget) { |
818 | 864 | if (angular.isDefined(widget.config.showTitle)) { |
819 | 865 | return widget.config.showTitle; | ... | ... |
... | ... | @@ -22,7 +22,8 @@ |
22 | 22 | </md-progress-circular> |
23 | 23 | </md-content> |
24 | 24 | <md-menu md-position-mode="target target" tb-mousepoint-menu> |
25 | - <md-content id="gridster-parent" class="tb-dashboard-content" flex layout-wrap ng-click="" tb-contextmenu="vm.openDashboardContextMenu($event, $mdOpenMousepointMenu)"> | |
25 | + <md-content id="gridster-parent" class="tb-dashboard-content" flex layout-wrap ng-click="" ng-style="{'overflowY': vm.isAutofillHeight() ? 'hidden' : 'auto'}" | |
26 | + tb-contextmenu="vm.openDashboardContextMenu($event, $mdOpenMousepointMenu)"> | |
26 | 27 | <div ng-class="vm.dashboardClass" id="gridster-background" style="height: auto; min-height: 100%; display: inline;"> |
27 | 28 | <div id="gridster-child" gridster="vm.gridsterOpts"> |
28 | 29 | <ul> |
... | ... | @@ -42,10 +43,7 @@ |
42 | 43 | tb-mousedown="vm.widgetMouseDown($event, widget)" |
43 | 44 | ng-click="vm.widgetClicked($event, widget)" |
44 | 45 | tb-contextmenu="vm.openWidgetContextMenu($event, widget, $mdOpenMousepointMenu)" |
45 | - ng-style="{cursor: 'pointer', | |
46 | - color: vm.widgetColor(widget), | |
47 | - backgroundColor: vm.widgetBackgroundColor(widget), | |
48 | - padding: vm.widgetPadding(widget)}"> | |
46 | + ng-style="vm.widgetStyle(widget)"> | |
49 | 47 | <div class="tb-widget-title" layout="column" layout-align="center start" ng-show="vm.showWidgetTitlePanel(widget)"> |
50 | 48 | <div ng-if="vm.hasWidgetTitleTemplate(widget)" ng-include="vm.widgetTitleTemplate(widget)"></div> |
51 | 49 | <span ng-show="vm.showWidgetTitle(widget)" ng-style="vm.widgetTitleStyle(widget)" class="md-subhead">{{vm.widgetTitle(widget)}}</span> | ... | ... |
... | ... | @@ -64,7 +64,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout |
64 | 64 | '*' |
65 | 65 | ]; |
66 | 66 | |
67 | - scope.titleStyleEditorOptions = { | |
67 | + scope.styleEditorOptions = { | |
68 | 68 | useWrapMode: true, |
69 | 69 | mode: 'json', |
70 | 70 | advanced: { |
... | ... | @@ -106,6 +106,9 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout |
106 | 106 | scope.backgroundColor = config.backgroundColor; |
107 | 107 | scope.color = config.color; |
108 | 108 | scope.padding = config.padding; |
109 | + scope.margin = config.margin; | |
110 | + scope.widgetStyle = | |
111 | + angular.toJson(angular.isDefined(config.widgetStyle) ? config.widgetStyle : {}, true); | |
109 | 112 | scope.titleStyle = |
110 | 113 | angular.toJson(angular.isDefined(config.titleStyle) ? config.titleStyle : { |
111 | 114 | fontSize: '16px', |
... | ... | @@ -205,6 +208,12 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout |
205 | 208 | ngModelCtrl.$setValidity('datasources', valid); |
206 | 209 | } |
207 | 210 | try { |
211 | + angular.fromJson(scope.widgetStyle); | |
212 | + ngModelCtrl.$setValidity('widgetStyle', true); | |
213 | + } catch (e) { | |
214 | + ngModelCtrl.$setValidity('widgetStyle', false); | |
215 | + } | |
216 | + try { | |
208 | 217 | angular.fromJson(scope.titleStyle); |
209 | 218 | ngModelCtrl.$setValidity('titleStyle', true); |
210 | 219 | } catch (e) { |
... | ... | @@ -215,7 +224,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout |
215 | 224 | }; |
216 | 225 | |
217 | 226 | scope.$watch('title + showTitle + dropShadow + enableFullscreen + backgroundColor + color + ' + |
218 | - 'padding + titleStyle + mobileOrder + mobileHeight + units + decimals + useDashboardTimewindow + ' + | |
227 | + 'padding + margin + widgetStyle + titleStyle + mobileOrder + mobileHeight + units + decimals + useDashboardTimewindow + ' + | |
219 | 228 | 'alarmSearchStatus + alarmsPollingInterval + showLegend', function () { |
220 | 229 | if (ngModelCtrl.$viewValue) { |
221 | 230 | var value = ngModelCtrl.$viewValue; |
... | ... | @@ -228,6 +237,12 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout |
228 | 237 | config.backgroundColor = scope.backgroundColor; |
229 | 238 | config.color = scope.color; |
230 | 239 | config.padding = scope.padding; |
240 | + config.margin = scope.margin; | |
241 | + try { | |
242 | + config.widgetStyle = angular.fromJson(scope.widgetStyle); | |
243 | + } catch (e) { | |
244 | + config.widgetStyle = {}; | |
245 | + } | |
231 | 246 | try { |
232 | 247 | config.titleStyle = angular.fromJson(scope.titleStyle); |
233 | 248 | } catch (e) { | ... | ... |
... | ... | @@ -179,7 +179,7 @@ |
179 | 179 | </md-input-container> |
180 | 180 | <div flex ng-show="showTitle"> |
181 | 181 | <label translate>widget-config.title-style</label> |
182 | - <div ui-ace="titleStyleEditorOptions" ng-model="titleStyle" ng-style="{ minHeight: '100px' }"> | |
182 | + <div ui-ace="styleEditorOptions" ng-model="titleStyle" ng-style="{ minHeight: '100px' }"> | |
183 | 183 | </div> |
184 | 184 | </div> |
185 | 185 | </div> |
... | ... | @@ -199,6 +199,11 @@ |
199 | 199 | ng-model="enableFullscreen">{{ 'widget-config.enable-fullscreen' | translate }} |
200 | 200 | </md-checkbox> |
201 | 201 | </div> |
202 | + <div flex> | |
203 | + <label translate>widget-config.widget-style</label> | |
204 | + <div ui-ace="styleEditorOptions" ng-model="widgetStyle" ng-style="{ minHeight: '100px' }"> | |
205 | + </div> | |
206 | + </div> | |
202 | 207 | </div> |
203 | 208 | <div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center"> |
204 | 209 | <div flex |
... | ... | @@ -227,6 +232,10 @@ |
227 | 232 | <label translate>widget-config.padding</label> |
228 | 233 | <input ng-model="padding"> |
229 | 234 | </md-input-container> |
235 | + <md-input-container flex> | |
236 | + <label translate>widget-config.margin</label> | |
237 | + <input ng-model="margin"> | |
238 | + </md-input-container> | |
230 | 239 | </div> |
231 | 240 | <div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center"> |
232 | 241 | <md-input-container flex> | ... | ... |
... | ... | @@ -69,6 +69,8 @@ export default function DashboardSettingsController($scope, $mdDialog, statesCon |
69 | 69 | vm.gridSettings.columns = vm.gridSettings.columns || 24; |
70 | 70 | vm.gridSettings.margins = vm.gridSettings.margins || [10, 10]; |
71 | 71 | vm.gridSettings.autoFillHeight = angular.isDefined(vm.gridSettings.autoFillHeight) ? vm.gridSettings.autoFillHeight : false; |
72 | + vm.gridSettings.mobileAutoFillHeight = angular.isDefined(vm.gridSettings.mobileAutoFillHeight) ? vm.gridSettings.mobileAutoFillHeight : false; | |
73 | + vm.gridSettings.mobileRowHeight = angular.isDefined(vm.gridSettings.mobileRowHeight) ? vm.gridSettings.mobileRowHeight : 70; | |
72 | 74 | vm.hMargin = vm.gridSettings.margins[0]; |
73 | 75 | vm.vMargin = vm.gridSettings.margins[1]; |
74 | 76 | vm.gridSettings.backgroundSizeMode = vm.gridSettings.backgroundSizeMode || '100%'; | ... | ... |
... | ... | @@ -171,6 +171,22 @@ |
171 | 171 | <md-option value="auto">Original size</md-option> |
172 | 172 | </md-select> |
173 | 173 | </md-input-container> |
174 | + <small translate>dashboard.mobile-layout</small> | |
175 | + <div flex layout="row" layout-align="start center"> | |
176 | + <md-checkbox flex aria-label="{{ 'dashboard.autofill-height' | translate }}" | |
177 | + ng-model="vm.gridSettings.mobileAutoFillHeight">{{ 'dashboard.autofill-height' | translate }} | |
178 | + </md-checkbox> | |
179 | + <md-input-container flex class="md-block"> | |
180 | + <label translate>dashboard.mobile-row-height</label> | |
181 | + <input ng-required="vm.gridSettings" type="number" step="any" name="mobileRowHeight" ng-model="vm.gridSettings.mobileRowHeight" | |
182 | + min="5" max="200" /> | |
183 | + <div ng-messages="theForm.mobileRowHeight.$error" multiple md-auto-hide="false"> | |
184 | + <div ng-message="required" translate>dashboard.mobile-row-height-required</div> | |
185 | + <div ng-message="min" translate>dashboard.min-mobile-row-height-message</div> | |
186 | + <div ng-message="max" translate>dashboard.max-mobile-row-height-message</div> | |
187 | + </div> | |
188 | + </md-input-container> | |
189 | + </div> | |
174 | 190 | </div> |
175 | 191 | </fieldset> |
176 | 192 | </div> | ... | ... |
... | ... | @@ -50,6 +50,8 @@ |
50 | 50 | dashboard-timewindow="vm.dashboardCtx.dashboardTimewindow" |
51 | 51 | is-edit="vm.isEdit" |
52 | 52 | autofill-height="vm.layoutCtx.gridSettings.autoFillHeight && !vm.isEdit" |
53 | + mobile-autofill-height="vm.layoutCtx.gridSettings.mobileAutoFillHeight && !vm.isEdit" | |
54 | + mobile-row-height="vm.layoutCtx.gridSettings.mobileRowHeight" | |
53 | 55 | is-mobile="vm.isMobile" |
54 | 56 | is-mobile-disabled="vm.widgetEditMode" |
55 | 57 | is-edit-action-enabled="vm.isEdit" | ... | ... |
... | ... | @@ -432,6 +432,11 @@ export default angular.module('thingsboard.locale', []) |
432 | 432 | "min-vertical-margin-message": "Only 0 is allowed as minimum vertical margin value.", |
433 | 433 | "max-vertical-margin-message": "Only 50 is allowed as maximum vertical margin value.", |
434 | 434 | "autofill-height": "Auto fill layout height", |
435 | + "mobile-layout": "Mobile layout settings", | |
436 | + "mobile-row-height": "Mobile row height, px", | |
437 | + "mobile-row-height-required": "Mobile row height value is required.", | |
438 | + "min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.", | |
439 | + "max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.", | |
435 | 440 | "display-title": "Display dashboard title", |
436 | 441 | "toolbar-always-open": "Keep toolbar opened", |
437 | 442 | "title-color": "Title color", |
... | ... | @@ -1154,6 +1159,8 @@ export default angular.module('thingsboard.locale', []) |
1154 | 1159 | "background-color": "Background color", |
1155 | 1160 | "text-color": "Text color", |
1156 | 1161 | "padding": "Padding", |
1162 | + "margin": "Margin", | |
1163 | + "widget-style": "Widget style", | |
1157 | 1164 | "title-style": "Title style", |
1158 | 1165 | "mobile-mode-settings": "Mobile mode settings", |
1159 | 1166 | "order": "Order", | ... | ... |