Browse Source

Merge pull request #6545 from thingsboard/feature/widget-settings

[3.4] Advanced widget settings: replace auto-generated JSON forms with Angular components
pull/6555/head
Igor Kulikov 4 years ago
committed by GitHub
parent
commit
c5a7159499
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      application/src/main/data/json/system/widget_bundles/alarm_widgets.json
  2. 15
      application/src/main/data/json/system/widget_bundles/analogue_gauges.json
  3. 59
      application/src/main/data/json/system/widget_bundles/cards.json
  4. 36
      application/src/main/data/json/system/widget_bundles/charts.json
  5. 30
      application/src/main/data/json/system/widget_bundles/control_widgets.json
  6. 3
      application/src/main/data/json/system/widget_bundles/date.json
  7. 39
      application/src/main/data/json/system/widget_bundles/digital_gauges.json
  8. 5
      application/src/main/data/json/system/widget_bundles/edge_widgets.json
  9. 12
      application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json
  10. 9
      application/src/main/data/json/system/widget_bundles/gateway_widgets.json
  11. 12
      application/src/main/data/json/system/widget_bundles/gpio_widgets.json
  12. 93
      application/src/main/data/json/system/widget_bundles/input_widgets.json
  13. 77
      application/src/main/data/json/system/widget_bundles/maps.json
  14. 8
      application/src/main/data/json/system/widget_bundles/navigation_widgets.json
  15. 3
      ui-ngx/.editorconfig
  16. 27
      ui-ngx/extra-webpack.config.js
  17. 1
      ui-ngx/package.json
  18. 6
      ui-ngx/src/app/core/api/entity-data-subscription.ts
  19. 2
      ui-ngx/src/app/core/http/rule-chain.service.ts
  20. 41
      ui-ngx/src/app/core/services/resources.service.ts
  21. 4
      ui-ngx/src/app/core/utils.ts
  22. 5
      ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html
  23. 5
      ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts
  24. 5
      ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.html
  25. 5
      ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.ts
  26. 5
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  27. 2
      ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.html
  28. 2
      ui-ngx/src/app/modules/home/components/import-export/import-dialog.component.html
  29. 4
      ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html
  30. 8
      ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.ts
  31. 7
      ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html
  32. 29
      ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts
  33. 2
      ui-ngx/src/app/modules/home/components/widget/data-keys.component.html
  34. 67
      ui-ngx/src/app/modules/home/components/widget/data-keys.component.scss
  35. 16
      ui-ngx/src/app/modules/home/components/widget/data-keys.component.ts
  36. 319
      ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.models.ts
  37. 10
      ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.ts
  38. 771
      ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts
  39. 64
      ui-ngx/src/app/modules/home/components/widget/lib/analogue-linear-gauge.models.ts
  40. 9
      ui-ngx/src/app/modules/home/components/widget/lib/analogue-linear-gauge.ts
  41. 31
      ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.models.ts
  42. 10
      ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.ts
  43. 902
      ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts
  44. 7
      ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts
  45. 937
      ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts
  46. 28
      ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts
  47. 25
      ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts
  48. 7
      ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts
  49. 110
      ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts
  50. 878
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts
  51. 70
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts
  52. 12
      ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts
  53. 37
      ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts
  54. 47
      ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts
  55. 24
      ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts
  56. 7
      ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts
  57. 7
      ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts
  58. 11
      ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts
  59. 7
      ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts
  60. 7
      ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts
  61. 95
      ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.html
  62. 86
      ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.ts
  63. 110
      ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html
  64. 104
      ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts
  65. 65
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/dashboard-state-widget-settings.component.html
  66. 99
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/dashboard-state-widget-settings.component.ts
  67. 22
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/edge-quick-overview-widget-settings.component.html
  68. 52
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/edge-quick-overview-widget-settings.component.ts
  69. 74
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-hierarchy-widget-settings.component.html
  70. 64
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-hierarchy-widget-settings.component.ts
  71. 95
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-key-settings.component.html
  72. 86
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-key-settings.component.ts
  73. 116
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-widget-settings.component.html
  74. 118
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-widget-settings.component.ts
  75. 25
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/html-card-widget-settings.component.html
  76. 54
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/html-card-widget-settings.component.ts
  77. 73
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.html
  78. 40
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.scss
  79. 113
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.ts
  80. 50
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-settings.component.html
  81. 110
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-settings.component.ts
  82. 36
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/markdown-widget-settings.component.html
  83. 76
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/markdown-widget-settings.component.ts
  84. 37
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/qrcode-widget-settings.component.html
  85. 74
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/qrcode-widget-settings.component.ts
  86. 30
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/simple-card-widget-settings.component.html
  87. 52
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/simple-card-widget-settings.component.ts
  88. 69
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.html
  89. 80
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.ts
  90. 76
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.html
  91. 96
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.ts
  92. 96
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html
  93. 98
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts
  94. 25
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/chart-widget-settings.component.html
  95. 52
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/chart-widget-settings.component.ts
  96. 64
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-chart-widget-settings.component.html
  97. 87
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-chart-widget-settings.component.ts
  98. 24
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-key-settings.component.html
  99. 61
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-key-settings.component.ts
  100. 23
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-widget-settings.component.html

6
application/src/main/data/json/system/widget_bundles/alarm_widgets.json

@ -19,8 +19,10 @@
"templateHtml": "<tb-alarms-table-widget \n [ctx]=\"ctx\">\n</tb-alarms-table-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.alarmsTableWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"AlarmTableSettings\",\n \"properties\": {\n \"alarmsTitle\": {\n \"title\": \"Alarms table title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"enableSelection\": {\n \"title\": \"Enable alarms selection\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSearch\": {\n \"title\": \"Enable alarms 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 \"enableFilter\": {\n \"title\": \"Enable alarm filter\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyHeader\": {\n \"title\": \"Always display header\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyAction\": {\n \"title\": \"Always display actions column\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"reserveSpaceForHiddenAction\": {\n \"title\": \"Hidden cell button actions display mode\",\n \"type\": \"string\",\n \"default\": \"true\"\n },\n \"displayDetails\": {\n \"title\": \"Display alarm details\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowAcknowledgment\": {\n \"title\": \"Allow alarms acknowledgment\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowClear\": {\n \"title\": \"Allow alarms clear\",\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\": \"-createdTime\"\n },\n \"useRowStyleFunction\": {\n \"title\": \"Use row style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"rowStyleFunction\": {\n \"title\": \"Row style function: f(alarm, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"alarmsTitle\",\n \"enableSelection\",\n \"enableSearch\",\n \"enableSelectColumnDisplay\",\n \"enableFilter\",\n \"enableStickyHeader\",\n \"enableStickyAction\",\n {\n \"key\": \"reserveSpaceForHiddenAction\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"true\",\n \"label\": \"Show empty space instead of hidden cell button action\"\n },\n {\n \"value\": \"false\",\n \"label\": \"Don't reserve space for hidden action buttons\"\n }\n ]\n },\n \"displayDetails\",\n \"allowAcknowledgment\",\n \"allowClear\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\",\n \"useRowStyleFunction\",\n {\n \"key\": \"rowStyleFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/alarm/row_style_fn\",\n \"condition\": \"model.useRowStyleFunction === true\"\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, alarm, ctx)\",\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, alarm, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"defaultColumnVisibility\": {\n \"title\": \"Default column visibility\",\n \"type\": \"string\",\n \"default\": \"visible\"\n },\n \"columnSelectionToDisplay\": {\n \"title\": \"Column selection in 'Columns to Display'\",\n \"type\": \"string\",\n \"default\": \"enabled\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"columnWidth\",\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/alarm/cell_style_fn\",\n \"condition\": \"model.useCellStyleFunction === true\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/alarm/cell_content_fn\",\n \"condition\": \"model.useCellContentFunction === true\"\n },\n {\n \"key\": \"defaultColumnVisibility\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"visible\",\n \"label\": \"Visible\"\n },\n {\n \"value\": \"hidden\",\n \"label\": \"Hidden\"\n } \n ]\n },\n {\n \"key\": \"columnSelectionToDisplay\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"enabled\",\n \"label\": \"Enabled\"\n },\n {\n \"value\": \"disabled\",\n \"label\": \"Disabled\"\n } \n ]\n }\n ]\n}",
"settingsSchema": "",
"dataKeySettingsSchema": "",
"settingsDirective": "tb-alarms-table-widget-settings",
"dataKeySettingsDirective": "tb-alarms-table-key-settings",
"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,\"allowAcknowledgment\":true,\"allowClear\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}"
}
}

15
application/src/main/data/json/system/widget_bundles/analogue_gauges.json

@ -18,9 +18,10 @@
"resources": [],
"templateHtml": "<canvas id=\"compass\"></canvas>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueCompass(self.ctx, 'compass');\n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueCompass.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueCompass(self.ctx, 'compass');\n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-analogue-compass-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"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;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"minorTicks\":22,\"needleCircleSize\":15,\"showBorder\":true,\"borderOuterWidth\":10,\"colorPlate\":\"#222\",\"colorMajorTicks\":\"#f5f5f5\",\"colorMinorTicks\":\"#ddd\",\"colorNeedle\":\"#f08080\",\"colorNeedleCircle\":\"#e8e8e8\",\"colorBorder\":\"#ccc\",\"majorTickFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ccc\"},\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\",\"animationTarget\":\"needle\",\"majorTicks\":[\"N\",\"NE\",\"E\",\"SE\",\"S\",\"SW\",\"W\",\"NW\"]},\"title\":\"Compass\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
}
},
@ -36,9 +37,10 @@
"resources": [],
"templateHtml": "<canvas id=\"linearGauge\"></canvas>\n",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueLinearGauge(self.ctx, 'linearGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueLinearGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueLinearGauge(self.ctx, 'linearGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-analogue-linear-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 30 - 15;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":100,\"defaultColor\":\"#e64a19\",\"barStrokeWidth\":2.5,\"colorBar\":\"rgba(255, 255, 255, 0.4)\",\"colorBarEnd\":\"rgba(221, 221, 221, 0.38)\",\"showUnitTitle\":true,\"minorTicks\":2,\"valueBox\":true,\"valueInt\":3,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"colorNeedleShadowUp\":\"rgba(2,255,255,0.2)\",\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"highlightsWidth\":10,\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\",\"showBorder\":false,\"majorTicksCount\":8,\"numbersFont\":{\"family\":\"Arial\",\"size\":18,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#78909c\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":26,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#37474f\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":40,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#444\",\"shadowColor\":\"rgba(0,0,0,0.3)\"},\"minValue\":-60,\"highlights\":[{\"from\":-60,\"to\":-40,\"color\":\"#90caf9\"},{\"from\":-40,\"to\":-20,\"color\":\"rgba(144, 202, 249, 0.66)\"},{\"from\":-20,\"to\":0,\"color\":\"rgba(144, 202, 249, 0.33)\"},{\"from\":0,\"to\":20,\"color\":\"rgba(244, 67, 54, 0.2)\"},{\"from\":20,\"to\":40,\"color\":\"rgba(244, 67, 54, 0.4)\"},{\"from\":40,\"to\":60,\"color\":\"rgba(244, 67, 54, 0.6)\"},{\"from\":60,\"to\":80,\"color\":\"rgba(244, 67, 54, 0.8)\"},{\"from\":80,\"to\":100,\"color\":\"#f44336\"}],\"unitTitle\":\"Temperature\",\"units\":\"°C\",\"colorBarProgress\":\"#90caf9\",\"colorBarProgressEnd\":\"#f44336\",\"colorBarStroke\":\"#b0bec5\",\"valueDec\":1},\"title\":\"Thermometer scale\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
@ -54,9 +56,10 @@
"resources": [],
"templateHtml": "<canvas id=\"radialGauge\"></canvas>\n",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueRadialGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-analogue-radial-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":60,\"startAngle\":67.5,\"ticksAngle\":225,\"showBorder\":true,\"defaultColor\":\"#e65100\",\"needleCircleSize\":7,\"highlights\":[{\"from\":-60,\"to\":-50,\"color\":\"#42a5f5\"},{\"from\":-50,\"to\":-40,\"color\":\"rgba(66, 165, 245, 0.83)\"},{\"from\":-40,\"to\":-30,\"color\":\"rgba(66, 165, 245, 0.66)\"},{\"from\":-30,\"to\":-20,\"color\":\"rgba(66, 165, 245, 0.5)\"},{\"from\":-20,\"to\":-10,\"color\":\"rgba(66, 165, 245, 0.33)\"},{\"from\":-10,\"to\":0,\"color\":\"rgba(66, 165, 245, 0.16)\"},{\"from\":0,\"to\":10,\"color\":\"rgba(229, 115, 115, 0.16)\"},{\"from\":10,\"to\":20,\"color\":\"rgba(229, 115, 115, 0.33)\"},{\"from\":20,\"to\":30,\"color\":\"rgba(229, 115, 115, 0.5)\"},{\"from\":30,\"to\":40,\"color\":\"rgba(229, 115, 115, 0.66)\"},{\"from\":40,\"to\":50,\"color\":\"rgba(229, 115, 115, 0.83)\"},{\"from\":50,\"to\":60,\"color\":\"#e57373\"}],\"showUnitTitle\":true,\"colorPlate\":\"#cfd8dc\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":2,\"valueInt\":3,\"valueDec\":1,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":1000,\"animationRule\":\"bounce\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"units\":\"°C\",\"majorTicksCount\":12,\"numbersFont\":{\"family\":\"Roboto\",\"size\":20,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"size\":30,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\"},\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"unitTitle\":\"Temperature\",\"minValue\":-60},\"title\":\"Temperature radial gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
@ -72,9 +75,10 @@
"resources": [],
"templateHtml": "<canvas id=\"radialGauge\"></canvas>\n",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueRadialGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-analogue-radial-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 220) {\\n\\tvalue = 220;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":180,\"startAngle\":45,\"ticksAngle\":270,\"showBorder\":false,\"defaultColor\":\"#e65100\",\"needleCircleSize\":7,\"highlights\":[{\"from\":80,\"to\":120,\"color\":\"#fdd835\"},{\"color\":\"#e57373\",\"from\":120,\"to\":180}],\"showUnitTitle\":false,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":2,\"valueInt\":3,\"minValue\":0,\"valueDec\":0,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"units\":\"MPH\",\"majorTicksCount\":9,\"numbersFont\":{\"family\":\"Roboto\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"size\":32,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\",\"family\":\"Segment7Standard\"},\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\"},\"title\":\"Speed gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
@ -90,9 +94,10 @@
"resources": [],
"templateHtml": "<canvas id=\"radialGauge\"></canvas>\n",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.getSettingsSchema = function() {\n return TbAnalogueRadialGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-analogue-radial-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nif (value < -100) {\\n\\tvalue = -100;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":100,\"startAngle\":45,\"ticksAngle\":270,\"showBorder\":true,\"defaultColor\":\"#e65100\",\"needleCircleSize\":10,\"highlights\":[],\"showUnitTitle\":true,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":10,\"valueInt\":3,\"valueDec\":0,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":18,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"size\":36,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\"},\"minValue\":-100,\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\"},\"title\":\"Radial gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
}

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

File diff suppressed because one or more lines are too long

36
application/src/main/data/json/system/widget_bundles/charts.json

@ -23,8 +23,9 @@
"templateHtml": "<canvas id=\"barChart\"></canvas>\n",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var barData = {\n labels: [],\n datasets: []\n };\n \n for (var i = 0; i < self.ctx.datasources.length; i++) {\n var datasource = self.ctx.datasources[i];\n for (var d = 0; d < datasource.dataKeys.length; d++) {\n var dataKey = datasource.dataKeys[d];\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n var dataset = {\n label: dataKey.label + units,\n data: [0],\n backgroundColor: [dataKey.color],\n borderColor: [dataKey.color],\n borderWidth: 1\n }\n barData.datasets.push(dataset);\n }\n }\n\n var ctx = $('#barChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'bar',\n data: barData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scales: {\n yAxes: [{\n ticks: {\n beginAtZero:true\n }\n }]\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var c = 0;\n for (var i = 0; i < self.ctx.chart.data.datasets.length; i++) {\n var dataset = self.ctx.chart.data.datasets[i];\n var cellData = self.ctx.data[i]; \n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n dataset.data[0] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"showTooltip\": {\n \"title\": \"Show Tooltip\",\n \"type\": \"boolean\",\n \"default\": true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTooltip\"\n ]\n}",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-chart-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Bars\"}"
}
},
@ -45,8 +46,9 @@
"templateHtml": "<canvas id=\"pieChart\"></canvas>\n",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n \n var borderColor = self.ctx.settings.borderColor || '#fff';\n var borderWidth = typeof self.ctx.settings.borderWidth !== 'undefined' ? self.ctx.settings.borderWidth : 5;\n \n pieData.datasets.push(dataset);\n \n for (var i=0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n pieData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push(borderColor);\n dataset.borderWidth.push(borderWidth);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n\n var options = {\n responsive: false,\n maintainAspectRatio: false,\n legend: {\n display: true,\n labels: {\n fontColor: '#666'\n }\n },\n tooltips: {\n callbacks: {\n label: function(tooltipItem, data) {\n var label = data.labels[tooltipItem.index];\n var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];\n var content = label + ': ' + value;\n var units = self.ctx.settings.units ? self.ctx.settings.units : self.ctx.units;\n if (units) {\n content += ' ' + units;\n } \n return content;\n }\n }\n }\n };\n\n if (self.ctx.settings.legend) {\n options.legend.display = self.ctx.settings.legend.display !== false;\n options.legend.labels.fontColor = self.ctx.settings.legend.labelsFontColor || '#666';\n }\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'doughnut',\n data: pieData,\n options: options\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"showTooltip\": {\n \"title\": \"Show Tooltip\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"borderWidth\": {\n \"title\": \"Border width\",\n \"type\": \"number\",\n \"default\": 5\n },\n \"borderColor\": {\n \"title\": \"Border color\",\n \"type\": \"string\",\n \"default\": \"#fff\"\n },\n \"legend\": {\n \"title\": \"Legend settings\",\n \"type\": \"object\",\n \"properties\": {\n \"display\": {\n \"title\": \"Display legend\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"labelsFontColor\": {\n \"title\": \"Labels font color\",\n \"type\": \"string\",\n \"default\": \"#666\"\n }\n }\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTooltip\",\n \"borderWidth\", \n {\n \"key\": \"borderColor\",\n \"type\": \"color\"\n }, \n {\n \"key\": \"legend\",\n \"items\": [\n \"legend.display\",\n {\n \"key\": \"legend.labelsFontColor\",\n \"type\": \"color\"\n }\n ]\n }\n ]\n}",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-doughnut-chart-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#26a69a\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#f57c00\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#afb42b\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#673ab7\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"borderWidth\":5,\"borderColor\":\"#fff\",\"legend\":{\"display\":true,\"labelsFontColor\":\"#666666\"}},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
@ -62,10 +64,12 @@
"resources": [],
"templateHtml": "",
"templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.pie-label {\n font-size: 12px;\n font-family: 'Roboto';\n font-weight: bold;\n text-align: center;\n padding: 2px;\n color: white;\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'pie'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.pieSettingsSchema();\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.pieDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\nself.actionSources = function() {\n return {\n 'sliceClick': {\n name: 'widget-action.pie-slice-click',\n multiple: false\n }\n };\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'pie'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\nself.actionSources = function() {\n return {\n 'sliceClick': {\n name: 'widget-action.pie-slice-click',\n multiple: false\n }\n };\n}\n",
"settingsSchema": "{}\n",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
"settingsDirective": "tb-flot-pie-widget-settings",
"dataKeySettingsDirective": "tb-flot-pie-key-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie- Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
{
@ -85,8 +89,9 @@
"templateHtml": "<canvas id=\"pieChart\"></canvas>\n",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n \n pieData.datasets.push(dataset);\n \n for (var i=0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n pieData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n var borderColor = tinycolor(dataKey.color).darken();\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push('#fff');\n dataset.borderWidth.push(5);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'pie',\n data: pieData,\n options: {\n responsive: false,\n maintainAspectRatio: false\n }\n }); \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"showTooltip\": {\n \"title\": \"Show Tooltip\",\n \"type\": \"boolean\",\n \"default\": true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTooltip\"\n ]\n}",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-chart-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Pie - Chart.js\"}"
}
},
@ -107,8 +112,9 @@
"templateHtml": "<canvas id=\"pieChart\"></canvas>\n",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n\n pieData.datasets.push(dataset);\n \n for (var i = 0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n pieData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n var borderColor = tinycolor(dataKey.color).darken();\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push('#fff');\n dataset.borderWidth.push(5);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n \n var floatingPoint;\n if (typeof self.ctx.decimals !== 'undefined' && self.ctx.decimals !== null) {\n floatingPoint = self.ctx.widget.config.decimals;\n } else {\n floatingPoint = 2;\n }\n\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'polarArea',\n data: pieData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scale: {\n ticks: {\n callback: function(tick) {\n \treturn tick.toFixed(floatingPoint);\n }\n }\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n if (self.ctx.height >= 70) {\n try {\n self.ctx.chart.resize();\n } catch (e) {}\n }\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"showTooltip\": {\n \"title\": \"Show Tooltip\",\n \"type\": \"boolean\",\n \"default\": true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTooltip\"\n ]\n}",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-chart-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fifth\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.2074391823443591,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Polar Area\"}"
}
},
@ -129,8 +135,9 @@
"templateHtml": "<canvas id=\"radarChart\"></canvas>\n",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var barData = {\n labels: [],\n datasets: []\n };\n\n var backgroundColor = tinycolor(self.ctx.data[0].dataKey.color);\n backgroundColor.setAlpha(0.2);\n var borderColor = tinycolor(self.ctx.data[0].dataKey.color);\n borderColor.setAlpha(1);\n var dataset = {\n label: self.ctx.datasources[0].name,\n data: [],\n backgroundColor: backgroundColor.toRgbString(),\n borderColor: borderColor.toRgbString(),\n pointBackgroundColor: borderColor.toRgbString(),\n pointBorderColor: borderColor.darken().toRgbString(),\n borderWidth: 1\n }\n \n barData.datasets.push(dataset);\n \n for (var i = 0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n barData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n }\n \n var floatingPoint;\n if (typeof self.ctx.decimals !== 'undefined' && self.ctx.decimals !== null) {\n floatingPoint = self.ctx.widget.config.decimals;\n } else {\n floatingPoint = 2;\n }\n\n var ctx = $('#radarChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'radar',\n data: barData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scale: {\n ticks: {\n callback: function(tick) {\n \treturn tick.toFixed(floatingPoint);\n }\n }\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n } \n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n if (self.ctx.height >= 70) {\n self.ctx.chart.resize();\n }\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"showTooltip\": {\n \"title\": \"Show Tooltip\",\n \"type\": \"boolean\",\n \"default\": true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTooltip\"\n ]\n}",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-chart-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Radar\"}"
}
},
@ -146,9 +153,12 @@
"resources": [],
"templateHtml": "",
"templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
"controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true,\n hasAdditionalLatestDataKeys: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.getLatestDataKeySettingsSchema = function() {\n return TbFlot.latestDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true,\n hasAdditionalLatestDataKeys: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}",
"settingsDirective": "tb-flot-line-widget-settings",
"dataKeySettingsDirective": "tb-flot-line-key-settings",
"latestDataKeySettingsDirective": "tb-flot-latest-key-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}"
}
},
@ -164,10 +174,13 @@
"resources": [],
"templateHtml": "",
"templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
"controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.getLatestDataKeySettingsSchema = function() {\n return TbFlot.latestDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true\n };\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true\n };\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}",
"latestDataKeySettingsSchema": "{}",
"settingsDirective": "tb-flot-line-widget-settings",
"dataKeySettingsDirective": "tb-flot-line-key-settings",
"latestDataKeySettingsDirective": "tb-flot-latest-key-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"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;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"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;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Timeseries Line Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}"
}
},
@ -183,9 +196,12 @@
"resources": [],
"templateHtml": "",
"templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
"controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('bar');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(false, 'bar');\n}\n\nself.getLatestDataKeySettingsSchema = function() {\n return TbFlot.latestDatakeySettingsSchema();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true\n };\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'bar'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.flot.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true\n };\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}",
"settingsDirective": "tb-flot-bar-widget-settings",
"dataKeySettingsDirective": "tb-flot-bar-key-settings",
"latestDataKeySettingsDirective": "tb-flot-latest-key-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":true,\"tooltipIndividual\":false,\"defaultBarWidth\":600},\"title\":\"Timeseries Bar Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{}}"
}
}

30
application/src/main/data/json/system/widget_bundles/control_widgets.json

File diff suppressed because one or more lines are too long

3
application/src/main/data/json/system/widget_bundles/date.json

@ -19,8 +19,9 @@
"templateHtml": "<tb-date-range-navigator-widget [ctx]=\"ctx\"></tb-date-range-navigator-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"hidePicker\": {\n \"title\": \"Hide date range picker\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"onePanel\": {\n \"title\": \"Date range picker one panel\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"autoConfirm\": {\n \"title\": \"Date range picker auto confirm\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"showTemplate\": {\n \"title\": \"Date range picker show template\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"firstDayOfWeek\": {\n \"title\": \"First day of the week\",\n \"type\": \"number\",\n \"default\": 1\n },\n \"hideInterval\": {\n \"title\": \"Hide interval\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"initialInterval\": {\n\t\t\t\t\"title\": \"Initial interval\",\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"default\": \"week\"\n\t\t\t},\n \"hideStepSize\": {\n \"title\": \"Hide step size\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"stepSize\": {\n\t\t\t\t\"title\": \"Initial step size\",\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"default\": \"day\"\n\t\t\t},\n \"hideLabels\": {\n \"title\": \"Hide labels\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"useSessionStorage\": {\n \"title\": \"Use session storage\",\n \"type\": \"boolean\",\n \"default\": true\n }\n }\n },\n \"form\": [\n \"hidePicker\",\n\t\t\"onePanel\",\n\t\t\"autoConfirm\",\n\t\t\"showTemplate\",\n\t\t\"firstDayOfWeek\",\n \"hideInterval\",\n {\n\t\t\t\"key\": \"initialInterval\",\n\t\t\t\"type\": \"rc-select\",\n\t\t\t\"multiple\": false,\n\t\t\t\"items\": [\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"hour\",\n\t\t\t\t\t\"label\": \"Hour\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"day\",\n\t\t\t\t\t\"label\": \"Day\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"week\",\n\t\t\t\t\t\"label\": \"Week\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"twoWeeks\",\n\t\t\t\t\t\"label\": \"2 weeks\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"month\",\n\t\t\t\t\t\"label\": \"Month\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"threeMonths\",\n\t\t\t\t\t\"label\": \"3 months\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"sixMonths\",\n\t\t\t\t\t\"label\": \"6 months\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n \"hideStepSize\",\n {\n\t\t\t\"key\": \"stepSize\",\n\t\t\t\"type\": \"rc-select\",\n\t\t\t\"multiple\": false,\n\t\t\t\"items\": [\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"hour\",\n\t\t\t\t\t\"label\": \"Hour\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"day\",\n\t\t\t\t\t\"label\": \"Day\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"week\",\n\t\t\t\t\t\"label\": \"Week\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"twoWeeks\",\n\t\t\t\t\t\"label\": \"2 weeks\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"month\",\n\t\t\t\t\t\"label\": \"Month\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"threeMonths\",\n\t\t\t\t\t\"label\": \"3 months\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"sixMonths\",\n\t\t\t\t\t\"label\": \"6 months\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t\"hideLabels\",\n\t\t\"useSessionStorage\"\n ]\n}",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-date-range-navigator-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"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;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"defaultInterval\":\"week\",\"stepSize\":\"day\"},\"title\":\"Date-range-navigator\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
}
}

39
application/src/main/data/json/system/widget_bundles/digital_gauges.json

@ -18,9 +18,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":36,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":0,\"gaugeColor\":\"#eeeeee\",\"showTitle\":true,\"gaugeType\":\"arc\"},\"title\":\"Gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
@ -36,9 +37,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < -60) {\\n\\tvalue = 60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[\"#304ffe\",\"#7e57c2\",\"#ff4081\",\"#d32f2f\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"dashThickness\":1.5,\"minValue\":-60,\"gaugeColor\":\"#333333\",\"neonGlowBrightness\":35,\"gaugeType\":\"donut\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital thermometer\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
}
},
@ -54,9 +56,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 45) {\\n\\tvalue = 45;\\n} else if (value > 130) {\\n\\tvalue = 130;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"arc\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital speedometer\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
}
},
@ -72,9 +75,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":70,\"dashThickness\":1,\"gaugeType\":\"arc\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Neon gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
}
},
@ -90,9 +94,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 180) {\\n\\tvalue = 180;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#babab2\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\"linear\",\"refreshAnimationTime\":700,\"startAnimationType\":\"linear\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":1.5,\"decimals\":0,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"defaultColor\":\"#444444\",\"gaugeType\":\"arc\"},\"title\":\"LCD gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
@ -108,9 +113,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#babab2\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\"linear\",\"refreshAnimationTime\":700,\"startAnimationType\":\"linear\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"400\",\"size\":16},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":1.5,\"decimals\":0,\"showUnitTitle\":true,\"defaultColor\":\"#444444\",\"gaugeType\":\"verticalBar\",\"units\":\"%\"},\"title\":\"LCD bar gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
@ -126,9 +132,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#388e3c\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"gaugeType\":\"donut\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Simple neon gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
}
},
@ -144,9 +151,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#f57c00\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":12,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":1.5,\"gaugeColor\":\"#eeeeee\",\"showTitle\":false,\"gaugeType\":\"verticalBar\"},\"title\":\"Vertical bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
@ -162,9 +170,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>\n",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "\nself.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"controllerScript": "\nself.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#ef6c00\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":0,\"decimals\":0,\"gaugeColor\":\"#eeeeee\",\"gaugeType\":\"donut\"},\"title\":\"Simple gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
@ -180,9 +189,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 80) {\\n\\tvalue = 80;\\n} else if (value > 160) {\\n\\tvalue = 160;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"horizontalBar\",\"showTitle\":false,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital horizontal bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
}
},
@ -198,9 +208,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#7cb342\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":0,\"decimals\":0,\"roundedLineCap\":true,\"gaugeType\":\"donut\"},\"title\":\"Mini gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
@ -216,9 +227,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>\n",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":0,\"gaugeColor\":\"#eeeeee\",\"showTitle\":true,\"gaugeType\":\"horizontalBar\"},\"title\":\"Horizontal bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"
}
},
@ -234,9 +246,10 @@
"resources": [],
"templateHtml": "<canvas id=\"digitalGauge\"></canvas>",
"templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.getSettingsSchema = function() {\n return TbCanvasDigitalGauge.settingsSchema;\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-digital-gauge-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#3d5afe\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":14},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":8,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#cccccc\"},\"neonGlowBrightness\":20,\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"verticalBar\",\"showTitle\":false,\"minValue\":-60,\"dashThickness\":1.2,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital vertical bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
}
}

