From 15d7970ba92f4d543e6b045915d4daf60b08ec17 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 26 Feb 2020 18:15:04 +0200 Subject: [PATCH] Propagate UI changes. --- .../json/system/widget_bundles/cards.json | 2 +- ui-ngx/package-lock.json | 153 +++-------------- ui-ngx/package.json | 1 - ui-ngx/src/app/core/api/alias-controller.ts | 9 +- .../src/app/core/api/widget-subscription.ts | 7 +- .../app/core/services/item-buffer.service.ts | 11 +- ui-ngx/src/app/core/utils.ts | 7 +- .../dashboard/dashboard.component.html | 1 + ...custom-action-pretty-editor.component.html | 2 +- ...ction-pretty-resources-tabs.component.html | 2 +- .../widget-action-dialog.component.html | 2 +- .../components/widget/lib/digital-gauge.ts | 4 +- .../lib/entities-table-widget.component.html | 2 +- .../lib/entities-table-widget.component.ts | 5 +- .../widget/lib/flot-widget.models.ts | 139 ++++++++++++++- .../home/components/widget/lib/flot-widget.ts | 159 ++++++++++++++++-- .../widget/lib/rpc/knob.component.html | 2 +- .../widget/lib/rpc/knob.component.scss | 3 - .../widget/lib/rpc/knob.component.ts | 6 +- .../lib/rpc/led-indicator.component.scss | 3 - .../lib/rpc/round-switch.component.scss | 3 - .../widget/lib/rpc/switch.component.scss | 3 - .../components/widget/widget.component.ts | 8 +- .../home/models/dashboard-component.models.ts | 7 +- ...age-dashboard-states-dialog.component.html | 2 +- .../entity/entity-keys-list.component.ts | 38 ++--- .../json-form/react/json-form-utils.ts | 5 +- .../json-form/react/json-form.models.ts | 4 - ui-ngx/src/styles.scss | 9 + 29 files changed, 378 insertions(+), 221 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json index 65c9b6cceb..1c96d7168e 100644 --- a/application/src/main/data/json/system/widget_bundles/cards.json +++ b/application/src/main/data/json/system/widget_bundles/cards.json @@ -31,7 +31,7 @@ "resources": [], "templateHtml": "\n", "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n },\n 'rowDoubleClick': {\n name: 'widget-action.row-double-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"entitiesTitle\": {\n \"title\": \"Entities table title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"enableSearch\": {\n \"title\": \"Enable entities search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSelectColumnDisplay\": {\n \"title\": \"Enable select columns to display\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayEntityName\": {\n \"title\": \"Display entity name column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"entityNameColumnTitle\": {\n \"title\": \"Entity name column title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"displayEntityLabel\": {\n \"title\": \"Display entity label column\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"entityLabelColumnTitle\": {\n \"title\": \"Entity label column title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"displayEntityType\": {\n \"title\": \"Display entity type column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultSortOrder\": {\n \"title\": \"Default sort order\",\n \"type\": \"string\",\n \"default\": \"entityName\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"entitiesTitle\",\n \"enableSearch\",\n \"enableSelectColumnDisplay\",\n \"displayEntityName\",\n \"entityNameColumnTitle\",\n \"displayEntityLabel\",\n \"entityLabelColumnTitle\",\n \"displayEntityType\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\"\n ]\n}", "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"columnWidth\": {\n \"title\": \"Column width (px or %)\",\n \"type\": \"string\",\n \"default\": \"0px\"\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 \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, entity, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"columnWidth\",\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}", "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"displayEntityName\":true,\"displayEntityType\":true},\"title\":\"Entities table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}]}" diff --git a/ui-ngx/package-lock.json b/ui-ngx/package-lock.json index db25e430bf..57f1a26264 100644 --- a/ui-ngx/package-lock.json +++ b/ui-ngx/package-lock.json @@ -4905,32 +4905,6 @@ "mimic-response": "^1.0.0" } }, - "deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.1.tgz", - "integrity": "sha512-7Et6r6XfNW61CPPCIYfm1YPGSmh6+CliYeL4km7GWJcpX5LTAflGF8drLLR+MZX+2P3NZfAfSduutBbSWqER4g==", - "requires": { - "es-abstract": "^1.16.3", - "es-get-iterator": "^1.0.1", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "isarray": "^2.0.5", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0", - "side-channel": "^1.0.1", - "which-boxed-primitive": "^1.0.1", - "which-collection": "^1.0.0" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - } - } - }, "deep-freeze-strict": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-freeze-strict/-/deep-freeze-strict-1.1.1.tgz", @@ -5007,6 +4981,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -5582,6 +5557,7 @@ "version": "1.17.4", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", @@ -5596,31 +5572,11 @@ "string.prototype.trimright": "^2.1.1" } }, - "es-get-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", - "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", - "requires": { - "es-abstract": "^1.17.4", - "has-symbols": "^1.0.1", - "is-arguments": "^1.0.4", - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-string": "^1.0.5", - "isarray": "^2.0.5" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - } - } - }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -6328,7 +6284,8 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "gauge": { "version": "2.7.4", @@ -6748,6 +6705,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -6797,7 +6755,8 @@ "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true }, "has-to-string-tag-x": { "version": "1.4.1", @@ -7372,18 +7331,14 @@ "is-arguments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, - "is-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", - "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==" - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -7393,11 +7348,6 @@ "binary-extensions": "^2.0.0" } }, - "is-boolean-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", - "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==" - }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -7407,7 +7357,8 @@ "is-callable": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true }, "is-color-stop": { "version": "1.1.0", @@ -7446,7 +7397,8 @@ "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true }, "is-descriptor": { "version": "0.1.6", @@ -7518,22 +7470,12 @@ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true }, - "is-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", - "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==" - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==" - }, "is-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", @@ -7592,6 +7534,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, "requires": { "has": "^1.0.3" } @@ -7607,21 +7550,11 @@ "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" }, - "is-set": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", - "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==" - }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==" - }, "is-subset": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", @@ -7640,6 +7573,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, "requires": { "has-symbols": "^1.0.1" } @@ -7662,16 +7596,6 @@ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" }, - "is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==" - }, - "is-weakset": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", - "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==" - }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -9773,17 +9697,20 @@ "object-inspect": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true }, "object-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz", - "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==" + "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==", + "dev": true }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true }, "object-visit": { "version": "1.0.1", @@ -9798,6 +9725,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, "requires": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", @@ -11855,6 +11783,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.0-next.1" @@ -12535,15 +12464,6 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, - "side-channel": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz", - "integrity": "sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==", - "requires": { - "es-abstract": "^1.17.0-next.1", - "object-inspect": "^1.7.0" - } - }, "sigmund": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", @@ -13260,6 +13180,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "dev": true, "requires": { "define-properties": "^1.1.3", "function-bind": "^1.1.1" @@ -13269,6 +13190,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "dev": true, "requires": { "define-properties": "^1.1.3", "function-bind": "^1.1.1" @@ -16048,29 +15970,6 @@ "isexe": "^2.0.0" } }, - "which-boxed-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz", - "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==", - "requires": { - "is-bigint": "^1.0.0", - "is-boolean-object": "^1.0.0", - "is-number-object": "^1.0.3", - "is-string": "^1.0.4", - "is-symbol": "^1.0.2" - } - }, - "which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "requires": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - } - }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", diff --git a/ui-ngx/package.json b/ui-ngx/package.json index a8375a169d..d11f30e086 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -46,7 +46,6 @@ "compass-sass-mixins": "^0.12.7", "core-js": "^3.6.4", "date-fns": "^2.9.0", - "deep-equal": "^2.0.1", "flot": "git://github.com/thingsboard/flot.git#0.9-work", "flot.curvedlines": "git://github.com/MichaelZinsmaier/CurvedLines.git#master", "font-awesome": "^4.7.0", diff --git a/ui-ngx/src/app/core/api/alias-controller.ts b/ui-ngx/src/app/core/api/alias-controller.ts index f4977fd30d..90014ad90f 100644 --- a/ui-ngx/src/app/core/api/alias-controller.ts +++ b/ui-ngx/src/app/core/api/alias-controller.ts @@ -17,12 +17,11 @@ import { AliasInfo, IAliasController, StateControllerHolder, StateEntityInfo } from '@core/api/widget-api.models'; import { forkJoin, Observable, of, ReplaySubject, Subject } from 'rxjs'; import { DataKey, Datasource, DatasourceType } from '@app/shared/models/widget.models'; -import { deepClone } from '@core/utils'; +import { deepClone, isEqual } from '@core/utils'; import { EntityService } from '@core/http/entity.service'; import { UtilsService } from '@core/services/utils.service'; import { EntityAliases } from '@shared/models/alias.models'; import { EntityInfo } from '@shared/models/entity.models'; -import * as equal from 'deep-equal'; import { map } from 'rxjs/operators'; export class AliasController implements IAliasController { @@ -53,7 +52,7 @@ export class AliasController implements IAliasController { for (const aliasId of Object.keys(newEntityAliases)) { const newEntityAlias = newEntityAliases[aliasId]; const prevEntityAlias = this.entityAliases[aliasId]; - if (!equal(newEntityAlias, prevEntityAlias)) { + if (!isEqual(newEntityAlias, prevEntityAlias)) { changedAliasIds.push(aliasId); this.setAliasUnresolved(aliasId); } @@ -76,7 +75,7 @@ export class AliasController implements IAliasController { const stateEntityInfo = this.resolvedAliasesToStateEntities[aliasId]; const newEntityId = this.stateControllerHolder().getEntityId(stateEntityInfo.entityParamName); const prevEntityId = stateEntityInfo.entityId; - if (!equal(newEntityId, prevEntityId)) { + if (!isEqual(newEntityId, prevEntityId)) { changedAliasIds.push(aliasId); this.setAliasUnresolved(aliasId); } @@ -319,7 +318,7 @@ export class AliasController implements IAliasController { const aliasInfo = this.resolvedAliases[aliasId]; if (aliasInfo) { const prevCurrentEntity = aliasInfo.currentEntity; - if (!equal(currentEntity, prevCurrentEntity)) { + if (!isEqual(currentEntity, prevCurrentEntity)) { aliasInfo.currentEntity = currentEntity; this.entityAliasesChangedSubject.next([aliasId]); } diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index 2bf0048a3a..77a60cb86b 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -47,10 +47,9 @@ import { Observable, ReplaySubject, Subject, throwError } from 'rxjs'; import { CancelAnimationFrame } from '@core/services/raf.service'; import { EntityType } from '@shared/models/entity-type.models'; import { AlarmInfo, AlarmSearchStatus } from '@shared/models/alarm.models'; -import { deepClone, isDefined } from '@core/utils'; +import { deepClone, isDefined, isEqual } from '@core/utils'; import { AlarmSourceListener } from '@core/http/alarm.service'; import { DatasourceListener } from '@core/api/datasource.service'; -import * as deepEqual from 'deep-equal'; import { EntityId } from '@app/shared/models/id/entity-id'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { entityFields } from '@shared/models/entity.models'; @@ -539,7 +538,7 @@ export class WidgetSubscription implements IWidgetSubscription { onDashboardTimewindowChanged(newDashboardTimewindow: Timewindow): void { if (this.type === widgetType.timeseries || this.type === widgetType.alarm) { if (this.useDashboardTimewindow) { - if (!deepEqual(this.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) { + if (!isEqual(this.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) { this.timeWindowConfig = deepClone(newDashboardTimewindow); this.update(); } @@ -1010,7 +1009,7 @@ export class WidgetSubscription implements IWidgetSubscription { private alarmsUpdated(alarms: Array) { this.notifyDataLoaded(); - const updated = !this.alarms || !deepEqual(this.alarms, alarms); + const updated = !this.alarms || !isEqual(this.alarms, alarms); this.alarms = alarms; if (this.subscriptionTimewindow && this.subscriptionTimewindow.realtimeWindowMs) { this.updateTimewindow(); diff --git a/ui-ngx/src/app/core/services/item-buffer.service.ts b/ui-ngx/src/app/core/services/item-buffer.service.ts index 1b02ca341b..081953e2bf 100644 --- a/ui-ngx/src/app/core/services/item-buffer.service.ts +++ b/ui-ngx/src/app/core/services/item-buffer.service.ts @@ -19,16 +19,13 @@ import { Dashboard, DashboardLayoutId } from '@app/shared/models/dashboard.model import { EntityAlias, EntityAliasFilter, EntityAliases, EntityAliasInfo, AliasesInfo } from '@shared/models/alias.models'; import { DatasourceType, Widget, WidgetPosition, WidgetSize } from '@shared/models/widget.models'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; -import { deepClone } from '@core/utils'; -import * as equal from 'deep-equal'; +import { deepClone, isEqual } from '@core/utils'; import { UtilsService } from '@core/services/utils.service'; import { Observable, of, throwError } from 'rxjs'; import { map } from 'rxjs/operators'; -import { FcRuleEdge, FcRuleNode, ruleNodeTypeDescriptors } from '@shared/models/rule-node.models'; +import { FcRuleNode, ruleNodeTypeDescriptors } from '@shared/models/rule-node.models'; import { RuleChainService } from '@core/http/rule-chain.service'; import { RuleChainImport } from '@shared/models/rule-chain.models'; -import { Simulate } from 'react-dom/test-utils'; -import error = Simulate.error; const WIDGET_ITEM = 'widget_item'; const WIDGET_REFERENCE = 'widget_reference'; @@ -219,7 +216,7 @@ export class ItemBufferService { let callAliasUpdateFunction = false; if (aliasesInfo) { const newEntityAliases = this.updateAliases(theDashboard, widget, aliasesInfo); - const aliasesUpdated = !equal(newEntityAliases, theDashboard.configuration.entityAliases); + const aliasesUpdated = !isEqual(newEntityAliases, theDashboard.configuration.entityAliases); if (aliasesUpdated) { theDashboard.configuration.entityAliases = newEntityAliases; if (onAliasesUpdateFunction) { @@ -405,7 +402,7 @@ export class ItemBufferService { } private isEntityAliasEqual(alias1: EntityAliasInfo, alias2: EntityAliasInfo): boolean { - return equal(alias1.filter, alias2.filter); + return isEqual(alias1.filter, alias2.filter); } private getEntityAliasId(entityAliases: EntityAliases, aliasInfo: EntityAliasInfo): string { diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 11f2b3408d..381aadfdb7 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -14,7 +14,8 @@ /// limitations under the License. /// -import { BehaviorSubject, Observable, Subject } from 'rxjs'; +import _ from 'lodash'; +import { Observable, Subject } from 'rxjs'; import { finalize, share } from 'rxjs/operators'; import base64js from 'base64-js'; @@ -384,6 +385,10 @@ export function deepClone(target: T, ignoreFields?: string[]): T { return target; } +export function isEqual(a: any, b: any): boolean { + return _.isEqual(a, b); +} + export function guid(): string { function s4(): string { return Math.floor((1 + Math.random()) * 0x10000) diff --git a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html index c7894281bc..7cec52461e 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html @@ -85,6 +85,7 @@ {{widget.titleIcon}} diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.html b/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.html index f6841e5da5..fabca60ebd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.html @@ -43,7 +43,7 @@ [(ngModel)]="action.customFunction" (ngModelChange)="onActionUpdated()" [fillHeight]="true" - [functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'htmlTemplate', 'additionalParams']" + [functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel']" [validationArgs]="[]"> diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html b/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html index 6a002dc932..67463d57a0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html @@ -93,7 +93,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html index 2ab416754a..912db30ce1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html @@ -133,7 +133,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts index d52e71d6e7..82f5d0e9e4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts @@ -193,7 +193,9 @@ export class TbCanvasDigitalGauge { } const value = tvPair[1]; if(value !== this.gauge.value) { - this.gauge._value = value; + if (!this.gauge.options.animation) { + this.gauge._value = value; + } this.gauge.value = value; } else if (this.localSettings.showTimestamp && this.gauge.timestamp !== timestamp) { this.gauge.timestamp = timestamp; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html index aece1be212..75515eeeea 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html @@ -84,7 +84,7 @@ + (click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"> ; private readonly yAxisTickFormatter: TbFlotTicksFormatterFunction; - private ticksFormatterFunction: TbFlotTicksFormatterFunction; private readonly yaxis: TbFlotAxisOptions; private readonly xaxis: TbFlotAxisOptions; private yaxes: Array; @@ -73,6 +85,9 @@ export class TbFlot { private readonly defaultBarWidth: number; + private thresholdsSourcesSubscription: IWidgetSubscription; + private predefinedThresholds: TbFlotThresholdMarking[]; + private plotInited = false; private plot: JQueryPlot; @@ -118,7 +133,7 @@ export class TbFlot { constructor(private ctx: WidgetContext, private readonly chartType: ChartType) { this.chartType = this.chartType || 'line'; this.settings = ctx.settings as TbFlotSettings; - const utils = this.ctx.$injector.get(UtilsService); + this.utils = this.ctx.$injector.get(UtilsService); this.tooltip = $('#flot-series-tooltip'); if (this.tooltip.length === 0) { this.tooltip = this.createTooltipElement(); @@ -145,7 +160,8 @@ export class TbFlot { grid: { hoverable: true, mouseActiveRadius: 10, - autoHighlight: this.tooltipIndividual === true + autoHighlight: this.tooltipIndividual === true, + markings: [] }, selection : { mode : ctx.isMobile ? null : 'x' }, legend : { @@ -167,7 +183,7 @@ export class TbFlot { }; if (this.settings.xaxis) { this.xaxis.font.color = this.settings.xaxis.color || this.xaxis.font.color; - this.xaxis.label = utils.customTranslation(this.settings.xaxis.title, this.settings.xaxis.title) || null; + this.xaxis.label = this.utils.customTranslation(this.settings.xaxis.title, this.settings.xaxis.title) || null; this.xaxis.labelFont.color = this.xaxis.font.color; this.xaxis.labelFont.size = this.xaxis.font.size + 2; this.xaxis.labelFont.weight = 'bold'; @@ -181,7 +197,7 @@ export class TbFlot { this.yaxis.font.color = this.settings.yaxis.color || this.yaxis.font.color; this.yaxis.min = isDefined(this.settings.yaxis.min) ? this.settings.yaxis.min : null; this.yaxis.max = isDefined(this.settings.yaxis.max) ? this.settings.yaxis.max : null; - this.yaxis.label = utils.customTranslation(this.settings.yaxis.title, this.settings.yaxis.title) || null; + this.yaxis.label = this.utils.customTranslation(this.settings.yaxis.title, this.settings.yaxis.title) || null; this.yaxis.labelFont.color = this.yaxis.font.color; this.yaxis.labelFont.size = this.yaxis.font.size + 2; this.yaxis.labelFont.weight = 'bold'; @@ -244,7 +260,7 @@ export class TbFlot { return ''; }; } - xaxis.label = utils.customTranslation(this.settings.xaxisSecond.title, this.settings.xaxisSecond.title) || null; + xaxis.label = this.utils.customTranslation(this.settings.xaxisSecond.title, this.settings.xaxisSecond.title) || null; xaxis.position = this.settings.xaxisSecond.axisPosition; } xaxis.tickLength = 0; @@ -270,6 +286,10 @@ export class TbFlot { }; } + if (this.chartType === 'line' && isFinite(this.settings.thresholdsLineWidth)) { + this.options.grid.markingsLineWidth = this.settings.thresholdsLineWidth; + } + if (this.chartType === 'bar') { this.options.series.lines = { show: false, @@ -279,7 +299,8 @@ export class TbFlot { this.options.series.bars = { show: true, lineWidth: 0, - fill: 0.9 + fill: 0.9, + align: this.settings.barAlignment || 'left' }; this.defaultBarWidth = this.settings.defaultBarWidth || 600; } @@ -346,6 +367,8 @@ export class TbFlot { const colors: string[] = []; this.yaxes = []; const yaxesMap: {[units: string]: TbFlotAxisOptions} = {}; + const predefinedThresholds: TbFlotThresholdMarking[] = []; + const thresholdsDatasources: Datasource[] = []; let tooltipValueFormatFunction: TooltipValueFormatFunction = null; if (this.settings.tooltipValueFormatter && this.settings.tooltipValueFormatter.length) { @@ -441,8 +464,54 @@ export class TbFlot { series.yaxis = series.yaxisIndex + 1; yaxis.keysInfo[i] = {hidden: false}; yaxis.show = true; + + if (keySettings.thresholds && keySettings.thresholds.length) { + for (const threshold of keySettings.thresholds) { + if (threshold.thresholdValueSource === 'predefinedValue' && isFinite(threshold.thresholdValue)) { + const colorIndex = this.subscription.data.length + predefinedThresholds.length; + this.generateThreshold(predefinedThresholds, series.yaxis, threshold.lineWidth, + threshold.color, colorIndex, threshold.thresholdValue); + } else if (threshold.thresholdEntityAlias && threshold.thresholdAttribute) { + const entityAliasId = this.ctx.aliasController.getEntityAliasId(threshold.thresholdEntityAlias); + if (!entityAliasId) { + continue; + } + let datasource = thresholdsDatasources.filter((thresholdDatasource) => { + return thresholdDatasource.entityAliasId === entityAliasId; + })[0]; + const dataKey: DataKey = { + type: DataKeyType.attribute, + name: threshold.thresholdAttribute, + label: threshold.thresholdAttribute, + settings: { + yaxis: series.yaxis, + lineWidth: threshold.lineWidth, + color: threshold.color + } as TbFlotThresholdKeySettings, + _hash: Math.random() + }; + if (datasource) { + datasource.dataKeys.push(dataKey); + } else { + datasource = { + type: DatasourceType.entity, + name: threshold.thresholdEntityAlias, + aliasName: threshold.thresholdEntityAlias, + entityAliasId, + dataKeys: [ dataKey ] + }; + thresholdsDatasources.push(datasource); + } + } + } + } } } + + this.subscribeForThresholdsAttributes(thresholdsDatasources); + this.options.grid.markings = predefinedThresholds; + this.predefinedThresholds = predefinedThresholds; + this.options.colors = colors; this.options.yaxes = deepClone(this.yaxes); if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') { @@ -714,6 +783,72 @@ export class TbFlot { return yaxis; } + private subscribeForThresholdsAttributes(datasources: Datasource[]) { + const thresholdsSourcesSubscriptionOptions: WidgetSubscriptionOptions = { + datasources, + useDashboardTimewindow: false, + type: widgetType.latest, + callbacks: { + onDataUpdated: (subscription) => {this.thresholdsSourcesDataUpdated(subscription.data)} + } + }; + this.ctx.subscriptionApi.createSubscription(thresholdsSourcesSubscriptionOptions, true).subscribe( + (subscription) => { + this.thresholdsSourcesSubscription = subscription; + } + ); + } + + private thresholdsSourcesDataUpdated(data: DatasourceData[]) { + const allThresholds = deepClone(this.predefinedThresholds); + data.forEach((keyData) => { + if (keyData && keyData.data && keyData.data[0]) { + const attrValue = keyData.data[0][1]; + if (isFinite(attrValue)) { + const settings: TbFlotThresholdKeySettings = keyData.dataKey.settings; + const colorIndex = this.subscription.data.length + allThresholds.length; + this.generateThreshold(allThresholds, settings.yaxis, settings.lineWidth, settings.color, colorIndex, attrValue); + } + } + }); + this.options.grid.markings = allThresholds; + this.redrawPlot(); + } + + private generateThreshold(existingThresholds: TbFlotThresholdMarking[], yaxis: number, lineWidth: number, + color: string, defaultColorIndex: number, thresholdValue: number) { + const marking: TbFlotThresholdMarking = {}; + let markingYAxis; + + if (yaxis !== 1) { + markingYAxis = 'y' + yaxis + 'axis'; + } else { + markingYAxis = 'yaxis'; + } + + if (isFinite(lineWidth)) { + marking.lineWidth = lineWidth; + } + + if (isDefined(color)) { + marking.color = color; + } else { + marking.color = this.utils.getMaterialColor(defaultColorIndex); + } + + marking[markingYAxis] = { + from: thresholdValue, + to: thresholdValue + }; + + const similarMarkings = existingThresholds.filter((existingMarking) => { + return isEqual(existingMarking[markingYAxis], marking[markingYAxis]); + }); + if (!similarMarkings.length) { + existingThresholds.push(marking); + } + } + private seriesInfoDiv(label: string, color: string, value: any, units: string, trackDecimals: number, active: boolean, percent: number, valueFormatFunction: TooltipValueFormatFunction): JQuery { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.html index eeed8fa486..38ac3f67c5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.html @@ -29,7 +29,7 @@
- {{ error }} +
{{ title }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.scss index 990647f2df..af0bdf158b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.scss @@ -26,13 +26,10 @@ $minmax-height: percentage(.04) !default; $minmax-container-margin-pct: percentage(.18) !default; $minmax-container-margin-bottom-pct: percentage(.12) !default; -$background-color: #e6e7e8 !default; - :host { .tb-knob { width: 100%; height: 100%; - background: $background-color; .knob { position: relative; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts index a0aef62729..e98170501d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts @@ -24,6 +24,7 @@ import { isDefined, isNumber } from '@core/utils'; import { CanvasDigitalGauge, CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; import GenericOptions = CanvasGauges.GenericOptions; import * as tinycolor_ from 'tinycolor2'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; const tinycolor = tinycolor_; @@ -338,6 +339,9 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { let textWidth = this.measureTextWidth(text, fontSize); while (textWidth > maxWidth) { fontSize--; + if (fontSize < 0) { + break; + } textWidth = this.measureTextWidth(text, fontSize); } element.css({fontSize: fontSize+'px', lineHeight: fontSize+'px'}); @@ -345,7 +349,7 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { private measureTextWidth(text: string, fontSize: number): number { this.textMeasure.css({fontSize: fontSize+'px', lineHeight: fontSize+'px'}); - this.textMeasure.text(text); + this.textMeasure.html(text); return this.textMeasure.width(); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/led-indicator.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/led-indicator.component.scss index dcb1752256..bf484a847d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/led-indicator.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/led-indicator.component.scss @@ -17,13 +17,10 @@ $error-height: 14px !default; -$background-color: #e6e7e8 !default; - :host { .tb-led-indicator { width: 100%; height: 100%; - background: $background-color; .title-container { .led-title { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/round-switch.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/round-switch.component.scss index 3da3ed3d18..f083096b24 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/round-switch.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/round-switch.component.scss @@ -17,13 +17,10 @@ $error-height: 14px !default; -$background-color: #e6e7e8 !default; - :host { .tb-round-switch { width: 100%; height: 100%; - background: $background-color; .title-container { .switch-title { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.scss index d57cafc7dc..f878f45320 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.scss @@ -19,8 +19,6 @@ $thumb-checked-img: url("./svg/thumb-checked.svg") !default; $thumb-bar-img: url("./svg/thumb-bar.svg") !default; $thumb-bar-checked-img: url("./svg/thumb-bar-checked.svg") !default; -$background-color: #e6e7e8 !default; - $error-height: 14px !default; :host { @@ -28,7 +26,6 @@ $error-height: 14px !default; .tb-switch { width: 100%; height: 100%; - background: $background-color; .error-container { position: absolute; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts index 5ecf72ca2d..2a226e07b6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts @@ -1007,8 +1007,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI additionalParams = {}; } const customActionFunction = new Function('$event', 'widgetContext', 'entityId', - 'entityName', 'additionalParams', customFunction); - customActionFunction($event, this.widgetContext, entityId, entityName, additionalParams); + 'entityName', 'additionalParams', 'entityLabel', customFunction); + customActionFunction($event, this.widgetContext, entityId, entityName, additionalParams, entityLabel); } catch (e) { // } @@ -1032,8 +1032,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI additionalParams = {}; } const customActionPrettyFunction = new Function('$event', 'widgetContext', 'entityId', - 'entityName', 'htmlTemplate', 'additionalParams', customPrettyFunction); - customActionPrettyFunction($event, this.widgetContext, entityId, entityName, htmlTemplate, additionalParams); + 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel', customPrettyFunction); + customActionPrettyFunction($event, this.widgetContext, entityId, entityName, htmlTemplate, additionalParams, entityLabel); } catch (e) { // } diff --git a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts index 102567ffef..3ccba35805 100644 --- a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts @@ -20,10 +20,9 @@ import { WidgetLayout, WidgetLayouts } from '@app/shared/models/dashboard.models import { IDashboardWidget, WidgetAction, WidgetContext, WidgetHeaderAction } from './widget-component.models'; import { Timewindow } from '@shared/models/time/time.models'; import { Observable, of, Subject } from 'rxjs'; -import { guid, isDefined, isUndefined } from '@app/core/utils'; +import { guid, isDefined, isEqual, isUndefined } from '@app/core/utils'; import { IterableDiffer, KeyValueDiffer, NgZone } from '@angular/core'; import { IAliasController, IStateController } from '@app/core/api/widget-api.models'; -import * as deepEqual from 'deep-equal'; export interface WidgetsData { widgets: Array; @@ -166,8 +165,8 @@ export class DashboardWidgets implements Iterable { index = this.dashboardWidgets.findIndex((dashboardWidget) => dashboardWidget.widgetId === record.widgetId); if (index > -1) { const prevDashboardWidget = this.dashboardWidgets[index]; - if (!deepEqual(prevDashboardWidget.widget, record.widget) || - !deepEqual(prevDashboardWidget.widgetLayout, record.widgetLayout)) { + if (!isEqual(prevDashboardWidget.widget, record.widget) || + !isEqual(prevDashboardWidget.widgetLayout, record.widgetLayout)) { this.dashboardWidgets[index] = new DashboardWidget(this.dashboard, record.widget, record.widgetLayout); this.dashboardWidgets[index].highlighted = prevDashboardWidget.highlighted; this.dashboardWidgets[index].selected = prevDashboardWidget.selected; diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/states/manage-dashboard-states-dialog.component.html b/ui-ngx/src/app/modules/home/pages/dashboard/states/manage-dashboard-states-dialog.component.html index 9f2d7ce31f..1c7abc2ec0 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/states/manage-dashboard-states-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/dashboard/states/manage-dashboard-states-dialog.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+

dashboard.manage-states

diff --git a/ui-ngx/src/app/shared/components/entity/entity-keys-list.component.ts b/ui-ngx/src/app/shared/components/entity/entity-keys-list.component.ts index 8564157410..21d6a6572d 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-keys-list.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-keys-list.component.ts @@ -14,31 +14,21 @@ /// limitations under the License. /// -import {COMMA, ENTER, SEMICOLON} from '@angular/cdk/keycodes'; -import {AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, SkipSelf, ViewChild} from '@angular/core'; -import { - ControlValueAccessor, - FormBuilder, - FormControl, - FormGroup, - FormGroupDirective, - NG_VALUE_ACCESSOR, NgForm -} from '@angular/forms'; -import {Observable, of} from 'rxjs'; -import {map, mergeMap, startWith, tap, share, pairwise, filter} from 'rxjs/operators'; -import {Store} from '@ngrx/store'; -import {AppState} from '@app/core/core.state'; -import {TranslateService} from '@ngx-translate/core'; -import {AliasEntityType, EntityType} from '@shared/models/entity-type.models'; -import {BaseData} from '@shared/models/base-data'; -import {EntityId} from '@shared/models/id/entity-id'; -import {EntityService} from '@core/http/entity.service'; +import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; +import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { Observable, of } from 'rxjs'; +import { map, mergeMap, share } from 'rxjs/operators'; +import { Store } from '@ngrx/store'; +import { AppState } from '@app/core/core.state'; +import { TranslateService } from '@ngx-translate/core'; +import { EntityId } from '@shared/models/id/entity-id'; +import { EntityService } from '@core/http/entity.service'; import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; -import { MatChipList, MatChipInputEvent } from '@angular/material/chips'; -import { ErrorStateMatcher } from '@angular/material/core'; +import { MatChipInputEvent, MatChipList } from '@angular/material/chips'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import {DataKeyType} from '@shared/models/telemetry/telemetry.models'; -import * as equal from 'deep-equal'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { isEqual } from '@core/utils'; @Component({ selector: 'tb-entity-keys-list', @@ -62,7 +52,7 @@ export class EntityKeysListComponent implements ControlValueAccessor, OnInit, Af @Input() set entityId(entityId: EntityId) { - if (!equal(this.entityIdValue, entityId)) { + if (!isEqual(this.entityIdValue, entityId)) { this.entityIdValue = entityId; this.dirty = true; } diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts b/ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts index 11dd292fbe..18e985ed45 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts @@ -24,8 +24,7 @@ import { JsonSchemaData, JsonFormData } from './json-form.models'; -import { isDefined, isString, isUndefined } from '@core/utils'; -import * as equal from 'deep-equal'; +import { isDefined, isEqual, isString, isUndefined } from '@core/utils'; function validateBySchema(schema: any, value: any): SchemaValidationResult { return tv.validateResult(value, schema); @@ -530,7 +529,7 @@ function setValue(obj: any, key: string, val: any): boolean { changed = true; } } else { - changed = !equal(obj[key], val); + changed = !isEqual(obj[key], val); obj[key] = val; } } diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts b/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts index c6888685f3..29d21b9e5f 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts @@ -14,10 +14,6 @@ /// limitations under the License. /// -import { isUndefined, isDefined, isString } from '@app/core/utils'; -import * as equal from 'deep-equal'; -import ObjectPath from 'objectpath'; -import * as React from 'react'; import * as tinycolor_ from 'tinycolor2'; import { GroupInfo } from '@shared/models/widget.models'; diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index e6f8c23aef..5094c52664 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -968,4 +968,13 @@ mat-label { height: 100%; } } + + .tb-tooltip-multiline { + max-width: 400px; + height: auto !important; + padding-top: 6px; + padding-bottom: 6px; + line-height: 1.5; + white-space: pre-line; + } }