Browse Source

Add widgets copy/past ability

pull/24/head
Igor Kulikov 10 years ago
parent
commit
29c80b3b85
  1. 2
      ui/src/app/app.js
  2. 108
      ui/src/app/components/dashboard.directive.js
  3. 144
      ui/src/app/components/dashboard.tpl.html
  4. 40
      ui/src/app/components/keyboard-shortcut.filter.js
  5. 51
      ui/src/app/components/mousepoint-menu.directive.js
  6. 2
      ui/src/app/components/tb-event-directives.js
  7. 23
      ui/src/app/components/widgets-bundle-select.directive.js
  8. 4
      ui/src/app/dashboard/dashboard-settings.controller.js
  9. 5
      ui/src/app/dashboard/dashboard-settings.tpl.html
  10. 211
      ui/src/app/dashboard/dashboard.controller.js
  11. 8
      ui/src/app/dashboard/dashboard.tpl.html
  12. 2
      ui/src/app/dashboard/index.js
  13. 81
      ui/src/app/device/attribute/add-widget-to-dashboard-dialog.controller.js
  14. 10
      ui/src/app/device/attribute/attribute-table.directive.js
  15. 3
      ui/src/app/device/attribute/attribute-table.tpl.html
  16. 191
      ui/src/app/services/item-buffer.service.js
  17. 7
      ui/src/locale/en_US.json
  18. 3
      ui/src/scss/main.scss

2
ui/src/app/app.js

