Browse Source

Merge branch 'features/widget_entity_datakeys_mixed_creation_order' of git://github.com/pgrisu/thingsboard into pgrisu-features/widget_entity_datakeys_mixed_creation_order

pull/1807/head
Igor Kulikov 7 years ago
parent
commit
e4e3b0450e
  1. 85
      ui/src/app/components/datasource-entity.directive.js
  2. 1
      ui/src/app/components/datasource-entity.scss
  3. 109
      ui/src/app/components/datasource-entity.tpl.html
  4. 6
      ui/src/app/components/datasource-func.directive.js
  5. 4
      ui/src/app/components/datasource-func.tpl.html
  6. 180
      ui/src/app/components/md-chip-draggable.directive.js
  7. 2
      ui/src/app/widget/index.js

85
ui/src/app/components/datasource-entity.directive.js

@ -50,11 +50,8 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
scope.alarmFields.push(alarmField);
}
scope.selectedTimeseriesDataKey = null;
scope.timeseriesDataKeySearchText = null;
scope.selectedAttributeDataKey = null;
scope.attributeDataKeySearchText = null;
scope.selectedDataKey = null;
scope.dataKeySearchText = null;
scope.selectedAlarmDataKey = null;
scope.alarmDataKeySearchText = null;
@ -92,11 +89,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
}
});
scope.$watch('timeseriesDataKeys', function () {
updateDataKeys();
}, true);
scope.$watch('attributeDataKeys', function () {
scope.$watch('dataKeys', function () {
updateDataKeys();
}, true);
@ -107,10 +100,13 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
function updateDataKeys() {
if (ngModelCtrl.$viewValue) {
var dataKeys = [];
dataKeys = dataKeys.concat(scope.timeseriesDataKeys);
dataKeys = dataKeys.concat(scope.attributeDataKeys);
dataKeys = dataKeys.concat(scope.dataKeys);
dataKeys = dataKeys.concat(scope.alarmDataKeys);
ngModelCtrl.$viewValue.dataKeys = dataKeys;
if (!angular.equals(ngModelCtrl.$viewValue.dataKeys, dataKeys))
{
ngModelCtrl.$setDirty();
ngModelCtrl.$viewValue.dataKeys = dataKeys;
}
scope.updateValidity();
}
}
@ -124,21 +120,17 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
} else {
scope.entityAlias = null;
}
var timeseriesDataKeys = [];
var attributeDataKeys = [];
var dataKeys = [];
var alarmDataKeys = [];
for (var d in ngModelCtrl.$viewValue.dataKeys) {
var dataKey = ngModelCtrl.$viewValue.dataKeys[d];
if (dataKey.type === types.dataKeyType.timeseries) {
timeseriesDataKeys.push(dataKey);
} else if (dataKey.type === types.dataKeyType.attribute) {
attributeDataKeys.push(dataKey);
if ((dataKey.type === types.dataKeyType.timeseries) || (dataKey.type === types.dataKeyType.attribute)) {
dataKeys.push(dataKey);
} else if (dataKey.type === types.dataKeyType.alarm) {
alarmDataKeys.push(dataKey);
}
}
scope.timeseriesDataKeys = timeseriesDataKeys;
scope.attributeDataKeys = attributeDataKeys;
scope.dataKeys = dataKeys;
scope.alarmDataKeys = alarmDataKeys;
}
};
@ -148,30 +140,19 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
}
scope.selectedEntityAliasChange = function () {
if (!scope.timeseriesDataKeySearchText || scope.timeseriesDataKeySearchText === '') {
scope.timeseriesDataKeySearchText = scope.timeseriesDataKeySearchText === '' ? null : '';
}
if (!scope.attributeDataKeySearchText || scope.attributeDataKeySearchText === '') {
scope.attributeDataKeySearchText = scope.attributeDataKeySearchText === '' ? null : '';
if (!scope.dataKeySearchText || scope.dataKeySearchText === '') {
scope.dataKeySearchText = scope.dataKeySearchText === '' ? null : '';
}
if (!scope.alarmDataKeySearchText || scope.alarmDataKeySearchText === '') {
scope.alarmDataKeySearchText = scope.alarmDataKeySearchText === '' ? null : '';
}
};
scope.transformTimeseriesDataKeyChip = function (chip) {
if (scope.maxDataKeys > 0 && ngModelCtrl.$viewValue.dataKeys.length >= scope.maxDataKeys ) {
return null;
} else {
return scope.generateDataKey({chip: chip, type: types.dataKeyType.timeseries});
}
};
scope.transformAttributeDataKeyChip = function (chip) {
scope.transformDataKeyChip = function (chip) {
if (scope.maxDataKeys > 0 && ngModelCtrl.$viewValue.dataKeys.length >= scope.maxDataKeys ) {
return null;
} else {
return scope.generateDataKey({chip: chip, type: types.dataKeyType.attribute});
return scope.generateDataKey({chip: chip.name, type: chip.type});
}
};
@ -226,10 +207,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 +217,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 +225,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 +261,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();
}

1
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 {

109
ui/src/app/components/datasource-entity.tpl.html

@ -25,82 +25,45 @@
<section flex layout='column'>
<section flex layout='column' layout-align="center" style="padding-left: 4px;">
<md-chips flex ng-if="widgetType != types.widgetType.alarm.value"
id="timeseries_datakey_chips"
ng-model="timeseriesDataKeys" md-autocomplete-snap
md-transform-chip="transformTimeseriesDataKeyChip($chip)"
md-require-match="false">
<md-autocomplete
md-no-cache="true"
id="timeseries_datakey"
md-selected-item="selectedTimeseriesDataKey"
md-search-text="timeseriesDataKeySearchText"
md-items="item in dataKeysSearch(timeseriesDataKeySearchText, types.dataKeyType.timeseries)"
md-item-text="item.name"
md-min-length="0"
placeholder="{{'datakey.timeseries' | translate }}"
md-menu-class="tb-timeseries-datakey-autocomplete">
<span md-highlight-text="timeseriesDataKeySearchText" md-highlight-flags="^i">{{item}}</span>
<md-not-found>
<div class="tb-not-found">
<div class="tb-no-entries" ng-if="!textIsNotEmpty(timeseriesDataKeySearchText)">
<span translate>entity.no-keys-found</span>
</div>
<div ng-if="textIsNotEmpty(timeseriesDataKeySearchText)">
<span translate translate-values='{ key: "{{timeseriesDataKeySearchText | truncate:true:6:&apos;...&apos;}}" }'>entity.no-key-matching</span>
<span>
<a translate ng-click="createKey($event, '#timeseries_datakey_chips')">entity.create-new-key</a>
</span>
</div>
</div>
</md-not-found>
</md-autocomplete>
<md-chip-template>
<div layout="row" layout-align="start center" class="tb-attribute-chip">
<div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
<div class="tb-color-result" ng-style="{background: $chip.color}"></div>
</div>
<div layout="row">
<div class="tb-chip-label">
{{$chip.label}}
</div>
<div class="tb-chip-separator">: </div>
<div class="tb-chip-label">
<strong ng-if="!$chip.postFuncBody">{{$chip.name}}</strong>
<strong ng-if="$chip.postFuncBody">f({{$chip.name}})</strong>
</div>
</div>
<md-button ng-click="editDataKey($event, $chip, $index)" class="md-icon-button tb-md-32">
<md-icon aria-label="edit" class="material-icons tb-md-20">edit</md-icon>
</md-button>
</div>
</md-chip-template>
</md-chips>
<md-chips flex ng-if="widgetType === types.widgetType.latest.value"
id="attribute_datakey_chips"
ng-model="attributeDataKeys" md-autocomplete-snap
md-transform-chip="transformAttributeDataKeyChip($chip)"
id="datakey_chips"
ng-model="dataKeys" md-autocomplete-snap
md-transform-chip="transformDataKeyChip($chip)"
md-require-match="false">
<md-autocomplete
md-no-cache="true"
id="attribute_datakey"
md-selected-item="selectedAttributeDataKey"
md-search-text="attributeDataKeySearchText"
md-items="item in dataKeysSearch(attributeDataKeySearchText, types.dataKeyType.attribute)"
id="datakey"
md-selected-item="selectedDataKey"
md-search-text="dataKeySearchText"
md-items="item in dataKeysSearch(dataKeySearchText)"
md-item-text="item.name"
md-min-length="0"
placeholder="{{'datakey.attributes' | translate }}"
md-menu-class="tb-attribute-datakey-autocomplete">
<span md-highlight-text="attributeDataKeySearchText" md-highlight-flags="^i">{{item}}</span>
placeholder=""
md-menu-class="tb-datakey-autocomplete">
<span ng-show="item.type==types.dataKeyType.attribute">
<md-tooltip>{{'datakey.attributes' | translate }}</md-tooltip>
<ng-md-icon size="16" icon="perm_device_information"></ng-md-icon>
</span>
<span ng-show="item.type==types.dataKeyType.timeseries">
<md-tooltip>{{'datakey.timeseries' | translate }}</md-tooltip>
<ng-md-icon size="16" icon="timeline"></ng-md-icon>
</span>
<span md-highlight-text="dataKeySearchText" md-highlight-flags="^i">{{item.name}}</span>
<md-not-found>
<div class="tb-not-found">
<div class="tb-no-entries" ng-if="!textIsNotEmpty(attributeDataKeySearchText)">
<div class="tb-no-entries" ng-if="!textIsNotEmpty(dataKeySearchText)">
<span translate>entity.no-keys-found</span>
</div>
<div ng-if="textIsNotEmpty(attributeDataKeySearchText)">
<span translate translate-values='{ key: "{{attributeDataKeySearchText | truncate:true:6:&apos;...&apos;}}" }'>entity.no-key-matching</span>
<span>
<a translate ng-click="createKey($event, '#attribute_datakey_chips')">entity.create-new-key</a>
<div ng-if="textIsNotEmpty(dataKeySearchText)">
<span translate translate-values='{ key: "{{dataKeySearchText | truncate:true:6:&apos;...&apos;}}" }'>entity.no-key-matching</span>
<span>{{'entity.create-new-key' | translate }} </span>
<span ng-show="widgetType == types.widgetType.latest.value">
<md-tooltip>{{'datakey.attributes' | translate }}</md-tooltip>
<ng-md-icon size="16" icon="perm_device_information" ng-click="createKey($event, types.dataKeyType.attribute, '#datakey_chips')"></ng-md-icon>
</span>
<span>
<md-tooltip>{{'datakey.timeseries' | translate }}</md-tooltip>
<ng-md-icon size="16" icon="timeline" ng-click="createKey($event, types.dataKeyType.timeseries, '#datakey_chips')"></ng-md-icon>
</span>
</div>
</div>
</md-not-found>
@ -111,7 +74,15 @@
<div class="tb-color-result" ng-style="{background: $chip.color}"></div>
</div>
<div layout="row">
<div class="tb-chip-label">
<div class="tb-chip-label" tb-chip-draggable>
<span ng-show="$chip.type==types.dataKeyType.attribute">
<md-tooltip>{{'datakey.attributes' | translate }}</md-tooltip>
<ng-md-icon size="20" icon="perm_device_information"></ng-md-icon>
</span>
<span ng-show="$chip.type==types.dataKeyType.timeseries">
<md-tooltip>{{'datakey.timeseries' | translate }}</md-tooltip>
<ng-md-icon size="20" icon="timeline"></ng-md-icon>
</span>
{{$chip.label}}
</div>
<div class="tb-chip-separator">: </div>
@ -137,7 +108,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 }}"
@ -163,7 +134,7 @@
<div class="tb-color-result" ng-style="{background: $chip.color}"></div>
</div>
<div layout="row">
<div class="tb-chip-label">
<div class="tb-chip-label" tb-chip-draggable>
{{$chip.label}}
</div>
<div class="tb-chip-separator">: </div>

6
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();
}
}

4
ui/src/app/components/datasource-func.tpl.html

@ -62,7 +62,7 @@
<div class="tb-color-result" ng-style="{background: $chip.color}"></div>
</div>
<div layout="row">
<div class="tb-chip-label">
<div class="tb-chip-label" tb-chip-draggable>
{{$chip.label}}
</div>
<div class="tb-chip-separator">: </div>
@ -113,7 +113,7 @@
<div class="tb-color-result" ng-style="{background: $chip.color}"></div>
</div>
<div layout="row">
<div class="tb-chip-label">
<div class="tb-chip-label" tb-chip-draggable>
{{$chip.label}}
</div>
<div class="tb-chip-separator">: </div>

180
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 */

2
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'
])

Loading…
Cancel
Save