Browse Source

Propagate UI changes.

pull/2471/head
Igor Kulikov 7 years ago
parent
commit
15d7970ba9
  1. 2
      application/src/main/data/json/system/widget_bundles/cards.json
  2. 153
      ui-ngx/package-lock.json
  3. 1
      ui-ngx/package.json
  4. 9
      ui-ngx/src/app/core/api/alias-controller.ts
  5. 7
      ui-ngx/src/app/core/api/widget-subscription.ts
  6. 11
      ui-ngx/src/app/core/services/item-buffer.service.ts
  7. 7
      ui-ngx/src/app/core/utils.ts
  8. 1
      ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html
  9. 2
      ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.html
  10. 2
      ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html
  11. 2
      ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html
  12. 4
      ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts
  13. 2
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html
  14. 5
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts
  15. 139
      ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts
  16. 159
      ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts
  17. 2
      ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.html
  18. 3
      ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.scss
  19. 6
      ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts
  20. 3
      ui-ngx/src/app/modules/home/components/widget/lib/rpc/led-indicator.component.scss
  21. 3
      ui-ngx/src/app/modules/home/components/widget/lib/rpc/round-switch.component.scss
  22. 3
      ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.scss
  23. 8
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  24. 7
      ui-ngx/src/app/modules/home/models/dashboard-component.models.ts
  25. 2
      ui-ngx/src/app/modules/home/pages/dashboard/states/manage-dashboard-states-dialog.component.html
  26. 38
      ui-ngx/src/app/shared/components/entity/entity-keys-list.component.ts
  27. 5
      ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts
  28. 4
      ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts
  29. 9
      ui-ngx/src/styles.scss

2
application/src/main/data/json/system/widget_bundles/cards.json

@ -31,7 +31,7 @@
"resources": [], "resources": [],
"templateHtml": "<tb-entities-table-widget \n [ctx]=\"ctx\">\n</tb-entities-table-widget>", "templateHtml": "<tb-entities-table-widget \n [ctx]=\"ctx\">\n</tb-entities-table-widget>",
"templateCss": "", "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}", "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}", "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;\"}]}]}" "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;\"}]}]}"

153
ui-ngx/package-lock.json