5
application/src/main/data/json/system/widget_bundles/edge_widgets.json

@ -19,10 +19,11 @@
"templateHtml": "<tb-edges-overview-widget \n [ctx]=\"ctx\">\n</tb-edges-overview-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true\n };\n}\n\nself.onDestroy = function() {\n};\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EdgeOverviewSettings\",\n \"properties\": {\n \"enableDefaultTitle\": {\n \"title\": \"Display default title\",\n \"type\": \"boolean\",\n \"default\": true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"enableDefaultTitle\"\n ]\n}",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-edge-quick-overview-widget-settings",
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"showTitleIcon\":true,\"titleIcon\":\"router\",\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{},\"title\":\"Edge Quick Overview\",\"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));\"}]}],\"widgetStyle\":{},\"actions\":{}}"
}
}
]
}
}

12
application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json

File diff suppressed because one or more lines are too long

9
application/src/main/data/json/system/widget_bundles/gateway_widgets.json

File diff suppressed because one or more lines are too long

12
application/src/main/data/json/system/widget_bundles/gpio_widgets.json

File diff suppressed because one or more lines are too long

93
application/src/main/data/json/system/widget_bundles/input_widgets.json

File diff suppressed because one or more lines are too long

77
application/src/main/data/json/system/widget_bundles/maps.json

File diff suppressed because one or more lines are too long

8
application/src/main/data/json/system/widget_bundles/navigation_widgets.json

@ -19,8 +19,9 @@
"templateHtml": "<tb-navigation-cards-widget [ctx]=\"ctx\"></tb-navigation-cards-widget>",
"templateCss": "/*#widget-container {\n overflow-y: auto;\n box-sizing: content-box !important;\n cursor: auto;\n}*/\n\n#widget-container #container {\n overflow-y: auto;\n box-sizing: content-box;\n cursor: auto;\n}",
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onResize = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"filterType\": {\n \"title\": \"Filter type\",\n \"type\": \"string\",\n \"default\": \"all\"\n },\n \"filter\": {\n \"title\": \"Items\",\n \"type\": \"array\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"filterType\",\n \"type\": \"radios\",\n \"direction\": \"row\",\n \"titleMap\": [\n {\n \"value\": \"all\",\n \"name\": \"All items\"\n },\n {\n \"value\": \"include\",\n \"name\": \"Include items\"\n },\n {\n \"value\": \"exclude\",\n \"name\": \"Exclude items\"\n }\n ]\n },\n {\n \"key\": \"filter\",\n \"type\": \"rc-select\",\n \"condition\": \"model.filterType !== 'all'\",\n \"tags\": true,\n \"placeholder\": \"Enter urls to filter\",\n \"items\": [{\"value\": \"/devices\", \"label\": \"/devices\"}, {\"value\": \"/assets\", \"label\": \"/assets\"}, {\"value\": \"/deviceProfiles\", \"label\": \"/deviceProfiles\"}]\n }\n ]\n}\n",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-navigation-cards-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"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;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"filterType\":\"all\"},\"title\":\"Navigation cards\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
}
},
@ -37,10 +38,11 @@
"templateHtml": "<tb-navigation-card-widget [ctx]=\"ctx\"></tb-navigation-card-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n\n}\n\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"name\": {\n \"title\": \"Title\",\n \"type\": \"string\",\n \"default\": \"{i18n:device.devices}\"\n },\n \"icon\": {\n \"title\": \"icon\",\n \"type\": \"string\",\n \"default\": \"devices_other\"\n },\n \"path\": {\n \"title\": \"Navigation path\",\n \"type\": \"string\",\n \"default\": \"/devices\"\n }\n },\n \"required\": [\"name\", \"icon\", \"path\"]\n },\n \"form\": [\n \"name\",\n {\n \"key\": \"icon\",\n \"type\": \"icon\"\n },\n \"path\"\n ]\n}\n",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",
"settingsDirective": "tb-navigation-card-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"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;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(255,255,255,0.87)\",\"padding\":\"8px\",\"settings\":{\"name\":\"{i18n:device.devices}\",\"icon\":\"devices_other\",\"path\":\"/devices\"},\"title\":\"Navigation card\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}"
}
}
]
}
}

3
ui-ngx/.editorconfig

@ -24,6 +24,9 @@ indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.json]
indent_size = 4
[*.md]
max_line_length = off
trim_trailing_whitespace = false

27
ui-ngx/extra-webpack.config.js

@ -18,6 +18,7 @@ const JavaScriptOptimizerPlugin = require("@angular-devkit/build-angular/src/web
const webpack = require("webpack");
const dirTree = require("directory-tree");
const ngWebpack = require('@ngtools/webpack');
const keysTransformer = require('ts-transformer-keys/transformer').default;
var langs = [];
@ -58,17 +59,37 @@ module.exports = (config, options) => {
})
);
const index = config.plugins.findIndex(p => p instanceof ngWebpack.ivy.AngularWebpackPlugin || p instanceof ngWebpack.AngularWebpackPlugin);
let angularWebpackPlugin = config.plugins[index];
if (config.mode === 'production') {
const index = config.plugins.findIndex(p => p instanceof ngWebpack.ivy.AngularWebpackPlugin);
const angularCompilerOptions = config.plugins[index].pluginOptions;
const angularCompilerOptions = angularWebpackPlugin.pluginOptions;
angularCompilerOptions.emitClassMetadata = true;
angularCompilerOptions.emitNgModuleScope = true;
config.plugins.splice(index, 1);
config.plugins.push(new ngWebpack.ivy.AngularWebpackPlugin(angularCompilerOptions));
angularWebpackPlugin = new ngWebpack.ivy.AngularWebpackPlugin(angularCompilerOptions);
config.plugins.push(angularWebpackPlugin);
const javascriptOptimizerOptions = config.optimization.minimizer[1].options;
delete javascriptOptimizerOptions.define.ngJitMode;
config.optimization.minimizer.splice(1, 1);
config.optimization.minimizer.push(new JavaScriptOptimizerPlugin(javascriptOptimizerOptions));
}
addTransformerToAngularWebpackPlugin(angularWebpackPlugin, keysTransformer);
return config;
};
function addTransformerToAngularWebpackPlugin(plugin, transformer) {
const originalCreateFileEmitter = plugin.createFileEmitter; // private method
plugin.createFileEmitter = function (program, transformers, getExtraDependencies, onAfterEmit) {
if (!transformers) {
transformers = {};
}
if (!transformers.before) {
transformers = { before: [] };
}
transformers.before.push(transformer(program.getProgram()));
return originalCreateFileEmitter.apply(plugin, [program, transformers, getExtraDependencies, onAfterEmit]);
};
}

1
ui-ngx/package.json

@ -92,6 +92,7 @@
"systemjs": "6.11.0",
"tinycolor2": "^1.4.2",
"tooltipster": "^4.2.8",
"ts-transformer-keys": "^0.4.3",
"tslib": "^2.3.1",
"tv4": "^1.3.0",
"typeface-roboto": "^1.1.13",

6
ui-ngx/src/app/core/api/entity-data-subscription.ts

