UI: widget config improvements.

This commit is contained in:
Igor Kulikov 2017-02-13 21:22:15 +02:00
parent b7cbbcb28a
commit 9205f5383b
9 changed files with 344 additions and 192 deletions

File diff suppressed because one or more lines are too long

View File

@ -162,7 +162,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
window = datasourceSubscription.subscriptionTimewindow.fixedWindow.endTimeMs -
datasourceSubscription.subscriptionTimewindow.fixedWindow.startTimeMs;
}
frequency = window / 1000 * 5;
frequency = window / 1000 * 20;
} else if (datasourceSubscription.type === types.widgetType.latest.value) {
dataGenFunction = generateLatest;
frequency = 1000;

View File

@ -16,7 +16,7 @@
import './dashboard.scss';
import $ from 'jquery';
import gridster from 'angular-gridster';
import angularGridster from 'angular-gridster';
import thingsboardTypes from '../common/types.constant';
import thingsboardApiWidget from '../api/widget.service';
import thingsboardWidget from './widget.directive';
@ -40,7 +40,7 @@ export default angular.module('thingsboard.directives.dashboard', [thingsboardTy
thingsboardTimewindow,
thingsboardEvents,
thingsboardMousepointMenu,
gridster.name])
angularGridster.name])
.directive('tbDashboard', Dashboard)
.name;
@ -89,10 +89,12 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
var gridsterParent = null;
var gridsterElement = null;
var gridster = null;
var vm = this;
vm.gridster = null;
vm.isMobileDisabled = angular.isDefined(vm.isMobileDisabled) ? vm.isMobileDisabled : false;
vm.dashboardLoading = true;
@ -122,6 +124,25 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
saveGridItemCalculatedHeightInMobile: true
};
vm.widgetItemMap = {
sizeX: 'widget.sizeX',
sizeY: 'widget.sizeY',
row: 'widget.row',
col: 'widget.col',
minSizeY: 'widget.minSizeY',
maxSizeY: 'widget.maxSizeY'
};
vm.mobileWidgetItemMap = {
sizeX: 'widget.sizeX',
sizeY: 'vm.widgetSizeY(widget)',
row: 'widget.row',
col: 'widget.col',
minSizeY: 'widget.minSizeY',
maxSizeY: 'widget.maxSizeY'
};
vm.currentWidgetItemMap = vm.gridsterOpts.isMobile ? vm.mobileWidgetItemMap : vm.widgetItemMap;
vm.isWidgetExpanded = false;
vm.isHighlighted = isHighlighted;
vm.isNotHighlighted = isNotHighlighted;
@ -135,11 +156,14 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
vm.widgetMouseMove = widgetMouseMove;
vm.widgetMouseUp = widgetMouseUp;
vm.widgetSizeY = widgetSizeY;
vm.widgetColor = widgetColor;
vm.widgetBackgroundColor = widgetBackgroundColor;
vm.widgetPadding = widgetPadding;
vm.showWidgetTitle = showWidgetTitle;
vm.widgetTitleStyle = widgetTitleStyle;
vm.dropWidgetShadow = dropWidgetShadow;
vm.enableWidgetFullscreen = enableWidgetFullscreen;
vm.hasTimewindow = hasTimewindow;
vm.editWidget = editWidget;
vm.exportWidget = exportWidget;
@ -185,8 +209,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
$scope.$watch('vm.columns', function () {
vm.gridsterOpts.columns = vm.columns ? vm.columns : 24;
if (gridster) {
gridster.columns = vm.columns;
if (vm.gridster) {
vm.gridster.columns = vm.columns;
updateGridsterParams();
}
//TODO: widgets visibility
@ -195,8 +219,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
$scope.$watch('vm.margins', function () {
vm.gridsterOpts.margins = vm.margins ? vm.margins : [10, 10];
if (gridster) {
gridster.margins = vm.margins;
if (vm.gridster) {
vm.gridster.margins = vm.margins;
updateGridsterParams();
}
//TODO: widgets visibility
@ -215,7 +239,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
$scope.$on('gridster-resized', function (event, sizes, theGridster) {
if (checkIsLocalGridsterElement(theGridster)) {
gridster = theGridster;
vm.gridster = theGridster;
//TODO: widgets visibility
//updateVisibleRect(false, true);
}
@ -223,12 +247,13 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
$scope.$on('gridster-mobile-changed', function (event, theGridster) {
if (checkIsLocalGridsterElement(theGridster)) {
gridster = theGridster;
if (gridster.isMobile) {
vm.gridster = theGridster;
if (vm.gridster.isMobile) {
vm.gridsterOpts.rowHeight = 70;
} else {
vm.gridsterOpts.rowHeight = 'match';
}
vm.currentWidgetItemMap = vm.gridster.isMobile ? vm.mobileWidgetItemMap : vm.widgetItemMap;
//TODO: widgets visibility
/*$timeout(function () {
updateVisibleRect(true);
@ -238,7 +263,19 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
$scope.$on('widgetPositionChanged', function () {
vm.widgets.sort(function (widget1, widget2) {
var res = widget1.row - widget2.row;
var row1;
var row2;
if (angular.isDefined(widget1.config.mobileOrder)) {
row1 = widget1.config.mobileOrder;
} else {
row1 = widget1.row;
}
if (angular.isDefined(widget2.config.mobileOrder)) {
row2 = widget2.config.mobileOrder;
} else {
row2 = widget2.row;
}
var res = row1 - row2;
if (res === 0) {
res = widget1.col - widget2.col;
}
@ -269,20 +306,20 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
}
function updateGridsterParams() {
if (gridster) {
if (gridster.colWidth === 'auto') {
gridster.curColWidth = (gridster.curWidth + (gridster.outerMargin ? -gridster.margins[1] : gridster.margins[1])) / gridster.columns;
if (vm.gridster) {
if (vm.gridster.colWidth === 'auto') {
vm.gridster.curColWidth = (vm.gridster.curWidth + (vm.gridster.outerMargin ? -vm.gridster.margins[1] : vm.gridster.margins[1])) / vm.gridster.columns;
} else {
gridster.curColWidth = gridster.colWidth;
vm.gridster.curColWidth = vm.gridster.colWidth;
}
gridster.curRowHeight = gridster.rowHeight;
if (angular.isString(gridster.rowHeight)) {
if (gridster.rowHeight === 'match') {
gridster.curRowHeight = Math.round(gridster.curColWidth);
} else if (gridster.rowHeight.indexOf('*') !== -1) {
gridster.curRowHeight = Math.round(gridster.curColWidth * gridster.rowHeight.replace('*', '').replace(' ', ''));
} else if (gridster.rowHeight.indexOf('/') !== -1) {
gridster.curRowHeight = Math.round(gridster.curColWidth / gridster.rowHeight.replace('/', '').replace(' ', ''));
vm.gridster.curRowHeight = vm.gridster.rowHeight;
if (angular.isString(vm.gridster.rowHeight)) {
if (vm.gridster.rowHeight === 'match') {
vm.gridster.curRowHeight = Math.round(vm.gridster.curColWidth);
} else if (vm.gridster.rowHeight.indexOf('*') !== -1) {
vm.gridster.curRowHeight = Math.round(vm.gridster.curColWidth * vm.gridster.rowHeight.replace('*', '').replace(' ', ''));
} else if (vm.gridster.rowHeight.indexOf('/') !== -1) {
vm.gridster.curRowHeight = Math.round(vm.gridster.curColWidth / vm.gridster.rowHeight.replace('/', '').replace(' ', ''));
}
}
}
@ -290,8 +327,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
//TODO: widgets visibility
/*function updateVisibleRect (force, containerResized) {
if (gridster) {
var position = $(gridster.$element).position()
if (vm.gridster) {
var position = $(vm.gridster.$element).position()
if (position) {
var viewportWidth = gridsterParent.width();
var viewportHeight = gridsterParent.height();
@ -301,14 +338,14 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
var right = left + viewportWidth;
var newVisibleRect = {
top: gridster.pixelsToRows(top),
top: vm.gridster.pixelsToRows(top),
topPx: top,
bottom: gridster.pixelsToRows(bottom),
bottom: vm.gridster.pixelsToRows(bottom),
bottomPx: bottom,
left: gridster.pixelsToColumns(left),
right: gridster.pixelsToColumns(right),
isMobile: gridster.isMobile,
curRowHeight: gridster.curRowHeight,
left: vm.gridster.pixelsToColumns(left),
right: vm.gridster.pixelsToColumns(right),
isMobile: vm.gridster.isMobile,
curRowHeight: vm.gridster.curRowHeight,
containerResized: containerResized
};
@ -330,7 +367,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
}*/
function checkIsLocalGridsterElement (gridster) {
return gridsterElement[0] == gridster.$element[0];
return gridsterElement[0] === gridster.$element[0];
}
function resetWidgetClick () {
@ -406,9 +443,9 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
var offset = gridsterParent.offset();
var x = event.pageX - offset.left + gridsterParent.scrollLeft();
var y = event.pageY - offset.top + gridsterParent.scrollTop();
if (gridster) {
pos.row = gridster.pixelsToRows(y);
pos.column = gridster.pixelsToColumns(x);
if (vm.gridster) {
pos.row = vm.gridster.pixelsToRows(y);
pos.column = vm.gridster.pixelsToColumns(x);
}
return pos;
}
@ -446,7 +483,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
function highlightWidget(widget, delay) {
highlightedMode = true;
highlightedWidget = widget;
var item = $('.gridster-item', gridster.$element)[vm.widgets.indexOf(widget)];
var item = $('.gridster-item', vm.gridster.$element)[vm.widgets.indexOf(widget)];
if (item) {
var height = $(item).outerHeight(true);
var rectHeight = gridsterParent.height();
@ -463,7 +500,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
function selectWidget(widget, delay) {
selectedWidget = widget;
var item = $('.gridster-item', gridster.$element)[vm.widgets.indexOf(widget)];
var item = $('.gridster-item', vm.gridster.$element)[vm.widgets.indexOf(widget)];
if (item) {
var height = $(item).outerHeight(true);
var rectHeight = gridsterParent.height();
@ -496,6 +533,14 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
return highlightedMode && highlightedWidget != widget;
}
function widgetSizeY(widget) {
if (vm.gridster && vm.gridster.isMobile && widget.config.mobileHeight) {
return widget.config.mobileHeight;
} else {
return widget.sizeY;
}
}
function widgetColor(widget) {
if (widget.config.color) {
return widget.config.color;
@ -528,6 +573,14 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
}
}
function widgetTitleStyle(widget) {
if (angular.isDefined(widget.config.titleStyle)) {
return widget.config.titleStyle;
} else {
return {};
}
}
function dropWidgetShadow(widget) {
if (angular.isDefined(widget.config.dropShadow)) {
return widget.config.dropShadow;
@ -536,6 +589,14 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
}
}
function enableWidgetFullscreen(widget) {
if (angular.isDefined(widget.config.enableFullscreen)) {
return widget.config.enableFullscreen;
} else {
return true;
}
}
function hasTimewindow(widget) {
return widget.type === types.widgetType.timeseries.value;
}

View File

@ -25,10 +25,13 @@
<div id="gridster-child" gridster="vm.gridsterOpts">
<ul>
<!-- ng-click="widgetClicked($event, widget)" -->
<li gridster-item="widget" ng-repeat="widget in vm.widgets">
<li gridster-item="vm.currentWidgetItemMap" ng-repeat="widget in vm.widgets">
<md-menu md-position-mode="target target" tb-mousepoint-menu>
<div tb-expand-fullscreen
expand-button-id="expand-button" on-fullscreen-changed="vm.onWidgetFullscreenChanged(expanded, widget)" layout="column" class="tb-widget"
expand-button-id="expand-button"
on-fullscreen-changed="vm.onWidgetFullscreenChanged(expanded, widget)"
layout="column"
class="tb-widget"
ng-class="{'tb-highlighted': vm.isHighlighted(widget),
'tb-not-highlighted': vm.isNotHighlighted(widget),
'md-whiteframe-4dp': vm.dropWidgetShadow(widget)}"
@ -42,12 +45,12 @@
backgroundColor: vm.widgetBackgroundColor(widget),
padding: vm.widgetPadding(widget)}">
<div class="tb-widget-title" layout="column" ng-show="vm.showWidgetTitle(widget) || vm.hasTimewindow(widget)">
<span ng-show="vm.showWidgetTitle(widget)" class="md-subhead">{{widget.config.title}}</span>
<span ng-show="vm.showWidgetTitle(widget)" ng-style="vm.widgetTitleStyle(widget)" class="md-subhead">{{widget.config.title}}</span>
<tb-timewindow ng-if="vm.hasTimewindow(widget)" ng-model="widget.config.timewindow"></tb-timewindow>
</div>
<div class="tb-widget-actions" layout="row" layout-align="start center">
<md-button id="expand-button"
ng-show="!vm.isEdit"
ng-show="!vm.isEdit && vm.enableWidgetFullscreen(widget)"
aria-label="{{ 'fullscreen.fullscreen' | translate }}"
class="md-icon-button md-primary"></md-button>
<md-button ng-show="vm.isEditActionEnabled && !vm.isWidgetExpanded"

View File

@ -20,6 +20,7 @@ import thingsboardDeviceAliasSelect from './device-alias-select.directive';
import thingsboardDatasource from './datasource.directive';
import thingsboardTimewindow from './timewindow.directive';
import thingsboardJsonForm from "./json-form.directive";
import 'angular-ui-ace';
/* eslint-disable import/no-unresolved, import/default */
@ -34,7 +35,8 @@ export default angular.module('thingsboard.directives.widgetConfig', [thingsboar
thingsboardJsonForm,
thingsboardDeviceAliasSelect,
thingsboardDatasource,
thingsboardTimewindow])
thingsboardTimewindow,
'ui.ace'])
.directive('tbWidgetConfig', WidgetConfig)
.name;
@ -58,6 +60,16 @@ function WidgetConfig($compile, $templateCache, $rootScope, types, utils) {
'*'
];
scope.titleStyleEditorOptions = {
useWrapMode: true,
mode: 'json',
advanced: {
enableSnippets: true,
enableBasicAutocompletion: true,
enableLiveAutocompletion: true
}
};
if (angular.isUndefined(scope.forceExpandDatasources)) {
scope.forceExpandDatasources = false;
}
@ -75,9 +87,17 @@ function WidgetConfig($compile, $templateCache, $rootScope, types, utils) {
scope.title = ngModelCtrl.$viewValue.title;
scope.showTitle = ngModelCtrl.$viewValue.showTitle;
scope.dropShadow = angular.isDefined(ngModelCtrl.$viewValue.dropShadow) ? ngModelCtrl.$viewValue.dropShadow : true;
scope.enableFullscreen = angular.isDefined(ngModelCtrl.$viewValue.enableFullscreen) ? ngModelCtrl.$viewValue.enableFullscreen : true;
scope.backgroundColor = ngModelCtrl.$viewValue.backgroundColor;
scope.color = ngModelCtrl.$viewValue.color;
scope.padding = ngModelCtrl.$viewValue.padding;
scope.titleStyle =
angular.toJson(angular.isDefined(ngModelCtrl.$viewValue.titleStyle) ? ngModelCtrl.$viewValue.titleStyle : {
fontSize: '16px',
fontWeight: 400
}, true);
scope.mobileOrder = ngModelCtrl.$viewValue.mobileOrder;
scope.mobileHeight = ngModelCtrl.$viewValue.mobileHeight;
scope.timewindow = ngModelCtrl.$viewValue.timewindow;
if (scope.widgetType !== types.widgetType.rpc.value && scope.widgetType !== types.widgetType.static.value) {
if (scope.datasources) {
@ -145,20 +165,35 @@ function WidgetConfig($compile, $templateCache, $rootScope, types, utils) {
valid = value && value.datasources && value.datasources.length > 0;
ngModelCtrl.$setValidity('datasources', valid);
}
try {
angular.fromJson(scope.titleStyle);
ngModelCtrl.$setValidity('titleStyle', true);
} catch (e) {
ngModelCtrl.$setValidity('titleStyle', false);
}
}
};
scope.$watch('title + showTitle + dropShadow + backgroundColor + color + padding + intervalSec', function () {
scope.$watch('title + showTitle + dropShadow + enableFullscreen + backgroundColor + color + padding + titleStyle + mobileOrder + mobileHeight + intervalSec', function () {
if (ngModelCtrl.$viewValue) {
var value = ngModelCtrl.$viewValue;
value.title = scope.title;
value.showTitle = scope.showTitle;
value.dropShadow = scope.dropShadow;
value.enableFullscreen = scope.enableFullscreen;
value.backgroundColor = scope.backgroundColor;
value.color = scope.color;
value.padding = scope.padding;
try {
value.titleStyle = angular.fromJson(scope.titleStyle);
} catch (e) {
value.titleStyle = {};
}
value.mobileOrder = angular.isNumber(scope.mobileOrder) ? scope.mobileOrder : undefined;
value.mobileHeight = scope.mobileHeight;
value.intervalSec = scope.intervalSec;
ngModelCtrl.$setViewValue(value);
scope.updateValidity();
}
});

View File

@ -20,10 +20,17 @@
<md-tab label="{{ 'widget-config.settings' | translate }}">
<div id="settings-tab">
<md-content class="md-padding" layout="column">
<md-input-container class="md-block">
<div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
<md-input-container flex class="md-block">
<label translate>widget-config.title</label>
<input name="title" ng-model="title">
</md-input-container>
<div flex ng-show="showTitle">
<label translate>widget-config.title-style</label>
<div ui-ace="titleStyleEditorOptions" ng-model="titleStyle" ng-style="{ minHeight: '100px' }">
</div>
</div>
</div>
<span translate>widget-config.general-settings</span>
<div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
<div layout="row" layout-padding>
@ -36,6 +43,13 @@
ng-model="dropShadow">{{ 'widget-config.drop-shadow' | translate }}
</md-checkbox>
</div>
<div layout="row" layout-padding>
<md-checkbox flex aria-label="{{ 'widget-config.enable-fullscreen' | translate }}"
ng-model="enableFullscreen">{{ 'widget-config.enable-fullscreen' | translate }}
</md-checkbox>
</div>
</div>
<div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
<div flex
md-color-picker
ng-model="backgroundColor"
@ -63,6 +77,17 @@
<input ng-model="padding">
</md-input-container>
</div>
<span translate>widget-config.mobile-mode-settings</span>
<div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
<md-input-container flex>
<label translate>widget-config.order</label>
<input ng-model="mobileOrder" type="number" step="1" ng-pattern="/^-?[0-9]+$/">
</md-input-container>
<md-input-container flex>
<label translate>widget-config.height</label>
<input ng-model="mobileHeight" type="number">
</md-input-container>
</div>
<div ng-show="widgetType === types.widgetType.timeseries.value" layout="row"
layout-align="center center">
<span translate style="padding-right: 8px;">widget-config.timewindow</span>

View File

@ -56,6 +56,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
var inited = false;
// var gridsterItemElement;
var gridsterItem;
var timer;
var init = fns.init || function () {
@ -172,6 +174,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
function gridsterItemInitialized(item) {
if (item) {
gridsterItem = item;
// gridsterItemElement = $(item.$element);
//updateVisibility();
onRedraw();
@ -226,7 +229,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
});
$scope.$watch(function () {
return widget.row + ',' + widget.col;
return widget.row + ',' + widget.col + ',' + widget.config.mobileOrder;
}, function () {
updateBounds();
$scope.$emit("widgetPositionChanged", widget);
@ -377,7 +380,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
if (width > 20 && height > 20) {
if (!inited) {
init(containerElement, widget.config.settings, widget.config.datasources, data, $scope, controlApi, timewindowFunctions);
init(containerElement, widget.config.settings, widget.config.datasources, data, $scope, controlApi, timewindowFunctions, gridsterItem);
inited = true;
}
if (widget.type === types.widgetType.timeseries.value) {
@ -393,7 +396,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
timeWindow.minTime = subscriptionTimewindow.fixedWindow.startTimeMs;
}
}
redraw(containerElement, width, height, data, timeWindow, sizeChanged, $scope, dataUpdate, tickUpdate);
redraw(containerElement, width, height, data, timeWindow, sizeChanged, $scope, dataUpdate, tickUpdate, gridsterItem);
}
}, delay, false);
}

View File

@ -577,10 +577,12 @@ export default class TbDigitalGauge {
var tvPair = cellData.data[cellData.data.length -
1];
var value = tvPair[1];
if (this.gauge.config.value !== value) {
this.gauge.refresh(value);
}
}
}
}
}
/* eslint-enable angular/angularelement */

View File

@ -665,9 +665,14 @@
"general-settings": "General settings",
"display-title": "Display title",
"drop-shadow": "Drop shadow",
"enable-fullscreen": "Enable fullscreen",
"background-color": "Background color",
"text-color": "Text color",
"padding": "Padding",
"title-style": "Title style",
"mobile-mode-settings": "Mobile mode settings",
"order": "Order",
"height": "Height",
"timewindow": "Timewindow",
"datasources": "Datasources",
"datasource-type": "Type",