@ -49,6 +49,7 @@ import thingsboardDialogs from './components/datakey-config-dialog.controller';
import thingsboardMenu from './services/menu.service'; import thingsboardMenu from './services/menu.service';
import thingsboardUtils from './common/utils.service'; import thingsboardUtils from './common/utils.service';
import thingsboardTypes from './common/types.constant'; import thingsboardTypes from './common/types.constant';
import thingsboardKeyboardShortcut from './components/keyboard-shortcut.filter';
import thingsboardHelp from './help/help.directive'; import thingsboardHelp from './help/help.directive';
import thingsboardToast from './services/toast'; import thingsboardToast from './services/toast';
import thingsboardHome from './layout'; import thingsboardHome from './layout';
@ -95,6 +96,7 @@ angular.module('thingsboard', [
thingsboardMenu, thingsboardMenu,
thingsboardUtils, thingsboardUtils,
thingsboardTypes, thingsboardTypes,
thingsboardKeyboardShortcut,
thingsboardHelp, thingsboardHelp,
thingsboardToast, thingsboardToast,
thingsboardHome, thingsboardHome,

108
ui/src/app/components/dashboard.directive.js

@ -23,6 +23,7 @@ import thingsboardWidget from './widget.directive';
import thingsboardToast from '../services/toast'; import thingsboardToast from '../services/toast';
import thingsboardTimewindow from './timewindow.directive'; import thingsboardTimewindow from './timewindow.directive';
import thingsboardEvents from './tb-event-directives'; import thingsboardEvents from './tb-event-directives';
import thingsboardMousepointMenu from './mousepoint-menu.directive';
/* eslint-disable import/no-unresolved, import/default */ /* eslint-disable import/no-unresolved, import/default */
@ -38,6 +39,7 @@ export default angular.module('thingsboard.directives.dashboard', [thingsboardTy
thingsboardWidget, thingsboardWidget,
thingsboardTimewindow, thingsboardTimewindow,
thingsboardEvents, thingsboardEvents,
thingsboardMousepointMenu,
gridster.name]) gridster.name])
.directive('tbDashboard', Dashboard) .directive('tbDashboard', Dashboard)
.name; .name;
@ -59,7 +61,10 @@ function Dashboard() {
isRemoveActionEnabled: '=', isRemoveActionEnabled: '=',
onEditWidget: '&?', onEditWidget: '&?',
onRemoveWidget: '&?', onRemoveWidget: '&?',
onWidgetMouseDown: '&?',
onWidgetClicked: '&?', onWidgetClicked: '&?',
prepareDashboardContextMenu: '&?',
prepareWidgetContextMenu: '&?',
loadWidgets: '&?', loadWidgets: '&?',
onInit: '&?', onInit: '&?',
onInitFailed: '&?', onInitFailed: '&?',
@ -75,8 +80,9 @@ function Dashboard() {
function DashboardController($scope, $rootScope, $element, $timeout, $log, toast, types) { function DashboardController($scope, $rootScope, $element, $timeout, $log, toast, types) {
var highlightedMode = false; var highlightedMode = false;
var highlightedIndex = -1; var highlightedWidget = null;
var mouseDownIndex = -1; var selectedWidget = null;
var mouseDownWidget = -1;
var widgetMouseMoved = false; var widgetMouseMoved = false;
var gridsterParent = null; var gridsterParent = null;
@ -117,6 +123,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
vm.isWidgetExpanded = false; vm.isWidgetExpanded = false;
vm.isHighlighted = isHighlighted; vm.isHighlighted = isHighlighted;
vm.isNotHighlighted = isNotHighlighted; vm.isNotHighlighted = isNotHighlighted;
vm.selectWidget = selectWidget;
vm.getSelectedWidget = getSelectedWidget;
vm.highlightWidget = highlightWidget; vm.highlightWidget = highlightWidget;
vm.resetHighlight = resetHighlight; vm.resetHighlight = resetHighlight;
@ -134,6 +142,17 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
vm.removeWidget = removeWidget; vm.removeWidget = removeWidget;
vm.loading = loading; vm.loading = loading;
vm.openDashboardContextMenu = openDashboardContextMenu;
vm.openWidgetContextMenu = openWidgetContextMenu;
vm.getEventGridPosition = getEventGridPosition;
vm.contextMenuItems = [];
vm.contextMenuEvent = null;
vm.widgetContextMenuItems = [];
vm.widgetContextMenuEvent = null;
//$element[0].onmousemove=function(){ //$element[0].onmousemove=function(){
// widgetMouseMove(); // widgetMouseMove();
// } // }
@ -305,7 +324,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
} }
function resetWidgetClick () { function resetWidgetClick () {
mouseDownIndex = -1; mouseDownWidget = -1;
widgetMouseMoved = false; widgetMouseMoved = false;
} }
@ -315,25 +334,27 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
} }
function widgetMouseDown ($event, widget) { function widgetMouseDown ($event, widget) {
mouseDownIndex = vm.widgets.indexOf(widget); mouseDownWidget = widget;
widgetMouseMoved = false; widgetMouseMoved = false;
if (vm.onWidgetMouseDown) {
vm.onWidgetMouseDown({event: $event, widget: widget});
}
} }
function widgetMouseMove () { function widgetMouseMove () {
if (mouseDownIndex > -1) { if (mouseDownWidget) {
widgetMouseMoved = true; widgetMouseMoved = true;
} }
} }
function widgetMouseUp ($event, widget) { function widgetMouseUp ($event, widget) {
$timeout(function () { $timeout(function () {
if (!widgetMouseMoved && mouseDownIndex > -1) { if (!widgetMouseMoved && mouseDownWidget) {
var index = vm.widgets.indexOf(widget); if (widget === mouseDownWidget) {
if (index === mouseDownIndex) {
widgetClicked($event, widget); widgetClicked($event, widget);
} }
} }
mouseDownIndex = -1; mouseDownWidget = null;
widgetMouseMoved = false; widgetMouseMoved = false;
}, 0); }, 0);
} }
@ -347,6 +368,41 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
} }
} }
function openDashboardContextMenu($event, $mdOpenMousepointMenu) {
if (vm.prepareDashboardContextMenu) {
vm.contextMenuItems = vm.prepareDashboardContextMenu();
if (vm.contextMenuItems && vm.contextMenuItems.length > 0) {
vm.contextMenuEvent = $event;
$mdOpenMousepointMenu($event);
}
}
}
function openWidgetContextMenu($event, widget, $mdOpenMousepointMenu) {
if (vm.prepareWidgetContextMenu) {
vm.widgetContextMenuItems = vm.prepareWidgetContextMenu({widget: widget});
if (vm.widgetContextMenuItems && vm.widgetContextMenuItems.length > 0) {
vm.widgetContextMenuEvent = $event;
$mdOpenMousepointMenu($event);
}
}
}
function getEventGridPosition(event) {
var pos = {
row: 0,
column: 0
}
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);
}
return pos;
}
function editWidget ($event, widget) { function editWidget ($event, widget) {
resetWidgetClick(); resetWidgetClick();
if ($event) { if ($event) {
@ -367,10 +423,10 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
} }
} }
function highlightWidget(widgetIndex, delay) { function highlightWidget(widget, delay) {
highlightedMode = true; highlightedMode = true;
highlightedIndex = widgetIndex; highlightedWidget = widget;
var item = $('.gridster-item', gridster.$element)[widgetIndex]; var item = $('.gridster-item', gridster.$element)[vm.widgets.indexOf(widget)];
if (item) { if (item) {
var height = $(item).outerHeight(true); var height = $(item).outerHeight(true);
var rectHeight = gridsterParent.height(); var rectHeight = gridsterParent.height();
@ -385,17 +441,39 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
} }
} }
function selectWidget(widget, delay) {
selectedWidget = widget;
var item = $('.gridster-item', gridster.$element)[vm.widgets.indexOf(widget)];
if (item) {
var height = $(item).outerHeight(true);
var rectHeight = gridsterParent.height();
var offset = (rectHeight - height) / 2;
var scrollTop = item.offsetTop;
if (offset > 0) {
scrollTop -= offset;
}
gridsterParent.animate({
scrollTop: scrollTop
}, delay);
}
}
function getSelectedWidget() {
return selectedWidget;
}
function resetHighlight() { function resetHighlight() {
highlightedMode = false; highlightedMode = false;
highlightedIndex = -1; highlightedWidget = null;
selectedWidget = null;
} }
function isHighlighted(widget) { function isHighlighted(widget) {
return highlightedMode && vm.widgets.indexOf(widget) === highlightedIndex; return (highlightedMode && highlightedWidget === widget) || (selectedWidget === widget);
} }
function isNotHighlighted(widget) { function isNotHighlighted(widget) {
return highlightedMode && vm.widgets.indexOf(widget) != highlightedIndex; return highlightedMode && highlightedWidget != widget;
} }
function widgetColor(widget) { function widgetColor(widget) {

144
ui/src/app/components/dashboard.tpl.html

@ -19,64 +19,90 @@
ng-show="(vm.loading() || vm.dashboardLoading) && !vm.isEdit"> ng-show="(vm.loading() || vm.dashboardLoading) && !vm.isEdit">
<md-progress-circular md-mode="indeterminate" class="md-warn" md-diameter="100"></md-progress-circular> <md-progress-circular md-mode="indeterminate" class="md-warn" md-diameter="100"></md-progress-circular>
</md-content> </md-content>
<md-content id="gridster-parent" class="tb-dashboard-content" flex layout-wrap> <md-menu md-position-mode="target target" tb-mousepoint-menu>
<div ng-style="vm.dashboardStyle" id="gridster-background" style="height: auto; min-height: 100%;"> <md-content id="gridster-parent" class="tb-dashboard-content" flex layout-wrap ng-click="" tb-contextmenu="vm.openDashboardContextMenu($event, $mdOpenMousepointMenu)">
<div id="gridster-child" gridster="vm.gridsterOpts"> <div ng-style="vm.dashboardStyle" id="gridster-background" style="height: auto; min-height: 100%;">
<ul> <div id="gridster-child" gridster="vm.gridsterOpts">
<!-- ng-click="widgetClicked($event, widget)" --> <ul>
<li gridster-item="widget" ng-repeat="widget in vm.widgets"> <!-- ng-click="widgetClicked($event, widget)" -->
<div tb-expand-fullscreen expand-button-id="expand-button" on-fullscreen-changed="vm.onWidgetFullscreenChanged(expanded, widget)" layout="column" class="tb-widget md-whiteframe-4dp" <li gridster-item="widget" ng-repeat="widget in vm.widgets">
ng-class="{'tb-highlighted': vm.isHighlighted(widget), 'tb-not-highlighted': vm.isNotHighlighted(widget)}" <md-menu md-position-mode="target target" tb-mousepoint-menu>
tb-mousedown="vm.widgetMouseDown($event, widget)" <div tb-expand-fullscreen
tb-mousemove="vm.widgetMouseMove($event, widget)" expand-button-id="expand-button" on-fullscreen-changed="vm.onWidgetFullscreenChanged(expanded, widget)" layout="column" class="tb-widget md-whiteframe-4dp"
tb-mouseup="vm.widgetMouseUp($event, widget)" ng-class="{'tb-highlighted': vm.isHighlighted(widget), 'tb-not-highlighted': vm.isNotHighlighted(widget)}"
style=" tb-mousedown="vm.widgetMouseDown($event, widget)"
cursor: pointer; tb-mousemove="vm.widgetMouseMove($event, widget)"
color: {{vm.widgetColor(widget)}}; tb-mouseup="vm.widgetMouseUp($event, widget)"
background-color: {{vm.widgetBackgroundColor(widget)}}; ng-click=""
padding: {{vm.widgetPadding(widget)}} tb-contextmenu="vm.openWidgetContextMenu($event, widget, $mdOpenMousepointMenu)"
"> style="
<div class="tb-widget-title" layout="column" ng-show="vm.showWidgetTitle(widget) || vm.hasTimewindow(widget)"> cursor: pointer;
<span ng-show="vm.showWidgetTitle(widget)" class="md-subhead">{{widget.config.title}}</span> color: {{vm.widgetColor(widget)}};
<tb-timewindow ng-if="vm.hasTimewindow(widget)" ng-model="widget.config.timewindow"></tb-timewindow> background-color: {{vm.widgetBackgroundColor(widget)}};
</div> padding: {{vm.widgetPadding(widget)}}
<div class="tb-widget-actions" layout="row" layout-align="start center"> ">
<md-button id="expand-button" <div class="tb-widget-title" layout="column" ng-show="vm.showWidgetTitle(widget) || vm.hasTimewindow(widget)">
aria-label="{{ 'fullscreen.fullscreen' | translate }}" <span ng-show="vm.showWidgetTitle(widget)" class="md-subhead">{{widget.config.title}}</span>
class="md-icon-button md-primary"></md-button> <tb-timewindow ng-if="vm.hasTimewindow(widget)" ng-model="widget.config.timewindow"></tb-timewindow>
<md-button ng-show="vm.isEditActionEnabled && !vm.isWidgetExpanded" </div>
ng-disabled="vm.loading()" <div class="tb-widget-actions" layout="row" layout-align="start center">
class="md-icon-button md-primary" <md-button id="expand-button"
ng-click="vm.editWidget($event, widget)" ng-show="!vm.isEdit"
aria-label="{{ 'widget.edit' | translate }}"> aria-label="{{ 'fullscreen.fullscreen' | translate }}"
<md-tooltip md-direction="top"> class="md-icon-button md-primary"></md-button>
{{ 'widget.edit' | translate }} <md-button ng-show="vm.isEditActionEnabled && !vm.isWidgetExpanded"
</md-tooltip> ng-disabled="vm.loading()"
<md-icon class="material-icons"> class="md-icon-button md-primary"
edit ng-click="vm.editWidget($event, widget)"
</md-icon> aria-label="{{ 'widget.edit' | translate }}">
</md-button> <md-tooltip md-direction="top">
<md-button ng-show="vm.isRemoveActionEnabled && !vm.isWidgetExpanded" {{ 'widget.edit' | translate }}
ng-disabled="vm.loading()" </md-tooltip>
class="md-icon-button md-primary" <md-icon class="material-icons">
ng-click="vm.removeWidget($event, widget)" edit
aria-label="{{ 'widget.remove' | translate }}"> </md-icon>
<md-tooltip md-direction="top"> </md-button>
{{ 'widget.remove' | translate }} <md-button ng-show="vm.isRemoveActionEnabled && !vm.isWidgetExpanded"
</md-tooltip> ng-disabled="vm.loading()"
<md-icon class="material-icons"> class="md-icon-button md-primary"
close ng-click="vm.removeWidget($event, widget)"
</md-icon> aria-label="{{ 'widget.remove' | translate }}">
</md-button> <md-tooltip md-direction="top">
</div> {{ 'widget.remove' | translate }}
<div flex layout="column" class="tb-widget-content"> </md-tooltip>
<div flex tb-widget <md-icon class="material-icons">
locals="{ visibleRect: vm.visibleRect, widget: widget, deviceAliasList: vm.deviceAliasList, isPreview: vm.isEdit }"> close
</md-icon>
</md-button>
</div>
<div flex layout="column" class="tb-widget-content">
<div flex tb-widget
locals="{ visibleRect: vm.visibleRect, widget: widget, deviceAliasList: vm.deviceAliasList, isPreview: vm.isEdit }">
</div>
</div>
</div> </div>
</div> <md-menu-content id="menu" width="4" ng-mouseleave="$mdCloseMousepointMenu()">
</div> <md-menu-item ng-repeat ="item in vm.widgetContextMenuItems">
</li> <md-button ng-disabled="!item.enabled" ng-click="item.action(vm.widgetContextMenuEvent, widget)">
</ul> <md-icon ng-if="item.icon" md-menu-align-target aria-label="{{ item.value | translate }}" class="material-icons">{{item.icon}}</md-icon>
<span translate>{{item.value}}</span>
<span ng-if="item.shortcut" class="tb-alt-text"> {{ item.shortcut | keyboardShortcut }}</span>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
</li>
</ul>
</div>
</div> </div>
</div> </md-content>
</md-content> <md-menu-content id="menu" width="4" ng-mouseleave="$mdCloseMousepointMenu()">
<md-menu-item ng-repeat ="item in vm.contextMenuItems">
<md-button ng-disabled="!item.enabled" ng-click="item.action(vm.contextMenuEvent)">
<md-icon ng-if="item.icon" md-menu-align-target aria-label="{{ item.value | translate }}" class="material-icons">{{item.icon}}</md-icon>
<span translate>{{item.value}}</span>
<span ng-if="item.shortcut" class="tb-alt-text"> {{ item.shortcut | keyboardShortcut }}</span>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>

40
ui/src/app/components/keyboard-shortcut.filter.js

@ -0,0 +1,40 @@
/*
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export default angular.module('thingsboard.filters.keyboardShortcut', [])
.filter('keyboardShortcut', KeyboardShortcut)
.name;
/*@ngInject*/
function KeyboardShortcut($window) {
return function(str) {
if (!str) return;
var keys = str.split('-');
var isOSX = /Mac OS X/.test($window.navigator.userAgent);
var seperator = (!isOSX || keys.length > 2) ? '+' : '';
var abbreviations = {
M: isOSX ? '⌘' : 'Ctrl',
A: isOSX ? 'Option' : 'Alt',
S: 'Shift'
};
return keys.map(function(key, index) {
var last = index == keys.length - 1;
return last ? key : abbreviations[key];
}).join(seperator);
};
}

51
ui/src/app/components/mousepoint-menu.directive.js

@ -0,0 +1,51 @@
/*
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export default angular.module('thingsboard.directives.mousepointMenu', [])
.directive('tbMousepointMenu', MousepointMenu)
.name;
/*@ngInject*/
function MousepointMenu() {
var linker = function ($scope, $element, $attrs, RightClickContextMenu) {
$scope.$mdOpenMousepointMenu = function($event){
RightClickContextMenu.offsets = function(){
var offset = $element.offset();
var x = $event.pageX - offset.left;
var y = $event.pageY - offset.top;
var offsets = {
left: x,
top: y
}
return offsets;
}
RightClickContextMenu.open($event);
};
$scope.$mdCloseMousepointMenu = function() {
RightClickContextMenu.close();
}
}
return {
restrict: "A",
link: linker,
require: 'mdMenu'
};
}

2
ui/src/app/components/tb-event-directives.js

@ -20,7 +20,7 @@ const PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
var tbEventDirectives = {}; var tbEventDirectives = {};
angular.forEach( angular.forEach(
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '), 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave contextmenu keydown keyup keypress submit focus blur copy cut paste'.split(' '),
function(eventName) { function(eventName) {
var directiveName = directiveNormalize('tb-' + eventName); var directiveName = directiveNormalize('tb-' + eventName);
tbEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse) { tbEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse) {

23
ui/src/app/components/widgets-bundle-select.directive.js

@ -55,12 +55,26 @@ function WidgetsBundleSelect($compile, $templateCache, widgetService, types) {
if (widgetsBundles.length > 0) { if (widgetsBundles.length > 0) {
scope.widgetsBundle = widgetsBundles[0]; scope.widgetsBundle = widgetsBundles[0];
} }
} else if (angular.isDefined(scope.selectBundleAlias)) {
selectWidgetsBundleByAlias(scope.selectBundleAlias);
} }
}, },
function fail() { function fail() {
} }
); );
function selectWidgetsBundleByAlias(alias) {
if (scope.widgetsBundles && alias) {
for (var w in scope.widgetsBundles) {
var widgetsBundle = scope.widgetsBundles[w];
if (widgetsBundle.alias === alias) {
scope.widgetsBundle = widgetsBundle;
break;
}
}
}
}
scope.isSystem = function(item) { scope.isSystem = function(item) {
return item && item.tenantId.id === types.id.nullUid; return item && item.tenantId.id === types.id.nullUid;
} }
@ -79,6 +93,12 @@ function WidgetsBundleSelect($compile, $templateCache, widgetService, types) {
scope.updateView(); scope.updateView();
}); });
scope.$watch('selectBundleAlias', function (newVal, prevVal) {
if (newVal !== prevVal) {
selectWidgetsBundleByAlias(scope.selectBundleAlias);
}
});
$compile(element.contents())(scope); $compile(element.contents())(scope);
} }
@ -90,7 +110,8 @@ function WidgetsBundleSelect($compile, $templateCache, widgetService, types) {
bundlesScope: '@', bundlesScope: '@',
theForm: '=?', theForm: '=?',
tbRequired: '=?', tbRequired: '=?',
selectFirstBundle: '=' selectFirstBundle: '=',
selectBundleAlias: '=?'
} }
}; };
} }

