Commit a51c44e5dbc90b4975ded1a9f73a4560d120a507

Authored by Chantsova Ekaterina
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
@@ -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>