diff --git a/application/src/main/data/json/system/widget_types/digital_horizontal_bar.json b/application/src/main/data/json/system/widget_types/digital_horizontal_bar.json index deb82422c6..70e470e273 100644 --- a/application/src/main/data/json/system/widget_types/digital_horizontal_bar.json +++ b/application/src/main/data/json/system/widget_types/digital_horizontal_bar.json @@ -15,7 +15,9 @@ "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\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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\":{},\"configMode\":\"basic\"}" }, "externalId": null, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/digital_speedometer.json b/application/src/main/data/json/system/widget_types/digital_speedometer.json index a3844fc560..4ac6ac7c1d 100644 --- a/application/src/main/data/json/system/widget_types/digital_speedometer.json +++ b/application/src/main/data/json/system/widget_types/digital_speedometer.json @@ -15,7 +15,9 @@ "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\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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\":{},\"configMode\":\"basic\"}" }, "externalId": null, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/digital_thermometer.json b/application/src/main/data/json/system/widget_types/digital_thermometer.json index ab75474f9c..df7d63ea1d 100644 --- a/application/src/main/data/json/system/widget_types/digital_thermometer.json +++ b/application/src/main/data/json/system/widget_types/digital_thermometer.json @@ -15,7 +15,9 @@ "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\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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\":{},\"configMode\":\"basic\"}" }, "externalId": null, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/digital_vertical_bar.json b/application/src/main/data/json/system/widget_types/digital_vertical_bar.json index aecd305300..7daf1f3b9c 100644 --- a/application/src/main/data/json/system/widget_types/digital_vertical_bar.json +++ b/application/src/main/data/json/system/widget_types/digital_vertical_bar.json @@ -15,7 +15,9 @@ "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\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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\":{},\"configMode\":\"basic\"}" }, "externalId": null, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/gauge.json b/application/src/main/data/json/system/widget_types/gauge.json index f5494f8b4a..3739623ccf 100644 --- a/application/src/main/data/json/system/widget_types/gauge.json +++ b/application/src/main/data/json/system/widget_types/gauge.json @@ -15,7 +15,9 @@ "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}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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},\"configMode\":\"basic\"}" }, "externalId": null, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/horizontal_bar.json b/application/src/main/data/json/system/widget_types/horizontal_bar.json index 27ec8d2367..08abe291df 100644 --- a/application/src/main/data/json/system/widget_types/horizontal_bar.json +++ b/application/src/main/data/json/system/widget_types/horizontal_bar.json @@ -15,7 +15,9 @@ "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}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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},\"configMode\":\"basic\"}" }, "externalId": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/lcd_bar_gauge.json b/application/src/main/data/json/system/widget_types/lcd_bar_gauge.json index b2c8caa4c3..bab8886b0b 100644 --- a/application/src/main/data/json/system/widget_types/lcd_bar_gauge.json +++ b/application/src/main/data/json/system/widget_types/lcd_bar_gauge.json @@ -15,7 +15,9 @@ "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}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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},\"configMode\":\"basic\"}" }, "externalId": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/lcd_gauge.json b/application/src/main/data/json/system/widget_types/lcd_gauge.json index bfe7a64a5f..175dc089f4 100644 --- a/application/src/main/data/json/system/widget_types/lcd_gauge.json +++ b/application/src/main/data/json/system/widget_types/lcd_gauge.json @@ -15,7 +15,9 @@ "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}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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},\"configMode\":\"basic\"}" }, "externalId": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/mini_gauge.json b/application/src/main/data/json/system/widget_types/mini_gauge.json index 2789102c6c..3d1b01fa58 100644 --- a/application/src/main/data/json/system/widget_types/mini_gauge.json +++ b/application/src/main/data/json/system/widget_types/mini_gauge.json @@ -15,7 +15,9 @@ "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}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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},\"configMode\":\"basic\"}" }, "externalId": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/neon_gauge.json b/application/src/main/data/json/system/widget_types/neon_gauge.json index 4f595392b6..a4f03169cf 100644 --- a/application/src/main/data/json/system/widget_types/neon_gauge.json +++ b/application/src/main/data/json/system/widget_types/neon_gauge.json @@ -15,7 +15,9 @@ "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\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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\":{},\"configMode\":\"basic\"}" }, "externalId": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_gauge.json b/application/src/main/data/json/system/widget_types/simple_gauge.json index 1683efee14..c9e451039c 100644 --- a/application/src/main/data/json/system/widget_types/simple_gauge.json +++ b/application/src/main/data/json/system/widget_types/simple_gauge.json @@ -15,7 +15,9 @@ "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}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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},\"configMode\":\"basic\"}" }, "externalId": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_neon_gauge.json b/application/src/main/data/json/system/widget_types/simple_neon_gauge.json index fd84396430..ab46ed93e6 100644 --- a/application/src/main/data/json/system/widget_types/simple_neon_gauge.json +++ b/application/src/main/data/json/system/widget_types/simple_neon_gauge.json @@ -15,7 +15,9 @@ "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\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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\":{},\"configMode\":\"basic\"}" }, "externalId": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/vertical_bar.json b/application/src/main/data/json/system/widget_types/vertical_bar.json index 31d3b3fdd9..84fdb8c1c8 100644 --- a/application/src/main/data/json/system/widget_types/vertical_bar.json +++ b/application/src/main/data/json/system/widget_types/vertical_bar.json @@ -15,7 +15,9 @@ "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}}" + "hasBasicMode": true, + "basicModeDirective": "tb-digital-simple-gauge-basic-config", + "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},\"configMode\":\"basic\"}" }, "externalId": null } \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts index 18b80f86a5..e75174291a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts @@ -129,6 +129,9 @@ import { import { RadarChartBasicConfigComponent } from '@home/components/widget/config/basic/chart/radar-chart-basic-config.component'; +import { + DigitalSimpleGaugeBasicConfigComponent +} from '@home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component'; @NgModule({ declarations: [ @@ -171,7 +174,8 @@ import { PieChartBasicConfigComponent, BarChartBasicConfigComponent, PolarAreaChartBasicConfigComponent, - RadarChartBasicConfigComponent + RadarChartBasicConfigComponent, + DigitalSimpleGaugeBasicConfigComponent ], imports: [ CommonModule, @@ -216,7 +220,8 @@ import { PieChartBasicConfigComponent, BarChartBasicConfigComponent, PolarAreaChartBasicConfigComponent, - RadarChartBasicConfigComponent + RadarChartBasicConfigComponent, + DigitalSimpleGaugeBasicConfigComponent ] }) export class BasicWidgetConfigModule { @@ -255,5 +260,6 @@ export const basicWidgetConfigComponentsMap: {[key: string]: Type + + + + + +
+
widgets.gauge.gauge-appearance
+ + + {{ digitalGaugeLayoutTranslationMap.get(layout) | translate }} + + + +
+
{{ 'widgets.gauge.donut-start-angle' | translate }}
+ + + +
+ +
+ + {{ 'widgets.gauge.min-and-max-value' | translate }} + +
+
widgets.gauge.min-value-short
+ + + +
widgets.gauge.max-value-short
+ + + + + + + +
+
+ +
+ + {{ 'widgets.gauge.value' | translate }} + +
+ + + + +
widget-config.decimals-suffix
+
+ + + + +
+
+ +
+ + {{ 'widgets.gauge.label' | translate }} + +
+ + + + + + + +
+
+ +
+
widgets.gauge.default-color
+ + +
+ +
+
widgets.gauge.gauge-bar-background
+ + +
+ +
+
{{ 'widgets.gauge.bar-color' | translate }}
+ + +
+ +
+
+
widget-config.card-appearance
+
+
{{ 'widget-config.background-color' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts new file mode 100644 index 0000000000..b344e4b044 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts @@ -0,0 +1,276 @@ +/// +/// Copyright © 2016-2024 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 { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { + Datasource, + datasourcesHasAggregation, + datasourcesHasOnlyComparisonAggregation, + WidgetConfig, +} from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; +import { formatValue, isUndefined } from '@core/utils'; +import { Component } from '@angular/core'; +import { + backwardCompatibilityFixedLevelColors, + defaultDigitalSimpleGaugeOptions, + digitalGaugeLayoutImages, + digitalGaugeLayouts, + digitalGaugeLayoutTranslations, + DigitalGaugeSettings, + DigitalGaugeType +} from '@home/components/widget/lib/digital-gauge.models'; +import { ColorSettings, ColorType, constantColor } from '@shared/models/widget-settings.models'; + +@Component({ + selector: 'tb-digital-simple-gauge-basic-config', + templateUrl: './digital-simple-gauge-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) + +export class DigitalSimpleGaugeBasicConfigComponent extends BasicWidgetConfigComponent { + + public get displayTimewindowConfig(): boolean { + const datasources = this.simpleGaugeWidgetConfigForm.get('datasources').value; + return datasourcesHasAggregation(datasources); + } + + public onlyHistoryTimewindow(): boolean { + const datasources = this.simpleGaugeWidgetConfigForm.get('datasources').value; + return datasourcesHasOnlyComparisonAggregation(datasources); + } + + public get datasource(): Datasource { + const datasources: Datasource[] = this.widgetConfig.config.datasources; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + digitalGaugeType = DigitalGaugeType; + digitalGaugeLayouts = digitalGaugeLayouts; + + digitalGaugeLayoutTranslationMap = digitalGaugeLayoutTranslations; + digitalGaugeLayoutImageMap = digitalGaugeLayoutImages; + + simpleGaugeWidgetConfigForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this, true); + previewFn = this._valuePreviewFn.bind(this, false); + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + protected fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.simpleGaugeWidgetConfigForm; + } + + protected setupDefaults(configData: WidgetConfigComponentData) { + super.setupDefaults(configData); + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: DigitalGaugeSettings = {...defaultDigitalSimpleGaugeOptions, ...(configData.config.settings || {})}; + + if (!settings.barColor) { + settings.barColor = constantColor(settings.gaugeColor); + if (settings.fixedLevelColors?.length) { + settings.barColor.rangeList = { + advancedMode: settings.useFixedLevelColor, + range: null, + rangeAdvanced: backwardCompatibilityFixedLevelColors(settings.fixedLevelColors) + }; + } + if (settings.levelColors?.length) { + settings.barColor.gradient = { + advancedMode: false, + gradient: settings.levelColors as string[], + gradientAdvanced: null + }; + } + if (settings.useFixedLevelColor) { + settings.barColor.type = ColorType.range; + } else if (settings.levelColors.length) { + settings.barColor.type = ColorType.gradient; + } + } + + this.simpleGaugeWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + + gaugeType: [settings.gaugeType, []], + donutStartAngle: [settings.donutStartAngle, []], + + showMinMax: [settings.showMinMax, []], + minValue: [settings.minValue, []], + maxValue: [settings.maxValue, []], + minMaxFont: [settings.minMaxFont, []], + minMaxColor: [settings.minMaxFont?.color, []], + + showValue: [settings.showValue, []], + decimals: [configData.config.decimals, []], + units: [configData.config.units, []], + valueFont: [settings.valueFont, []], + valueColor: [settings.valueFont?.color, []], + + showTitle: [settings.showTitle, []], + title: [settings.title, []], + titleFont: [settings.titleFont, []], + titleColor: [settings.titleFont?.color, []], + + defaultColor: [settings.defaultColor, []], + gaugeColor: [settings.gaugeColor, []], + barColor: [settings.barColor, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + backgroundColor: [configData.config.backgroundColor, []], + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + this.widgetConfig.config.actions = config.actions; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + this.widgetConfig.config.backgroundColor = config.backgroundColor; + + this.widgetConfig.config.settings.gaugeType = config.gaugeType; + this.widgetConfig.config.settings.donutStartAngle = config.donutStartAngle; + + this.widgetConfig.config.settings.showMinMax = config.showMinMax; + this.widgetConfig.config.settings.minValue = config.minValue; + this.widgetConfig.config.settings.maxValue = config.maxValue; + this.widgetConfig.config.settings.minMaxFont = config.minMaxFont; + this.widgetConfig.config.settings.minMaxFont.color = config.minMaxColor; + + this.widgetConfig.config.settings.showValue = config.showValue; + this.widgetConfig.config.units = config.units; + this.widgetConfig.config.decimals = config.decimals; + this.widgetConfig.config.settings.valueFont = config.valueFont; + this.widgetConfig.config.settings.valueFont.color = config.valueColor; + + this.widgetConfig.config.settings.showTitle = config.showTitle; + this.widgetConfig.config.settings.title = config.title; + this.widgetConfig.config.settings.titleFont = config.titleFont; + this.widgetConfig.config.settings.titleFont.color = config.titleColor; + + this.widgetConfig.config.settings.defaultColor = config.defaultColor; + this.widgetConfig.config.settings.gaugeColor = config.gaugeColor; + this.widgetConfig.config.settings.barColor = config.barColor; + const barColor: ColorSettings = config.barColor; + + if (barColor.type === ColorType.range) { + this.widgetConfig.config.settings.useFixedLevelColor = true; + this.widgetConfig.config.settings.fixedLevelColors = + barColor.rangeList.advancedMode ? barColor.rangeList.rangeAdvanced : barColor.rangeList.range; + } else { + this.widgetConfig.config.settings.useFixedLevelColor = false; + } + if (barColor.gradient?.gradient?.length) { + this.widgetConfig.config.settings.levelColors = barColor.gradient.gradient; + } + + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['gaugeType', 'showValue', 'showTitle', 'showMinMax']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const isDonut = this.simpleGaugeWidgetConfigForm.get('gaugeType').value === this.digitalGaugeType.donut; + + if (isDonut) { + this.simpleGaugeWidgetConfigForm.get('donutStartAngle').enable(); + this.simpleGaugeWidgetConfigForm.get('showMinMax').disable({emitEvent: false}); + this.simpleGaugeWidgetConfigForm.get('minValue').enable(); + this.simpleGaugeWidgetConfigForm.get('maxValue').enable(); + this.simpleGaugeWidgetConfigForm.get('minMaxFont').disable(); + this.simpleGaugeWidgetConfigForm.get('minMaxColor').disable(); + } else { + this.simpleGaugeWidgetConfigForm.get('donutStartAngle').disable(); + this.simpleGaugeWidgetConfigForm.get('showMinMax').enable({emitEvent: false}); + if (this.simpleGaugeWidgetConfigForm.get('showMinMax').value) { + this.simpleGaugeWidgetConfigForm.get('minValue').enable(); + this.simpleGaugeWidgetConfigForm.get('maxValue').enable(); + this.simpleGaugeWidgetConfigForm.get('minMaxFont').enable(); + this.simpleGaugeWidgetConfigForm.get('minMaxColor').enable(); + } else { + this.simpleGaugeWidgetConfigForm.get('minValue').disable(); + this.simpleGaugeWidgetConfigForm.get('maxValue').disable(); + this.simpleGaugeWidgetConfigForm.get('minMaxFont').disable(); + this.simpleGaugeWidgetConfigForm.get('minMaxColor').disable(); + } + } + + if (this.simpleGaugeWidgetConfigForm.get('showValue').value) { + this.simpleGaugeWidgetConfigForm.get('decimals').enable(); + this.simpleGaugeWidgetConfigForm.get('units').enable(); + this.simpleGaugeWidgetConfigForm.get('valueFont').enable(); + this.simpleGaugeWidgetConfigForm.get('valueColor').enable(); + } else { + this.simpleGaugeWidgetConfigForm.get('decimals').disable(); + this.simpleGaugeWidgetConfigForm.get('units').disable(); + this.simpleGaugeWidgetConfigForm.get('valueFont').disable(); + this.simpleGaugeWidgetConfigForm.get('valueColor').disable(); + } + + if (this.simpleGaugeWidgetConfigForm.get('showTitle').value) { + this.simpleGaugeWidgetConfigForm.get('title').enable(); + this.simpleGaugeWidgetConfigForm.get('titleFont').enable(); + this.simpleGaugeWidgetConfigForm.get('titleColor').enable(); + } else { + this.simpleGaugeWidgetConfigForm.get('title').disable(); + this.simpleGaugeWidgetConfigForm.get('titleFont').disable(); + this.simpleGaugeWidgetConfigForm.get('titleColor').disable(); + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + + private _valuePreviewFn(units: boolean): string { + return formatValue(22, 0, units ? this.simpleGaugeWidgetConfigForm.get('units').value : null, true); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.html index 3cbc1d4a6c..1c377029bb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.html @@ -99,18 +99,40 @@ [autoScale]="batteryLevelWidgetConfigForm.get('autoScaleValueSize').value" [previewText]="valuePreviewFn"> - +
{{ 'widgets.battery-level.battery-level-color' | translate }}
- +
{{ 'widgets.battery-level.battery-shape-color' | translate }}
- +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.ts index 8244fc85db..c9a5bf176a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/battery-level-basic-config.component.ts @@ -22,6 +22,7 @@ import { BasicWidgetConfigComponent } from '@home/components/widget/config/widge import { WidgetConfigComponentData } from '@home/models/widget-component.models'; import { DataKey, + Datasource, datasourcesHasAggregation, datasourcesHasOnlyComparisonAggregation, WidgetConfig, @@ -74,6 +75,15 @@ export class BatteryLevelBasicConfigComponent extends BasicWidgetConfigComponent return [BatteryLevelLayout.vertical_divided, BatteryLevelLayout.horizontal_divided].includes(layout); } + public get datasource(): Datasource { + const datasources: Datasource[] = this.widgetConfig.config.datasources; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.models.ts index 79976f45cf..ceea08384a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.models.ts @@ -101,10 +101,12 @@ export const countDefaultSettings = (alarmElseEntity: boolean): CountWidgetSetti ? { color: 'rgba(0, 105, 92, 1)', type: ColorType.range, - rangeList: [ - {from: 0, to: 0, color: 'rgba(0, 105, 92, 1)'}, - {from: 1, color: 'rgba(209, 39, 48, 1)'} - ], + rangeList: { + range: [ + {from: 0, to: 0, color: 'rgba(0, 105, 92, 1)'}, + {from: 1, color: 'rgba(209, 39, 48, 1)'} + ] + }, colorFunction: defaultColorFunction } : constantColor('rgba(241, 141, 23, 1)'), diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts index 26a3373cfa..c2c59046fe 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts @@ -14,10 +14,17 @@ /// limitations under the License. /// -import { JsonSettingsSchema } from '@shared/models/widget.models'; import { GaugeType } from '@home/components/widget/lib/canvas-digital-gauge'; import { AnimationRule } from '@home/components/widget/lib/analogue-gauge.models'; import { FontSettings } from '@home/components/widget/lib/settings.models'; +import { + AdvancedColorRange, + ColorSettings, + ValueSourceDataKeyType, + ValueSourceWithDataKey +} from '@shared/models/widget-settings.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { isDefinedAndNotNull } from '@core/utils'; export interface AttributeSourceProperty { valueSource: string; @@ -27,8 +34,8 @@ export interface AttributeSourceProperty { } export interface FixedLevelColors { - from?: AttributeSourceProperty; - to?: AttributeSourceProperty; + from?: ValueSourceWithDataKey | number; + to?: ValueSourceWithDataKey | number; color: string; } @@ -41,6 +48,33 @@ export type colorLevel = Array; export type attributesGaugeType = 'levelColors' | 'ticks'; +export enum DigitalGaugeType { + arc = 'arc', + donut = 'donut', + horizontalBar = 'horizontalBar', + verticalBar = 'verticalBar' +} + +export const digitalGaugeLayouts = Object.keys(DigitalGaugeType) as DigitalGaugeType[]; + +export const digitalGaugeLayoutTranslations = new Map( + [ + [DigitalGaugeType.arc, 'widgets.gauge.gauge-type-arc'], + [DigitalGaugeType.donut, 'widgets.gauge.gauge-type-donut'], + [DigitalGaugeType.horizontalBar, 'widgets.gauge.gauge-type-horizontal-bar'], + [DigitalGaugeType.verticalBar, 'widgets.gauge.gauge-type-vertical-bar'] + ] +); + +export const digitalGaugeLayoutImages = new Map( + [ + [DigitalGaugeType.arc, 'assets/widget/simple-gauge/arc-layout.svg'], + [DigitalGaugeType.donut, 'assets/widget/simple-gauge/donut-layout.svg'], + [DigitalGaugeType.horizontalBar, 'assets/widget/simple-gauge/horizontal-bar-layout.svg'], + [DigitalGaugeType.verticalBar, 'assets/widget/simple-gauge/vertical-bar-layout.svg'] + ] +); + export interface DigitalGaugeSettings { minValue?: number; maxValue?: number; @@ -60,6 +94,9 @@ export interface DigitalGaugeSettings { gaugeWidthScale?: number; defaultColor?: string; gaugeColor?: string; + + barColor?: ColorSettings; + useFixedLevelColor?: boolean; levelColors?: colorLevel; fixedLevelColors?: FixedLevelColors[]; @@ -75,8 +112,51 @@ export interface DigitalGaugeSettings { hideValue?: boolean; hideMinMax?: boolean; showTicks?: boolean; - ticksValue?: AttributeSourceProperty[]; + ticksValue?: ValueSourceWithDataKey[]; ticks?: number[]; colorTicks?: string; tickWidth?: number; } + +export const defaultDigitalSimpleGaugeOptions: DigitalGaugeSettings = { + gaugeType: DigitalGaugeType.donut, + timestampFormat: 'yyyy-MM-dd HH:mm:ss', +}; + +export const backwardCompatibilityFixedLevelColors = (fixedLevelColors) => { + const valueSourceWithDataKey: AdvancedColorRange[] = []; + fixedLevelColors.forEach(fixedLevelColor => valueSourceWithDataKey.push({ + from: { + type: fixedLevelColor?.from?.valueSource === 'predefinedValue' ? ValueSourceDataKeyType.constant : ValueSourceDataKeyType.entity, + value: fixedLevelColor?.from?.value || null, + entityAlias: fixedLevelColor?.from?.entityAlias || '', + entityKey: fixedLevelColor?.from?.attribute || '', + entityKeyType: DataKeyType.attribute + }, + to: { + type: fixedLevelColor?.to?.valueSource === 'predefinedValue' ? ValueSourceDataKeyType.constant : ValueSourceDataKeyType.entity, + value: fixedLevelColor?.to?.value || null, + entityAlias: fixedLevelColor?.to?.entityAlias || '', + entityKey: fixedLevelColor?.to?.attribute || '', + entityKeyType: DataKeyType.attribute + }, + color: fixedLevelColor.color + }) ); + return valueSourceWithDataKey; +}; + +export const backwardCompatibilityTicks = (ticksValue) => { + const ticks: ValueSourceWithDataKey[] = []; + if (ticksValue?.length && isDefinedAndNotNull(ticksValue[0]?.valueSource)) { + ticksValue.forEach(tick => ticks.push({ + type: tick?.valueSource === 'predefinedValue' ? ValueSourceDataKeyType.constant : ValueSourceDataKeyType.entity, + value: tick?.value || null, + entityAlias: tick?.entityAlias || '', + entityKey: tick?.attribute || '', + entityKeyType: DataKeyType.attribute + }) ); + } else { + return ticksValue; + } + return ticks; +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts index fce1b27697..1958a22cc4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts @@ -18,27 +18,25 @@ import * as CanvasGauges from 'canvas-gauges'; import { WidgetContext } from '@home/models/widget-component.models'; import { attributesGaugeType, - AttributeSourceProperty, ColorLevelSetting, DigitalGaugeSettings, FixedLevelColors } from '@home/components/widget/lib/digital-gauge.models'; import tinycolor from 'tinycolor2'; -import { isDefined, isDefinedAndNotNull } from '@core/utils'; +import { isDefined, isDefinedAndNotNull, parseFunction, safeExecute } from '@core/utils'; import { prepareFontSettings } from '@home/components/widget/lib/settings.models'; import { CanvasDigitalGauge, CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; import { DatePipe } from '@angular/common'; -import { - DataKey, - Datasource, - DatasourceData, - DatasourceType, - JsonSettingsSchema, - widgetType -} from '@shared/models/widget.models'; +import { DataKey, Datasource, DatasourceData, DatasourceType, widgetType } from '@shared/models/widget.models'; import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { EMPTY, Observable } from 'rxjs'; +import { + ColorProcessor, + ColorType, + ValueSourceDataKeyType, + ValueSourceWithDataKey +} from '@shared/models/widget-settings.models'; import GenericOptions = CanvasGauges.GenericOptions; // @dynamic @@ -70,6 +68,7 @@ export class TbCanvasDigitalGauge { this.localSettings.gaugeWidthScale = settings.gaugeWidthScale || 0.75; this.localSettings.gaugeColor = settings.gaugeColor || tinycolor(keyColor).setAlpha(0.2).toRgbString(); + this.localSettings.barColor = settings.barColor; this.localSettings.useFixedLevelColor = settings.useFixedLevelColor || false; if (!settings.useFixedLevelColor) { @@ -217,9 +216,9 @@ export class TbCanvasDigitalGauge { throw new Error('Not valid entity aliase name ' + entityAlias); } - const datasource = datasources.find((datasourceIteration) => { - return datasourceIteration.entityAliasId === entityAliasId; - }); + const datasource = datasources.find((datasourceIteration) => + datasourceIteration.entityAliasId === entityAliasId + ); const dataKey: DataKey = { type: DataKeyType.attribute, @@ -230,9 +229,9 @@ export class TbCanvasDigitalGauge { }; if (datasource) { - const findDataKey = datasource.dataKeys.find((dataKeyIteration) => { - return dataKeyIteration.name === attribute; - }); + const findDataKey = datasource.dataKeys.find((dataKeyIteration) => + dataKeyIteration.name === attribute + ); if (findDataKey) { findDataKey.settings.push(settings); @@ -282,20 +281,26 @@ export class TbCanvasDigitalGauge { color: this.localSettings.gaugeColor }); - function setLevelColor(levelSetting: AttributeSourceProperty, color: string) { - if (levelSetting.valueSource === 'predefinedValue' && isFinite(levelSetting.value)) { + function setLevelColor(levelSetting, color: string) { + if (levelSetting.type === ValueSourceDataKeyType.constant && isFinite(levelSetting.value)) { predefineLevelColors.push({ value: levelSetting.value, color }); - } else if (levelSetting.entityAlias && levelSetting.attribute) { + } else if (levelSetting.type === ValueSourceDataKeyType.latestKey || levelSetting.type === ValueSourceDataKeyType.entity) { try { - levelColorsDatasource = TbCanvasDigitalGauge.generateDatasource(this.ctx, levelColorsDatasource, - levelSetting.entityAlias, levelSetting.attribute, {color, index: predefineLevelColors.length}); + levelColorsDatasource = ColorProcessor.generateDatasource( + this.ctx, levelColorsDatasource, levelSetting, {color, index: predefineLevelColors.length} + ); } catch (e) { return; } predefineLevelColors.push(null); + } else if (isFinite(levelSetting)) { + predefineLevelColors.push({ + value: levelSetting, + color + }); } } @@ -315,17 +320,18 @@ export class TbCanvasDigitalGauge { return predefineLevelColors; } - settingTicksSubscribe(options: AttributeSourceProperty[]): number[] { + settingTicksSubscribe(options: ValueSourceWithDataKey[]): number[] { let ticksDatasource: Datasource[] = []; const predefineTicks: number[] = []; for (const tick of options) { - if (tick.valueSource === 'predefinedValue' && isFinite(tick.value)) { + if (tick.type === ValueSourceDataKeyType.constant && isFinite(tick.value)) { predefineTicks.push(tick.value); - } else if (tick.entityAlias && tick.attribute) { + } else { try { - ticksDatasource = TbCanvasDigitalGauge - .generateDatasource(this.ctx, ticksDatasource, tick.entityAlias, tick.attribute, predefineTicks.length); + ticksDatasource = ColorProcessor.generateDatasource( + this.ctx, ticksDatasource, tick, {index: predefineTicks.length} + ); } catch (e) { continue; } @@ -364,7 +370,7 @@ export class TbCanvasDigitalGauge { if (keyData && keyData.data && keyData.data[0]) { const attrValue = keyData.data[0][1]; if (isFinite(attrValue)) { - for (const setting of keyData.dataKey.settings) { + for (const setting of keyData.dataKey.settings.dataKeySettings) { switch (typeAttributes) { case 'levelColors': this.localSettings.levelColors[setting.index] = { @@ -373,7 +379,7 @@ export class TbCanvasDigitalGauge { }; break; case 'ticks': - this.localSettings.ticks[setting] = attrValue; + this.localSettings.ticks[setting.index] = attrValue; break; } } @@ -409,6 +415,13 @@ export class TbCanvasDigitalGauge { this.gauge._value = value; } this.gauge.value = value; + if (this.localSettings.barColor?.type === ColorType.function && isDefined(this.localSettings.barColor?.colorFunction)) { + this.localSettings.levelColors = [safeExecute(parseFunction(this.localSettings.barColor.colorFunction, ['value']), [value])]; + this.updateSetting(); + } else if (this.localSettings.barColor?.type === ColorType.constant && isDefinedAndNotNull(this.localSettings.barColor?.color)) { + this.localSettings.levelColors = [this.localSettings.barColor.color]; + this.updateSetting(); + } } else if (this.localSettings.showTimestamp && this.gauge.timestamp !== timestamp) { this.gauge.timestamp = timestamp; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts index 0cc6d266b0..c74c357739 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts @@ -37,7 +37,8 @@ import { getDataKey, getSingleTsValue, overlayStyle, - textStyle + textStyle, + updateGradientMinMaxValues } from '@shared/models/widget-settings.models'; import { WidgetComponent } from '@home/components/widget/widget.component'; import { @@ -191,16 +192,20 @@ export class BatteryLevelWidgetComponent implements OnInit, OnDestroy, AfterView this.showValue = this.settings.showValue; this.autoScaleValueSize = this.showValue && this.settings.autoScaleValueSize; this.valueStyle = textStyle(this.settings.valueFont); - this.valueColor = ColorProcessor.fromSettings(this.settings.valueColor); + this.valueColor = ColorProcessor.fromSettings(updateGradientMinMaxValues(this.settings.valueColor, 0, 100), this.ctx); - this.batteryLevelColor = ColorProcessor.fromSettings(this.settings.batteryLevelColor); + this.batteryLevelColor = ColorProcessor.fromSettings(updateGradientMinMaxValues(this.settings.batteryLevelColor, 0, 100), this.ctx); - this.batteryShapeColor = ColorProcessor.fromSettings(this.settings.batteryShapeColor); + this.batteryShapeColor = ColorProcessor.fromSettings(updateGradientMinMaxValues(this.settings.batteryShapeColor, 0, 100), this.ctx); this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); this.overlayStyle = overlayStyle(this.settings.background.overlay); this.hasCardClickAction = this.ctx.actionsApi.getActionDescriptors('cardClick').length > 0; + + this.valueColor.colorUpdated.subscribe(() => this.cd.markForCheck()); + this.batteryLevelColor.colorUpdated.subscribe(() => this.cd.markForCheck()); + this.batteryShapeColor.colorUpdated.subscribe(() => this.cd.markForCheck()); } ngAfterViewInit() { @@ -218,6 +223,14 @@ export class BatteryLevelWidgetComponent implements OnInit, OnDestroy, AfterView if (this.batteryBoxResize$) { this.batteryBoxResize$.disconnect(); } + + this.valueColor.colorUpdated.unsubscribe(); + this.batteryLevelColor.colorUpdated.unsubscribe(); + this.batteryShapeColor.colorUpdated.unsubscribe(); + + this.batteryLevelColor.destroy(); + this.valueColor.destroy(); + this.batteryShapeColor.destroy(); } public onInit() { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.models.ts index 081aae6558..281b8fe40f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.models.ts @@ -80,21 +80,25 @@ export const batteryLevelDefaultSettings: BatteryLevelWidgetSettings = { batteryLevelColor: { color: 'rgba(224, 224, 224, 1)', type: ColorType.range, - rangeList: [ - {from: null, to: 25, color: 'rgba(227, 71, 71, 1)'}, - {from: 25, to: 50, color: 'rgba(246, 206, 67, 1)'}, - {from: 50, to: null, color: 'rgba(92, 223, 144, 1)'} - ], + rangeList: { + range: [ + {from: null, to: 25, color: 'rgba(227, 71, 71, 1)'}, + {from: 25, to: 50, color: 'rgba(246, 206, 67, 1)'}, + {from: 50, to: null, color: 'rgba(92, 223, 144, 1)'} + ] + }, colorFunction: defaultColorFunction }, batteryShapeColor: { color: 'rgba(224, 224, 224, 0.32)', type: ColorType.range, - rangeList: [ - {from: null, to: 25, color: 'rgba(227, 71, 71, 0.32)'}, - {from: 25, to: 50, color: 'rgba(246, 206, 67, 0.32)'}, - {from: 50, to: null, color: 'rgba(92, 223, 144, 0.32)'} - ], + rangeList: { + range: [ + {from: null, to: 25, color: 'rgba(227, 71, 71, 0.32)'}, + {from: 25, to: 50, color: 'rgba(246, 206, 67, 0.32)'}, + {from: 50, to: null, color: 'rgba(92, 223, 144, 0.32)'} + ] + }, colorFunction: defaultColorFunction }, background: { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.models.ts index 6b426197d0..cb626a70dc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.models.ts @@ -84,12 +84,14 @@ export const signalStrengthDefaultSettings: SignalStrengthWidgetSettings = { activeBarsColor: { color: 'rgba(92, 223, 144, 1)', type: ColorType.range, - rangeList: [ - {to: -85, color: 'rgba(227, 71, 71, 1)'}, - {from: -85, to: -70, color: 'rgba(255, 122, 0, 1)'}, - {from: -70, to: -55, color: 'rgba(246, 206, 67, 1)'}, - {from: -55, color: 'rgba(92, 223, 144, 1)'} - ], + rangeList: { + range: [ + {to: -85, color: 'rgba(227, 71, 71, 1)'}, + {from: -85, to: -70, color: 'rgba(255, 122, 0, 1)'}, + {from: -70, to: -55, color: 'rgba(246, 206, 67, 1)'}, + {from: -55, color: 'rgba(92, 223, 144, 1)'} + ] + }, colorFunction: defaultColorFunction }, noSignalRssiValue: -100, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.html index cbde25d5e8..4767f393a8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.html @@ -209,7 +209,6 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.html index aa497b0d3b..ef7ad17878 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.html @@ -15,34 +15,22 @@ limitations under the License. --> -
- - -
-
{{ thresholdText() }}
- - - - -
-
- - -
-
widgets.chart.line-width
- - - px - -
-
-
- +
+ +
+ + + + + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.scss deleted file mode 100644 index 2ed7a91eb4..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.scss +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright © 2016-2024 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. - */ -.tb-flot-threshold { - display: flex; - flex-direction: row; - align-items: start; - gap: 4px; - .mat-expansion-panel.tb-settings.tb-flot-threshold-settings { - box-shadow: none; - border-radius: 6px; - border: 1px solid rgba(0, 0, 0, 0.12); - .mat-expansion-panel-header { - height: 56px; - border-radius: 0; - display: flex; - flex-direction: row; - align-items: stretch; - .mat-content { - overflow: hidden; - } - .tb-threshold-header { - flex: 1; - display: flex; - flex-direction: row; - gap: 16px; - align-items: center; - padding-left: 16px; - overflow: hidden; - .mat-divider-vertical { - height: 100%; - } - } - .tb-threshold-text { - flex: 1; - font-size: 16px; - font-style: normal; - font-weight: 400; - line-height: 16px; - letter-spacing: 0.15px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - .mat-expansion-indicator { - margin-right: 22px; - margin-left: 22px; - margin-top: 12px; - } - } - > .mat-expansion-panel-content { - > .mat-expansion-panel-body { - padding: 16px !important; - } - } - &.mat-expanded { - .mat-expansion-panel-header { - border-bottom: 1px solid rgba(0, 0, 0, 0.12); - } - } - } - > .mdc-icon-button { - margin-top: 4px; - color: rgba(0, 0, 0, 0.54); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts index 1dea0cc0c6..b6b4c88962 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts @@ -26,15 +26,13 @@ import { 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 { isNumber } from '@core/utils'; import { IAliasController } from '@core/api/widget-api.models'; import { TbFlotKeyThreshold } from '@home/components/widget/lib/flot-widget.models'; @Component({ selector: 'tb-flot-threshold', templateUrl: './flot-threshold.component.html', - styleUrls: ['./flot-threshold.component.scss'], + styleUrls: [], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -49,9 +47,6 @@ export class FlotThresholdComponent extends PageComponent implements OnInit, Con @Input() disabled: boolean; - @Input() - expanded = false; - @Input() aliasController: IAliasController; @@ -65,7 +60,6 @@ export class FlotThresholdComponent extends PageComponent implements OnInit, Con public thresholdFormGroup: UntypedFormGroup; constructor(protected store: Store, - private translate: TranslateService, private fb: UntypedFormBuilder) { super(store); } @@ -110,26 +104,8 @@ export class FlotThresholdComponent extends PageComponent implements OnInit, Con ); } - thresholdText(): string { - const value: ValueSourceProperty = this.thresholdFormGroup.get('valueSource').value; - return this.valueSourcePropertyText(value); - } - - private valueSourcePropertyText(source?: ValueSourceProperty): string { - if (source) { - if (source.valueSource === 'predefinedValue') { - return `${isNumber(source.value) ? source.value : 0}`; - } else if (source.valueSource === 'entityAttribute') { - const alias = source.entityAlias || 'Undefined'; - const key = source.attribute || 'Undefined'; - return `${alias}.${key}`; - } - } - return 'Undefined'; - } - private updateModel() { - const value: {valueSource: ValueSourceProperty, lineWidth: number, color: string} = this.thresholdFormGroup.value; + const value: {valueSource: ValueSourceProperty; lineWidth: number; color: string} = this.thresholdFormGroup.value; this.modelValue = { thresholdValueSource: value?.valueSource?.valueSource, thresholdEntityAlias: value?.valueSource?.entityAlias, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.html new file mode 100644 index 0000000000..4bd50a81c6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.html @@ -0,0 +1,56 @@ + +
+
+
+
+
widgets.color.from
+ + +
+
+
widgets.color.to
+ + +
+
+ + +
+ +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.scss new file mode 100644 index 0000000000..cf4059b0c7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.scss @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2024 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 { + .range { + &-container { + display: flex; + flex: 1; + flex-direction: row; + align-items: center; + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 6px; + } + &-input { + padding: 7px; + &-fields { + display: flex; + flex-direction: column; + flex: 1; + gap: 12px; + border-right: 1px solid rgba(0, 0, 0, 0.12);; + } + } + &-text { + min-width: 50px; + font-size: 14px; + color: rgba(0, 0, 0, 0.38); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts new file mode 100644 index 0000000000..bfb6433a82 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts @@ -0,0 +1,109 @@ +/// +/// Copyright © 2016-2024 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, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { IAliasController } from '@core/api/widget-api.models'; +import { AdvancedColorRange } from '@shared/models/widget-settings.models'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { Datasource } from '@shared/models/widget.models'; + +@Component({ + selector: 'tb-advanced-range', + templateUrl: './advanced-range.component.html', + styleUrls: ['./advanced-range.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AdvancedRangeComponent), + multi: true + } + ] +}) +export class AdvancedRangeComponent extends PageComponent implements OnInit, ControlValueAccessor { + + @Input() + disabled: boolean; + + @Input() + aliasController: IAliasController; + + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + + @Output() + removeAdvancedRange = new EventEmitter(); + + private modelValue: AdvancedColorRange; + + private propagateChange = (v: any) => { }; + + public advancedRangeLevelFormGroup: UntypedFormGroup; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + ngOnInit(): void { + this.advancedRangeLevelFormGroup = this.fb.group({ + from: [null, []], + to: [null, []], + color: [null, [Validators.required]] + }); + this.advancedRangeLevelFormGroup.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.advancedRangeLevelFormGroup.disable({emitEvent: false}); + } else { + this.advancedRangeLevelFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: AdvancedColorRange): void { + this.modelValue = value; + this.advancedRangeLevelFormGroup.patchValue(value, {emitEvent: false}); + } + + private updateModel() { + this.modelValue = this.advancedRangeLevelFormGroup.value; + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.html index 68030f89bf..2c0787c778 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.html @@ -15,36 +15,97 @@ limitations under the License. --> -
{{ panelTitle }}
-
-
-
-
-
widgets.color.from
- - - -
widgets.color.to
- - - - - -
- + +
+
widgets.color.settings-type
+ + + {{ 'widgets.color.basic-mode' | translate }} + + + {{ 'widgets.color.advanced-mode' | translate}} + + +
+ + +
+
{{ panelTitle }}
+
+ +
+ +
+
+
+
+
widgets.color.from
+ + + +
widgets.color.to
+ + + + + +
+ + +
+
+
+
+ + +
+
+ + + +
+
+ +
+
-
- + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.scss similarity index 60% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.scss index c5711899ea..63265f6dbc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.scss @@ -13,28 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + :host { - display: block; - .mat-expansion-panel { - box-shadow: none; - &.fixed-color-level { - border: 1px groove rgba(0, 0, 0, .25); - .mat-expansion-panel-header { - padding: 0 24px 0 8px; - &.mat-expanded { - height: 48px; - } - } - } - } -} + .range { + display: flex; + flex: 1; + align-items: center; + flex-direction: row; -:host ::ng-deep { - .mat-expansion-panel { - &.fixed-color-level { - .mat-expansion-panel-body { - padding: 0 8px 8px; - } + &-container { + display: flex; + flex: 1; + flex-direction: row; + align-items: center; + gap: 12px; + padding: 8px; + margin-right: 12px; + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 6px; } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts index 287ab13ce1..e37186748d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts @@ -18,21 +18,44 @@ import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, + FormControl, FormGroup, NG_VALUE_ACCESSOR, UntypedFormArray, UntypedFormBuilder, - UntypedFormGroup + UntypedFormGroup, + ValidationErrors } from '@angular/forms'; -import { ColorRange } from '@shared/models/widget-settings.models'; +import { + AdvancedColorRange, + ColorRange, + ColorRangeSettings, + ValueSourceDataKeyType +} from '@shared/models/widget-settings.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { deepClone, isDefinedAndNotNull, isUndefined } from '@core/utils'; +import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { IAliasController } from '@core/api/widget-api.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { Datasource } from '@shared/models/widget.models'; + +export function advancedRangeValidator(control: AbstractControl): ValidationErrors | null { + const range: AdvancedColorRange = control.value; + if (!range || !range.color) { + return { + advancedRange: true + }; + } + return null; +} @Component({ selector: 'tb-color-range-list', templateUrl: './color-range-list.component.html', - styleUrls: ['color-settings-panel.component.scss'], + styleUrls: ['color-settings-panel.component.scss', 'color-range-list.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -52,19 +75,34 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On @Input() panelTitle: string; + @Input() + aliasController: IAliasController; + + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + + @Input() + @coerceBoolean() + advancedMode = false; + modelValue: any; colorRangeListFormGroup: UntypedFormGroup; private destroy$ = new Subject(); - private propagateChange = null; + private propagateChange = (v: any) => { }; constructor(private fb: UntypedFormBuilder) {} ngOnInit(): void { this.colorRangeListFormGroup = this.fb.group({ - rangeList: this.fb.array([]) + advancedMode: [false], + range: this.fb.array([]), + rangeAdvanced: this.fb.array([]) }); this.colorRangeListFormGroup.valueChanges.pipe( @@ -89,8 +127,21 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On } writeValue(value: any): void { - if (value && value?.length) { - value.forEach((r) => this.rangeListFormArray.push(this.colorRangeControl(r), {emitEvent: false})); + if (value) { + let rangeList: ColorRangeSettings = {}; + if (isUndefined(value?.advancedMode) && value?.length) { + rangeList.advancedMode = false; + rangeList.range = value; + } else { + rangeList = deepClone(value); + } + this.colorRangeListFormGroup.get('advancedMode').patchValue(rangeList.advancedMode, {emitEvent: false}); + if (isDefinedAndNotNull(rangeList?.range)) { + rangeList.range.forEach((r) => this.rangeListFormArray.push(this.colorRangeControl(r), {emitEvent: false})); + } + if (isDefinedAndNotNull(rangeList?.rangeAdvanced)) { + rangeList.rangeAdvanced.forEach((r) => this.advancedRangeFormArray.push(this.fb.control(r), {emitEvent: false})); + } } } @@ -103,7 +154,7 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On } get rangeListFormArray(): UntypedFormArray { - return this.colorRangeListFormGroup.get('rangeList') as UntypedFormArray; + return this.colorRangeListFormGroup.get('range') as UntypedFormArray; } get rangeListFormGroups(): FormGroup[] { @@ -114,23 +165,65 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On return rangeControl; } + public trackByAdvancedRange(index: number, advancedRangeControl: AbstractControl): any { + return advancedRangeControl; + } + + public removeAdvancedRange(index: number) { + (this.colorRangeListFormGroup.get('rangeAdvanced') as UntypedFormArray).removeAt(index); + } + + get advancedRangeFormArray(): UntypedFormArray { + return this.colorRangeListFormGroup.get('rangeAdvanced') as UntypedFormArray; + } + + get advancedRangeControls(): FormControl[] { + return this.advancedRangeFormArray.controls as FormControl[]; + } + removeRange(index: number) { this.rangeListFormArray.removeAt(index); this.colorRangeListFormGroup.markAsDirty(); setTimeout(() => {this.popover?.updatePosition();}, 0); } - addRange() { - const newRange: ColorRange = { - color: 'rgba(0,0,0,0.87)' + rangeDrop(event: CdkDragDrop, range: string) { + const rangeColorsArray = this.colorRangeListFormGroup.get(range) as UntypedFormArray; + const rangeColor = rangeColorsArray.at(event.previousIndex); + rangeColorsArray.removeAt(event.previousIndex); + rangeColorsArray.insert(event.currentIndex, rangeColor); + } + + public addAdvancedRange() { + const advancedRange: AdvancedColorRange = { + from: { + type: ValueSourceDataKeyType.constant + }, + to: { + type: ValueSourceDataKeyType.constant + }, + color: null }; - this.rangeListFormArray.push(this.colorRangeControl(newRange)); - this.colorRangeListFormGroup.markAsDirty(); - setTimeout(() => {this.popover?.updatePosition();}, 0); + const advancedRangeColorsArray = this.colorRangeListFormGroup.get('rangeAdvanced') as UntypedFormArray; + const advancedRangeColorControl = this.fb.control(advancedRange, [advancedRangeValidator]); + advancedRangeColorsArray.push(advancedRangeColorControl); + } + + addRange() { + if (this.colorRangeListFormGroup.get('advancedMode').value) { + this.addAdvancedRange(); + } else { + const newRange: ColorRange = { + color: 'rgba(0,0,0,0.87)' + }; + this.rangeListFormArray.push(this.colorRangeControl(newRange)); + this.colorRangeListFormGroup.markAsDirty(); + setTimeout(() => {this.popover?.updatePosition();}, 0); + } } updateModel() { - this.propagateChange(this.colorRangeListFormGroup.get('rangeList').value); + this.propagateChange(this.colorRangeListFormGroup.value); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-panel.component.html index 1b9a44286b..347db57baa 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-panel.component.html @@ -18,7 +18,7 @@
widgets.color.color-settings
- diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html index b85a96325e..f7df367657 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html @@ -33,10 +33,26 @@
+
+ + +
-
@@ -50,7 +66,8 @@ color="primary" type="button" [matMenuTriggerFor]="settingsSourcesMenu" [matMenuTriggerData]="{menuWidth: copySettingsButton._elementRef.nativeElement.clientWidth}"> - {{ 'widgets.color.copy-color-settings-from' | translate }} + {{ 'widgets.color.copy-color-settings-from' | translate }} + {{ 'widgets.color.copy-from' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.scss index 610832007f..1b0fda35b3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.scss @@ -16,8 +16,10 @@ @import '../../../../../../../../scss/constants'; .tb-color-settings-panel { - width: 500px; - height: 470px; + width: 700px; + max-width: 90vw; + min-height: 300px; + max-height: 90vh; display: flex; flex-direction: column; gap: 16px; @@ -31,6 +33,12 @@ letter-spacing: 0.25px; color: rgba(0, 0, 0, 0.87); } + .tb-gradient-panel { + flex: 1; + gap: 16px; + display: flex; + flex-direction: column; + } .tb-color-ranges-panel { flex: 1; min-height: 0; @@ -82,3 +90,12 @@ align-items: flex-end; } } + +.tb-advanced-range-drag { + display: flex; + flex-direction: row; + align-items: center; + tb-advanced-range { + flex: 1; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts index e96d906b45..84d668565f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts @@ -16,7 +16,12 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; -import { ColorSettings, ColorType, colorTypeTranslations } from '@shared/models/widget-settings.models'; +import { + ColorSettings, + ColorType, + colorTypeTranslations, + defaultGradient +} from '@shared/models/widget-settings.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; @@ -24,6 +29,10 @@ import { AppState } from '@core/core.state'; import { deepClone } from '@core/utils'; import { WidgetService } from '@core/http/widget.service'; import { ColorSettingsComponent } from '@home/components/widget/lib/settings/common/color-settings.component'; +import { IAliasController } from '@core/api/widget-api.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { Datasource } from '@shared/models/widget.models'; @Component({ selector: 'tb-color-settings-panel', @@ -46,6 +55,29 @@ export class ColorSettingsPanelComponent extends PageComponent implements OnInit @Output() colorSettingsApplied = new EventEmitter(); + @Input() + aliasController: IAliasController; + + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + + @Input() + @coerceBoolean() + rangeAdvancedMode = false; + + @Input() + @coerceBoolean() + gradientAdvancedMode = false; + + @Input() + minValue: string; + + @Input() + maxValue: string; + colorType = ColorType; colorTypes = Object.keys(ColorType) as ColorType[]; @@ -67,6 +99,7 @@ export class ColorSettingsPanelComponent extends PageComponent implements OnInit { type: [this.colorSettings?.type || ColorType.constant, []], color: [this.colorSettings?.color, []], + gradient: [this.colorSettings?.gradient || defaultGradient(+this.minValue, +this.maxValue), []], rangeList: [this.colorSettings?.rangeList, []], colorFunction: [this.colorSettings?.colorFunction, []] } @@ -82,8 +115,9 @@ export class ColorSettingsPanelComponent extends PageComponent implements OnInit this.colorSettingsFormGroup.patchValue({ type: this.colorSettings.type, color: this.colorSettings.color, + gradient: this.colorSettings.gradient || null, colorFunction: this.colorSettings.colorFunction, - rangeList: this.colorSettings.rangeList || [] + rangeList: this.colorSettings.rangeList || null }, {emitEvent: false}); this.colorSettingsFormGroup.markAsDirty(); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.ts index 912c1e30ac..7594719cff 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.ts @@ -25,12 +25,17 @@ import { ViewContainerRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { ColorSettings, ColorType, ComponentStyle } from '@shared/models/widget-settings.models'; +import { ColorRange, ColorSettings, ColorType, ComponentStyle } from '@shared/models/widget-settings.models'; import { MatButton } from '@angular/material/button'; import { TbPopoverService } from '@shared/components/popover.service'; import { ColorSettingsPanelComponent } from '@home/components/widget/lib/settings/common/color-settings-panel.component'; +import { IAliasController } from '@core/api/widget-api.models'; +import { deepClone, isDefinedAndNotNull } from '@core/utils'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { Datasource } from '@shared/models/widget.models'; @Injectable() export class ColorSettingsComponentService { @@ -79,13 +84,36 @@ export class ColorSettingsComponent implements OnInit, ControlValueAccessor, OnD @Input() settingsKey: string; + @Input() + aliasController: IAliasController; + + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + + @Input() + @coerceBoolean() + rangeAdvancedMode = false; + + @Input() + @coerceBoolean() + gradientAdvancedMode = false; + + @Input() + minValue: string; + + @Input() + maxValue: string; + colorType = ColorType; modelValue: ColorSettings; colorStyle: ComponentStyle = {}; - private propagateChange = null; + private propagateChange = (v: any) => { }; constructor(private popoverService: TbPopoverService, private renderer: Renderer2, @@ -113,8 +141,14 @@ export class ColorSettingsComponent implements OnInit, ControlValueAccessor, OnD } writeValue(value: ColorSettings): void { - this.modelValue = value; - this.updateColorStyle(); + if (value) { + this.modelValue = value; + if (isDefinedAndNotNull(this.modelValue.rangeList) && !isDefinedAndNotNull(this.modelValue.rangeList?.advancedMode)) { + const range = deepClone(this.modelValue.rangeList) as ColorRange[]; + this.modelValue.rangeList = deepClone({advancedMode: false, range}); + } + this.updateColorStyle(); + } } openColorSettingsPopup($event: Event, matButton: MatButton) { @@ -127,7 +161,14 @@ export class ColorSettingsComponent implements OnInit, ControlValueAccessor, OnD } else { const ctx: any = { colorSettings: this.modelValue, - settingsComponents: this.colorSettingsComponentService.getOtherColorSettingsComponents(this) + settingsComponents: this.colorSettingsComponentService.getOtherColorSettingsComponents(this), + aliasController: this.aliasController, + dataKeyCallbacks: this.dataKeyCallbacks, + datasource: this.datasource, + rangeAdvancedMode: this.rangeAdvancedMode, + gradientAdvancedMode: this.gradientAdvancedMode, + minValue: this.minValue, + maxValue: this.maxValue }; const colorSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, this.viewContainerRef, ColorSettingsPanelComponent, 'left', true, null, @@ -147,18 +188,32 @@ export class ColorSettingsComponent implements OnInit, ControlValueAccessor, OnD private updateColorStyle() { if (!this.disabled && this.modelValue) { let colors: string[] = [this.modelValue.color]; - if (this.modelValue.type === ColorType.range && this.modelValue.rangeList?.length) { - const rangeColors = this.modelValue.rangeList.slice(0, Math.min(2, this.modelValue.rangeList.length)).map(r => r.color); + const rangeList = this.modelValue.rangeList; + if (this.modelValue.type === ColorType.range && (rangeList?.range?.length || rangeList?.rangeAdvanced?.length)) { + let rangeColors: Array; + if (rangeList?.advancedMode) { + rangeColors = rangeList.rangeAdvanced.slice(0, Math.min(2, rangeList.rangeAdvanced.length)).map(r => r.color); + } else { + rangeColors = rangeList.range.slice(0, Math.min(2, rangeList.range.length)).map(r => r.color); + } colors = colors.concat(rangeColors); + } else if (this.modelValue.type === ColorType.gradient) { + colors = this.modelValue.gradient?.advancedMode ? + this.modelValue.gradient.gradientAdvanced.map(color => color.color) : + this.modelValue.gradient.gradient; } if (colors.length === 1) { this.colorStyle = {backgroundColor: colors[0]}; } else { const gradientValues: string[] = []; - const step = 100 / colors.length; - for (let i = 0; i < colors.length; i++) { - gradientValues.push(`${colors[i]} ${step*i}%`); - gradientValues.push(`${colors[i]} ${step*(i+1)}%`); + if (this.modelValue.type === ColorType.gradient) { + gradientValues.push(...colors); + } else { + const step = 100 / colors.length; + for (let i = 0; i < colors.length; i++) { + gradientValues.push(`${colors[i]} ${step*i}%`); + gradientValues.push(`${colors[i]} ${step*(i+1)}%`); + } } this.colorStyle = {background: `linear-gradient(90deg, ${gradientValues.join(', ')})`}; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss index f8564a46ac..43dbc042cd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss @@ -28,8 +28,8 @@ padding-right: 0; .mat-mdc-form-field-infix { - padding-top: 0; - padding-bottom: 6px; + padding-top: 0 !important; + padding-bottom: 6px !important; .mdc-evolution-chip-set .mdc-evolution-chip { margin: 0; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.html new file mode 100644 index 0000000000..2296aaf4e4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.html @@ -0,0 +1,208 @@ + + + +
+
widgets.color.gradient-type
+ + + {{ 'widgets.color.basic-mode' | translate }} + + + {{ 'widgets.color.advanced-mode' | translate}} + + +
+ +
+
+
+
+ +
+ +
+
+
widgets.color.gradient-start
+
+
+
widgets.color.start-value
+ + + +
+ + +
+
+
+ +
+
+
+
+
+
widgets.color.gradient-color
+ + +
+ + +
+
+
+
+ + +
+
+
widgets.color.gradient-end
+
+
+
widgets.color.end-value
+ + + +
+ + +
+
+
+
+ +
+
+
+ widgets.color.gradient-start + widgets.color.gradient-start-min +
+
+ + + + +
+
+
+ +
+
+
+
+
+
+ widgets.color.gradient-color + widgets.color.gradient-color-min +
+
+ + + + +
+
+ + +
+
+
+
+ + +
+
+
+ widgets.color.gradient-end + widgets.color.gradient-end-min +
+
+ + + + +
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.scss new file mode 100644 index 0000000000..38f320b940 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.scss @@ -0,0 +1,145 @@ +/** + * Copyright © 2016-2024 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 "../../../../../../../../scss/constants"; + + +:host { + overflow: auto; + height: 100%; + max-height: 420px; + .gradient { + display: flex; + flex: 1; + align-items: center; + flex-direction: row; + + &-container { + display: flex; + flex: 1; + flex-direction: row; + justify-content: space-between; + align-items: center; + gap: 12px; + padding: 8px; + margin-right: 12px; + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 6px; + &-start, &-end { + margin-right: 92px; + @media #{$mat-lt-lg} { + margin-right: 52px; + } + } + + .tb-gradient-text { + width: 140px; + @media #{$mat-xs} { + width: 50px; + } + } + } + .start-gradient-container, .end-gradient-container, .list-gradient-container { + display: flex; + gap: 8px; + .gradient-text { + font-size: 14px; + color: rgba(0, 0, 0, 0.38); + } + &-advanced { + flex: 1; + @media #{$mat-xs} { + align-items: center; + } + } + } + } + + .gradient-preview { + width: 100%; + padding: 40px 12px 0; + .gradient-background { + position: relative; + height: 56px; + border-radius: 8px; + } + } + + .gradient-settings { + flex: 1; + gap: 16px; + display: flex; + flex-direction: column; + } + + .tb-add-gradient { + margin-right: 92px; + @media #{$mat-lt-lg} { + margin-right: 52px; + } + } +} +::ng-deep { + .gradient-background { + .pointer { + position: absolute; + width: 8px; + height: 60px; + border-radius: 6px; + top: -2px; + border: 2px solid white; + -webkit-box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.75); + -moz-box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.75); + box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.75); + + &.start { + left: 1px; + } + + &.end { + right: 1px; + } + + &-value { + position: absolute; + top: -40px; + left: 50%; + transform: translateX(-50%); + display: flex; + align-items: center; + justify-content: center; + width: 33px; + height: 24px; + border-radius: 4px; + background-color: rgba(0, 0, 0, 0.06); + &-text { + font-size: 14px; + font-weight: 500; + } + &:after { + content: ""; + bottom: -8px; + position: absolute; + width: 0; + height: 0; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-top: 8px solid rgba(0, 0, 0, 0.06); + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts new file mode 100644 index 0000000000..b66e5ed1e9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts @@ -0,0 +1,266 @@ +/// +/// Copyright © 2016-2024 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, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormGroup, + NG_VALUE_ACCESSOR, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormGroup +} from '@angular/forms'; +import { AdvancedGradient, ColorGradientSettings, ValueSourceDataKeyType } from '@shared/models/widget-settings.models'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { IAliasController } from '@core/api/widget-api.models'; +import { DomSanitizer } from '@angular/platform-browser'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { isDefinedAndNotNull } from '@core/utils'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { Datasource } from '@shared/models/widget.models'; + +@Component({ + selector: 'tb-gradient', + templateUrl: './gradient.component.html', + styleUrls: ['color-settings-panel.component.scss', 'gradient.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => GradientComponent), + multi: true + } + ] +}) +export class GradientComponent implements OnInit, ControlValueAccessor, OnDestroy { + + @ViewChild('gradient') gradient: ElementRef; + + @Input() + disabled: boolean; + + @Input() + popover: TbPopoverComponent; + + @Input() + panelTitle: string; + + @Input() + aliasController: IAliasController; + + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + + @Input() + minValue: string; + + @Input() + maxValue: string; + + @Input() + @coerceBoolean() + advancedMode = true; + + modelValue: any; + + gradientFormGroup: UntypedFormGroup; + + private destroy$ = new Subject(); + + private propagateChange = (v: any) => { }; + + constructor(private fb: UntypedFormBuilder, + private sanitizer: DomSanitizer) {} + + ngOnInit(): void { + this.gradientFormGroup = this.fb.group({ + advancedMode: [false], + gradient: this.fb.group({ + start: ['rgba(0, 255, 0, 1)'], + gradientList: this.fb.array([]), + end: ['rgba(255, 0, 0, 1)'] + }), + gradientAdvanced: this.fb.group({ + start: this.fb.group({ + source: [{type: ValueSourceDataKeyType.constant}], + color: ['rgba(0, 255, 0, 1)'] + }), + gradientList: this.fb.array([]), + end: this.fb.group({ + source: [{type: ValueSourceDataKeyType.constant}], + color: ['rgba(255, 0, 0, 1)'] + }) + }) + }); + + this.gradientFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel()); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + writeValue(value: ColorGradientSettings): void { + if (isDefinedAndNotNull(value)) { + this.gradientFormGroup.get('advancedMode').patchValue(value.advancedMode, {emitEvent: false}); + if (value?.gradient?.length) { + this.gradientFormGroup.get('gradient').get('start').patchValue(value.gradient[0], {emitEvent: false}); + this.gradientFormGroup.get('gradient').get('end').patchValue(value.gradient[value.gradient.length - 1], {emitEvent: false}); + value.gradient.slice(1, -1).forEach(r => this.gradientListFormArray.push(this.colorGradientControl(r), {emitEvent: false})); + } + if (value?.gradientAdvanced?.length) { + this.gradientFormGroup.get('gradientAdvanced').get('start').patchValue(value.gradientAdvanced[0], {emitEvent: false}); + this.gradientFormGroup.get('gradientAdvanced').get('end').patchValue( + value.gradientAdvanced[value.gradientAdvanced.length - 1], {emitEvent: false} + ); + value.gradientAdvanced.slice(1, -1).forEach( + r => this.advancedGradientListFormArray.push(this.advancedGradientControl(r), {emitEvent: false}) + ); + } + } + } + + get generatePointers() { + if (this.gradientFormGroup.get('advancedMode').value) { + const shift = 100 / (this.advancedGradientListFormArray.value.length + 1); + return `
` + + this.advancedGradientListFormArray.value.map((v, i) => this.pointer(shift * (i + 1), i+1, null, true)).join('') + + `
`; + } else { + const point = (+this.maxValue - +this.minValue) / (this.gradientListFormArray.value.length + 1); + const shift = 100 / (this.gradientListFormArray.value.length + 1); + const min = isDefinedAndNotNull(this.minValue) ? this.minValue : 0; + const max = isDefinedAndNotNull(this.maxValue) ? this.maxValue : 100; + return `
${min}
` + + this.gradientListFormArray.value.map((v, i) => this.pointer(shift * (i + 1), i+1, point)).join('') + + `
${max}
`; + } + } + + pointer(shift: number, index?: number, value?: number, advanced = false) { + if (advanced) { + return `
`; + } else { + return `
` + + `
${Math.floor(+this.minValue + (value * index))}
`; + } + } + + get linearGradient() { + const gradient = this.gradientFormGroup.get('advancedMode').value ? + [this.gradientFormGroup.value.gradientAdvanced.start.color, + ...this.gradientFormGroup.value.gradientAdvanced.gradientList.map(item => item.color), + this.gradientFormGroup.value.gradientAdvanced.end.color].join(', ') : + [this.gradientFormGroup.value.gradient.start, + ...this.gradientFormGroup.value.gradient.gradientList.map(item => item.color), + this.gradientFormGroup.value.gradient.end].join(', '); + return this.sanitizer.bypassSecurityTrustStyle(`background-image: linear-gradient(90deg, ${gradient})`); + } + + private colorGradientControl(gradient: string): UntypedFormGroup { + return this.fb.group({ + color: [gradient, []] + }); + } + + get gradientListFormArray(): UntypedFormArray { + return this.gradientFormGroup.get('gradient.gradientList') as UntypedFormArray; + } + get gradientListFormGroups(): FormGroup[] { + return this.gradientListFormArray.controls as FormGroup[]; + } + + private advancedGradientControl(gradient: AdvancedGradient): UntypedFormGroup { + return this.fb.group({ + source: [gradient.source, []], + color: [gradient.color, []] + }); + } + + get advancedGradientListFormArray(): UntypedFormArray { + return this.gradientFormGroup.get('gradientAdvanced.gradientList') as UntypedFormArray; + } + get advancedGradientListFormGroups(): FormGroup[] { + return this.advancedGradientListFormArray.controls as FormGroup[]; + } + + trackByGradient(index: number, gradientControl: AbstractControl): any { + return gradientControl; + } + + removeGradient(index: number, advanced = false) { + if (advanced) { + this.advancedGradientListFormArray.removeAt(index); + } else { + this.gradientListFormArray.removeAt(index); + } + this.gradientFormGroup.markAsDirty(); + setTimeout(() => {this.popover?.updatePosition();}, 0); + } + + gradientDrop(event: CdkDragDrop, advanced = false) { + const gradientColorsArray = advanced ? this.advancedGradientListFormArray : this.gradientListFormArray; + const gradientColor = gradientColorsArray.at(event.previousIndex); + gradientColorsArray.removeAt(event.previousIndex); + gradientColorsArray.insert(event.currentIndex, gradientColor); + } + + addGradient(advanced = false) { + if (advanced) { + this.advancedGradientListFormArray.push( + this.advancedGradientControl({source: {type: ValueSourceDataKeyType.constant}, color: 'rgba(0,0,0,0.87)'}) + ); + } else { + this.gradientListFormArray.push(this.colorGradientControl('rgba(0,0,0,0.87)')); + } + this.gradientFormGroup.markAsDirty(); + setTimeout(() => {this.popover?.updatePosition();}, 0); + } + + updateModel() { + this.propagateChange( + { + advancedMode: this.gradientFormGroup.value.advancedMode, + gradient: [this.gradientFormGroup.value.gradient.start, + ...this.gradientFormGroup.value.gradient.gradientList.map(item => item.color), + this.gradientFormGroup.value.gradient.end], + gradientAdvanced: [this.gradientFormGroup.value.gradientAdvanced.start, + ...this.gradientFormGroup.value.gradientAdvanced.gradientList, + this.gradientFormGroup.value.gradientAdvanced.end] + } + ); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.html new file mode 100644 index 0000000000..be6faaefaf --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.html @@ -0,0 +1,77 @@ + +
+
+ + + + {{ valueSourceDataKeyTypeTranslation.get(type) | translate }} + + + + + +
+
+ + + + warning + + + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.scss similarity index 61% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.scss index cafbf3b17b..5d769d867a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.scss @@ -13,28 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + :host { - display: block; - .mat-expansion-panel { - box-shadow: none; - &.tick-value { - border: 1px groove rgba(0, 0, 0, .25); - .mat-expansion-panel-header { - padding: 0 24px 0 8px; - &.mat-expanded { - height: 48px; - } - } + .tb-value-source { + display: flex; + gap: 12px; + } + .tb-source-field { + display: flex; + flex: 1; + gap: 12px; + .tb-type-field, .tb-entity-alias-field { + flex: 1; } } -} - -:host ::ng-deep { - .mat-expansion-panel { - &.tick-value { - .mat-expansion-panel-body { - padding: 0 8px 8px; - } + .tb-key-value-field, .tb-constant-field { + display: flex; + flex: 1; + } + ::ng-deep { + .tb-data-key-input { + flex: 1; } } } + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts new file mode 100644 index 0000000000..ed5f8eadf5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts @@ -0,0 +1,191 @@ +/// +/// Copyright © 2016-2024 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 { ChangeDetectorRef, Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { IAliasController } from '@core/api/widget-api.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { DataKey, Datasource, DatasourceType } from '@app/shared/models/widget.models'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { + ValueSourceDataKeyType, + ValueSourceDataKeyTypes, + ValueSourceDataKeyTypeTranslation +} from '@shared/models/widget-settings.models'; + +@Component({ + selector: 'tb-value-source-data-key', + templateUrl: './value-source-data-key.component.html', + styleUrls: ['value-source-data-key.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ValueSourceDataKeyComponent), + multi: true + } + ] +}) +export class ValueSourceDataKeyComponent extends PageComponent implements OnInit, ControlValueAccessor { + + dataKeyType = DataKeyType; + datasourceType = DatasourceType; + + valueSourceDataKeyType = ValueSourceDataKeyType; + valueSourceDataKeyTypes = ValueSourceDataKeyTypes; + valueSourceDataKeyTypeTranslation = ValueSourceDataKeyTypeTranslation; + + @Input() + disabled: boolean; + + @Input() + aliasController: IAliasController; + + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + + valueSourceFormGroup: UntypedFormGroup; + + modelValue; + + latestKeyFormControl: UntypedFormControl; + entityKeyFormControl: UntypedFormControl; + + private propagateChange = (_val: any) => {}; + + constructor(protected store: Store, + private fb: UntypedFormBuilder, + private cd: ChangeDetectorRef) { + super(store); + } + + ngOnInit(): void { + this.valueSourceFormGroup = this.fb.group({ + type: [ValueSourceDataKeyType.constant, []], + value: [null, [Validators.required]], + entityAlias: [null, [Validators.required]] + }); + this.latestKeyFormControl = this.fb.control(null, [Validators.required]); + this.entityKeyFormControl = this.fb.control(null, [Validators.required]); + this.valueSourceFormGroup.valueChanges.subscribe( + () => this.updateModel() + ); + this.latestKeyFormControl.valueChanges.subscribe( + () => this.updateModel() + ); + this.entityKeyFormControl.valueChanges.subscribe( + () => this.updateModel() + ); + this.valueSourceFormGroup.get('type').valueChanges.subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.valueSourceFormGroup.disable({emitEvent: false}); + this.latestKeyFormControl.disable({emitEvent: false}); + this.entityKeyFormControl.disable({emitEvent: false}); + } else { + this.valueSourceFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value): void { + this.modelValue = value; + this.valueSourceFormGroup.patchValue( + { + type: value.type, + value: value.value, + entityAlias: value.entityAlias + }, {emitEvent: false} + ); + if (value.type === ValueSourceDataKeyType.latestKey) { + this.latestKeyFormControl.patchValue({ + type: value.latestKeyType, + name: value.latestKey + }, {emitEvent: false}); + } else if (value.type === ValueSourceDataKeyType.entity) { + this.entityKeyFormControl.patchValue({ + type: value.entityKeyType, + name: value.entityKey + }, {emitEvent: false}); + } + + this.updateValidators(); + this.cd.markForCheck(); + } + + private updateModel() { + const value = this.valueSourceFormGroup.value; + this.modelValue.type = value.type; + this.modelValue.value = value.value; + this.modelValue.entityAlias = value.entityAlias; + + if (value.type === ValueSourceDataKeyType.latestKey) { + const latestKey: DataKey = this.latestKeyFormControl.value; + this.modelValue.latestKey = latestKey?.name; + this.modelValue.latestKeyType = (latestKey?.type as any); + } else if (value.type === ValueSourceDataKeyType.entity) { + const entityKey: DataKey = this.entityKeyFormControl.value; + this.modelValue.entityKey = entityKey?.name; + this.modelValue.entityKeyType = (entityKey?.type as any); + } + this.propagateChange(this.modelValue); + } + + private updateValidators(): void { + const type: ValueSourceDataKeyType = this.valueSourceFormGroup.get('type').value; + if (type === ValueSourceDataKeyType.constant) { + this.valueSourceFormGroup.get('value').enable({emitEvent: false}); + this.valueSourceFormGroup.get('entityAlias').disable({emitEvent: false}); + this.latestKeyFormControl.disable({emitEvent: false}); + this.entityKeyFormControl.disable({emitEvent: false}); + } else if (type === ValueSourceDataKeyType.latestKey) { + this.valueSourceFormGroup.get('value').disable({emitEvent: false}); + this.valueSourceFormGroup.get('entityAlias').disable({emitEvent: false}); + this.latestKeyFormControl.enable({emitEvent: false}); + this.entityKeyFormControl.disable({emitEvent: false}); + } else if (type === ValueSourceDataKeyType.entity) { + this.valueSourceFormGroup.get('value').disable({emitEvent: false}); + this.valueSourceFormGroup.get('entityAlias').enable({emitEvent: false}); + this.latestKeyFormControl.disable({emitEvent: false}); + this.entityKeyFormControl.enable({emitEvent: false}); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.html index cb5d62040d..24c8564ffb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.html @@ -15,65 +15,59 @@ limitations under the License. --> - -
- - + +
+ + + {{ 'widgets.value-source.predefined-value' | translate }} - - + + {{ 'widgets.value-source.entity-attribute' | translate }} - - -
-
-
widgets.value-source.value
- - - -
-
-
widgets.value-source.source-entity-alias
- - - - - - - - - -
-
-
widgets.value-source.source-entity-attribute
- - - - - - - - - -
- + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts index 9df42f0eb8..f290a05083 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts @@ -19,7 +19,6 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFor 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 { IAliasController } from '@core/api/widget-api.models'; import { Observable, of } from 'rxjs'; import { catchError, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators'; @@ -60,9 +59,15 @@ export class ValueSourceComponent extends PageComponent implements OnInit, Contr @Input() aliasController: IAliasController; + @Input() + entityAliasPlaceholder = 'widgets.value-source.source-entity-alias'; + + @Input() + entityAttributePlaceholder = 'widgets.value-source.source-entity-attribute'; + private modelValue: ValueSourceProperty; - private propagateChange = null; + private propagateChange = (v: any) => { }; public valueSourceFormGroup: UntypedFormGroup; @@ -78,7 +83,6 @@ export class ValueSourceComponent extends PageComponent implements OnInit, Contr private entityAliasList: Array = []; constructor(protected store: Store, - private translate: TranslateService, private entityService: EntityService, private fb: UntypedFormBuilder) { super(store); @@ -247,5 +251,4 @@ export class ValueSourceComponent extends PageComponent implements OnInit, Contr this.valueSourceFormGroup.get('attribute').updateValueAndValidity({emitEvent: false}); this.valueSourceFormGroup.get('value').updateValueAndValidity({emitEvent: false}); } - } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index bbe3dfb20c..781042a4cf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts @@ -49,7 +49,8 @@ import { import { ColorRangeListComponent } from '@home/components/widget/lib/settings/common/color-range-list.component'; import { ColorRangePanelComponent } from '@home/components/widget/lib/settings/common/color-range-panel.component'; import { - ColorRangeSettingsComponent, ColorRangeSettingsComponentService + ColorRangeSettingsComponent, + ColorRangeSettingsComponentService } from '@home/components/widget/lib/settings/common/color-range-settings.component'; import { GetValueActionSettingsComponent @@ -149,6 +150,11 @@ import { StatusWidgetStateSettingsComponent } from '@home/components/widget/lib/settings/common/indicator/status-widget-state-settings.component'; import { ChartBarSettingsComponent } from '@home/components/widget/lib/settings/common/chart/chart-bar-settings.component'; +import { AdvancedRangeComponent } from '@home/components/widget/lib/settings/common/advanced-range.component'; +import { GradientComponent } from '@home/components/widget/lib/settings/common/gradient.component'; +import { + ValueSourceDataKeyComponent +} from '@home/components/widget/lib/settings/common/value-source-data-key.component'; @NgModule({ declarations: [ @@ -167,6 +173,7 @@ import { ChartBarSettingsComponent } from '@home/components/widget/lib/settings/ BackgroundSettingsComponent, BackgroundSettingsPanelComponent, ValueSourceComponent, + ValueSourceDataKeyComponent, LegendConfigComponent, WidgetFontComponent, CountWidgetSettingsComponent, @@ -205,7 +212,9 @@ import { ChartBarSettingsComponent } from '@home/components/widget/lib/settings/ TimeSeriesChartGridSettingsComponent, StatusWidgetStateSettingsComponent, DataKeyInputComponent, - EntityAliasInputComponent + EntityAliasInputComponent, + AdvancedRangeComponent, + GradientComponent ], imports: [ CommonModule, @@ -228,6 +237,7 @@ import { ChartBarSettingsComponent } from '@home/components/widget/lib/settings/ BackgroundSettingsComponent, BackgroundSettingsPanelComponent, ValueSourceComponent, + ValueSourceDataKeyComponent, LegendConfigComponent, WidgetFontComponent, CountWidgetSettingsComponent, @@ -266,7 +276,9 @@ import { ChartBarSettingsComponent } from '@home/components/widget/lib/settings/ TimeSeriesChartGridSettingsComponent, StatusWidgetStateSettingsComponent, DataKeyInputComponent, - EntityAliasInputComponent + EntityAliasInputComponent, + AdvancedRangeComponent, + GradientComponent ], providers: [ ColorSettingsComponentService, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.html index e3592e73ad..86cd0ff825 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.html @@ -15,368 +15,298 @@ limitations under the License. --> -
-
- widgets.gauge.common-settings -
- - widgets.gauge.min-value - + +
+
widgets.gauge.gauge-appearance
+ + + {{ digitalGaugeLayoutTranslationMap.get(layout) | translate }} + + + +
+
{{ 'widgets.gauge.donut-start-angle' | translate }}
+ + - - widgets.gauge.max-value - +
+ +
+ + {{ 'widgets.gauge.min-and-max-value' | translate }} + +
+
widgets.gauge.min-value-short
+ + + +
widgets.gauge.max-value-short
+ + + + + + + +
+
+ +
+ + {{ 'widgets.gauge.value' | translate }} + +
+ + + + +
+
+ +
+ + {{ 'widgets.gauge.label' | translate }} + +
+ + + + + + + +
+
+ +
+
widgets.gauge.unit-title-and-timestamp-settings
+
+ + {{ 'widgets.gauge.show-unit-title' | translate }} + + + + +
+ +
+ + {{ 'widgets.gauge.show-timestamp' | translate }} + + +
+ +
+
{{ 'widgets.gauge.font' | translate }}
+
+ + + + +
+
+
+
+ +
+
widgets.gauge.bar-settings
+
+
{{ 'widgets.gauge.relative-bar-width' | translate }}
+ + -
- - widgets.gauge.gauge-type - - - {{ 'widgets.gauge.gauge-type-arc' | translate }} - - - {{ 'widgets.gauge.gauge-type-donut' | translate }} - - - {{ 'widgets.gauge.gauge-type-horizontal-bar' | translate }} - - - {{ 'widgets.gauge.gauge-type-vertical-bar' | translate }} - - - - - widgets.gauge.donut-start-angle - - - - -
-
- widgets.gauge.bar-settings - - widgets.gauge.relative-bar-width - - - - widgets.gauge.neon-glow-brightness - - -
- - widgets.gauge.stripes-thickness - +
+ +
+
{{ 'widgets.gauge.neon-glow-brightness' | translate }}
+ + + +
+ +
+
{{ 'widgets.gauge.stripes-thickness' | translate }}
+ + - +
+ +
+ {{ 'widgets.gauge.rounded-line-cap' | translate }} - - -
- widgets.gauge.bar-color-settings - - - - {{ 'widgets.gauge.use-precise-level-color-values' | translate }} -
- widgets.gauge.bar-colors -
-
-
-
- drag_handle -
- - - -
-
-
- widgets.gauge.no-bar-colors -
-
- +
+ +
+
{{ 'widgets.gauge.default-color' | translate }}
+ + +
+ +
+
{{ 'widgets.gauge.gauge-bar-background' | translate }}
+ + +
+ +
+
{{ 'widgets.gauge.bar-color' | translate }}
+ + +
+ +
+ +
+ + + + + {{ 'widgets.gauge.ticks' | translate }} + + + + widget-config.advanced-settings + + + +
+
{{ 'widgets.gauge.tick-width-and-color' | translate }}
+
+ + + + +
-
-
- widgets.gauge.fixed-level-colors -
+
+
widgets.gauge.tick-values
-
- - + (cdkDropListDropped)="tickValueDrop($event)"> +
+ +
-
- widgets.gauge.no-bar-colors +
+ widgets.gauge.no-tick-values
-
-
-
- -
- widgets.gauge.gauge-title-settings - - - - - {{ 'widgets.gauge.show-gauge-title' | translate }} - - - - widget-config.advanced-settings - - - -
- - widgets.gauge.gauge-title - - -
- widgets.gauge.gauge-title-font - -
-
-
-
- widgets.gauge.unit-title-and-timestamp-settings -
- - {{ 'widgets.gauge.show-unit-title' | translate }} - - - widgets.gauge.unit-title - - -
-
- - {{ 'widgets.gauge.show-timestamp' | translate }} - - - widgets.gauge.timestamp-format - - -
- - - - widget-config.advanced-settings - - - -
- widgets.gauge.label-font - -
-
-
-
-
- widgets.gauge.value-settings - - - - - {{ 'widgets.gauge.show-value' | translate }} - - - - widget-config.advanced-settings - - - -
- widgets.gauge.value-font - -
-
-
-
-
- widgets.gauge.min-max-settings - - - - - {{ 'widgets.gauge.show-min-max' | translate }} - - - - widget-config.advanced-settings - - - -
- widgets.gauge.min-max-font - -
-
-
-
-
- widgets.gauge.ticks-settings - - - - - {{ 'widgets.gauge.show-ticks' | translate }} - - - - widget-config.advanced-settings - - - -
- - widgets.gauge.tick-width - - - - -
- widgets.gauge.tick-values -
-
-
+
- - -
-
-
- widgets.gauge.no-tick-values -
-
- -
-
- - - - - -
- widgets.gauge.animation-settings +
- {{ 'widgets.gauge.enable-animation' | translate }} - + widget-config.advanced-settings -
- - widgets.gauge.animation-duration - - - - widgets.gauge.animation-rule - - - {{ 'widgets.gauge.animation-linear' | translate }} - - - {{ 'widgets.gauge.animation-quad' | translate }} - - - {{ 'widgets.gauge.animation-quint' | translate }} - - - {{ 'widgets.gauge.animation-cycle' | translate }} - - - {{ 'widgets.gauge.animation-bounce' | translate }} - - - {{ 'widgets.gauge.animation-elastic' | translate }} - - - {{ 'widgets.gauge.animation-dequad' | translate }} - - - {{ 'widgets.gauge.animation-dequint' | translate }} - - - {{ 'widgets.gauge.animation-decycle' | translate }} - - - {{ 'widgets.gauge.animation-debounce' | translate }} - - - {{ 'widgets.gauge.animation-delastic' | translate }} - - - -
+
+
widgets.gauge.animation-duration-rule
+
+ + + + + + + {{ 'widgets.gauge.animation-linear' | translate }} + + + {{ 'widgets.gauge.animation-quad' | translate }} + + + {{ 'widgets.gauge.animation-quint' | translate }} + + + {{ 'widgets.gauge.animation-cycle' | translate }} + + + {{ 'widgets.gauge.animation-bounce' | translate }} + + + {{ 'widgets.gauge.animation-elastic' | translate }} + + + {{ 'widgets.gauge.animation-dequad' | translate }} + + + {{ 'widgets.gauge.animation-dequint' | translate }} + + + {{ 'widgets.gauge.animation-decycle' | translate }} + + + {{ 'widgets.gauge.animation-debounce' | translate }} + + + {{ 'widgets.gauge.animation-delastic' | translate }} + + + +
+
-
- +
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts index 77cdf367bb..579e01b830 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { Datasource, WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; import { Component } from '@angular/core'; import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; @@ -22,10 +22,22 @@ import { AppState } from '@core/core.state'; import { GaugeType } from '@home/components/widget/lib/canvas-digital-gauge'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { - FixedColorLevel, - fixedColorLevelValidator -} from '@home/components/widget/lib/settings/gauge/fixed-color-level.component'; -import { ValueSourceProperty } from '@home/components/widget/lib/settings/common/value-source.component'; + backwardCompatibilityFixedLevelColors, + backwardCompatibilityTicks, + digitalGaugeLayoutImages, + digitalGaugeLayouts, + digitalGaugeLayoutTranslations, + DigitalGaugeType +} from '@home/components/widget/lib/digital-gauge.models'; +import { formatValue } from '@core/utils'; +import { + ColorSettings, + ColorType, + constantColor, + simpleDateFormat, + ValueSourceDataKeyType, + ValueSourceWithDataKey +} from '@shared/models/widget-settings.models'; @Component({ selector: 'tb-digital-gauge-widget-settings', @@ -34,8 +46,26 @@ import { ValueSourceProperty } from '@home/components/widget/lib/settings/common }) export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent { + digitalGaugeType = DigitalGaugeType; + digitalGaugeLayouts = digitalGaugeLayouts; + + digitalGaugeLayoutTranslationMap = digitalGaugeLayoutTranslations; + digitalGaugeLayoutImageMap = digitalGaugeLayoutImages; + digitalGaugeWidgetSettingsForm: UntypedFormGroup; + valuePreviewFn = this._valuePreviewFn.bind(this, true); + previewFn = this._valuePreviewFn.bind(this, false); + + public get datasource(): Datasource { + const datasources: Datasource[] = this.widgetConfig.config.datasources; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + constructor(protected store: Store, protected fb: UntypedFormBuilder) { super(store); @@ -107,63 +137,101 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent } protected onSettingsSet(settings: WidgetSettings) { + if (!settings.barColor) { + settings.barColor = constantColor(settings.gaugeColor); + + if (settings.fixedLevelColors.length) { + settings.barColor.rangeList = { + advancedMode: settings.useFixedLevelColor, + range: null, + rangeAdvanced: backwardCompatibilityFixedLevelColors(settings.fixedLevelColors) + }; + } + if (settings.levelColors.length) { + settings.barColor.gradient = { + advancedMode: false, + gradient: settings.levelColors, + gradientAdvanced: null + }; + } + if (settings.useFixedLevelColor) { + settings.barColor.type = ColorType.range; + } else if (settings.levelColors.length) { + settings.barColor.type = ColorType.gradient; + } + } + this.digitalGaugeWidgetSettingsForm = this.fb.group({ - // Common gauge settings - minValue: [settings.minValue, []], - maxValue: [settings.maxValue, []], gaugeType: [settings.gaugeType, []], donutStartAngle: [settings.donutStartAngle, []], - defaultColor: [settings.defaultColor, []], - - // Gauge bar settings - gaugeWidthScale: [settings.gaugeWidthScale, [Validators.min(0)]], - neonGlowBrightness: [settings.neonGlowBrightness, [Validators.min(0), Validators.max(100)]], - dashThickness: [settings.dashThickness, [Validators.min(0)]], - roundedLineCap: [settings.roundedLineCap, []], + showMinMax: [settings.showMinMax, []], + minValue: [settings.minValue, []], + maxValue: [settings.maxValue, []], + minMaxFont: [settings.minMaxFont, []], + minMaxColor: [settings.minMaxFont.color, []], - // Gauge bar colors settings - gaugeColor: [settings.gaugeColor, []], - useFixedLevelColor: [settings.useFixedLevelColor, []], - levelColors: this.prepareLevelColorFormArray(settings.levelColors), - fixedLevelColors: this.prepareFixedLevelColorFormArray(settings.fixedLevelColors), + showValue: [settings.showValue, []], + valueFont: [settings.valueFont, []], + valueColor: [settings.valueFont.color, []], - // Title settings showTitle: [settings.showTitle, []], title: [settings.title, []], titleFont: [settings.titleFont, []], + titleColor: [settings.titleFont.color, []], - // Unit title/timestamp settings showUnitTitle: [settings.showUnitTitle, []], unitTitle: [settings.unitTitle, []], showTimestamp: [settings.showTimestamp, []], - timestampFormat: [settings.timestampFormat, []], + timestampFormat: [simpleDateFormat(settings.timestampFormat), []], labelFont: [settings.labelFont, []], + labelColor: [settings.labelFont.color, []], - // Value settings - showValue: [settings.showValue, []], - valueFont: [settings.valueFont, []], + gaugeWidthScale: [settings.gaugeWidthScale, [Validators.min(0)]], + neonGlowBrightness: [settings.neonGlowBrightness, [Validators.min(0), Validators.max(100)]], + dashThickness: [settings.dashThickness, [Validators.min(0)]], + roundedLineCap: [settings.roundedLineCap, []], - // Min/max labels settings - showMinMax: [settings.showMinMax, []], - minMaxFont: [settings.minMaxFont, []], + defaultColor: [settings.defaultColor, []], + gaugeColor: [settings.gaugeColor, []], + barColor: [settings.barColor], - // Ticks settings showTicks: [settings.showTicks, []], tickWidth: [settings.tickWidth, [Validators.min(0)]], colorTicks: [settings.colorTicks, []], - ticksValue: this.prepareTicksValueFormArray(settings.ticksValue), + ticksValue: this.prepareTicksValueFormArray(backwardCompatibilityTicks(settings.ticksValue)), - // Animation settings animation: [settings.animation, []], animationDuration: [settings.animationDuration, [Validators.min(0)]], animationRule: [settings.animationRule, []] - }); } + protected prepareOutputSettings(settings) { + + const barColor: ColorSettings = this.digitalGaugeWidgetSettingsForm.get('barColor').value; + + if (barColor.type === ColorType.range) { + settings.useFixedLevelColor = true; + settings.fixedLevelColors = barColor.rangeList.advancedMode ? barColor.rangeList.rangeAdvanced : barColor.rangeList.range; + } else { + settings.useFixedLevelColor = false; + } + if (barColor.gradient?.gradient?.length) { + settings.levelColors = barColor.gradient.gradient; + } + settings.barColor = this.digitalGaugeWidgetSettingsForm.get('barColor').value; + settings.timestampFormat = this.digitalGaugeWidgetSettingsForm.get('timestampFormat').value.format; + settings.minMaxFont.color = this.digitalGaugeWidgetSettingsForm.get('minMaxColor').value; + settings.valueFont.color = this.digitalGaugeWidgetSettingsForm.get('valueColor').value; + settings.titleFont.color = this.digitalGaugeWidgetSettingsForm.get('titleColor').value; + settings.labelFont.color = this.digitalGaugeWidgetSettingsForm.get('labelColor').value; + + return settings; + } + protected validatorTriggers(): string[] { - return ['gaugeType', 'showTitle', 'showUnitTitle', 'showValue', 'showMinMax', 'showTimestamp', 'useFixedLevelColor', 'showTicks', 'animation']; + return ['gaugeType', 'showTitle', 'showUnitTitle', 'showValue', 'showMinMax', 'showTimestamp', 'showTicks', 'animation']; } protected updateValidators(emitEvent: boolean) { @@ -173,21 +241,43 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent const showValue: boolean = this.digitalGaugeWidgetSettingsForm.get('showValue').value; const showMinMax: boolean = this.digitalGaugeWidgetSettingsForm.get('showMinMax').value; const showTimestamp: boolean = this.digitalGaugeWidgetSettingsForm.get('showTimestamp').value; - const useFixedLevelColor: boolean = this.digitalGaugeWidgetSettingsForm.get('useFixedLevelColor').value; const showTicks: boolean = this.digitalGaugeWidgetSettingsForm.get('showTicks').value; const animation: boolean = this.digitalGaugeWidgetSettingsForm.get('animation').value; + if (gaugeType === 'donut') { this.digitalGaugeWidgetSettingsForm.get('donutStartAngle').enable(); + + this.digitalGaugeWidgetSettingsForm.get('showMinMax').disable({emitEvent: false}); + this.digitalGaugeWidgetSettingsForm.get('minValue').enable({emitEvent: false}); + this.digitalGaugeWidgetSettingsForm.get('maxValue').enable({emitEvent: false}); + this.digitalGaugeWidgetSettingsForm.get('minMaxFont').disable({emitEvent: false}); + this.digitalGaugeWidgetSettingsForm.get('minMaxColor').disable({emitEvent: false}); } else { this.digitalGaugeWidgetSettingsForm.get('donutStartAngle').disable(); + + this.digitalGaugeWidgetSettingsForm.get('showMinMax').enable({emitEvent: false}); + if (showMinMax) { + this.digitalGaugeWidgetSettingsForm.get('minValue').enable(); + this.digitalGaugeWidgetSettingsForm.get('maxValue').enable(); + this.digitalGaugeWidgetSettingsForm.get('minMaxFont').enable(); + this.digitalGaugeWidgetSettingsForm.get('minMaxColor').enable(); + } else { + this.digitalGaugeWidgetSettingsForm.get('minValue').disable(); + this.digitalGaugeWidgetSettingsForm.get('maxValue').disable(); + this.digitalGaugeWidgetSettingsForm.get('minMaxFont').disable(); + this.digitalGaugeWidgetSettingsForm.get('minMaxColor').disable(); + } } + if (showTitle) { this.digitalGaugeWidgetSettingsForm.get('title').enable(); this.digitalGaugeWidgetSettingsForm.get('titleFont').enable(); + this.digitalGaugeWidgetSettingsForm.get('titleColor').enable(); } else { this.digitalGaugeWidgetSettingsForm.get('title').disable(); this.digitalGaugeWidgetSettingsForm.get('titleFont').disable(); + this.digitalGaugeWidgetSettingsForm.get('titleColor').disable(); } if (showUnitTitle) { this.digitalGaugeWidgetSettingsForm.get('unitTitle').enable(); @@ -201,25 +291,17 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent } if (showUnitTitle || showTimestamp) { this.digitalGaugeWidgetSettingsForm.get('labelFont').enable(); + this.digitalGaugeWidgetSettingsForm.get('labelColor').enable(); } else { this.digitalGaugeWidgetSettingsForm.get('labelFont').disable(); + this.digitalGaugeWidgetSettingsForm.get('labelColor').disable(); } if (showValue) { this.digitalGaugeWidgetSettingsForm.get('valueFont').enable(); + this.digitalGaugeWidgetSettingsForm.get('valueColor').enable(); } else { this.digitalGaugeWidgetSettingsForm.get('valueFont').disable(); - } - if (showMinMax) { - this.digitalGaugeWidgetSettingsForm.get('minMaxFont').enable(); - } else { - this.digitalGaugeWidgetSettingsForm.get('minMaxFont').disable(); - } - if (useFixedLevelColor) { - this.digitalGaugeWidgetSettingsForm.get('fixedLevelColors').enable(); - this.digitalGaugeWidgetSettingsForm.get('levelColors').disable(); - } else { - this.digitalGaugeWidgetSettingsForm.get('fixedLevelColors').disable(); - this.digitalGaugeWidgetSettingsForm.get('levelColors').enable(); + this.digitalGaugeWidgetSettingsForm.get('valueColor').disable(); } if (showTicks) { this.digitalGaugeWidgetSettingsForm.get('tickWidth').enable(); @@ -240,13 +322,21 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent this.digitalGaugeWidgetSettingsForm.get('donutStartAngle').updateValueAndValidity({emitEvent}); this.digitalGaugeWidgetSettingsForm.get('title').updateValueAndValidity({emitEvent}); this.digitalGaugeWidgetSettingsForm.get('titleFont').updateValueAndValidity({emitEvent}); + this.digitalGaugeWidgetSettingsForm.get('titleColor').updateValueAndValidity({emitEvent}); + this.digitalGaugeWidgetSettingsForm.get('unitTitle').updateValueAndValidity({emitEvent}); this.digitalGaugeWidgetSettingsForm.get('timestampFormat').updateValueAndValidity({emitEvent}); this.digitalGaugeWidgetSettingsForm.get('labelFont').updateValueAndValidity({emitEvent}); + this.digitalGaugeWidgetSettingsForm.get('labelColor').updateValueAndValidity({emitEvent}); + this.digitalGaugeWidgetSettingsForm.get('valueFont').updateValueAndValidity({emitEvent}); + this.digitalGaugeWidgetSettingsForm.get('valueColor').updateValueAndValidity({emitEvent}); + + this.digitalGaugeWidgetSettingsForm.get('minValue').updateValueAndValidity({emitEvent}); + this.digitalGaugeWidgetSettingsForm.get('maxValue').updateValueAndValidity({emitEvent}); this.digitalGaugeWidgetSettingsForm.get('minMaxFont').updateValueAndValidity({emitEvent}); - this.digitalGaugeWidgetSettingsForm.get('fixedLevelColors').updateValueAndValidity({emitEvent}); - this.digitalGaugeWidgetSettingsForm.get('levelColors').updateValueAndValidity({emitEvent}); + this.digitalGaugeWidgetSettingsForm.get('minMaxColor').updateValueAndValidity({emitEvent}); + this.digitalGaugeWidgetSettingsForm.get('tickWidth').updateValueAndValidity({emitEvent}); this.digitalGaugeWidgetSettingsForm.get('colorTicks').updateValueAndValidity({emitEvent}); this.digitalGaugeWidgetSettingsForm.get('ticksValue').updateValueAndValidity({emitEvent}); @@ -255,32 +345,10 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent } protected doUpdateSettings(settingsForm: UntypedFormGroup, settings: WidgetSettings) { - settingsForm.setControl('levelColors', this.prepareLevelColorFormArray(settings.levelColors), {emitEvent: false}); - settingsForm.setControl('fixedLevelColors', this.prepareFixedLevelColorFormArray(settings.fixedLevelColors), {emitEvent: false}); settingsForm.setControl('ticksValue', this.prepareTicksValueFormArray(settings.ticksValue), {emitEvent: false}); } - private prepareLevelColorFormArray(levelColors: string[] | undefined): UntypedFormArray { - const levelColorsControls: Array = []; - if (levelColors) { - levelColors.forEach((levelColor) => { - levelColorsControls.push(this.fb.control(levelColor, [Validators.required])); - }); - } - return this.fb.array(levelColorsControls); - } - - private prepareFixedLevelColorFormArray(fixedLevelColors: FixedColorLevel[] | undefined): UntypedFormArray { - const fixedLevelColorsControls: Array = []; - if (fixedLevelColors) { - fixedLevelColors.forEach((fixedLevelColor) => { - fixedLevelColorsControls.push(this.fb.control(fixedLevelColor, [fixedColorLevelValidator])); - }); - } - return this.fb.array(fixedLevelColorsControls); - } - - private prepareTicksValueFormArray(ticksValue: ValueSourceProperty[] | undefined): UntypedFormArray { + private prepareTicksValueFormArray(ticksValue: ValueSourceWithDataKey[] | undefined): UntypedFormArray { const ticksValueControls: Array = []; if (ticksValue) { ticksValue.forEach((tickValue) => { @@ -290,71 +358,6 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent return this.fb.array(ticksValueControls); } - levelColorsFormArray(): UntypedFormArray { - return this.digitalGaugeWidgetSettingsForm.get('levelColors') as UntypedFormArray; - } - - public trackByLevelColor(index: number, levelColorControl: AbstractControl): any { - return levelColorControl; - } - - public removeLevelColor(index: number) { - (this.digitalGaugeWidgetSettingsForm.get('levelColors') as UntypedFormArray).removeAt(index); - } - - public addLevelColor() { - const levelColorsArray = this.digitalGaugeWidgetSettingsForm.get('levelColors') as UntypedFormArray; - const levelColorControl = this.fb.control(null, []); - levelColorsArray.push(levelColorControl); - this.digitalGaugeWidgetSettingsForm.updateValueAndValidity(); - } - - levelColorDrop(event: CdkDragDrop) { - const levelColorsArray = this.digitalGaugeWidgetSettingsForm.get('levelColors') as UntypedFormArray; - const levelColor = levelColorsArray.at(event.previousIndex); - levelColorsArray.removeAt(event.previousIndex); - levelColorsArray.insert(event.currentIndex, levelColor); - } - - fixedLevelColorFormArray(): UntypedFormArray { - return this.digitalGaugeWidgetSettingsForm.get('fixedLevelColors') as UntypedFormArray; - } - - public trackByFixedLevelColor(index: number, fixedLevelColorControl: AbstractControl): any { - return fixedLevelColorControl; - } - - public removeFixedLevelColor(index: number) { - (this.digitalGaugeWidgetSettingsForm.get('fixedLevelColors') as UntypedFormArray).removeAt(index); - } - - public addFixedLevelColor() { - const fixedLevelColor: FixedColorLevel = { - from: { - valueSource: 'predefinedValue' - }, - to: { - valueSource: 'predefinedValue' - }, - color: null - }; - const fixedLevelColorsArray = this.digitalGaugeWidgetSettingsForm.get('fixedLevelColors') as UntypedFormArray; - const fixedLevelColorControl = this.fb.control(fixedLevelColor, [fixedColorLevelValidator]); - (fixedLevelColorControl as any).new = true; - fixedLevelColorsArray.push(fixedLevelColorControl); - this.digitalGaugeWidgetSettingsForm.updateValueAndValidity(); - if (!this.digitalGaugeWidgetSettingsForm.valid) { - this.onSettingsChanged(this.digitalGaugeWidgetSettingsForm.value); - } - } - - fixedLevelColorDrop(event: CdkDragDrop) { - const fixedLevelColorsArray = this.digitalGaugeWidgetSettingsForm.get('fixedLevelColors') as UntypedFormArray; - const fixedLevelColor = fixedLevelColorsArray.at(event.previousIndex); - fixedLevelColorsArray.removeAt(event.previousIndex); - fixedLevelColorsArray.insert(event.currentIndex, fixedLevelColor); - } - tickValuesFormArray(): UntypedFormArray { return this.digitalGaugeWidgetSettingsForm.get('ticksValue') as UntypedFormArray; } @@ -368,8 +371,8 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent } public addTickValue() { - const tickValue: ValueSourceProperty = { - valueSource: 'predefinedValue' + const tickValue: ValueSourceWithDataKey = { + type: ValueSourceDataKeyType.constant }; const tickValuesArray = this.digitalGaugeWidgetSettingsForm.get('ticksValue') as UntypedFormArray; const tickValueControl = this.fb.control(tickValue, []); @@ -385,4 +388,7 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent tickValuesArray.insert(event.currentIndex, tickValue); } + private _valuePreviewFn(units: boolean): string { + return formatValue(22, 0, units ? this.widget.config.units : null, true); + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.html deleted file mode 100644 index 30008112ba..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.html +++ /dev/null @@ -1,60 +0,0 @@ - - - -
- -
-
{{ fixedColorLevelRangeText() }}
-
-
-
-
-
- - -
-
- -
- -
-
- widgets.gauge.from - -
-
- widgets.gauge.to - -
- - -
-
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.ts deleted file mode 100644 index b1464e7e8a..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.ts +++ /dev/null @@ -1,147 +0,0 @@ -/// -/// Copyright © 2016-2024 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 { ValueSourceProperty } from '@home/components/widget/lib/settings/common/value-source.component'; -import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; -import { - AbstractControl, - ControlValueAccessor, - UntypedFormBuilder, - UntypedFormGroup, - NG_VALUE_ACCESSOR, ValidationErrors, - 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 { isNumber } from '@core/utils'; -import { IAliasController } from '@core/api/widget-api.models'; - -export interface FixedColorLevel { - from?: ValueSourceProperty; - to?: ValueSourceProperty; - color: string; -} - -export function fixedColorLevelValidator(control: AbstractControl): ValidationErrors | null { - const fixedColorLevel: FixedColorLevel = control.value; - if (!fixedColorLevel || !fixedColorLevel.color) { - return { - fixedColorLevel: true - }; - } - return null; -} - -@Component({ - selector: 'tb-fixed-color-level', - templateUrl: './fixed-color-level.component.html', - styleUrls: ['./fixed-color-level.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => FixedColorLevelComponent), - multi: true - } - ] -}) -export class FixedColorLevelComponent extends PageComponent implements OnInit, ControlValueAccessor { - - @Input() - disabled: boolean; - - @Input() - expanded = false; - - @Input() - aliasController: IAliasController; - - @Output() - removeFixedColorLevel = new EventEmitter(); - - private modelValue: FixedColorLevel; - - private propagateChange = null; - - public fixedColorLevelFormGroup: UntypedFormGroup; - - constructor(protected store: Store, - private translate: TranslateService, - private fb: UntypedFormBuilder) { - super(store); - } - - ngOnInit(): void { - this.fixedColorLevelFormGroup = this.fb.group({ - from: [null, []], - to: [null, []], - color: [null, [Validators.required]] - }); - this.fixedColorLevelFormGroup.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.fixedColorLevelFormGroup.disable({emitEvent: false}); - } else { - this.fixedColorLevelFormGroup.enable({emitEvent: false}); - } - } - - writeValue(value: FixedColorLevel): void { - this.modelValue = value; - this.fixedColorLevelFormGroup.patchValue( - value, {emitEvent: false} - ); - } - - fixedColorLevelRangeText(): string { - const value: FixedColorLevel = this.fixedColorLevelFormGroup.value; - const from = this.valueSourcePropertyText(value?.from); - const to = this.valueSourcePropertyText(value?.to); - return `${from} - ${to}`; - } - - private valueSourcePropertyText(source?: ValueSourceProperty): string { - if (source) { - if (source.valueSource === 'predefinedValue') { - return `${isNumber(source.value) ? source.value : 0}`; - } else if (source.valueSource === 'entityAttribute') { - const alias = source.entityAlias || 'Undefined'; - const key = source.attribute || 'Undefined'; - return `${alias}.${key}`; - } - } - return 'Undefined'; - } - - private updateModel() { - const value: FixedColorLevel = this.fixedColorLevelFormGroup.value; - this.modelValue = value; - this.propagateChange(this.modelValue); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.html index c91880c432..7155ad8d5e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.html @@ -15,30 +15,20 @@ limitations under the License. --> - - -
- -
-
{{ tickValueText() }}
-
-
- - -
-
- -
- -
- -
-
-
-
+ +
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts index 9a7bb98c1c..fcbeded87d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts @@ -16,18 +16,19 @@ import { ValueSourceProperty } from '@home/components/widget/lib/settings/common/value-source.component'; import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; -import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } 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 { isNumber } from '@core/utils'; import { IAliasController } from '@core/api/widget-api.models'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { Datasource } from '@shared/models/widget.models'; @Component({ selector: 'tb-tick-value', templateUrl: './tick-value.component.html', - styleUrls: ['./tick-value.component.scss'], + styleUrls: [], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -42,10 +43,13 @@ export class TickValueComponent extends PageComponent implements OnInit, Control disabled: boolean; @Input() - expanded = false; + aliasController: IAliasController; @Input() - aliasController: IAliasController; + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; @Output() removeTickValue = new EventEmitter(); @@ -94,24 +98,6 @@ export class TickValueComponent extends PageComponent implements OnInit, Control ); } - tickValueText(): string { - const value: ValueSourceProperty = this.tickValueFormGroup.get('tickValue').value; - return this.valueSourcePropertyText(value); - } - - private valueSourcePropertyText(source?: ValueSourceProperty): string { - if (source) { - if (source.valueSource === 'predefinedValue') { - return `${isNumber(source.value) ? source.value : 0}`; - } else if (source.valueSource === 'entityAttribute') { - const alias = source.entityAlias || 'Undefined'; - const key = source.attribute || 'Undefined'; - return `${alias}.${key}`; - } - } - return 'Undefined'; - } - private updateModel() { const value: ValueSourceProperty = this.tickValueFormGroup.get('tickValue').value; this.modelValue = value; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.html index ed79ec1820..2903cbd456 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.html @@ -48,18 +48,40 @@ [autoScale]="batteryLevelWidgetSettingsForm.get('autoScaleValueSize').value" [previewText]="valuePreviewFn"> - +
{{ 'widgets.battery-level.battery-level-color' | translate }}
- +
{{ 'widgets.battery-level.battery-shape-color' | translate }}
- +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.ts index fb0e2b540c..2b27a40eff 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.ts @@ -15,7 +15,7 @@ /// import { Component, Injector } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { Datasource, WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -48,6 +48,15 @@ export class BatteryLevelWidgetSettingsComponent extends WidgetSettingsComponent return [BatteryLevelLayout.vertical_divided, BatteryLevelLayout.horizontal_divided].includes(layout); } + public get datasource(): Datasource { + const datasources: Datasource[] = this.widgetConfig.config.datasources; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + constructor(protected store: Store, private $injector: Injector, private fb: UntypedFormBuilder) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index f093c4c568..461c92bfea 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -74,7 +74,6 @@ import { import { DigitalGaugeWidgetSettingsComponent } from '@home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component'; -import { FixedColorLevelComponent } from '@home/components/widget/lib/settings/gauge/fixed-color-level.component'; import { TickValueComponent } from '@home/components/widget/lib/settings/gauge/tick-value.component'; import { FlotWidgetSettingsComponent } from '@home/components/widget/lib/settings/chart/flot-widget-settings.component'; import { @@ -380,7 +379,6 @@ import { AnalogueLinearGaugeWidgetSettingsComponent, AnalogueCompassWidgetSettingsComponent, DigitalGaugeWidgetSettingsComponent, - FixedColorLevelComponent, TickValueComponent, FlotWidgetSettingsComponent, LabelDataKeyComponent, @@ -513,7 +511,6 @@ import { AnalogueLinearGaugeWidgetSettingsComponent, AnalogueCompassWidgetSettingsComponent, DigitalGaugeWidgetSettingsComponent, - FixedColorLevelComponent, TickValueComponent, FlotWidgetSettingsComponent, LabelDataKeyComponent, diff --git a/ui-ngx/src/app/shared/components/color-input.component.html b/ui-ngx/src/app/shared/components/color-input.component.html index 29df75d8d6..d57824b443 100644 --- a/ui-ngx/src/app/shared/components/color-input.component.html +++ b/ui-ngx/src/app/shared/components/color-input.component.html @@ -38,6 +38,7 @@