4
ui/src/app/dashboard/dashboard-settings.controller.js

@ -28,6 +28,10 @@ export default function DashboardSettingsController($scope, $mdDialog, gridSetti
vm.gridSettings = gridSettings || {}; vm.gridSettings = gridSettings || {};
if (angular.isUndefined(vm.gridSettings.showTitle)) {
vm.gridSettings.showTitle = true;
}
vm.gridSettings.backgroundColor = vm.gridSettings.backgroundColor || 'rgba(0,0,0,0)'; vm.gridSettings.backgroundColor = vm.gridSettings.backgroundColor || 'rgba(0,0,0,0)';
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];

5
ui/src/app/dashboard/dashboard-settings.tpl.html

@ -31,6 +31,11 @@
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="loading">
<div layout="row" layout-padding>
<md-checkbox flex aria-label="{{ 'dashboard.display-title' | translate }}"
ng-model="vm.gridSettings.showTitle">{{ 'dashboard.display-title' | translate }}
</md-checkbox>
</div>
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>dashboard.columns-count</label> <label translate>dashboard.columns-count</label>
<input required type="number" step="any" name="columns" ng-model="vm.gridSettings.columns" min="10" <input required type="number" step="any" name="columns" ng-model="vm.gridSettings.columns" min="10"

211
ui/src/app/dashboard/dashboard.controller.js

