diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js
index 43c537cafc..a70308b52f 100644
--- a/ui/src/app/api/entity.service.js
+++ b/ui/src/app/api/entity.service.js
@@ -36,7 +36,8 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
saveRelatedEntity: saveRelatedEntity,
getRelatedEntity: getRelatedEntity,
deleteRelatedEntity: deleteRelatedEntity,
- moveEntity: moveEntity
+ moveEntity: moveEntity,
+ copyEntity: copyEntity
};
return service;
@@ -626,6 +627,32 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
return deferred.promise;
}
+ function copyEntity(entity, targetParentId, keys) {
+ var deferred = $q.defer();
+ if (!entity.id && !entity.id.id) {
+ deferred.reject();
+ } else {
+ getRelatedEntity(entity.id, keys).then(
+ function success(relatedEntity) {
+ delete relatedEntity.id.id;
+ relatedEntity.name = entity.name;
+ saveRelatedEntity(relatedEntity, targetParentId, keys).then(
+ function success(savedEntity) {
+ deferred.resolve(savedEntity);
+ },
+ function fail() {
+ deferred.reject();
+ }
+ );
+ },
+ function fail() {
+ deferred.reject();
+ }
+ );
+ }
+ return deferred.promise;
+ }
+
function saveEntityPromise(entity) {
var entityType = entity.id.entityType;
if (!entity.id.id) {
diff --git a/ui/src/app/common/dashboard-utils.service.js b/ui/src/app/common/dashboard-utils.service.js
index 57317b5b71..78136dfb2e 100644
--- a/ui/src/app/common/dashboard-utils.service.js
+++ b/ui/src/app/common/dashboard-utils.service.js
@@ -179,6 +179,7 @@ function DashboardUtils(types, utils, timeService) {
dashboard.configuration.settings.showEntitiesSelect = true;
dashboard.configuration.settings.showDashboardTimewindow = true;
dashboard.configuration.settings.showDashboardExport = true;
+ dashboard.configuration.settings.toolbarAlwaysOpen = false;
} else {
if (angular.isUndefined(dashboard.configuration.settings.stateControllerId)) {
dashboard.configuration.settings.stateControllerId = 'default';
diff --git a/ui/src/app/components/expand-fullscreen.directive.js b/ui/src/app/components/expand-fullscreen.directive.js
index 37491e71df..b70fd9479a 100644
--- a/ui/src/app/components/expand-fullscreen.directive.js
+++ b/ui/src/app/components/expand-fullscreen.directive.js
@@ -24,7 +24,7 @@ export default angular.module('thingsboard.directives.expandFullscreen', [])
/* eslint-disable angular/angularelement */
/*@ngInject*/
-function ExpandFullscreen($compile, $document) {
+function ExpandFullscreen($compile, $document, $timeout) {
var uniqueId = 1;
var linker = function (scope, element, attrs) {
@@ -97,10 +97,6 @@ function ExpandFullscreen($compile, $document) {
scope.expanded = !scope.expanded;
}
- var expandButton = null;
- if (attrs.expandButtonId) {
- expandButton = $('#' + attrs.expandButtonId, element)[0];
- }
var buttonSize;
if (attrs.expandButtonSize) {
buttonSize = attrs.expandButtonSize;
@@ -115,27 +111,38 @@ function ExpandFullscreen($compile, $document) {
'options=\'{"easing": "circ-in-out", "duration": 375, "rotation": "none"}\'>' +
'';
- if (expandButton) {
- expandButton = angular.element(expandButton);
- if (scope.hideExpandButton()) {
- expandButton.remove();
- } else {
- expandButton.attr('md-ink-ripple', 'false');
- expandButton.append(html);
+ if (attrs.expandButtonId) {
+ $timeout(function() {
+ var expandButton = $('#' + attrs.expandButtonId, element)[0];
+ renderExpandButton(expandButton);
+ });
+ } else {
+ renderExpandButton();
+ }
+
+ function renderExpandButton(expandButton) {
+ if (expandButton) {
+ expandButton = angular.element(expandButton);
+ if (scope.hideExpandButton()) {
+ expandButton.remove();
+ } else {
+ expandButton.attr('md-ink-ripple', 'false');
+ expandButton.append(html);
- $compile(expandButton.contents())(scope);
+ $compile(expandButton.contents())(scope);
- expandButton.on("click", scope.toggleExpand);
- }
- } else if (!scope.hideExpandButton()) {
- var button = angular.element('' +
- html +
- '');
+ expandButton.on("click", scope.toggleExpand);
+ }
+ } else if (!scope.hideExpandButton()) {
+ var button = angular.element('' +
+ html +
+ '');
- $compile(button)(scope);
+ $compile(button)(scope);
- element.prepend(button);
+ element.prepend(button);
+ }
}
}
diff --git a/ui/src/app/dashboard/dashboard-settings.controller.js b/ui/src/app/dashboard/dashboard-settings.controller.js
index dbe4cbe992..ac987093e6 100644
--- a/ui/src/app/dashboard/dashboard-settings.controller.js
+++ b/ui/src/app/dashboard/dashboard-settings.controller.js
@@ -57,6 +57,10 @@ export default function DashboardSettingsController($scope, $mdDialog, statesCon
if (angular.isUndefined(vm.settings.showDashboardExport)) {
vm.settings.showDashboardExport = true;
}
+
+ if (angular.isUndefined(vm.settings.toolbarAlwaysOpen)) {
+ vm.settings.toolbarAlwaysOpen = false;
+ }
}
if (vm.gridSettings) {
diff --git a/ui/src/app/dashboard/dashboard-settings.tpl.html b/ui/src/app/dashboard/dashboard-settings.tpl.html
index 88fc66d651..f3ff704c60 100644
--- a/ui/src/app/dashboard/dashboard-settings.tpl.html
+++ b/ui/src/app/dashboard/dashboard-settings.tpl.html
@@ -41,6 +41,9 @@
+
{{ 'dashboard.toolbar-always-open' | translate }}
+
{{ 'dashboard.display-title' | translate }}
diff --git a/ui/src/app/dashboard/dashboard-toolbar.directive.js b/ui/src/app/dashboard/dashboard-toolbar.directive.js
new file mode 100644
index 0000000000..63a34f8918
--- /dev/null
+++ b/ui/src/app/dashboard/dashboard-toolbar.directive.js
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2016-2017 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 './dashboard-toolbar.scss';
+
+import 'javascript-detect-element-resize/detect-element-resize';
+
+/* eslint-disable import/no-unresolved, import/default */
+
+import dashboardToolbarTemplate from './dashboard-toolbar.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+/*@ngInject*/
+export default function DashboardToolbar() {
+ return {
+ restrict: "E",
+ scope: true,
+ transclude: true,
+ bindToController: {
+ toolbarOpened: '=',
+ forceFullscreen: '=',
+ onTriggerClick: '&'
+ },
+ controller: DashboardToolbarController,
+ controllerAs: 'vm',
+ templateUrl: dashboardToolbarTemplate
+ };
+}
+
+/* eslint-disable angular/angularelement */
+
+
+/*@ngInject*/
+function DashboardToolbarController($scope, $element, $timeout, mdFabToolbarAnimation) {
+
+ let vm = this;
+
+ vm.mdFabToolbarElement = angular.element($element[0].querySelector('md-fab-toolbar'));
+
+ $timeout(function() {
+ vm.mdFabBackgroundElement = angular.element(vm.mdFabToolbarElement[0].querySelector('.md-fab-toolbar-background'));
+ vm.mdFabTriggerElement = angular.element(vm.mdFabToolbarElement[0].querySelector('md-fab-trigger button'));
+ });
+
+ addResizeListener(vm.mdFabToolbarElement[0], triggerFabResize); // eslint-disable-line no-undef
+
+ $scope.$on("$destroy", function () {
+ removeResizeListener(vm.mdFabToolbarElement[0], triggerFabResize); // eslint-disable-line no-undef
+ });
+
+ function triggerFabResize() {
+ var ctrl = vm.mdFabToolbarElement.controller('mdFabToolbar');
+ if (ctrl.isOpen) {
+ if (!vm.mdFabBackgroundElement[0].offsetWidth) {
+ mdFabToolbarAnimation.addClass(vm.mdFabToolbarElement, 'md-is-open', function () {
+ });
+ } else {
+ var color = window.getComputedStyle(vm.mdFabTriggerElement[0]).getPropertyValue('background-color'); //eslint-disable-line
+
+ var width = vm.mdFabToolbarElement[0].offsetWidth;
+ var scale = 2 * (width / vm.mdFabTriggerElement[0].offsetWidth);
+ vm.mdFabBackgroundElement[0].style.backgroundColor = color;
+ vm.mdFabBackgroundElement[0].style.borderRadius = width + 'px';
+
+ var transform = vm.mdFabBackgroundElement[0].style.transform;
+ var targetTransform = 'scale(' + scale + ')';
+ if (!transform || !angular.equals(transform, targetTransform)) {
+ vm.mdFabBackgroundElement[0].style.transform = targetTransform;
+ }
+ }
+ }
+ }
+}
+
diff --git a/ui/src/app/dashboard/dashboard-toolbar.scss b/ui/src/app/dashboard/dashboard-toolbar.scss
new file mode 100644
index 0000000000..af4889ed03
--- /dev/null
+++ b/ui/src/app/dashboard/dashboard-toolbar.scss
@@ -0,0 +1,114 @@
+/**
+ * Copyright © 2016-2017 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 "~compass-sass-mixins/lib/compass";
+@import '../../scss/constants';
+
+tb-dashboard-toolbar {
+ md-fab-toolbar {
+ &.md-is-open {
+ md-fab-trigger {
+ .md-button {
+ &.md-fab {
+ opacity: 1;
+ @include transition(opacity .3s cubic-bezier(.55,0,.55,.2));
+ .md-fab-toolbar-background {
+ background-color: $primary-default !important;
+ }
+ }
+ }
+ }
+ }
+ md-fab-trigger {
+ .md-button {
+ &.md-fab {
+ line-height: 36px;
+ width: 36px;
+ height: 36px;
+ margin: 4px 0 0 4px;
+ opacity: 0.5;
+ @include transition(opacity .3s cubic-bezier(.55,0,.55,.2) .2s);
+ md-icon {
+ position: absolute;
+ top: 25%;
+ margin: 0;
+ line-height: 18px;
+ height: 18px;
+ width: 18px;
+ min-height: 18px;
+ min-width: 18px;
+ }
+ }
+ }
+ }
+ &.is-fullscreen {
+ &.md-is-open {
+ md-fab-trigger {
+ .md-button {
+ &.md-fab {
+ .md-fab-toolbar-background {
+ transition-delay: 0ms !important;
+ transition-duration: 0ms !important;
+ }
+ }
+ }
+ }
+ }
+ .md-fab-toolbar-wrapper {
+ height: 64px;
+ md-toolbar {
+ min-height: 64px;
+ height: 64px;
+ }
+ }
+ }
+ .md-fab-toolbar-wrapper {
+ height: 50px;
+ md-toolbar {
+ min-height: 50px;
+ height: 50px;
+ md-fab-actions {
+ font-size: 16px;
+ margin-top: 0px;
+ .close-action {
+ margin-right: -18px;
+ }
+ .md-fab-action-item {
+ width: 100%;
+ height: 46px;
+ .tb-dashboard-action-panels {
+ height: 46px;
+ flex-direction: row-reverse;
+ .tb-dashboard-action-panel {
+ height: 46px;
+ flex-direction: row-reverse;
+ div {
+ height: 46px;
+ }
+ md-select {
+ pointer-events: all;
+ }
+ tb-states-component {
+ pointer-events: all;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/src/app/dashboard/dashboard-toolbar.tpl.html b/ui/src/app/dashboard/dashboard-toolbar.tpl.html
new file mode 100644
index 0000000000..46192ffa7b
--- /dev/null
+++ b/ui/src/app/dashboard/dashboard-toolbar.tpl.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ {{ 'dashboard.open-toolbar' | translate }}
+
+ more_horiz
+
+
+
+
+
+
+
+
diff --git a/ui/src/app/dashboard/dashboard.controller.js b/ui/src/app/dashboard/dashboard.controller.js
index c446be43cd..ecf072d7a7 100644
--- a/ui/src/app/dashboard/dashboard.controller.js
+++ b/ui/src/app/dashboard/dashboard.controller.js
@@ -63,7 +63,9 @@ export default function DashboardController(types, dashboardUtils, widgetService
}
Object.defineProperty(vm, 'toolbarOpened', {
- get: function() { return !vm.widgetEditMode && ($scope.forceFullscreen || vm.isToolbarOpened || vm.isEdit || vm.showRightLayoutSwitch()); },
+ get: function() {
+ return !vm.widgetEditMode &&
+ (toolbarAlwaysOpen() || $scope.forceFullscreen || vm.isToolbarOpened || vm.isEdit || vm.showRightLayoutSwitch()); },
set: function() { }
});
@@ -103,9 +105,11 @@ export default function DashboardController(types, dashboardUtils, widgetService
}
vm.showCloseToolbar = function() {
- return !$scope.forceFullscreen && !vm.isEdit && !vm.showRightLayoutSwitch();
+ return !vm.toolbarAlwaysOpen() && !$scope.forceFullscreen && !vm.isEdit && !vm.showRightLayoutSwitch();
}
+ vm.toolbarAlwaysOpen = toolbarAlwaysOpen;
+
vm.showRightLayoutSwitch = function() {
return vm.isMobile && vm.layouts.right.show;
}
@@ -738,6 +742,15 @@ export default function DashboardController(types, dashboardUtils, widgetService
return link;
}
+ function toolbarAlwaysOpen() {
+ if (vm.dashboard && vm.dashboard.configuration.settings &&
+ angular.isDefined(vm.dashboard.configuration.settings.toolbarAlwaysOpen)) {
+ return vm.dashboard.configuration.settings.toolbarAlwaysOpen;
+ } else {
+ return false;
+ }
+ }
+
function displayTitle() {
if (vm.dashboard && vm.dashboard.configuration.settings &&
angular.isDefined(vm.dashboard.configuration.settings.showTitle)) {
diff --git a/ui/src/app/dashboard/dashboard.scss b/ui/src/app/dashboard/dashboard.scss
index bc5ec56a33..ca2bbabfc1 100644
--- a/ui/src/app/dashboard/dashboard.scss
+++ b/ui/src/app/dashboard/dashboard.scss
@@ -68,87 +68,23 @@ section.tb-dashboard-toolbar {
pointer-events: none;
&.tb-dashboard-toolbar-opened {
right: 0px;
- @include transition(right .3s cubic-bezier(.55,0,.55,.2));
+ // @include transition(right .3s cubic-bezier(.55,0,.55,.2));
}
&.tb-dashboard-toolbar-closed {
right: 18px;
@include transition(right .3s cubic-bezier(.55,0,.55,.2) .2s);
}
- md-fab-toolbar {
- &.md-is-open {
- md-fab-trigger {
- .md-button {
- &.md-fab {
- opacity: 1;
- @include transition(opacity .3s cubic-bezier(.55,0,.55,.2));
- }
- }
- }
- }
- md-fab-trigger {
- .md-button {
- &.md-fab {
- line-height: 36px;
- width: 36px;
- height: 36px;
- margin: 4px 0 0 4px;
- opacity: 0.5;
- @include transition(opacity .3s cubic-bezier(.55,0,.55,.2) .2s);
- md-icon {
- position: absolute;
- top: 25%;
- margin: 0;
- line-height: 18px;
- height: 18px;
- width: 18px;
- min-height: 18px;
- min-width: 18px;
- }
- }
- }
- }
- .md-fab-toolbar-wrapper {
- height: 50px;
- md-toolbar {
- min-height: 46px;
- height: 46px;
- md-fab-actions {
- font-size: 16px;
- margin-top: 0px;
- .close-action {
- margin-right: -18px;
- }
- .md-fab-action-item {
- width: 100%;
- height: 46px;
- .tb-dashboard-action-panels {
- height: 46px;
- flex-direction: row-reverse;
- .tb-dashboard-action-panel {
- height: 46px;
- flex-direction: row-reverse;
- div {
- height: 46px;
- }
- md-select {
- pointer-events: all;
- }
- tb-states-component {
- pointer-events: all;
- }
- }
- }
- }
- }
- }
- }
- }
}
.tb-dashboard-container {
&.tb-dashboard-toolbar-opened {
- margin-top: 50px;
- @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2));
+ &.is-fullscreen {
+ margin-top: 64px;
+ }
+ &:not(.is-fullscreen) {
+ margin-top: 50px;
+ @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2));
+ }
}
&.tb-dashboard-toolbar-closed {
margin-top: 0px;
diff --git a/ui/src/app/dashboard/dashboard.tpl.html b/ui/src/app/dashboard/dashboard.tpl.html
index 94e016eaa7..8338612484 100644
--- a/ui/src/app/dashboard/dashboard.tpl.html
+++ b/ui/src/app/dashboard/dashboard.tpl.html
@@ -19,110 +19,98 @@
hide-expand-button="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-tooltip-direction="bottom">