Commit f93dc9058f625b3dbdd401d9e10c46e1aca6b785

Authored by Igor Kulikov
1 parent 13a6c9a1

UI: Dashboard layout improvements.

... ... @@ -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",
... ...