diff --git a/ui/src/app/asset/assets.tpl.html b/ui/src/app/asset/assets.tpl.html index 8370d3dbac..c4c0267993 100644 --- a/ui/src/app/asset/assets.tpl.html +++ b/ui/src/app/asset/assets.tpl.html @@ -31,7 +31,7 @@ on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, isPublic)" on-delete-asset="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"> - + - + - + - + - + diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index ad09c820ed..81ce0fa4f9 100644 --- a/ui/src/app/common/types.constant.js +++ b/ui/src/app/common/types.constant.js @@ -394,7 +394,6 @@ export default angular.module('thingsboard.types', []) cards: "cards" }, translate: { - dashboardStatePrefix: "dashboardState.state.", customTranslationsPrefix: "custom." } } diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js index ebded08f63..a3433c255f 100644 --- a/ui/src/app/common/utils.service.js +++ b/ui/src/app/common/utils.service.js @@ -135,7 +135,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, types) { isLocalUrl: isLocalUrl, validateDatasources: validateDatasources, createKey: createKey, - createLabelFromDatasource: createLabelFromDatasource + createLabelFromDatasource: createLabelFromDatasource, + insertVariable: insertVariable } return service; @@ -407,4 +408,18 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, types) { return label; } + function insertVariable(pattern, name, value) { + var result = angular.copy(pattern); + var match = varsRegex.exec(pattern); + while (match !== null) { + var variable = match[0]; + var variableName = match[1]; + if (variableName === name) { + result = result.split(variable).join(value); + } + match = varsRegex.exec(pattern); + } + return result; + } + } diff --git a/ui/src/app/components/dashboard.directive.js b/ui/src/app/components/dashboard.directive.js index f21df59927..1c2c6543af 100644 --- a/ui/src/app/components/dashboard.directive.js +++ b/ui/src/app/components/dashboard.directive.js @@ -58,6 +58,7 @@ function Dashboard() { columns: '=', margins: '=', isEdit: '=', + autofillHeight: '=', isMobile: '=', isMobileDisabled: '=?', isEditActionEnabled: '=', @@ -102,6 +103,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ vm.isMobileDisabled = angular.isDefined(vm.isMobileDisabled) ? vm.isMobileDisabled : false; + vm.isMobileSize = false; + if (!('dashboardTimewindow' in vm)) { vm.dashboardTimewindow = timeService.defaultTimewindow(); } @@ -178,6 +181,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ vm.widgetPadding = widgetPadding; vm.showWidgetTitle = showWidgetTitle; vm.showWidgetTitlePanel = showWidgetTitlePanel; + vm.showWidgetActions = showWidgetActions; vm.widgetTitleStyle = widgetTitleStyle; vm.widgetTitle = widgetTitle; vm.widgetActions = widgetActions; @@ -271,12 +275,15 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ function updateMobileOpts() { var isMobileDisabled = vm.isMobileDisabled === true; - var isMobile = vm.isMobile === true && !isMobileDisabled; + var isMobile = vm.isMobile === true && !isMobileDisabled || vm.autofillHeight; var mobileBreakPoint = isMobileDisabled ? 0 : (isMobile ? 20000 : 960); + if (!isMobile && !isMobileDisabled) { isMobile = !$mdMedia('gt-sm'); } - var rowHeight = isMobile ? 70 : 'match'; + + var rowHeight = detectRowSize(isMobile); + if (vm.gridsterOpts.isMobile != isMobile) { vm.gridsterOpts.isMobile = isMobile; vm.gridsterOpts.mobileModeEnabled = isMobile; @@ -287,6 +294,17 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ if (vm.gridsterOpts.rowHeight != rowHeight) { vm.gridsterOpts.rowHeight = rowHeight; } + + vm.isMobileSize = checkIsMobileSize(); + } + + function checkIsMobileSize() { + var isMobileDisabled = vm.isMobileDisabled === true; + var isMobileSize = vm.isMobile === true && !isMobileDisabled; + if (!isMobileSize && !isMobileDisabled) { + isMobileSize = !$mdMedia('gt-sm'); + } + return isMobileSize; } $scope.$watch(function() { return $mdMedia('gt-sm'); }, function() { @@ -297,6 +315,34 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ updateMobileOpts(); }); + $scope.$watch('vm.autofillHeight', function () { + if (vm.autofillHeight) { + //if (gridsterParent.height()) { + // updateMobileOpts(); + //} else { + if ($scope.parentHeighWatcher) { + $scope.parentHeighWatcher(); + } + if (gridsterParent.height()) { + updateMobileOpts(); + } + $scope.parentHeighWatcher = $scope.$watch(function() { return gridsterParent.height(); }, + function(newHeight) { + if (newHeight) { + updateMobileOpts(); + } + } + ); + } else { + if ($scope.parentHeighWatcher) { + $scope.parentHeighWatcher(); + $scope.parentHeighWatcher = null; + } + + updateMobileOpts(); + } + }); + $scope.$watch('vm.isMobileDisabled', function () { updateMobileOpts(); }); @@ -333,6 +379,12 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ $scope.$broadcast('toggleDashboardEditMode', vm.isEdit); }); + $scope.$watch('vm.isMobileSize', function (newVal, prevVal) { + if (!angular.equals(newVal, prevVal)) { + $scope.$broadcast('mobileModeChanged', vm.isMobileSize); + } + }); + $scope.$on('gridster-resized', function (event, sizes, theGridster) { if (checkIsLocalGridsterElement(theGridster)) { vm.gridster = theGridster; @@ -345,13 +397,12 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ $scope.$on('gridster-mobile-changed', function (event, theGridster) { if (checkIsLocalGridsterElement(theGridster)) { vm.gridster = theGridster; - var rowHeight = vm.gridster.isMobile ? 70 : 'match'; + var rowHeight = detectRowSize(vm.gridster.isMobile); if (vm.gridsterOpts.rowHeight != rowHeight) { vm.gridsterOpts.rowHeight = rowHeight; updateGridsterParams(); } - - $scope.$broadcast('mobileModeChanged', vm.gridster.isMobile); + vm.isMobileSize = checkIsMobileSize(); //TODO: widgets visibility /*$timeout(function () { @@ -360,6 +411,21 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ } }); + function detectRowSize(isMobile) { + var rowHeight = isMobile ? 70 : 'match'; + if (vm.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; + } + rowHeight = (viewportHeight - (vm.gridsterOpts.margins[1])) / totalRows; + } + return rowHeight; + } + function widgetOrder(widget) { var order; if (vm.widgetLayouts && vm.widgetLayouts[widget.id]) { @@ -650,7 +716,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ } function widgetSizeY(widget) { - if (vm.gridsterOpts.isMobile) { + if (vm.gridsterOpts.isMobile && !vm.autofillHeight) { var mobileHeight; if (vm.widgetLayouts && vm.widgetLayouts[widget.id]) { mobileHeight = vm.widgetLayouts[widget.id].mobileHeight; @@ -673,7 +739,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ } function setWidgetSizeY(widget, sizeY) { - if (!vm.gridsterOpts.isMobile) { + if (!vm.gridsterOpts.isMobile && !vm.autofillHeight) { if (vm.widgetLayouts && vm.widgetLayouts[widget.id]) { vm.widgetLayouts[widget.id].sizeY = sizeY; } else { @@ -759,6 +825,15 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $ } } + function showWidgetActions(widget) { + var ctx = widgetContext(widget); + if (ctx && ctx.hideTitlePanel) { + return false; + } else { + return true; + } + } + function widgetTitleStyle(widget) { if (angular.isDefined(widget.config.titleStyle)) { return widget.config.titleStyle; diff --git a/ui/src/app/components/dashboard.tpl.html b/ui/src/app/components/dashboard.tpl.html index da069b0ad3..478e2e562a 100644 --- a/ui/src/app/components/dashboard.tpl.html +++ b/ui/src/app/components/dashboard.tpl.html @@ -23,7 +23,7 @@ -
+
  • @@ -50,7 +50,7 @@ {{vm.widgetTitle(widget)}}