@ -872,7 +872,11 @@ export class EntityDataSubscription {
}
}
if (tsDataKeys.length) {
this.timeseriesTimer = setTimeout(this.onTimeseriesTick.bind(this, tsDataKeys, true), 0);
if (this.history) {
this.onTimeseriesTick(tsDataKeys, true);
} else {
this.timeseriesTimer = setTimeout(this.onTimeseriesTick.bind(this, tsDataKeys, true), 0);
}
}
if (latestDataKeys.length) {
this.onLatestTick(latestDataKeys, detectChanges);

2
ui-ngx/src/app/core/http/rule-chain.service.ts

@ -219,7 +219,7 @@ export class RuleChainService {
map((res) => {
if (nodeDefinition.configDirective && nodeDefinition.configDirective.length) {
const selector = snakeCase(nodeDefinition.configDirective, '-');
const componentFactory = res.find((factory) =>
const componentFactory = res.factories.find((factory) =>
factory.selector === selector);
if (componentFactory) {
this.ruleNodeConfigFactories[nodeDefinition.configDirective] = componentFactory;

41
ui-ngx/src/app/core/services/resources.service.ts

@ -30,6 +30,11 @@ import { IModulesMap } from '@modules/common/modules-map.models';
declare const System;
export interface ModulesWithFactories {
modules: Type<any>[];
factories: ComponentFactory<any>[];
}
@Injectable({
providedIn: 'root'
})
@ -37,7 +42,7 @@ export class ResourcesService {
private loadedResources: { [url: string]: ReplaySubject<any> } = {};
private loadedModules: { [url: string]: ReplaySubject<Type<any>[]> } = {};
private loadedFactories: { [url: string]: ReplaySubject<ComponentFactory<any>[]> } = {};
private loadedModulesAndFactories: { [url: string]: ReplaySubject<ModulesWithFactories> } = {};
private anchor = this.document.getElementsByTagName('head')[0] || this.document.getElementsByTagName('body')[0];
@ -64,13 +69,13 @@ export class ResourcesService {
return this.loadResourceByType(fileType, url);
}
public loadFactories(url: string, modulesMap: IModulesMap): Observable<ComponentFactory<any>[]> {
if (this.loadedFactories[url]) {
return this.loadedFactories[url].asObservable();
public loadFactories(url: string, modulesMap: IModulesMap): Observable<ModulesWithFactories> {
if (this.loadedModulesAndFactories[url]) {
return this.loadedModulesAndFactories[url].asObservable();
}
modulesMap.init();
const subject = new ReplaySubject<ComponentFactory<any>[]>();
this.loadedFactories[url] = subject;
const subject = new ReplaySubject<ModulesWithFactories>();
this.loadedModulesAndFactories[url] = subject;
import('@angular/compiler').then(
() => {
System.import(url).then(
@ -88,25 +93,29 @@ export class ResourcesService {
c.ngModuleFactory.create(this.injector);
componentFactories.push(...c.componentFactories);
}
this.loadedFactories[url].next(componentFactories);
this.loadedFactories[url].complete();
const modulesWithFactories: ModulesWithFactories = {
modules,
factories: componentFactories
};
this.loadedModulesAndFactories[url].next(modulesWithFactories);
this.loadedModulesAndFactories[url].complete();
} catch (e) {
this.loadedFactories[url].error(new Error(`Unable to init module from url: ${url}`));
delete this.loadedFactories[url];
this.loadedModulesAndFactories[url].error(new Error(`Unable to init module from url: ${url}`));
delete this.loadedModulesAndFactories[url];
}
},
(e) => {
this.loadedFactories[url].error(new Error(`Unable to compile module from url: ${url}`));
delete this.loadedFactories[url];
this.loadedModulesAndFactories[url].error(new Error(`Unable to compile module from url: ${url}`));
delete this.loadedModulesAndFactories[url];
});
} else {
this.loadedFactories[url].error(new Error(`Module '${url}' doesn't have default export!`));
delete this.loadedFactories[url];
this.loadedModulesAndFactories[url].error(new Error(`Module '${url}' doesn't have default export!`));
delete this.loadedModulesAndFactories[url];
}
},
(e) => {
this.loadedFactories[url].error(new Error(`Unable to load module from url: ${url}`));
delete this.loadedFactories[url];
this.loadedModulesAndFactories[url].error(new Error(`Unable to load module from url: ${url}`));
delete this.loadedModulesAndFactories[url];
}
);
}

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

@ -313,6 +313,10 @@ export function deepClone<T>(target: T, ignoreFields?: string[]): T {
return target;
}
export function extractType<T extends object>(target: any, keysOfProps: (keyof T)[]): T {
return _.pick(target, keysOfProps);
}
export function isEqual(a: any, b: any): boolean {
return _.isEqual(a, b);
}

5
ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html

@ -33,9 +33,8 @@
<tb-widget-config
[aliasController]="aliasController"
[functionsOnly]="false"
[entityAliases]="dashboard.configuration.entityAliases"
[filters]="dashboard.configuration.filters"
[dashboardStates]="dashboard.configuration.states"
[dashboard]="dashboard"
[widget]="widget"
formControlName="widgetConfig">
</tb-widget-config>
</fieldset>

5
ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts

@ -100,7 +100,10 @@ export class AddWidgetDialogComponent extends DialogComponent<AddWidgetDialogCom
isDataEnabled,
settingsSchema,
dataKeySettingsSchema,
latestDataKeySettingsSchema
latestDataKeySettingsSchema,
settingsDirective: widgetInfo.settingsDirective,
dataKeySettingsDirective: widgetInfo.dataKeySettingsDirective,
latestDataKeySettingsDirective: widgetInfo.latestDataKeySettingsDirective
};
this.widgetFormGroup = this.fb.group({

5
ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.html

@ -20,9 +20,8 @@
<tb-widget-config
[aliasController]="aliasController"
[functionsOnly]="widgetEditMode"
[entityAliases]="dashboard.configuration.entityAliases"
[filters]="dashboard.configuration.filters"
[dashboardStates]="dashboard.configuration.states"
[dashboard]="dashboard"
[widget]="widget"
formControlName="widgetConfig">
</tb-widget-config>
</fieldset>

5
ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.ts

@ -121,7 +121,10 @@ export class EditWidgetComponent extends PageComponent implements OnInit, OnChan
isDataEnabled,
settingsSchema,
dataKeySettingsSchema,
latestDataKeySettingsSchema
latestDataKeySettingsSchema,
settingsDirective: widgetInfo.settingsDirective,
dataKeySettingsDirective: widgetInfo.dataKeySettingsDirective,
latestDataKeySettingsDirective: widgetInfo.latestDataKeySettingsDirective
};
this.widgetFormGroup.reset({widgetConfig: this.widgetConfig});
}

5
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -149,6 +149,8 @@ import {
} from '@home/components/tokens';
import { DashboardStateComponent } from '@home/components/dashboard-page/dashboard-state.component';
import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component';
import { WidgetSettingsModule } from '@home/components/widget/lib/settings/widget-settings.module';
import { WidgetSettingsComponent } from '@home/components/widget/widget-settings.component';
@NgModule({
declarations:
@ -183,6 +185,7 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai
WidgetContainerComponent,
WidgetComponent,
LegendComponent,
WidgetSettingsComponent,
WidgetConfigComponent,
EntityFilterViewComponent,
EntityFilterComponent,
@ -275,6 +278,7 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai
CommonModule,
SharedModule,
SharedHomeComponentsModule,
WidgetSettingsModule,
Lwm2mProfileComponentsModule,
SnmpDeviceProfileTransportModule,
StatesControllerModule,
@ -303,6 +307,7 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai
WidgetContainerComponent,
WidgetComponent,
LegendComponent,
WidgetSettingsComponent,
WidgetConfigComponent,
EntityFilterViewComponent,
EntityFilterComponent,

2
ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.html

@ -40,7 +40,7 @@
label="{{importFileLabel | translate}}"
[allowedExtensions]="'csv,txt'"
[accept]="'.csv,application/csv,text/csv,.txt,text/plain'"
dropLabel="{{'import.drop-file-csv' | translate}}">
dropLabel="{{'import.drop-file-csv-or' | translate}}">
</tb-file-input>
</fieldset>
</form>

2
ui-ngx/src/app/modules/home/components/import-export/import-dialog.component.html

@ -35,7 +35,7 @@
formControlName="jsonContent"
required
label="{{importFileLabel | translate}}"
dropLabel="{{ 'import.drop-file' | translate }}"
dropLabel="{{ 'import.drop-json-file-or' | translate }}"
accept=".json,application/json"
allowedExtensions="json">
</tb-file-input>

4
ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html

@ -31,7 +31,11 @@
<div mat-dialog-content>
<tb-data-key-config #dataKeyConfig
[dataKeySettingsSchema]="data.dataKeySettingsSchema"
[dataKeySettingsDirective]="data.dataKeySettingsDirective"
[entityAliasId]="data.entityAliasId"
[dashboard]="data.dashboard"
[aliasController]="data.aliasController"
[widget]="data.widget"
[showPostProcessing]="data.showPostProcessing"
[callbacks]="data.callbacks"
formControlName="dataKey">

8
ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.ts

@ -22,13 +22,19 @@ import { AppState } from '@core/core.state';
import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { DialogComponent } from '@shared/components/dialog.component';
import { DataKey } from '@shared/models/widget.models';
import { DataKey, Widget } from '@shared/models/widget.models';
import { DataKeysCallbacks } from './data-keys.component.models';
import { DataKeyConfigComponent } from '@home/components/widget/data-key-config.component';
import { Dashboard } from '@shared/models/dashboard.models';
import { IAliasController } from '@core/api/widget-api.models';
export interface DataKeyConfigDialogData {
dataKey: DataKey;
dataKeySettingsSchema: any;
dataKeySettingsDirective: string;
dashboard: Dashboard;
aliasController: IAliasController;
widget: Widget;
entityAliasId?: string;
showPostProcessing?: boolean;
callbacks?: DataKeysCallbacks;

7
ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html

@ -98,9 +98,12 @@
</mat-tab>
<mat-tab [formGroup]="dataKeySettingsFormGroup" label="{{ 'datakey.advanced' | translate }}" *ngIf="displayAdvanced">
<div class="mat-padding" fxLayout="column">
<tb-json-form
<tb-widget-settings
[dashboard]="dashboard"
[aliasController]="aliasController"
[widget]="widget"
formControlName="settings">
</tb-json-form>
</tb-widget-settings>
</div>
</mat-tab>
</mat-tab-group>

29
ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts

@ -18,7 +18,7 @@ import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@an
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { DataKey } from '@shared/models/widget.models';
import { DataKey, Widget } from '@shared/models/widget.models';
import {
ControlValueAccessor,
FormBuilder,
@ -41,6 +41,8 @@ import { alarmFields } from '@shared/models/alarm.models';
import { JsFuncComponent } from '@shared/components/js-func.component';
import { JsonFormComponentData } from '@shared/components/json-form/json-form-component.models';
import { WidgetService } from '@core/http/widget.service';
import { Dashboard } from '@shared/models/dashboard.models';
import { IAliasController } from '@core/api/widget-api.models';
@Component({
selector: 'tb-data-key-config',
@ -69,9 +71,21 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
@Input()
callbacks: DataKeysCallbacks;
@Input()
dashboard: Dashboard;
@Input()
aliasController: IAliasController;
@Input()
widget: Widget;
@Input()
dataKeySettingsSchema: any;
@Input()
dataKeySettingsDirective: string;
@Input()
showPostProcessing = true;
@ -121,11 +135,16 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
type: DataKeyType.alarm
});
}
if (this.dataKeySettingsSchema && this.dataKeySettingsSchema.schema) {
if (this.dataKeySettingsSchema && this.dataKeySettingsSchema.schema ||
this.dataKeySettingsDirective && this.dataKeySettingsDirective.length) {
this.displayAdvanced = true;
this.dataKeySettingsData = {
schema: this.dataKeySettingsSchema.schema,
form: this.dataKeySettingsSchema.form || ['*']
schema: this.dataKeySettingsSchema?.schema || {
type: 'object',
properties: {}
},
form: this.dataKeySettingsSchema?.form || ['*'],
settingsDirective: this.dataKeySettingsDirective
};
this.dataKeySettingsFormGroup = this.fb.group({
settings: [null, []]
@ -273,7 +292,7 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
}
};
}
if (this.displayAdvanced && !this.dataKeySettingsFormGroup.valid) {
if (this.displayAdvanced && (!this.dataKeySettingsFormGroup.valid || !this.modelValue.settings)) {
return {
dataKeySettings: {
valid: false

2
ui-ngx/src/app/modules/home/components/widget/data-keys.component.html

@ -17,7 +17,7 @@
-->
<mat-form-field appearance="standard" [formGroup]="keysListFormGroup" class="mat-block">
<mat-chip-list #chipList formControlName="keys" tb-chip-draggable (chipDrop)="onChipDrop($event)">
<mat-chip
<mat-chip class="tb-datakey-chip"
*ngFor="let key of keys; let $index = index"
[selectable]="!disabled"
[removable]="!disabled && !isEntityCountDatasource"

67
ui-ngx/src/app/modules/home/components/widget/data-keys.component.scss

@ -13,49 +13,48 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
.mat-chip.mat-standard-chip {
overflow: hidden;
.tb-attribute-chip {
max-width: 100%;
color: rgb(66, 66, 66);
font-weight: normal;
font-size: 16px;
.mat-chip.mat-standard-chip.tb-datakey-chip {
overflow: hidden;
.tb-chip-drag-handle {
cursor: move;
.tb-attribute-chip {
max-width: 100%;
color: rgb(66, 66, 66);
font-weight: normal;
font-size: 16px;
mat-icon {
pointer-events: none;
}
}
.tb-chip-drag-handle {
cursor: move;
.tb-chip-labels {
display: flex;
flex-direction: row;
align-items: flex-end;
min-width: 0;
.tb-chip-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
mat-icon {
pointer-events: none;
}
}
.tb-chip-separator {
white-space: pre;
}
.tb-chip-labels {
display: flex;
flex-direction: row;
align-items: flex-end;
min-width: 0;
.tb-chip-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mat-chip-remove.mat-icon {
width: 24px;
height: 24px;
font-size: 24px;
margin-left: 0;
color: inherit;
opacity: inherit;
.tb-chip-separator {
white-space: pre;
}
}
.mat-chip-remove.mat-icon {
width: 24px;
height: 24px;
font-size: 24px;
margin-left: 0;
color: inherit;
opacity: inherit;
}
}
}

16
ui-ngx/src/app/modules/home/components/widget/data-keys.component.ts

@ -46,7 +46,7 @@ import { MatAutocomplete } from '@angular/material/autocomplete';
import { MatChipInputEvent, MatChipList } from '@angular/material/chips';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { DataKey, DatasourceType, JsonSettingsSchema, widgetType } from '@shared/models/widget.models';
import { DataKey, DatasourceType, Widget, JsonSettingsSchema, widgetType } from '@shared/models/widget.models';
import { IAliasController } from '@core/api/widget-api.models';
import { DataKeysCallbacks } from './data-keys.component.models';
import { alarmFields } from '@shared/models/alarm.models';
@ -61,6 +61,7 @@ import {
} from '@home/components/widget/data-key-config-dialog.component';
import { deepClone } from '@core/utils';
import { MatChipDropEvent } from '@app/shared/components/mat-chip-draggable.directive';
import { Dashboard } from '@shared/models/dashboard.models';
@Component({
selector: 'tb-data-keys',
@ -113,6 +114,15 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie
@Input()
datakeySettingsSchema: JsonSettingsSchema;
@Input()
dataKeySettingsDirective: string;
@Input()
dashboard: Dashboard;
@Input()
widget: Widget;
@Input()
callbacks: DataKeysCallbacks;
@ -410,6 +420,10 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie
data: {
dataKey: deepClone(key),
dataKeySettingsSchema: this.datakeySettingsSchema,
dataKeySettingsDirective: this.dataKeySettingsDirective,
dashboard: this.dashboard,
aliasController: this.aliasController,
widget: this.widget,
entityAliasId: this.entityAliasId,
showPostProcessing: this.widgetType !== widgetType.alarm,
callbacks: this.callbacks

319
ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.models.ts

@ -14,7 +14,6 @@
/// limitations under the License.
///
import { JsonSettingsSchema } from '@shared/models/widget.models';
import { FontSettings } from '@home/components/widget/lib/settings.models';
import { AnimationRule, AnimationTarget } from '@home/components/widget/lib/analogue-gauge.models';
@ -37,321 +36,3 @@ export interface AnalogueCompassSettings {
animationRule: AnimationRule;
animationTarget: AnimationTarget;
}
export const analogueCompassSettingsSchema: JsonSettingsSchema = {
schema: {
type: 'object',
title: 'Settings',
properties: {
majorTicks: {
title: 'Major ticks names',
type: 'array',
items: {
title: 'Tick name',
type: 'string'
}
},
minorTicks: {
title: 'Minor ticks count',
type: 'number',
default: 22
},
showStrokeTicks: {
title: 'Show ticks stroke',
type: 'boolean',
default: false
},
needleCircleSize: {
title: 'Needle circle size',
type: 'number',
default: 15
},
showBorder: {
title: 'Show border',
type: 'boolean',
default: true
},
borderOuterWidth: {
title: 'Border width',
type: 'number',
default: 10
},
colorPlate: {
title: 'Plate color',
type: 'string',
default: '#222'
},
colorMajorTicks: {
title: 'Major ticks color',
type: 'string',
default: '#f5f5f5'
},
colorMinorTicks: {
title: 'Minor ticks color',
type: 'string',
default: '#ddd'
},
colorNeedle: {
title: 'Needle color',
type: 'string',
default: '#f08080'
},
colorNeedleCircle: {
title: 'Needle circle color',
type: 'string',
default: '#e8e8e8'
},
colorBorder: {
title: 'Border color',
type: 'string',
default: '#ccc'
},
majorTickFont: {
title: 'Major tick font',
type: 'object',
properties: {
family: {
title: 'Font family',
type: 'string',
default: 'Roboto'
},
size: {
title: 'Size',
type: 'number',
default: 20
},
style: {
title: 'Style',
type: 'string',
default: 'normal'
},
weight: {
title: 'Weight',
type: 'string',
default: '500'
},
color: {
title: 'color',
type: 'string',
default: '#ccc'
}
}
},
animation: {
title: 'Enable animation',
type: 'boolean',
default: true
},
animationDuration: {
title: 'Animation duration',
type: 'number',
default: 500
},
animationRule: {
title: 'Animation rule',
type: 'string',
default: 'cycle'
},
animationTarget: {
title: 'Animation target',
type: 'string',
default: 'needle'
}
},
required: []
},
form: [
{
key: 'majorTicks',
items: [
'majorTicks[]'
]
},
'minorTicks',
'showStrokeTicks',
'needleCircleSize',
'showBorder',
'borderOuterWidth',
{
key: 'colorPlate',
type: 'color'
},
{
key: 'colorMajorTicks',
type: 'color'
},
{
key: 'colorMinorTicks',
type: 'color'
},
{
key: 'colorNeedle',
type: 'color'
},
{
key: 'colorNeedleCircle',
type: 'color'
},
{
key: 'colorBorder',
type: 'color'
},
{
key: 'majorTickFont',
items: [
'majorTickFont.family',
'majorTickFont.size',
{
key: 'majorTickFont.style',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'italic',
label: 'Italic'
},
{
value: 'oblique',
label: 'Oblique'
}
]
},
{
key: 'majorTickFont.weight',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'bold',
label: 'Bold'
},
{
value: 'bolder',
label: 'Bolder'
},
{
value: 'lighter',
label: 'Lighter'
},
{
value: '100',
label: '100'
},
{
value: '200',
label: '200'
},
{
value: '300',
label: '300'
},
{
value: '400',
label: '400'
},
{
value: '500',
label: '500'
},
{
value: '600',
label: '600'
},
{
value: '700',
label: '700'
},
{
value: '800',
label: '800'
},
{
value: '900',
label: '900'
}
]
},
{
key: 'majorTickFont.color',
type: 'color'
}
]
},
'animation',
'animationDuration',
{
key: 'animationRule',
type: 'rc-select',
multiple: false,
items: [
{
value: 'linear',
label: 'Linear'
},
{
value: 'quad',
label: 'Quad'
},
{
value: 'quint',
label: 'Quint'
},
{
value: 'cycle',
label: 'Cycle'
},
{
value: 'bounce',
label: 'Bounce'
},
{
value: 'elastic',
label: 'Elastic'
},
{
value: 'dequad',
label: 'Dequad'
},
{
value: 'dequint',
label: 'Dequint'
},
{
value: 'decycle',
label: 'Decycle'
},
{
value: 'debounce',
label: 'Debounce'
},
{
value: 'delastic',
label: 'Delastic'
}
]
},
{
key: 'animationTarget',
type: 'rc-select',
multiple: false,
items: [
{
value: 'needle',
label: 'Needle'
},
{
value: 'plate',
label: 'Plate'
}
]
}
]
};

10
ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.ts

@ -17,26 +17,18 @@
import { WidgetContext } from '@home/models/widget-component.models';
import * as CanvasGauges from 'canvas-gauges';
import {
AnalogueCompassSettings,
analogueCompassSettingsSchema
AnalogueCompassSettings
} from '@home/components/widget/lib/analogue-compass.models';
import { deepClone, isDefined } from '@core/utils';
import { JsonSettingsSchema } from '@shared/models/widget.models';
import { getFontFamily } from '@home/components/widget/lib/settings.models';
import { TbBaseGauge } from '@home/components/widget/lib/analogue-gauge.models';
import RadialGaugeOptions = CanvasGauges.RadialGaugeOptions;
import BaseGauge = CanvasGauges.BaseGauge;
import RadialGauge = CanvasGauges.RadialGauge;
const analogueCompassSettingsSchemaValue = analogueCompassSettingsSchema;
// @dynamic
export class TbAnalogueCompass extends TbBaseGauge<AnalogueCompassSettings, RadialGaugeOptions> {
static get settingsSchema(): JsonSettingsSchema {
return analogueCompassSettingsSchemaValue;
}
constructor(ctx: WidgetContext, canvasId: string) {
super(ctx, canvasId);
}

771
ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts

@ -16,7 +16,6 @@
import * as CanvasGauges from 'canvas-gauges';
import { FontSettings, getFontFamily } from '@home/components/widget/lib/settings.models';
import { JsonSettingsSchema } from '@shared/models/widget.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { isDefined } from '@core/utils';
import * as tinycolor_ from 'tinycolor2';
@ -67,776 +66,6 @@ export interface AnalogueGaugeSettings {
animationRule: AnimationRule;
}
export const analogueGaugeSettingsSchema: JsonSettingsSchema = {
schema: {
type: 'object',
title: 'Settings',
properties: {
minValue: {
title: 'Minimum value',
type: 'number',
default: 0
},
maxValue: {
title: 'Maximum value',
type: 'number',
default: 100
},
unitTitle: {
title: 'Unit title',
type: 'string',
default: null
},
showUnitTitle: {
title: 'Show unit title',
type: 'boolean',
default: true
},
majorTicksCount: {
title: 'Major ticks count',
type: 'number',
default: null
},
minorTicks: {
title: 'Minor ticks count',
type: 'number',
default: 2
},
valueBox: {
title: 'Show value box',
type: 'boolean',
default: true
},
valueInt: {
title: 'Digits count for integer part of value',
type: 'number',
default: 3
},
defaultColor: {
title: 'Default color',
type: 'string',
default: null
},
colorPlate: {
title: 'Plate color',
type: 'string',
default: '#fff'
},
colorMajorTicks: {
title: 'Major ticks color',
type: 'string',
default: '#444'
},
colorMinorTicks: {
title: 'Minor ticks color',
type: 'string',
default: '#666'
},
colorNeedle: {
title: 'Needle color',
type: 'string',
default: null
},
colorNeedleEnd: {
title: 'Needle color - end gradient',
type: 'string',
default: null
},
colorNeedleShadowUp: {
title: 'Upper half of the needle shadow color',
type: 'string',
default: 'rgba(2,255,255,0.2)'
},
colorNeedleShadowDown: {
title: 'Drop shadow needle color.',
type: 'string',
default: 'rgba(188,143,143,0.45)'
},
colorValueBoxRect: {
title: 'Value box rectangle stroke color',
type: 'string',
default: '#888'
},
colorValueBoxRectEnd: {
title: 'Value box rectangle stroke color - end gradient',
type: 'string',
default: '#666'
},
colorValueBoxBackground: {
title: 'Value box background color',
type: 'string',
default: '#babab2'
},
colorValueBoxShadow: {
title: 'Value box shadow color',
type: 'string',
default: 'rgba(0,0,0,1)'
},
highlights: {
title: 'Highlights',
type: 'array',
items: {
title: 'Highlight',
type: 'object',
properties: {
from: {
title: 'From',
type: 'number'
},
to: {
title: 'To',
type: 'number'
},
color: {
title: 'Color',
type: 'string'
}
}
}
},
highlightsWidth: {
title: 'Highlights width',
type: 'number',
default: 15
},
showBorder: {
title: 'Show border',
type: 'boolean',
default: true
},
numbersFont: {
title: 'Tick numbers font',
type: 'object',
properties: {
family: {
title: 'Font family',
type: 'string',
default: 'Roboto'
},
size: {
title: 'Size',
type: 'number',
default: 18
},
style: {
title: 'Style',
type: 'string',
default: 'normal'
},
weight: {
title: 'Weight',
type: 'string',
default: '500'
},
color: {
title: 'color',
type: 'string',
default: null
}
}
},
titleFont: {
title: 'Title text font',
type: 'object',
properties: {
family: {
title: 'Font family',
type: 'string',
default: 'Roboto'
},
size: {
title: 'Size',
type: 'number',
default: 24
},
style: {
title: 'Style',
type: 'string',
default: 'normal'
},
weight: {
title: 'Weight',
type: 'string',
default: '500'
},
color: {
title: 'color',
type: 'string',
default: '#888'
}
}
},
unitsFont: {
title: 'Units text font',
type: 'object',
properties: {
family: {
title: 'Font family',
type: 'string',
default: 'Roboto'
},
size: {
title: 'Size',
type: 'number',
default: 22
},
style: {
title: 'Style',
type: 'string',
default: 'normal'
},
weight: {
title: 'Weight',
type: 'string',
default: '500'
},
color: {
title: 'color',
type: 'string',
default: '#888'
}
}
},
valueFont: {
title: 'Value text font',
type: 'object',
properties: {
family: {
title: 'Font family',
type: 'string',
default: 'Roboto'
},
size: {
title: 'Size',
type: 'number',
default: 40
},
style: {
title: 'Style',
type: 'string',
default: 'normal'
},
weight: {
title: 'Weight',
type: 'string',
default: '500'
},
color: {
title: 'color',
type: 'string',
default: '#444'
},
shadowColor: {
title: 'Shadow color',
type: 'string',
default: 'rgba(0,0,0,0.3)'
}
}
},
animation: {
title: 'Enable animation',
type: 'boolean',
default: true
},
animationDuration: {
title: 'Animation duration',
type: 'number',
default: 500
},
animationRule: {
title: 'Animation rule',
type: 'string',
default: 'cycle'
}
},
required: []
},
form: [
'minValue',
'maxValue',
'unitTitle',
'showUnitTitle',
'majorTicksCount',
'minorTicks',
'valueBox',
'valueInt',
{
key: 'defaultColor',
type: 'color'
},
{
key: 'colorPlate',
type: 'color'
},
{
key: 'colorMajorTicks',
type: 'color'
},
{
key: 'colorMinorTicks',
type: 'color'
},
{
key: 'colorNeedle',
type: 'color'
},
{
key: 'colorNeedleEnd',
type: 'color'
},
{
key: 'colorNeedleShadowUp',
type: 'color'
},
{
key: 'colorNeedleShadowDown',
type: 'color'
},
{
key: 'colorValueBoxRect',
type: 'color'
},
{
key: 'colorValueBoxRectEnd',
type: 'color'
},
{
key: 'colorValueBoxBackground',
type: 'color'
},
{
key: 'colorValueBoxShadow',
type: 'color'
},
{
key: 'highlights',
items: [
'highlights[].from',
'highlights[].to',
{
key: 'highlights[].color',
type: 'color'
}
]
},
'highlightsWidth',
'showBorder',
{
key: 'numbersFont',
items: [
'numbersFont.family',
'numbersFont.size',
{
key: 'numbersFont.style',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'italic',
label: 'Italic'
},
{
value: 'oblique',
label: 'Oblique'
}
]
},
{
key: 'numbersFont.weight',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'bold',
label: 'Bold'
},
{
value: 'bolder',
label: 'Bolder'
},
{
value: 'lighter',
label: 'Lighter'
},
{
value: '100',
label: '100'
},
{
value: '200',
label: '200'
},
{
value: '300',
label: '300'
},
{
value: '400',
label: '400'
},
{
value: '500',
label: '500'
},
{
value: '600',
label: '600'
},
{
value: '700',
label: '700'
},
{
value: '800',
label: '800'
},
{
value: '900',
label: '900'
}
]
},
{
key: 'numbersFont.color',
type: 'color'
}
]
},
{
key: 'titleFont',
items: [
'titleFont.family',
'titleFont.size',
{
key: 'titleFont.style',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'italic',
label: 'Italic'
},
{
value: 'oblique',
label: 'Oblique'
}
]
},
{
key: 'titleFont.weight',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'bold',
label: 'Bold'
},
{
value: 'bolder',
label: 'Bolder'
},
{
value: 'lighter',
label: 'Lighter'
},
{
value: '100',
label: '100'
},
{
value: '200',
label: '200'
},
{
value: '300',
label: '300'
},
{
value: '400',
label: '400'
},
{
value: '500',
label: '500'
},
{
value: '600',
label: '600'
},
{
value: '700',
label: '700'
},
{
value: '800',
label: '800'
},
{
value: '900',
label: '900'
}
]
},
{
key: 'titleFont.color',
type: 'color'
}
]
},
{
key: 'unitsFont',
items: [
'unitsFont.family',
'unitsFont.size',
{
key: 'unitsFont.style',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'italic',
label: 'Italic'
},
{
value: 'oblique',
label: 'Oblique'
}
]
},
{
key: 'unitsFont.weight',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'bold',
label: 'Bold'
},
{
value: 'bolder',
label: 'Bolder'
},
{
value: 'lighter',
label: 'Lighter'
},
{
value: '100',
label: '100'
},
{
value: '200',
label: '200'
},
{
value: '300',
label: '300'
},
{
value: '400',
label: '400'
},
{
value: '500',
label: '500'
},
{
value: '600',
label: '600'
},
{
value: '700',
label: '700'
},
{
value: '800',
label: '800'
},
{
value: '900',
label: '900'
}
]
},
{
key: 'unitsFont.color',
type: 'color'
}
]
},
{
key: 'valueFont',
items: [
'valueFont.family',
'valueFont.size',
{
key: 'valueFont.style',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'italic',
label: 'Italic'
},
{
value: 'oblique',
label: 'Oblique'
}
]
},
{
key: 'valueFont.weight',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'bold',
label: 'Bold'
},
{
value: 'bolder',
label: 'Bolder'
},
{
value: 'lighter',
label: 'Lighter'
},
{
value: '100',
label: '100'
},
{
value: '200',
label: '200'
},
{
value: '300',
label: '300'
},
{
value: '400',
label: '400'
},
{
value: '500',
label: '500'
},
{
value: '600',
label: '600'
},
{
value: '700',
label: '700'
},
{
value: '800',
label: '800'
},
{
value: '900',
label: '900'
}
]
},
{
key: 'valueFont.color',
type: 'color'
},
{
key: 'valueFont.shadowColor',
type: 'color'
}
]
},
'animation',
'animationDuration',
{
key: 'animationRule',
type: 'rc-select',
multiple: false,
items: [
{
value: 'linear',
label: 'Linear'
},
{
value: 'quad',
label: 'Quad'
},
{
value: 'quint',
label: 'Quint'
},
{
value: 'cycle',
label: 'Cycle'
},
{
value: 'bounce',
label: 'Bounce'
},
{
value: 'elastic',
label: 'Elastic'
},
{
value: 'dequad',
label: 'Dequad'
},
{
value: 'dequint',
label: 'Dequint'
},
{
value: 'decycle',
label: 'Decycle'
},
{
value: 'debounce',
label: 'Debounce'
},
{
value: 'delastic',
label: 'Delastic'
}
]
}
]
};
export abstract class TbBaseGauge<S, O extends GenericOptions> {
private gauge: BaseGauge;

64
ui-ngx/src/app/modules/home/components/widget/lib/analogue-linear-gauge.models.ts

@ -14,9 +14,7 @@
/// limitations under the License.
///
import { JsonSettingsSchema } from '@shared/models/widget.models';
import { AnalogueGaugeSettings, analogueGaugeSettingsSchema } from '@home/components/widget/lib/analogue-gauge.models';
import { deepClone } from '@core/utils';
import { AnalogueGaugeSettings } from '@home/components/widget/lib/analogue-gauge.models';
export interface AnalogueLinearGaugeSettings extends AnalogueGaugeSettings {
barStrokeWidth: number;
@ -26,63 +24,3 @@ export interface AnalogueLinearGaugeSettings extends AnalogueGaugeSettings {
colorBarProgress: string;
colorBarProgressEnd: string;
}
export function getAnalogueLinearGaugeSettingsSchema(): JsonSettingsSchema {
const analogueLinearGaugeSettingsSchema = deepClone(analogueGaugeSettingsSchema);
analogueLinearGaugeSettingsSchema.schema.properties =
{...analogueLinearGaugeSettingsSchema.schema.properties, ...{
barStrokeWidth: {
title: 'Bar stroke width',
type: 'number',
default: 2.5
},
colorBarStroke: {
title: 'Bar stroke color',
type: 'string',
default: null
},
colorBar: {
title: 'Bar background color',
type: 'string',
default: '#fff'
},
colorBarEnd: {
title: 'Bar background color - end gradient',
type: 'string',
default: '#ddd'
},
colorBarProgress: {
title: 'Progress bar color',
type: 'string',
default: null
},
colorBarProgressEnd: {
title: 'Progress bar color - end gradient',
type: 'string',
default: null
}}};
analogueLinearGaugeSettingsSchema.form.unshift(
'barStrokeWidth',
{
key: 'colorBarStroke',
type: 'color'
},
{
key: 'colorBar',
type: 'color'
},
{
key: 'colorBarEnd',
type: 'color'
},
{
key: 'colorBarProgress',
type: 'color'
},
{
key: 'colorBarProgressEnd',
type: 'color'
}
);
return analogueLinearGaugeSettingsSchema;
}

9
ui-ngx/src/app/modules/home/components/widget/lib/analogue-linear-gauge.ts

@ -19,8 +19,7 @@ import { JsonSettingsSchema } from '@shared/models/widget.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { TbAnalogueGauge } from '@home/components/widget/lib/analogue-gauge.models';
import {
AnalogueLinearGaugeSettings,
getAnalogueLinearGaugeSettingsSchema
AnalogueLinearGaugeSettings
} from '@home/components/widget/lib/analogue-linear-gauge.models';
import { isDefined } from '@core/utils';
import * as tinycolor_ from 'tinycolor2';
@ -30,15 +29,9 @@ import BaseGauge = CanvasGauges.BaseGauge;
const tinycolor = tinycolor_;
const analogueLinearGaugeSettingsSchemaValue = getAnalogueLinearGaugeSettingsSchema();
// @dynamic
export class TbAnalogueLinearGauge extends TbAnalogueGauge<AnalogueLinearGaugeSettings, LinearGaugeOptions>{
static get settingsSchema(): JsonSettingsSchema {
return analogueLinearGaugeSettingsSchemaValue;
}
constructor(ctx: WidgetContext, canvasId: string) {
super(ctx, canvasId);
}

31
ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.models.ts

@ -14,39 +14,10 @@
/// limitations under the License.
///
import { JsonSettingsSchema } from '@shared/models/widget.models';
import { AnalogueGaugeSettings, analogueGaugeSettingsSchema } from '@home/components/widget/lib/analogue-gauge.models';
import { deepClone } from '@core/utils';
import { AnalogueGaugeSettings } from '@home/components/widget/lib/analogue-gauge.models';
export interface AnalogueRadialGaugeSettings extends AnalogueGaugeSettings {
startAngle: number;
ticksAngle: number;
needleCircleSize: number;
}
export function getAnalogueRadialGaugeSettingsSchema(): JsonSettingsSchema {
const analogueRadialGaugeSettingsSchema = deepClone(analogueGaugeSettingsSchema);
analogueRadialGaugeSettingsSchema.schema.properties =
{...analogueRadialGaugeSettingsSchema.schema.properties, ...{
startAngle: {
title: 'Start ticks angle',
type: 'number',
default: 45
},
ticksAngle: {
title: 'Ticks angle',
type: 'number',
default: 270
},
needleCircleSize: {
title: 'Needle circle size',
type: 'number',
default: 10
}}};
analogueRadialGaugeSettingsSchema.form.unshift(
'startAngle',
'ticksAngle',
'needleCircleSize'
);
return analogueRadialGaugeSettingsSchema;
}

10
ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.ts

@ -16,25 +16,17 @@
import * as CanvasGauges from 'canvas-gauges';
import {
AnalogueRadialGaugeSettings,
getAnalogueRadialGaugeSettingsSchema
AnalogueRadialGaugeSettings
} from '@home/components/widget/lib/analogue-radial-gauge.models';
import { JsonSettingsSchema } from '@shared/models/widget.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { TbAnalogueGauge } from '@home/components/widget/lib/analogue-gauge.models';
import RadialGauge = CanvasGauges.RadialGauge;
import RadialGaugeOptions = CanvasGauges.RadialGaugeOptions;
import BaseGauge = CanvasGauges.BaseGauge;
const analogueRadialGaugeSettingsSchemaValue = getAnalogueRadialGaugeSettingsSchema();
// @dynamic
export class TbAnalogueRadialGauge extends TbAnalogueGauge<AnalogueRadialGaugeSettings, RadialGaugeOptions>{
static get settingsSchema(): JsonSettingsSchema {
return analogueRadialGaugeSettingsSchemaValue;
}
constructor(ctx: WidgetContext, canvasId: string) {
super(ctx, canvasId);
}

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

@ -80,905 +80,3 @@ export interface DigitalGaugeSettings {
colorTicks?: string;
tickWidth?: number;
}
export const digitalGaugeSettingsSchema: JsonSettingsSchema = {
schema: {
type: 'object',
title: 'Settings',
properties: {
minValue: {
title: 'Minimum value',
type: 'number',
default: 0
},
maxValue: {
title: 'Maximum value',
type: 'number',
default: 100
},
gaugeType: {
title: 'Gauge type',
type: 'string',
default: 'arc'
},
donutStartAngle: {
title: 'Angle to start from when in donut mode',
type: 'number',
default: 90
},
neonGlowBrightness: {
title: 'Neon glow effect brightness, (0-100), 0 - disable effect',
type: 'number',
default: 0
},
dashThickness: {
title: 'Thickness of the stripes, 0 - no stripes',
type: 'number',
default: 0
},
roundedLineCap: {
title: 'Display rounded line cap',
type: 'boolean',
default: false
},
title: {
title: 'Gauge title',
type: 'string',
default: null
},
showTitle: {
title: 'Show gauge title',
type: 'boolean',
default: false
},
unitTitle: {
title: 'Unit title',
type: 'string',
default: null
},
showUnitTitle: {
title: 'Show unit title',
type: 'boolean',
default: false
},
showTimestamp: {
title: 'Show value timestamp',
type: 'boolean',
default: false
},
timestampFormat: {
title: 'Timestamp format',
type: 'string',
default: 'yyyy-MM-dd HH:mm:ss'
},
showValue: {
title: 'Show value text',
type: 'boolean',
default: true
},
showMinMax: {
title: 'Show min and max values',
type: 'boolean',
default: true
},
gaugeWidthScale: {
title: 'Width of the gauge element',
type: 'number',
default: 0.75
},
defaultColor: {
title: 'Default color',
type: 'string',
default: null
},
gaugeColor: {
title: 'Background color of the gauge element',
type: 'string',
default: null
},
useFixedLevelColor: {
title: 'Use precise value for the color indicator',
type: 'boolean',
default: false
},
levelColors: {
title: 'Colors of indicator, from lower to upper',
type: 'array',
items: {
title: 'Color',
type: 'string'
}
},
fixedLevelColors: {
title: 'The colors for the indicator using boundary values',
type: 'array',
items: {
title: 'levelColor',
type: 'object',
properties: {
from: {
title: 'From',
type: 'object',
properties: {
valueSource: {
title: '[From] Value source',
type: 'string',
default: 'predefinedValue'
},
entityAlias: {
title: '[From] Source entity alias',
type: 'string'
},
attribute: {
title: '[From] Source entity attribute',
type: 'string'
},
value: {
title: '[From] Value (if predefined value is selected)',
type: 'number'
}
}
},
to: {
title: 'To',
type: 'object',
properties: {
valueSource: {
title: '[To] Value source',
type: 'string',
default: 'predefinedValue'
},
entityAlias: {
title: '[To] Source entity alias',
type: 'string'
},
attribute: {
title: '[To] Source entity attribute',
type: 'string'
},
value: {
title: '[To] Value (if predefined value is selected)',
type: 'number'
}
}
},
color: {
title: 'Color',
type: 'string'
}
},
required: ['color'],
}
},
showTicks: {
title: 'Show ticks',
type: 'boolean',
default: false
},
tickWidth: {
title: 'Width ticks',
type: 'number',
default: 4
},
colorTicks: {
title: 'Color ticks',
type: 'string',
default: '#666'
},
ticksValue: {
title: 'The ticks predefined value',
type: 'array',
items: {
title: 'tickValue',
type: 'object',
properties: {
valueSource: {
title: 'Value source',
type: 'string',
default: 'predefinedValue'
},
entityAlias: {
title: 'Source entity alias',
type: 'string'
},
attribute: {
title: 'Source entity attribute',
type: 'string'
},
value: {
title: 'Value (if predefined value is selected)',
type: 'number'
}
}
}
},
animation: {
title: 'Enable animation',
type: 'boolean',
default: true
},
animationDuration: {
title: 'Animation duration',
type: 'number',
default: 500
},
animationRule: {
title: 'Animation rule',
type: 'string',
default: 'linear'
},
titleFont: {
title: 'Gauge title font',
type: 'object',
properties: {
family: {
title: 'Font family',
type: 'string',
default: 'Roboto'
},
size: {
title: 'Size',
type: 'number',
default: 12
},
style: {
title: 'Style',
type: 'string',
default: 'normal'
},
weight: {
title: 'Weight',
type: 'string',
default: '500'
},
color: {
title: 'color',
type: 'string',
default: null
}
}
},
labelFont: {
title: 'Font of label showing under value',
type: 'object',
properties: {
family: {
title: 'Font family',
type: 'string',
default: 'Roboto'
},
size: {
title: 'Size',
type: 'number',
default: 8
},
style: {
title: 'Style',
type: 'string',
default: 'normal'
},
weight: {
title: 'Weight',
type: 'string',
default: '500'
},
color: {
title: 'color',
type: 'string',
default: null
}
}
},
valueFont: {
title: 'Font of label showing current value',
type: 'object',
properties: {
family: {
title: 'Font family',
type: 'string',
default: 'Roboto'
},
size: {
title: 'Size',
type: 'number',
default: 18
},
style: {
title: 'Style',
type: 'string',
default: 'normal'
},
weight: {
title: 'Weight',
type: 'string',
default: '500'
},
color: {
title: 'color',
type: 'string',
default: null
}
}
},
minMaxFont: {
title: 'Font of minimum and maximum labels',
type: 'object',
properties: {
family: {
title: 'Font family',
type: 'string',
default: 'Roboto'
},
size: {
title: 'Size',
type: 'number',
default: 10
},
style: {
title: 'Style',
type: 'string',
default: 'normal'
},
weight: {
title: 'Weight',
type: 'string',
default: '500'
},
color: {
title: 'color',
type: 'string',
default: null
}
}
}
}
},
form: [
'minValue',
'maxValue',
{
key: 'gaugeType',
type: 'rc-select',
multiple: false,
items: [
{
value: 'arc',
label: 'Arc'
},
{
value: 'donut',
label: 'Donut'
},
{
value: 'horizontalBar',
label: 'Horizontal bar'
},
{
value: 'verticalBar',
label: 'Vertical bar'
}
]
},
'donutStartAngle',
'neonGlowBrightness',
'dashThickness',
'roundedLineCap',
'title',
'showTitle',
'unitTitle',
'showUnitTitle',
'showTimestamp',
'timestampFormat',
'showValue',
'showMinMax',
'gaugeWidthScale',
{
key: 'defaultColor',
type: 'color'
},
{
key: 'gaugeColor',
type: 'color'
},
'useFixedLevelColor',
{
key: 'levelColors',
condition: 'model.useFixedLevelColor !== true',
items: [
{
key: 'levelColors[]',
type: 'color'
}
]
},
{
key: 'fixedLevelColors',
condition: 'model.useFixedLevelColor === true',
items: [
{
key: 'fixedLevelColors[].from.valueSource',
type: 'rc-select',
multiple: false,
items: [
{
value: 'predefinedValue',
label: 'Predefined value (Default)'
},
{
value: 'entityAttribute',
label: 'Value taken from entity attribute'
}
]
},
'fixedLevelColors[].from.value',
'fixedLevelColors[].from.entityAlias',
'fixedLevelColors[].from.attribute',
{
key: 'fixedLevelColors[].to.valueSource',
type: 'rc-select',
multiple: false,
items: [
{
value: 'predefinedValue',
label: 'Predefined value (Default)'
},
{
value: 'entityAttribute',
label: 'Value taken from entity attribute'
}
]
},
'fixedLevelColors[].to.value',
'fixedLevelColors[].to.entityAlias',
'fixedLevelColors[].to.attribute',
{
key: 'fixedLevelColors[].color',
type: 'color'
}
]
},
'showTicks',
{
key: 'tickWidth',
condition: 'model.showTicks === true'
},
{
key: 'colorTicks',
condition: 'model.showTicks === true',
type: 'color'
},
{
key: 'ticksValue',
condition: 'model.showTicks === true',
items: [
{
key: 'ticksValue[].valueSource',
type: 'rc-select',
multiple: false,
items: [
{
value: 'predefinedValue',
label: 'Predefined value (Default)'
},
{
value: 'entityAttribute',
label: 'Value taken from entity attribute'
}
]
},
'ticksValue[].value',
'ticksValue[].entityAlias',
'ticksValue[].attribute'
]
},
'animation',
'animationDuration',
{
key: 'animationRule',
type: 'rc-select',
multiple: false,
items: [
{
value: 'linear',
label: 'Linear'
},
{
value: 'quad',
label: 'Quad'
},
{
value: 'quint',
label: 'Quint'
},
{
value: 'cycle',
label: 'Cycle'
},
{
value: 'bounce',
label: 'Bounce'
},
{
value: 'elastic',
label: 'Elastic'
},
{
value: 'dequad',
label: 'Dequad'
},
{
value: 'dequint',
label: 'Dequint'
},
{
value: 'decycle',
label: 'Decycle'
},
{
value: 'debounce',
label: 'Debounce'
},
{
value: 'delastic',
label: 'Delastic'
}
]
},
{
key: 'titleFont',
items: [
'titleFont.family',
'titleFont.size',
{
key: 'titleFont.style',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'italic',
label: 'Italic'
},
{
value: 'oblique',
label: 'Oblique'
}
]
},
{
key: 'titleFont.weight',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'bold',
label: 'Bold'
},
{
value: 'bolder',
label: 'Bolder'
},
{
value: 'lighter',
label: 'Lighter'
},
{
value: '100',
label: '100'
},
{
value: '200',
label: '200'
},
{
value: '300',
label: '300'
},
{
value: '400',
label: '400'
},
{
value: '500',
label: '500'
},
{
value: '600',
label: '600'
},
{
value: '700',
label: '700'
},
{
value: '800',
label: '800'
},
{
value: '900',
label: '900'
}
]
},
{
key: 'titleFont.color',
type: 'color'
}
]
},
{
key: 'labelFont',
items: [
'labelFont.family',
'labelFont.size',
{
key: 'labelFont.style',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'italic',
label: 'Italic'
},
{
value: 'oblique',
label: 'Oblique'
}
]
},
{
key: 'labelFont.weight',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'bold',
label: 'Bold'
},
{
value: 'bolder',
label: 'Bolder'
},
{
value: 'lighter',
label: 'Lighter'
},
{
value: '100',
label: '100'
},
{
value: '200',
label: '200'
},
{
value: '300',
label: '300'
},
{
value: '400',
label: '400'
},
{
value: '500',
label: '500'
},
{
value: '600',
label: '600'
},
{
value: '700',
label: '700'
},
{
value: '800',
label: '800'
},
{
value: '900',
label: '900'
}
]
},
{
key: 'labelFont.color',
type: 'color'
}
]
},
{
key: 'valueFont',
items: [
'valueFont.family',
'valueFont.size',
{
key: 'valueFont.style',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'italic',
label: 'Italic'
},
{
value: 'oblique',
label: 'Oblique'
}
]
},
{
key: 'valueFont.weight',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'bold',
label: 'Bold'
},
{
value: 'bolder',
label: 'Bolder'
},
{
value: 'lighter',
label: 'Lighter'
},
{
value: '100',
label: '100'
},
{
value: '200',
label: '200'
},
{
value: '300',
label: '300'
},
{
value: '400',
label: '400'
},
{
value: '500',
label: '500'
},
{
value: '600',
label: '600'
},
{
value: '700',
label: '700'
},
{
value: '800',
label: '800'
},
{
value: '900',
label: '900'
}
]
},
{
key: 'valueFont.color',
type: 'color'
}
]
},
{
key: 'minMaxFont',
items: [
'minMaxFont.family',
'minMaxFont.size',
{
key: 'minMaxFont.style',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'italic',
label: 'Italic'
},
{
value: 'oblique',
label: 'Oblique'
}
]
},
{
key: 'minMaxFont.weight',
type: 'rc-select',
multiple: false,
items: [
{
value: 'normal',
label: 'Normal'
},
{
value: 'bold',
label: 'Bold'
},
{
value: 'bolder',
label: 'Bolder'
},
{
value: 'lighter',
label: 'Lighter'
},
{
value: '100',
label: '100'
},
{
value: '200',
label: '200'
},
{
value: '300',
label: '300'
},
{
value: '400',
label: '400'
},
{
value: '500',
label: '500'
},
{
value: '600',
label: '600'
},
{
value: '700',
label: '700'
},
{
value: '800',
label: '800'
},
{
value: '900',
label: '900'
}
]
},
{
key: 'minMaxFont.color',
type: 'color'
}
]
}
]
};

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

