Merge branch 'develop/1.5' of github.com:thingsboard/thingsboard into develop/1.5

This commit is contained in:
Andrew Shvayka 2018-03-26 16:58:36 +03:00
commit bb0533c528
7 changed files with 201 additions and 95 deletions

View File

@ -18,17 +18,17 @@ export default angular.module('thingsboard.directives.confirmOnExit', [])
.name; .name;
/*@ngInject*/ /*@ngInject*/
function ConfirmOnExit($state, $mdDialog, $window, $filter, userService) { function ConfirmOnExit($state, $mdDialog, $window, $filter, $parse, userService) {
return { return {
link: function ($scope) { link: function ($scope, $element, $attributes) {
$scope.confirmForm = $scope.$eval($attributes.confirmForm);
$window.onbeforeunload = function () { $window.onbeforeunload = function () {
if (userService.isAuthenticated() && (($scope.confirmForm && $scope.confirmForm.$dirty) || $scope.isDirty)) { if (userService.isAuthenticated() && (($scope.confirmForm && $scope.confirmForm.$dirty) || $scope.$eval($attributes.isDirty))) {
return $filter('translate')('confirm-on-exit.message'); return $filter('translate')('confirm-on-exit.message');
} }
} }
$scope.$on('$stateChangeStart', function (event, next, current, params) { $scope.$on('$stateChangeStart', function (event, next, current, params) {
if (userService.isAuthenticated() && (($scope.confirmForm && $scope.confirmForm.$dirty) || $scope.isDirty)) { if (userService.isAuthenticated() && (($scope.confirmForm && $scope.confirmForm.$dirty) || $scope.$eval($attributes.isDirty))) {
event.preventDefault(); event.preventDefault();
var confirm = $mdDialog.confirm() var confirm = $mdDialog.confirm()
.title($filter('translate')('confirm-on-exit.title')) .title($filter('translate')('confirm-on-exit.title'))
@ -40,7 +40,9 @@ function ConfirmOnExit($state, $mdDialog, $window, $filter, userService) {
if ($scope.confirmForm) { if ($scope.confirmForm) {
$scope.confirmForm.$setPristine(); $scope.confirmForm.$setPristine();
} else { } else {
$scope.isDirty = false; var remoteSetter = $parse($attributes.isDirty).assign;
remoteSetter($scope, false);
//$scope.isDirty = false;
} }
$state.go(next.name, params); $state.go(next.name, params);
}, function () { }, function () {
@ -48,9 +50,6 @@ function ConfirmOnExit($state, $mdDialog, $window, $filter, userService) {
} }
}); });
}, },
scope: { scope: false
confirmForm: '=',
isDirty: '='
}
}; };
} }

View File

@ -1177,6 +1177,9 @@ export default angular.module('thingsboard.locale', [])
"type": "Type", "type": "Type",
"description": "Description", "description": "Description",
"delete": "Delete rule node", "delete": "Delete rule node",
"select-all": "Select all nodes and connections",
"deselect-all": "Deselect all nodes and connections",
"delete-selected-objects": "Delete selected nodes and connections",
"rulenode-details": "Rule node details", "rulenode-details": "Rule node details",
"debug-mode": "Debug mode", "debug-mode": "Debug mode",
"configuration": "Configuration", "configuration": "Configuration",

View File

@ -27,15 +27,10 @@ import addRuleNodeLinkTemplate from './add-link.tpl.html';
/* eslint-enable import/no-unresolved, import/default */ /* eslint-enable import/no-unresolved, import/default */
const deleteKeyCode = 46;
const ctrlKeyCode = 17;
const aKeyCode = 65;
const escKeyCode = 27;
/*@ngInject*/ /*@ngInject*/
export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, $timeout, $mdExpansionPanel, $document, $mdDialog, export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, $timeout, $mdExpansionPanel, $document, $mdDialog,
$filter, $translate, types, ruleChainService, Modelfactory, flowchartConstants, ruleChain, ruleChainMetaData) { $filter, $translate, hotkeys, types, ruleChainService, Modelfactory, flowchartConstants,
ruleChain, ruleChainMetaData, ruleNodeComponents) {
var vm = this; var vm = this;
@ -76,39 +71,62 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
vm.modelservice = Modelfactory(vm.ruleChainModel, vm.selectedObjects); vm.modelservice = Modelfactory(vm.ruleChainModel, vm.selectedObjects);
vm.ctrlDown = false;
vm.saveRuleChain = saveRuleChain; vm.saveRuleChain = saveRuleChain;
vm.revertRuleChain = revertRuleChain; vm.revertRuleChain = revertRuleChain;
vm.keyDown = function (evt) { vm.objectsSelected = objectsSelected;
if (evt.keyCode === ctrlKeyCode) { vm.deleteSelected = deleteSelected;
vm.ctrlDown = true;
evt.stopPropagation();
evt.preventDefault();
}
};
vm.keyUp = function (evt) { initHotKeys();
if (evt.keyCode === deleteKeyCode) { function initHotKeys() {
vm.modelservice.deleteSelected(); hotkeys.bindTo($scope)
} .add({
combo: 'ctrl+a',
if (evt.keyCode == aKeyCode && vm.ctrlDown) { description: $translate.instant('rulenode.select-all'),
vm.modelservice.selectAll(); allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
} callback: function (event) {
event.preventDefault();
if (evt.keyCode == escKeyCode) { vm.modelservice.selectAll();
vm.modelservice.deselectAll(); }
} })
.add({
if (evt.keyCode === ctrlKeyCode) { combo: 'esc',
vm.ctrlDown = false; description: $translate.instant('rulenode.deselect-all'),
evt.stopPropagation(); allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
evt.preventDefault(); callback: function (event) {
} event.preventDefault();
}; vm.modelservice.deselectAll();
}
})
.add({
combo: 'ctrl+s',
description: $translate.instant('action.apply'),
allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
callback: function (event) {
event.preventDefault();
vm.saveRuleChain();
}
})
.add({
combo: 'ctrl+z',
description: $translate.instant('action.decline-changes'),
allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
callback: function (event) {
event.preventDefault();
vm.revertRuleChain();
}
})
.add({
combo: 'del',
description: $translate.instant('rulenode.delete-selected-objects'),
allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
callback: function (event) {
event.preventDefault();
vm.modelservice.deleteSelected();
}
})
}
vm.onEditRuleNodeClosed = function() { vm.onEditRuleNodeClosed = function() {
vm.editingRuleNode = null; vm.editingRuleNode = null;
@ -286,44 +304,40 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
loadRuleChainLibrary(); loadRuleChainLibrary();
function loadRuleChainLibrary() { function loadRuleChainLibrary() {
ruleChainService.getRuleNodeComponents().then( for (var i=0;i<ruleNodeComponents.length;i++) {
(ruleNodeComponents) => { var ruleNodeComponent = ruleNodeComponents[i];
for (var i=0;i<ruleNodeComponents.length;i++) { var componentType = ruleNodeComponent.type;
var ruleNodeComponent = ruleNodeComponents[i]; var model = vm.ruleNodeTypesModel[componentType].model;
var componentType = ruleNodeComponent.type; var node = {
var model = vm.ruleNodeTypesModel[componentType].model; id: model.nodes.length,
var node = { component: ruleNodeComponent,
id: model.nodes.length, name: '',
component: ruleNodeComponent, nodeClass: vm.types.ruleNodeType[componentType].nodeClass,
name: '', icon: vm.types.ruleNodeType[componentType].icon,
nodeClass: vm.types.ruleNodeType[componentType].nodeClass, x: 30,
icon: vm.types.ruleNodeType[componentType].icon, y: 10+50*model.nodes.length,
x: 30, connectors: []
y: 10+50*model.nodes.length, };
connectors: [] if (ruleNodeComponent.configurationDescriptor.nodeDefinition.inEnabled) {
}; node.connectors.push(
if (ruleNodeComponent.configurationDescriptor.nodeDefinition.inEnabled) { {
node.connectors.push( type: flowchartConstants.leftConnectorType,
{ id: model.nodes.length * 2
type: flowchartConstants.leftConnectorType,
id: model.nodes.length * 2
}
);
} }
if (ruleNodeComponent.configurationDescriptor.nodeDefinition.outEnabled) { );
node.connectors.push(
{
type: flowchartConstants.rightConnectorType,
id: model.nodes.length * 2 + 1
}
);
}
model.nodes.push(node);
}
vm.ruleChainLibraryLoaded = true;
prepareRuleChain();
} }
); if (ruleNodeComponent.configurationDescriptor.nodeDefinition.outEnabled) {
node.connectors.push(
{
type: flowchartConstants.rightConnectorType,
id: model.nodes.length * 2 + 1
}
);
}
model.nodes.push(node);
}
vm.ruleChainLibraryLoaded = true;
prepareRuleChain();
} }
function prepareRuleChain() { function prepareRuleChain() {
@ -632,6 +646,14 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
}); });
} }
function objectsSelected() {
return vm.modelservice.nodes.getSelectedNodes().length > 0 ||
vm.modelservice.edges.getSelectedEdges().length > 0
}
function deleteSelected() {
vm.modelservice.deleteSelected();
}
} }
/*@ngInject*/ /*@ngInject*/