@ -4905,32 +4905,6 @@
"mimic-response": "^1.0.0" "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": { "deep-freeze-strict": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/deep-freeze-strict/-/deep-freeze-strict-1.1.1.tgz", "resolved": "https://registry.npmjs.org/deep-freeze-strict/-/deep-freeze-strict-1.1.1.tgz",
@ -5007,6 +4981,7 @@
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": { "requires": {
"object-keys": "^1.0.12" "object-keys": "^1.0.12"
} }
@ -5582,6 +5557,7 @@
"version": "1.17.4", "version": "1.17.4",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
"integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
"dev": true,
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
@ -5596,31 +5572,11 @@
"string.prototype.trimright": "^2.1.1" "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": { "es-to-primitive": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"dev": true,
"requires": { "requires": {
"is-callable": "^1.1.4", "is-callable": "^1.1.4",
"is-date-object": "^1.0.1", "is-date-object": "^1.0.1",
@ -6328,7 +6284,8 @@
"function-bind": { "function-bind": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
}, },
"gauge": { "gauge": {
"version": "2.7.4", "version": "2.7.4",
@ -6748,6 +6705,7 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": { "requires": {
"function-bind": "^1.1.1" "function-bind": "^1.1.1"
} }
@ -6797,7 +6755,8 @@
"has-symbols": { "has-symbols": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "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": { "has-to-string-tag-x": {
"version": "1.4.1", "version": "1.4.1",
@ -7372,18 +7331,14 @@
"is-arguments": { "is-arguments": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
"integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
"dev": true
}, },
"is-arrayish": { "is-arrayish": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" "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": { "is-binary-path": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@ -7393,11 +7348,6 @@
"binary-extensions": "^2.0.0" "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": { "is-buffer": {
"version": "1.1.6", "version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
@ -7407,7 +7357,8 @@
"is-callable": { "is-callable": {
"version": "1.1.5", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", "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": { "is-color-stop": {
"version": "1.1.0", "version": "1.1.0",
@ -7446,7 +7397,8 @@
"is-date-object": { "is-date-object": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", "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": { "is-descriptor": {
"version": "0.1.6", "version": "0.1.6",
@ -7518,22 +7470,12 @@
"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
"dev": true "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": { "is-number": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true "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": { "is-obj": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
@ -7592,6 +7534,7 @@
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
"integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
"dev": true,
"requires": { "requires": {
"has": "^1.0.3" "has": "^1.0.3"
} }
@ -7607,21 +7550,11 @@
"resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
"integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" "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": { "is-stream": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" "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": { "is-subset": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz",
@ -7640,6 +7573,7 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
"integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
"dev": true,
"requires": { "requires": {
"has-symbols": "^1.0.1" "has-symbols": "^1.0.1"
} }
@ -7662,16 +7596,6 @@
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
"integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" "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": { "is-windows": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
@ -9773,17 +9697,20 @@
"object-inspect": { "object-inspect": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", "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": { "object-is": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz", "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": { "object-keys": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "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": { "object-visit": {
"version": "1.0.1", "version": "1.0.1",
@ -9798,6 +9725,7 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
"integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
"dev": true,
"requires": { "requires": {
"define-properties": "^1.1.2", "define-properties": "^1.1.2",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
@ -11855,6 +11783,7 @@
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
"integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==",
"dev": true,
"requires": { "requires": {
"define-properties": "^1.1.3", "define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1" "es-abstract": "^1.17.0-next.1"
@ -12535,15 +12464,6 @@
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" "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": { "sigmund": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
@ -13260,6 +13180,7 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
"integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
"dev": true,
"requires": { "requires": {
"define-properties": "^1.1.3", "define-properties": "^1.1.3",
"function-bind": "^1.1.1" "function-bind": "^1.1.1"
@ -13269,6 +13190,7 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
"integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
"dev": true,
"requires": { "requires": {
"define-properties": "^1.1.3", "define-properties": "^1.1.3",
"function-bind": "^1.1.1" "function-bind": "^1.1.1"
@ -16048,29 +15970,6 @@
"isexe": "^2.0.0" "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": { "which-module": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",

1
ui-ngx/package.json

@ -46,7 +46,6 @@
"compass-sass-mixins": "^0.12.7", "compass-sass-mixins": "^0.12.7",
"core-js": "^3.6.4", "core-js": "^3.6.4",
"date-fns": "^2.9.0", "date-fns": "^2.9.0",
"deep-equal": "^2.0.1",
"flot": "git://github.com/thingsboard/flot.git#0.9-work", "flot": "git://github.com/thingsboard/flot.git#0.9-work",
"flot.curvedlines": "git://github.com/MichaelZinsmaier/CurvedLines.git#master", "flot.curvedlines": "git://github.com/MichaelZinsmaier/CurvedLines.git#master",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",

9
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 { AliasInfo, IAliasController, StateControllerHolder, StateEntityInfo } from '@core/api/widget-api.models';
import { forkJoin, Observable, of, ReplaySubject, Subject } from 'rxjs'; import { forkJoin, Observable, of, ReplaySubject, Subject } from 'rxjs';
import { DataKey, Datasource, DatasourceType } from '@app/shared/models/widget.models'; 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 { EntityService } from '@core/http/entity.service';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { EntityAliases } from '@shared/models/alias.models'; import { EntityAliases } from '@shared/models/alias.models';
import { EntityInfo } from '@shared/models/entity.models'; import { EntityInfo } from '@shared/models/entity.models';
import * as equal from 'deep-equal';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
export class AliasController implements IAliasController { export class AliasController implements IAliasController {
@ -53,7 +52,7 @@ export class AliasController implements IAliasController {
for (const aliasId of Object.keys(newEntityAliases)) { for (const aliasId of Object.keys(newEntityAliases)) {
const newEntityAlias = newEntityAliases[aliasId]; const newEntityAlias = newEntityAliases[aliasId];
const prevEntityAlias = this.entityAliases[aliasId]; const prevEntityAlias = this.entityAliases[aliasId];
if (!equal(newEntityAlias, prevEntityAlias)) { if (!isEqual(newEntityAlias, prevEntityAlias)) {
changedAliasIds.push(aliasId); changedAliasIds.push(aliasId);
this.setAliasUnresolved(aliasId); this.setAliasUnresolved(aliasId);
} }
@ -76,7 +75,7 @@ export class AliasController implements IAliasController {
const stateEntityInfo = this.resolvedAliasesToStateEntities[aliasId]; const stateEntityInfo = this.resolvedAliasesToStateEntities[aliasId];
const newEntityId = this.stateControllerHolder().getEntityId(stateEntityInfo.entityParamName); const newEntityId = this.stateControllerHolder().getEntityId(stateEntityInfo.entityParamName);
const prevEntityId = stateEntityInfo.entityId; const prevEntityId = stateEntityInfo.entityId;
if (!equal(newEntityId, prevEntityId)) { if (!isEqual(newEntityId, prevEntityId)) {
changedAliasIds.push(aliasId); changedAliasIds.push(aliasId);
this.setAliasUnresolved(aliasId); this.setAliasUnresolved(aliasId);
} }
@ -319,7 +318,7 @@ export class AliasController implements IAliasController {
const aliasInfo = this.resolvedAliases[aliasId]; const aliasInfo = this.resolvedAliases[aliasId];
if (aliasInfo) { if (aliasInfo) {
const prevCurrentEntity = aliasInfo.currentEntity; const prevCurrentEntity = aliasInfo.currentEntity;
if (!equal(currentEntity, prevCurrentEntity)) { if (!isEqual(currentEntity, prevCurrentEntity)) {
aliasInfo.currentEntity = currentEntity; aliasInfo.currentEntity = currentEntity;
this.entityAliasesChangedSubject.next([aliasId]); this.entityAliasesChangedSubject.next([aliasId]);
} }

7
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 { CancelAnimationFrame } from '@core/services/raf.service';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { AlarmInfo, AlarmSearchStatus } from '@shared/models/alarm.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 { AlarmSourceListener } from '@core/http/alarm.service';
import { DatasourceListener } from '@core/api/datasource.service'; import { DatasourceListener } from '@core/api/datasource.service';
import * as deepEqual from 'deep-equal';
import { EntityId } from '@app/shared/models/id/entity-id'; import { EntityId } from '@app/shared/models/id/entity-id';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { entityFields } from '@shared/models/entity.models'; import { entityFields } from '@shared/models/entity.models';
@ -539,7 +538,7 @@ export class WidgetSubscription implements IWidgetSubscription {
onDashboardTimewindowChanged(newDashboardTimewindow: Timewindow): void { onDashboardTimewindowChanged(newDashboardTimewindow: Timewindow): void {
if (this.type === widgetType.timeseries || this.type === widgetType.alarm) { if (this.type === widgetType.timeseries || this.type === widgetType.alarm) {
if (this.useDashboardTimewindow) { if (this.useDashboardTimewindow) {
if (!deepEqual(this.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) { if (!isEqual(this.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) {
this.timeWindowConfig = deepClone(newDashboardTimewindow); this.timeWindowConfig = deepClone(newDashboardTimewindow);
this.update(); this.update();
} }
@ -1010,7 +1009,7 @@ export class WidgetSubscription implements IWidgetSubscription {
private alarmsUpdated(alarms: Array<AlarmInfo>) { private alarmsUpdated(alarms: Array<AlarmInfo>) {
this.notifyDataLoaded(); this.notifyDataLoaded();
const updated = !this.alarms || !deepEqual(this.alarms, alarms); const updated = !this.alarms || !isEqual(this.alarms, alarms);
this.alarms = alarms; this.alarms = alarms;
if (this.subscriptionTimewindow && this.subscriptionTimewindow.realtimeWindowMs) { if (this.subscriptionTimewindow && this.subscriptionTimewindow.realtimeWindowMs) {
this.updateTimewindow(); this.updateTimewindow();

11
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 { EntityAlias, EntityAliasFilter, EntityAliases, EntityAliasInfo, AliasesInfo } from '@shared/models/alias.models';
import { DatasourceType, Widget, WidgetPosition, WidgetSize } from '@shared/models/widget.models'; import { DatasourceType, Widget, WidgetPosition, WidgetSize } from '@shared/models/widget.models';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { deepClone } from '@core/utils'; import { deepClone, isEqual } from '@core/utils';
import * as equal from 'deep-equal';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { Observable, of, throwError } from 'rxjs'; import { Observable, of, throwError } from 'rxjs';
import { map } from 'rxjs/operators'; 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 { RuleChainService } from '@core/http/rule-chain.service';
import { RuleChainImport } from '@shared/models/rule-chain.models'; 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_ITEM = 'widget_item';
const WIDGET_REFERENCE = 'widget_reference'; const WIDGET_REFERENCE = 'widget_reference';
@ -219,7 +216,7 @@ export class ItemBufferService {
let callAliasUpdateFunction = false; let callAliasUpdateFunction = false;
if (aliasesInfo) { if (aliasesInfo) {
const newEntityAliases = this.updateAliases(theDashboard, widget, aliasesInfo); const newEntityAliases = this.updateAliases(theDashboard, widget, aliasesInfo);
const aliasesUpdated = !equal(newEntityAliases, theDashboard.configuration.entityAliases); const aliasesUpdated = !isEqual(newEntityAliases, theDashboard.configuration.entityAliases);
if (aliasesUpdated) { if (aliasesUpdated) {
theDashboard.configuration.entityAliases = newEntityAliases; theDashboard.configuration.entityAliases = newEntityAliases;
if (onAliasesUpdateFunction) { if (onAliasesUpdateFunction) {
@ -405,7 +402,7 @@ export class ItemBufferService {
} }
private isEntityAliasEqual(alias1: EntityAliasInfo, alias2: EntityAliasInfo): boolean { 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 { private getEntityAliasId(entityAliases: EntityAliases, aliasInfo: EntityAliasInfo): string {

7
ui-ngx/src/app/core/utils.ts

@ -14,7 +14,8 @@
/// limitations under the License. /// 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 { finalize, share } from 'rxjs/operators';
import base64js from 'base64-js'; import base64js from 'base64-js';
@ -384,6 +385,10 @@ export function deepClone<T>(target: T, ignoreFields?: string[]): T {
return target; return target;
} }
export function isEqual(a: any, b: any): boolean {
return _.isEqual(a, b);
}
export function guid(): string { export function guid(): string {
function s4(): string { function s4(): string {
return Math.floor((1 + Math.random()) * 0x10000) return Math.floor((1 + Math.random()) * 0x10000)

1
ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html

@ -85,6 +85,7 @@
<span [fxShow]="widget.showTitle" <span [fxShow]="widget.showTitle"
[ngStyle]="widget.titleStyle" [ngStyle]="widget.titleStyle"
[matTooltip]="widget.titleTooltip" [matTooltip]="widget.titleTooltip"
matTooltipClass="tb-tooltip-multiline"
matTooltipPosition="above" matTooltipPosition="above"
class="mat-subheading-2 title"> class="mat-subheading-2 title">
<mat-icon *ngIf="widget.showTitleIcon" [ngStyle]="widget.titleIconStyle">{{widget.titleIcon}}</mat-icon> <mat-icon *ngIf="widget.showTitleIcon" [ngStyle]="widget.titleIconStyle">{{widget.titleIcon}}</mat-icon>

2
ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.html

@ -43,7 +43,7 @@
[(ngModel)]="action.customFunction" [(ngModel)]="action.customFunction"
(ngModelChange)="onActionUpdated()" (ngModelChange)="onActionUpdated()"
[fillHeight]="true" [fillHeight]="true"
[functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'htmlTemplate', 'additionalParams']" [functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel']"
[validationArgs]="[]"> [validationArgs]="[]">
</tb-js-func> </tb-js-func>
</div> </div>

2
ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html

@ -93,7 +93,7 @@
<tb-js-func <tb-js-func
[(ngModel)]="action.customFunction" [(ngModel)]="action.customFunction"
(ngModelChange)="notifyActionUpdated()" (ngModelChange)="notifyActionUpdated()"
[functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'htmlTemplate', 'additionalParams']" [functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel']"
[validationArgs]="[]"> [validationArgs]="[]">
</tb-js-func> </tb-js-func>
</mat-tab> </mat-tab>

2
ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html

@ -133,7 +133,7 @@
<ng-template [ngSwitchCase]="widgetActionType.custom"> <ng-template [ngSwitchCase]="widgetActionType.custom">
<tb-js-func <tb-js-func
formControlName="customFunction" formControlName="customFunction"
[functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams']" [functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']"
[validationArgs]="[]" [validationArgs]="[]"
></tb-js-func> ></tb-js-func>
</ng-template> </ng-template>

4
ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts

@ -193,7 +193,9 @@ export class TbCanvasDigitalGauge {
} }
const value = tvPair[1]; const value = tvPair[1];
if(value !== this.gauge.value) { if(value !== this.gauge.value) {
this.gauge._value = value; if (!this.gauge.options.animation) {
this.gauge._value = value;
}
this.gauge.value = value; this.gauge.value = value;
} else if (this.localSettings.showTimestamp && this.gauge.timestamp !== timestamp) { } else if (this.localSettings.showTimestamp && this.gauge.timestamp !== timestamp) {
this.gauge.timestamp = timestamp; this.gauge.timestamp = timestamp;

2
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html

@ -84,7 +84,7 @@
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> <mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row [ngClass]="{'tb-current-entity': entityDatasource.isCurrentEntity(entity)}" <mat-row [ngClass]="{'tb-current-entity': entityDatasource.isCurrentEntity(entity)}"
*matRowDef="let entity; columns: displayedColumns;" *matRowDef="let entity; columns: displayedColumns;"
(click)="onRowClick($event, entity)"></mat-row> (click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row>
</mat-table> </mat-table>
<span [fxShow]="entityDatasource.isEmpty() | async" <span [fxShow]="entityDatasource.isEmpty() | async"
fxLayoutAlign="center center" fxLayoutAlign="center center"

5
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts

@ -475,12 +475,13 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
} }
} }
public onRowClick($event: Event, entity: EntityData) { public onRowClick($event: Event, entity: EntityData, isDouble?: boolean) {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
this.entityDatasource.toggleCurrentEntity(entity); this.entityDatasource.toggleCurrentEntity(entity);
const descriptors = this.ctx.actionsApi.getActionDescriptors('rowClick'); const actionSourceId = isDouble ? 'rowDoubleClick' : 'rowClick';
const descriptors = this.ctx.actionsApi.getActionDescriptors(actionSourceId);
if (descriptors.length) { if (descriptors.length) {
let entityId; let entityId;
let entityName; let entityName;

139
ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts

@ -79,6 +79,18 @@ export interface TbFlotSeriesHoverInfo {
distance: number; distance: number;
} }
export interface TbFlotThresholdMarking {
lineWidth?: number;
color?: string;
[key: string]: any;
}
export interface TbFlotThresholdKeySettings {
yaxis: number;
lineWidth: number;
color: string;
}
export interface TbFlotGridSettings { export interface TbFlotGridSettings {
color: string; color: string;
backgroundColor: string; backgroundColor: string;
@ -133,12 +145,19 @@ export interface TbFlotComparisonSettings {
xaxisSecond: TbFlotSecondXAxisSettings; xaxisSecond: TbFlotSecondXAxisSettings;
} }
export interface TbFlotGraphSettings extends TbFlotBaseSettings, TbFlotComparisonSettings { export interface TbFlotThresholdsSettings {
thresholdsLineWidth: number;
}
export interface TbFlotGraphSettings extends TbFlotBaseSettings, TbFlotThresholdsSettings, TbFlotComparisonSettings {
smoothLines: boolean; smoothLines: boolean;
} }
export interface TbFlotBarSettings extends TbFlotBaseSettings, TbFlotComparisonSettings { export declare type BarAlignment = 'left' | 'right' | 'center';
export interface TbFlotBarSettings extends TbFlotBaseSettings, TbFlotThresholdsSettings, TbFlotComparisonSettings {
defaultBarWidth: number; defaultBarWidth: number;
barAlignment: BarAlignment;
} }
export interface TbFlotPieSettings { export interface TbFlotPieSettings {
@ -158,6 +177,23 @@ export interface TbFlotPieSettings {
export declare type TbFlotYAxisPosition = 'left' | 'right'; export declare type TbFlotYAxisPosition = 'left' | 'right';
export declare type TbFlotXAxisPosition = 'top' | 'bottom'; export declare type TbFlotXAxisPosition = 'top' | 'bottom';
export declare type TbFlotThresholdValueSource = 'predefinedValue' | 'entityAttribute';
export interface TbFlotKeyThreshold {
thresholdValueSource: TbFlotThresholdValueSource;
thresholdEntityAlias: string;
thresholdAttribute: string;
thresholdValue: number;
lineWidth: number;
color: string;
}
export interface TbFlotKeyComparisonSettings {
showValuesForComparison: boolean;
comparisonValuesLabel: string;
color: string;
}
export interface TbFlotKeySettings { export interface TbFlotKeySettings {
excludeFromStacking: boolean; excludeFromStacking: boolean;
hideDataByDefault: boolean; hideDataByDefault: boolean;
@ -180,6 +216,8 @@ export interface TbFlotKeySettings {
axisTickSize: number; axisTickSize: number;
axisPosition: TbFlotYAxisPosition; axisPosition: TbFlotYAxisPosition;
axisTicksFormatter: string; axisTicksFormatter: string;
thresholds: TbFlotKeyThreshold[];
comparisonSettings: TbFlotKeyComparisonSettings;
} }
export function flotSettingsSchema(chartType: ChartType): JsonSettingsSchema { export function flotSettingsSchema(chartType: ChartType): JsonSettingsSchema {
@ -212,6 +250,17 @@ export function flotSettingsSchema(chartType: ChartType): JsonSettingsSchema {
type: 'number', type: 'number',
default: 600 default: 600
}; };
properties.barAlignment = {
title: 'Bar alignment',
type: 'string',
default: 'left'
};
}
if (chartType === 'graph' || chartType === 'bar') {
properties.thresholdsLineWidth = {
title: 'Default line width for all thresholds',
type: 'number'
};
} }
properties.shadowSize = { properties.shadowSize = {
title: 'Shadow size', title: 'Shadow size',
@ -362,6 +411,28 @@ export function flotSettingsSchema(chartType: ChartType): JsonSettingsSchema {
} }
if (chartType === 'bar') { if (chartType === 'bar') {
schema.form.push('defaultBarWidth'); schema.form.push('defaultBarWidth');
schema.form.push({
key: 'barAlignment',
type: 'rc-select',
multiple: false,
items: [
{
value: 'left',
label: 'Left'
},
{
value: 'right',
label: 'Right'
},
{
value: 'center',
label: 'Center'
}
]
});
}
if (chartType === 'graph' || chartType === 'bar') {
schema.form.push('thresholdsLineWidth');
} }
schema.form.push('shadowSize'); schema.form.push('shadowSize');
schema.form.push({ schema.form.push({
@ -837,6 +908,70 @@ export function flotDatakeySettingsSchema(defaultShowLines: boolean, chartType:
const properties = schema.schema.properties; const properties = schema.schema.properties;
if (chartType === 'graph' || chartType === 'bar') { if (chartType === 'graph' || chartType === 'bar') {
properties.thresholds = {
title: 'Thresholds',
type: 'array',
items: {
title: 'Threshold',
type: 'object',
properties: {
thresholdValueSource: {
title: 'Threshold value source',
type: 'string',
default: 'predefinedValue'
},
thresholdEntityAlias: {
title: 'Thresholds source entity alias',
type: 'string'
},
thresholdAttribute: {
title: 'Threshold source entity attribute',
type: 'string'
},
thresholdValue: {
title: 'Threshold value (if predefined value is selected)',
type: 'number'
},
lineWidth: {
title: 'Line width',
type: 'number'
},
color: {
title: 'Color',
type: 'string'
}
}
},
required: []
};
schema.form.push({
key: 'thresholds',
items: [
{
key: 'thresholds[].thresholdValueSource',
type: 'rc-select',
multiple: false,
items: [
{
value: 'predefinedValue',
label: 'Predefined value (Default)'
},
{
value: 'entityAttribute',
label: 'Value taken from entity attribute'
}
]
},
'thresholds[].thresholdValue',
'thresholds[].thresholdEntityAlias',
'thresholds[].thresholdAttribute',
{
key: 'thresholds[].color',
type: 'color'
},
'thresholds[].lineWidth'
]
});
properties.comparisonSettings = { properties.comparisonSettings = {
title: 'Comparison Settings', title: 'Comparison Settings',
type: 'object', type: 'object',

159
ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts

@ -16,12 +16,20 @@
import { WidgetContext } from '@home/models/widget-component.models'; import { WidgetContext } from '@home/models/widget-component.models';
import { deepClone, isDefined, isNumber, isUndefined } from '@app/core/utils'; import { deepClone, isDefined, isEqual, isNumber, isUndefined } from '@app/core/utils';
import { IWidgetSubscription } from '@core/api/widget-api.models'; import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models';
import { DatasourceData, JsonSettingsSchema } from '@app/shared/models/widget.models'; import {
DataKey,
Datasource,
DatasourceData,
DatasourceType,
JsonSettingsSchema,
widgetType
} from '@app/shared/models/widget.models';
import { import {
ChartType, ChartType,
flotDatakeySettingsSchema, flotPieDatakeySettingsSchema, flotDatakeySettingsSchema,
flotPieDatakeySettingsSchema,
flotPieSettingsSchema, flotPieSettingsSchema,
flotSettingsSchema, flotSettingsSchema,
TbFlotAxisOptions, TbFlotAxisOptions,
@ -33,6 +41,8 @@ import {
TbFlotSeries, TbFlotSeries,
TbFlotSeriesHoverInfo, TbFlotSeriesHoverInfo,
TbFlotSettings, TbFlotSettings,
TbFlotThresholdKeySettings,
TbFlotThresholdMarking,
TbFlotTicksFormatterFunction, TbFlotTicksFormatterFunction,
TooltipValueFormatFunction TooltipValueFormatFunction
} from './flot-widget.models'; } from './flot-widget.models';
@ -40,8 +50,9 @@ import * as moment_ from 'moment';
import * as tinycolor_ from 'tinycolor2'; import * as tinycolor_ from 'tinycolor2';
import { AggregationType } from '@shared/models/time/time.models'; import { AggregationType } from '@shared/models/time/time.models';
import { CancelAnimationFrame } from '@core/services/raf.service'; import { CancelAnimationFrame } from '@core/services/raf.service';
import Timeout = NodeJS.Timeout;
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import Timeout = NodeJS.Timeout;
const tinycolor = tinycolor_; const tinycolor = tinycolor_;
const moment = moment_; const moment = moment_;
@ -51,12 +62,13 @@ const flotPieDatakeySettingsSchemaValue = flotPieDatakeySettingsSchema;
export class TbFlot { export class TbFlot {
private readonly utils: UtilsService;
private settings: TbFlotSettings; private settings: TbFlotSettings;
private readonly tooltip: JQuery<any>; private readonly tooltip: JQuery<any>;
private readonly yAxisTickFormatter: TbFlotTicksFormatterFunction; private readonly yAxisTickFormatter: TbFlotTicksFormatterFunction;
private ticksFormatterFunction: TbFlotTicksFormatterFunction;
private readonly yaxis: TbFlotAxisOptions; private readonly yaxis: TbFlotAxisOptions;
private readonly xaxis: TbFlotAxisOptions; private readonly xaxis: TbFlotAxisOptions;
private yaxes: Array<TbFlotAxisOptions>; private yaxes: Array<TbFlotAxisOptions>;
@ -73,6 +85,9 @@ export class TbFlot {
private readonly defaultBarWidth: number; private readonly defaultBarWidth: number;
private thresholdsSourcesSubscription: IWidgetSubscription;
private predefinedThresholds: TbFlotThresholdMarking[];
private plotInited = false; private plotInited = false;
private plot: JQueryPlot; private plot: JQueryPlot;
@ -118,7 +133,7 @@ export class TbFlot {
constructor(private ctx: WidgetContext, private readonly chartType: ChartType) { constructor(private ctx: WidgetContext, private readonly chartType: ChartType) {
this.chartType = this.chartType || 'line'; this.chartType = this.chartType || 'line';
this.settings = ctx.settings as TbFlotSettings; 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'); this.tooltip = $('#flot-series-tooltip');
if (this.tooltip.length === 0) { if (this.tooltip.length === 0) {
this.tooltip = this.createTooltipElement(); this.tooltip = this.createTooltipElement();
@ -145,7 +160,8 @@ export class TbFlot {
grid: { grid: {
hoverable: true, hoverable: true,
mouseActiveRadius: 10, mouseActiveRadius: 10,
autoHighlight: this.tooltipIndividual === true autoHighlight: this.tooltipIndividual === true,
markings: []
}, },
selection : { mode : ctx.isMobile ? null : 'x' }, selection : { mode : ctx.isMobile ? null : 'x' },
legend : { legend : {
@ -167,7 +183,7 @@ export class TbFlot {
}; };
if (this.settings.xaxis) { if (this.settings.xaxis) {
this.xaxis.font.color = this.settings.xaxis.color || this.xaxis.font.color; 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.color = this.xaxis.font.color;
this.xaxis.labelFont.size = this.xaxis.font.size + 2; this.xaxis.labelFont.size = this.xaxis.font.size + 2;
this.xaxis.labelFont.weight = 'bold'; 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.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.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.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.color = this.yaxis.font.color;
this.yaxis.labelFont.size = this.yaxis.font.size + 2; this.yaxis.labelFont.size = this.yaxis.font.size + 2;
this.yaxis.labelFont.weight = 'bold'; this.yaxis.labelFont.weight = 'bold';
@ -244,7 +260,7 @@ export class TbFlot {
return ''; 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.position = this.settings.xaxisSecond.axisPosition;
} }
xaxis.tickLength = 0; 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') { if (this.chartType === 'bar') {
this.options.series.lines = { this.options.series.lines = {
show: false, show: false,
@ -279,7 +299,8 @@ export class TbFlot {
this.options.series.bars = { this.options.series.bars = {
show: true, show: true,
lineWidth: 0, lineWidth: 0,
fill: 0.9 fill: 0.9,
align: this.settings.barAlignment || 'left'
}; };
this.defaultBarWidth = this.settings.defaultBarWidth || 600; this.defaultBarWidth = this.settings.defaultBarWidth || 600;
} }
@ -346,6 +367,8 @@ export class TbFlot {
const colors: string[] = []; const colors: string[] = [];
this.yaxes = []; this.yaxes = [];
const yaxesMap: {[units: string]: TbFlotAxisOptions} = {}; const yaxesMap: {[units: string]: TbFlotAxisOptions} = {};
const predefinedThresholds: TbFlotThresholdMarking[] = [];
const thresholdsDatasources: Datasource[] = [];
let tooltipValueFormatFunction: TooltipValueFormatFunction = null; let tooltipValueFormatFunction: TooltipValueFormatFunction = null;
if (this.settings.tooltipValueFormatter && this.settings.tooltipValueFormatter.length) { if (this.settings.tooltipValueFormatter && this.settings.tooltipValueFormatter.length) {
@ -441,8 +464,54 @@ export class TbFlot {
series.yaxis = series.yaxisIndex + 1; series.yaxis = series.yaxisIndex + 1;
yaxis.keysInfo[i] = {hidden: false}; yaxis.keysInfo[i] = {hidden: false};
yaxis.show = true; 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.colors = colors;
this.options.yaxes = deepClone(this.yaxes); this.options.yaxes = deepClone(this.yaxes);
if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') { if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') {
@ -714,6 +783,72 @@ export class TbFlot {
return yaxis; 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, private seriesInfoDiv(label: string, color: string, value: any,
units: string, trackDecimals: number, active: boolean, units: string, trackDecimals: number, active: boolean,
percent: number, valueFormatFunction: TooltipValueFormatFunction): JQuery<HTMLElement> { percent: number, valueFormatFunction: TooltipValueFormatFunction): JQuery<HTMLElement> {

2
ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.html

@ -29,7 +29,7 @@
<div class="top"></div> <div class="top"></div>
<div #knobErrorContainer class="error-container" [ngStyle]="{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}" <div #knobErrorContainer class="error-container" [ngStyle]="{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}"
fxLayout="row" fxLayoutAlign="center center"> fxLayout="row" fxLayoutAlign="center center">
<span #knobError class="knob-error">{{ error }}</span> <span #knobError class="knob-error" [innerHTML]="error"></span>
</div> </div>
<div #knobTitleContainer class="title-container" fxLayout="row" fxLayoutAlign="center center"> <div #knobTitleContainer class="title-container" fxLayout="row" fxLayoutAlign="center center">
<span #knobTitle class="knob-title">{{ title }}</span> <span #knobTitle class="knob-title">{{ title }}</span>

3
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-pct: percentage(.18) !default;
$minmax-container-margin-bottom-pct: percentage(.12) !default; $minmax-container-margin-bottom-pct: percentage(.12) !default;
$background-color: #e6e7e8 !default;
:host { :host {
.tb-knob { .tb-knob {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: $background-color;
.knob { .knob {
position: relative; position: relative;

6
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 { CanvasDigitalGauge, CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge';
import GenericOptions = CanvasGauges.GenericOptions; import GenericOptions = CanvasGauges.GenericOptions;
import * as tinycolor_ from 'tinycolor2'; import * as tinycolor_ from 'tinycolor2';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
const tinycolor = tinycolor_; const tinycolor = tinycolor_;
@ -338,6 +339,9 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
let textWidth = this.measureTextWidth(text, fontSize); let textWidth = this.measureTextWidth(text, fontSize);
while (textWidth > maxWidth) { while (textWidth > maxWidth) {
fontSize--; fontSize--;
if (fontSize < 0) {
break;
}
textWidth = this.measureTextWidth(text, fontSize); textWidth = this.measureTextWidth(text, fontSize);
} }
element.css({fontSize: fontSize+'px', lineHeight: fontSize+'px'}); 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 { private measureTextWidth(text: string, fontSize: number): number {
this.textMeasure.css({fontSize: fontSize+'px', lineHeight: fontSize+'px'}); this.textMeasure.css({fontSize: fontSize+'px', lineHeight: fontSize+'px'});
this.textMeasure.text(text); this.textMeasure.html(text);
return this.textMeasure.width(); return this.textMeasure.width();
} }

3
ui-ngx/src/app/modules/home/components/widget/lib/rpc/led-indicator.component.scss

@ -17,13 +17,10 @@
$error-height: 14px !default; $error-height: 14px !default;
$background-color: #e6e7e8 !default;
:host { :host {
.tb-led-indicator { .tb-led-indicator {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: $background-color;
.title-container { .title-container {
.led-title { .led-title {

3
ui-ngx/src/app/modules/home/components/widget/lib/rpc/round-switch.component.scss

@ -17,13 +17,10 @@
$error-height: 14px !default; $error-height: 14px !default;
$background-color: #e6e7e8 !default;
:host { :host {
.tb-round-switch { .tb-round-switch {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: $background-color;
.title-container { .title-container {
.switch-title { .switch-title {

3
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-img: url("./svg/thumb-bar.svg") !default;
$thumb-bar-checked-img: url("./svg/thumb-bar-checked.svg") !default; $thumb-bar-checked-img: url("./svg/thumb-bar-checked.svg") !default;
$background-color: #e6e7e8 !default;
$error-height: 14px !default; $error-height: 14px !default;
:host { :host {
@ -28,7 +26,6 @@ $error-height: 14px !default;
.tb-switch { .tb-switch {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: $background-color;
.error-container { .error-container {
position: absolute; position: absolute;

8
ui-ngx/src/app/modules/home/components/widget/widget.component.ts

@ -1007,8 +1007,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
additionalParams = {}; additionalParams = {};
} }
const customActionFunction = new Function('$event', 'widgetContext', 'entityId', const customActionFunction = new Function('$event', 'widgetContext', 'entityId',
'entityName', 'additionalParams', customFunction); 'entityName', 'additionalParams', 'entityLabel', customFunction);
customActionFunction($event, this.widgetContext, entityId, entityName, additionalParams); customActionFunction($event, this.widgetContext, entityId, entityName, additionalParams, entityLabel);
} catch (e) { } catch (e) {
// //
} }
@ -1032,8 +1032,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
additionalParams = {}; additionalParams = {};
} }
const customActionPrettyFunction = new Function('$event', 'widgetContext', 'entityId', const customActionPrettyFunction = new Function('$event', 'widgetContext', 'entityId',
'entityName', 'htmlTemplate', 'additionalParams', customPrettyFunction); 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel', customPrettyFunction);
customActionPrettyFunction($event, this.widgetContext, entityId, entityName, htmlTemplate, additionalParams); customActionPrettyFunction($event, this.widgetContext, entityId, entityName, htmlTemplate, additionalParams, entityLabel);
} catch (e) { } catch (e) {
// //
} }

7
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 { IDashboardWidget, WidgetAction, WidgetContext, WidgetHeaderAction } from './widget-component.models';
import { Timewindow } from '@shared/models/time/time.models'; import { Timewindow } from '@shared/models/time/time.models';
import { Observable, of, Subject } from 'rxjs'; 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 { IterableDiffer, KeyValueDiffer, NgZone } from '@angular/core';
import { IAliasController, IStateController } from '@app/core/api/widget-api.models'; import { IAliasController, IStateController } from '@app/core/api/widget-api.models';
import * as deepEqual from 'deep-equal';
export interface WidgetsData { export interface WidgetsData {
widgets: Array<Widget>; widgets: Array<Widget>;
@ -166,8 +165,8 @@ export class DashboardWidgets implements Iterable<DashboardWidget> {
index = this.dashboardWidgets.findIndex((dashboardWidget) => dashboardWidget.widgetId === record.widgetId); index = this.dashboardWidgets.findIndex((dashboardWidget) => dashboardWidget.widgetId === record.widgetId);
if (index > -1) { if (index > -1) {
const prevDashboardWidget = this.dashboardWidgets[index]; const prevDashboardWidget = this.dashboardWidgets[index];
if (!deepEqual(prevDashboardWidget.widget, record.widget) || if (!isEqual(prevDashboardWidget.widget, record.widget) ||
!deepEqual(prevDashboardWidget.widgetLayout, record.widgetLayout)) { !isEqual(prevDashboardWidget.widgetLayout, record.widgetLayout)) {
this.dashboardWidgets[index] = new DashboardWidget(this.dashboard, record.widget, record.widgetLayout); this.dashboardWidgets[index] = new DashboardWidget(this.dashboard, record.widget, record.widgetLayout);
this.dashboardWidgets[index].highlighted = prevDashboardWidget.highlighted; this.dashboardWidgets[index].highlighted = prevDashboardWidget.highlighted;
this.dashboardWidgets[index].selected = prevDashboardWidget.selected; this.dashboardWidgets[index].selected = prevDashboardWidget.selected;

2
ui-ngx/src/app/modules/home/pages/dashboard/states/manage-dashboard-states-dialog.component.html

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<form #statesForm="ngForm" [formGroup]="statesFormGroup" (ngSubmit)="save()" style="min-width: 600px;"> <form #statesForm="ngForm" [formGroup]="statesFormGroup" (ngSubmit)="save()" style="min-width: 620px;">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar fxLayout="row" color="primary">
<h2 translate>dashboard.manage-states</h2> <h2 translate>dashboard.manage-states</h2>
<span fxFlex></span> <span fxFlex></span>

38
ui-ngx/src/app/shared/components/entity/entity-keys-list.component.ts

@ -14,31 +14,21 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {COMMA, ENTER, SEMICOLON} from '@angular/cdk/keycodes'; import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
import {AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, SkipSelf, ViewChild} from '@angular/core'; import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
ControlValueAccessor, import { Observable, of } from 'rxjs';
FormBuilder, import { map, mergeMap, share } from 'rxjs/operators';
FormControl, import { Store } from '@ngrx/store';
FormGroup, import { AppState } from '@app/core/core.state';
FormGroupDirective, import { TranslateService } from '@ngx-translate/core';
NG_VALUE_ACCESSOR, NgForm import { EntityId } from '@shared/models/id/entity-id';
} from '@angular/forms'; import { EntityService } from '@core/http/entity.service';
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 { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatChipList, MatChipInputEvent } from '@angular/material/chips'; import { MatChipInputEvent, MatChipList } from '@angular/material/chips';
import { ErrorStateMatcher } from '@angular/material/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {DataKeyType} from '@shared/models/telemetry/telemetry.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import * as equal from 'deep-equal'; import { isEqual } from '@core/utils';
@Component({ @Component({
selector: 'tb-entity-keys-list', selector: 'tb-entity-keys-list',
@ -62,7 +52,7 @@ export class EntityKeysListComponent implements ControlValueAccessor, OnInit, Af
@Input() @Input()
set entityId(entityId: EntityId) { set entityId(entityId: EntityId) {
if (!equal(this.entityIdValue, entityId)) { if (!isEqual(this.entityIdValue, entityId)) {
this.entityIdValue = entityId; this.entityIdValue = entityId;
this.dirty = true; this.dirty = true;
} }

5
ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts

@ -24,8 +24,7 @@ import {
JsonSchemaData, JsonSchemaData,
JsonFormData JsonFormData
} from './json-form.models'; } from './json-form.models';
import { isDefined, isString, isUndefined } from '@core/utils'; import { isDefined, isEqual, isString, isUndefined } from '@core/utils';
import * as equal from 'deep-equal';
function validateBySchema(schema: any, value: any): SchemaValidationResult { function validateBySchema(schema: any, value: any): SchemaValidationResult {
return tv.validateResult(value, schema); return tv.validateResult(value, schema);
@ -530,7 +529,7 @@ function setValue(obj: any, key: string, val: any): boolean {
changed = true; changed = true;
} }
} else { } else {
changed = !equal(obj[key], val); changed = !isEqual(obj[key], val);
obj[key] = val; obj[key] = val;
} }
} }

4
ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts

@ -14,10 +14,6 @@
/// limitations under the License. /// 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 * as tinycolor_ from 'tinycolor2';
import { GroupInfo } from '@shared/models/widget.models'; import { GroupInfo } from '@shared/models/widget.models';

9
ui-ngx/src/styles.scss

@ -968,4 +968,13 @@ mat-label {
height: 100%; 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;
}
} }

Loading…
Cancel
Save