@ -23,7 +23,7 @@ import addWidgetTemplate from './add-widget.tpl.html';
/*@ngInject*/ /*@ngInject*/
export default function DashboardController(types, widgetService, userService, export default function DashboardController(types, widgetService, userService,
dashboardService, $window, $rootScope, dashboardService, itembuffer, hotkeys, $window, $rootScope,
$scope, $state, $stateParams, $mdDialog, $timeout, $document, $q, $translate, $filter) { $scope, $state, $stateParams, $mdDialog, $timeout, $document, $q, $translate, $filter) {
var user = userService.getCurrentUser(); var user = userService.getCurrentUser();
@ -48,7 +48,10 @@ export default function DashboardController(types, widgetService, userService,
vm.addWidgetFromType = addWidgetFromType; vm.addWidgetFromType = addWidgetFromType;
vm.dashboardInited = dashboardInited; vm.dashboardInited = dashboardInited;
vm.dashboardInitFailed = dashboardInitFailed; vm.dashboardInitFailed = dashboardInitFailed;
vm.widgetMouseDown = widgetMouseDown;
vm.widgetClicked = widgetClicked; vm.widgetClicked = widgetClicked;
vm.prepareDashboardContextMenu = prepareDashboardContextMenu;
vm.prepareWidgetContextMenu = prepareWidgetContextMenu;
vm.editWidget = editWidget; vm.editWidget = editWidget;
vm.isTenantAdmin = isTenantAdmin; vm.isTenantAdmin = isTenantAdmin;
vm.loadDashboard = loadDashboard; vm.loadDashboard = loadDashboard;
@ -63,6 +66,7 @@ export default function DashboardController(types, widgetService, userService,
vm.toggleDashboardEditMode = toggleDashboardEditMode; vm.toggleDashboardEditMode = toggleDashboardEditMode;
vm.onRevertWidgetEdit = onRevertWidgetEdit; vm.onRevertWidgetEdit = onRevertWidgetEdit;
vm.helpLinkIdForWidgetType = helpLinkIdForWidgetType; vm.helpLinkIdForWidgetType = helpLinkIdForWidgetType;
vm.displayTitle = displayTitle;
vm.widgetsBundle; vm.widgetsBundle;
@ -194,6 +198,7 @@ export default function DashboardController(types, widgetService, userService,
function dashboardInited(dashboard) { function dashboardInited(dashboard) {
vm.dashboardContainer = dashboard; vm.dashboardContainer = dashboard;
initHotKeys();
} }
function isTenantAdmin() { function isTenantAdmin() {
@ -289,18 +294,188 @@ export default function DashboardController(types, widgetService, userService,
var delayOffset = transition ? 350 : 0; var delayOffset = transition ? 350 : 0;
var delay = transition ? 400 : 300; var delay = transition ? 400 : 300;
$timeout(function () { $timeout(function () {
vm.dashboardContainer.highlightWidget(vm.editingWidgetIndex, delay); vm.dashboardContainer.highlightWidget(widget, delay);
}, delayOffset, false); }, delayOffset, false);
} }
} }
} }
function widgetMouseDown($event, widget) {
if (vm.isEdit && !vm.isEditingWidget) {
vm.dashboardContainer.selectWidget(widget, 0);
}
}
function widgetClicked($event, widget) { function widgetClicked($event, widget) {
if (vm.isEditingWidget) { if (vm.isEditingWidget) {
editWidget($event, widget); editWidget($event, widget);
} }
} }
function initHotKeys() {
$translate(['action.copy', 'action.paste', 'action.delete']).then(function (translations) {
hotkeys.bindTo($scope)
.add({
combo: 'ctrl+c',
description: translations['action.copy'],
allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
callback: function (event) {
if (vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) {
var widget = vm.dashboardContainer.getSelectedWidget();
if (widget) {
event.preventDefault();
copyWidget(event, widget);
}
}
}
})
.add({
combo: 'ctrl+v',
description: translations['action.paste'],
allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
callback: function (event) {
if (vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) {
if (itembuffer.hasWidget()) {
event.preventDefault();
pasteWidget(event);
}
}
}
})
.add({
combo: 'ctrl+x',
description: translations['action.delete'],
allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
callback: function (event) {
if (vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) {
var widget = vm.dashboardContainer.getSelectedWidget();
if (widget) {
event.preventDefault();
removeWidget(event, widget);
}
}
}
});
});
}
function prepareDashboardContextMenu() {
var dashboardContextActions = [];
if (vm.isEdit && !vm.isEditingWidget && !vm.widgetEditMode) {
dashboardContextActions.push(
{
action: openDashboardSettings,
enabled: true,
value: "dashboard.settings",
icon: "settings"
}
);
dashboardContextActions.push(
{
action: openDeviceAliases,
enabled: true,
value: "device.aliases",
icon: "devices_other"
}
);
dashboardContextActions.push(
{
action: pasteWidget,
enabled: itembuffer.hasWidget(),
value: "action.paste",
icon: "content_paste",
shortcut: "M-V"
}
);
}
return dashboardContextActions;
}
function pasteWidget($event) {
var pos = vm.dashboardContainer.getEventGridPosition($event);
itembuffer.pasteWidget(vm.dashboard, pos);
}
function prepareWidgetContextMenu() {
var widgetContextActions = [];
if (vm.isEdit && !vm.isEditingWidget) {
widgetContextActions.push(
{
action: editWidget,
enabled: true,
value: "action.edit",
icon: "edit"
}
);
if (!vm.widgetEditMode) {
widgetContextActions.push(
{
action: copyWidget,
enabled: true,
value: "action.copy",
icon: "content_copy",
shortcut: "M-C"
}
);
widgetContextActions.push(
{
action: removeWidget,
enabled: true,
value: "action.delete",
icon: "clear",
shortcut: "M-X"
}
);
}
}
return widgetContextActions;
}
function copyWidget($event, widget) {
var aliasesInfo = {
datasourceAliases: {},
targetDeviceAliases: {}
};
var originalColumns = 24;
if (vm.dashboard.configuration.gridSettings &&
vm.dashboard.configuration.gridSettings.columns) {
originalColumns = vm.dashboard.configuration.gridSettings.columns;
}
if (widget.config && vm.dashboard.configuration
&& vm.dashboard.configuration.deviceAliases) {
var deviceAlias;
if (widget.config.datasources) {
for (var i=0;i<widget.config.datasources.length;i++) {
var datasource = widget.config.datasources[i];
if (datasource.type === types.datasourceType.device && datasource.deviceAliasId) {
deviceAlias = vm.dashboard.configuration.deviceAliases[datasource.deviceAliasId];
if (deviceAlias) {
aliasesInfo.datasourceAliases[i] = {
aliasName: deviceAlias.alias,
deviceId: deviceAlias.deviceId
}
}
}
}
}
if (widget.config.targetDeviceAliasIds) {
for (i=0;i<widget.config.targetDeviceAliasIds.length;i++) {
var targetDeviceAliasId = widget.config.targetDeviceAliasIds[i];
if (targetDeviceAliasId) {
deviceAlias = vm.dashboard.configuration.deviceAliases[targetDeviceAliasId];
if (deviceAlias) {
aliasesInfo.targetDeviceAliases[i] = {
aliasName: deviceAlias.alias,
deviceId: deviceAlias.deviceId
}
}
}
}
}
}
itembuffer.copyWidget(widget, aliasesInfo, originalColumns);
}
function helpLinkIdForWidgetType() { function helpLinkIdForWidgetType() {
var link = 'widgetsConfig'; var link = 'widgetsConfig';
if (vm.editingWidget && vm.editingWidget.type) { if (vm.editingWidget && vm.editingWidget.type) {
@ -322,6 +497,15 @@ export default function DashboardController(types, widgetService, userService,
return link; return link;
} }
function displayTitle() {
if (vm.dashboard && vm.dashboard.configuration.gridSettings &&
angular.isDefined(vm.dashboard.configuration.gridSettings.showTitle)) {
return vm.dashboard.configuration.gridSettings.showTitle;
} else {
return true;
}
}
function onRevertWidgetEdit(widgetForm) { function onRevertWidgetEdit(widgetForm) {
if (widgetForm.$dirty) { if (widgetForm.$dirty) {
widgetForm.$setPristine(); widgetForm.$setPristine();
@ -331,7 +515,9 @@ export default function DashboardController(types, widgetService, userService,
function saveWidget(widgetForm) { function saveWidget(widgetForm) {
widgetForm.$setPristine(); widgetForm.$setPristine();
vm.widgets[vm.editingWidgetIndex] = angular.copy(vm.editingWidget); var widget = angular.copy(vm.editingWidget);
vm.widgets[vm.editingWidgetIndex] = widget;
vm.dashboardContainer.highlightWidget(widget, 0);
} }
function onEditWidgetClosed() { function onEditWidgetClosed() {
@ -421,8 +607,8 @@ export default function DashboardController(types, widgetService, userService,
}); });
} }
function toggleDashboardEditMode() { function setEditMode(isEdit, revert) {
vm.isEdit = !vm.isEdit; vm.isEdit = isEdit;
if (vm.isEdit) { if (vm.isEdit) {
if (vm.widgetEditMode) { if (vm.widgetEditMode) {
vm.prevWidgets = angular.copy(vm.widgets); vm.prevWidgets = angular.copy(vm.widgets);
@ -433,14 +619,23 @@ export default function DashboardController(types, widgetService, userService,
if (vm.widgetEditMode) { if (vm.widgetEditMode) {
vm.widgets = vm.prevWidgets; vm.widgets = vm.prevWidgets;
} else { } else {
vm.dashboard = vm.prevDashboard; if (vm.dashboardContainer) {
vm.widgets = vm.dashboard.configuration.widgets; vm.dashboardContainer.resetHighlight();
}
if (revert) {
vm.dashboard = vm.prevDashboard;
vm.widgets = vm.dashboard.configuration.widgets;
}
} }
} }
} }
function toggleDashboardEditMode() {
setEditMode(!vm.isEdit, true);
}
function saveDashboard() { function saveDashboard() {
vm.isEdit = false; setEditMode(false, false);
notifyDashboardUpdated(); notifyDashboardUpdated();
} }

8
ui/src/app/dashboard/dashboard.tpl.html

@ -51,7 +51,7 @@
</md-button> </md-button>
</section> </section>
<section ng-if="!vm.widgetEditMode" class="tb-dashboard-title" layout="row" layout-align="center center"> <section ng-if="!vm.widgetEditMode" class="tb-dashboard-title" layout="row" layout-align="center center">
<h3 ng-show="!vm.isEdit">{{ vm.dashboard.title }}</h3> <h3 ng-show="!vm.isEdit && vm.displayTitle()">{{ vm.dashboard.title }}</h3>
<md-input-container ng-show="vm.isEdit" class="md-block" style="height: 30px;"> <md-input-container ng-show="vm.isEdit" class="md-block" style="height: 30px;">
<label translate>dashboard.title</label> <label translate>dashboard.title</label>
<input class="tb-dashboard-title" required name="title" ng-model="vm.dashboard.title"> <input class="tb-dashboard-title" required name="title" ng-model="vm.dashboard.title">
@ -64,7 +64,7 @@
</md-button> </md-button>
</section> </section>
<div class="tb-absolute-fill" <div class="tb-absolute-fill"
ng-class="{ 'tb-padded' : !vm.widgetEditMode, 'tb-shrinked' : vm.isEditingWidget }"> ng-class="{ 'tb-padded' : !vm.widgetEditMode && (vm.isEdit || vm.displayTitle()), 'tb-shrinked' : vm.isEditingWidget }">
<tb-dashboard <tb-dashboard
dashboard-style="{'background-color': vm.dashboard.configuration.gridSettings.backgroundColor, dashboard-style="{'background-color': vm.dashboard.configuration.gridSettings.backgroundColor,
'background-image': 'url('+vm.dashboard.configuration.gridSettings.backgroundImageUrl+')', 'background-image': 'url('+vm.dashboard.configuration.gridSettings.backgroundImageUrl+')',
@ -82,7 +82,11 @@
is-edit-action-enabled="vm.isEdit || vm.widgetEditMode" is-edit-action-enabled="vm.isEdit || vm.widgetEditMode"
is-remove-action-enabled="vm.isEdit && !vm.widgetEditMode" is-remove-action-enabled="vm.isEdit && !vm.widgetEditMode"
on-edit-widget="vm.editWidget(event, widget)" on-edit-widget="vm.editWidget(event, widget)"
on-widget-mouse-down="vm.widgetMouseDown(event, widget)"
on-widget-clicked="vm.widgetClicked(event, widget)" on-widget-clicked="vm.widgetClicked(event, widget)"
on-widget-context-menu="vm.widgetContextMenu(event, widget)"
prepare-dashboard-context-menu="vm.prepareDashboardContextMenu()"
prepare-widget-context-menu="vm.prepareWidgetContextMenu(widget)"
on-remove-widget="vm.removeWidget(event, widget)" on-remove-widget="vm.removeWidget(event, widget)"
load-widgets="vm.loadDashboard()" load-widgets="vm.loadDashboard()"
on-init="vm.dashboardInited(dashboard)" on-init="vm.dashboardInited(dashboard)"

2
ui/src/app/dashboard/index.js

@ -29,6 +29,7 @@ import thingsboardDashboard from '../components/dashboard.directive';
import thingsboardExpandFullscreen from '../components/expand-fullscreen.directive'; import thingsboardExpandFullscreen from '../components/expand-fullscreen.directive';
import thingsboardWidgetsBundleSelect from '../components/widgets-bundle-select.directive'; import thingsboardWidgetsBundleSelect from '../components/widgets-bundle-select.directive';
import thingsboardTypes from '../common/types.constant'; import thingsboardTypes from '../common/types.constant';
import thingsboardItemBuffer from '../services/item-buffer.service';
import DashboardRoutes from './dashboard.routes'; import DashboardRoutes from './dashboard.routes';
import DashboardsController from './dashboards.controller'; import DashboardsController from './dashboards.controller';
@ -45,6 +46,7 @@ export default angular.module('thingsboard.dashboard', [
uiRouter, uiRouter,
gridster.name, gridster.name,
thingsboardTypes, thingsboardTypes,
thingsboardItemBuffer,
thingsboardGrid, thingsboardGrid,
thingsboardApiWidget, thingsboardApiWidget,
thingsboardApiUser, thingsboardApiUser,

81
ui/src/app/device/attribute/add-widget-to-dashboard-dialog.controller.js

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
/*@ngInject*/ /*@ngInject*/
export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, dashboardService, deviceId, deviceName, widget) { export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, itembuffer, dashboardService, deviceId, deviceName, widget) {
var vm = this; var vm = this;
@ -34,62 +34,20 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog,
function add() { function add() {
$scope.theForm.$setPristine(); $scope.theForm.$setPristine();
var theDashboard; var theDashboard;
var deviceAliases;
widget.col = 0;
widget.sizeX /= 2;
widget.sizeY /= 2;
if (vm.addToDashboardType === 0) { if (vm.addToDashboardType === 0) {
theDashboard = vm.dashboard; theDashboard = vm.dashboard;
if (!theDashboard.configuration) {
theDashboard.configuration = {};
}
deviceAliases = theDashboard.configuration.deviceAliases;
if (!deviceAliases) {
deviceAliases = {};
theDashboard.configuration.deviceAliases = deviceAliases;
}
var newAliasId;
for (var aliasId in deviceAliases) {
if (deviceAliases[aliasId].deviceId === deviceId) {
newAliasId = aliasId;
break;
}
}
if (!newAliasId) {
var newAliasName = createDeviceAliasName(deviceAliases, deviceName);
newAliasId = 0;
for (aliasId in deviceAliases) {
newAliasId = Math.max(newAliasId, aliasId);
}
newAliasId++;
deviceAliases[newAliasId] = {alias: newAliasName, deviceId: deviceId};
}
widget.config.datasources[0].deviceAliasId = newAliasId;
if (!theDashboard.configuration.widgets) {
theDashboard.configuration.widgets = [];
}
var row = 0;
for (var w in theDashboard.configuration.widgets) {
var existingWidget = theDashboard.configuration.widgets[w];
var wRow = existingWidget.row ? existingWidget.row : 0;
var wSizeY = existingWidget.sizeY ? existingWidget.sizeY : 1;
var bottom = wRow + wSizeY;
row = Math.max(row, bottom);
}
widget.row = row;
theDashboard.configuration.widgets.push(widget);
} else { } else {
theDashboard = vm.newDashboard; theDashboard = vm.newDashboard;
deviceAliases = {};
deviceAliases['1'] = {alias: deviceName, deviceId: deviceId};
theDashboard.configuration = {};
theDashboard.configuration.widgets = [];
widget.row = 0;
theDashboard.configuration.widgets.push(widget);
theDashboard.configuration.deviceAliases = deviceAliases;
} }
var aliasesInfo = {
datasourceAliases: {},
targetDeviceAliases: {}
};
aliasesInfo.datasourceAliases[0] = {
aliasName: deviceName,
deviceId: deviceId
};
theDashboard = itembuffer.addWidgetToDashboard(theDashboard, widget, aliasesInfo, 48, -1, -1);
dashboardService.saveDashboard(theDashboard).then( dashboardService.saveDashboard(theDashboard).then(
function success(dashboard) { function success(dashboard) {
$mdDialog.hide(); $mdDialog.hide();
@ -98,25 +56,6 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog,
} }
} }
); );
}
function createDeviceAliasName(deviceAliases, alias) {
var c = 0;
var newAlias = angular.copy(alias);
var unique = false;
while (!unique) {
unique = true;
for (var devAliasId in deviceAliases) {
var devAlias = deviceAliases[devAliasId];
if (newAlias === devAlias.alias) {
c++;
newAlias = alias + c;
unique = false;
}
}
}
return newAlias;
} }
} }

10
ui/src/app/device/attribute/attribute-table.directive.js

@ -239,6 +239,8 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
index: 0 index: 0
} }
scope.widgetsBundle = null; scope.widgetsBundle = null;
scope.firstBundle = true;
scope.selectedWidgetsBundleAlias = types.systemBundleAlias.cards;
scope.deviceAliases = {}; scope.deviceAliases = {};
scope.deviceAliases['1'] = {alias: scope.deviceName, deviceId: scope.deviceId}; scope.deviceAliases['1'] = {alias: scope.deviceName, deviceId: scope.deviceId};
@ -326,13 +328,6 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
} }
} }
}); });
widgetService.getWidgetsBundleByAlias(types.systemBundleAlias.cards).then(
function success(widgetsBundle) {
scope.firstBundle = true;
scope.widgetsBundle = widgetsBundle;
}
);
} }
scope.exitWidgetMode = function() { scope.exitWidgetMode = function() {
@ -344,6 +339,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
scope.widgetsIndexWatch(); scope.widgetsIndexWatch();
scope.widgetsIndexWatch = null; scope.widgetsIndexWatch = null;
} }
scope.selectedWidgetsBundleAlias = null;
scope.mode = 'default'; scope.mode = 'default';
scope.getDeviceAttributes(true); scope.getDeviceAttributes(true);
} }

