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 319 "resources": [],
320 320 "templateHtml": "<tb-multiple-input-widget \n form-id=\"formId\"\n ctx=\"ctx\">\n</tb-multiple-input-widget>",
321 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 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 41 }
42 42
43 43 /*@ngInject*/
44   -function MultipleInputWidgetController($q, $scope, attributeService, toast, types, utils) {
  44 +function MultipleInputWidgetController($q, $scope, $translate, attributeService, toast, types, utils) {
45 45 var vm = this;
46 46
47   - vm.dataKeyDetected = false;
48   - vm.hasAnyChange = false;
49 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 51 vm.datasources = null;
57 52
58   - vm.cellStyle = cellStyle;
59   - vm.textColor = textColor;
60 53 vm.discardAll = discardAll;
61 54 vm.inputChanged = inputChanged;
62   - vm.postData = postData;
  55 + vm.save = save;
63 56
64 57 $scope.$watch('vm.ctx', function() {
65 58 if (vm.ctx && vm.ctx.defaultSubscription) {
... ... @@ -74,127 +67,109 @@ function MultipleInputWidgetController($q, $scope, attributeService, toast, type
74 67
75 68 $scope.$on('multiple-input-data-updated', function(event, formId) {
76 69 if (vm.formId == formId) {
77   - updateRowData(vm.subscription.data);
  70 + updateWidgetData(vm.subscription.data);
78 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 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 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 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 170 function fail() {
196 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 186
212 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 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 */
16 16 .tb-multiple-input {
17 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 39 .md-icon-button md-icon {
... ... @@ -32,10 +42,56 @@
32 42 height: 20px;
33 43 min-height: 20px;
34 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 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 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 128 </div>
46   - </md-input-container>
  129 + </div>
47 130 </div>
48 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 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 138 </div>
58 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 142 {{ 'action.undo' | translate }}
62 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 146 {{ 'action.save' | translate }}
66 147 </md-button>
67 148 </div>
... ...