Commit e157f76fcf055594eadd9a73d3ec5e6913a022ab
1 parent
8daa3f99
add widgets dataKeys reorder by drag-and-drop dataKey chips in widget editor
Showing
6 changed files
with
197 additions
and
7 deletions
@@ -110,7 +110,11 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc | @@ -110,7 +110,11 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc | ||
110 | dataKeys = dataKeys.concat(scope.timeseriesDataKeys); | 110 | dataKeys = dataKeys.concat(scope.timeseriesDataKeys); |
111 | dataKeys = dataKeys.concat(scope.attributeDataKeys); | 111 | dataKeys = dataKeys.concat(scope.attributeDataKeys); |
112 | dataKeys = dataKeys.concat(scope.alarmDataKeys); | 112 | dataKeys = dataKeys.concat(scope.alarmDataKeys); |
113 | - ngModelCtrl.$viewValue.dataKeys = dataKeys; | 113 | + if (ngModelCtrl.$viewValue.dataKeys != dataKeys) |
114 | + { | ||
115 | + ngModelCtrl.$setDirty(); | ||
116 | + ngModelCtrl.$viewValue.dataKeys = dataKeys; | ||
117 | + } | ||
114 | scope.updateValidity(); | 118 | scope.updateValidity(); |
115 | } | 119 | } |
116 | } | 120 | } |
@@ -60,7 +60,7 @@ | @@ -60,7 +60,7 @@ | ||
60 | <div class="tb-color-result" ng-style="{background: $chip.color}"></div> | 60 | <div class="tb-color-result" ng-style="{background: $chip.color}"></div> |
61 | </div> | 61 | </div> |
62 | <div layout="row"> | 62 | <div layout="row"> |
63 | - <div class="tb-chip-label"> | 63 | + <div class="tb-chip-label" tb-chip-draggable> |
64 | {{$chip.label}} | 64 | {{$chip.label}} |
65 | </div> | 65 | </div> |
66 | <div class="tb-chip-separator">: </div> | 66 | <div class="tb-chip-separator">: </div> |
@@ -111,7 +111,7 @@ | @@ -111,7 +111,7 @@ | ||
111 | <div class="tb-color-result" ng-style="{background: $chip.color}"></div> | 111 | <div class="tb-color-result" ng-style="{background: $chip.color}"></div> |
112 | </div> | 112 | </div> |
113 | <div layout="row"> | 113 | <div layout="row"> |
114 | - <div class="tb-chip-label"> | 114 | + <div class="tb-chip-label" tb-chip-draggable> |
115 | {{$chip.label}} | 115 | {{$chip.label}} |
116 | </div> | 116 | </div> |
117 | <div class="tb-chip-separator">: </div> | 117 | <div class="tb-chip-separator">: </div> |
@@ -163,7 +163,7 @@ | @@ -163,7 +163,7 @@ | ||
163 | <div class="tb-color-result" ng-style="{background: $chip.color}"></div> | 163 | <div class="tb-color-result" ng-style="{background: $chip.color}"></div> |
164 | </div> | 164 | </div> |
165 | <div layout="row"> | 165 | <div layout="row"> |
166 | - <div class="tb-chip-label"> | 166 | + <div class="tb-chip-label" tb-chip-draggable> |
167 | {{$chip.label}} | 167 | {{$chip.label}} |
168 | </div> | 168 | </div> |
169 | <div class="tb-chip-separator">: </div> | 169 | <div class="tb-chip-separator">: </div> |
@@ -88,7 +88,11 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document, | @@ -88,7 +88,11 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document, | ||
88 | var dataKeys = []; | 88 | var dataKeys = []; |
89 | dataKeys = dataKeys.concat(scope.funcDataKeys); | 89 | dataKeys = dataKeys.concat(scope.funcDataKeys); |
90 | dataKeys = dataKeys.concat(scope.alarmDataKeys); | 90 | dataKeys = dataKeys.concat(scope.alarmDataKeys); |
91 | - ngModelCtrl.$viewValue.dataKeys = dataKeys; | 91 | + if (ngModelCtrl.$viewValue.dataKeys != dataKeys) |
92 | + { | ||
93 | + ngModelCtrl.$setDirty(); | ||
94 | + ngModelCtrl.$viewValue.dataKeys = dataKeys; | ||
95 | + } | ||
92 | scope.updateValidity(); | 96 | scope.updateValidity(); |
93 | } | 97 | } |
94 | } | 98 | } |
@@ -62,7 +62,7 @@ | @@ -62,7 +62,7 @@ | ||
62 | <div class="tb-color-result" ng-style="{background: $chip.color}"></div> | 62 | <div class="tb-color-result" ng-style="{background: $chip.color}"></div> |
63 | </div> | 63 | </div> |
64 | <div layout="row"> | 64 | <div layout="row"> |
65 | - <div class="tb-chip-label"> | 65 | + <div class="tb-chip-label" tb-chip-draggable> |
66 | {{$chip.label}} | 66 | {{$chip.label}} |
67 | </div> | 67 | </div> |
68 | <div class="tb-chip-separator">: </div> | 68 | <div class="tb-chip-separator">: </div> |
@@ -113,7 +113,7 @@ | @@ -113,7 +113,7 @@ | ||
113 | <div class="tb-color-result" ng-style="{background: $chip.color}"></div> | 113 | <div class="tb-color-result" ng-style="{background: $chip.color}"></div> |
114 | </div> | 114 | </div> |
115 | <div layout="row"> | 115 | <div layout="row"> |
116 | - <div class="tb-chip-label"> | 116 | + <div class="tb-chip-label" tb-chip-draggable> |
117 | {{$chip.label}} | 117 | {{$chip.label}} |
118 | </div> | 118 | </div> |
119 | <div class="tb-chip-separator">: </div> | 119 | <div class="tb-chip-separator">: </div> |
1 | +/* | ||
2 | + * Copyright © 2016-2019 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +export default angular.module('thingsboard.directives.mdChipDraggable', []) | ||
17 | + .directive('tbChipDraggable', function () { | ||
18 | + return { | ||
19 | + restrict: 'A', | ||
20 | + scope: {}, | ||
21 | + bindToController: true, | ||
22 | + controllerAs: 'vm', | ||
23 | + controller: ['$document', '$scope', '$element', '$timeout', | ||
24 | + function ($document, $scope, $element, $timeout) { | ||
25 | + var options = { | ||
26 | + axis: 'horizontal', | ||
27 | + }; | ||
28 | + var handle = $element[0]; | ||
29 | + var draggingClassName = 'dragging'; | ||
30 | + var droppingClassName = 'dropping'; | ||
31 | + var droppingBeforeClassName = 'dropping--before'; | ||
32 | + var droppingAfterClassName = 'dropping--after'; | ||
33 | + var dragging = false; | ||
34 | + var preventDrag = false; | ||
35 | + var dropPosition; | ||
36 | + var dropTimeout; | ||
37 | + | ||
38 | + var move = function (from, to) { | ||
39 | + this.splice(to, 0, this.splice(from, 1)[0]); | ||
40 | + }; | ||
41 | + | ||
42 | + $element = angular.element($element[0].closest('md-chip')); | ||
43 | + | ||
44 | + $element.attr('draggable', true); | ||
45 | + | ||
46 | + $element.on('mousedown', function (event) { | ||
47 | + if (event.target !== handle) { | ||
48 | + preventDrag = true; | ||
49 | + } | ||
50 | + }); | ||
51 | + | ||
52 | + $document.on('mouseup', function () { | ||
53 | + preventDrag = false; | ||
54 | + }); | ||
55 | + | ||
56 | + $element.on('dragstart', function (event) { | ||
57 | + if (preventDrag) { | ||
58 | + event.preventDefault(); | ||
59 | + | ||
60 | + } else { | ||
61 | + dragging = true; | ||
62 | + | ||
63 | + $element.addClass(draggingClassName); | ||
64 | + | ||
65 | + var dataTransfer = event.dataTransfer || event.originalEvent.dataTransfer; | ||
66 | + | ||
67 | + dataTransfer.effectAllowed = 'copyMove'; | ||
68 | + dataTransfer.dropEffect = 'move'; | ||
69 | + dataTransfer.setData('text/plain', $scope.$parent.$mdChipsCtrl.items.indexOf($scope.$parent.$chip)); | ||
70 | + } | ||
71 | + }); | ||
72 | + | ||
73 | + $element.on('dragend', function () { | ||
74 | + dragging = false; | ||
75 | + | ||
76 | + $element.removeClass(draggingClassName); | ||
77 | + }); | ||
78 | + | ||
79 | + var dragOverHandler = function (event) { | ||
80 | + if (dragging) { | ||
81 | + return; | ||
82 | + } | ||
83 | + | ||
84 | + event.preventDefault(); | ||
85 | + | ||
86 | + var bounds = $element[0].getBoundingClientRect(); | ||
87 | + | ||
88 | + var props = { | ||
89 | + width: bounds.right - bounds.left, | ||
90 | + height: bounds.bottom - bounds.top, | ||
91 | + x: (event.originalEvent || event).clientX - bounds.left, | ||
92 | + y: (event.originalEvent || event).clientY - bounds.top, | ||
93 | + }; | ||
94 | + | ||
95 | + var offset = options.axis === 'vertical' ? props.y : props.x; | ||
96 | + var midPoint = (options.axis === 'vertical' ? props.height : props.width) / 2; | ||
97 | + | ||
98 | + $element.addClass(droppingClassName); | ||
99 | + | ||
100 | + if (offset < midPoint) { | ||
101 | + dropPosition = 'before'; | ||
102 | + $element.removeClass(droppingAfterClassName); | ||
103 | + $element.addClass(droppingBeforeClassName); | ||
104 | + | ||
105 | + } else { | ||
106 | + dropPosition = 'after'; | ||
107 | + $element.removeClass(droppingBeforeClassName); | ||
108 | + $element.addClass(droppingAfterClassName); | ||
109 | + } | ||
110 | + }; | ||
111 | + | ||
112 | + var dropHandler = function (event) { | ||
113 | + event.preventDefault(); | ||
114 | + | ||
115 | + var droppedItemIndex = parseInt((event.dataTransfer || event.originalEvent.dataTransfer).getData('text/plain'), 10); | ||
116 | + var currentIndex = $scope.$parent.$mdChipsCtrl.items.indexOf($scope.$parent.$chip); | ||
117 | + var newIndex = null; | ||
118 | + | ||
119 | + if (dropPosition === 'before') { | ||
120 | + if (droppedItemIndex < currentIndex) { | ||
121 | + newIndex = currentIndex - 1; | ||
122 | + } else { | ||
123 | + newIndex = currentIndex; | ||
124 | + } | ||
125 | + } else { | ||
126 | + if (droppedItemIndex < currentIndex) { | ||
127 | + newIndex = currentIndex; | ||
128 | + } else { | ||
129 | + newIndex = currentIndex + 1; | ||
130 | + } | ||
131 | + } | ||
132 | + | ||
133 | + // prevent event firing multiple times in firefox | ||
134 | + $timeout.cancel(dropTimeout); | ||
135 | + dropTimeout = $timeout(function () { | ||
136 | + dropPosition = null; | ||
137 | + | ||
138 | + move.apply($scope.$parent.$mdChipsCtrl.items, [droppedItemIndex, newIndex]); | ||
139 | + | ||
140 | + $scope.$apply(function () { | ||
141 | + $scope.$emit('mdChipDraggable:change', { | ||
142 | + collection: $scope.$parent.$mdChipsCtrl.items, | ||
143 | + item: $scope.$parent.$mdChipsCtrl.items[droppedItemIndex], | ||
144 | + from: droppedItemIndex, | ||
145 | + to: newIndex, | ||
146 | + }); | ||
147 | + }); | ||
148 | + | ||
149 | + $element.removeClass(droppingClassName); | ||
150 | + $element.removeClass(droppingBeforeClassName); | ||
151 | + $element.removeClass(droppingAfterClassName); | ||
152 | + | ||
153 | + $element.off('drop', dropHandler); | ||
154 | + }, 1000 / 16); | ||
155 | + }; | ||
156 | + | ||
157 | + $element.on('dragenter', function () { | ||
158 | + if (dragging) { | ||
159 | + return; | ||
160 | + } | ||
161 | + | ||
162 | + $element.off('dragover', dragOverHandler); | ||
163 | + $element.off('drop', dropHandler); | ||
164 | + | ||
165 | + $element.on('dragover', dragOverHandler); | ||
166 | + $element.on('drop', dropHandler); | ||
167 | + }); | ||
168 | + | ||
169 | + $element.on('dragleave', function () { | ||
170 | + $element.removeClass(droppingClassName); | ||
171 | + $element.removeClass(droppingBeforeClassName); | ||
172 | + $element.removeClass(droppingAfterClassName); | ||
173 | + }); | ||
174 | + | ||
175 | + }], | ||
176 | + }; | ||
177 | + }) | ||
178 | + .name; | ||
179 | + | ||
180 | +/* eslint-enable angular/angularelement */ |
@@ -27,6 +27,7 @@ import thingsboardConfirmOnExit from '../components/confirm-on-exit.directive'; | @@ -27,6 +27,7 @@ import thingsboardConfirmOnExit from '../components/confirm-on-exit.directive'; | ||
27 | import thingsboardDashboard from '../components/dashboard.directive'; | 27 | import thingsboardDashboard from '../components/dashboard.directive'; |
28 | import thingsboardExpandFullscreen from '../components/expand-fullscreen.directive'; | 28 | import thingsboardExpandFullscreen from '../components/expand-fullscreen.directive'; |
29 | import thingsboardCircularProgress from '../components/circular-progress.directive'; | 29 | import thingsboardCircularProgress from '../components/circular-progress.directive'; |
30 | +import thingsboardMdChipDraggable from '../components/md-chip-draggable.directive'; | ||
30 | 31 | ||
31 | import WidgetLibraryRoutes from './widget-library.routes'; | 32 | import WidgetLibraryRoutes from './widget-library.routes'; |
32 | import WidgetLibraryController from './widget-library.controller'; | 33 | import WidgetLibraryController from './widget-library.controller'; |
@@ -46,6 +47,7 @@ export default angular.module('thingsboard.widget-library', [ | @@ -46,6 +47,7 @@ export default angular.module('thingsboard.widget-library', [ | ||
46 | thingsboardDashboard, | 47 | thingsboardDashboard, |
47 | thingsboardExpandFullscreen, | 48 | thingsboardExpandFullscreen, |
48 | thingsboardCircularProgress, | 49 | thingsboardCircularProgress, |
50 | + thingsboardMdChipDraggable, | ||
49 | 'cfp.hotkeys', | 51 | 'cfp.hotkeys', |
50 | 'ui.ace' | 52 | 'ui.ace' |
51 | ]) | 53 | ]) |