@ -21,7 +21,6 @@ import {
AttributeSourceProperty,
ColorLevelSetting,
DigitalGaugeSettings,
digitalGaugeSettingsSchema,
FixedLevelColors
} from '@home/components/widget/lib/digital-gauge.models';
import * as tinycolor_ from 'tinycolor2';
@ -44,15 +43,9 @@ import GenericOptions = CanvasGauges.GenericOptions;
const tinycolor = tinycolor_;
const digitalGaugeSettingsSchemaValue = digitalGaugeSettingsSchema;
// @dynamic
export class TbCanvasDigitalGauge {
static get settingsSchema(): JsonSettingsSchema {
return digitalGaugeSettingsSchemaValue;
}
constructor(protected ctx: WidgetContext, canvasId: string) {
const gaugeElement = $('#' + canvasId, ctx.$container)[0];
const settings: DigitalGaugeSettings = ctx.settings;

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

@ -18,7 +18,6 @@
/// <reference path="../../../../../../../src/typings/jquery.flot.typings.d.ts" />
import { DataKey, Datasource, DatasourceData, JsonSettingsSchema } from '@shared/models/widget.models';
import * as moment_ from 'moment';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { ComparisonDuration } from '@shared/models/time/time.models';
@ -100,8 +99,8 @@ export interface TbFlotGridSettings {
outlineWidth: number;
verticalLines: boolean;
horizontalLines: boolean;
minBorderMargin: number;
margin: number;
minBorderMargin?: number;
margin?: number;
}
export interface TbFlotXAxisSettings {
@ -163,13 +162,15 @@ export interface TbFlotLabelPatternSettings {
settings?: any;
}
export interface TbFlotGraphSettings extends TbFlotBaseSettings, TbFlotThresholdsSettings, TbFlotComparisonSettings, TbFlotCustomLegendSettings {
export interface TbFlotGraphSettings extends TbFlotBaseSettings,
TbFlotThresholdsSettings, TbFlotComparisonSettings, TbFlotCustomLegendSettings {
smoothLines: boolean;
}
export declare type BarAlignment = 'left' | 'right' | 'center';
export interface TbFlotBarSettings extends TbFlotBaseSettings, TbFlotThresholdsSettings, TbFlotComparisonSettings, TbFlotCustomLegendSettings {
export interface TbFlotBarSettings extends TbFlotBaseSettings,
TbFlotThresholdsSettings, TbFlotComparisonSettings, TbFlotCustomLegendSettings {
defaultBarWidth: number;
barAlignment: BarAlignment;
}
@ -183,7 +184,7 @@ export interface TbFlotPieSettings {
color: string;
width: number;
};
showTooltip: boolean,
showTooltip: boolean;
showLabels: boolean;
fontColor: string;
fontSize: number;
@ -240,927 +241,3 @@ export interface TbFlotLatestKeySettings {
thresholdLineWidth: number;
thresholdColor: string;
}
export function flotSettingsSchema(chartType: ChartType): JsonSettingsSchema {
const schema: JsonSettingsSchema = {
schema: {
type: 'object',
title: 'Settings',
properties: {
}
}
};
const properties: any = schema.schema.properties;
properties.stack = {
title: 'Stacking',
type: 'boolean',
default: false
};
if (chartType === 'graph') {
properties.smoothLines = {
title: 'Display smooth (curved) lines',
type: 'boolean',
default: false
};
}
if (chartType === 'bar') {
properties.defaultBarWidth = {
title: 'Default bar width for non-aggregated data (milliseconds)',
type: 'number',
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 = {
title: 'Shadow size',
type: 'number',
default: 4
};
properties.fontColor = {
title: 'Font color',
type: 'string',
default: '#545454'
};
properties.fontSize = {
title: 'Font size',
type: 'number',
default: 10
};
properties.tooltipIndividual = {
title: 'Hover individual points',
type: 'boolean',
default: false
};
properties.tooltipCumulative = {
title: 'Show cumulative values in stacking mode',
type: 'boolean',
default: false
};
properties.tooltipValueFormatter = {
title: 'Tooltip value format function, f(value)',
type: 'string',
default: ''
};
properties.hideZeros = {
title: 'Hide zero/false values from tooltip',
type: 'boolean',
default: false
};
properties.showTooltip = {
title: 'Show tooltip',
type: 'boolean',
default: true
};
properties.grid = {
title: 'Grid settings',
type: 'object',
properties: {
color: {
title: 'Primary color',
type: 'string',
default: '#545454'
},
backgroundColor: {
title: 'Background color',
type: 'string',
default: null
},
tickColor: {
title: 'Ticks color',
type: 'string',
default: '#DDDDDD'
},
outlineWidth: {
title: 'Grid outline/border width (px)',
type: 'number',
default: 1
},
verticalLines: {
title: 'Show vertical lines',
type: 'boolean',
default: true
},
horizontalLines: {
title: 'Show horizontal lines',
type: 'boolean',
default: true
}
}
};
properties.xaxis = {
title: 'X axis settings',
type: 'object',
properties: {
showLabels: {
title: 'Show labels',
type: 'boolean',
default: true
},
title: {
title: 'Axis title',
type: 'string',
default: null
},
color: {
title: 'Ticks color',
type: 'string',
default: null
}
}
};
properties.yaxis = {
title: 'Y axis settings',
type: 'object',
properties: {
min: {
title: 'Minimum value on the scale',
type: 'number',
default: null
},
max: {
title: 'Maximum value on the scale',
type: 'number',
default: null
},
showLabels: {
title: 'Show labels',
type: 'boolean',
default: true
},
title: {
title: 'Axis title',
type: 'string',
default: null
},
color: {
title: 'Ticks color',
type: 'string',
default: null
},
ticksFormatter: {
title: 'Ticks formatter function, f(value)',
type: 'string',
default: ''
},
tickDecimals: {
title: 'The number of decimals to display',
type: 'number',
default: 0
},
tickSize: {
title: 'Step size between ticks',
type: 'number',
default: null
}
}
};
schema.schema.required = [];
schema.form = ['stack'];
if (chartType === 'graph') {
schema.form.push('smoothLines');
}
if (chartType === 'bar') {
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({
key: 'fontColor',
type: 'color'
});
schema.form.push('fontSize');
schema.form.push('tooltipIndividual');
schema.form.push('tooltipCumulative');
schema.form.push({
key: 'tooltipValueFormatter',
type: 'javascript',
helpId: 'widget/lib/flot/tooltip_value_format_fn'
});
schema.form.push('hideZeros');
schema.form.push('showTooltip');
schema.form.push({
key: 'grid',
items: [
{
key: 'grid.color',
type: 'color'
},
{
key: 'grid.backgroundColor',
type: 'color'
},
{
key: 'grid.tickColor',
type: 'color'
},
'grid.outlineWidth',
'grid.verticalLines',
'grid.horizontalLines'
]
});
schema.form.push({
key: 'xaxis',
items: [
'xaxis.showLabels',
'xaxis.title',
{
key: 'xaxis.color',
type: 'color'
}
]
});
schema.form.push({
key: 'yaxis',
items: [
'yaxis.min',
'yaxis.max',
'yaxis.tickDecimals',
'yaxis.tickSize',
'yaxis.showLabels',
'yaxis.title',
{
key: 'yaxis.color',
type: 'color'
},
{
key: 'yaxis.ticksFormatter',
type: 'javascript',
helpId: 'widget/lib/flot/ticks_formatter_fn'
}
]
});
if (chartType === 'graph' || chartType === 'bar') {
schema.groupInfoes = [{
formIndex: 0,
GroupTitle: 'Common Settings'
}];
schema.form = [schema.form];
schema.schema.properties = {...schema.schema.properties, ...chartSettingsSchemaForComparison.schema.properties, ...chartSettingsSchemaForCustomLegend.schema.properties};
schema.schema.required = schema.schema.required.concat(chartSettingsSchemaForComparison.schema.required, chartSettingsSchemaForCustomLegend.schema.required);
schema.form.push(chartSettingsSchemaForComparison.form, chartSettingsSchemaForCustomLegend.form);
schema.groupInfoes.push({
formIndex: schema.groupInfoes.length,
GroupTitle: 'Comparison Settings'
});
schema.groupInfoes.push({
formIndex: schema.groupInfoes.length,
GroupTitle: 'Custom Legend Settings'
});
}
return schema;
}
const chartSettingsSchemaForComparison: JsonSettingsSchema = {
schema: {
title: 'Comparison Settings',
type: 'object',
properties: {
comparisonEnabled: {
title: 'Enable comparison',
type: 'boolean',
default: false
},
timeForComparison: {
title: 'Time to show historical data',
type: 'string',
default: 'previousInterval'
},
comparisonCustomIntervalValue: {
title: 'Custom interval value (ms)',
type: 'number',
default: 7200000
},
xaxisSecond: {
title: 'Second X axis',
type: 'object',
properties: {
axisPosition: {
title: 'Axis position',
type: 'string',
default: 'top'
},
showLabels: {
title: 'Show labels',
type: 'boolean',
default: true
},
title: {
title: 'Axis title',
type: 'string',
default: null
}
}
}
},
required: []
},
form: [
'comparisonEnabled',
{
key: 'timeForComparison',
type: 'rc-select',
multiple: false,
items: [
{
value: 'previousInterval',
label: 'Previous interval (default)'
},
{
value: 'days',
label: 'Day ago'
},
{
value: 'weeks',
label: 'Week ago'
},
{
value: 'months',
label: 'Month ago'
},
{
value: 'years',
label: 'Year ago'
},
{
value: 'customInterval',
label: 'Custom interval'
}
]
},
{
key: 'comparisonCustomIntervalValue',
condition: 'model.timeForComparison === "customInterval"'
},
{
key: 'xaxisSecond',
items: [
{
key: 'xaxisSecond.axisPosition',
type: 'rc-select',
multiple: false,
items: [
{
value: 'top',
label: 'Top (default)'
},
{
value: 'bottom',
label: 'Bottom'
}
]
},
'xaxisSecond.showLabels',
'xaxisSecond.title',
]
}
]
};
const chartSettingsSchemaForCustomLegend: JsonSettingsSchema = {
schema: {
title: 'Custom Legend Settings',
type: 'object',
properties: {
customLegendEnabled: {
title: 'Enable custom legend (this will allow you to use attribute/timeseries values in key labels)',
type: 'boolean',
default: false
},
dataKeysListForLabels: {
title: 'Datakeys list to use in labels',
type: 'array',
items: {
type: 'object',
properties: {
name: {
title: 'Key name',
type: 'string'
},
type: {
title: 'Key type',
type: 'string',
default: 'attribute'
}
},
required: [
'name'
]
}
}
},
required: []
},
form: [
'customLegendEnabled',
{
key: 'dataKeysListForLabels',
condition: 'model.customLegendEnabled === true',
items: [
{
key: 'dataKeysListForLabels[].type',
type: 'rc-select',
multiple: false,
items: [
{
value: 'attribute',
label: 'Attribute'
},
{
value: 'timeseries',
label: 'Timeseries'
}
]
},
'dataKeysListForLabels[].name'
]
}
]
};
export const flotPieSettingsSchema: JsonSettingsSchema = {
schema: {
type: 'object',
title: 'Settings',
properties: {
radius: {
title: 'Radius',
type: 'number',
default: 1
},
innerRadius: {
title: 'Inner radius',
type: 'number',
default: 0
},
tilt: {
title: 'Tilt',
type: 'number',
default: 1
},
animatedPie: {
title: 'Enable pie animation (experimental)',
type: 'boolean',
default: false
},
stroke: {
title: 'Stroke',
type: 'object',
properties: {
color: {
title: 'Color',
type: 'string',
default: ''
},
width: {
title: 'Width (pixels)',
type: 'number',
default: 0
}
}
},
showTooltip: {
title: 'Show Tooltip',
type: 'boolean',
default: true,
},
showLabels: {
title: 'Show labels',
type: 'boolean',
default: false
},
fontColor: {
title: 'Font color',
type: 'string',
default: '#545454'
},
fontSize: {
title: 'Font size',
type: 'number',
default: 10
}
},
required: []
},
form: [
'radius',
'innerRadius',
'animatedPie',
'tilt',
{
key: 'stroke',
items: [
{
key: 'stroke.color',
type: 'color'
},
'stroke.width'
]
},
'showTooltip',
'showLabels',
{
key: 'fontColor',
type: 'color'
},
'fontSize'
]
};
export const flotPieDatakeySettingsSchema: JsonSettingsSchema = {
schema: {
type: 'object',
title: 'DataKeySettings',
properties: {
hideDataByDefault: {
title: 'Data is hidden by default',
type: 'boolean',
default: false
},
disableDataHiding: {
title: 'Disable data hiding',
type: 'boolean',
default: false
},
removeFromLegend: {
title: 'Remove datakey from legend',
type: 'boolean',
default: false
}
},
required: []
},
form: [
'hideDataByDefault',
'disableDataHiding',
'removeFromLegend'
]
};
export function flotDatakeySettingsSchema(defaultShowLines: boolean, chartType: ChartType): JsonSettingsSchema {
const schema: JsonSettingsSchema = {
schema: {
type: 'object',
title: 'DataKeySettings',
properties: {
excludeFromStacking: {
title: 'Exclude from stacking(available in "Stacking" mode)',
type: 'boolean',
default: false
},
hideDataByDefault: {
title: 'Data is hidden by default',
type: 'boolean',
default: false
},
disableDataHiding: {
title: 'Disable data hiding',
type: 'boolean',
default: false
},
removeFromLegend: {
title: 'Remove datakey from legend',
type: 'boolean',
default: false
},
showLines: {
title: 'Show lines',
type: 'boolean',
default: defaultShowLines
},
fillLines: {
title: 'Fill lines',
type: 'boolean',
default: false
},
showPoints: {
title: 'Show points',
type: 'boolean',
default: false
},
showPointShape: {
title: 'Select point shape:',
type: 'string',
default: 'circle'
},
pointShapeFormatter: {
title: 'Point shape format function, f(ctx, x, y, radius, shadow)',
type: 'string',
default: 'var size = radius * Math.sqrt(Math.PI) / 2;\n' +
'ctx.moveTo(x - size, y - size);\n' +
'ctx.lineTo(x + size, y + size);\n' +
'ctx.moveTo(x - size, y + size);\n' +
'ctx.lineTo(x + size, y - size);'
},
showPointsLineWidth: {
title: 'Line width of points',
type: 'number',
default: 5
},
showPointsRadius: {
title: 'Radius of points',
type: 'number',
default: 3
},
tooltipValueFormatter: {
title: 'Tooltip value format function, f(value)',
type: 'string',
default: ''
},
showSeparateAxis: {
title: 'Show separate axis',
type: 'boolean',
default: false
},
axisMin: {
title: 'Minimum value on the axis scale',
type: 'number',
default: null
},
axisMax: {
title: 'Maximum value on the axis scale',
type: 'number',
default: null
},
axisTitle: {
title: 'Axis title',
type: 'string',
default: ''
},
axisTickDecimals: {
title: 'Axis tick number of digits after floating point',
type: 'number',
default: null
},
axisTickSize: {
title: 'Axis step size between ticks',
type: 'number',
default: null
},
axisPosition: {
title: 'Axis position',
type: 'string',
default: 'left'
},
axisTicksFormatter: {
title: 'Ticks formatter function, f(value)',
type: 'string',
default: ''
}
},
required: ['showLines', 'fillLines', 'showPoints']
},
form: [
'hideDataByDefault',
'disableDataHiding',
'removeFromLegend',
'excludeFromStacking',
'showLines',
'fillLines',
'showPoints',
{
key: 'showPointShape',
type: 'rc-select',
multiple: false,
items: [
{
value: 'circle',
label: 'Circle'
},
{
value: 'cross',
label: 'Cross'
},
{
value: 'diamond',
label: 'Diamond'
},
{
value: 'square',
label: 'Square'
},
{
value: 'triangle',
label: 'Triangle'
},
{
value: 'custom',
label: 'Custom function'
}
]
},
{
key: 'pointShapeFormatter',
type: 'javascript',
helpId: 'widget/lib/flot/point_shape_format_fn'
},
'showPointsLineWidth',
'showPointsRadius',
{
key: 'tooltipValueFormatter',
type: 'javascript',
helpId: 'widget/lib/flot/tooltip_value_format_fn'
},
'showSeparateAxis',
'axisMin',
'axisMax',
'axisTitle',
'axisTickDecimals',
'axisTickSize',
{
key: 'axisPosition',
type: 'rc-select',
multiple: false,
items: [
{
value: 'left',
label: 'Left'
},
{
value: 'right',
label: 'Right'
}
]
},
{
key: 'axisTicksFormatter',
type: 'javascript',
helpId: 'widget/lib/flot/ticks_formatter_fn'
}
]
};
const properties = schema.schema.properties;
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 = {
title: 'Comparison Settings',
type: 'object',
properties: {
showValuesForComparison: {
title: 'Show historical values for comparison',
type: 'boolean',
default: true
},
comparisonValuesLabel: {
title: 'Historical values label',
type: 'string',
default: ''
},
color: {
title: 'Color',
type: 'string',
default: ''
}
},
required: ['showValuesForComparison']
};
schema.form.push({
key: 'comparisonSettings',
items: [
'comparisonSettings.showValuesForComparison',
'comparisonSettings.comparisonValuesLabel',
{
key: 'comparisonSettings.color',
type: 'color'
}
]
});
}
return schema;
}
export const flotLatestDatakeySettingsSchema: JsonSettingsSchema = {
schema: {
type: 'object',
title: 'LatestDataKeySettings',
properties: {
useAsThreshold: {
title: 'Use key value as threshold',
type: 'boolean',
default: false
},
thresholdLineWidth: {
title: 'Threshold line width',
type: 'number'
},
thresholdColor: {
title: 'Threshold color',
type: 'string'
}
}
},
form: [
'useAsThreshold',
{
key: 'thresholdLineWidth',
condition: 'model.useAsThreshold === true'
},
{
key: 'thresholdColor',
type: 'color',
condition: 'model.useAsThreshold === true'
},
]
};

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

@ -38,10 +38,6 @@ import {
} from '@app/shared/models/widget.models';
import {
ChartType,
flotDatakeySettingsSchema, flotLatestDatakeySettingsSchema,
flotPieDatakeySettingsSchema,
flotPieSettingsSchema,
flotSettingsSchema,
TbFlotAxisOptions,
TbFlotHoverInfo,
TbFlotKeySettings, TbFlotLatestKeySettings,
@ -67,10 +63,6 @@ import Timeout = NodeJS.Timeout;
const tinycolor = tinycolor_;
const moment = moment_;
const flotPieSettingsSchemaValue = flotPieSettingsSchema;
const flotPieDatakeySettingsSchemaValue = flotPieDatakeySettingsSchema;
const latestDatakeySettingsSchemaValue = flotLatestDatakeySettingsSchema;
export class TbFlot {
private readonly utils: UtilsService;
@ -133,26 +125,6 @@ export class TbFlot {
private pieAnimationLastTime: number;
private pieAnimationCaf: CancelAnimationFrame;
static pieSettingsSchema(): JsonSettingsSchema {
return flotPieSettingsSchemaValue;
}
static pieDatakeySettingsSchema(): JsonSettingsSchema {
return flotPieDatakeySettingsSchemaValue;
}
static settingsSchema(chartType: ChartType): JsonSettingsSchema {
return flotSettingsSchema(chartType);
}
static datakeySettingsSchema(defaultShowLines: boolean, chartType: ChartType): JsonSettingsSchema {
return flotDatakeySettingsSchema(defaultShowLines, chartType);
}
static latestDatakeySettingsSchema(): JsonSettingsSchema {
return latestDatakeySettingsSchemaValue;
}
constructor(private ctx: WidgetContext, private readonly chartType: ChartType) {
this.chartType = this.chartType || 'line';
this.settings = ctx.settings as TbFlotSettings;

25
ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts

@ -15,7 +15,7 @@
///
import L, { LeafletMouseEvent } from 'leaflet';
import { CircleData, UnitedMapSettings } from '@home/components/widget/lib/maps/map-models';
import { CircleData, WidgetCircleSettings } from '@home/components/widget/lib/maps/map-models';
import {
functionValueCalculator,
parseWithTranslation
@ -34,10 +34,11 @@ export class Circle {
tooltip: L.Popup;
constructor(private map: LeafletMap,
public data: FormattedData,
public dataSources: FormattedData[],
private settings: UnitedMapSettings,
private onDragendListener?) {
private data: FormattedData,
private dataSources: FormattedData[],
private settings: Partial<WidgetCircleSettings>,
private onDragendListener?,
snappable = false) {
this.circleData = this.map.convertToCircleFormat(JSON.parse(data[this.settings.circleKeyName]));
const centerPosition = this.createCenterPosition();
const circleFillColor = this.getFillColor();
@ -50,7 +51,7 @@ export class Circle {
fillOpacity: settings.circleFillColorOpacity,
opacity: settings.circleStrokeOpacity,
pmIgnore: !settings.editableCircle,
snapIgnore: !settings.snappable
snapIgnore: !snappable
}).addTo(this.map.map);
if (settings.showCircleLabel) {
@ -93,12 +94,14 @@ export class Circle {
if (this.settings.showCircleLabel) {
if (!this.map.circleLabelText || this.settings.useCircleLabelFunction) {
const pattern = this.settings.useCircleLabelFunction ?
safeExecute(this.settings.circleLabelFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.circleLabel;
safeExecute(this.settings.parsedCircleLabelFunction,
[this.data, this.dataSources, this.data.dsIndex]) : this.settings.circleLabel;
this.map.circleLabelText = parseWithTranslation.prepareProcessPattern(pattern, true);
this.map.replaceInfoTooltipCircle = processDataPattern(this.map.circleLabelText, this.data);
}
const circleLabelText = fillDataPattern(this.map.circleLabelText, this.map.replaceInfoTooltipCircle, this.data);
this.leafletCircle.bindTooltip(`<div style="color: ${this.settings.labelColor};"><b>${circleLabelText}</b></div>`,
const labelColor = this.map.ctx.widgetConfig.color;
this.leafletCircle.bindTooltip(`<div style="color: ${labelColor};"><b>${circleLabelText}</b></div>`,
{ className: 'tb-polygon-label', permanent: true, sticky: true, direction: 'center'})
.openTooltip(this.leafletCircle.getLatLng());
}
@ -106,7 +109,7 @@ export class Circle {
private updateTooltip() {
const pattern = this.settings.useCircleTooltipFunction ?
safeExecute(this.settings.circleTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) :
safeExecute(this.settings.parsedCircleTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) :
this.settings.circleTooltipPattern;
this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, this.data, true));
}
@ -151,12 +154,12 @@ export class Circle {
}
private getFillColor(): string | null {
return functionValueCalculator(this.settings.useCircleFillColorFunction, this.settings.circleFillColorFunction,
return functionValueCalculator(this.settings.useCircleFillColorFunction, this.settings.parsedCircleFillColorFunction,
[this.data, this.dataSources, this.data.dsIndex], this.settings.circleFillColor);
}
private getStrokeColor(): string | null {
return functionValueCalculator(this.settings.useCircleStrokeColorFunction, this.settings.circleStrokeColorFunction,
return functionValueCalculator(this.settings.useCircleStrokeColorFunction, this.settings.parsedCircleStrokeColorFunction,
[this.data, this.dataSources, this.data.dsIndex], this.settings.circleStrokeColor);
}

7
ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts

@ -76,9 +76,12 @@ export function findAngle(startPoint: FormattedData, endPoint: FormattedData, la
}
export function getDefCenterPosition(position) {
export function getDefCenterPosition(position): [number, number] {
if (typeof (position) === 'string') {
return position.split(',');
const parts = position.split(',');
if (parts.length === 2) {
return [Number(parts[0]), Number(parts[1])];
}
}
if (typeof (position) === 'object') {
return position;

110
ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts

@ -21,15 +21,11 @@ import { MarkerClusterGroup, MarkerClusterGroupOptions } from 'leaflet.markerclu
import '@geoman-io/leaflet-geoman-free';
import {
CircleData,
defaultSettings,
MapSettings,
CircleData, defaultMapSettings,
MarkerClusteringSettings,
MarkerIconInfo,
MarkerImageInfo,
MarkerSettings,
PolygonSettings,
PolylineSettings,
UnitedMapSettings
WidgetPolygonSettings, WidgetPolylineSettings, WidgetMarkersSettings, WidgetUnitedMapSettings
} from './map-models';
import { Marker } from './markers';
import { Observable, of } from 'rxjs';
@ -66,7 +62,7 @@ export default abstract class LeafletMap {
polygons: Map<string, Polygon> = new Map();
circles: Map<string, Circle> = new Map();
map: L.Map;
options: UnitedMapSettings;
options: WidgetUnitedMapSettings;
bounds: L.LatLngBounds;
datasources: FormattedData[];
markersCluster: MarkerClusterGroup;
@ -86,12 +82,12 @@ export default abstract class LeafletMap {
replaceInfoTooltipCircle: Array<ReplaceInfo> = [];
markerTooltipText: string;
drawRoutes: boolean;
showPolygon: boolean;
updatePending = false;
editPolygons = false;
editCircle = false;
selectedEntity: FormattedData;
ignoreUpdateBounds = false;
initDragModeIgnoreUpdateBoundsSet = false;
// tslint:disable-next-line:no-string-literal
southWest = new L.LatLng(-Projection.SphericalMercator['MAX_LATITUDE'], -180);
// tslint:disable-next-line:no-string-literal
@ -104,37 +100,30 @@ export default abstract class LeafletMap {
protected constructor(public ctx: WidgetContext,
public $container: HTMLElement,
options: UnitedMapSettings) {
options: WidgetUnitedMapSettings) {
this.options = options;
this.options.tinyColor = tinycolor(this.options.color || defaultMapSettings.color);
this.editPolygons = options.showPolygon && options.editablePolygon;
this.editCircle = options.showCircle && options.editableCircle;
L.Icon.Default.imagePath = '/';
this.translateService = this.ctx.$injector.get(TranslateService);
this.initMarkerClusterSettings();
}
public initSettings(options: MapSettings) {
this.options.tinyColor = tinycolor(this.options.color || defaultSettings.color);
const { useClusterMarkers,
zoomOnClick,
showCoverageOnHover,
removeOutsideVisibleBounds,
animate,
chunkedLoading,
spiderfyOnMaxZoom,
maxClusterRadius,
maxZoom }: MapSettings = options;
if (useClusterMarkers) {
private initMarkerClusterSettings() {
const markerClusteringSettings: MarkerClusteringSettings = this.options;
if (markerClusteringSettings.useClusterMarkers) {
// disabled marker cluster icon
(L as any).MarkerCluster = (L as any).MarkerCluster.extend({
options: { pmIgnore: true, ...L.Icon.prototype.options }
});
const clusteringSettings: MarkerClusterGroupOptions = {
spiderfyOnMaxZoom,
zoomToBoundsOnClick: zoomOnClick,
showCoverageOnHover,
removeOutsideVisibleBounds,
animate,
chunkedLoading,
spiderfyOnMaxZoom: markerClusteringSettings.spiderfyOnMaxZoom,
zoomToBoundsOnClick: markerClusteringSettings.zoomOnClick,
showCoverageOnHover: markerClusteringSettings.showCoverageOnHover,
removeOutsideVisibleBounds: markerClusteringSettings.removeOutsideVisibleBounds,
animate: markerClusteringSettings.animate,
chunkedLoading: markerClusteringSettings.chunkedLoading,
pmIgnore: true,
spiderLegPolylineOptions: {
pmIgnore: true
@ -143,11 +132,11 @@ export default abstract class LeafletMap {
pmIgnore: true
}
};
if (maxClusterRadius && maxClusterRadius > 0) {
clusteringSettings.maxClusterRadius = Math.floor(maxClusterRadius);
if (markerClusteringSettings.maxClusterRadius && markerClusteringSettings.maxClusterRadius > 0) {
clusteringSettings.maxClusterRadius = Math.floor(markerClusteringSettings.maxClusterRadius);
}
if (maxZoom && maxZoom >= 0 && maxZoom < 19) {
clusteringSettings.disableClusteringAtZoom = Math.floor(maxZoom);
if (markerClusteringSettings.maxZoom && markerClusteringSettings.maxZoom >= 0 && markerClusteringSettings.maxZoom < 19) {
clusteringSettings.disableClusteringAtZoom = Math.floor(markerClusteringSettings.maxZoom);
}
this.markersCluster = new MarkerClusterGroup(clusteringSettings);
}
@ -355,7 +344,6 @@ export default abstract class LeafletMap {
if (this.options.initDragMode) {
this.map.pm.enableGlobalDragMode();
this.ignoreUpdateBounds = true;
}
this.map.on('pm:globaldrawmodetoggled', (e) => this.ignoreUpdateBounds = e.enabled);
@ -468,7 +456,7 @@ export default abstract class LeafletMap {
});
});
if (this.options.useDefaultCenterPosition) {
this.map.panTo(this.options.defaultCenterPosition);
this.map.panTo(this.options.parsedDefaultCenterPosition);
this.bounds = map.getBounds();
} else {
this.bounds = new L.LatLngBounds(null, null);
@ -488,7 +476,7 @@ export default abstract class LeafletMap {
}
if (this.updatePending) {
this.updatePending = false;
this.updateData(this.drawRoutes, this.showPolygon);
this.updateData(this.drawRoutes);
}
this.createdControlButtonTooltip();
}
@ -565,7 +553,7 @@ export default abstract class LeafletMap {
if (!this.options.fitMapBounds && this.options.defaultZoomLevel) {
this.map.setZoom(this.options.defaultZoomLevel, { animate: false });
if (this.options.useDefaultCenterPosition) {
this.map.panTo(this.options.defaultCenterPosition, { animate: false });
this.map.panTo(this.options.parsedDefaultCenterPosition, { animate: false });
}
else {
this.map.panTo(this.bounds.getCenter());
@ -581,7 +569,7 @@ export default abstract class LeafletMap {
}
});
if (this.options.useDefaultCenterPosition) {
this.bounds = this.bounds.extend(this.options.defaultCenterPosition);
this.bounds = this.bounds.extend(this.options.parsedDefaultCenterPosition);
}
this.map.fitBounds(this.bounds, { padding: padding || [50, 50], animate: false });
this.map.invalidateSize();
@ -643,7 +631,7 @@ export default abstract class LeafletMap {
};
}
updateData(drawRoutes: boolean, showPolygon: boolean) {
updateData(drawRoutes: boolean) {
const data = this.ctx.data;
let formattedData = formattedDataFormDatasourceData(data);
if (this.ctx.latestData && this.ctx.latestData.length) {
@ -654,18 +642,17 @@ export default abstract class LeafletMap {
if (drawRoutes) {
polyData = formattedDataArrayFromDatasourceData(data);
}
this.updateFromData(drawRoutes, showPolygon, formattedData, polyData);
this.updateFromData(drawRoutes, formattedData, polyData);
}
updateFromData(drawRoutes: boolean, showPolygon: boolean, formattedData: FormattedData[],
updateFromData(drawRoutes: boolean, formattedData: FormattedData[],
polyData: FormattedData[][], markerClickCallback?: any) {
this.drawRoutes = drawRoutes;
this.showPolygon = showPolygon;
if (this.map) {
if (drawRoutes) {
this.updatePolylines(polyData, formattedData, false);
}
if (showPolygon) {
if (this.options.showPolygon) {
this.updatePolygons(formattedData, false);
}
if (this.options.showCircle) {
@ -776,7 +763,7 @@ export default abstract class LeafletMap {
bounds.extend(polyline.leafletPoly.getBounds());
});
}
if (this.showPolygon) {
if (this.options.showPolygon) {
this.polygons.forEach((polygon) => {
bounds.extend(polygon.leafletPoly.getBounds());
});
@ -786,7 +773,7 @@ export default abstract class LeafletMap {
bounds.extend(polygon.leafletCircle.getBounds());
});
}
if ((this.options as MarkerSettings).useClusterMarkers && this.markersCluster.getBounds().isValid()) {
if (this.options.useClusterMarkers && this.markersCluster.getBounds().isValid()) {
bounds.extend(this.markersCluster.getBounds());
} else {
this.markers.forEach((marker) => {
@ -802,6 +789,10 @@ export default abstract class LeafletMap {
this.fitBounds(bounds);
}
}
if (this.options.initDragMode && !this.initDragModeIgnoreUpdateBoundsSet) {
this.initDragModeIgnoreUpdateBoundsSet = true;
this.ignoreUpdateBounds = true;
}
}
// Markers
@ -815,7 +806,7 @@ export default abstract class LeafletMap {
rawMarkers.forEach(data => {
if (data.rotationAngle || data.rotationAngle === 0) {
const currentImage: MarkerImageInfo = this.options.useMarkerImageFunction ?
safeExecute(this.options.markerImageFunction,
safeExecute(this.options.parsedMarkerImageFunction,
[data, this.options.markerImages, markersData, data.dsIndex]) : this.options.currentImage;
const style = currentImage ? 'background-image: url(' + currentImage.url + ');' : '';
this.options.icon = { icon: L.divIcon({
@ -833,7 +824,7 @@ export default abstract class LeafletMap {
updatedMarkers.push(m);
}
} else {
m = this.createMarker(data.entityName, data, markersData, this.options, updateBounds, callback);
m = this.createMarker(data.entityName, data, markersData, this.options, updateBounds, callback, this.options.snappable);
if (m) {
createdMarkers.push(m);
}
@ -867,9 +858,9 @@ export default abstract class LeafletMap {
this.saveLocation(data, this.convertToCustomFormat(e.target._latlng)).subscribe();
}
private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: UnitedMapSettings,
updateBounds = true, callback?): Marker {
const newMarker = new Marker(this, this.convertPosition(data), settings, data, dataSources, this.dragMarker);
private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: Partial<WidgetMarkersSettings>,
updateBounds = true, callback?, snappable = false): Marker {
const newMarker = new Marker(this, this.convertPosition(data), settings, data, dataSources, this.dragMarker, snappable);
if (callback) {
newMarker.leafletMarker.on('click', () => {
callback(data, true);
@ -885,7 +876,7 @@ export default abstract class LeafletMap {
return newMarker;
}
private updateMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings): Marker {
private updateMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: Partial<WidgetMarkersSettings>): Marker {
const marker: Marker = this.markers.get(key);
const location = this.convertPosition(data);
marker.updateMarkerPosition(location);
@ -933,7 +924,7 @@ export default abstract class LeafletMap {
if (!!this.convertPosition(pdata)) {
const dsData = pointsData.map(ds => ds[tsIndex]);
if (this.options.useColorPointFunction) {
pointColor = safeExecute(this.options.colorPointFunction, [pdata, dsData, pdata.dsIndex]);
pointColor = safeExecute(this.options.parsedColorPointFunction, [pdata, dsData, pdata.dsIndex]);
}
const point = L.circleMarker(this.convertPosition(pdata), {
color: pointColor,
@ -980,7 +971,8 @@ export default abstract class LeafletMap {
});
}
createPolyline(data: FormattedData, tsData: FormattedData[], dsData: FormattedData[], settings: PolylineSettings, updateBounds = true) {
createPolyline(data: FormattedData, tsData: FormattedData[], dsData: FormattedData[],
settings: Partial<WidgetPolylineSettings>, updateBounds = true) {
const poly = new Polyline(this.map,
tsData.map(el => this.convertPosition(el)).filter(el => !!el), data, dsData, settings);
if (updateBounds) {
@ -990,7 +982,8 @@ export default abstract class LeafletMap {
this.polylines.set(data.entityName, poly);
}
updatePolyline(data: FormattedData, tsData: FormattedData[], dsData: FormattedData[], settings: PolylineSettings, updateBounds = true) {
updatePolyline(data: FormattedData, tsData: FormattedData[], dsData: FormattedData[],
settings: Partial<WidgetPolylineSettings>, updateBounds = true) {
const poly = this.polylines.get(data.entityName);
const oldBounds = poly.leafletPoly.getBounds();
poly.updatePolyline(tsData.map(el => this.convertPosition(el)).filter(el => !!el), data, dsData, settings);
@ -1031,7 +1024,7 @@ export default abstract class LeafletMap {
if (this.polygons.get(data.entityName)) {
this.updatePolygon(data, polyData, this.options, updateBounds);
} else {
this.createPolygon(data, polyData, this.options, updateBounds);
this.createPolygon(data, polyData, this.options, updateBounds, this.options.snappable);
}
keys.push(data.entityName);
}
@ -1066,8 +1059,9 @@ export default abstract class LeafletMap {
this.saveLocation(data, this.convertPolygonToCustomFormat(coordinates)).subscribe(() => {});
}
createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: UnitedMapSettings, updateBounds = true) {
const polygon = new Polygon(this.map, polyData, dataSources, settings, this.dragPolygonVertex);
createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: Partial<WidgetPolygonSettings>,
updateBounds = true, snappable = false) {
const polygon = new Polygon(this, polyData, dataSources, settings, this.dragPolygonVertex, snappable);
if (updateBounds) {
const bounds = polygon.leafletPoly.getBounds();
this.fitBounds(bounds);
@ -1075,7 +1069,7 @@ export default abstract class LeafletMap {
this.polygons.set(polyData.entityName, polygon);
}
updatePolygon(polyData: FormattedData, dataSources: FormattedData[], settings: PolygonSettings, updateBounds = true) {
updatePolygon(polyData: FormattedData, dataSources: FormattedData[], settings: Partial<WidgetPolygonSettings>, updateBounds = true) {
const poly = this.polygons.get(polyData.entityName);
const oldBounds = poly.leafletPoly.getBounds();
poly.updatePolygon(polyData, dataSources, settings);
@ -1167,7 +1161,7 @@ export default abstract class LeafletMap {
}
createdCircle(data: FormattedData, dataSources: FormattedData[], updateBounds = true) {
const circle = new Circle(this, data, dataSources, this.options, this.dragCircleVertex);
const circle = new Circle(this, data, dataSources, this.options, this.dragCircleVertex, this.options.snappable);
if (updateBounds) {
const bounds = circle.leafletCircle.getBounds();
this.fitBounds(bounds);

878
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts

@ -17,138 +17,433 @@
import { Datasource, FormattedData } from '@app/shared/models/widget.models';
import tinycolor from 'tinycolor2';
import { BaseIconOptions, Icon } from 'leaflet';
import { DeviceProfileType } from '@shared/models/device.models';
export const DEFAULT_MAP_PAGE_SIZE = 16384;
export const DEFAULT_ZOOM_LEVEL = 8;
export type MarkerImageInfo = {
url: string;
size: number;
markerOffset?: [number, number];
tooltipOffset?: [number, number];
};
export type MarkerIconInfo = {
icon: Icon<BaseIconOptions>;
size: [number, number];
};
export interface MapImage {
imageUrl: string;
aspect: number;
update?: boolean;
}
export type actionsHandler = ($event: Event, datasource: Datasource) => void;
export interface CircleData {
latitude: number;
longitude: number;
radius: number;
}
export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => MarkerImageInfo;
export type PosFuncton = (origXPos, origYPos) => { x, y };
export type MarkerIconReadyFunction = (icon: MarkerIconInfo) => void;
export type MapSettings = {
draggableMarker: boolean;
editablePolygon: boolean;
posFunction: PosFuncton;
defaultZoomLevel?: number;
disableScrollZooming?: boolean;
disableZoomControl?: boolean;
minZoomLevel?: number;
useClusterMarkers: boolean;
latKeyName?: string;
lngKeyName?: string;
xPosKeyName?: string;
yPosKeyName?: string;
imageEntityAlias: string;
imageUrlAttribute: string;
mapProvider: MapProviders;
mapProviderHere: string;
mapUrl?: string;
mapImageUrl?: string;
provider?: MapProviders;
credentials?: any; // declare credentials format
gmApiKey?: string;
defaultCenterPosition?: [number, number];
markerClusteringSetting?;
useDefaultCenterPosition?: boolean;
gmDefaultMapType?: string;
useLabelFunction: boolean;
zoomOnClick: boolean,
maxZoom: number,
showCoverageOnHover: boolean,
animate: boolean,
maxClusterRadius: number,
spiderfyOnMaxZoom: boolean,
chunkedLoading: boolean,
removeOutsideVisibleBounds: boolean,
useCustomProvider: boolean,
customProviderTileUrl: string;
mapPageSize: number;
export enum GoogleMapType {
roadmap = 'roadmap',
satellite = 'satellite',
hybrid = 'hybrid',
terrain = 'terrain'
}
export const googleMapTypeProviderTranslationMap = new Map<GoogleMapType, string>(
[
[GoogleMapType.roadmap, 'widgets.maps.google-map-type-roadmap'],
[GoogleMapType.satellite, 'widgets.maps.google-map-type-satelite'],
[GoogleMapType.hybrid, 'widgets.maps.google-map-type-hybrid'],
[GoogleMapType.terrain, 'widgets.maps.google-map-type-terrain']
]
);
export interface GoogleMapProviderSettings {
gmApiKey: string;
gmDefaultMapType: GoogleMapType;
}
export const defaultGoogleMapProviderSettings: GoogleMapProviderSettings = {
gmApiKey: 'AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q',
gmDefaultMapType: GoogleMapType.roadmap
};
export enum OpenStreetMapProvider {
openStreetMapnik = 'OpenStreetMap.Mapnik',
openStreetHot = 'OpenStreetMap.HOT',
esriWorldStreetMap = 'Esri.WorldStreetMap',
esriWorldTopoMap = 'Esri.WorldTopoMap',
cartoDbPositron = 'CartoDB.Positron',
cartoDbDarkMatter = 'CartoDB.DarkMatter'
}
export const openStreetMapProviderTranslationMap = new Map<OpenStreetMapProvider, string>(
[
[OpenStreetMapProvider.openStreetMapnik, 'widgets.maps.openstreet-provider-mapnik'],
[OpenStreetMapProvider.openStreetHot, 'widgets.maps.openstreet-provider-hot'],
[OpenStreetMapProvider.esriWorldStreetMap, 'widgets.maps.openstreet-provider-esri-street'],
[OpenStreetMapProvider.esriWorldTopoMap, 'widgets.maps.openstreet-provider-esri-topo'],
[OpenStreetMapProvider.cartoDbPositron, 'widgets.maps.openstreet-provider-cartodb-positron'],
[OpenStreetMapProvider.cartoDbDarkMatter, 'widgets.maps.openstreet-provider-cartodb-dark-matter']
]
);
export interface OpenStreetMapProviderSettings {
mapProvider: OpenStreetMapProvider;
useCustomProvider: boolean;
customProviderTileUrl?: string;
}
export const defaultOpenStreetMapProviderSettings: OpenStreetMapProviderSettings = {
mapProvider: OpenStreetMapProvider.openStreetMapnik,
useCustomProvider: false,
customProviderTileUrl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
};
export enum HereMapProvider {
hereNormalDay = 'HERE.normalDay',
hereNormalNight = 'HERE.normalNight',
hereHybridDay = 'HERE.hybridDay',
hereTerrainDay = 'HERE.terrainDay'
}
export const hereMapProviderTranslationMap = new Map<HereMapProvider, string>(
[
[HereMapProvider.hereNormalDay, 'widgets.maps.here-map-normal-day'],
[HereMapProvider.hereNormalNight, 'widgets.maps.here-map-normal-night'],
[HereMapProvider.hereHybridDay, 'widgets.maps.here-map-hybrid-day'],
[HereMapProvider.hereTerrainDay, 'widgets.maps.here-map-terrain-day']
]
);
export interface HereMapProviderSettings {
mapProviderHere: HereMapProvider;
credentials: {
app_id: string;
app_code: string;
};
}
export const defaultHereMapProviderSettings: HereMapProviderSettings = {
mapProviderHere: HereMapProvider.hereNormalDay,
credentials: {
app_id: 'AhM6TzD9ThyK78CT3ptx',
app_code: 'p6NPiITB3Vv0GMUFnkLOOg'
}
};
export interface ImageMapProviderSettings {
mapImageUrl?: string;
imageEntityAlias?: string;
imageUrlAttribute?: string;
}
export const defaultImageMapProviderSettings: ImageMapProviderSettings = {
mapImageUrl: 'data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==',
imageEntityAlias: '',
imageUrlAttribute: ''
};
export enum TencentMapType {
roadmap = 'roadmap',
satellite = 'satellite',
hybrid = 'hybrid'
}
export const tencentMapTypeProviderTranslationMap = new Map<TencentMapType, string>(
[
[TencentMapType.roadmap, 'widgets.maps.tencent-map-type-roadmap'],
[TencentMapType.satellite, 'widgets.maps.tencent-map-type-satelite'],
[TencentMapType.hybrid, 'widgets.maps.tencent-map-type-hybrid']
]
);
export interface TencentMapProviderSettings {
tmApiKey: string;
tmDefaultMapType: TencentMapType;
}
export const defaultTencentMapProviderSettings: TencentMapProviderSettings = {
tmApiKey: '84d6d83e0e51e481e50454ccbe8986b',
tmDefaultMapType: TencentMapType.roadmap
};
export enum MapProviders {
google = 'google-map',
openstreet = 'openstreet-map',
here = 'here',
image = 'image-map',
tencent = 'tencent-map'
google = 'google-map',
openstreet = 'openstreet-map',
here = 'here',
image = 'image-map',
tencent = 'tencent-map'
}
export type MarkerImageInfo = {
url: string;
size: number;
markerOffset?: [number, number];
tooltipOffset?: [number, number];
export const mapProviderTranslationMap = new Map<MapProviders, string>(
[
[MapProviders.google, 'widgets.maps.map-provider-google'],
[MapProviders.openstreet, 'widgets.maps.map-provider-openstreet'],
[MapProviders.here, 'widgets.maps.map-provider-here'],
[MapProviders.image, 'widgets.maps.map-provider-image'],
[MapProviders.tencent, 'widgets.maps.map-provider-tencent']
]
);
export interface MapProviderSettings extends GoogleMapProviderSettings, OpenStreetMapProviderSettings,
HereMapProviderSettings, ImageMapProviderSettings, TencentMapProviderSettings {
provider: MapProviders;
}
export const defaultMapProviderSettings: MapProviderSettings = {
provider: MapProviders.openstreet,
...defaultGoogleMapProviderSettings,
...defaultOpenStreetMapProviderSettings,
...defaultHereMapProviderSettings,
...defaultImageMapProviderSettings,
...defaultTencentMapProviderSettings
};
export type MarkerIconInfo = {
icon: Icon<BaseIconOptions>;
size: [number, number];
export interface CommonMapSettings {
latKeyName: string;
lngKeyName: string;
xPosKeyName: string;
yPosKeyName: string;
defaultZoomLevel: number;
defaultCenterPosition?: string;
disableScrollZooming: boolean;
disableZoomControl: boolean;
fitMapBounds: boolean;
useDefaultCenterPosition: boolean;
mapPageSize: number;
}
export interface WidgetCommonMapSettings extends CommonMapSettings {
parsedDefaultCenterPosition: [number, number];
minZoomLevel: number;
}
export interface WidgetToolipSettings {
tooltipAction: { [name: string]: actionsHandler };
}
export const defaultCommonMapSettings: CommonMapSettings = {
latKeyName: 'latitude',
lngKeyName: 'longitude',
xPosKeyName: 'xPos',
yPosKeyName: 'yPos',
defaultZoomLevel: null,
defaultCenterPosition: '0,0',
disableScrollZooming: false,
disableZoomControl: false,
fitMapBounds: true,
useDefaultCenterPosition: false,
mapPageSize: DEFAULT_MAP_PAGE_SIZE
};
export interface TripAnimationCommonSettings {
normalizationStep: number;
latKeyName: string;
lngKeyName: string;
showTooltip: boolean;
tooltipColor: string;
tooltipFontColor: string;
tooltipOpacity: number;
useTooltipFunction: boolean;
tooltipPattern?: string;
tooltipFunction?: string;
autocloseTooltip: boolean;
}
export interface WidgetTripAnimationCommonSettings extends TripAnimationCommonSettings {
parsedTooltipFunction: GenericFunction;
}
export const defaultTripAnimationCommonSettings: TripAnimationCommonSettings = {
normalizationStep: 1000,
latKeyName: 'latitude',
lngKeyName: 'longitude',
showTooltip: true,
tooltipColor: '#fff',
tooltipFontColor: '#000',
tooltipOpacity: 1,
useTooltipFunction: false,
tooltipPattern: '<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}',
tooltipFunction: null,
autocloseTooltip: true
};
export type MarkerSettings = {
tooltipPattern?: any;
tooltipAction: { [name: string]: actionsHandler };
icon?: MarkerIconInfo;
showLabel?: boolean;
label: string;
labelColor: string;
labelText: string;
useLabelFunction: boolean;
draggableMarker: boolean;
showTooltip?: boolean;
useTooltipFunction: boolean;
useColorFunction: boolean;
color?: string;
tinyColor?: tinycolor.Instance;
autocloseTooltip: boolean;
showTooltipAction: string;
useClusterMarkers: boolean;
currentImage?: MarkerImageInfo;
useMarkerImageFunction?: boolean;
markerImages?: string[];
markerImageSize: number;
fitMapBounds: boolean;
markerImage: string;
markerClick: { [name: string]: actionsHandler };
colorFunction: GenericFunction;
tooltipFunction: GenericFunction;
labelFunction: GenericFunction;
markerImageFunction?: MarkerImageFunction;
markerOffsetX: number;
markerOffsetY: number;
tooltipOffsetX: number;
tooltipOffsetY: number;
export enum ShowTooltipAction {
click = 'click',
hover = 'hover'
}
export const showTooltipActionTranslationMap = new Map<ShowTooltipAction, string>(
[
[ShowTooltipAction.click, 'widgets.maps.show-tooltip-action-click'],
[ShowTooltipAction.hover, 'widgets.maps.show-tooltip-action-hover']
]
);
export interface MarkersSettings {
markerOffsetX: number;
markerOffsetY: number;
posFunction?: string;
draggableMarker: boolean;
showLabel: boolean;
useLabelFunction: boolean;
label?: string;
labelFunction?: string;
showTooltip: boolean;
showTooltipAction: ShowTooltipAction;
autocloseTooltip: boolean;
useTooltipFunction: boolean;
tooltipPattern?: string;
tooltipFunction?: string;
tooltipOffsetX: number;
tooltipOffsetY: number;
color?: string;
useColorFunction: boolean;
colorFunction?: string;
useMarkerImageFunction: boolean;
markerImage?: string;
markerImageSize?: number;
markerImageFunction?: string;
markerImages?: string[];
}
export interface WidgetMarkersSettings extends MarkersSettings, WidgetToolipSettings {
parsedLabelFunction: GenericFunction;
parsedTooltipFunction: GenericFunction;
parsedColorFunction: GenericFunction;
parsedMarkerImageFunction: MarkerImageFunction;
markerClick: { [name: string]: actionsHandler };
currentImage: MarkerImageInfo;
tinyColor: tinycolor.Instance;
icon: MarkerIconInfo;
}
export const defaultMarkersSettings: MarkersSettings = {
markerOffsetX: 0.5,
markerOffsetY: 1,
posFunction: 'return {x: origXPos, y: origYPos};',
draggableMarker: false,
showLabel: true,
useLabelFunction: false,
label: '${entityName}',
labelFunction: null,
showTooltip: true,
showTooltipAction: ShowTooltipAction.click,
autocloseTooltip: true,
useTooltipFunction: false,
tooltipPattern: '<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}',
tooltipFunction: null,
tooltipOffsetX: 0,
tooltipOffsetY: -1,
color: '#FE7569',
useColorFunction: false,
colorFunction: null,
useMarkerImageFunction: false,
markerImage: null,
markerImageSize: 34,
markerImageFunction: null,
markerImages: []
};
export interface TripAnimationMarkerSettings {
rotationAngle: number;
showLabel: boolean;
useLabelFunction: boolean;
label?: string;
labelFunction?: string;
useMarkerImageFunction: boolean;
markerImage?: string;
markerImageSize?: number;
markerImageFunction?: string;
markerImages?: string[];
}
export interface WidgetTripAnimationMarkerSettings extends TripAnimationMarkerSettings {
parsedLabelFunction: GenericFunction;
parsedMarkerImageFunction: MarkerImageFunction;
}
export const defaultTripAnimationMarkersSettings: TripAnimationMarkerSettings = {
rotationAngle: 0,
showLabel: true,
useLabelFunction: false,
label: '${entityName}',
labelFunction: null,
useMarkerImageFunction: false,
markerImage: null,
markerImageSize: 34,
markerImageFunction: null,
markerImages: []
};
export type PolygonSettings = {
showPolygon: boolean;
polygonKeyName: string;
polKeyName: string; // deprecated
polygonStrokeOpacity: number;
polygonOpacity: number;
polygonStrokeWeight: number;
polygonStrokeColor: string;
polygonColor: string;
showPolygonLabel?: boolean;
polygonLabel: string;
polygonLabelColor: string;
polygonLabelText: string;
usePolygonLabelFunction: boolean;
showPolygonTooltip: boolean;
autoClosePolygonTooltip: boolean;
showPolygonTooltipAction: string;
tooltipAction: { [name: string]: actionsHandler };
polygonTooltipPattern: string;
usePolygonTooltipFunction: boolean;
polygonClick: { [name: string]: actionsHandler };
usePolygonColorFunction: boolean;
usePolygonStrokeColorFunction: boolean;
polygonTooltipFunction: GenericFunction;
polygonColorFunction?: GenericFunction;
polygonStrokeColorFunction?: GenericFunction;
polygonLabelFunction?: GenericFunction;
editablePolygon: boolean;
export interface PolygonSettings {
showPolygon: boolean;
polygonKeyName: string;
editablePolygon: boolean;
showPolygonLabel: boolean;
usePolygonLabelFunction: boolean;
polygonLabel?: string;
polygonLabelFunction?: string;
showPolygonTooltip: boolean;
showPolygonTooltipAction: ShowTooltipAction;
autoClosePolygonTooltip: boolean;
usePolygonTooltipFunction: boolean;
polygonTooltipPattern?: string;
polygonTooltipFunction?: string;
polygonColor?: string;
polygonOpacity?: number;
usePolygonColorFunction: boolean;
polygonColorFunction?: string;
polygonStrokeColor?: string;
polygonStrokeOpacity?: number;
polygonStrokeWeight?: number;
usePolygonStrokeColorFunction: boolean;
polygonStrokeColorFunction?: string;
}
export interface WidgetPolygonSettings extends PolygonSettings, WidgetToolipSettings {
parsedPolygonLabelFunction: GenericFunction;
parsedPolygonTooltipFunction: GenericFunction;
parsedPolygonColorFunction: GenericFunction;
parsedPolygonStrokeColorFunction: GenericFunction;
polygonClick: { [name: string]: actionsHandler };
}
export const defaultPolygonSettings: PolygonSettings = {
showPolygon: false,
polygonKeyName: 'perimeter',
editablePolygon: false,
showPolygonLabel: false,
usePolygonLabelFunction: false,
polygonLabel: '${entityName}',
polygonLabelFunction: null,
showPolygonTooltip: false,
showPolygonTooltipAction: ShowTooltipAction.click,
autoClosePolygonTooltip: true,
usePolygonTooltipFunction: false,
polygonTooltipPattern: '<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}',
polygonTooltipFunction: null,
polygonColor: '#3388ff',
polygonOpacity: 0.2,
usePolygonColorFunction: false,
polygonColorFunction: null,
polygonStrokeColor: '#3388ff',
polygonStrokeOpacity: 1,
polygonStrokeWeight: 3,
usePolygonStrokeColorFunction: false,
polygonStrokeColorFunction: null
};
export interface CircleSettings {
@ -157,178 +452,221 @@ export interface CircleSettings {
editableCircle: boolean;
showCircleLabel: boolean;
useCircleLabelFunction: boolean;
circleLabel: string;
circleLabelFunction?: GenericFunction;
circleFillColor: string;
useCircleFillColorFunction: boolean;
circleFillColorFunction?: GenericFunction;
circleFillColorOpacity: number;
circleStrokeColor: string;
useCircleStrokeColorFunction: boolean;
circleStrokeColorFunction: GenericFunction;
circleStrokeOpacity: number;
circleStrokeWeight: number;
circleLabel?: string;
circleLabelFunction?: string;
showCircleTooltip: boolean;
showCircleTooltipAction: string;
showCircleTooltipAction: ShowTooltipAction;
autoCloseCircleTooltip: boolean;
useCircleTooltipFunction: boolean;
circleTooltipPattern: string;
circleTooltipFunction?: GenericFunction;
circleClick?: { [name: string]: actionsHandler };
}
export type PolylineSettings = {
usePolylineDecorator: any;
autocloseTooltip: boolean;
showTooltipAction: string;
useColorFunction: any;
tooltipAction: { [name: string]: actionsHandler };
color: string;
useStrokeOpacityFunction: any;
strokeOpacity: number;
useStrokeWeightFunction: any;
strokeWeight: number;
decoratorOffset: string | number;
endDecoratorOffset: string | number;
decoratorRepeat: string | number;
decoratorSymbol: any;
decoratorSymbolSize: any;
useDecoratorCustomColor: any;
decoratorCustomColor: any;
colorFunction: GenericFunction;
strokeOpacityFunction: GenericFunction;
strokeWeightFunction: GenericFunction;
circleTooltipPattern?: string;
circleTooltipFunction?: string;
circleFillColor?: string;
circleFillColorOpacity?: number;
useCircleFillColorFunction: boolean;
circleFillColorFunction?: string;
circleStrokeColor?: string;
circleStrokeOpacity?: number;
circleStrokeWeight?: number;
useCircleStrokeColorFunction: boolean;
circleStrokeColorFunction?: string;
}
export interface WidgetCircleSettings extends CircleSettings, WidgetToolipSettings {
parsedCircleLabelFunction: GenericFunction;
parsedCircleTooltipFunction: GenericFunction;
parsedCircleFillColorFunction: GenericFunction;
parsedCircleStrokeColorFunction: GenericFunction;
circleClick: { [name: string]: actionsHandler };
}
export const defaultCircleSettings: CircleSettings = {
showCircle: false,
circleKeyName: 'perimeter',
editableCircle: false,
showCircleLabel: false,
useCircleLabelFunction: false,
circleLabel: '${entityName}',
circleLabelFunction: null,
showCircleTooltip: false,
showCircleTooltipAction: ShowTooltipAction.click,
autoCloseCircleTooltip: true,
useCircleTooltipFunction: false,
circleTooltipPattern: '<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}',
circleTooltipFunction: null,
circleFillColor: '#3388ff',
circleFillColorOpacity: 0.2,
useCircleFillColorFunction: false,
circleFillColorFunction: null,
circleStrokeColor: '#3388ff',
circleStrokeOpacity: 1,
circleStrokeWeight: 3,
useCircleStrokeColorFunction: false,
circleStrokeColorFunction: null
};
export interface EditorSettings {
snappable: boolean;
initDragMode: boolean;
hideAllControlButton: boolean;
hideDrawControlButton: boolean;
hideEditControlButton: boolean;
hideRemoveControlButton: boolean;
export enum PolylineDecoratorSymbol {
arrowHead = 'arrowHead',
dash = 'dash'
}
export interface HistorySelectSettings {
buttonColor: string;
export const polylineDecoratorSymbolTranslationMap = new Map<PolylineDecoratorSymbol, string>(
[
[PolylineDecoratorSymbol.arrowHead, 'widgets.maps.decorator-symbol-arrow-head'],
[PolylineDecoratorSymbol.dash, 'widgets.maps.decorator-symbol-dash']
]
);
export interface PolylineSettings {
useStrokeWeightFunction?: boolean;
strokeWeight: number;
strokeWeightFunction?: string;
useStrokeOpacityFunction?: boolean;
strokeOpacity: number;
strokeOpacityFunction?: string;
useColorFunction?: boolean;
color?: string;
colorFunction?: string;
usePolylineDecorator?: boolean;
decoratorSymbol?: PolylineDecoratorSymbol;
decoratorSymbolSize?: number;
useDecoratorCustomColor?: boolean;
decoratorCustomColor?: string;
decoratorOffset?: string;
endDecoratorOffset?: string;
decoratorRepeat?: string;
}
export interface MapImage {
imageUrl: string;
aspect: number;
update?: boolean;
}
export interface TripAnimationSettings extends PolygonSettings, CircleSettings {
showPoints: boolean;
pointColor: string;
pointSize: number;
pointTooltipOnRightPanel: boolean;
usePointAsAnchor: boolean;
normalizationStep: number;
showPolygon: boolean;
showLabel: boolean;
showTooltip: boolean;
latKeyName: string;
lngKeyName: string;
rotationAngle: number;
label: string;
tooltipPattern: string;
tooltipColor: string;
tooltipOpacity: number;
tooltipFontColor: string;
useTooltipFunction: boolean;
useLabelFunction: boolean;
pointAsAnchorFunction: GenericFunction;
tooltipFunction: GenericFunction;
labelFunction: GenericFunction;
useColorPointFunction: boolean;
colorPointFunction: GenericFunction;
export interface WidgetPolylineSettings extends PolylineSettings {
parsedColorFunction: GenericFunction;
parsedStrokeOpacityFunction: GenericFunction;
parsedStrokeWeightFunction: GenericFunction;
}
export type actionsHandler = ($event: Event, datasource: Datasource) => void;
export const defaultRouteMapSettings: PolylineSettings = {
strokeWeight: 2,
strokeOpacity: 1.0
};
export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings
& CircleSettings & TripAnimationSettings & EditorSettings;
export const defaultSettings: Partial<UnitedMapSettings> = {
xPosKeyName: 'xPos',
yPosKeyName: 'yPos',
markerOffsetX: 0.5,
markerOffsetY: 1,
tooltipOffsetX: 0,
tooltipOffsetY: -1,
latKeyName: 'latitude',
lngKeyName: 'longitude',
polygonKeyName: 'perimeter',
showLabel: false,
label: '${entityName}',
showTooltip: false,
useDefaultCenterPosition: false,
showTooltipAction: 'click',
autocloseTooltip: false,
showPolygon: false,
labelColor: '#000000',
color: '#FE7569',
showPolygonLabel: false,
polygonColor: '#3388ff',
polygonStrokeColor: '#3388ff',
polygonLabelColor: '#000000',
polygonOpacity: 0.2,
polygonStrokeOpacity: 1,
polygonStrokeWeight: 3,
showPolygonTooltipAction: 'click',
autoClosePolygonTooltip: true,
useLabelFunction: false,
markerImages: [],
strokeWeight: 2,
strokeOpacity: 1.0,
disableScrollZooming: false,
minZoomLevel: 16,
credentials: '',
markerClusteringSetting: null,
draggableMarker: false,
editablePolygon: false,
fitMapBounds: true,
mapPageSize: DEFAULT_MAP_PAGE_SIZE,
snappable: false,
initDragMode: false,
hideAllControlButton: false,
hideDrawControlButton: false,
hideEditControlButton: false,
hideRemoveControlButton: false,
showCircle: true,
circleKeyName: 'perimeter',
editableCircle: false,
showCircleLabel: false,
useCircleLabelFunction: false,
circleLabel: '${entityName}',
circleFillColor: '#3388ff',
useCircleFillColorFunction: false,
circleFillColorOpacity: 0.2,
circleStrokeColor: '#3388ff',
useCircleStrokeColorFunction: false,
circleStrokeOpacity: 1,
circleStrokeWeight: 3,
showCircleTooltip: false,
showCircleTooltipAction: 'click',
autoCloseCircleTooltip: true,
useCircleTooltipFunction: false
export const defaultTripAnimationPathSettings: PolylineSettings = {
color: null,
strokeWeight: 2,
strokeOpacity: 1,
useColorFunction: false,
colorFunction: null,
usePolylineDecorator: false,
decoratorSymbol: PolylineDecoratorSymbol.arrowHead,
decoratorSymbolSize: 10,
useDecoratorCustomColor: false,
decoratorCustomColor: '#000',
decoratorOffset: '20px',
endDecoratorOffset: '20px',
decoratorRepeat: '20px'
};
export interface CircleData {
latitude: number;
longitude: number;
radius: number;
export interface PointsSettings {
showPoints?: boolean;
pointColor?: string;
useColorPointFunction?: false;
colorPointFunction?: string;
pointSize?: number;
usePointAsAnchor?: false;
pointAsAnchorFunction?: string;
pointTooltipOnRightPanel?: boolean;
}
export interface WidgetPointsSettings extends PointsSettings {
parsedColorPointFunction: GenericFunction;
parsedPointAsAnchorFunction: GenericFunction;
}
export const defaultTripAnimationPointSettings: PointsSettings = {
showPoints: false,
pointColor: null,
useColorPointFunction: false,
colorPointFunction: null,
pointSize: 10,
usePointAsAnchor: false,
pointAsAnchorFunction: null,
pointTooltipOnRightPanel: true
};
export interface MarkerClusteringSettings {
useClusterMarkers: boolean;
zoomOnClick: boolean;
maxZoom: number;
maxClusterRadius: number;
animate: boolean;
spiderfyOnMaxZoom: boolean;
showCoverageOnHover: boolean;
chunkedLoading: boolean;
removeOutsideVisibleBounds: boolean;
}
export const defaultMarkerClusteringSettings: MarkerClusteringSettings = {
useClusterMarkers: false,
zoomOnClick: true,
maxZoom: null,
maxClusterRadius: 80,
animate: true,
spiderfyOnMaxZoom: false,
showCoverageOnHover: true,
chunkedLoading: false,
removeOutsideVisibleBounds: true
};
export interface MapEditorSettings {
snappable: boolean;
initDragMode: boolean;
hideAllControlButton: boolean;
hideDrawControlButton: boolean;
hideEditControlButton: boolean;
hideRemoveControlButton: boolean;
}
export const defaultMapEditorSettings: MapEditorSettings = {
snappable: false,
initDragMode: false,
hideAllControlButton: false,
hideDrawControlButton: false,
hideEditControlButton: false,
hideRemoveControlButton: false
};
export type UnitedMapSettings = MapProviderSettings & CommonMapSettings & MarkersSettings &
PolygonSettings & CircleSettings & PolylineSettings & PointsSettings & MarkerClusteringSettings & MapEditorSettings;
export const defaultMapSettings: UnitedMapSettings = {
...defaultMapProviderSettings,
...defaultCommonMapSettings,
...defaultMarkersSettings,
...defaultPolygonSettings,
...defaultCircleSettings,
...defaultRouteMapSettings,
...defaultMarkerClusteringSettings,
...defaultMapEditorSettings
};
export type WidgetUnitedMapSettings = UnitedMapSettings & Partial<WidgetCommonMapSettings &
WidgetMarkersSettings & WidgetPolygonSettings & WidgetCircleSettings & WidgetPolylineSettings & WidgetPointsSettings>;
export type UnitedTripAnimationSettings = MapProviderSettings & TripAnimationCommonSettings & TripAnimationMarkerSettings &
PolylineSettings & PointsSettings & PolygonSettings & CircleSettings;
export const defaultTripAnimationSettings: UnitedTripAnimationSettings = {
...defaultMapProviderSettings,
...defaultTripAnimationCommonSettings,
...defaultTripAnimationMarkersSettings,
...defaultTripAnimationPathSettings,
...defaultTripAnimationPointSettings,
...defaultPolygonSettings,
...defaultCircleSettings,
};
export interface HistorySelectSettings {
buttonColor: string;
}
export const circleDataKeys: Array<keyof CircleData> = ['latitude', 'longitude', 'radius'];
export type WidgetUnitedTripAnimationSettings = UnitedTripAnimationSettings & HistorySelectSettings &
Partial<WidgetTripAnimationCommonSettings & WidgetTripAnimationMarkerSettings &
WidgetPolylineSettings & WidgetPointsSettings & WidgetPolygonSettings & WidgetCircleSettings>;
export const hereProviders = [
'HERE.normalDay',
'HERE.normalNight',
'HERE.hybridDay',
'HERE.terrainDay'
];

70
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts

@ -14,7 +14,12 @@
/// limitations under the License.
///
import { defaultSettings, hereProviders, MapProviders, UnitedMapSettings } from './map-models';
import {
defaultMapSettings,
MapProviders,
UnitedMapSettings,
WidgetUnitedMapSettings
} from './map-models';
import LeafletMap from './leaflet-map';
import {
commonMapSettingsSchema,
@ -96,7 +101,7 @@ export class MapWidgetController implements MapWidgetInterface {
provider: MapProviders;
schema: JsonSettingsSchema;
data: DatasourceData[];
settings: UnitedMapSettings;
settings: WidgetUnitedMapSettings;
pageLink: EntityDataPageLink;
public static dataKeySettingsSchema(): object {
@ -190,7 +195,7 @@ export class MapWidgetController implements MapWidgetInterface {
if (isDefined(lat) && isDefined(lng)) {
const point = lat != null && lng !== null ? L.latLng(lat, lng) : null;
markerValue = this.map.convertToCustomFormat(point);
} else if (this.settings.mapProvider !== MapProviders.image) {
} else if (this.settings.provider !== MapProviders.image) {
markerValue = {
[this.settings.latKeyName]: e[this.settings.latKeyName],
[this.settings.lngKeyName]: e[this.settings.lngKeyName],
@ -269,60 +274,55 @@ export class MapWidgetController implements MapWidgetInterface {
}
}
initSettings(settings: UnitedMapSettings, isEditMap?: boolean): UnitedMapSettings {
initSettings(settings: UnitedMapSettings, isEditMap?: boolean): WidgetUnitedMapSettings {
const functionParams = ['data', 'dsData', 'dsIndex'];
this.provider = settings.provider || this.mapProvider;
if (this.provider === MapProviders.here && !settings.mapProviderHere) {
if (settings.mapProvider && hereProviders.includes(settings.mapProvider)) {
settings.mapProviderHere = settings.mapProvider;
} else {
settings.mapProviderHere = hereProviders[0];
}
}
const customOptions: Partial<UnitedMapSettings> = {
const parsedOptions: Partial<WidgetUnitedMapSettings> = {
provider: this.provider,
mapUrl: settings?.mapImageUrl,
labelFunction: parseFunction(settings.labelFunction, functionParams),
tooltipFunction: parseFunction(settings.tooltipFunction, functionParams),
colorFunction: parseFunction(settings.colorFunction, functionParams),
colorPointFunction: parseFunction(settings.colorPointFunction, functionParams),
polygonLabelFunction: parseFunction(settings.polygonLabelFunction, functionParams),
polygonColorFunction: parseFunction(settings.polygonColorFunction, functionParams),
polygonStrokeColorFunction: parseFunction(settings.polygonStrokeColorFunction, functionParams),
polygonTooltipFunction: parseFunction(settings.polygonTooltipFunction, functionParams),
circleLabelFunction: parseFunction(settings.circleLabelFunction, functionParams),
circleStrokeColorFunction: parseFunction(settings.circleStrokeColorFunction, functionParams),
circleFillColorFunction: parseFunction(settings.circleFillColorFunction, functionParams),
circleTooltipFunction: parseFunction(settings.circleTooltipFunction, functionParams),
markerImageFunction: parseFunction(settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex']),
labelColor: this.ctx.widgetConfig.color,
polygonLabelColor: this.ctx.widgetConfig.color,
polygonKeyName: settings.polKeyName ? settings.polKeyName : settings.polygonKeyName,
parsedLabelFunction: parseFunction(settings.labelFunction, functionParams),
parsedTooltipFunction: parseFunction(settings.tooltipFunction, functionParams),
parsedColorFunction: parseFunction(settings.colorFunction, functionParams),
parsedColorPointFunction: parseFunction(settings.colorPointFunction, functionParams),
parsedStrokeOpacityFunction: parseFunction(settings.strokeOpacityFunction, functionParams),
parsedStrokeWeightFunction: parseFunction(settings.strokeWeightFunction, functionParams),
parsedPolygonLabelFunction: parseFunction(settings.polygonLabelFunction, functionParams),
parsedPolygonColorFunction: parseFunction(settings.polygonColorFunction, functionParams),
parsedPolygonStrokeColorFunction: parseFunction(settings.polygonStrokeColorFunction, functionParams),
parsedPolygonTooltipFunction: parseFunction(settings.polygonTooltipFunction, functionParams),
parsedCircleLabelFunction: parseFunction(settings.circleLabelFunction, functionParams),
parsedCircleStrokeColorFunction: parseFunction(settings.circleStrokeColorFunction, functionParams),
parsedCircleFillColorFunction: parseFunction(settings.circleFillColorFunction, functionParams),
parsedCircleTooltipFunction: parseFunction(settings.circleTooltipFunction, functionParams),
parsedMarkerImageFunction: parseFunction(settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex']),
// labelColor: this.ctx.widgetConfig.color,
// polygonLabelColor: this.ctx.widgetConfig.color,
polygonKeyName: (settings as any).polKeyName ? (settings as any).polKeyName : settings.polygonKeyName,
tooltipPattern: settings.tooltipPattern ||
'<b>${entityName}</b><br/><br/><b>Latitude:</b> ${' +
settings.latKeyName + ':7}<br/><b>Longitude:</b> ${' + settings.lngKeyName + ':7}',
defaultCenterPosition: getDefCenterPosition(settings?.defaultCenterPosition),
parsedDefaultCenterPosition: getDefCenterPosition(settings?.defaultCenterPosition),
currentImage: (settings.markerImage?.length) ? {
url: settings.markerImage,
size: settings.markerImageSize || 34
} : null
};
if (isEditMap && !settings.hasOwnProperty('draggableMarker')) {
settings.draggableMarker = true;
parsedOptions.draggableMarker = true;
}
if (isEditMap && !settings.hasOwnProperty('editablePolygon')) {
settings.editablePolygon = true;
parsedOptions.editablePolygon = true;
}
return { ...defaultSettings, ...settings, ...customOptions, };
parsedOptions.minZoomLevel = 16;
return { ...defaultMapSettings, ...settings, ...parsedOptions };
}
update() {
this.map.updateData(this.drawRoutes, this.settings.showPolygon);
this.map.updateData(this.drawRoutes);
this.map.setLoading(false);
}
latestDataUpdate() {
this.map.updateData(this.drawRoutes, this.settings.showPolygon);
this.map.updateData(this.drawRoutes);
}
resize() {

12
ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts

@ -15,20 +15,22 @@
///
import L from 'leaflet';
import { MarkerSettings, PolygonSettings, PolylineSettings } from './map-models';
import {
ShowTooltipAction, WidgetToolipSettings
} from './map-models';
import { Datasource } from '@app/shared/models/widget.models';
export function createTooltip(target: L.Layer,
settings: MarkerSettings | PolylineSettings | PolygonSettings,
settings: Partial<WidgetToolipSettings>,
datasource: Datasource,
autoClose = false,
showTooltipAction = 'click',
showTooltipAction = ShowTooltipAction.click,
content?: string | HTMLElement
): L.Popup {
const popup = L.popup();
popup.setContent(content);
target.bindPopup(popup, { autoClose, closeOnClick: false });
if (showTooltipAction === 'hover') {
if (showTooltipAction === ShowTooltipAction.hover) {
target.off('click');
target.on('mouseover', () => {
target.openPopup();
@ -47,7 +49,7 @@ export function createTooltip(target: L.Layer,
return popup;
}
export function bindPopupActions(popup: L.Popup, settings: MarkerSettings | PolylineSettings | PolygonSettings,
export function bindPopupActions(popup: L.Popup, settings: Partial<WidgetToolipSettings>,
datasource: Datasource) {
const actions = popup.getElement().getElementsByClassName('tb-custom-action');
Array.from(actions).forEach(

37
ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts

@ -18,9 +18,7 @@ import L, { LeafletMouseEvent } from 'leaflet';
import {
MarkerIconInfo,
MarkerIconReadyFunction,
MarkerImageInfo,
MarkerSettings,
UnitedMapSettings
MarkerImageInfo, WidgetMarkersSettings,
} from './map-models';
import { bindPopupActions, createTooltip } from './maps-utils';
import { aspectCache, parseWithTranslation } from './common-maps-utils';
@ -38,15 +36,17 @@ export class Marker {
tooltipOffset: L.LatLngTuple;
markerOffset: L.LatLngTuple;
tooltip: L.Popup;
data: FormattedData;
dataSources: FormattedData[];
constructor(private map: LeafletMap, private location: L.LatLng, public settings: UnitedMapSettings,
data?: FormattedData, dataSources?, onDragendListener?) {
this.setDataSources(data, dataSources);
constructor(private map: LeafletMap,
private location: L.LatLng,
private settings: Partial<WidgetMarkersSettings>,
private data?: FormattedData,
private dataSources?,
private onDragendListener?,
snappable = false) {
this.leafletMarker = L.marker(location, {
pmIgnore: !settings.draggableMarker,
snapIgnore: !settings.snappable
snapIgnore: !snappable
});
this.markerOffset = [
@ -59,7 +59,7 @@ export class Marker {
isDefined(settings.tooltipOffsetY) ? settings.tooltipOffsetY : -1,
];
this.updateMarkerIcon(settings);
this.updateMarkerIcon(this.settings);
if (settings.showTooltip) {
this.tooltip = createTooltip(this.leafletMarker, settings, data.$datasource,
@ -100,7 +100,7 @@ export class Marker {
updateMarkerTooltip(data: FormattedData) {
if (!this.map.markerTooltipText || this.settings.useTooltipFunction) {
const pattern = this.settings.useTooltipFunction ?
safeExecute(this.settings.tooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern;
safeExecute(this.settings.parsedTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern;
this.map.markerTooltipText = parseWithTranslation.prepareProcessPattern(pattern, true);
this.map.replaceInfoTooltipMarker = processDataPattern(this.map.markerTooltipText, data);
}
@ -117,17 +117,18 @@ export class Marker {
}
}
updateMarkerLabel(settings: MarkerSettings) {
updateMarkerLabel(settings: Partial<WidgetMarkersSettings>) {
this.leafletMarker.unbindTooltip();
if (settings.showLabel) {
if (!this.map.markerLabelText || settings.useLabelFunction) {
const pattern = settings.useLabelFunction ?
safeExecute(settings.labelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.label;
safeExecute(settings.parsedLabelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.label;
this.map.markerLabelText = parseWithTranslation.prepareProcessPattern(pattern, true);
this.map.replaceInfoLabelMarker = processDataPattern(this.map.markerLabelText, this.data);
}
settings.labelText = fillDataPattern(this.map.markerLabelText, this.map.replaceInfoLabelMarker, this.data);
this.leafletMarker.bindTooltip(`<div style="color: ${settings.labelColor};"><b>${settings.labelText}</b></div>`,
const labelText = fillDataPattern(this.map.markerLabelText, this.map.replaceInfoLabelMarker, this.data);
const labelColor = this.map.ctx.widgetConfig.color;
this.leafletMarker.bindTooltip(`<div style="color: ${labelColor};"><b>${labelText}</b></div>`,
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: this.labelOffset });
}
}
@ -138,7 +139,7 @@ export class Marker {
});
}
updateMarkerIcon(settings: MarkerSettings) {
updateMarkerIcon(settings: Partial<WidgetMarkersSettings>) {
this.createMarkerIcon((iconInfo) => {
this.leafletMarker.setIcon(iconInfo.icon);
const anchor = iconInfo.icon.options.iconAnchor;
@ -157,11 +158,11 @@ export class Marker {
return;
}
const currentImage: MarkerImageInfo = this.settings.useMarkerImageFunction ?
safeExecute(this.settings.markerImageFunction,
safeExecute(this.settings.parsedMarkerImageFunction,
[this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage;
let currentColor = this.settings.tinyColor;
if (this.settings.useColorFunction) {
const functionColor = safeExecute(this.settings.colorFunction,
const functionColor = safeExecute(this.settings.parsedColorFunction,
[this.data, this.dataSources, this.data.dsIndex]);
if (isDefinedAndNotNull(functionColor)) {
currentColor = tinycolor(functionColor);

47
ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts

@ -20,9 +20,10 @@ import {
functionValueCalculator,
parseWithTranslation
} from './common-maps-utils';
import { PolygonSettings, UnitedMapSettings } from './map-models';
import { WidgetPolygonSettings } from './map-models';
import { FormattedData } from '@shared/models/widget.models';
import { fillDataPattern, processDataPattern, safeExecute } from '@core/utils';
import LeafletMap from '@home/components/widget/lib/maps/leaflet-map';
export class Polygon {
@ -30,13 +31,13 @@ export class Polygon {
leafletPoly: L.Polygon;
tooltip: L.Popup;
data: FormattedData;
dataSources: FormattedData[];
constructor(public map, data: FormattedData, dataSources: FormattedData[], private settings: UnitedMapSettings,
private onDragendListener?) {
this.dataSources = dataSources;
this.data = data;
constructor(private map: LeafletMap,
private data: FormattedData,
private dataSources: FormattedData[],
private settings: Partial<WidgetPolygonSettings>,
private onDragendListener?,
snappable = false) {
const polygonColor = this.getPolygonColor(settings);
const polygonStrokeColor = this.getPolygonStrokeColor(settings);
const polyData = data[this.settings.polygonKeyName];
@ -49,8 +50,8 @@ export class Polygon {
fillOpacity: settings.polygonOpacity,
opacity: settings.polygonStrokeOpacity,
pmIgnore: !settings.editablePolygon,
snapIgnore: !settings.snappable
}).addTo(this.map);
snapIgnore: !snappable
}).addTo(this.map.map);
if (settings.showPolygonLabel) {
this.updateLabel(settings);
@ -91,28 +92,30 @@ export class Polygon {
updateTooltip(data: FormattedData) {
const pattern = this.settings.usePolygonTooltipFunction ?
safeExecute(this.settings.polygonTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) :
safeExecute(this.settings.parsedPolygonTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) :
this.settings.polygonTooltipPattern;
this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true));
}
updateLabel(settings: PolygonSettings) {
updateLabel(settings: Partial<WidgetPolygonSettings>) {
this.leafletPoly.unbindTooltip();
if (settings.showPolygonLabel) {
if (!this.map.polygonLabelText || settings.usePolygonLabelFunction) {
const pattern = settings.usePolygonLabelFunction ?
safeExecute(settings.polygonLabelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.polygonLabel;
safeExecute(settings.parsedPolygonLabelFunction,
[this.data, this.dataSources, this.data.dsIndex]) : settings.polygonLabel;
this.map.polygonLabelText = parseWithTranslation.prepareProcessPattern(pattern, true);
this.map.replaceInfoLabelPolygon = processDataPattern(this.map.polygonLabelText, this.data);
}
const polygonLabelText = fillDataPattern(this.map.polygonLabelText, this.map.replaceInfoLabelPolygon, this.data);
this.leafletPoly.bindTooltip(`<div style="color: ${settings.polygonLabelColor};"><b>${polygonLabelText}</b></div>`,
const labelColor = this.map.ctx.widgetConfig.color;
this.leafletPoly.bindTooltip(`<div style="color: ${labelColor};"><b>${polygonLabelText}</b></div>`,
{ className: 'tb-polygon-label', permanent: true, sticky: true, direction: 'center' })
.openTooltip(this.leafletPoly.getBounds().getCenter());
}
}
updatePolygon(data: FormattedData, dataSources: FormattedData[], settings: PolygonSettings) {
updatePolygon(data: FormattedData, dataSources: FormattedData[], settings: Partial<WidgetPolygonSettings>) {
if (this.editing) {
return;
}
@ -121,7 +124,7 @@ export class Polygon {
const polyData = data[this.settings.polygonKeyName];
if (isCutPolygon(polyData) || polyData.length !== 2) {
if (this.leafletPoly instanceof L.Rectangle) {
this.map.removeLayer(this.leafletPoly);
this.map.map.removeLayer(this.leafletPoly);
const polygonColor = this.getPolygonColor(settings);
const polygonStrokeColor = this.getPolygonStrokeColor(settings);
this.leafletPoly = L.polygon(polyData, {
@ -132,7 +135,7 @@ export class Polygon {
fillOpacity: settings.polygonOpacity,
opacity: settings.polygonStrokeOpacity,
pmIgnore: !settings.editablePolygon
}).addTo(this.map);
}).addTo(this.map.map);
if (settings.showPolygonTooltip) {
this.tooltip = createTooltip(this.leafletPoly, settings, data.$datasource,
settings.autoClosePolygonTooltip, settings.showPolygonTooltipAction);
@ -156,10 +159,10 @@ export class Polygon {
}
removePolygon() {
this.map.removeLayer(this.leafletPoly);
this.map.map.removeLayer(this.leafletPoly);
}
updatePolygonColor(settings: PolygonSettings) {
updatePolygonColor(settings: Partial<WidgetPolygonSettings>) {
const polygonColor = this.getPolygonColor(settings);
const polygonStrokeColor = this.getPolygonStrokeColor(settings);
const style: L.PathOptions = {
@ -178,13 +181,13 @@ export class Polygon {
this.leafletPoly.redraw();
}
private getPolygonColor(settings: PolygonSettings): string | null {
return functionValueCalculator(settings.usePolygonColorFunction, settings.polygonColorFunction,
private getPolygonColor(settings: Partial<WidgetPolygonSettings>): string | null {
return functionValueCalculator(settings.usePolygonColorFunction, settings.parsedPolygonColorFunction,
[this.data, this.dataSources, this.data.dsIndex], settings.polygonColor);
}
private getPolygonStrokeColor(settings: PolygonSettings): string | null {
return functionValueCalculator(settings.usePolygonStrokeColorFunction, settings.polygonStrokeColorFunction,
private getPolygonStrokeColor(settings: Partial<WidgetPolygonSettings>): string | null {
return functionValueCalculator(settings.usePolygonStrokeColorFunction, settings.parsedPolygonStrokeColorFunction,
[this.data, this.dataSources, this.data.dsIndex], settings.polygonStrokeColor);
}
}

24
ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts

@ -18,7 +18,7 @@
import L, { PolylineDecorator, PolylineDecoratorOptions, Symbol } from 'leaflet';
import 'leaflet-polylinedecorator';
import { PolylineSettings } from './map-models';
import { WidgetPolylineSettings } from './map-models';
import { functionValueCalculator } from '@home/components/widget/lib/maps/common-maps-utils';
import { FormattedData } from '@shared/models/widget.models';
@ -26,12 +26,12 @@ export class Polyline {
leafletPoly: L.Polyline;
polylineDecorator: PolylineDecorator;
dataSources: FormattedData[];
data: FormattedData;
constructor(private map: L.Map, locations: L.LatLng[], data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings) {
this.dataSources = dataSources;
this.data = data;
constructor(private map: L.Map,
locations: L.LatLng[],
private data: FormattedData,
private dataSources: FormattedData[],
settings: Partial<WidgetPolylineSettings>) {
this.leafletPoly = L.polyline(locations,
this.getPolyStyle(settings)
@ -42,7 +42,7 @@ export class Polyline {
}
}
getDecoratorSettings(settings: PolylineSettings): PolylineDecoratorOptions {
getDecoratorSettings(settings: Partial<WidgetPolylineSettings>): PolylineDecoratorOptions {
return {
patterns: [
{
@ -62,7 +62,7 @@ export class Polyline {
};
}
updatePolyline(locations: L.LatLng[], data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings) {
updatePolyline(locations: L.LatLng[], data: FormattedData, dataSources: FormattedData[], settings: Partial<WidgetPolylineSettings>) {
this.data = data;
this.dataSources = dataSources;
this.leafletPoly.setLatLngs(locations);
@ -72,14 +72,14 @@ export class Polyline {
}
}
getPolyStyle(settings: PolylineSettings): L.PolylineOptions {
getPolyStyle(settings: Partial<WidgetPolylineSettings>): L.PolylineOptions {
return {
interactive: false,
color: functionValueCalculator(settings.useColorFunction, settings.colorFunction,
color: functionValueCalculator(settings.useColorFunction, settings.parsedColorFunction,
[this.data, this.dataSources, this.data.dsIndex], settings.color),
opacity: functionValueCalculator(settings.useStrokeOpacityFunction, settings.strokeOpacityFunction,
opacity: functionValueCalculator(settings.useStrokeOpacityFunction, settings.parsedStrokeOpacityFunction,
[this.data, this.dataSources, this.data.dsIndex], settings.strokeOpacity),
weight: functionValueCalculator(settings.useStrokeWeightFunction, settings.strokeWeightFunction,
weight: functionValueCalculator(settings.useStrokeWeightFunction, settings.parsedStrokeWeightFunction,
[this.data, this.dataSources, this.data.dsIndex], settings.strokeWeight),
pmIgnore: true
};

7
ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts

@ -17,7 +17,7 @@
import L from 'leaflet';
import LeafletMap from '../leaflet-map';
import { DEFAULT_ZOOM_LEVEL, UnitedMapSettings } from '../map-models';
import { DEFAULT_ZOOM_LEVEL, WidgetUnitedMapSettings } from '../map-models';
import 'leaflet.gridlayer.googlemutant';
import { ResourcesService } from '@core/services/resources.service';
import { WidgetContext } from '@home/models/widget-component.models';
@ -31,16 +31,15 @@ interface GmGlobal {
export class GoogleMap extends LeafletMap {
private resource: ResourcesService;
constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) {
constructor(ctx: WidgetContext, $container, options: WidgetUnitedMapSettings) {
super(ctx, $container, options);
this.resource = ctx.$injector.get(ResourcesService);
super.initSettings(options);
this.loadGoogle(() => {
const map = L.map($container, {
attributionControl: false,
zoomControl: !this.options.disableZoomControl,
tap: L.Browser.safari && L.Browser.mobile
}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
}).setView(options?.parsedDefaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
(L.gridLayer as any).googleMutant({
type: options?.gmDefaultMapType || 'roadmap'
}).addTo(map);

7
ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts

@ -16,19 +16,18 @@
import L from 'leaflet';
import LeafletMap from '../leaflet-map';
import { DEFAULT_ZOOM_LEVEL, UnitedMapSettings } from '../map-models';
import { DEFAULT_ZOOM_LEVEL, WidgetUnitedMapSettings } from '../map-models';
import { WidgetContext } from '@home/models/widget-component.models';
export class HEREMap extends LeafletMap {
constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) {
constructor(ctx: WidgetContext, $container, options: WidgetUnitedMapSettings) {
super(ctx, $container, options);
const map = L.map($container, {
tap: L.Browser.safari && L.Browser.mobile,
zoomControl: !this.options.disableZoomControl
}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
}).setView(options?.parsedDefaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
const tileLayer = (L.tileLayer as any).provider(options.mapProviderHere || 'HERE.normalDay', options.credentials);
tileLayer.addTo(map);
super.initSettings(options);
super.setMap(map);
}
}

11
ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts

@ -16,7 +16,7 @@
import L, { LatLngBounds, LatLngLiteral, LatLngTuple } from 'leaflet';
import LeafletMap from '../leaflet-map';
import { CircleData, MapImage, PosFuncton, UnitedMapSettings } from '../map-models';
import { CircleData, MapImage, PosFuncton, WidgetUnitedMapSettings } from '../map-models';
import { Observable, ReplaySubject } from 'rxjs';
import { map, mergeMap } from 'rxjs/operators';
import {
@ -41,7 +41,7 @@ export class ImageMap extends LeafletMap {
imageUrl: string;
posFunction: PosFuncton;
constructor(ctx: WidgetContext, $container: HTMLElement, options: UnitedMapSettings) {
constructor(ctx: WidgetContext, $container: HTMLElement, options: WidgetUnitedMapSettings) {
super(ctx, $container, options);
this.posFunction = parseFunction(options.posFunction, ['origXPos', 'origYPos']) as PosFuncton;
this.mapImage(options).subscribe((mapImage) => {
@ -51,21 +51,20 @@ export class ImageMap extends LeafletMap {
this.onResize(true);
} else {
this.onResize();
super.initSettings(options);
super.setMap(this.map);
}
});
}
private mapImage(options: UnitedMapSettings): Observable<MapImage> {
private mapImage(options: WidgetUnitedMapSettings): Observable<MapImage> {
const imageEntityAlias = options.imageEntityAlias;
const imageUrlAttribute = options.imageUrlAttribute;
if (!imageEntityAlias || !imageUrlAttribute) {
return this.imageFromUrl(options.mapUrl);
return this.imageFromUrl(options.mapImageUrl);
}
const entityAliasId = this.ctx.aliasController.getEntityAliasId(imageEntityAlias);
if (!entityAliasId) {
return this.imageFromUrl(options.mapUrl);
return this.imageFromUrl(options.mapImageUrl);
}
const datasources = [
{

7
ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts

@ -16,16 +16,16 @@
import L from 'leaflet';
import LeafletMap from '../leaflet-map';
import { DEFAULT_ZOOM_LEVEL, UnitedMapSettings } from '../map-models';
import { DEFAULT_ZOOM_LEVEL, WidgetUnitedMapSettings } from '../map-models';
import { WidgetContext } from '@home/models/widget-component.models';
export class OpenStreetMap extends LeafletMap {
constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) {
constructor(ctx: WidgetContext, $container, options: WidgetUnitedMapSettings) {
super(ctx, $container, options);
const map = L.map($container, {
zoomControl: !this.options.disableZoomControl,
tap: L.Browser.safari && L.Browser.mobile
}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
}).setView(options?.parsedDefaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
let tileLayer;
if (options.useCustomProvider) {
tileLayer = L.tileLayer(options.customProviderTileUrl);
@ -33,7 +33,6 @@ export class OpenStreetMap extends LeafletMap {
tileLayer = (L.tileLayer as any).provider(options.mapProvider || 'OpenStreetMap.Mapnik');
}
tileLayer.addTo(map);
super.initSettings(options);
super.setMap(map);
}
}

7
ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts

@ -17,24 +17,23 @@
import L from 'leaflet';
import LeafletMap from '../leaflet-map';
import { DEFAULT_ZOOM_LEVEL, UnitedMapSettings } from '../map-models';
import { DEFAULT_ZOOM_LEVEL, WidgetUnitedMapSettings } from '../map-models';
import { WidgetContext } from '@home/models/widget-component.models';
export class TencentMap extends LeafletMap {
constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) {
constructor(ctx: WidgetContext, $container, options: WidgetUnitedMapSettings) {
super(ctx, $container, options);
const txUrl = 'http://rt{s}.map.gtimg.com/realtimerender?z={z}&x={x}&y={y}&type=vector&style=0';
const map = L.map($container, {
zoomControl: !this.options.disableZoomControl,
tap: L.Browser.safari && L.Browser.mobile
}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
}).setView(options?.parsedDefaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
const txLayer = L.tileLayer(txUrl, {
subdomains: '0123',
tms: true,
attribution: '&copy;2021 Tencent - GS(2020)2236号- Data&copy; NavInfo'
}).addTo(map);
txLayer.addTo(map);
super.initSettings(options);
super.setMap(map);
}
}

95
ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.html

@ -0,0 +1,95 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="alarmsTableKeySettingsForm" fxLayout="column">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.column-width</mat-label>
<input matInput formControlName="columnWidth">
</mat-form-field>
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.table.cell-style</legend>
<mat-expansion-panel class="tb-settings" [expanded]="alarmsTableKeySettingsForm.get('useCellStyleFunction').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="useCellStyleFunction" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.table.use-cell-style-function' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-js-func required
formControlName="cellStyleFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['value', 'alarm', 'ctx']"
functionTitle="{{ 'widgets.table.cell-style-function' | translate }}"
helpId="widget/lib/alarm/cell_style_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
</fieldset>
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.table.cell-content</legend>
<mat-expansion-panel class="tb-settings" [expanded]="alarmsTableKeySettingsForm.get('useCellContentFunction').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="useCellContentFunction" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.table.use-cell-content-function' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-js-func required
formControlName="cellContentFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['value', 'alarm', 'ctx']"
functionTitle="{{ 'widgets.table.cell-content-function' | translate }}"
helpId="widget/lib/alarm/cell_content_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
</fieldset>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.default-column-visibility</mat-label>
<mat-select formControlName="defaultColumnVisibility">
<mat-option [value]="'visible'">
{{ 'widgets.table.column-visibility-visible' | translate }}
</mat-option>
<mat-option [value]="'hidden'">
{{ 'widgets.table.column-visibility-hidden' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.column-selection-to-display</mat-label>
<mat-select formControlName="columnSelectionToDisplay">
<mat-option [value]="'enabled'">
{{ 'widgets.table.column-selection-to-display-enabled' | translate }}
</mat-option>
<mat-option [value]="'disabled'">
{{ 'widgets.table.column-selection-to-display-disabled' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</section>

86
ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.ts

@ -0,0 +1,86 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-alarms-table-key-settings',
templateUrl: './alarms-table-key-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class AlarmsTableKeySettingsComponent extends WidgetSettingsComponent {
alarmsTableKeySettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.alarmsTableKeySettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
columnWidth: '0px',
useCellStyleFunction: false,
cellStyleFunction: '',
useCellContentFunction: false,
cellContentFunction: '',
defaultColumnVisibility: 'visible',
columnSelectionToDisplay: 'enabled'
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.alarmsTableKeySettingsForm = this.fb.group({
columnWidth: [settings.columnWidth, []],
useCellStyleFunction: [settings.useCellStyleFunction, []],
cellStyleFunction: [settings.cellStyleFunction, [Validators.required]],
useCellContentFunction: [settings.useCellContentFunction, []],
cellContentFunction: [settings.cellContentFunction, [Validators.required]],
defaultColumnVisibility: [settings.defaultColumnVisibility, []],
columnSelectionToDisplay: [settings.columnSelectionToDisplay, []],
});
}
protected validatorTriggers(): string[] {
return ['useCellStyleFunction', 'useCellContentFunction'];
}
protected updateValidators(emitEvent: boolean) {
const useCellStyleFunction: boolean = this.alarmsTableKeySettingsForm.get('useCellStyleFunction').value;
const useCellContentFunction: boolean = this.alarmsTableKeySettingsForm.get('useCellContentFunction').value;
if (useCellStyleFunction) {
this.alarmsTableKeySettingsForm.get('cellStyleFunction').enable();
} else {
this.alarmsTableKeySettingsForm.get('cellStyleFunction').disable();
}
if (useCellContentFunction) {
this.alarmsTableKeySettingsForm.get('cellContentFunction').enable();
} else {
this.alarmsTableKeySettingsForm.get('cellContentFunction').disable();
}
this.alarmsTableKeySettingsForm.get('cellStyleFunction').updateValueAndValidity({emitEvent});
this.alarmsTableKeySettingsForm.get('cellContentFunction').updateValueAndValidity({emitEvent});
}
}

110
ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html

@ -0,0 +1,110 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="alarmsTableWidgetSettingsForm" fxLayout="column">
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.table.common-table-settings</legend>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.alarms-table-title</mat-label>
<input matInput formControlName="alarmsTitle">
</mat-form-field>
<section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px">
<section fxLayout="column" fxFlex>
<mat-checkbox formControlName="enableSelection" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-alarms-selection' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="enableSearch" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-alarms-search' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="enableSelectColumnDisplay" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-select-column-display' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="enableFilter" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-alarm-filter' | translate }}
</mat-checkbox>
</section>
<section fxLayout="column" fxFlex>
<mat-checkbox formControlName="enableStickyHeader" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-sticky-header' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="enableStickyAction" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-sticky-action' | translate }}
</mat-checkbox>
</section>
</section>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.hidden-cell-button-display-mode</mat-label>
<mat-select formControlName="reserveSpaceForHiddenAction">
<mat-option [value]="'true'">
{{ 'widgets.table.show-empty-space-hidden-action' | translate }}
</mat-option>
<mat-option [value]="'false'">
{{ 'widgets.table.dont-reserve-space-hidden-action' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<section fxLayout="column" fxLayoutGap="8px">
<mat-slide-toggle fxFlex formControlName="displayDetails">
{{ 'widgets.table.display-alarm-details' | translate }}
</mat-slide-toggle>
<mat-slide-toggle formControlName="allowAcknowledgment">
{{ 'widgets.table.allow-alarms-ack' | translate }}
</mat-slide-toggle>
<mat-slide-toggle formControlName="allowClear">
{{ 'widgets.table.allow-alarms-clear' | translate }}
</mat-slide-toggle>
<section fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<mat-slide-toggle fxFlex formControlName="displayPagination">
{{ 'widgets.table.display-pagination' | translate }}
</mat-slide-toggle>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.default-page-size</mat-label>
<input matInput type="number" min="1" step="1" formControlName="defaultPageSize">
</mat-form-field>
</section>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.default-sort-order</mat-label>
<input matInput formControlName="defaultSortOrder">
</mat-form-field>
</section>
</fieldset>
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.table.row-style</legend>
<mat-expansion-panel class="tb-settings" [expanded]="alarmsTableWidgetSettingsForm.get('useRowStyleFunction').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="useRowStyleFunction" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.table.use-row-style-function' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-js-func required
formControlName="rowStyleFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['alarm', 'ctx']"
functionTitle="{{ 'widgets.table.row-style-function' | translate }}"
helpId="widget/lib/alarm/row_style_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
</fieldset>
</section>

104
ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts

@ -0,0 +1,104 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-alarms-table-widget-settings',
templateUrl: './alarms-table-widget-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class AlarmsTableWidgetSettingsComponent extends WidgetSettingsComponent {
alarmsTableWidgetSettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.alarmsTableWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
alarmsTitle: '',
enableSelection: true,
enableSearch: true,
enableSelectColumnDisplay: true,
enableFilter: true,
enableStickyHeader: true,
enableStickyAction: true,
reserveSpaceForHiddenAction: 'true',
displayDetails: true,
allowAcknowledgment: true,
allowClear: true,
displayPagination: true,
defaultPageSize: 10,
defaultSortOrder: '-createdTime',
useRowStyleFunction: false,
rowStyleFunction: ''
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.alarmsTableWidgetSettingsForm = this.fb.group({
alarmsTitle: [settings.alarmsTitle, []],
enableSelection: [settings.enableSelection, []],
enableSearch: [settings.enableSearch, []],
enableSelectColumnDisplay: [settings.enableSelectColumnDisplay, []],
enableFilter: [settings.enableFilter, []],
enableStickyHeader: [settings.enableStickyHeader, []],
enableStickyAction: [settings.enableStickyAction, []],
reserveSpaceForHiddenAction: [settings.reserveSpaceForHiddenAction, []],
displayDetails: [settings.displayDetails, []],
allowAcknowledgment: [settings.allowAcknowledgment, []],
allowClear: [settings.allowClear, []],
displayPagination: [settings.displayPagination, []],
defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]],
defaultSortOrder: [settings.defaultSortOrder, []],
useRowStyleFunction: [settings.useRowStyleFunction, []],
rowStyleFunction: [settings.rowStyleFunction, [Validators.required]]
});
}
protected validatorTriggers(): string[] {
return ['useRowStyleFunction', 'displayPagination'];
}
protected updateValidators(emitEvent: boolean) {
const useRowStyleFunction: boolean = this.alarmsTableWidgetSettingsForm.get('useRowStyleFunction').value;
const displayPagination: boolean = this.alarmsTableWidgetSettingsForm.get('displayPagination').value;
if (useRowStyleFunction) {
this.alarmsTableWidgetSettingsForm.get('rowStyleFunction').enable();
} else {
this.alarmsTableWidgetSettingsForm.get('rowStyleFunction').disable();
}
if (displayPagination) {
this.alarmsTableWidgetSettingsForm.get('defaultPageSize').enable();
} else {
this.alarmsTableWidgetSettingsForm.get('defaultPageSize').disable();
}
this.alarmsTableWidgetSettingsForm.get('rowStyleFunction').updateValueAndValidity({emitEvent});
this.alarmsTableWidgetSettingsForm.get('defaultPageSize').updateValueAndValidity({emitEvent});
}
}

65
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/dashboard-state-widget-settings.component.html

@ -0,0 +1,65 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="dashboardStateWidgetSettingsForm" fxLayout="column">
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.dashboard-state.dashboard-state-settings</legend>
<mat-form-field class="mat-block">
<input matInput type="text" placeholder="{{ 'widgets.dashboard-state.dashboard-state' | translate }}"
#dashboardStateInput
formControlName="stateId"
[matAutocomplete]="dashboardStateAutocomplete">
<button *ngIf="dashboardStateWidgetSettingsForm.get('stateId').value"
type="button"
matSuffix mat-icon-button aria-label="Clear"
(click)="clearDashboardState()">
<mat-icon class="material-icons">close</mat-icon>
</button>
<mat-autocomplete
class="tb-autocomplete"
#dashboardStateAutocomplete="matAutocomplete">
<mat-option *ngFor="let state of filteredDashboardStates | async" [value]="state">
<span [innerHTML]="state | highlight:dashboardStateSearchText"></span>
</mat-option>
</mat-autocomplete>
</mat-form-field>
<mat-expansion-panel class="tb-settings">
<mat-expansion-panel-header>
<mat-panel-description fxLayoutAlign="end" translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<mat-slide-toggle class="mat-slide" formControlName="defaultAutofillLayout">
{{ 'widgets.dashboard-state.autofill-state-layout' | translate }}
</mat-slide-toggle>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.dashboard-state.default-margin</mat-label>
<input matInput type="number" min="0" step="1" formControlName="defaultMargin">
</mat-form-field>
<tb-color-input
formControlName="defaultBackgroundColor"
icon="format_color_fill"
label="{{ 'widgets.dashboard-state.default-background-color' | translate }}" openOnInput colorClearButton>
</tb-color-input>
<mat-slide-toggle class="mat-slide" formControlName="syncParentStateParams">
{{ 'widgets.dashboard-state.sync-parent-state-params' | translate }}
</mat-slide-toggle>
</ng-template>
</mat-expansion-panel>
</fieldset>
</section>

99
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/dashboard-state-widget-settings.component.ts

@ -0,0 +1,99 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, ElementRef, ViewChild } from '@angular/core';
import { WidgetActionType, WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Observable, of } from 'rxjs';
import { map, mergeMap, startWith } from 'rxjs/operators';
@Component({
selector: 'tb-dashboard-state-widget-settings',
templateUrl: './dashboard-state-widget-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class DashboardStateWidgetSettingsComponent extends WidgetSettingsComponent {
@ViewChild('dashboardStateInput') dashboardStateInput: ElementRef;
dashboardStateWidgetSettingsForm: FormGroup;
filteredDashboardStates: Observable<Array<string>>;
dashboardStateSearchText = '';
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.dashboardStateWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
stateId: '',
defaultAutofillLayout: true,
defaultMargin: 0,
defaultBackgroundColor: '#fff',
syncParentStateParams: true
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.dashboardStateWidgetSettingsForm = this.fb.group({
stateId: [settings.stateId, []],
defaultAutofillLayout: [settings.defaultAutofillLayout, []],
defaultMargin: [settings.defaultMargin, [Validators.min(0)]],
defaultBackgroundColor: [settings.defaultBackgroundColor, []],
syncParentStateParams: [settings.syncParentStateParams, []]
});
this.dashboardStateSearchText = '';
this.filteredDashboardStates = this.dashboardStateWidgetSettingsForm.get('stateId').valueChanges
.pipe(
startWith(''),
map(value => value ? value : ''),
mergeMap(name => this.fetchDashboardStates(name) )
);
}
public clearDashboardState(value: string = '') {
this.dashboardStateInput.nativeElement.value = value;
this.dashboardStateWidgetSettingsForm.get('stateId').patchValue(value, {emitEvent: true});
setTimeout(() => {
this.dashboardStateInput.nativeElement.blur();
this.dashboardStateInput.nativeElement.focus();
}, 0);
}
private fetchDashboardStates(searchText?: string): Observable<Array<string>> {
this.dashboardStateSearchText = searchText;
const stateIds = Object.keys(this.dashboard.configuration.states);
const result = searchText ? stateIds.filter(this.createFilterForDashboardState(searchText)) : stateIds;
if (result && result.length) {
return of(result);
} else {
return of([searchText]);
}
}
private createFilterForDashboardState(query: string): (stateId: string) => boolean {
const lowercaseQuery = query.toLowerCase();
return stateId => stateId.toLowerCase().indexOf(lowercaseQuery) === 0;
}
}

22
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/edge-quick-overview-widget-settings.component.html

@ -0,0 +1,22 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="edgeQuickOverviewWidgetSettingsForm" fxLayout="column">
<mat-slide-toggle class="mat-slide" formControlName="enableDefaultTitle">
{{ 'widgets.edge.display-default-title' | translate }}
</mat-slide-toggle>
</section>

52
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/edge-quick-overview-widget-settings.component.ts

@ -0,0 +1,52 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-edge-quick-overview-widget-settings',
templateUrl: './edge-quick-overview-widget-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class EdgeQuickOverviewWidgetSettingsComponent extends WidgetSettingsComponent {
edgeQuickOverviewWidgetSettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.edgeQuickOverviewWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
enableDefaultTitle: true
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.edgeQuickOverviewWidgetSettingsForm = this.fb.group({
enableDefaultTitle: [settings.enableDefaultTitle, []]
});
}
}

74
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-hierarchy-widget-settings.component.html

@ -0,0 +1,74 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="entitiesHierarchyWidgetSettingsForm" fxLayout="column">
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.entities-hierarchy.hierarchy-data-settings</legend>
<tb-js-func formControlName="nodeRelationQueryFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['nodeCtx']"
functionTitle="{{ 'widgets.entities-hierarchy.relations-query-function' | translate }}"
helpId="widget/lib/entities_hierarchy/node_relation_query_fn">
</tb-js-func>
<tb-js-func formControlName="nodeHasChildrenFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['nodeCtx']"
functionTitle="{{ 'widgets.entities-hierarchy.has-children-function' | translate }}"
helpId="widget/lib/entities_hierarchy/node_has_children_fn">
</tb-js-func>
</fieldset>
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.entities-hierarchy.node-state-settings</legend>
<tb-js-func formControlName="nodeOpenedFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['nodeCtx']"
functionTitle="{{ 'widgets.entities-hierarchy.node-opened-function' | translate }}"
helpId="widget/lib/entities_hierarchy/node_opened_fn">
</tb-js-func>
<tb-js-func formControlName="nodeDisabledFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['nodeCtx']"
functionTitle="{{ 'widgets.entities-hierarchy.node-disabled-function' | translate }}"
helpId="widget/lib/entities_hierarchy/node_disabled_fn">
</tb-js-func>
</fieldset>
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.entities-hierarchy.display-settings</legend>
<tb-js-func formControlName="nodeIconFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['nodeCtx']"
functionTitle="{{ 'widgets.entities-hierarchy.node-icon-function' | translate }}"
helpId="widget/lib/entities_hierarchy/node_icon_fn">
</tb-js-func>
<tb-js-func formControlName="nodeTextFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['nodeCtx']"
functionTitle="{{ 'widgets.entities-hierarchy.node-text-function' | translate }}"
helpId="widget/lib/entities_hierarchy/node_text_fn">
</tb-js-func>
</fieldset>
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.entities-hierarchy.sort-settings</legend>
<tb-js-func formControlName="nodesSortFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['nodeCtx1', 'nodeCtx2']"
functionTitle="{{ 'widgets.entities-hierarchy.nodes-sort-function' | translate }}"
helpId="widget/lib/entities_hierarchy/nodes_sort_fn">
</tb-js-func>
</fieldset>
</section>

64
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-hierarchy-widget-settings.component.ts

@ -0,0 +1,64 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-entities-hierarchy-widget-settings',
templateUrl: './entities-hierarchy-widget-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class EntitiesHierarchyWidgetSettingsComponent extends WidgetSettingsComponent {
entitiesHierarchyWidgetSettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.entitiesHierarchyWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
nodeRelationQueryFunction: '',
nodeHasChildrenFunction: '',
nodeOpenedFunction: '',
nodeDisabledFunction: '',
nodeIconFunction: '',
nodeTextFunction: '',
nodesSortFunction: '',
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.entitiesHierarchyWidgetSettingsForm = this.fb.group({
nodeRelationQueryFunction: [settings.nodeRelationQueryFunction, []],
nodeHasChildrenFunction: [settings.nodeHasChildrenFunction, []],
nodeOpenedFunction: [settings.nodeOpenedFunction, []],
nodeDisabledFunction: [settings.nodeDisabledFunction, []],
nodeIconFunction: [settings.nodeIconFunction, []],
nodeTextFunction: [settings.nodeTextFunction, []],
nodesSortFunction: [settings.nodesSortFunction, []]
});
}
}

95
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-key-settings.component.html

@ -0,0 +1,95 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="entitiesTableKeySettingsForm" fxLayout="column">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.column-width</mat-label>
<input matInput formControlName="columnWidth">
</mat-form-field>
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.table.cell-style</legend>
<mat-expansion-panel class="tb-settings" [expanded]="entitiesTableKeySettingsForm.get('useCellStyleFunction').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="useCellStyleFunction" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.table.use-cell-style-function' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-js-func required
formControlName="cellStyleFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['value', 'entity', 'ctx']"
functionTitle="{{ 'widgets.table.cell-style-function' | translate }}"
helpId="widget/lib/entity/cell_style_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
</fieldset>
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.table.cell-content</legend>
<mat-expansion-panel class="tb-settings" [expanded]="entitiesTableKeySettingsForm.get('useCellContentFunction').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="useCellContentFunction" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.table.use-cell-content-function' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-js-func required
formControlName="cellContentFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['value', 'entity', 'ctx']"
functionTitle="{{ 'widgets.table.cell-content-function' | translate }}"
helpId="widget/lib/entity/cell_content_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
</fieldset>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.default-column-visibility</mat-label>
<mat-select formControlName="defaultColumnVisibility">
<mat-option [value]="'visible'">
{{ 'widgets.table.column-visibility-visible' | translate }}
</mat-option>
<mat-option [value]="'hidden'">
{{ 'widgets.table.column-visibility-hidden' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.column-selection-to-display</mat-label>
<mat-select formControlName="columnSelectionToDisplay">
<mat-option [value]="'enabled'">
{{ 'widgets.table.column-selection-to-display-enabled' | translate }}
</mat-option>
<mat-option [value]="'disabled'">
{{ 'widgets.table.column-selection-to-display-disabled' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</section>

86
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-key-settings.component.ts

@ -0,0 +1,86 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-entities-table-key-settings',
templateUrl: './entities-table-key-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class EntitiesTableKeySettingsComponent extends WidgetSettingsComponent {
entitiesTableKeySettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.entitiesTableKeySettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
columnWidth: '0px',
useCellStyleFunction: false,
cellStyleFunction: '',
useCellContentFunction: false,
cellContentFunction: '',
defaultColumnVisibility: 'visible',
columnSelectionToDisplay: 'enabled'
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.entitiesTableKeySettingsForm = this.fb.group({
columnWidth: [settings.columnWidth, []],
useCellStyleFunction: [settings.useCellStyleFunction, []],
cellStyleFunction: [settings.cellStyleFunction, [Validators.required]],
useCellContentFunction: [settings.useCellContentFunction, []],
cellContentFunction: [settings.cellContentFunction, [Validators.required]],
defaultColumnVisibility: [settings.defaultColumnVisibility, []],
columnSelectionToDisplay: [settings.columnSelectionToDisplay, []],
});
}
protected validatorTriggers(): string[] {
return ['useCellStyleFunction', 'useCellContentFunction'];
}
protected updateValidators(emitEvent: boolean) {
const useCellStyleFunction: boolean = this.entitiesTableKeySettingsForm.get('useCellStyleFunction').value;
const useCellContentFunction: boolean = this.entitiesTableKeySettingsForm.get('useCellContentFunction').value;
if (useCellStyleFunction) {
this.entitiesTableKeySettingsForm.get('cellStyleFunction').enable();
} else {
this.entitiesTableKeySettingsForm.get('cellStyleFunction').disable();
}
if (useCellContentFunction) {
this.entitiesTableKeySettingsForm.get('cellContentFunction').enable();
} else {
this.entitiesTableKeySettingsForm.get('cellContentFunction').disable();
}
this.entitiesTableKeySettingsForm.get('cellStyleFunction').updateValueAndValidity({emitEvent});
this.entitiesTableKeySettingsForm.get('cellContentFunction').updateValueAndValidity({emitEvent});
}
}

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

@ -0,0 +1,116 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="entitiesTableWidgetSettingsForm" fxLayout="column">
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.table.common-table-settings</legend>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.entities-table-title</mat-label>
<input matInput formControlName="entitiesTitle">
</mat-form-field>
<section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px">
<section fxLayout="column" fxFlex>
<mat-checkbox formControlName="enableSearch" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-search' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="enableSelectColumnDisplay" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-select-column-display' | translate }}
</mat-checkbox>
</section>
<section fxLayout="column" fxFlex>
<mat-checkbox formControlName="enableStickyHeader" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-sticky-header' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="enableStickyAction" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-sticky-action' | translate }}
</mat-checkbox>
</section>
</section>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.hidden-cell-button-display-mode</mat-label>
<mat-select formControlName="reserveSpaceForHiddenAction">
<mat-option [value]="'true'">
{{ 'widgets.table.show-empty-space-hidden-action' | translate }}
</mat-option>
<mat-option [value]="'false'">
{{ 'widgets.table.dont-reserve-space-hidden-action' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<section fxLayout="column" fxLayoutGap="8px">
<section fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<mat-slide-toggle fxFlex formControlName="displayEntityName">
{{ 'widgets.table.display-entity-name' | translate }}
</mat-slide-toggle>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.entity-name-column-title</mat-label>
<input matInput formControlName="entityNameColumnTitle">
</mat-form-field>
</section>
<section fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<mat-slide-toggle fxFlex formControlName="displayEntityLabel">
{{ 'widgets.table.display-entity-label' | translate }}
</mat-slide-toggle>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.entity-label-column-title</mat-label>
<input matInput formControlName="entityLabelColumnTitle">
</mat-form-field>
</section>
<mat-slide-toggle formControlName="displayEntityType">
{{ 'widgets.table.display-entity-type' | translate }}
</mat-slide-toggle>
<section fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<mat-slide-toggle fxFlex formControlName="displayPagination">
{{ 'widgets.table.display-pagination' | translate }}
</mat-slide-toggle>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.default-page-size</mat-label>
<input matInput type="number" min="1" step="1" formControlName="defaultPageSize">
</mat-form-field>
</section>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.default-sort-order</mat-label>
<input matInput formControlName="defaultSortOrder">
</mat-form-field>
</section>
</fieldset>
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.table.row-style</legend>
<mat-expansion-panel class="tb-settings" [expanded]="entitiesTableWidgetSettingsForm.get('useRowStyleFunction').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="useRowStyleFunction" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.table.use-row-style-function' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-js-func required
formControlName="rowStyleFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['entity', 'ctx']"
functionTitle="{{ 'widgets.table.row-style-function' | translate }}"
helpId="widget/lib/entity/row_style_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
</fieldset>
</section>

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

@ -0,0 +1,118 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-entities-table-widget-settings',
templateUrl: './entities-table-widget-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class EntitiesTableWidgetSettingsComponent extends WidgetSettingsComponent {
entitiesTableWidgetSettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.entitiesTableWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
entitiesTitle: '',
enableSearch: true,
enableSelectColumnDisplay: true,
enableStickyHeader: true,
enableStickyAction: true,
reserveSpaceForHiddenAction: 'true',
displayEntityName: true,
entityNameColumnTitle: '',
displayEntityLabel: false,
entityLabelColumnTitle: '',
displayEntityType: true,
displayPagination: true,
defaultPageSize: 10,
defaultSortOrder: 'entityName',
useRowStyleFunction: false,
rowStyleFunction: ''
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.entitiesTableWidgetSettingsForm = this.fb.group({
entitiesTitle: [settings.entitiesTitle, []],
enableSearch: [settings.enableSearch, []],
enableSelectColumnDisplay: [settings.enableSelectColumnDisplay, []],
enableStickyHeader: [settings.enableStickyHeader, []],
enableStickyAction: [settings.enableStickyAction, []],
reserveSpaceForHiddenAction: [settings.reserveSpaceForHiddenAction, []],
displayEntityName: [settings.displayEntityName, []],
entityNameColumnTitle: [settings.entityNameColumnTitle, []],
displayEntityLabel: [settings.displayEntityLabel, []],
entityLabelColumnTitle: [settings.entityLabelColumnTitle, []],
displayEntityType: [settings.displayEntityType, []],
displayPagination: [settings.displayPagination, []],
defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]],
defaultSortOrder: [settings.defaultSortOrder, []],
useRowStyleFunction: [settings.useRowStyleFunction, []],
rowStyleFunction: [settings.rowStyleFunction, [Validators.required]]
});
}
protected validatorTriggers(): string[] {
return ['useRowStyleFunction', 'displayPagination', 'displayEntityName', 'displayEntityLabel'];
}
protected updateValidators(emitEvent: boolean) {
const useRowStyleFunction: boolean = this.entitiesTableWidgetSettingsForm.get('useRowStyleFunction').value;
const displayPagination: boolean = this.entitiesTableWidgetSettingsForm.get('displayPagination').value;
const displayEntityName: boolean = this.entitiesTableWidgetSettingsForm.get('displayEntityName').value;
const displayEntityLabel: boolean = this.entitiesTableWidgetSettingsForm.get('displayEntityLabel').value;
if (useRowStyleFunction) {
this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').enable();
} else {
this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').disable();
}
if (displayPagination) {
this.entitiesTableWidgetSettingsForm.get('defaultPageSize').enable();
} else {
this.entitiesTableWidgetSettingsForm.get('defaultPageSize').disable();
}
if (displayEntityName) {
this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').enable();
} else {
this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').disable();
}
if (displayEntityLabel) {
this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').enable();
} else {
this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').disable();
}
this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').updateValueAndValidity({emitEvent});
this.entitiesTableWidgetSettingsForm.get('defaultPageSize').updateValueAndValidity({emitEvent});
this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').updateValueAndValidity({emitEvent});
this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').updateValueAndValidity({emitEvent});
}
}

25
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/html-card-widget-settings.component.html

@ -0,0 +1,25 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section [formGroup]="htmlCardWidgetSettingsForm" fxLayout="column">
<tb-html formControlName="cardHtml" required
label="{{ 'widgets.html-card.html' | translate }}">
</tb-html>
<tb-css formControlName="cardCss"
label="{{ 'widgets.html-card.css' | translate }}">
</tb-css>
</section>

54
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/html-card-widget-settings.component.ts

@ -0,0 +1,54 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-html-card-widget-settings',
templateUrl: './html-card-widget-settings.component.html',
styleUrls: []
})
export class HtmlCardWidgetSettingsComponent extends WidgetSettingsComponent {
htmlCardWidgetSettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.htmlCardWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
cardHtml: '<div class=\'card\'>HTML code here</div>',
cardCss: '.card {\n font-weight: bold; \n}'
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.htmlCardWidgetSettingsForm = this.fb.group({
cardHtml: [settings.cardHtml, [Validators.required]],
cardCss: [settings.cardCss, []]
});
}
}

73
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.html

@ -0,0 +1,73 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<mat-expansion-panel class="label-widget-label" fxFlex [formGroup]="labelWidgetLabelFormGroup" [(expanded)]="expanded">
<mat-expansion-panel-header>
<div fxFlex fxLayout="row" fxLayoutAlign="start center">
<mat-panel-title>
<div fxLayout="row" fxFlex fxLayoutAlign="start center">
{{ labelWidgetLabelFormGroup.get('pattern').value }}
</div>
</mat-panel-title>
<span fxFlex></span>
<button *ngIf="!disabled" mat-icon-button style="min-width: 40px;"
type="button"
(click)="removeLabel.emit()"
matTooltip="{{ 'action.remove' | translate }}"
matTooltipPosition="above">
<mat-icon>delete</mat-icon>
</button>
</div>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div fxLayout="column" fxLayoutGap="0.5em">
<mat-divider></mat-divider>
<section class="tb-widget-settings" fxLayout="column">
<mat-form-field style="padding-bottom: 16px;">
<mat-label translate>widgets.label-widget.label-pattern</mat-label>
<input required matInput formControlName="pattern">
<mat-error *ngIf="labelWidgetLabelFormGroup.get('pattern').hasError('required')">
{{ 'widgets.label-widget.label-pattern-required' | translate }}
</mat-error>
<mat-hint [innerHTML]="'widgets.label-widget.label-pattern-hint' | translate | safe: 'html'"></mat-hint>
</mat-form-field>
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.label-widget.label-position</legend>
<section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px">
<mat-form-field fxFlex>
<mat-label translate>widgets.label-widget.x-pos</mat-label>
<input matInput type="number" min="0" max="100" formControlName="x">
</mat-form-field>
<mat-form-field fxFlex>
<mat-label translate>widgets.label-widget.y-pos</mat-label>
<input matInput type="number" min="0" max="100" formControlName="y">
</mat-form-field>
</section>
</fieldset>
<tb-color-input
formControlName="backgroundColor"
icon="format_color_fill"
label="{{ 'widgets.label-widget.background-color' | translate }}" openOnInput colorClearButton>
</tb-color-input>
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.label-widget.font-settings</legend>
<tb-widget-font formControlName="font" sizeTitle="widgets.widget-font.relative-font-size"></tb-widget-font>
</fieldset>
</section>
</div>
</ng-template>
</mat-expansion-panel>

40
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.scss

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
display: block;
.mat-expansion-panel {
box-shadow: none;
&.label-widget-label {
border: 1px groove rgba(0, 0, 0, .25);
.mat-expansion-panel-header {
padding: 0 24px 0 8px;
&.mat-expanded {
height: 48px;
}
}
}
}
}
:host ::ng-deep {
.mat-expansion-panel {
&.label-widget-label {
.mat-expansion-panel-body {
padding: 0 8px 8px;
}
}
}
}

113
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.ts

@ -0,0 +1,113 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { WidgetFont } from '@home/components/widget/lib/settings/common/widget-font.component';
export interface LabelWidgetLabel {
pattern: string;
x: number;
y: number;
backgroundColor: string;
font: WidgetFont;
}
@Component({
selector: 'tb-label-widget-label',
templateUrl: './label-widget-label.component.html',
styleUrls: ['./label-widget-label.component.scss', './../widget-settings.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => LabelWidgetLabelComponent),
multi: true
}
]
})
export class LabelWidgetLabelComponent extends PageComponent implements OnInit, ControlValueAccessor {
@Input()
disabled: boolean;
@Input()
expanded = false;
@Output()
removeLabel = new EventEmitter();
private modelValue: LabelWidgetLabel;
private propagateChange = null;
public labelWidgetLabelFormGroup: FormGroup;
constructor(protected store: Store<AppState>,
private translate: TranslateService,
private fb: FormBuilder) {
super(store);
}
ngOnInit(): void {
this.labelWidgetLabelFormGroup = this.fb.group({
pattern: [null, [Validators.required]],
x: [null, [Validators.min(0), Validators.max(100)]],
y: [null, [Validators.min(0), Validators.max(100)]],
backgroundColor: [null, []],
font: [null, []]
});
this.labelWidgetLabelFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (isDisabled) {
this.labelWidgetLabelFormGroup.disable({emitEvent: false});
} else {
this.labelWidgetLabelFormGroup.enable({emitEvent: false});
}
}
writeValue(value: LabelWidgetLabel): void {
this.modelValue = value;
this.labelWidgetLabelFormGroup.patchValue(
value, {emitEvent: false}
);
}
private updateModel() {
const value: LabelWidgetLabel = this.labelWidgetLabelFormGroup.value;
this.modelValue = value;
if (this.labelWidgetLabelFormGroup.valid) {
this.propagateChange(this.modelValue);
} else {
this.propagateChange(null);
}
}
}

50
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-settings.component.html

@ -0,0 +1,50 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="labelWidgetSettingsForm" fxLayout="column">
<tb-image-input required
label="{{ 'widgets.label-widget.background-image' | translate }}"
formControlName="backgroundImageUrl">
</tb-image-input>
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.label-widget.labels</legend>
<div fxLayout="column">
<div class="tb-control-list tb-drop-list" cdkDropList cdkDropListOrientation="vertical"
(cdkDropListDropped)="labelDrop($event)">
<div cdkDrag class="tb-draggable" *ngFor="let labelControl of labelsFormArray().controls; trackBy: trackByLabelControl;
let $index = index; last as isLast;"
fxLayout="column" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}">
<tb-label-widget-label [formControl]="labelControl"
[expanded]="labelControl.new"
(removeLabel)="removeLabel($index)">
</tb-label-widget-label>
</div>
</div>
<div *ngIf="!labelsFormArray().controls.length">
<span translate fxLayoutAlign="center center"
class="tb-prompt">widgets.label-widget.no-labels</span>
</div>
<div style="padding-top: 16px;">
<button mat-raised-button color="primary"
type="button"
(click)="addLabel()">
<span translate>widgets.label-widget.add-label</span>
</button>
</div>
</div>
</fieldset>
</section>

110
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-settings.component.ts

@ -0,0 +1,110 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { LabelWidgetLabel } from '@home/components/widget/lib/settings/cards/label-widget-label.component';
import { CdkDragDrop } from '@angular/cdk/drag-drop';
@Component({
selector: 'tb-label-widget-settings',
templateUrl: './label-widget-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class LabelWidgetSettingsComponent extends WidgetSettingsComponent {
labelWidgetSettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.labelWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
backgroundImageUrl: 'data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==',
labels: []
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.labelWidgetSettingsForm = this.fb.group({
backgroundImageUrl: [settings.backgroundImageUrl, [Validators.required]],
labels: this.prepareLabelsFormArray(settings.labels)
});
}
protected doUpdateSettings(settingsForm: FormGroup, settings: WidgetSettings) {
settingsForm.setControl('labels', this.prepareLabelsFormArray(settings.labels), {emitEvent: false});
}
private prepareLabelsFormArray(labels: LabelWidgetLabel[] | undefined): FormArray {
const labelsControls: Array<AbstractControl> = [];
if (labels) {
labels.forEach((label) => {
labelsControls.push(this.fb.control(label, [Validators.required]));
});
}
return this.fb.array(labelsControls);
}
labelsFormArray(): FormArray {
return this.labelWidgetSettingsForm.get('labels') as FormArray;
}
public trackByLabelControl(index: number, labelControl: AbstractControl): any {
return labelControl;
}
public removeLabel(index: number) {
(this.labelWidgetSettingsForm.get('labels') as FormArray).removeAt(index);
}
public addLabel() {
const label: LabelWidgetLabel = {
pattern: '${#0}',
x: 50,
y: 50,
backgroundColor: 'rgba(0,0,0,0)',
font: {
family: 'Roboto',
size: 6,
style: 'normal',
weight: '500',
color: '#fff'
}
};
const labelsArray = this.labelWidgetSettingsForm.get('labels') as FormArray;
const labelControl = this.fb.control(label, [Validators.required]);
(labelControl as any).new = true;
labelsArray.push(labelControl);
this.labelWidgetSettingsForm.updateValueAndValidity();
}
labelDrop(event: CdkDragDrop<string[]>) {
const labelsArray = this.labelWidgetSettingsForm.get('labels') as FormArray;
const label = labelsArray.at(event.previousIndex);
labelsArray.removeAt(event.previousIndex);
labelsArray.insert(event.currentIndex, label);
}
}

36
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/markdown-widget-settings.component.html

@ -0,0 +1,36 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="markdownWidgetSettingsForm" fxLayout="column">
<mat-slide-toggle class="mat-slide" formControlName="useMarkdownTextFunction">
{{ 'widgets.markdown.use-markdown-text-function' | translate }}
</mat-slide-toggle>
<tb-js-func [fxShow]="markdownWidgetSettingsForm.get('useMarkdownTextFunction').value"
formControlName="markdownTextFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['data']"
functionTitle="{{ 'widgets.markdown.markdown-text-function' | translate }}"
helpId="widget/lib/markdown/markdown_text_fn">
</tb-js-func>
<tb-markdown-editor [fxShow]="!markdownWidgetSettingsForm.get('useMarkdownTextFunction').value"
formControlName="markdownTextPattern"
label="{{ 'widgets.markdown.markdown-text-pattern' | translate }}">
</tb-markdown-editor>
<tb-css formControlName="markdownCss"
label="{{ 'widgets.markdown.markdown-css' | translate }}">
</tb-css>
</section>

76
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/markdown-widget-settings.component.ts

@ -0,0 +1,76 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-markdown-widget-settings',
templateUrl: './markdown-widget-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class MarkdownWidgetSettingsComponent extends WidgetSettingsComponent {
markdownWidgetSettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.markdownWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
useMarkdownTextFunction: false,
markdownTextPattern: '# Markdown/HTML card \\n - **Current entity**: **${entityName}**. \\n - **Current value**: **${Random}**.',
markdownTextFunction: 'return \'# Some title\\\\n - Entity name: \' + data[0][\'entityName\'];',
markdownCss: ''
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.markdownWidgetSettingsForm = this.fb.group({
useMarkdownTextFunction: [settings.useMarkdownTextFunction, []],
markdownTextPattern: [settings.markdownTextPattern, []],
markdownTextFunction: [settings.qrCodeTextFunction, []],
markdownCss: [settings.markdownCss, []]
});
}
protected validatorTriggers(): string[] {
return ['useMarkdownTextFunction'];
}
protected updateValidators(emitEvent: boolean) {
const useMarkdownTextFunction: boolean = this.markdownWidgetSettingsForm.get('useMarkdownTextFunction').value;
if (useMarkdownTextFunction) {
this.markdownWidgetSettingsForm.get('markdownTextPattern').disable();
this.markdownWidgetSettingsForm.get('markdownTextFunction').enable();
} else {
this.markdownWidgetSettingsForm.get('markdownTextPattern').enable();
this.markdownWidgetSettingsForm.get('markdownTextFunction').disable();
}
this.markdownWidgetSettingsForm.get('markdownTextPattern').updateValueAndValidity({emitEvent});
this.markdownWidgetSettingsForm.get('markdownTextFunction').updateValueAndValidity({emitEvent});
}
}

37
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/qrcode-widget-settings.component.html

@ -0,0 +1,37 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="qrCodeWidgetSettingsForm" fxLayout="column">
<mat-slide-toggle class="mat-slide" formControlName="useQrCodeTextFunction">
{{ 'widgets.qr-code.use-qr-code-text-function' | translate }}
</mat-slide-toggle>
<mat-form-field [fxShow]="!qrCodeWidgetSettingsForm.get('useQrCodeTextFunction').value">
<mat-label translate>widgets.qr-code.qr-code-text-pattern</mat-label>
<input required matInput formControlName="qrCodeTextPattern">
<mat-error *ngIf="qrCodeWidgetSettingsForm.get('qrCodeTextPattern').hasError('required')">
{{ 'widgets.qr-code.qr-code-text-pattern-required' | translate }}
</mat-error>
</mat-form-field>
<tb-js-func required
[fxShow]="qrCodeWidgetSettingsForm.get('useQrCodeTextFunction').value"
formControlName="qrCodeTextFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['data']"
functionTitle="{{ 'widgets.qr-code.qr-code-text-function' | translate }}"
helpId="widget/lib/qrcode/qrcode_text_fn">
</tb-js-func>
</section>

74
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/qrcode-widget-settings.component.ts

@ -0,0 +1,74 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-qrcode-widget-settings',
templateUrl: './qrcode-widget-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class QrCodeWidgetSettingsComponent extends WidgetSettingsComponent {
qrCodeWidgetSettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.qrCodeWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
qrCodeTextPattern: '${entityName}',
useQrCodeTextFunction: false,
qrCodeTextFunction: 'return data[0][\'entityName\'];'
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.qrCodeWidgetSettingsForm = this.fb.group({
qrCodeTextPattern: [settings.qrCodeTextPattern, [Validators.required]],
useQrCodeTextFunction: [settings.useQrCodeTextFunction, [Validators.required]],
qrCodeTextFunction: [settings.qrCodeTextFunction, [Validators.required]]
});
}
protected validatorTriggers(): string[] {
return ['useQrCodeTextFunction'];
}
protected updateValidators(emitEvent: boolean) {
const useQrCodeTextFunction: boolean = this.qrCodeWidgetSettingsForm.get('useQrCodeTextFunction').value;
if (useQrCodeTextFunction) {
this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').disable();
this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').enable();
} else {
this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').enable();
this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').disable();
}
this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').updateValueAndValidity({emitEvent});
this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').updateValueAndValidity({emitEvent});
}
}

30
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/simple-card-widget-settings.component.html

@ -0,0 +1,30 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section [formGroup]="simpleCardWidgetSettingsForm" fxLayout="column">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.simple-card.label-position</mat-label>
<mat-select formControlName="labelPosition">
<mat-option [value]="'left'">
{{ 'widgets.simple-card.label-position-left' | translate }}
</mat-option>
<mat-option [value]="'top'">
{{ 'widgets.simple-card.label-position-top' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</section>

52
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/simple-card-widget-settings.component.ts

@ -0,0 +1,52 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-simple-card-widget-settings',
templateUrl: './simple-card-widget-settings.component.html',
styleUrls: []
})
export class SimpleCardWidgetSettingsComponent extends WidgetSettingsComponent {
simpleCardWidgetSettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.simpleCardWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
labelPosition: 'left'
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.simpleCardWidgetSettingsForm = this.fb.group({
labelPosition: [settings.labelPosition, []]
});
}
}

69
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.html

@ -0,0 +1,69 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="timeseriesTableKeySettingsForm" fxLayout="column">
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.table.cell-style</legend>
<mat-expansion-panel class="tb-settings" [expanded]="timeseriesTableKeySettingsForm.get('useCellStyleFunction').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="useCellStyleFunction" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.table.use-cell-style-function' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-js-func required
formControlName="cellStyleFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['value', 'rowData', 'ctx']"
functionTitle="{{ 'widgets.table.cell-style-function' | translate }}"
helpId="widget/lib/timeseries/cell_style_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
</fieldset>
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.table.cell-content</legend>
<mat-expansion-panel class="tb-settings" [expanded]="timeseriesTableKeySettingsForm.get('useCellContentFunction').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="useCellContentFunction" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.table.use-cell-content-function' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-js-func required
formControlName="cellContentFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['value', 'rowData', 'ctx']"
functionTitle="{{ 'widgets.table.cell-content-function' | translate }}"
helpId="widget/lib/timeseries/cell_content_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
</fieldset>
</section>

80
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.ts

@ -0,0 +1,80 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-timeseries-table-key-settings',
templateUrl: './timeseries-table-key-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class TimeseriesTableKeySettingsComponent extends WidgetSettingsComponent {
timeseriesTableKeySettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.timeseriesTableKeySettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
useCellStyleFunction: false,
cellStyleFunction: '',
useCellContentFunction: false,
cellContentFunction: ''
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.timeseriesTableKeySettingsForm = this.fb.group({
useCellStyleFunction: [settings.useCellStyleFunction, []],
cellStyleFunction: [settings.cellStyleFunction, [Validators.required]],
useCellContentFunction: [settings.useCellContentFunction, []],
cellContentFunction: [settings.cellContentFunction, [Validators.required]],
});
}
protected validatorTriggers(): string[] {
return ['useCellStyleFunction', 'useCellContentFunction'];
}
protected updateValidators(emitEvent: boolean) {
const useCellStyleFunction: boolean = this.timeseriesTableKeySettingsForm.get('useCellStyleFunction').value;
const useCellContentFunction: boolean = this.timeseriesTableKeySettingsForm.get('useCellContentFunction').value;
if (useCellStyleFunction) {
this.timeseriesTableKeySettingsForm.get('cellStyleFunction').enable();
} else {
this.timeseriesTableKeySettingsForm.get('cellStyleFunction').disable();
}
if (useCellContentFunction) {
this.timeseriesTableKeySettingsForm.get('cellContentFunction').enable();
} else {
this.timeseriesTableKeySettingsForm.get('cellContentFunction').disable();
}
this.timeseriesTableKeySettingsForm.get('cellStyleFunction').updateValueAndValidity({emitEvent});
this.timeseriesTableKeySettingsForm.get('cellContentFunction').updateValueAndValidity({emitEvent});
}
}

76
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.html

@ -0,0 +1,76 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="timeseriesTableLatestKeySettingsForm" fxLayout="column">
<mat-slide-toggle class="mat-slide" formControlName="show">
{{ 'widgets.table.show-latest-data-column' | translate }}
</mat-slide-toggle>
<mat-form-field [fxShow]="timeseriesTableLatestKeySettingsForm.get('show').value" fxFlex class="mat-block">
<mat-label translate>widgets.table.latest-data-column-order</mat-label>
<input matInput type="number" step="1" formControlName="order">
</mat-form-field>
<fieldset [fxShow]="timeseriesTableLatestKeySettingsForm.get('show').value" class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.table.cell-style</legend>
<mat-expansion-panel class="tb-settings" [expanded]="timeseriesTableLatestKeySettingsForm.get('useCellStyleFunction').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="useCellStyleFunction" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.table.use-cell-style-function' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-js-func required
formControlName="cellStyleFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['value', 'rowData', 'ctx']"
functionTitle="{{ 'widgets.table.cell-style-function' | translate }}"
helpId="widget/lib/timeseries/cell_style_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
</fieldset>
<fieldset [fxShow]="timeseriesTableLatestKeySettingsForm.get('show').value" class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.table.cell-content</legend>
<mat-expansion-panel class="tb-settings" [expanded]="timeseriesTableLatestKeySettingsForm.get('useCellContentFunction').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="useCellContentFunction" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.table.use-cell-content-function' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-js-func required
formControlName="cellContentFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['value', 'rowData', 'ctx']"
functionTitle="{{ 'widgets.table.cell-content-function' | translate }}"
helpId="widget/lib/timeseries/cell_content_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
</fieldset>
</section>

96
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.ts

@ -0,0 +1,96 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-timeseries-table-latest-key-settings',
templateUrl: './timeseries-table-latest-key-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class TimeseriesTableLatestKeySettingsComponent extends WidgetSettingsComponent {
timeseriesTableLatestKeySettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.timeseriesTableLatestKeySettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
show: true,
useCellStyleFunction: false,
cellStyleFunction: '',
useCellContentFunction: false,
cellContentFunction: ''
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.timeseriesTableLatestKeySettingsForm = this.fb.group({
show: [settings.show, []],
order: [settings.order, []],
useCellStyleFunction: [settings.useCellStyleFunction, []],
cellStyleFunction: [settings.cellStyleFunction, [Validators.required]],
useCellContentFunction: [settings.useCellContentFunction, []],
cellContentFunction: [settings.cellContentFunction, [Validators.required]],
});
}
protected validatorTriggers(): string[] {
return ['show', 'useCellStyleFunction', 'useCellContentFunction'];
}
protected updateValidators(emitEvent: boolean) {
const show: boolean = this.timeseriesTableLatestKeySettingsForm.get('show').value;
if (show) {
this.timeseriesTableLatestKeySettingsForm.get('order').enable();
this.timeseriesTableLatestKeySettingsForm.get('useCellStyleFunction').enable({emitEvent: false});
this.timeseriesTableLatestKeySettingsForm.get('useCellContentFunction').enable({emitEvent: false});
const useCellStyleFunction: boolean = this.timeseriesTableLatestKeySettingsForm.get('useCellStyleFunction').value;
const useCellContentFunction: boolean = this.timeseriesTableLatestKeySettingsForm.get('useCellContentFunction').value;
if (useCellStyleFunction) {
this.timeseriesTableLatestKeySettingsForm.get('cellStyleFunction').enable();
} else {
this.timeseriesTableLatestKeySettingsForm.get('cellStyleFunction').disable();
}
if (useCellContentFunction) {
this.timeseriesTableLatestKeySettingsForm.get('cellContentFunction').enable();
} else {
this.timeseriesTableLatestKeySettingsForm.get('cellContentFunction').disable();
}
} else {
this.timeseriesTableLatestKeySettingsForm.get('order').disable();
this.timeseriesTableLatestKeySettingsForm.get('useCellStyleFunction').disable({emitEvent: false});
this.timeseriesTableLatestKeySettingsForm.get('cellStyleFunction').disable();
this.timeseriesTableLatestKeySettingsForm.get('useCellContentFunction').disable({emitEvent: false});
this.timeseriesTableLatestKeySettingsForm.get('cellContentFunction').disable();
}
this.timeseriesTableLatestKeySettingsForm.get('order').updateValueAndValidity({emitEvent});
this.timeseriesTableLatestKeySettingsForm.get('cellStyleFunction').updateValueAndValidity({emitEvent});
this.timeseriesTableLatestKeySettingsForm.get('cellContentFunction').updateValueAndValidity({emitEvent});
}
}

96
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html

@ -0,0 +1,96 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="timeseriesTableWidgetSettingsForm" fxLayout="column">
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.table.common-table-settings</legend>
<section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px">
<section fxLayout="column" fxFlex>
<mat-checkbox formControlName="enableSearch" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-search' | translate }}
</mat-checkbox>
</section>
<section fxLayout="column" fxFlex>
<mat-checkbox formControlName="enableStickyHeader" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-sticky-header' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="enableStickyAction" style="margin-bottom: 16px;">
{{ 'widgets.table.enable-sticky-action' | translate }}
</mat-checkbox>
</section>
</section>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.hidden-cell-button-display-mode</mat-label>
<mat-select formControlName="reserveSpaceForHiddenAction">
<mat-option [value]="'true'">
{{ 'widgets.table.show-empty-space-hidden-action' | translate }}
</mat-option>
<mat-option [value]="'false'">
{{ 'widgets.table.dont-reserve-space-hidden-action' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<section fxLayout="column" fxLayoutGap="8px">
<mat-slide-toggle formControlName="showTimestamp">
{{ 'widgets.table.display-timestamp' | translate }}
</mat-slide-toggle>
<mat-slide-toggle formControlName="showMilliseconds">
{{ 'widgets.table.display-milliseconds' | translate }}
</mat-slide-toggle>
<section fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<mat-slide-toggle fxFlex formControlName="displayPagination">
{{ 'widgets.table.display-pagination' | translate }}
</mat-slide-toggle>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.default-page-size</mat-label>
<input matInput type="number" min="1" step="1" formControlName="defaultPageSize">
</mat-form-field>
</section>
<mat-slide-toggle formControlName="useEntityLabel">
{{ 'widgets.table.use-entity-label-tab-name' | translate }}
</mat-slide-toggle>
<mat-slide-toggle formControlName="hideEmptyLines">
{{ 'widgets.table.hide-empty-lines' | translate }}
</mat-slide-toggle>
</section>
</fieldset>
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.table.row-style</legend>
<mat-expansion-panel class="tb-settings" [expanded]="timeseriesTableWidgetSettingsForm.get('useRowStyleFunction').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="useRowStyleFunction" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.table.use-row-style-function' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-js-func required
formControlName="rowStyleFunction"
[globalVariables]="functionScopeVariables"
[functionArgs]="['rowData', 'ctx']"
functionTitle="{{ 'widgets.table.row-style-function' | translate }}"
helpId="widget/lib/timeseries/row_style_fn">
</tb-js-func>
</ng-template>
</mat-expansion-panel>
</fieldset>
</section>

98
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts

@ -0,0 +1,98 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-timeseries-table-widget-settings',
templateUrl: './timeseries-table-widget-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsComponent {
timeseriesTableWidgetSettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.timeseriesTableWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
enableSearch: true,
enableStickyHeader: true,
enableStickyAction: true,
reserveSpaceForHiddenAction: 'true',
showTimestamp: true,
showMilliseconds: false,
displayPagination: true,
useEntityLabel: false,
defaultPageSize: 10,
hideEmptyLines: false,
disableStickyHeader: false,
useRowStyleFunction: false,
rowStyleFunction: ''
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.timeseriesTableWidgetSettingsForm = this.fb.group({
enableSearch: [settings.enableSearch, []],
enableStickyHeader: [settings.enableStickyHeader, []],
enableStickyAction: [settings.enableStickyAction, []],
reserveSpaceForHiddenAction: [settings.reserveSpaceForHiddenAction, []],
showTimestamp: [settings.showTimestamp, []],
showMilliseconds: [settings.showMilliseconds, []],
displayPagination: [settings.displayPagination, []],
useEntityLabel: [settings.useEntityLabel, []],
defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]],
hideEmptyLines: [settings.hideEmptyLines, []],
disableStickyHeader: [settings.disableStickyHeader, []],
useRowStyleFunction: [settings.useRowStyleFunction, []],
rowStyleFunction: [settings.rowStyleFunction, [Validators.required]]
});
}
protected validatorTriggers(): string[] {
return ['useRowStyleFunction', 'displayPagination'];
}
protected updateValidators(emitEvent: boolean) {
const useRowStyleFunction: boolean = this.timeseriesTableWidgetSettingsForm.get('useRowStyleFunction').value;
const displayPagination: boolean = this.timeseriesTableWidgetSettingsForm.get('displayPagination').value;
if (useRowStyleFunction) {
this.timeseriesTableWidgetSettingsForm.get('rowStyleFunction').enable();
} else {
this.timeseriesTableWidgetSettingsForm.get('rowStyleFunction').disable();
}
if (displayPagination) {
this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').enable();
} else {
this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').disable();
}
this.timeseriesTableWidgetSettingsForm.get('rowStyleFunction').updateValueAndValidity({emitEvent});
this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').updateValueAndValidity({emitEvent});
}
}

25
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/chart-widget-settings.component.html

@ -0,0 +1,25 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="chartWidgetSettingsForm" fxLayout="column">
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.chart.common-settings</legend>
<mat-slide-toggle formControlName="showTooltip" class="slide-block">
{{ 'widgets.chart.show-tooltip' | translate }}
</mat-slide-toggle>
</fieldset>
</section>

52
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/chart-widget-settings.component.ts

@ -0,0 +1,52 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-chart-widget-settings',
templateUrl: './chart-widget-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class ChartWidgetSettingsComponent extends WidgetSettingsComponent {
chartWidgetSettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.chartWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
showTooltip: true
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.chartWidgetSettingsForm = this.fb.group({
showTooltip: [settings.showTooltip, []]
});
}
}

64
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-chart-widget-settings.component.html

@ -0,0 +1,64 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section class="tb-widget-settings" [formGroup]="doughnutChartWidgetSettingsForm" fxLayout="column">
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.chart.common-settings</legend>
<mat-slide-toggle formControlName="showTooltip" class="slide-block">
{{ 'widgets.chart.show-tooltip' | translate }}
</mat-slide-toggle>
</fieldset>
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.chart.border-settings</legend>
<section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.chart.border-width</mat-label>
<input matInput type="number" min="0" formControlName="borderWidth">
</mat-form-field>
<tb-color-input fxFlex
formControlName="borderColor"
icon="format_color_fill"
label="{{ 'widgets.chart.border-color' | translate }}" openOnInput colorClearButton>
</tb-color-input>
</section>
</fieldset>
<fieldset class="fields-group fields-group-slider" formGroupName="legend">
<legend class="group-title" translate>widgets.chart.legend-settings</legend>
<mat-expansion-panel class="tb-settings" [expanded]="doughnutChartWidgetSettingsForm.get('legend.display').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="display" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widgets.chart.display-legend' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<section fxLayout="column">
<tb-color-input fxFlex
formControlName="labelsFontColor"
icon="format_color_fill"
label="{{ 'widgets.chart.labels-font-color' | translate }}" openOnInput colorClearButton>
</tb-color-input>
</section>
</ng-template>
</mat-expansion-panel>
</fieldset>
</section>

87
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-chart-widget-settings.component.ts

@ -0,0 +1,87 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Component({
selector: 'tb-doughnut-chart-widget-settings',
templateUrl: './doughnut-chart-widget-settings.component.html',
styleUrls: ['./../widget-settings.scss']
})
export class DoughnutChartWidgetSettingsComponent extends WidgetSettingsComponent {
doughnutChartWidgetSettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.doughnutChartWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {
showTooltip: true,
borderWidth: 5,
borderColor: '#fff',
legend: {
display: true,
labelsFontColor: '#666'
}
};
}
protected onSettingsSet(settings: WidgetSettings) {
this.doughnutChartWidgetSettingsForm = this.fb.group({
// Common settings
showTooltip: [settings.showTooltip, []],
// Border settings
borderWidth: [settings.borderWidth, [Validators.min(0)]],
borderColor: [settings.borderColor, []],
// Legend settings
legend: this.fb.group({
display: [settings.legend?.display, []],
labelsFontColor: [settings.legend?.labelsFontColor, []]
})
});
}
protected validatorTriggers(): string[] {
return ['legend.display'];
}
protected updateValidators(emitEvent: boolean) {
const displayLegend: boolean = this.doughnutChartWidgetSettingsForm.get('legend.display').value;
if (displayLegend) {
this.doughnutChartWidgetSettingsForm.get('legend.labelsFontColor').enable();
} else {
this.doughnutChartWidgetSettingsForm.get('legend.labelsFontColor').disable();
}
this.doughnutChartWidgetSettingsForm.get('legend.labelsFontColor').updateValueAndValidity({emitEvent});
}
}

24
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-key-settings.component.html

@ -0,0 +1,24 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section [formGroup]="flotBarKeySettingsForm" fxLayout="column">
<tb-flot-key-settings formControlName="flotKeySettings"
[aliasController]="aliasController"
[chartType]="'bar'">
</tb-flot-key-settings>
</section>

61
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-key-settings.component.ts

@ -0,0 +1,61 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { flotDataKeyDefaultSettings } from '@home/components/widget/lib/settings/chart/flot-key-settings.component';
@Component({
selector: 'tb-flot-bar-key-settings',
templateUrl: './flot-bar-key-settings.component.html',
styleUrls: []
})
export class FlotBarKeySettingsComponent extends WidgetSettingsComponent {
flotBarKeySettingsForm: FormGroup;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
protected settingsForm(): FormGroup {
return this.flotBarKeySettingsForm;
}
protected defaultSettings(): WidgetSettings {
return flotDataKeyDefaultSettings('bar');
}
protected onSettingsSet(settings: WidgetSettings) {
this.flotBarKeySettingsForm = this.fb.group({
flotKeySettings: [settings.flotKeySettings, []]
});
}
protected prepareInputSettings(settings: WidgetSettings): WidgetSettings {
return {
flotKeySettings: settings
};
}
protected prepareOutputSettings(settings: any): WidgetSettings {
return settings.flotKeySettings;
}
}

23
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-bar-widget-settings.component.html

@ -0,0 +1,23 @@
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section [formGroup]="flotBarWidgetSettingsForm" fxLayout="column">
<tb-flot-widget-settings formControlName="flotSettings"
[chartType]="'bar'">
</tb-flot-widget-settings>
</section>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save