diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js index f140893d2b..7f05481f0e 100644 --- a/ui/src/app/common/utils.service.js +++ b/ui/src/app/common/utils.service.js @@ -120,18 +120,6 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t ]; var defaultAlarmDataKeys = []; - for (var i=0;i 0 && ngModelCtrl.$viewValue.dataKeys.length >= scope.maxDataKeys ) { return null; } else { - return scope.generateDataKey({chip: chip, type: types.dataKeyType.timeseries}); - } - }; - - scope.transformAttributeDataKeyChip = function (chip) { - if (scope.maxDataKeys > 0 && ngModelCtrl.$viewValue.dataKeys.length >= scope.maxDataKeys ) { - return null; - } else { - return scope.generateDataKey({chip: chip, type: types.dataKeyType.attribute}); + if (chip.type) { + return scope.generateDataKey({chip: chip.name, type: chip.type}); + } else { + if (scope.widgetType != types.widgetType.latest.value) { + return scope.generateDataKey({chip: chip, type: types.dataKeyType.timeseries}); + } else { + return null; + } + } } }; @@ -226,10 +215,8 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc w.triggerHandler('resize'); } }).then(function (dataKey) { - if (dataKey.type === types.dataKeyType.timeseries) { - scope.timeseriesDataKeys[index] = dataKey; - } else if (dataKey.type === types.dataKeyType.attribute) { - scope.attributeDataKeys[index] = dataKey; + if ((dataKey.type === types.dataKeyType.timeseries) || (dataKey.type === types.dataKeyType.attribute)) { + scope.dataKeys[index] = dataKey; } else if (dataKey.type === types.dataKeyType.alarm) { scope.alarmDataKeys[index] = dataKey; } @@ -238,7 +225,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc }); }; - scope.dataKeysSearch = function (searchText, type) { + scope.dataKeysSearch = function (searchText) { if (scope.widgetType == types.widgetType.alarm.value) { var dataKeys = searchText ? scope.alarmFields.filter( scope.createFilterForDataKey(searchText)) : scope.alarmFields; @@ -246,9 +233,25 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc } else { if (scope.entityAlias) { var deferred = $q.defer(); - scope.fetchEntityKeys({entityAliasId: scope.entityAlias.id, query: searchText, type: type}) + scope.fetchEntityKeys({entityAliasId: scope.entityAlias.id, query: searchText, type: types.dataKeyType.timeseries}) .then(function (dataKeys) { - deferred.resolve(dataKeys); + var items = []; + for (var i = 0; i < dataKeys.length; i++) { + items.push({ name: dataKeys[i], type: types.dataKeyType.timeseries }); + } + if (scope.widgetType == types.widgetType.latest.value) { + scope.fetchEntityKeys({entityAliasId: scope.entityAlias.id, query: searchText, type: types.dataKeyType.attribute}) + .then(function (dataKeys) { + for (var i = 0; i < dataKeys.length; i++) { + items.push({ name: dataKeys[i], type: types.dataKeyType.attribute }); + } + deferred.resolve(items); + }, function (e) { + deferred.reject(e); + }); + } + else + deferred.resolve(items); }, function (e) { deferred.reject(e); }); @@ -266,13 +269,13 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc }; }; - scope.createKey = function (event, chipsId) { + scope.createKey = function (event, type, chipsId) { var chipsChild = $(chipsId, element)[0].firstElementChild; var el = angular.element(chipsChild); var chipBuffer = el.scope().$mdChipsCtrl.getChipBuffer(); event.preventDefault(); event.stopPropagation(); - el.scope().$mdChipsCtrl.appendChip(chipBuffer.trim()); + el.scope().$mdChipsCtrl.appendChip({ name: chipBuffer.trim(), type: type}); el.scope().$mdChipsCtrl.resetChipBuffer(); } diff --git a/ui/src/app/components/datasource-entity.scss b/ui/src/app/components/datasource-entity.scss index ef4fcd0545..75a2eed632 100644 --- a/ui/src/app/components/datasource-entity.scss +++ b/ui/src/app/components/datasource-entity.scss @@ -16,6 +16,7 @@ @import "../../scss/constants"; .tb-entity-alias-autocomplete, +.tb-datakey-autocomplete, .tb-timeseries-datakey-autocomplete, .tb-attribute-datakey-autocomplete, .tb-alarm-datakey-autocomplete { diff --git a/ui/src/app/components/datasource-entity.tpl.html b/ui/src/app/components/datasource-entity.tpl.html index c954e83608..b609daa361 100644 --- a/ui/src/app/components/datasource-entity.tpl.html +++ b/ui/src/app/components/datasource-entity.tpl.html @@ -25,94 +25,68 @@
- - {{item}} - -
-
- entity.no-keys-found -
-
- entity.no-key-matching - - entity.create-new-key - -
-
-
-
- -
-
-
-
-
-
- {{$chip.label}} -
-
:
-
- {{$chip.name}} - f({{$chip.name}}) -
-
- - edit - -
-
-
- - {{item}} + placeholder="" + md-menu-class="tb-datakey-autocomplete"> + + {{'datakey.attributes' | translate }} + + + + {{'datakey.timeseries' | translate }} + + + {{item.name}}
-
+
entity.no-keys-found
-
- entity.no-key-matching - - entity.create-new-key +
+ entity.no-key-matching + {{'entity.create-new-key' | translate }} + + {{'datakey.attributes' | translate }} + + + {{'datakey.timeseries' | translate }} + +
+
+ +
- {{$chip.label}} + + {{'datakey.attributes' | translate }} + + + + {{'datakey.timeseries' | translate }} + + + {{$chip.label}}
:
@@ -137,7 +111,7 @@ id="alarm_datakey" md-selected-item="selectedAlarmDataKey" md-search-text="alarmDataKeySearchText" - md-items="item in dataKeysSearch(alarmDataKeySearchText, types.dataKeyType.alarm)" + md-items="item in dataKeysSearch(alarmDataKeySearchText)" md-item-text="item.name" md-min-length="0" placeholder="{{'datakey.alarm' | translate }}" @@ -159,6 +133,9 @@
+
+ +
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..5cae48df77 100644 --- a/ui/src/app/components/datasource-func.tpl.html +++ b/ui/src/app/components/datasource-func.tpl.html @@ -58,7 +58,10 @@
-
+
+ +
+
@@ -109,7 +112,10 @@
-
+
+ +
+
diff --git a/ui/src/app/components/datasource.scss b/ui/src/app/components/datasource.scss index 217e73f6aa..8cd552ff47 100644 --- a/ui/src/app/components/datasource.scss +++ b/ui/src/app/components/datasource.scss @@ -58,6 +58,14 @@ } .tb-attribute-chip { + .tb-chip-drag-handle { + cursor: move; + + ng-md-icon { + pointer-events: none; + } + } + .tb-chip-label { overflow: hidden; text-overflow: ellipsis; 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..9ac63fdefc --- /dev/null +++ b/ui/src/app/components/md-chip-draggable.directive.js @@ -0,0 +1,179 @@ +/* + * 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 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 horizontalOffset = props.x; + var horizontalMidPoint = props.width / 2; + + var verticalOffset = props.y; + var verticalMidPoint = props.height / 2; + + $element.addClass(droppingClassName); + + if (horizontalOffset >= horizontalMidPoint || verticalOffset >= verticalMidPoint) { + dropPosition = 'after'; + $element.removeClass(droppingBeforeClassName); + $element.addClass(droppingAfterClassName); + } else { + dropPosition = 'before'; + $element.removeClass(droppingAfterClassName); + $element.addClass(droppingBeforeClassName); + } + + }; + + 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/components/widget/widget-config.tpl.html b/ui/src/app/components/widget/widget-config.tpl.html index c4db5386dc..0998ad9378 100644 --- a/ui/src/app/components/widget/widget-config.tpl.html +++ b/ui/src/app/components/widget/widget-config.tpl.html @@ -23,12 +23,14 @@
- {{ 'widget-config.use-dashboard-timewindow' | translate }} - - {{ 'widget-config.display-timewindow' | translate }} - +
+ {{ 'widget-config.use-dashboard-timewindow' | translate }} + + {{ 'widget-config.display-timewindow' | translate }} + +
widget-config.timewindow