View File

@ -68,6 +68,11 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
/*@ngInject*/ /*@ngInject*/
function($stateParams, ruleChainService) { function($stateParams, ruleChainService) {
return ruleChainService.getRuleChainMetaData($stateParams.ruleChainId); return ruleChainService.getRuleChainMetaData($stateParams.ruleChainId);
},
ruleNodeComponents:
/*@ngInject*/
function($stateParams, ruleChainService) {
return ruleChainService.getRuleNodeComponents();
} }
}, },
data: { data: {

View File

@ -75,6 +75,7 @@
padding: 5px 10px; padding: 5px 10px;
border-radius: 5px; border-radius: 5px;
background-color: #F15B26; background-color: #F15B26;
pointer-events: none;
color: #333; color: #333;
border: solid 1px #777; border: solid 1px #777;
font-size: 12px; font-size: 12px;
@ -121,10 +122,6 @@
.fc-node { .fc-node {
z-index: 1; z-index: 1;
outline: none; outline: none;
&.fc-hover, &.fc-selected {
-webkit-filter: brightness(70%);
filter: brightness(70%);
}
&.fc-dragging { &.fc-dragging {
z-index: 10; z-index: 10;
} }
@ -132,6 +129,26 @@
padding: 0 15px; padding: 0 15px;
text-align: center; text-align: center;
} }
.fc-node-overlay {
position: absolute;
pointer-events: none;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: #000;
opacity: 0;
}
&.fc-hover {
.fc-node-overlay {
opacity: 0.25;
}
}
&.fc-selected {
.fc-node-overlay {
opacity: 0.25;
}
}
} }
.fc-leftConnectors, .fc-rightConnectors { .fc-leftConnectors, .fc-rightConnectors {
@ -170,17 +187,33 @@
margin: 10px; margin: 10px;
border-radius: 5px; border-radius: 5px;
background-color: #ccc; background-color: #ccc;
pointer-events: all;
} }
.fc-connector.fc-hover { .fc-connector.fc-hover {
background-color: #000; background-color: #000;
} }
.fc-arrow-marker {
polygon {
stroke: gray;
fill: gray;
}
}
.fc-arrow-marker-selected {
polygon {
stroke: red;
fill: red;
}
}
.fc-edge { .fc-edge {
outline: none; outline: none;
stroke: gray; stroke: gray;
stroke-width: 4; stroke-width: 4;
fill: transparent; fill: transparent;
transition: stroke-width .2s;
&.fc-selected { &.fc-selected {
stroke: red; stroke: red;
stroke-width: 4; stroke-width: 4;
@ -229,24 +262,53 @@
cursor: pointer; cursor: pointer;
} }
.fc-noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
}
.fc-edge-label { .fc-edge-label {
position: absolute; position: absolute;
user-select: none; transition: transform .2s;
pointer-events: none;
opacity: 0.8; opacity: 0.8;
&.ng-leave {
transition: 0s none;
}
&.fc-hover {
transform: scale(1.25);
}
&.fc-selected {
.fc-edge-label-text {
span {
border: solid red;
color: red;
}
}
}
.fc-nodedelete {
right: -13px;
top: -30px;
}
&:focus {
outline: 0;
}
} }
.fc-edge-label-text { .fc-edge-label-text {
position: absolute; position: absolute;
left: 50%; -webkit-transform: translate(-50%, -50%);
-webkit-transform: translateX(-50%); transform: translate(-50%, -50%);
transform: translateX(-50%);
white-space: nowrap; white-space: nowrap;
text-align: center; text-align: center;
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
top: 5px;
span { span {
cursor: default;
border: solid 2px #003a79; border: solid 2px #003a79;
border-radius: 10px; border-radius: 10px;
color: #003a79; color: #003a79;
@ -255,6 +317,13 @@
} }
} }
.fc-select-rectangle {
border: 2px dashed #5262ff;
position: absolute;
background: rgba(20,125,255,0.1);
z-index: 2;
}
@keyframes dash { @keyframes dash {
from { from {
stroke-dashoffset: 500; stroke-dashoffset: 500;

View File

@ -16,8 +16,10 @@
--> -->
<md-content flex tb-expand-fullscreen <md-content flex tb-expand-fullscreen tb-confirm-on-exit is-dirty="vm.isDirty"
expand-tooltip-direction="bottom" layout="column" class="tb-rulechain"> expand-tooltip-direction="bottom" layout="column" class="tb-rulechain"
ng-keydown="vm.keyDown($event)"
ng-keyup="vm.keyUp($event)">
<section class="tb-rulechain-container" flex layout="column"> <section class="tb-rulechain-container" flex layout="column">
<div class="tb-rulechain-layout" flex layout="row"> <div class="tb-rulechain-layout" flex layout="row">
<div class="tb-rulechain-library"> <div class="tb-rulechain-library">
@ -50,8 +52,6 @@
</div> </div>
<div flex class="tb-rulechain-graph"> <div flex class="tb-rulechain-graph">
<fc-canvas id="tb-rulchain-canvas" <fc-canvas id="tb-rulchain-canvas"
ng-keydown="vm.keyDown($event)"
ng-keyup="vm.keyUp($event)"
model="vm.ruleChainModel" model="vm.ruleChainModel"
selected-objects="vm.selectedObjects" selected-objects="vm.selectedObjects"
edge-style="curved" edge-style="curved"
@ -112,6 +112,13 @@
</tb-details-sidenav> </tb-details-sidenav>
</section> </section>
<section layout="row" layout-wrap class="tb-footer-buttons md-fab" layout-align="start end"> <section layout="row" layout-wrap class="tb-footer-buttons md-fab" layout-align="start end">
<md-button ng-disabled="$root.loading" ng-show="vm.objectsSelected()" class="tb-btn-footer md-accent md-hue-2 md-fab"
ng-click="vm.deleteSelected()" aria-label="{{ 'action.delete' | translate }}">
<md-tooltip md-direction="top">
{{ 'rulenode.delete-selected-objects' | translate }}
</md-tooltip>
<ng-md-icon icon="delete"></ng-md-icon>
</md-button>
<md-button ng-disabled="$root.loading || !vm.isDirty" <md-button ng-disabled="$root.loading || !vm.isDirty"
class="tb-btn-footer md-accent md-hue-2 md-fab" class="tb-btn-footer md-accent md-hue-2 md-fab"
aria-label="{{ 'action.apply' | translate }}" aria-label="{{ 'action.apply' | translate }}"

View File

@ -22,6 +22,7 @@
ng-mousedown="callbacks.mouseDown($event, node)" ng-mousedown="callbacks.mouseDown($event, node)"
ng-mouseenter="callbacks.mouseEnter($event, node)" ng-mouseenter="callbacks.mouseEnter($event, node)"
ng-mouseleave="callbacks.mouseLeave($event, node)"> ng-mouseleave="callbacks.mouseLeave($event, node)">
<div class="{{flowchartConstants.nodeOverlayClass}}"></div>
<div class="tb-rule-node {{node.nodeClass}}"> <div class="tb-rule-node {{node.nodeClass}}">
<md-icon aria-label="node-type-icon" flex="15" <md-icon aria-label="node-type-icon" flex="15"
class="material-icons">{{node.icon}}</md-icon> class="material-icons">{{node.icon}}</md-icon>