-
+
diff --git a/ui/src/app/components/grid.directive.js b/ui/src/app/components/grid.directive.js index 296456a9c5..422501bb0f 100644 --- a/ui/src/app/components/grid.directive.js +++ b/ui/src/app/components/grid.directive.js @@ -124,7 +124,7 @@ function Grid() { } /*@ngInject*/ -function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $translate, $mdMedia, $templateCache) { +function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $translate, $mdMedia, $templateCache, $window) { var vm = this; @@ -155,6 +155,7 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra vm.refreshList = refreshList; vm.saveItem = saveItem; vm.toggleItemSelection = toggleItemSelection; + vm.triggerResize = triggerResize; $scope.$watch(function () { return $mdMedia('xs') || $mdMedia('sm'); @@ -600,6 +601,11 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra } } + function triggerResize() { + var w = angular.element($window); + w.triggerHandler('resize'); + } + function moveToTop() { moveToIndex(0, true); } diff --git a/ui/src/app/components/widget.controller.js b/ui/src/app/components/widget.controller.js index 63ad0ed89c..8d91b2d750 100644 --- a/ui/src/app/components/widget.controller.js +++ b/ui/src/app/components/widget.controller.js @@ -21,7 +21,7 @@ import Subscription from '../api/subscription'; /*@ngInject*/ export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService, - datasourceService, alarmService, entityService, deviceService, visibleRect, isEdit, stDiff, dashboardTimewindow, + datasourceService, alarmService, entityService, deviceService, visibleRect, isEdit, isMobile, stDiff, dashboardTimewindow, dashboardTimewindowApi, widget, aliasController, stateController, widgetInfo, widgetType) { var vm = this; @@ -50,7 +50,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q height: 0, hideTitlePanel: false, isEdit: isEdit, - isMobile: false, + isMobile: isMobile, widgetConfig: widget.config, settings: widget.config.settings, units: widget.config.units || '', @@ -618,7 +618,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q function gridsterItemInitialized(item) { if (item && item.gridster) { - widgetContext.isMobile = item.gridster.isMobile; gridsterItemInited = true; onInit(); // gridsterItemElement = $(item.$element); diff --git a/ui/src/app/customer/customers.tpl.html b/ui/src/app/customer/customers.tpl.html index a6521f066d..da0a2e950b 100644 --- a/ui/src/app/customer/customers.tpl.html +++ b/ui/src/app/customer/customers.tpl.html @@ -31,7 +31,7 @@ on-manage-dashboards="vm.openCustomerDashboards(event, vm.grid.detailsConfig.currentItem)" on-delete-customer="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"> - + - + - + - + - + diff --git a/ui/src/app/dashboard/dashboard-settings.controller.js b/ui/src/app/dashboard/dashboard-settings.controller.js index ac987093e6..c5743c9e78 100644 --- a/ui/src/app/dashboard/dashboard-settings.controller.js +++ b/ui/src/app/dashboard/dashboard-settings.controller.js @@ -68,6 +68,7 @@ export default function DashboardSettingsController($scope, $mdDialog, statesCon vm.gridSettings.color = vm.gridSettings.color || 'rgba(0,0,0,0.870588)'; 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.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 f3ff704c60..427ca1933e 100644 --- a/ui/src/app/dashboard/dashboard-settings.tpl.html +++ b/ui/src/app/dashboard/dashboard-settings.tpl.html @@ -121,6 +121,9 @@
+ {{ 'dashboard.autofill-height' | translate }} +
dashboard.state-name-required
-
dashboard.state-name-exists
- +
dashboard.state-id-required
dashboard.state-id-exists
-
dashboard.invalid-state-id-format
- + - + - + - + - + diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js index f35434c833..b21374600e 100644 --- a/ui/src/app/locale/locale.constant.js +++ b/ui/src/app/locale/locale.constant.js @@ -431,6 +431,7 @@ export default angular.module('thingsboard.locale', []) "vertical-margin-required": "Vertical margin value is required.", "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", "display-title": "Display dashboard title", "toolbar-always-open": "Keep toolbar opened", "title-color": "Title color", @@ -472,11 +473,9 @@ export default angular.module('thingsboard.locale', []) "state": "Dashboard state", "state-name": "Name", "state-name-required": "Dashboard state name is required.", - "state-name-exists": "Dashboard state with the same name is already exists.", "state-id": "State Id", "state-id-required": "Dashboard state id is required.", "state-id-exists": "Dashboard state with the same id is already exists.", - "invalid-state-id-format": "Only alphanumeric characters and underscore are allowed.", "is-root-state": "Root state", "delete-state-title": "Delete dashboard state", "delete-state-text": "Are you sure you want delete dashboard state with name '{{stateName}}'?", @@ -1169,9 +1168,6 @@ export default angular.module('thingsboard.locale', []) "es_ES": "Spanish" }, "custom": { - "alarms": { - "title": "Super ${entityName}" - } } } } diff --git a/ui/src/app/locale/translate-handler.js b/ui/src/app/locale/translate-handler.js index 20af2584cc..7410062a57 100644 --- a/ui/src/app/locale/translate-handler.js +++ b/ui/src/app/locale/translate-handler.js @@ -18,8 +18,7 @@ export default function ThingsboardMissingTranslateHandler($log, types) { return function (translationId) { - if (translationId && !translationId.startsWith(types.translate.dashboardStatePrefix) && - !translationId.startsWith(types.translate.customTranslationsPrefix)) { + if (translationId && !translationId.startsWith(types.translate.customTranslationsPrefix)) { $log.warn('Translation for ' + translationId + ' doesn\'t exist'); } }; diff --git a/ui/src/app/plugin/plugins.tpl.html b/ui/src/app/plugin/plugins.tpl.html index 73b0adc39e..b3563e9945 100644 --- a/ui/src/app/plugin/plugins.tpl.html +++ b/ui/src/app/plugin/plugins.tpl.html @@ -31,7 +31,7 @@ on-export-plugin="vm.exportPlugin(event, vm.grid.detailsConfig.currentItem)" on-delete-plugin="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"> - + - + - + - + - + diff --git a/ui/src/app/rule/rules.tpl.html b/ui/src/app/rule/rules.tpl.html index 336fe63ce9..064728baa9 100644 --- a/ui/src/app/rule/rules.tpl.html +++ b/ui/src/app/rule/rules.tpl.html @@ -31,7 +31,7 @@ on-export-rule="vm.exportRule(event, vm.grid.detailsConfig.currentItem)" on-delete-rule="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"> - + - + - + - + - + diff --git a/ui/src/app/tenant/tenants.tpl.html b/ui/src/app/tenant/tenants.tpl.html index e407291757..94a827f45f 100644 --- a/ui/src/app/tenant/tenants.tpl.html +++ b/ui/src/app/tenant/tenants.tpl.html @@ -29,7 +29,7 @@ on-manage-users="vm.openTenantUsers(event, vm.grid.detailsConfig.currentItem)" on-delete-tenant="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"> - + - + - + - + - + diff --git a/ui/src/app/widget/lib/alarms-table-widget.js b/ui/src/app/widget/lib/alarms-table-widget.js index ebe6b0304f..5a65af20b0 100644 --- a/ui/src/app/widget/lib/alarms-table-widget.js +++ b/ui/src/app/widget/lib/alarms-table-widget.js @@ -210,6 +210,9 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia var cssString = 'table.md-table th.md-column {\n'+ 'color: ' + mdDarkSecondary + ';\n'+ '}\n'+ + 'table.md-table th.md-column.md-checkbox-column md-checkbox:not(.md-checked) .md-icon {\n'+ + 'border-color: ' + mdDarkSecondary + ';\n'+ + '}\n'+ 'table.md-table th.md-column md-icon.md-sort-icon {\n'+ 'color: ' + mdDarkDisabled + ';\n'+ '}\n'+ @@ -220,6 +223,9 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia 'color: ' + mdDark + ';\n'+ 'border-top: 1px '+mdDarkDivider+' solid;\n'+ '}\n'+ + 'table.md-table td.md-cell.md-checkbox-cell md-checkbox:not(.md-checked) .md-icon {\n'+ + 'border-color: ' + mdDarkSecondary + ';\n'+ + '}\n'+ 'table.md-table td.md-cell.md-placeholder {\n'+ 'color: ' + mdDarkDisabled + ';\n'+ '}\n'+ diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss index 5b5f92947f..c47f177430 100644 --- a/ui/src/scss/main.scss +++ b/ui/src/scss/main.scss @@ -324,8 +324,14 @@ pre.tb-highlight { tr { &.md-row:not([disabled]) { outline: none; + &:hover { + background-color: rgba(221, 221, 221, 0.3) !important; + } + &.md-selected { + background-color: rgba(221, 221, 221, 0.5) !important; + } &.tb-current, &.tb-current:hover{ - background-color: #dddddd !important; + background-color: rgba(221, 221, 221, 0.65) !important; } } }