diff --git a/ui/src/app/components/datasource-entity.directive.js b/ui/src/app/components/datasource-entity.directive.js index b65ffd32ea..2b8f2ec0c5 100644 --- a/ui/src/app/components/datasource-entity.directive.js +++ b/ui/src/app/components/datasource-entity.directive.js @@ -110,7 +110,11 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc dataKeys = dataKeys.concat(scope.timeseriesDataKeys); dataKeys = dataKeys.concat(scope.attributeDataKeys); dataKeys = dataKeys.concat(scope.alarmDataKeys); - ngModelCtrl.$viewValue.dataKeys = dataKeys; + if (ngModelCtrl.$viewValue.dataKeys != dataKeys) + { + ngModelCtrl.$setDirty(); + ngModelCtrl.$viewValue.dataKeys = dataKeys; + } scope.updateValidity(); } } diff --git a/ui/src/app/components/datasource-entity.tpl.html b/ui/src/app/components/datasource-entity.tpl.html index c954e83608..128bf8305b 100644 --- a/ui/src/app/components/datasource-entity.tpl.html +++ b/ui/src/app/components/datasource-entity.tpl.html @@ -60,7 +60,7 @@
-
+
{{$chip.label}}
:
@@ -111,7 +111,7 @@
-
+
{{$chip.label}}
:
@@ -163,7 +163,7 @@
-
+
{{$chip.label}}
:
diff --git a/ui/src/app/components/datasource-func.directive.js b/ui/src/app/components/datasource-func.directive.js index 357dc2140a..c42cb9344e 100644 --- a/ui/src/app/components/datasource-func.directive.js +++ b/ui/src/app/components/datasource-func.directive.js @@ -88,7 +88,11 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document, var dataKeys = []; dataKeys = dataKeys.concat(scope.funcDataKeys); dataKeys = dataKeys.concat(scope.alarmDataKeys); - ngModelCtrl.$viewValue.dataKeys = dataKeys; + if (ngModelCtrl.$viewValue.dataKeys != dataKeys) + { + ngModelCtrl.$setDirty(); + ngModelCtrl.$viewValue.dataKeys = dataKeys; + } scope.updateValidity(); } } diff --git a/ui/src/app/components/datasource-func.tpl.html b/ui/src/app/components/datasource-func.tpl.html index c0e681b662..289570fd77 100644 --- a/ui/src/app/components/datasource-func.tpl.html +++ b/ui/src/app/components/datasource-func.tpl.html @@ -62,7 +62,7 @@
-
+
{{$chip.label}}
:
@@ -113,7 +113,7 @@
-
+
{{$chip.label}}
:
diff --git a/ui/src/app/components/md-chip-draggable.directive.js b/ui/src/app/components/md-chip-draggable.directive.js new file mode 100644 index 0000000000..8fcdc0ad73 --- /dev/null +++ b/ui/src/app/components/md-chip-draggable.directive.js @@ -0,0 +1,180 @@ +/* + * Copyright © 2016-2019 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.mdChipDraggable', []) + .directive('tbChipDraggable', function () { + return { + restrict: 'A', + scope: {}, + bindToController: true, + controllerAs: 'vm', + controller: ['$document', '$scope', '$element', '$timeout', + function ($document, $scope, $element, $timeout) { + var options = { + axis: 'horizontal', + }; + var handle = $element[0]; + var draggingClassName = 'dragging'; + var droppingClassName = 'dropping'; + var droppingBeforeClassName = 'dropping--before'; + var droppingAfterClassName = 'dropping--after'; + var dragging = false; + var preventDrag = false; + var dropPosition; + var dropTimeout; + + var move = function (from, to) { + this.splice(to, 0, this.splice(from, 1)[0]); + }; + + $element = angular.element($element[0].closest('md-chip')); + + $element.attr('draggable', true); + + $element.on('mousedown', function (event) { + if (event.target !== handle) { + preventDrag = true; + } + }); + + $document.on('mouseup', function () { + preventDrag = false; + }); + + $element.on('dragstart', function (event) { + if (preventDrag) { + event.preventDefault(); + + } else { + dragging = true; + + $element.addClass(draggingClassName); + + var dataTransfer = event.dataTransfer || event.originalEvent.dataTransfer; + + dataTransfer.effectAllowed = 'copyMove'; + dataTransfer.dropEffect = 'move'; + dataTransfer.setData('text/plain', $scope.$parent.$mdChipsCtrl.items.indexOf($scope.$parent.$chip)); + } + }); + + $element.on('dragend', function () { + dragging = false; + + $element.removeClass(draggingClassName); + }); + + var dragOverHandler = function (event) { + if (dragging) { + return; + } + + event.preventDefault(); + + var bounds = $element[0].getBoundingClientRect(); + + var props = { + width: bounds.right - bounds.left, + height: bounds.bottom - bounds.top, + x: (event.originalEvent || event).clientX - bounds.left, + y: (event.originalEvent || event).clientY - bounds.top, + }; + + var offset = options.axis === 'vertical' ? props.y : props.x; + var midPoint = (options.axis === 'vertical' ? props.height : props.width) / 2; + + $element.addClass(droppingClassName); + + if (offset < midPoint) { + dropPosition = 'before'; + $element.removeClass(droppingAfterClassName); + $element.addClass(droppingBeforeClassName); + + } else { + dropPosition = 'after'; + $element.removeClass(droppingBeforeClassName); + $element.addClass(droppingAfterClassName); + } + }; + + var dropHandler = function (event) { + event.preventDefault(); + + var droppedItemIndex = parseInt((event.dataTransfer || event.originalEvent.dataTransfer).getData('text/plain'), 10); + var currentIndex = $scope.$parent.$mdChipsCtrl.items.indexOf($scope.$parent.$chip); + var newIndex = null; + + if (dropPosition === 'before') { + if (droppedItemIndex < currentIndex) { + newIndex = currentIndex - 1; + } else { + newIndex = currentIndex; + } + } else { + if (droppedItemIndex < currentIndex) { + newIndex = currentIndex; + } else { + newIndex = currentIndex + 1; + } + } + + // prevent event firing multiple times in firefox + $timeout.cancel(dropTimeout); + dropTimeout = $timeout(function () { + dropPosition = null; + + move.apply($scope.$parent.$mdChipsCtrl.items, [droppedItemIndex, newIndex]); + + $scope.$apply(function () { + $scope.$emit('mdChipDraggable:change', { + collection: $scope.$parent.$mdChipsCtrl.items, + item: $scope.$parent.$mdChipsCtrl.items[droppedItemIndex], + from: droppedItemIndex, + to: newIndex, + }); + }); + + $element.removeClass(droppingClassName); + $element.removeClass(droppingBeforeClassName); + $element.removeClass(droppingAfterClassName); + + $element.off('drop', dropHandler); + }, 1000 / 16); + }; + + $element.on('dragenter', function () { + if (dragging) { + return; + } + + $element.off('dragover', dragOverHandler); + $element.off('drop', dropHandler); + + $element.on('dragover', dragOverHandler); + $element.on('drop', dropHandler); + }); + + $element.on('dragleave', function () { + $element.removeClass(droppingClassName); + $element.removeClass(droppingBeforeClassName); + $element.removeClass(droppingAfterClassName); + }); + + }], + }; + }) + .name; + +/* eslint-enable angular/angularelement */ diff --git a/ui/src/app/widget/index.js b/ui/src/app/widget/index.js index 41d85a8032..71c1edc0ab 100644 --- a/ui/src/app/widget/index.js +++ b/ui/src/app/widget/index.js @@ -27,6 +27,7 @@ import thingsboardConfirmOnExit from '../components/confirm-on-exit.directive'; import thingsboardDashboard from '../components/dashboard.directive'; import thingsboardExpandFullscreen from '../components/expand-fullscreen.directive'; import thingsboardCircularProgress from '../components/circular-progress.directive'; +import thingsboardMdChipDraggable from '../components/md-chip-draggable.directive'; import WidgetLibraryRoutes from './widget-library.routes'; import WidgetLibraryController from './widget-library.controller'; @@ -46,6 +47,7 @@ export default angular.module('thingsboard.widget-library', [ thingsboardDashboard, thingsboardExpandFullscreen, thingsboardCircularProgress, + thingsboardMdChipDraggable, 'cfp.hotkeys', 'ui.ace' ])