Commit a51c44e5dbc90b4975ded1a9f73a4560d120a507
Committed by
Igor Kulikov
1 parent
1059c21e
Multiple attributes input widget improvement (#2144)
* Extend multiple input widget * Update multiple attributes widget * Change bundle, minor changes * Fix disablefor date input
Showing
4 changed files
with
326 additions
and
217 deletions
@@ -319,9 +319,9 @@ | @@ -319,9 +319,9 @@ | ||
319 | "resources": [], | 319 | "resources": [], |
320 | "templateHtml": "<tb-multiple-input-widget \n form-id=\"formId\"\n ctx=\"ctx\">\n</tb-multiple-input-widget>", | 320 | "templateHtml": "<tb-multiple-input-widget \n form-id=\"formId\"\n ctx=\"ctx\">\n</tb-multiple-input-widget>", |
321 | "templateCss": "", | 321 | "templateCss": "", |
322 | - "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet toast;\r\nlet utils;\r\nlet types;\r\n\r\nself.onInit = function() {\r\n var scope = self.ctx.$scope;\r\n var id = self.ctx.$scope.$injector.get('utils').guid();\r\n scope.formId = \"form-\"+id;\r\n scope.ctx = self.ctx;\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n self.ctx.$scope.$broadcast('multiple-input-data-updated', self.ctx.$scope.formId);\r\n}\r\n", | ||
323 | - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"MultipleInput\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Multiple input title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"attributesShared\": {\n \"title\": \"Attributes are 'shared' (default value is 'server')\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\":true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"attributesShared\",\n \"showResultMessage\"\n ]\n}", | ||
324 | - "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"readOnly\": {\n \"title\": \"Value is read only\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"inputTypeNumber\": {\n \"title\": \"Datakey is a number\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"step\": {\n \"title\": \"Step interval between valid values (only for numbers)\",\n \"type\": \"number\",\n \"default\": \"1\"\n },\n \"icon\": {\n \"title\": \"Icon to show before input cell\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"readOnly\",\n \"inputTypeNumber\",\n \"step\",\n\t\t{\n \t\t\"key\": \"icon\",\n\t\t\t\"type\": \"icon\"\n\t\t},\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n }\n ]\n}\n", | 322 | + "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet toast;\r\nlet utils;\r\nlet types;\r\n\r\nself.onInit = function() {\r\n var scope = self.ctx.$scope;\r\n var id = self.ctx.$scope.$injector.get('utils').guid();\r\n scope.formId = \"form-\"+id;\r\n scope.ctx = self.ctx;\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n self.ctx.$scope.$broadcast('multiple-input-data-updated', self.ctx.$scope.formId);\r\n}\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1\r\n }\r\n}\r\n\r\nself.onResize = function() {\r\n self.ctx.$scope.$broadcast('multiple-input-resize', self.ctx.$scope.formId);\r\n}\r\n", |
323 | + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"MultipleInput\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showActionButtons\":{\n \"title\":\"Show action buttons\",\n \"type\":\"boolean\",\n \"default\": true\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\": true\n },\n \"fieldsAlignment\": {\n \"title\": \"Fields alignment\",\n \"type\": \"string\",\n \"default\": \"row\"\n },\n \"fieldsInRow\": {\n \"title\": \"Number of fields in the row\",\n \"type\": \"number\",\n \"default\": \"2\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"showActionButtons\",\n \"showResultMessage\",\n {\n \"key\": \"fieldsAlignment\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"row\",\n \"label\": \"Row (default)\"\n },\n {\n \"value\": \"column\",\n \"label\": \"Column\"\n }\n ]\n },\n \"fieldsInRow\"\n ]\n}", | ||
324 | + "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"dataKeyType\": {\n \"title\": \"Datakey type\",\n \"type\": \"string\",\n \"default\": \"server\"\n },\n \"dataKeyValueType\": {\n \"title\": \"Datakey value type\",\n \"type\": \"string\",\n \"default\": \"string\"\n },\n \"required\": {\n \"title\": \"Value is required\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"isEditable\": {\n \"title\": \"Ability to edit attribute\",\n \"type\": \"string\",\n \"default\": \"editable\"\n },\n \"disabledOnDataKey\": {\n \"title\": \"Disable on false value of another datakey (specify datakey name)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"dataKeyHidden\": {\n \"title\": \"Hide input field\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"step\": {\n \"title\": \"Step interval between values (only for numbers)\",\n \"type\": \"number\",\n \"default\": \"1\"\n },\n \"requiredErrorMessage\": {\n \"title\": \"'Required' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"icon\": {\n \"title\": \"Icon to show before input cell\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"dataKeyType\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"server\",\n \"label\": \"Server attribute (default)\"\n },\n {\n \"value\": \"shared\",\n \"label\": \"Shared attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Timeseries\"\n }\n ]\n },\n {\n \"key\": \"dataKeyValueType\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"string\",\n \"label\": \"String\"\n },\n {\n \"value\": \"double\",\n \"label\": \"Double\"\n },\n {\n \"value\": \"integer\",\n \"label\": \"Integer\"\n },\n {\n \"value\": \"booleanCheckbox\",\n \"label\": \"Boolean (Checkbox)\"\n },\n {\n \"value\": \"booleanSwitch\",\n \"label\": \"Boolean (Switch)\"\n },\n {\n \"value\": \"dateTime\",\n \"label\": \"Date & Time\"\n },\n {\n \"value\": \"date\",\n \"label\": \"Date\"\n },\n {\n \"value\": \"time\",\n \"label\": \"Time\"\n }\n ]\n },\n \"required\",\n {\n \"key\": \"isEditable\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"editable\",\n \"label\": \"Editable (default)\"\n },\n {\n \"value\": \"disabled\",\n \"label\": \"Disabled\"\n },\n {\n \"value\": \"readonly\",\n \"label\": \"Read-only\"\n }\n ]\n },\n \"disabledOnDataKey\",\n \"dataKeyHidden\",\n \"step\",\n \"requiredErrorMessage\",\n\t\t{\n \t\t\"key\": \"icon\",\n\t\t\t\"type\": \"icon\"\n\t\t}\n ]\n}\n", | ||
325 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update Multiple Attributes\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" | 325 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update Multiple Attributes\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" |
326 | } | 326 | } |
327 | }, | 327 | }, |
@@ -41,25 +41,18 @@ function MultipleInputWidget() { | @@ -41,25 +41,18 @@ function MultipleInputWidget() { | ||
41 | } | 41 | } |
42 | 42 | ||
43 | /*@ngInject*/ | 43 | /*@ngInject*/ |
44 | -function MultipleInputWidgetController($q, $scope, attributeService, toast, types, utils) { | 44 | +function MultipleInputWidgetController($q, $scope, $translate, attributeService, toast, types, utils) { |
45 | var vm = this; | 45 | var vm = this; |
46 | 46 | ||
47 | - vm.dataKeyDetected = false; | ||
48 | - vm.hasAnyChange = false; | ||
49 | vm.entityDetected = false; | 47 | vm.entityDetected = false; |
50 | - vm.isValidParameter = true; | ||
51 | - vm.message = 'No entity selected'; | ||
52 | - | ||
53 | - vm.rows = []; | ||
54 | - vm.rowIndex = 0; | 48 | + vm.isAllParametersValid = true; |
55 | 49 | ||
50 | + vm.data = []; | ||
56 | vm.datasources = null; | 51 | vm.datasources = null; |
57 | 52 | ||
58 | - vm.cellStyle = cellStyle; | ||
59 | - vm.textColor = textColor; | ||
60 | vm.discardAll = discardAll; | 53 | vm.discardAll = discardAll; |
61 | vm.inputChanged = inputChanged; | 54 | vm.inputChanged = inputChanged; |
62 | - vm.postData = postData; | 55 | + vm.save = save; |
63 | 56 | ||
64 | $scope.$watch('vm.ctx', function() { | 57 | $scope.$watch('vm.ctx', function() { |
65 | if (vm.ctx && vm.ctx.defaultSubscription) { | 58 | if (vm.ctx && vm.ctx.defaultSubscription) { |
@@ -74,127 +67,109 @@ function MultipleInputWidgetController($q, $scope, attributeService, toast, type | @@ -74,127 +67,109 @@ function MultipleInputWidgetController($q, $scope, attributeService, toast, type | ||
74 | 67 | ||
75 | $scope.$on('multiple-input-data-updated', function(event, formId) { | 68 | $scope.$on('multiple-input-data-updated', function(event, formId) { |
76 | if (vm.formId == formId) { | 69 | if (vm.formId == formId) { |
77 | - updateRowData(vm.subscription.data); | 70 | + updateWidgetData(vm.subscription.data); |
78 | $scope.$digest(); | 71 | $scope.$digest(); |
79 | } | 72 | } |
80 | }); | 73 | }); |
81 | 74 | ||
82 | - function defaultStyle() { | ||
83 | - return {}; | ||
84 | - } | ||
85 | - | ||
86 | - function cellStyle(key, rowIndex, firstKey, lastKey) { | ||
87 | - var style = {}; | ||
88 | - if (key) { | ||
89 | - var styleInfo = vm.stylesInfo[key.label]; | ||
90 | - var value = key.currentValue; | ||
91 | - if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) { | ||
92 | - try { | ||
93 | - style = styleInfo.cellStyleFunction(value); | ||
94 | - } catch (e) { | ||
95 | - style = {}; | ||
96 | - } | ||
97 | - } else { | ||
98 | - style = defaultStyle(); | ||
99 | - } | ||
100 | - } | ||
101 | - if (vm.settings.rowMargin) { | ||
102 | - if (angular.isUndefined(style.marginTop) && rowIndex != 0) { | ||
103 | - style.marginTop = (vm.settings.rowMargin / 2) + 'px'; | ||
104 | - } | ||
105 | - if (angular.isUndefined(style.marginBottom)) { | ||
106 | - style.marginBottom = (vm.settings.rowMargin / 2) + 'px'; | ||
107 | - } | ||
108 | - } | ||
109 | - if (vm.settings.columnMargin) { | ||
110 | - if (angular.isUndefined(style.marginLeft) && !firstKey) { | ||
111 | - style.marginLeft = (vm.settings.columnMargin / 2) + 'px'; | ||
112 | - } | ||
113 | - if (angular.isUndefined(style.marginRight) && !lastKey) { | ||
114 | - style.marginRight = (vm.settings.columnMargin / 2) + 'px'; | ||
115 | - } | ||
116 | - } | ||
117 | - return style; | ||
118 | - } | ||
119 | - | ||
120 | - function textColor(key) { | ||
121 | - var style = {}; | ||
122 | - if (key) { | ||
123 | - var styleInfo = vm.stylesInfo[key.label]; | ||
124 | - if (styleInfo.color) { | ||
125 | - style = { color: styleInfo.color }; | ||
126 | - } | 75 | + $scope.$on('multiple-input-resize', function(event, formId) { |
76 | + if (vm.formId == formId) { | ||
77 | + updateWidgetDisplaying(); | ||
127 | } | 78 | } |
128 | - return style; | ||
129 | - } | 79 | + }); |
130 | 80 | ||
131 | function discardAll() { | 81 | function discardAll() { |
132 | - for (var r = 0; r < vm.rows.length; r++) { | ||
133 | - var row = vm.rows[r]; | ||
134 | - for (var d = 0; d < row.data.length; d++ ) { | ||
135 | - row.data[d].currentValue = row.data[d].originalValue; | ||
136 | - } | 82 | + for (var i = 0; i < vm.data.length; i++) { |
83 | + vm.data[i].data.currentValue = vm.data[i].data.originalValue; | ||
137 | } | 84 | } |
138 | - vm.hasAnyChange = false; | 85 | + $scope.multipleInputForm.$setPristine(); |
139 | } | 86 | } |
140 | 87 | ||
141 | - function inputChanged() { | ||
142 | - var newValue = false; | ||
143 | - for (var r = 0; r < vm.rows.length; r++) { | ||
144 | - var row = vm.rows[r]; | ||
145 | - for (var d = 0; d < row.data.length; d++ ) { | ||
146 | - if (!row.data[d].currentValue) { | ||
147 | - return; | ||
148 | - } | ||
149 | - if (row.data[d].currentValue !== row.data[d].originalValue) { | ||
150 | - newValue = true; | ||
151 | - } | 88 | + function inputChanged(key) { |
89 | + if (!vm.settings.showActionButtons) { | ||
90 | + if (!key.settings.required || (key.settings.required && key.data && angular.isDefined(key.data.currentValue))) { | ||
91 | + vm.save(key); | ||
152 | } | 92 | } |
153 | } | 93 | } |
154 | - vm.hasAnyChange = newValue; | ||
155 | } | 94 | } |
156 | 95 | ||
157 | - function postData() { | ||
158 | - var promises = []; | ||
159 | - for (var r = 0; r < vm.rows.length; r++) { | ||
160 | - var row = vm.rows[r]; | ||
161 | - var datasource = row.datasource; | ||
162 | - var attributes = []; | ||
163 | - var newValues = false; | ||
164 | - | ||
165 | - for (var d = 0; d < row.data.length; d++ ) { | ||
166 | - if (row.data[d].currentValue !== row.data[d].originalValue) { | ||
167 | - attributes.push({ | ||
168 | - key : row.data[d].name, | ||
169 | - value : row.data[d].currentValue, | ||
170 | - }); | ||
171 | - newValues = true; | 96 | + function save(key) { |
97 | + var tasks = []; | ||
98 | + var serverAttributes = [], sharedAttributes = [], telemetry = []; | ||
99 | + var config = { | ||
100 | + ignoreLoading: !vm.settings.showActionButtons | ||
101 | + }; | ||
102 | + var data; | ||
103 | + if (key) { | ||
104 | + data = [key]; | ||
105 | + } else { | ||
106 | + data = vm.data; | ||
107 | + } | ||
108 | + for (let i = 0; i < data.length; i++) { | ||
109 | + var item = data[i]; | ||
110 | + if (item.data.currentValue !== item.data.originalValue) { | ||
111 | + var attribute = { | ||
112 | + key: item.name | ||
113 | + }; | ||
114 | + switch (item.settings.dataKeyValueType) { | ||
115 | + case 'dateTime': | ||
116 | + case 'date': | ||
117 | + attribute.value = item.data.currentValue.getTime(); | ||
118 | + break; | ||
119 | + case 'time': | ||
120 | + attribute.value = item.data.currentValue.getTime() - moment().startOf('day').valueOf();//eslint-disable-line | ||
121 | + break; | ||
122 | + default: | ||
123 | + attribute.value = item.data.currentValue; | ||
172 | } | 124 | } |
173 | - } | ||
174 | 125 | ||
175 | - if (newValues) { | ||
176 | - promises.push(attributeService.saveEntityAttributes( | ||
177 | - datasource.entityType, | ||
178 | - datasource.entityId, | ||
179 | - vm.attributeScope, | ||
180 | - attributes)); | 126 | + switch (item.settings.dataKeyType) { |
127 | + case 'shared': | ||
128 | + sharedAttributes.push(attribute); | ||
129 | + break; | ||
130 | + case 'timeseries': | ||
131 | + telemetry.push(attribute); | ||
132 | + break; | ||
133 | + default: | ||
134 | + serverAttributes.push(attribute); | ||
135 | + } | ||
181 | } | 136 | } |
182 | } | 137 | } |
183 | - | ||
184 | - if (promises.length) { | ||
185 | - $q.all(promises).then( | 138 | + for (let i = 0; i < serverAttributes.length; i++) { |
139 | + tasks.push(attributeService.saveEntityAttributes( | ||
140 | + vm.datasources[0].entityType, | ||
141 | + vm.datasources[0].entityId, | ||
142 | + types.attributesScope.server.value, | ||
143 | + serverAttributes, | ||
144 | + config)); | ||
145 | + } | ||
146 | + for (let i = 0; i < sharedAttributes.length; i++) { | ||
147 | + tasks.push(attributeService.saveEntityAttributes( | ||
148 | + vm.datasources[0].entityType, | ||
149 | + vm.datasources[0].entityId, | ||
150 | + types.attributesScope.shared.value, | ||
151 | + sharedAttributes, | ||
152 | + config)); | ||
153 | + } | ||
154 | + for (let i = 0; i < telemetry.length; i++) { | ||
155 | + tasks.push(attributeService.saveEntityTimeseries( | ||
156 | + vm.datasources[0].entityType, | ||
157 | + vm.datasources[0].entityId, | ||
158 | + types.latestTelemetry.value, | ||
159 | + telemetry, | ||
160 | + config)); | ||
161 | + } | ||
162 | + if (tasks.length) { | ||
163 | + $q.all(tasks).then( | ||
186 | function success() { | 164 | function success() { |
187 | - for (var d = 0; d < row.data.length; d++ ) { | ||
188 | - row.data[d].originalValue = row.data[d].currentValue; | ||
189 | - } | ||
190 | - vm.hasAnyChange = false; | 165 | + $scope.multipleInputForm.$setPristine(); |
191 | if (vm.settings.showResultMessage) { | 166 | if (vm.settings.showResultMessage) { |
192 | - toast.showSuccess('Update successful', 1000, angular.element(vm.ctx.$container), 'bottom left'); | 167 | + toast.showSuccess($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(vm.ctx.$container), 'bottom left'); |
193 | } | 168 | } |
194 | }, | 169 | }, |
195 | function fail() { | 170 | function fail() { |
196 | if (vm.settings.showResultMessage) { | 171 | if (vm.settings.showResultMessage) { |
197 | - toast.showError('Update failed', angular.element(vm.ctx.$container), 'bottom left'); | 172 | + toast.showError($translate.instant('widgets.input-widgets.update-failed'), angular.element(vm.ctx.$container), 'bottom left'); |
198 | } | 173 | } |
199 | } | 174 | } |
200 | ); | 175 | ); |
@@ -211,78 +186,75 @@ function MultipleInputWidgetController($q, $scope, attributeService, toast, type | @@ -211,78 +186,75 @@ function MultipleInputWidgetController($q, $scope, attributeService, toast, type | ||
211 | 186 | ||
212 | vm.ctx.widgetTitle = vm.widgetTitle; | 187 | vm.ctx.widgetTitle = vm.widgetTitle; |
213 | 188 | ||
214 | - vm.attributeScope = vm.settings.attributesShared ? types.attributesScope.shared.value : types.attributesScope.server.value; | 189 | + vm.isVerticalAlignment = !(vm.settings.fieldsAlignment === 'row'); |
190 | + | ||
191 | + if (!vm.isVerticalAlignment && vm.settings.fieldsInRow) { | ||
192 | + vm.inputWidthSettings = 100 / vm.settings.fieldsInRow + '%'; | ||
193 | + } | ||
215 | } | 194 | } |
216 | 195 | ||
217 | function updateDatasources() { | 196 | function updateDatasources() { |
197 | + if (vm.datasources && vm.datasources.length) { | ||
198 | + var datasource = vm.datasources[0]; | ||
199 | + if (datasource.type === types.datasourceType.entity) { | ||
200 | + for (var i = 0; i < datasource.dataKeys.length; i++) { | ||
201 | + if ((datasource.entityType !== types.entityType.device) && (datasource.dataKeys[i].settings.dataKeyType !== 'server')) { | ||
202 | + vm.isAllParametersValid = false; | ||
203 | + } | ||
204 | + vm.data.push(datasource.dataKeys[i]); | ||
205 | + vm.data[i].data = {}; | ||
206 | + } | ||
207 | + vm.entityDetected = true; | ||
208 | + } | ||
209 | + } | ||
210 | + } | ||
218 | 211 | ||
219 | - vm.stylesInfo = {}; | ||
220 | - vm.rows = []; | ||
221 | - vm.rowIndex = 0; | ||
222 | - | ||
223 | - if (vm.datasources) { | ||
224 | - vm.entityDetected = true; | ||
225 | - for (var ds = 0; ds < vm.datasources.length; ds++) { | ||
226 | - var row = {}; | ||
227 | - var datasource = vm.datasources[ds]; | ||
228 | - row.datasource = datasource; | ||
229 | - row.data = []; | ||
230 | - if (datasource.dataKeys) { | ||
231 | - vm.dataKeyDetected = true; | ||
232 | - for (var a = 0; a < datasource.dataKeys.length; a++ ) { | ||
233 | - var dataKey = datasource.dataKeys[a]; | ||
234 | - | ||
235 | - if (dataKey.units) { | ||
236 | - dataKey.label += ' (' + dataKey.units + ')'; | ||
237 | - } | ||
238 | - | ||
239 | - var keySettings = dataKey.settings; | ||
240 | - if (keySettings.inputTypeNumber) { | ||
241 | - keySettings.inputType = 'number'; | ||
242 | - } else { | ||
243 | - keySettings.inputType = 'text'; | ||
244 | - } | 212 | + function updateWidgetData(data) { |
213 | + for (var i = 0; i < vm.data.length; i++) { | ||
214 | + var keyData = data[i].data; | ||
215 | + if (keyData && keyData.length) { | ||
216 | + var value; | ||
217 | + switch (vm.data[i].settings.dataKeyValueType) { | ||
218 | + case 'dateTime': | ||
219 | + case 'date': | ||
220 | + value = moment(keyData[0][1]).toDate(); // eslint-disable-line | ||
221 | + break; | ||
222 | + case 'time': | ||
223 | + value = moment().startOf('day').add(keyData[0][1], 'ms').toDate(); // eslint-disable-line | ||
224 | + break; | ||
225 | + case 'booleanCheckbox': | ||
226 | + case 'booleanSwitch': | ||
227 | + value = (keyData[0][1] === 'true'); | ||
228 | + break; | ||
229 | + default: | ||
230 | + value = keyData[0][1]; | ||
231 | + } | ||
245 | 232 | ||
246 | - var cellStyleFunction = null; | ||
247 | - var useCellStyleFunction = false; | 233 | + vm.data[i].data = { |
234 | + currentValue: value, | ||
235 | + originalValue: value | ||
236 | + }; | ||
248 | 237 | ||
249 | - if (keySettings.useCellStyleFunction === true) { | ||
250 | - if (angular.isDefined(keySettings.cellStyleFunction) && keySettings.cellStyleFunction.length > 0) { | ||
251 | - try { | ||
252 | - cellStyleFunction = new Function('value', keySettings.cellStyleFunction); | ||
253 | - useCellStyleFunction = true; | ||
254 | - } catch (e) { | ||
255 | - cellStyleFunction = null; | ||
256 | - useCellStyleFunction = false; | ||
257 | - } | 238 | + if (vm.data[i].settings.isEditable === 'editable' && vm.data[i].settings.disabledOnDataKey) { |
239 | + var conditions = data.filter((item) => { | ||
240 | + return item.dataKey.name === vm.data[i].settings.disabledOnDataKey; | ||
241 | + }); | ||
242 | + if (conditions && conditions.length) { | ||
243 | + if (conditions[0].data.length) { | ||
244 | + if (conditions[0].data[0][1] === 'false') { | ||
245 | + vm.data[i].settings.disabledOnCondition = true; | ||
246 | + } else { | ||
247 | + vm.data[i].settings.disabledOnCondition = !conditions[0].data[0][1]; | ||
258 | } | 248 | } |
259 | } | 249 | } |
260 | - | ||
261 | - vm.stylesInfo[dataKey.label] = { | ||
262 | - useCellStyleFunction: useCellStyleFunction, | ||
263 | - cellStyleFunction: cellStyleFunction, | ||
264 | - color: keySettings.color | ||
265 | - }; | ||
266 | - | ||
267 | - row.data.push(dataKey); | ||
268 | } | 250 | } |
269 | - vm.rows.push(row); | ||
270 | } | 251 | } |
271 | } | 252 | } |
272 | } | 253 | } |
273 | } | 254 | } |
274 | 255 | ||
275 | - function updateRowData(data) { | ||
276 | - var dataIndex = 0; | ||
277 | - for (var r = 0; r < vm.rows.length; r++) { | ||
278 | - var row = vm.rows[r]; | ||
279 | - for (var d = 0; d < row.data.length; d++ ) { | ||
280 | - var keyData = data[dataIndex++].data; | ||
281 | - if (keyData && keyData.length && keyData[0].length > 1) { | ||
282 | - row.data[d].currentValue = row.data[d].originalValue = keyData[0][1]; | ||
283 | - } | ||
284 | - } | ||
285 | - } | 256 | + function updateWidgetDisplaying() { |
257 | + vm.changeAlignment = (vm.ctx.$container[0].offsetWidth < 620); | ||
258 | + vm.smallWidthContainer = (vm.ctx.$container[0].offsetWidth < 420); | ||
286 | } | 259 | } |
287 | - | ||
288 | } | 260 | } |
@@ -15,15 +15,25 @@ | @@ -15,15 +15,25 @@ | ||
15 | */ | 15 | */ |
16 | .tb-multiple-input { | 16 | .tb-multiple-input { |
17 | height: 100%; | 17 | height: 100%; |
18 | + overflow-x: hidden; | ||
19 | + overflow-y: auto; | ||
18 | 20 | ||
19 | - .md-button.md-icon-button { | ||
20 | - width: 32px; | ||
21 | - min-width: 32px; | ||
22 | - height: 32px; | ||
23 | - min-height: 32px; | ||
24 | - padding: 0 !important; | ||
25 | - margin: 0; | ||
26 | - line-height: 20px; | 21 | + .input-field { |
22 | + padding-right: 10px; | ||
23 | + | ||
24 | + md-input-container, | ||
25 | + md-checkbox, | ||
26 | + md-switch { | ||
27 | + margin-bottom: 5px; | ||
28 | + } | ||
29 | + } | ||
30 | + | ||
31 | + md-checkbox { | ||
32 | + margin-top: 22px; | ||
33 | + } | ||
34 | + | ||
35 | + md-switch { | ||
36 | + margin-top: 20px; | ||
27 | } | 37 | } |
28 | 38 | ||
29 | .md-icon-button md-icon { | 39 | .md-icon-button md-icon { |
@@ -32,10 +42,56 @@ | @@ -32,10 +42,56 @@ | ||
32 | height: 20px; | 42 | height: 20px; |
33 | min-height: 20px; | 43 | min-height: 20px; |
34 | font-size: 20px; | 44 | font-size: 20px; |
45 | + } | ||
46 | + | ||
47 | + .date-time-input { | ||
48 | + &__label { | ||
49 | + margin-left: 36px; | ||
50 | + font-size: 12px; | ||
51 | + color: rgba(0, 0, 0, .54); | ||
52 | + } | ||
53 | + | ||
54 | + mdp-date-picker, | ||
55 | + mdp-time-picker { | ||
56 | + width: 100%; | ||
57 | + | ||
58 | + .md-button.md-icon-button { | ||
59 | + margin: 5px 0 0; | ||
60 | + } | ||
35 | 61 | ||
36 | - &:not([disabled]) { | ||
37 | - color: #f66; | 62 | + md-input-container { |
63 | + width: 100%; | ||
64 | + margin: 2px 0; | ||
65 | + | ||
66 | + label { | ||
67 | + display: none; | ||
68 | + } | ||
69 | + } | ||
70 | + } | ||
71 | + } | ||
72 | + | ||
73 | + .vertical-alignment { | ||
74 | + flex-direction: column; | ||
75 | + | ||
76 | + md-checkbox, | ||
77 | + md-switch { | ||
78 | + margin-top: 18px; | ||
38 | } | 79 | } |
80 | + | ||
81 | + md-switch { | ||
82 | + display: flex; | ||
83 | + justify-content: space-between; | ||
84 | + } | ||
85 | + | ||
86 | + .date-time-input { | ||
87 | + &__label { | ||
88 | + margin-top: 10px; | ||
89 | + } | ||
90 | + } | ||
91 | + } | ||
92 | + | ||
93 | + .vertically-aligned { | ||
94 | + flex-direction: column; | ||
39 | } | 95 | } |
40 | } | 96 | } |
41 | 97 |
@@ -15,53 +15,134 @@ | @@ -15,53 +15,134 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<form class="tb-multiple-input" name="multipleInputForm" ng-submit="vm.postData($event)" novalidate> | 18 | +<form class="tb-multiple-input" name="multipleInputForm" ng-submit="vm.save()" novalidate autocomplete="off"> |
19 | <div style="padding: 0 8px; margin: auto 0;"> | 19 | <div style="padding: 0 8px; margin: auto 0;"> |
20 | - <div ng-show="vm.entityDetected" layout="row" flex ng-repeat="row in vm.rows" ng-init="rowIndex=$index"> | ||
21 | - <div layout="column" flex ng-repeat="key in row.data track by $index" ng-init="keyIndex=$index"> | ||
22 | - <md-tooltip class="tb-tooltip-multiline" ng-if="key.settings.tooltipMessage && key.settings.tooltipMessage.length" md-direction="left"> | ||
23 | - <span ng-bind-html="key.settings.tooltipMessage"></span> | ||
24 | - </md-tooltip> | ||
25 | - <md-input-container class="md-block" ng-style="vm.cellStyle(key, rowIndex, $first, $last)"> | ||
26 | - <label ng-style="vm.textColor(key)">{{key.label}}</label> | ||
27 | - <md-icon ng-style="vm.textColor(key)" class="material-icons" ng-if="key.settings.icon"> | ||
28 | - {{key.settings.icon}} | ||
29 | - </md-icon> | ||
30 | - <input name="value{{rowIndex}}{{keyIndex}}" | ||
31 | - ng-style="vm.textColor(key)" | ||
32 | - ng-disabled="key.settings.readOnly" | ||
33 | - ng-model="key.currentValue" | ||
34 | - min="{{key.settings.min}}" | ||
35 | - max="{{key.settings.max}}" | ||
36 | - ng-required="key.settings.required" | ||
37 | - type="{{key.settings.inputType}}" | ||
38 | - step="{{key.settings.step}}" | ||
39 | - md-select-on-focus | ||
40 | - ng-change="vm.inputChanged()"> | ||
41 | - <div ng-messages="multipleInputForm['value' + rowIndex + keyIndex].$error"> | ||
42 | - <div ng-message="min">Value must be greater than {{key.settings.min}}</div> | ||
43 | - <div ng-message="max">Value must be lower than {{key.settings.max}}</div> | ||
44 | - <div ng-message="required">This field is required</div> | 20 | + <div ng-show="vm.entityDetected" layout="row" layout-wrap ng-class="{'vertical-alignment': vm.isVerticalAlignment || vm.changeAlignment}"> |
21 | + <div ng-repeat="key in vm.data" ng-style="{'width': (vm.isVerticalAlignment || vm.changeAlignment) ? '100%' : vm.inputWidthSettings}"> | ||
22 | + <div class="input-field" ng-if="(key.settings.dataKeyValueType === 'string') && !key.settings.dataKeyHidden"> | ||
23 | + <md-input-container class="md-block"> | ||
24 | + <label>{{key.label}}</label> | ||
25 | + <md-icon class="material-icons" ng-if="key.settings.icon"> | ||
26 | + {{key.settings.icon}} | ||
27 | + </md-icon> | ||
28 | + <input name="{{key.name}}" | ||
29 | + ng-disabled="(key.settings.isEditable === 'disabled') || key.settings.disabledOnCondition" | ||
30 | + ng-readonly="key.settings.isEditable === 'readonly'" | ||
31 | + ng-model="key.data.currentValue" | ||
32 | + ng-required="key.settings.required" | ||
33 | + type="text" | ||
34 | + md-select-on-focus | ||
35 | + ng-blur="vm.inputChanged(key)"> | ||
36 | + <div ng-messages="multipleInputForm[key.name].$error"> | ||
37 | + <div ng-message="required">{{ key.settings.requiredErrorMessage }}</div> | ||
38 | + </div> | ||
39 | + </md-input-container> | ||
40 | + </div> | ||
41 | + <div class="input-field" ng-if="(key.settings.dataKeyValueType === 'double') && !key.settings.dataKeyHidden"> | ||
42 | + <md-input-container class="md-block"> | ||
43 | + <label>{{key.label}}</label> | ||
44 | + <md-icon class="material-icons" ng-if="key.settings.icon"> | ||
45 | + {{key.settings.icon}} | ||
46 | + </md-icon> | ||
47 | + <input name="{{key.name}}" | ||
48 | + ng-disabled="key.settings.isEditable === 'disabled' || key.settings.disabledOnCondition" | ||
49 | + ng-readonly="key.settings.isEditable === 'readonly'" | ||
50 | + ng-model="key.data.currentValue" | ||
51 | + ng-required="key.settings.required" | ||
52 | + type="number" | ||
53 | + step="key.settings.step" | ||
54 | + md-select-on-focus | ||
55 | + ng-blur="vm.inputChanged(key)"> | ||
56 | + <div ng-messages="multipleInputForm[key.name].$error"> | ||
57 | + <div ng-message="required">{{ key.settings.requiredErrorMessage }}</div> | ||
58 | + </div> | ||
59 | + </md-input-container> | ||
60 | + </div> | ||
61 | + <div class="input-field" ng-if="(key.settings.dataKeyValueType === 'integer') && !key.settings.dataKeyHidden"> | ||
62 | + <md-input-container class="md-block"> | ||
63 | + <label>{{key.label}}</label> | ||
64 | + <md-icon class="material-icons" ng-if="key.settings.icon"> | ||
65 | + {{key.settings.icon}} | ||
66 | + </md-icon> | ||
67 | + <input name="{{key.name}}" | ||
68 | + ng-disabled="key.settings.isEditable === 'disabled' || key.settings.disabledOnCondition" | ||
69 | + ng-readonly="key.settings.isEditable === 'readonly'" | ||
70 | + ng-model="key.data.currentValue" | ||
71 | + ng-required="key.settings.required" | ||
72 | + type="number" | ||
73 | + step="key.settings.step" | ||
74 | + md-select-on-focus | ||
75 | + ng-pattern="/^-?[0-9]+$/" | ||
76 | + ng-blur="vm.inputChanged(key)"> | ||
77 | + <div ng-messages="multipleInputForm[key.name].$error"> | ||
78 | + <div ng-message="required">{{ key.settings.requiredErrorMessage }}</div> | ||
79 | + <div translate ng-message="pattern">value.invalid-integer-value</div> | ||
80 | + </div> | ||
81 | + </md-input-container> | ||
82 | + </div> | ||
83 | + <div class="input-field" ng-if="(key.settings.dataKeyValueType === 'booleanCheckbox') && !key.settings.dataKeyHidden" class="md-block"> | ||
84 | + <md-checkbox name="{{key.name}}" | ||
85 | + ng-disabled="key.settings.isEditable === 'disabled' || key.settings.disabledOnCondition" | ||
86 | + ng-model="key.data.currentValue" | ||
87 | + ng-change="vm.inputChanged(key)"> | ||
88 | + {{key.label}} | ||
89 | + </md-checkbox> | ||
90 | + </div> | ||
91 | + <div class="input-field" ng-if="(key.settings.dataKeyValueType === 'booleanSwitch') && !key.settings.dataKeyHidden" class="md-block"> | ||
92 | + <md-switch name="{{key.name}}" | ||
93 | + ng-disabled="key.settings.isEditable === 'disabled' || key.settings.disabledOnCondition" | ||
94 | + ng-model="key.data.currentValue" | ||
95 | + ng-change="vm.inputChanged(key)" | ||
96 | + aria-label="{{key.label}}" | ||
97 | + md-invert> | ||
98 | + {{key.label}} | ||
99 | + </md-switch> | ||
100 | + </div> | ||
101 | + <div ng-if="(key.settings.dataKeyValueType === 'dateTime') || | ||
102 | + (key.settings.dataKeyValueType === 'date') || | ||
103 | + (key.settings.dataKeyValueType === 'time') && !key.settings.dataKeyHidden" | ||
104 | + class="md-block input-field date-time-input" layout="column"> | ||
105 | + <label class="date-time-input__label">{{key.label}}</label> | ||
106 | + <div layout="row" ng-class="{'vertically-aligned': vm.smallWidthContainer}"> | ||
107 | + <mdp-date-picker name="{{key.name + 'Date'}}" | ||
108 | + ng-if="key.settings.dataKeyValueType !== 'time'" | ||
109 | + mdp-disabled="key.settings.isEditable === 'disabled' || key.settings.disabledOnCondition" | ||
110 | + ng-model="key.data.currentValue" | ||
111 | + ng-change="vm.inputChanged(key)" | ||
112 | + mdp-placeholder="{{ 'widgets.input-widgets.date' | translate }}"> | ||
113 | + <div ng-messages="multipleInputForm[key.name].$error"> | ||
114 | + <div ng-message="required">{{ key.settings.requiredErrorMessage }}</div> | ||
115 | + </div> | ||
116 | + </mdp-date-picker> | ||
117 | + <mdp-time-picker name="{{key.name + 'Time'}}" | ||
118 | + ng-if="key.settings.dataKeyValueType !== 'date'" | ||
119 | + mdp-disabled="key.settings.isEditable === 'disabled' || key.settings.disabledOnCondition" | ||
120 | + ng-model="key.data.currentValue" | ||
121 | + ng-change="vm.inputChanged(key)" | ||
122 | + mdp-placeholder="{{ 'widgets.input-widgets.time' | translate }}" | ||
123 | + mdp-auto-switch="true"> | ||
124 | + <div ng-messages="multipleInputForm[key.name].$error"> | ||
125 | + <div ng-message="required">{{ key.settings.requiredErrorMessage }}</div> | ||
126 | + </div> | ||
127 | + </mdp-time-picker> | ||
45 | </div> | 128 | </div> |
46 | - </md-input-container> | 129 | + </div> |
47 | </div> | 130 | </div> |
48 | </div> | 131 | </div> |
49 | 132 | ||
50 | - <div style="text-align: center; font-size: 18px; color: #a0a0a0;" ng-hide="vm.entityDetected" ng-bind="vm.message" | ||
51 | - ></div> | ||
52 | - <div style="text-align: center; font-size: 18px; color: #a0a0a0;" ng-show="vm.entityDetected && !vm.dataKeyDetected"> | ||
53 | - No attribute is selected | 133 | + <div style="text-align: center; font-size: 18px; color: #a0a0a0;" ng-hide="vm.entityDetected"> |
134 | + {{ 'widgets.input-widgets.no-entity-selected' | translate }} | ||
54 | </div> | 135 | </div> |
55 | - <div style="text-align: center; font-size: 18px; color: #a0a0a0;" ng-show="vm.entityDetected && !vm.isValidParameter"> | ||
56 | - Timeseries parameter cannot be used in this widget | 136 | + <div style="text-align: center; font-size: 18px; color: #a0a0a0;" ng-show="vm.entityDetected && !vm.isAllParametersValid"> |
137 | + {{ 'widgets.input-widgets.not-allowed-entity' | translate }} | ||
57 | </div> | 138 | </div> |
58 | </div> | 139 | </div> |
59 | - <div class="md-padding" layout="row" layout-align="end center" ng-show="vm.entityDetected && vm.dataKeyDetected"> | ||
60 | - <md-button class="md-primary" ng-click="vm.discardAll()" style="max-height: 50px;margin-right:20px;" ng-disabled="!vm.hasAnyChange"> | 140 | + <div class="md-padding" layout="row" layout-align="end center" ng-if="vm.entityDetected && vm.settings.showActionButtons"> |
141 | + <md-button class="md-primary" ng-click="vm.discardAll()" style="max-height: 50px; margin-right:20px;" ng-disabled="!multipleInputForm.$dirty"> | ||
61 | {{ 'action.undo' | translate }} | 142 | {{ 'action.undo' | translate }} |
62 | </md-button> | 143 | </md-button> |
63 | - <md-button class="md-raised md-primary" type="submit" value="Submit" style="max-height: 50px;margin-right:20px;" | ||
64 | - ng-disabled="!vm.hasAnyChange || multipleInputForm.$invalid" ng-click="vm.isFocused = false"> | 144 | + <md-button class="md-raised md-primary" type="submit" value="Submit" style="max-height: 50px; margin-right:20px;" |
145 | + ng-disabled="!multipleInputForm.$dirty || multipleInputForm.$invalid" ng-click="vm.isFocused = false"> | ||
65 | {{ 'action.save' | translate }} | 146 | {{ 'action.save' | translate }} |
66 | </md-button> | 147 | </md-button> |
67 | </div> | 148 | </div> |