3
ui/src/app/device/attribute/attribute-table.tpl.html

@ -105,7 +105,8 @@
<tb-widgets-bundle-select flex-offset="5" <tb-widgets-bundle-select flex-offset="5"
flex flex
ng-model="widgetsBundle" ng-model="widgetsBundle"
select-first-bundle="false"> select-first-bundle="false"
select-bundle-alias="selectedWidgetsBundleAlias">
</tb-widgets-bundle-select> </tb-widgets-bundle-select>
</div> </div>
<md-button ng-show="widgetsList.length > 0" class="md-accent md-hue-2 md-raised" ng-click="addWidgetToDashboard($event)"> <md-button ng-show="widgetsList.length > 0" class="md-accent md-hue-2 md-raised" ng-click="addWidgetToDashboard($event)">

191
ui/src/app/services/item-buffer.service.js

@ -0,0 +1,191 @@
/*
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import angularStorage from 'angular-storage';
export default angular.module('thingsboard.itembuffer', [angularStorage])
.factory('itembuffer', ItemBuffer)
.factory('bufferStore', function(store) {
var newStore = store.getNamespacedStore('tbBufferStore', null, null, false);
return newStore;
})
.name;
/*@ngInject*/
function ItemBuffer(bufferStore) {
const WIDGET_ITEM = "widget_item";
var service = {
copyWidget: copyWidget,
hasWidget: hasWidget,
pasteWidget: pasteWidget,
addWidgetToDashboard: addWidgetToDashboard
}
return service;
/**
aliasesInfo {
datasourceAliases: {
datasourceIndex: {
aliasName: "...",
deviceId: "..."
}
}
targetDeviceAliases: {
targetDeviceAliasIndex: {
aliasName: "...",
deviceId: "..."
}
}
....
}
**/
function copyWidget(widget, aliasesInfo, originalColumns) {
var widgetItem = {
widget: widget,
aliasesInfo: aliasesInfo,
originalColumns: originalColumns
}
bufferStore.set(WIDGET_ITEM, angular.toJson(widgetItem));
}
function hasWidget() {
return bufferStore.get(WIDGET_ITEM);
}
function pasteWidget(targetDasgboard, position) {
var widgetItemJson = bufferStore.get(WIDGET_ITEM);
if (widgetItemJson) {
var widgetItem = angular.fromJson(widgetItemJson);
var widget = widgetItem.widget;
var aliasesInfo = widgetItem.aliasesInfo;
var originalColumns = widgetItem.originalColumns;
var targetRow = -1;
var targetColumn = -1;
if (position) {
targetRow = position.row;
targetColumn = position.column;
}
addWidgetToDashboard(targetDasgboard, widget, aliasesInfo, originalColumns, targetRow, targetColumn);
}
}
function addWidgetToDashboard(dashboard, widget, aliasesInfo, originalColumns, row, column) {
var theDashboard;
if (dashboard) {
theDashboard = dashboard;
} else {
theDashboard = {};
}
if (!theDashboard.configuration) {
theDashboard.configuration = {};
}
if (!theDashboard.configuration.deviceAliases) {
theDashboard.configuration.deviceAliases = {};
}
updateAliases(theDashboard, widget, aliasesInfo);
if (!theDashboard.configuration.widgets) {
theDashboard.configuration.widgets = [];
}
var targetColumns = 24;
if (theDashboard.configuration.gridSettings &&
theDashboard.configuration.gridSettings.columns) {
targetColumns = theDashboard.configuration.gridSettings.columns;
}
if (targetColumns != originalColumns) {
var ratio = targetColumns / originalColumns;
widget.sizeX *= ratio;
widget.sizeY *= ratio;
}
if (row > -1 && column > - 1) {
widget.row = row;
widget.col = column;
} else {
row = 0;
for (var w in theDashboard.configuration.widgets) {
var existingWidget = theDashboard.configuration.widgets[w];
var wRow = existingWidget.row ? existingWidget.row : 0;
var wSizeY = existingWidget.sizeY ? existingWidget.sizeY : 1;
var bottom = wRow + wSizeY;
row = Math.max(row, bottom);
}
widget.row = row;
widget.col = 0;
}
theDashboard.configuration.widgets.push(widget);
return theDashboard;
}
function updateAliases(dashboard, widget, aliasesInfo) {
var deviceAliases = dashboard.configuration.deviceAliases;
var aliasInfo;
var newAliasId;
for (var datasourceIndex in aliasesInfo.datasourceAliases) {
aliasInfo = aliasesInfo.datasourceAliases[datasourceIndex];
newAliasId = getDeviceAliasId(deviceAliases, aliasInfo);
widget.config.datasources[datasourceIndex].deviceAliasId = newAliasId;
}
for (var targetDeviceAliasIndex in aliasesInfo.targetDeviceAliases) {
aliasInfo = aliasesInfo.targetDeviceAliases[targetDeviceAliasIndex];
newAliasId = getDeviceAliasId(deviceAliases, aliasInfo);
widget.config.targetDeviceAliasIds[targetDeviceAliasIndex] = newAliasId;
}
}
function getDeviceAliasId(deviceAliases, aliasInfo) {
var newAliasId;
for (var aliasId in deviceAliases) {
if (deviceAliases[aliasId].deviceId === aliasInfo.deviceId) {
newAliasId = aliasId;
break;
}
}
if (!newAliasId) {
var newAliasName = createDeviceAliasName(deviceAliases, aliasInfo.aliasName);
newAliasId = 0;
for (aliasId in deviceAliases) {
newAliasId = Math.max(newAliasId, aliasId);
}
newAliasId++;
deviceAliases[newAliasId] = {alias: newAliasName, deviceId: aliasInfo.deviceId};
}
return newAliasId;
}
function createDeviceAliasName(deviceAliases, alias) {
var c = 0;
var newAlias = angular.copy(alias);
var unique = false;
while (!unique) {
unique = true;
for (var devAliasId in deviceAliases) {
var devAlias = deviceAliases[devAliasId];
if (newAlias === devAlias.alias) {
c++;
newAlias = alias + c;
unique = false;
}
}
}
return newAlias;
}
}

7
ui/src/locale/en_US.json

@ -38,7 +38,9 @@
"create": "Create", "create": "Create",
"drag": "Drag", "drag": "Drag",
"refresh": "Refresh", "refresh": "Refresh",
"undo": "Undo" "undo": "Undo",
"copy": "Copy",
"paste": "Paste"
}, },
"admin": { "admin": {
"general": "General", "general": "General",
@ -211,7 +213,8 @@
"vertical-margin": "Vertical margin", "vertical-margin": "Vertical margin",
"vertical-margin-required": "Vertical margin value is required.", "vertical-margin-required": "Vertical margin value is required.",
"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.",
"display-title": "Display dashboard title"
}, },
"datakey": { "datakey": {
"settings": "Settings", "settings": "Settings",

3
ui/src/scss/main.scss

@ -169,6 +169,9 @@ md-menu-item {
md-menu-item { md-menu-item {
.md-button { .md-button {
display: block; display: block;
.tb-alt-text {
float: right;
}
} }
} }

Loading…
Cancel
Save