diff --git a/ui/src/app/common/dashboard-utils.service.js b/ui/src/app/common/dashboard-utils.service.js index 915d5501eb..0fc940dd4d 100644 --- a/ui/src/app/common/dashboard-utils.service.js +++ b/ui/src/app/common/dashboard-utils.service.js @@ -215,6 +215,12 @@ function DashboardUtils(types, utils, timeService) { row: widget.row, col: widget.col, }; + if (angular.isDefined(widget.config.mobileHeight)) { + mainLayout.widgets[id].mobileHeight = widget.config.mobileHeight; + } + if (angular.isDefined(widget.config.mobileOrder)) { + mainLayout.widgets[id].mobileOrder = widget.config.mobileOrder; + } } } else { var states = dashboard.configuration.states; diff --git a/ui/src/app/components/dashboard.directive.js b/ui/src/app/components/dashboard.directive.js index 1a4cdba4e4..a722e04d15 100644 --- a/ui/src/app/components/dashboard.directive.js +++ b/ui/src/app/components/dashboard.directive.js @@ -60,6 +60,8 @@ function Dashboard() { margins: '=', isEdit: '=', autofillHeight: '=', + mobileAutofillHeight: '=?', + mobileRowHeight: '=?', isMobile: '=', isMobileDisabled: '=?', isEditActionEnabled: '=', @@ -124,8 +126,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ maxRows: 100, columns: vm.columns ? vm.columns : 24, margins: vm.margins ? vm.margins : [10, 10], - minSizeX: 2, - minSizeY: 2, + minSizeX: 1, + minSizeY: 1, defaultSizeX: 8, defaultSizeY: 6, resizable: { @@ -170,6 +172,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ vm.onWidgetFullscreenChanged = onWidgetFullscreenChanged; + vm.isAutofillHeight = autofillHeight; + vm.widgetMouseDown = widgetMouseDown; vm.widgetClicked = widgetClicked; @@ -177,9 +181,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ vm.widgetSizeY = widgetSizeY; vm.widgetRow = widgetRow; vm.widgetCol = widgetCol; - vm.widgetColor = widgetColor; - vm.widgetBackgroundColor = widgetBackgroundColor; - vm.widgetPadding = widgetPadding; + vm.widgetStyle = widgetStyle; vm.showWidgetTitle = showWidgetTitle; vm.hasWidgetTitleTemplate = hasWidgetTitleTemplate; vm.widgetTitleTemplate = widgetTitleTemplate; @@ -236,7 +238,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ }); function onGirdsterParentResize() { - if (gridsterParent.height() && vm.autofillHeight) { + if (gridsterParent.height() && autofillHeight()) { updateMobileOpts(); } } @@ -279,7 +281,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ delete vm.widgetLayoutInfo[widgetId]; } } - if (vm.autofillHeight) { + if (autofillHeight()) { updateMobileOpts(); } }); @@ -295,15 +297,13 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ function updateMobileOpts() { var isMobileDisabled = vm.isMobileDisabled === true; - var isMobile = vm.isMobile === true && !isMobileDisabled || vm.autofillHeight; + var isMobile = vm.isMobile === true && !isMobileDisabled; var mobileBreakPoint = isMobileDisabled ? 0 : (isMobile ? 20000 : 960); if (!isMobile && !isMobileDisabled) { isMobile = !$mdMedia('gt-sm'); } - var rowHeight = detectRowSize(isMobile); - if (vm.gridsterOpts.isMobile != isMobile) { vm.gridsterOpts.isMobile = isMobile; vm.gridsterOpts.mobileModeEnabled = isMobile; @@ -311,6 +311,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ if (vm.gridsterOpts.mobileBreakPoint != mobileBreakPoint) { vm.gridsterOpts.mobileBreakPoint = mobileBreakPoint; } + var rowHeight = detectRowSize(isMobile); if (vm.gridsterOpts.rowHeight != rowHeight) { vm.gridsterOpts.rowHeight = rowHeight; } @@ -339,6 +340,14 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ updateMobileOpts(); }); + $scope.$watch('vm.mobileAutofillHeight', function () { + updateMobileOpts(); + }); + + $scope.$watch('vm.mobileRowHeight', function () { + updateMobileOpts(); + }); + $scope.$watch('vm.isMobileDisabled', function () { updateMobileOpts(); }); @@ -408,32 +417,49 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ } }); + function autofillHeight() { + if (vm.gridsterOpts.isMobile) { + return angular.isDefined(vm.mobileAutofillHeight) ? vm.mobileAutofillHeight : false; + } else { + return angular.isDefined(vm.autofillHeight) ? vm.autofillHeight : false; + } + } + function detectRowSize(isMobile) { - var rowHeight = isMobile ? 70 : 'match'; - if (vm.autofillHeight) { + var rowHeight; + if (autofillHeight()) { var viewportHeight = gridsterParent.height(); var totalRows = 0; for (var i = 0; i < vm.widgets.length; i++) { var w = vm.widgets[i]; var sizeY = widgetSizeY(w); - totalRows += sizeY; + if (isMobile) { + totalRows += sizeY; + } else { + var row = widgetRow(w); + var bottom = row + sizeY; + totalRows = Math.max(totalRows, bottom); + } } rowHeight = (viewportHeight - vm.gridsterOpts.margins[1]*(vm.widgets.length+1) + vm.gridsterOpts.margins[0]*vm.widgets.length) / totalRows; + } else if (isMobile) { + rowHeight = angular.isDefined(vm.mobileRowHeight) ? vm.mobileRowHeight : 70; + } else { + rowHeight = 'match'; } return rowHeight; } function widgetOrder(widget) { var order; - if (vm.widgetLayouts && vm.widgetLayouts[widget.id]) { - if (angular.isDefined(vm.widgetLayouts[widget.id].mobileOrder) + var hasLayout = vm.widgetLayouts && vm.widgetLayouts[widget.id]; + if (hasLayout && angular.isDefined(vm.widgetLayouts[widget.id].mobileOrder) && vm.widgetLayouts[widget.id].mobileOrder >= 0) { - order = vm.widgetLayouts[widget.id].mobileOrder; - } else { - order = vm.widgetLayouts[widget.id].row; - } + order = vm.widgetLayouts[widget.id].mobileOrder; } else if (angular.isDefined(widget.config.mobileOrder) && widget.config.mobileOrder >= 0) { order = widget.config.mobileOrder; + } else if (hasLayout) { + order = vm.widgetLayouts[widget.id].row; } else { order = widget.row; } @@ -722,7 +748,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ } function widgetSizeY(widget) { - if (vm.gridsterOpts.isMobile && !vm.autofillHeight) { + if (vm.gridsterOpts.isMobile && !vm.mobileAutofillHeight) { var mobileHeight; if (vm.widgetLayouts && vm.widgetLayouts[widget.id]) { mobileHeight = vm.widgetLayouts[widget.id].mobileHeight; @@ -790,6 +816,18 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ } } + function widgetStyle(widget) { + var style = {cursor: 'pointer', + color: widgetColor(widget), + backgroundColor: widgetBackgroundColor(widget), + padding: widgetPadding(widget), + margin: widgetMargin(widget)}; + if (angular.isDefined(widget.config.widgetStyle)) { + Object.assign(style, widget.config.widgetStyle); + } + return style; + } + function widgetColor(widget) { if (widget.config.color) { return widget.config.color; @@ -814,6 +852,14 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ } } + function widgetMargin(widget) { + if (widget.config.margin) { + return widget.config.margin; + } else { + return '0px'; + } + } + function showWidgetTitle(widget) { if (angular.isDefined(widget.config.showTitle)) { return widget.config.showTitle; diff --git a/ui/src/app/components/dashboard.tpl.html b/ui/src/app/components/dashboard.tpl.html index 5d46fea5ca..2887419b5e 100644 --- a/ui/src/app/components/dashboard.tpl.html +++ b/ui/src/app/components/dashboard.tpl.html @@ -22,7 +22,8 @@ - +
    @@ -42,10 +43,7 @@ tb-mousedown="vm.widgetMouseDown($event, widget)" ng-click="vm.widgetClicked($event, widget)" tb-contextmenu="vm.openWidgetContextMenu($event, widget, $mdOpenMousepointMenu)" - ng-style="{cursor: 'pointer', - color: vm.widgetColor(widget), - backgroundColor: vm.widgetBackgroundColor(widget), - padding: vm.widgetPadding(widget)}"> + ng-style="vm.widgetStyle(widget)">
    {{vm.widgetTitle(widget)}} diff --git a/ui/src/app/components/widget/widget-config.directive.js b/ui/src/app/components/widget/widget-config.directive.js index afdaf8d745..8ec91a80c8 100644 --- a/ui/src/app/components/widget/widget-config.directive.js +++ b/ui/src/app/components/widget/widget-config.directive.js @@ -64,7 +64,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout '*' ]; - scope.titleStyleEditorOptions = { + scope.styleEditorOptions = { useWrapMode: true, mode: 'json', advanced: { @@ -106,6 +106,9 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout scope.backgroundColor = config.backgroundColor; scope.color = config.color; scope.padding = config.padding; + scope.margin = config.margin; + scope.widgetStyle = + angular.toJson(angular.isDefined(config.widgetStyle) ? config.widgetStyle : {}, true); scope.titleStyle = angular.toJson(angular.isDefined(config.titleStyle) ? config.titleStyle : { fontSize: '16px', @@ -204,6 +207,12 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout valid = config && config.datasources && config.datasources.length > 0; ngModelCtrl.$setValidity('datasources', valid); } + try { + angular.fromJson(scope.widgetStyle); + ngModelCtrl.$setValidity('widgetStyle', true); + } catch (e) { + ngModelCtrl.$setValidity('widgetStyle', false); + } try { angular.fromJson(scope.titleStyle); ngModelCtrl.$setValidity('titleStyle', true); @@ -215,7 +224,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout }; scope.$watch('title + showTitle + dropShadow + enableFullscreen + backgroundColor + color + ' + - 'padding + titleStyle + mobileOrder + mobileHeight + units + decimals + useDashboardTimewindow + ' + + 'padding + margin + widgetStyle + titleStyle + mobileOrder + mobileHeight + units + decimals + useDashboardTimewindow + ' + 'alarmSearchStatus + alarmsPollingInterval + showLegend', function () { if (ngModelCtrl.$viewValue) { var value = ngModelCtrl.$viewValue; @@ -228,6 +237,12 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout config.backgroundColor = scope.backgroundColor; config.color = scope.color; config.padding = scope.padding; + config.margin = scope.margin; + try { + config.widgetStyle = angular.fromJson(scope.widgetStyle); + } catch (e) { + config.widgetStyle = {}; + } try { config.titleStyle = angular.fromJson(scope.titleStyle); } catch (e) { diff --git a/ui/src/app/components/widget/widget-config.tpl.html b/ui/src/app/components/widget/widget-config.tpl.html index f2ee0f112d..6851ee8c83 100644 --- a/ui/src/app/components/widget/widget-config.tpl.html +++ b/ui/src/app/components/widget/widget-config.tpl.html @@ -179,7 +179,7 @@
    -
    +
    @@ -199,6 +199,11 @@ ng-model="enableFullscreen">{{ 'widget-config.enable-fullscreen' | translate }}
    +
    + +
    +
    +
widget-config.padding + + + +
diff --git a/ui/src/app/dashboard/dashboard-settings.controller.js b/ui/src/app/dashboard/dashboard-settings.controller.js index c5743c9e78..749b7d6c7f 100644 --- a/ui/src/app/dashboard/dashboard-settings.controller.js +++ b/ui/src/app/dashboard/dashboard-settings.controller.js @@ -69,6 +69,8 @@ export default function DashboardSettingsController($scope, $mdDialog, statesCon vm.gridSettings.columns = vm.gridSettings.columns || 24; vm.gridSettings.margins = vm.gridSettings.margins || [10, 10]; vm.gridSettings.autoFillHeight = angular.isDefined(vm.gridSettings.autoFillHeight) ? vm.gridSettings.autoFillHeight : false; + vm.gridSettings.mobileAutoFillHeight = angular.isDefined(vm.gridSettings.mobileAutoFillHeight) ? vm.gridSettings.mobileAutoFillHeight : false; + vm.gridSettings.mobileRowHeight = angular.isDefined(vm.gridSettings.mobileRowHeight) ? vm.gridSettings.mobileRowHeight : 70; vm.hMargin = vm.gridSettings.margins[0]; vm.vMargin = vm.gridSettings.margins[1]; vm.gridSettings.backgroundSizeMode = vm.gridSettings.backgroundSizeMode || '100%'; diff --git a/ui/src/app/dashboard/dashboard-settings.tpl.html b/ui/src/app/dashboard/dashboard-settings.tpl.html index 427ca1933e..0d6928739a 100644 --- a/ui/src/app/dashboard/dashboard-settings.tpl.html +++ b/ui/src/app/dashboard/dashboard-settings.tpl.html @@ -171,6 +171,22 @@ Original size + dashboard.mobile-layout +
+ {{ 'dashboard.autofill-height' | translate }} + + + + +
+
dashboard.mobile-row-height-required
+
dashboard.min-mobile-row-height-message
+
dashboard.max-mobile-row-height-message
+
+
+
diff --git a/ui/src/app/dashboard/layouts/dashboard-layout.tpl.html b/ui/src/app/dashboard/layouts/dashboard-layout.tpl.html index 3df1327fad..90a10b11ee 100644 --- a/ui/src/app/dashboard/layouts/dashboard-layout.tpl.html +++ b/ui/src/app/dashboard/layouts/dashboard-layout.tpl.html @@ -50,6 +50,8 @@ dashboard-timewindow="vm.dashboardCtx.dashboardTimewindow" is-edit="vm.isEdit" autofill-height="vm.layoutCtx.gridSettings.autoFillHeight && !vm.isEdit" + mobile-autofill-height="vm.layoutCtx.gridSettings.mobileAutoFillHeight && !vm.isEdit" + mobile-row-height="vm.layoutCtx.gridSettings.mobileRowHeight" is-mobile="vm.isMobile" is-mobile-disabled="vm.widgetEditMode" is-edit-action-enabled="vm.isEdit" diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js index 4d4ebe2600..819afbb5ee 100644 --- a/ui/src/app/locale/locale.constant.js +++ b/ui/src/app/locale/locale.constant.js @@ -432,6 +432,11 @@ export default angular.module('thingsboard.locale', []) "min-vertical-margin-message": "Only 0 is allowed as minimum vertical margin value.", "max-vertical-margin-message": "Only 50 is allowed as maximum vertical margin value.", "autofill-height": "Auto fill layout height", + "mobile-layout": "Mobile layout settings", + "mobile-row-height": "Mobile row height, px", + "mobile-row-height-required": "Mobile row height value is required.", + "min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.", + "max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.", "display-title": "Display dashboard title", "toolbar-always-open": "Keep toolbar opened", "title-color": "Title color", @@ -1154,6 +1159,8 @@ export default angular.module('thingsboard.locale', []) "background-color": "Background color", "text-color": "Text color", "padding": "Padding", + "margin": "Margin", + "widget-style": "Widget style", "title-style": "Title style", "mobile-mode-settings": "Mobile mode settings", "order": "Order",