UI: Dashboard layout improvements.

This commit is contained in:
Igor Kulikov 2017-06-23 12:59:10 +03:00
parent 13a6c9a142
commit f93dc9058f
9 changed files with 129 additions and 28 deletions

View File

@ -215,6 +215,12 @@ function DashboardUtils(types, utils, timeService) {
row: widget.row, row: widget.row,
col: widget.col, 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 { } else {
var states = dashboard.configuration.states; var states = dashboard.configuration.states;

View File

@ -60,6 +60,8 @@ function Dashboard() {
margins: '=', margins: '=',
isEdit: '=', isEdit: '=',
autofillHeight: '=', autofillHeight: '=',
mobileAutofillHeight: '=?',
mobileRowHeight: '=?',
isMobile: '=', isMobile: '=',
isMobileDisabled: '=?', isMobileDisabled: '=?',
isEditActionEnabled: '=', isEditActionEnabled: '=',
@ -124,8 +126,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
maxRows: 100, maxRows: 100,
columns: vm.columns ? vm.columns : 24, columns: vm.columns ? vm.columns : 24,
margins: vm.margins ? vm.margins : [10, 10], margins: vm.margins ? vm.margins : [10, 10],
minSizeX: 2, minSizeX: 1,
minSizeY: 2, minSizeY: 1,
defaultSizeX: 8, defaultSizeX: 8,
defaultSizeY: 6, defaultSizeY: 6,
resizable: { resizable: {
@ -170,6 +172,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
vm.onWidgetFullscreenChanged = onWidgetFullscreenChanged; vm.onWidgetFullscreenChanged = onWidgetFullscreenChanged;
vm.isAutofillHeight = autofillHeight;
vm.widgetMouseDown = widgetMouseDown; vm.widgetMouseDown = widgetMouseDown;
vm.widgetClicked = widgetClicked; vm.widgetClicked = widgetClicked;
@ -177,9 +181,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
vm.widgetSizeY = widgetSizeY; vm.widgetSizeY = widgetSizeY;
vm.widgetRow = widgetRow; vm.widgetRow = widgetRow;
vm.widgetCol = widgetCol; vm.widgetCol = widgetCol;
vm.widgetColor = widgetColor; vm.widgetStyle = widgetStyle;
vm.widgetBackgroundColor = widgetBackgroundColor;
vm.widgetPadding = widgetPadding;
vm.showWidgetTitle = showWidgetTitle; vm.showWidgetTitle = showWidgetTitle;
vm.hasWidgetTitleTemplate = hasWidgetTitleTemplate; vm.hasWidgetTitleTemplate = hasWidgetTitleTemplate;
vm.widgetTitleTemplate = widgetTitleTemplate; vm.widgetTitleTemplate = widgetTitleTemplate;
@ -236,7 +238,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
}); });
function onGirdsterParentResize() { function onGirdsterParentResize() {
if (gridsterParent.height() && vm.autofillHeight) { if (gridsterParent.height() && autofillHeight()) {
updateMobileOpts(); updateMobileOpts();
} }
} }
@ -279,7 +281,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
delete vm.widgetLayoutInfo[widgetId]; delete vm.widgetLayoutInfo[widgetId];
} }
} }
if (vm.autofillHeight) { if (autofillHeight()) {
updateMobileOpts(); updateMobileOpts();
} }
}); });
@ -295,15 +297,13 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
function updateMobileOpts() { function updateMobileOpts() {
var isMobileDisabled = vm.isMobileDisabled === true; 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); var mobileBreakPoint = isMobileDisabled ? 0 : (isMobile ? 20000 : 960);
if (!isMobile && !isMobileDisabled) { if (!isMobile && !isMobileDisabled) {
isMobile = !$mdMedia('gt-sm'); isMobile = !$mdMedia('gt-sm');
} }
var rowHeight = detectRowSize(isMobile);
if (vm.gridsterOpts.isMobile != isMobile) { if (vm.gridsterOpts.isMobile != isMobile) {
vm.gridsterOpts.isMobile = isMobile; vm.gridsterOpts.isMobile = isMobile;
vm.gridsterOpts.mobileModeEnabled = isMobile; vm.gridsterOpts.mobileModeEnabled = isMobile;
@ -311,6 +311,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
if (vm.gridsterOpts.mobileBreakPoint != mobileBreakPoint) { if (vm.gridsterOpts.mobileBreakPoint != mobileBreakPoint) {
vm.gridsterOpts.mobileBreakPoint = mobileBreakPoint; vm.gridsterOpts.mobileBreakPoint = mobileBreakPoint;
} }
var rowHeight = detectRowSize(isMobile);
if (vm.gridsterOpts.rowHeight != rowHeight) { if (vm.gridsterOpts.rowHeight != rowHeight) {
vm.gridsterOpts.rowHeight = rowHeight; vm.gridsterOpts.rowHeight = rowHeight;
} }
@ -339,6 +340,14 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
updateMobileOpts(); updateMobileOpts();
}); });
$scope.$watch('vm.mobileAutofillHeight', function () {
updateMobileOpts();
});
$scope.$watch('vm.mobileRowHeight', function () {
updateMobileOpts();
});
$scope.$watch('vm.isMobileDisabled', function () { $scope.$watch('vm.isMobileDisabled', function () {
updateMobileOpts(); 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) { function detectRowSize(isMobile) {
var rowHeight = isMobile ? 70 : 'match'; var rowHeight;
if (vm.autofillHeight) { if (autofillHeight()) {
var viewportHeight = gridsterParent.height(); var viewportHeight = gridsterParent.height();
var totalRows = 0; var totalRows = 0;
for (var i = 0; i < vm.widgets.length; i++) { for (var i = 0; i < vm.widgets.length; i++) {
var w = vm.widgets[i]; var w = vm.widgets[i];
var sizeY = widgetSizeY(w); var sizeY = widgetSizeY(w);
if (isMobile) {
totalRows += sizeY; 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; 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; return rowHeight;
} }
function widgetOrder(widget) { function widgetOrder(widget) {
var order; var order;
if (vm.widgetLayouts && vm.widgetLayouts[widget.id]) { var hasLayout = vm.widgetLayouts && vm.widgetLayouts[widget.id];
if (angular.isDefined(vm.widgetLayouts[widget.id].mobileOrder) if (hasLayout && angular.isDefined(vm.widgetLayouts[widget.id].mobileOrder)
&& vm.widgetLayouts[widget.id].mobileOrder >= 0) { && vm.widgetLayouts[widget.id].mobileOrder >= 0) {
order = vm.widgetLayouts[widget.id].mobileOrder; order = vm.widgetLayouts[widget.id].mobileOrder;
} else {
order = vm.widgetLayouts[widget.id].row;
}
} else if (angular.isDefined(widget.config.mobileOrder) && widget.config.mobileOrder >= 0) { } else if (angular.isDefined(widget.config.mobileOrder) && widget.config.mobileOrder >= 0) {
order = widget.config.mobileOrder; order = widget.config.mobileOrder;
} else if (hasLayout) {
order = vm.widgetLayouts[widget.id].row;
} else { } else {
order = widget.row; order = widget.row;
} }
@ -722,7 +748,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
} }
function widgetSizeY(widget) { function widgetSizeY(widget) {
if (vm.gridsterOpts.isMobile && !vm.autofillHeight) { if (vm.gridsterOpts.isMobile && !vm.mobileAutofillHeight) {
var mobileHeight; var mobileHeight;
if (vm.widgetLayouts && vm.widgetLayouts[widget.id]) { if (vm.widgetLayouts && vm.widgetLayouts[widget.id]) {
mobileHeight = vm.widgetLayouts[widget.id].mobileHeight; 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) { function widgetColor(widget) {
if (widget.config.color) { if (widget.config.color) {
return 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) { function showWidgetTitle(widget) {
if (angular.isDefined(widget.config.showTitle)) { if (angular.isDefined(widget.config.showTitle)) {
return widget.config.showTitle; return widget.config.showTitle;

View File

@ -22,7 +22,8 @@
</md-progress-circular> </md-progress-circular>
</md-content> </md-content>
<md-menu md-position-mode="target target" tb-mousepoint-menu> <md-menu md-position-mode="target target" tb-mousepoint-menu>
<md-content id="gridster-parent" class="tb-dashboard-content" flex layout-wrap ng-click="" tb-contextmenu="vm.openDashboardContextMenu($event, $mdOpenMousepointMenu)"> <md-content id="gridster-parent" class="tb-dashboard-content" flex layout-wrap ng-click="" ng-style="{'overflowY': vm.isAutofillHeight() ? 'hidden' : 'auto'}"
tb-contextmenu="vm.openDashboardContextMenu($event, $mdOpenMousepointMenu)">
<div ng-class="vm.dashboardClass" id="gridster-background" style="height: auto; min-height: 100%; display: inline;"> <div ng-class="vm.dashboardClass" id="gridster-background" style="height: auto; min-height: 100%; display: inline;">
<div id="gridster-child" gridster="vm.gridsterOpts"> <div id="gridster-child" gridster="vm.gridsterOpts">
<ul> <ul>
@ -42,10 +43,7 @@
tb-mousedown="vm.widgetMouseDown($event, widget)" tb-mousedown="vm.widgetMouseDown($event, widget)"
ng-click="vm.widgetClicked($event, widget)" ng-click="vm.widgetClicked($event, widget)"
tb-contextmenu="vm.openWidgetContextMenu($event, widget, $mdOpenMousepointMenu)" tb-contextmenu="vm.openWidgetContextMenu($event, widget, $mdOpenMousepointMenu)"
ng-style="{cursor: 'pointer', ng-style="vm.widgetStyle(widget)">
color: vm.widgetColor(widget),
backgroundColor: vm.widgetBackgroundColor(widget),
padding: vm.widgetPadding(widget)}">
<div class="tb-widget-title" layout="column" layout-align="center start" ng-show="vm.showWidgetTitlePanel(widget)"> <div class="tb-widget-title" layout="column" layout-align="center start" ng-show="vm.showWidgetTitlePanel(widget)">
<div ng-if="vm.hasWidgetTitleTemplate(widget)" ng-include="vm.widgetTitleTemplate(widget)"></div> <div ng-if="vm.hasWidgetTitleTemplate(widget)" ng-include="vm.widgetTitleTemplate(widget)"></div>
<span ng-show="vm.showWidgetTitle(widget)" ng-style="vm.widgetTitleStyle(widget)" class="md-subhead">{{vm.widgetTitle(widget)}}</span> <span ng-show="vm.showWidgetTitle(widget)" ng-style="vm.widgetTitleStyle(widget)" class="md-subhead">{{vm.widgetTitle(widget)}}</span>

View File

@ -64,7 +64,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
'*' '*'
]; ];
scope.titleStyleEditorOptions = { scope.styleEditorOptions = {
useWrapMode: true, useWrapMode: true,
mode: 'json', mode: 'json',
advanced: { advanced: {
@ -106,6 +106,9 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
scope.backgroundColor = config.backgroundColor; scope.backgroundColor = config.backgroundColor;
scope.color = config.color; scope.color = config.color;
scope.padding = config.padding; scope.padding = config.padding;
scope.margin = config.margin;
scope.widgetStyle =
angular.toJson(angular.isDefined(config.widgetStyle) ? config.widgetStyle : {}, true);
scope.titleStyle = scope.titleStyle =
angular.toJson(angular.isDefined(config.titleStyle) ? config.titleStyle : { angular.toJson(angular.isDefined(config.titleStyle) ? config.titleStyle : {
fontSize: '16px', fontSize: '16px',
@ -204,6 +207,12 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
valid = config && config.datasources && config.datasources.length > 0; valid = config && config.datasources && config.datasources.length > 0;
ngModelCtrl.$setValidity('datasources', valid); ngModelCtrl.$setValidity('datasources', valid);
} }
try {
angular.fromJson(scope.widgetStyle);
ngModelCtrl.$setValidity('widgetStyle', true);
} catch (e) {
ngModelCtrl.$setValidity('widgetStyle', false);
}
try { try {
angular.fromJson(scope.titleStyle); angular.fromJson(scope.titleStyle);
ngModelCtrl.$setValidity('titleStyle', true); ngModelCtrl.$setValidity('titleStyle', true);
@ -215,7 +224,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
}; };
scope.$watch('title + showTitle + dropShadow + enableFullscreen + backgroundColor + color + ' + 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 () { 'alarmSearchStatus + alarmsPollingInterval + showLegend', function () {
if (ngModelCtrl.$viewValue) { if (ngModelCtrl.$viewValue) {
var value = ngModelCtrl.$viewValue; var value = ngModelCtrl.$viewValue;
@ -228,6 +237,12 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
config.backgroundColor = scope.backgroundColor; config.backgroundColor = scope.backgroundColor;
config.color = scope.color; config.color = scope.color;
config.padding = scope.padding; config.padding = scope.padding;
config.margin = scope.margin;
try {
config.widgetStyle = angular.fromJson(scope.widgetStyle);
} catch (e) {
config.widgetStyle = {};
}
try { try {
config.titleStyle = angular.fromJson(scope.titleStyle); config.titleStyle = angular.fromJson(scope.titleStyle);
} catch (e) { } catch (e) {

View File

@ -179,7 +179,7 @@
</md-input-container> </md-input-container>
<div flex ng-show="showTitle"> <div flex ng-show="showTitle">
<label translate>widget-config.title-style</label> <label translate>widget-config.title-style</label>
<div ui-ace="titleStyleEditorOptions" ng-model="titleStyle" ng-style="{ minHeight: '100px' }"> <div ui-ace="styleEditorOptions" ng-model="titleStyle" ng-style="{ minHeight: '100px' }">
</div> </div>
</div> </div>
</div> </div>
@ -199,6 +199,11 @@
ng-model="enableFullscreen">{{ 'widget-config.enable-fullscreen' | translate }} ng-model="enableFullscreen">{{ 'widget-config.enable-fullscreen' | translate }}
</md-checkbox> </md-checkbox>
</div> </div>
<div flex>
<label translate>widget-config.widget-style</label>
<div ui-ace="styleEditorOptions" ng-model="widgetStyle" ng-style="{ minHeight: '100px' }">
</div>
</div>
</div> </div>
<div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center"> <div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
<div flex <div flex
@ -227,6 +232,10 @@
<label translate>widget-config.padding</label> <label translate>widget-config.padding</label>
<input ng-model="padding"> <input ng-model="padding">
</md-input-container> </md-input-container>
<md-input-container flex>
<label translate>widget-config.margin</label>
<input ng-model="margin">
</md-input-container>
</div> </div>
<div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center"> <div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
<md-input-container flex> <md-input-container flex>

View File

@ -69,6 +69,8 @@ export default function DashboardSettingsController($scope, $mdDialog, statesCon
vm.gridSettings.columns = vm.gridSettings.columns || 24; vm.gridSettings.columns = vm.gridSettings.columns || 24;
vm.gridSettings.margins = vm.gridSettings.margins || [10, 10]; vm.gridSettings.margins = vm.gridSettings.margins || [10, 10];
vm.gridSettings.autoFillHeight = angular.isDefined(vm.gridSettings.autoFillHeight) ? vm.gridSettings.autoFillHeight : false; 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.hMargin = vm.gridSettings.margins[0];
vm.vMargin = vm.gridSettings.margins[1]; vm.vMargin = vm.gridSettings.margins[1];
vm.gridSettings.backgroundSizeMode = vm.gridSettings.backgroundSizeMode || '100%'; vm.gridSettings.backgroundSizeMode = vm.gridSettings.backgroundSizeMode || '100%';

View File

@ -171,6 +171,22 @@
<md-option value="auto">Original size</md-option> <md-option value="auto">Original size</md-option>
</md-select> </md-select>
</md-input-container> </md-input-container>
<small translate>dashboard.mobile-layout</small>
<div flex layout="row" layout-align="start center">
<md-checkbox flex aria-label="{{ 'dashboard.autofill-height' | translate }}"
ng-model="vm.gridSettings.mobileAutoFillHeight">{{ 'dashboard.autofill-height' | translate }}
</md-checkbox>
<md-input-container flex class="md-block">
<label translate>dashboard.mobile-row-height</label>
<input ng-required="vm.gridSettings" type="number" step="any" name="mobileRowHeight" ng-model="vm.gridSettings.mobileRowHeight"
min="5" max="200" />
<div ng-messages="theForm.mobileRowHeight.$error" multiple md-auto-hide="false">
<div ng-message="required" translate>dashboard.mobile-row-height-required</div>
<div ng-message="min" translate>dashboard.min-mobile-row-height-message</div>
<div ng-message="max" translate>dashboard.max-mobile-row-height-message</div>
</div>
</md-input-container>
</div>
</div> </div>
</fieldset> </fieldset>
</div> </div>

View File

@ -50,6 +50,8 @@
dashboard-timewindow="vm.dashboardCtx.dashboardTimewindow" dashboard-timewindow="vm.dashboardCtx.dashboardTimewindow"
is-edit="vm.isEdit" is-edit="vm.isEdit"
autofill-height="vm.layoutCtx.gridSettings.autoFillHeight && !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="vm.isMobile"
is-mobile-disabled="vm.widgetEditMode" is-mobile-disabled="vm.widgetEditMode"
is-edit-action-enabled="vm.isEdit" is-edit-action-enabled="vm.isEdit"

View File

@ -432,6 +432,11 @@ export default angular.module('thingsboard.locale', [])
"min-vertical-margin-message": "Only 0 is allowed as minimum vertical margin value.", "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.", "max-vertical-margin-message": "Only 50 is allowed as maximum vertical margin value.",
"autofill-height": "Auto fill layout height", "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", "display-title": "Display dashboard title",
"toolbar-always-open": "Keep toolbar opened", "toolbar-always-open": "Keep toolbar opened",
"title-color": "Title color", "title-color": "Title color",
@ -1154,6 +1159,8 @@ export default angular.module('thingsboard.locale', [])
"background-color": "Background color", "background-color": "Background color",
"text-color": "Text color", "text-color": "Text color",
"padding": "Padding", "padding": "Padding",
"margin": "Margin",
"widget-style": "Widget style",
"title-style": "Title style", "title-style": "Title style",
"mobile-mode-settings": "Mobile mode settings", "mobile-mode-settings": "Mobile mode settings",
"order": "Order", "order": "Order",