diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json index a66975d1e2..b7b1d643e3 100644 --- a/application/src/main/data/json/system/widget_bundles/charts.json +++ b/application/src/main/data/json/system/widget_bundles/charts.json @@ -20,6 +20,7 @@ "charts.timeseries_bars_flot", "cards.aggregated_value_card", "charts.bars", + "pie", "charts.pie", "charts.pie_chart_js", "doughnut", diff --git a/application/src/main/data/json/system/widget_types/doughnut.json b/application/src/main/data/json/system/widget_types/doughnut.json index 8764bbcace..3e505938af 100644 --- a/application/src/main/data/json/system/widget_types/doughnut.json +++ b/application/src/main/data/json/system/widget_types/doughnut.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-doughnut-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-doughnut-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind power\",\"color\":\"#08872B\",\"settings\":{},\"_hash\":0.7227918773301678,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar power\",\"color\":\"#FF4D5A\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"totalValueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1\"},\"totalValueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showLegend\":true,\"legendPosition\":\"bottom\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.38)\",\"legendValueFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"20px\"},\"legendValueColor\":\"rgba(0, 0, 0, 0.87)\",\"showTooltip\":true,\"tooltipValueType\":\"percentage\",\"tooltipValueDecimals\":0,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"donut_large\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind power\",\"color\":\"#08872B\",\"settings\":{},\"_hash\":0.7227918773301678,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar power\",\"color\":\"#FF4D5A\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"donut_large\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" }, "externalId": null, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/horizontal_doughnut.json b/application/src/main/data/json/system/widget_types/horizontal_doughnut.json index f76bf50e48..26b00decb9 100644 --- a/application/src/main/data/json/system/widget_types/horizontal_doughnut.json +++ b/application/src/main/data/json/system/widget_types/horizontal_doughnut.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-doughnut-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-doughnut-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind power\",\"color\":\"#08872B\",\"settings\":{},\"_hash\":0.7227918773301678,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar power\",\"color\":\"#FF4D5A\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"totalValueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1\"},\"totalValueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showLegend\":true,\"legendPosition\":\"right\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.38)\",\"legendValueFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"20px\"},\"legendValueColor\":\"rgba(0, 0, 0, 0.87)\",\"showTooltip\":true,\"tooltipValueType\":\"percentage\",\"tooltipValueDecimals\":0,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"donut_large\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind power\",\"color\":\"#08872B\",\"settings\":{},\"_hash\":0.7227918773301678,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar power\",\"color\":\"#FF4D5A\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"donut_large\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" }, "externalId": null, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/pie.json b/application/src/main/data/json/system/widget_types/pie.json new file mode 100644 index 0000000000..4d54a874b5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pie.json @@ -0,0 +1,26 @@ +{ + "fqn": "pie", + "name": "Pie", + "deprecated": false, + "image": "tb-image:cGllX2NoYXJ0LnN2Zw==:IlBpZSIgc3lzdGVtIHdpZGdldCBpbWFnZQ==;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAxIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMSAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2RfNDUzOF8zNTMwNikiPgo8cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgcng9IjIuMzg4MDYiIGZpbGw9IndoaXRlIiBzaGFwZS1yZW5kZXJpbmc9ImNyaXNwRWRnZXMiLz4KPHBhdGggZD0iTTE0OC45NzUgMTI5LjQ5N0MxNTYuNzc0IDEyMS42OTggMTYyLjYyIDExMi4xNjYgMTY2LjAzNiAxMDEuNjc4TDk5LjQ3NzUgODBMMTQ4Ljk3NSAxMjkuNDk3WiIgZmlsbD0iI0ZGREUzMCIgc3Ryb2tlPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNOTkuNDc3NSAxMEM4Ny45NjYgMTAgNzYuNjMyMSAxMi44MzkgNjYuNDc5OCAxOC4yNjU1QzU2LjMyNzUgMjMuNjkyIDQ3LjY3MDIgMzEuNTM4NiA0MS4yNzQ3IDQxLjExMDFDMzQuODc5MiA1MC42ODE2IDMwLjk0MjkgNjEuNjgyNyAyOS44MTQ2IDczLjEzODhDMjguNjg2MyA4NC41OTQ5IDMwLjQwMDcgOTYuMTUyNSAzNC44MDYgMTA2Ljc4OEMzOS4yMTEzIDExNy40MjMgNDYuMTcxNCAxMjYuODA4IDU1LjA3IDEzNC4xMTFDNjMuOTY4NiAxNDEuNDE0IDc0LjUzMDggMTQ2LjQwOSA4NS44MjEyIDE0OC42NTVDOTcuMTExNiAxNTAuOTAxIDEwOC43ODIgMTUwLjMyNyAxMTkuNzk3IDE0Ni45ODZDMTMwLjgxMyAxNDMuNjQ0IDE0MC44MzUgMTM3LjYzNyAxNDguOTc1IDEyOS40OTdMOTkuNDc3NSA4MFYxMFoiIGZpbGw9IiMwODg3MkIiIHN0cm9rZT0id2hpdGUiLz4KPGcgZmlsdGVyPSJ1cmwoI2ZpbHRlcjFfZF80NTM4XzM1MzA2KSI+CjxwYXRoIGQ9Ik0xNjcuNTYyIDEwMi4wNTdDMTcxLjAxNCA5MS4yNTc1IDE3MS44ODIgNzkuNzg5OCAxNzAuMDk2IDY4LjU4ODVDMTY4LjMxMSA1Ny4zODcxIDE2My45MjIgNDYuNzY5MiAxNTcuMjg4IDM3LjYwMDJDMTUwLjY1MyAyOC40MzEyIDE0MS45NjEgMjAuOTcwNiAxMzEuOTIgMTUuODI2NkMxMjEuODc5IDEwLjY4MjYgMTEwLjc3MiA4LjAwMDc4IDk5LjUwNjQgNy45OTk3Nkw5OS41IDc5Ljk5OThMMTY3LjU2MiAxMDIuMDU3WiIgZmlsbD0iI0ZGNEQ1QSIvPgo8cGF0aCBkPSJNMTY3LjU2MiAxMDIuMDU3QzE3MS4wMTQgOTEuMjU3NSAxNzEuODgyIDc5Ljc4OTggMTcwLjA5NiA2OC41ODg1QzE2OC4zMTEgNTcuMzg3MSAxNjMuOTIyIDQ2Ljc2OTIgMTU3LjI4OCAzNy42MDAyQzE1MC42NTMgMjguNDMxMiAxNDEuOTYxIDIwLjk3MDYgMTMxLjkyIDE1LjgyNjZDMTIxLjg3OSAxMC42ODI2IDExMC43NzIgOC4wMDA3OCA5OS41MDY0IDcuOTk5NzZMOTkuNSA3OS45OTk4TDE2Ny41NjIgMTAyLjA1N1oiIHN0cm9rZT0id2hpdGUiLz4KPC9nPgo8L2c+CjxkZWZzPgo8ZmlsdGVyIGlkPSJmaWx0ZXIwX2RfNDUzOF8zNTMwNiIgeD0iLTcuMTY0MTgiIHk9Ii00Ljc3NjEyIiB3aWR0aD0iMjE0LjMyOCIgaGVpZ2h0PSIxNzQuMzI4IiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+CjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIiByZXN1bHQ9ImhhcmRBbHBoYSIvPgo8ZmVPZmZzZXQgZHk9IjIuMzg4MDYiLz4KPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMy41ODIwOSIvPgo8ZmVDb21wb3NpdGUgaW4yPSJoYXJkQWxwaGEiIG9wZXJhdG9yPSJvdXQiLz4KPGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAuMDYgMCIvPgo8ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvd180NTM4XzM1MzA2Ii8+CjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9kcm9wU2hhZG93XzQ1MzhfMzUzMDYiIHJlc3VsdD0ic2hhcGUiLz4KPC9maWx0ZXI+CjxmaWx0ZXIgaWQ9ImZpbHRlcjFfZF80NTM4XzM1MzA2IiB4PSI4NyIgeT0iLTQuNTAwMjQiIHdpZHRoPSI5Ni41IiBoZWlnaHQ9IjExOS4xODciIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KPGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz4KPGZlQ29sb3JNYXRyaXggaW49IlNvdXJjZUFscGhhIiB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMTI3IDAiIHJlc3VsdD0iaGFyZEFscGhhIi8+CjxmZU9mZnNldC8+CjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjYiLz4KPGZlQ29tcG9zaXRlIGluMj0iaGFyZEFscGhhIiBvcGVyYXRvcj0ib3V0Ii8+CjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwLjI1IDAiLz4KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJlZmZlY3QxX2Ryb3BTaGFkb3dfNDUzOF8zNTMwNiIvPgo8ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfZHJvcFNoYWRvd180NTM4XzM1MzA2IiByZXN1bHQ9InNoYXBlIi8+CjwvZmlsdGVyPgo8L2RlZnM+Cjwvc3ZnPgo=", + "description": "Displays the latest values of the attributes or time-series data in a pie chart. Supports numeric values only.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 4, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.pieChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.pieChartWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n previewWidth: '500px',\n previewHeight: '380px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'windPower', label: 'Wind', type: 'timeseries', color: '#08872B' },\n { name: 'solarPower', label: 'Solar', type: 'timeseries', color: '#FF4D5A' },\n { name: 'hydroelectricPower', label: 'Hydroelectric', type: 'timeseries', color: '#FFDE30' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n\nself.actionSources = function() {\n return {\n 'sliceClick': {\n name: 'widget-action.pie-slice-click',\n multiple: false\n }\n };\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-pie-chart-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-pie-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind\",\"color\":\"#08872B\",\"settings\":{},\"_hash\":0.7227918773301678,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar\",\"color\":\"#FF4D5A\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Hydroelectric\",\"color\":\"#FFDE30\",\"settings\":{},\"_hash\":0.7051898468567794,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{},\"title\":\"Pie\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"pie_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "tags": [ + "pie chart", + "pie" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pie___chart_js.json b/application/src/main/data/json/system/widget_types/pie___chart_js.json index f54c4f9702..fcbc2f2020 100644 --- a/application/src/main/data/json/system/widget_types/pie___chart_js.json +++ b/application/src/main/data/json/system/widget_types/pie___chart_js.json @@ -1,8 +1,8 @@ { "fqn": "charts.pie_chart_js", "name": "Pie - Chart.js", - "deprecated": false, - "image": "data:image/png;base64,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", + "deprecated": true, + "image": "tb-image:cGllX2Zsb3Rfc3lzdGVtX3dpZGdldF9pbWFnZS5wbmc=:IlBpZSAtIEZsb3QiIHN5c3RlbSB3aWRnZXQgaW1hZ2U=;data:image/png;base64,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", "description": "Displays the latest values of the attributes or time series data for multiple entities in a pie chart. Supports numeric values only.", "descriptor": { "type": "latest", @@ -21,5 +21,5 @@ "settingsDirective": "tb-chart-widget-settings", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Pie - Chart.js\"}" }, - "externalId": null + "tags": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pie___flot.json b/application/src/main/data/json/system/widget_types/pie___flot.json index a50993639e..428c599e74 100644 --- a/application/src/main/data/json/system/widget_types/pie___flot.json +++ b/application/src/main/data/json/system/widget_types/pie___flot.json @@ -1,8 +1,8 @@ { "fqn": "charts.pie", "name": "Pie - Flot", - "deprecated": false, - "image": "data:image/png;base64,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", + "deprecated": true, + "image": "tb-image:cGllX2Zsb3Rfc3lzdGVtX3dpZGdldF9pbWFnZS5wbmc=:IlBpZSAtIEZsb3QiIHN5c3RlbSB3aWRnZXQgaW1hZ2U=;data:image/png;base64,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", "description": "Displays the latest values of the attributes or time series data for multiple entities in a pie chart. Supports numeric values only.", "descriptor": { "type": "latest", @@ -18,5 +18,5 @@ "dataKeySettingsDirective": "tb-flot-pie-key-settings", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" }, - "externalId": null + "tags": null } \ No newline at end of file diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 3623d93291..608bdaf9ea 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -193,7 +193,7 @@ ui: # Help parameters help: # Base URL for UI help assets - base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.6.3}" + base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.7}" # Database telemetry parameters database: diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.scss b/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.scss index 36d3e276b3..1285b9568c 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.scss @@ -21,6 +21,7 @@ right: 0; bottom: 0; background: #fff; + z-index: 1; } .widget-preview-section { position: absolute; 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 132ca3d487..9e7c91c114 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 @@ -117,6 +117,9 @@ import { import { StatusWidgetBasicConfigComponent } from '@home/components/widget/config/basic/indicator/status-widget-basic-config.component'; +import { + PieChartBasicConfigComponent +} from '@home/components/widget/config/basic/chart/pie-chart-basic-config.component'; @NgModule({ declarations: [ @@ -155,7 +158,8 @@ import { TimeSeriesChartBasicConfigComponent, ComparisonKeyRowComponent, ComparisonKeysTableComponent, - StatusWidgetBasicConfigComponent + StatusWidgetBasicConfigComponent, + PieChartBasicConfigComponent ], imports: [ CommonModule, @@ -196,7 +200,8 @@ import { SliderBasicConfigComponent, ToggleButtonBasicConfigComponent, TimeSeriesChartBasicConfigComponent, - StatusWidgetBasicConfigComponent + StatusWidgetBasicConfigComponent, + PieChartBasicConfigComponent ] }) export class BasicWidgetConfigModule { @@ -231,5 +236,6 @@ export const basicWidgetConfigComponentsMap: {[key: string]: Type - - +
widget-config.card-appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.html index 9d6e9a4bd5..8ba255052d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.html @@ -59,7 +59,7 @@
- {{ 'widgets.doughnut.auto-scale' | translate }} + {{ 'widgets.latest-chart.auto-scale' | translate }}
@@ -99,12 +99,12 @@
- {{ 'widgets.doughnut.clockwise-layout' | translate }} + {{ 'widgets.latest-chart.clockwise-layout' | translate }}
- {{ 'widgets.doughnut.sort-series' | translate }} + {{ 'widgets.latest-chart.sort-series' | translate }}
@@ -194,8 +194,8 @@
- - {{ doughnutTooltipValueTypeTranslationMap.get(type) | translate }} + + {{ latestChartTooltipValueTypeTranslationMap.get(type) | translate }} @@ -229,6 +229,9 @@
+ +
widget-config.card-appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.ts index 99e45d49a7..d2bff75354 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.ts @@ -31,7 +31,7 @@ import { } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { formatValue, isDefinedAndNotNull, isUndefined } from '@core/utils'; +import { formatValue, isDefinedAndNotNull, isUndefined, mergeDeep } from '@core/utils'; import { getTimewindowConfig, setTimewindowConfig @@ -42,12 +42,14 @@ import { doughnutLayoutImages, doughnutLayouts, doughnutLayoutTranslations, - DoughnutTooltipValueType, - doughnutTooltipValueTypes, - doughnutTooltipValueTypeTranslations, DoughnutWidgetSettings, horizontalDoughnutLayoutImages } from '@home/components/widget/lib/chart/doughnut-widget.models'; +import { + LatestChartTooltipValueType, + latestChartTooltipValueTypes, + latestChartTooltipValueTypeTranslations +} from '@home/components/widget/lib/chart/latest-chart.models'; @Component({ selector: 'tb-doughnut-basic-config', @@ -87,9 +89,9 @@ export class DoughnutBasicConfigComponent extends BasicWidgetConfigComponent { legendPositionTranslationMap = legendPositionTranslationMap; - doughnutTooltipValueTypes = doughnutTooltipValueTypes; + latestChartTooltipValueTypes = latestChartTooltipValueTypes; - doughnutTooltipValueTypeTranslationMap = doughnutTooltipValueTypeTranslations; + latestChartTooltipValueTypeTranslationMap = latestChartTooltipValueTypeTranslations; doughnutWidgetConfigForm: UntypedFormGroup; @@ -125,7 +127,8 @@ export class DoughnutBasicConfigComponent extends BasicWidgetConfigComponent { } protected onConfigSet(configData: WidgetConfigComponentData) { - const settings: DoughnutWidgetSettings = {...doughnutDefaultSettings(this.horizontal), ...(configData.config.settings || {})}; + const settings: DoughnutWidgetSettings = mergeDeep({} as DoughnutWidgetSettings, + doughnutDefaultSettings(this.horizontal), configData.config.settings as DoughnutWidgetSettings); this.doughnutWidgetConfigForm = this.fb.group({ timewindowConfig: [getTimewindowConfig(configData.config), []], datasources: [configData.config.datasources, []], @@ -151,6 +154,8 @@ export class DoughnutBasicConfigComponent extends BasicWidgetConfigComponent { units: [configData.config.units, []], decimals: [configData.config.decimals, []], + animation: [settings.animation, []], + showLegend: [settings.showLegend, []], legendPosition: [settings.legendPosition, []], legendLabelFont: [settings.legendLabelFont, []], @@ -202,6 +207,8 @@ export class DoughnutBasicConfigComponent extends BasicWidgetConfigComponent { this.widgetConfig.config.units = config.units; this.widgetConfig.config.decimals = config.decimals; + this.widgetConfig.config.settings.animation = config.animation; + this.widgetConfig.config.settings.showLegend = config.showLegend; this.widgetConfig.config.settings.legendPosition = config.legendPosition; this.widgetConfig.config.settings.legendLabelFont = config.legendLabelFont; @@ -329,9 +336,9 @@ export class DoughnutBasicConfigComponent extends BasicWidgetConfigComponent { } private _tooltipValuePreviewFn(): string { - const tooltipValueType: DoughnutTooltipValueType = this.doughnutWidgetConfigForm.get('tooltipValueType').value; + const tooltipValueType: LatestChartTooltipValueType = this.doughnutWidgetConfigForm.get('tooltipValueType').value; const decimals: number = this.doughnutWidgetConfigForm.get('tooltipValueDecimals').value; - if (tooltipValueType === DoughnutTooltipValueType.percentage) { + if (tooltipValueType === LatestChartTooltipValueType.percentage) { return formatValue(35, decimals, '%', false); } else { const units: string = this.doughnutWidgetConfigForm.get('units').value; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/pie-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/pie-chart-basic-config.component.html new file mode 100644 index 0000000000..b0a5027944 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/pie-chart-basic-config.component.html @@ -0,0 +1,276 @@ + + + + + + + + +
+
widget-config.card-appearance
+
+ + {{ 'widgets.pie-chart.label' | translate }} + +
+ + + + {{ pieChartLabelPositionTranslationMap.get(position) | translate }} + + + + + + + +
+
+
+
{{ 'widgets.pie-chart.border' | translate }}
+
+ + +
px
+
+ + +
+
+
+
{{ 'widgets.pie-chart.radius' | translate }}
+
+ + +
%
+
+
+
+
+ + {{ 'widget-config.card-title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widget-config.card-icon' | translate }} + +
+ + + + +
+
+
+ + {{ 'widgets.latest-chart.clockwise-layout' | translate }} + +
+
+ + {{ 'widgets.latest-chart.sort-series' | translate }} + +
+
+
widget-config.units-short
+ + +
+
+
widget-config.decimals-short
+ + + +
+
+
+ + + + + {{ 'widget-config.legend' | translate }} + + + + +
+
{{ 'legend.position' | translate }}
+ + + + {{ legendPositionTranslationMap.get(pos) | translate }} + + + +
+
+
{{ 'legend.label' | translate }}
+
+ + + + +
+
+
+
{{ 'legend.value' | translate }}
+
+ + + + +
+
+
+
+
+
+ + + + + {{ 'widget-config.tooltip' | translate }} + + + + +
+
{{ 'tooltip.value' | translate }}
+
+ + + + {{ latestChartTooltipValueTypeTranslationMap.get(type) | translate }} + + + + + +
widget-config.decimals-suffix
+
+ + + + +
+
+
+
{{ 'tooltip.background-color' | translate }}
+ + +
+
+
{{ 'tooltip.background-blur' | translate }}
+ + +
px
+
+
+
+
+
+ + +
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | 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/chart/pie-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/pie-chart-basic-config.component.ts new file mode 100644 index 0000000000..7e0dc8d993 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/pie-chart-basic-config.component.ts @@ -0,0 +1,343 @@ +/// +/// 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 } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } 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 { + DataKey, + Datasource, + datasourcesHasAggregation, + datasourcesHasOnlyComparisonAggregation, + legendPositions, + legendPositionTranslationMap, + WidgetConfig +} from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { formatValue, isUndefined, mergeDeep } from '@core/utils'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; +import { + LatestChartTooltipValueType, + latestChartTooltipValueTypes, + latestChartTooltipValueTypeTranslations +} from '@home/components/widget/lib/chart/latest-chart.models'; +import { + pieChartWidgetDefaultSettings, + PieChartWidgetSettings +} from '@home/components/widget/lib/chart/pie-chart-widget.models'; +import { + pieChartLabelPositions, + pieChartLabelPositionTranslations +} from '@home/components/widget/lib/chart/pie-chart.models'; + +@Component({ + selector: 'tb-pie-chart-basic-config', + templateUrl: './pie-chart-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class PieChartBasicConfigComponent extends BasicWidgetConfigComponent { + + public get datasource(): Datasource { + const datasources: Datasource[] = this.pieChartWidgetConfigForm.get('datasources').value; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + public get displayTimewindowConfig(): boolean { + const datasources = this.pieChartWidgetConfigForm.get('datasources').value; + return datasourcesHasAggregation(datasources); + } + + public onlyHistoryTimewindow(): boolean { + const datasources = this.pieChartWidgetConfigForm.get('datasources').value; + return datasourcesHasOnlyComparisonAggregation(datasources); + } + + pieChartLabelPositions = pieChartLabelPositions; + + pieChartLabelPositionTranslationMap = pieChartLabelPositionTranslations; + + legendPositions = legendPositions; + + legendPositionTranslationMap = legendPositionTranslationMap; + + latestChartTooltipValueTypes = latestChartTooltipValueTypes; + + latestChartTooltipValueTypeTranslationMap = latestChartTooltipValueTypeTranslations; + + pieChartWidgetConfigForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.pieChartWidgetConfigForm; + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [{ name: 'windPower', label: 'Wind', type: DataKeyType.timeseries, units: '', decimals: 0, color: '#08872B' }, + { name: 'solarPower', label: 'Solar', type: DataKeyType.timeseries, units: '', decimals: 0, color: '#FF4D5A' }, + { name: 'hydroelectricPower', label: 'Hydroelectric', type: DataKeyType.timeseries, units: '', decimals: 0, color: '#FFDE30' }]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: PieChartWidgetSettings = mergeDeep({} as PieChartWidgetSettings, + pieChartWidgetDefaultSettings, configData.config.settings as PieChartWidgetSettings); + this.pieChartWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + + series: [this.getSeries(configData.config.datasources), []], + + showLabel: [settings.showLabel, []], + labelPosition: [settings.labelPosition, []], + labelFont: [settings.labelFont, []], + labelColor: [settings.labelColor, []], + + borderWidth: [settings.borderWidth, [Validators.min(0)]], + borderColor: [settings.borderColor, []], + + radius: [settings.radius, [Validators.min(0), Validators.max(100)]], + + clockwise: [settings.clockwise, []], + sortSeries: [settings.sortSeries, []], + + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + showTitleIcon: [configData.config.showTitleIcon, []], + titleIcon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + + units: [configData.config.units, []], + decimals: [configData.config.decimals, []], + + animation: [settings.animation, []], + + showLegend: [settings.showLegend, []], + legendPosition: [settings.legendPosition, []], + legendLabelFont: [settings.legendLabelFont, []], + legendLabelColor: [settings.legendLabelColor, []], + legendValueFont: [settings.legendValueFont, []], + legendValueColor: [settings.legendValueColor, []], + + showTooltip: [settings.showTooltip, []], + tooltipValueType: [settings.tooltipValueType, []], + tooltipValueDecimals: [settings.tooltipValueDecimals, []], + tooltipValueFont: [settings.tooltipValueFont, []], + tooltipValueColor: [settings.tooltipValueColor, []], + tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], + tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + this.setSeries(config.series, this.widgetConfig.config.datasources); + + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + + this.widgetConfig.config.showTitleIcon = config.showTitleIcon; + this.widgetConfig.config.titleIcon = config.titleIcon; + this.widgetConfig.config.iconColor = config.iconColor; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.showLabel = config.showLabel; + this.widgetConfig.config.settings.labelPosition = config.labelPosition; + this.widgetConfig.config.settings.labelFont = config.labelFont; + this.widgetConfig.config.settings.labelColor = config.labelColor; + + this.widgetConfig.config.settings.borderWidth = config.borderWidth; + this.widgetConfig.config.settings.borderColor = config.borderColor; + + this.widgetConfig.config.settings.radius = config.radius; + + this.widgetConfig.config.settings.clockwise = config.clockwise; + this.widgetConfig.config.settings.sortSeries = config.sortSeries; + + this.widgetConfig.config.units = config.units; + this.widgetConfig.config.decimals = config.decimals; + + this.widgetConfig.config.settings.animation = config.animation; + + this.widgetConfig.config.settings.showLegend = config.showLegend; + this.widgetConfig.config.settings.legendPosition = config.legendPosition; + this.widgetConfig.config.settings.legendLabelFont = config.legendLabelFont; + this.widgetConfig.config.settings.legendLabelColor = config.legendLabelColor; + this.widgetConfig.config.settings.legendValueFont = config.legendValueFont; + this.widgetConfig.config.settings.legendValueColor = config.legendValueColor; + + this.widgetConfig.config.settings.showTooltip = config.showTooltip; + this.widgetConfig.config.settings.tooltipValueType = config.tooltipValueType; + this.widgetConfig.config.settings.tooltipValueDecimals = config.tooltipValueDecimals; + this.widgetConfig.config.settings.tooltipValueFont = config.tooltipValueFont; + this.widgetConfig.config.settings.tooltipValueColor = config.tooltipValueColor; + this.widgetConfig.config.settings.tooltipBackgroundColor = config.tooltipBackgroundColor; + this.widgetConfig.config.settings.tooltipBackgroundBlur = config.tooltipBackgroundBlur; + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + + this.widgetConfig.config.actions = config.actions; + + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showLabel', 'showTitle', 'showTitleIcon', 'showLegend', 'showTooltip']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showLabel: boolean = this.pieChartWidgetConfigForm.get('showLabel').value; + const showTitle: boolean = this.pieChartWidgetConfigForm.get('showTitle').value; + const showTitleIcon: boolean = this.pieChartWidgetConfigForm.get('showTitleIcon').value; + const showLegend: boolean = this.pieChartWidgetConfigForm.get('showLegend').value; + const showTooltip: boolean = this.pieChartWidgetConfigForm.get('showTooltip').value; + + if (showLabel) { + this.pieChartWidgetConfigForm.get('labelPosition').enable(); + this.pieChartWidgetConfigForm.get('labelFont').enable(); + this.pieChartWidgetConfigForm.get('labelColor').enable(); + } else { + this.pieChartWidgetConfigForm.get('labelPosition').disable(); + this.pieChartWidgetConfigForm.get('labelFont').disable(); + this.pieChartWidgetConfigForm.get('labelColor').disable(); + } + + if (showTitle) { + this.pieChartWidgetConfigForm.get('title').enable(); + this.pieChartWidgetConfigForm.get('titleFont').enable(); + this.pieChartWidgetConfigForm.get('titleColor').enable(); + this.pieChartWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false}); + if (showTitleIcon) { + this.pieChartWidgetConfigForm.get('titleIcon').enable(); + this.pieChartWidgetConfigForm.get('iconColor').enable(); + } else { + this.pieChartWidgetConfigForm.get('titleIcon').disable(); + this.pieChartWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.pieChartWidgetConfigForm.get('title').disable(); + this.pieChartWidgetConfigForm.get('titleFont').disable(); + this.pieChartWidgetConfigForm.get('titleColor').disable(); + this.pieChartWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false}); + this.pieChartWidgetConfigForm.get('titleIcon').disable(); + this.pieChartWidgetConfigForm.get('iconColor').disable(); + } + if (showLegend) { + this.pieChartWidgetConfigForm.get('legendPosition').enable(); + this.pieChartWidgetConfigForm.get('legendLabelFont').enable(); + this.pieChartWidgetConfigForm.get('legendLabelColor').enable(); + this.pieChartWidgetConfigForm.get('legendValueFont').enable(); + this.pieChartWidgetConfigForm.get('legendValueColor').enable(); + } else { + this.pieChartWidgetConfigForm.get('legendPosition').disable(); + this.pieChartWidgetConfigForm.get('legendLabelFont').disable(); + this.pieChartWidgetConfigForm.get('legendLabelColor').disable(); + this.pieChartWidgetConfigForm.get('legendValueFont').disable(); + this.pieChartWidgetConfigForm.get('legendValueColor').disable(); + } + if (showTooltip) { + this.pieChartWidgetConfigForm.get('tooltipValueType').enable(); + this.pieChartWidgetConfigForm.get('tooltipValueDecimals').enable(); + this.pieChartWidgetConfigForm.get('tooltipValueFont').enable(); + this.pieChartWidgetConfigForm.get('tooltipValueColor').enable(); + this.pieChartWidgetConfigForm.get('tooltipBackgroundColor').enable(); + this.pieChartWidgetConfigForm.get('tooltipBackgroundBlur').enable(); + } else { + this.pieChartWidgetConfigForm.get('tooltipValueType').disable(); + this.pieChartWidgetConfigForm.get('tooltipValueDecimals').disable(); + this.pieChartWidgetConfigForm.get('tooltipValueFont').disable(); + this.pieChartWidgetConfigForm.get('tooltipValueColor').disable(); + this.pieChartWidgetConfigForm.get('tooltipBackgroundColor').disable(); + this.pieChartWidgetConfigForm.get('tooltipBackgroundBlur').disable(); + } + } + + private getSeries(datasources?: Datasource[]): DataKey[] { + if (datasources && datasources.length) { + return datasources[0].dataKeys || []; + } + return []; + } + + private setSeries(series: DataKey[], datasources?: Datasource[]) { + if (datasources && datasources.length) { + datasources[0].dataKeys = series; + } + } + + 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(): string { + const units: string = this.pieChartWidgetConfigForm.get('units').value; + const decimals: number = this.pieChartWidgetConfigForm.get('decimals').value; + return formatValue(110, decimals, units, false); + } + + private _tooltipValuePreviewFn(): string { + const tooltipValueType: LatestChartTooltipValueType = this.pieChartWidgetConfigForm.get('tooltipValueType').value; + const decimals: number = this.pieChartWidgetConfigForm.get('tooltipValueDecimals').value; + if (tooltipValueType === LatestChartTooltipValueType.percentage) { + return formatValue(35, decimals, '%', false); + } else { + const units: string = this.pieChartWidgetConfigForm.get('units').value; + return formatValue(110, decimals, units, false); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html index e087ce7d91..913974c313 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html @@ -367,9 +367,9 @@
- - +
widget-config.card-appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html index fcee2a9dba..f8e72b5676 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html @@ -328,9 +328,9 @@
- - +
widget-config.card-appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts index 3b6333bbc3..5351b219a3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts @@ -23,15 +23,17 @@ import { textStyle } from '@shared/models/widget-settings.models'; import { LegendPosition } from '@shared/models/widget.models'; -import { EChartsTooltipWidgetSettings } from '@home/components/widget/lib/chart/echarts-widget.models'; +import { + echartsAnimationDefaultSettings, + EChartsAnimationSettings, + EChartsTooltipWidgetSettings +} from '@home/components/widget/lib/chart/echarts-widget.models'; import { DeepPartial } from '@shared/models/common'; import { defaultTimeSeriesChartXAxisSettings, defaultTimeSeriesChartYAxisSettings, SeriesFillSettings, SeriesFillType, - timeSeriesChartAnimationDefaultSettings, - TimeSeriesChartAnimationSettings, timeSeriesChartGridDefaultSettings, TimeSeriesChartGridSettings, TimeSeriesChartKeySettings, @@ -63,7 +65,7 @@ export interface BarChartWithLabelsWidgetSettings extends EChartsTooltipWidgetSe grid: TimeSeriesChartGridSettings; yAxis: TimeSeriesChartYAxisSettings; xAxis: TimeSeriesChartXAxisSettings; - animation: TimeSeriesChartAnimationSettings; + animation: EChartsAnimationSettings; thresholds: TimeSeriesChartThreshold[]; showLegend: boolean; legendPosition: LegendPosition; @@ -116,8 +118,8 @@ export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings xAxis: mergeDeep({} as TimeSeriesChartXAxisSettings, defaultTimeSeriesChartXAxisSettings, {showTicks: false, showSplitLines: false} as TimeSeriesChartXAxisSettings), - animation: mergeDeep({} as TimeSeriesChartAnimationSettings, - timeSeriesChartAnimationDefaultSettings), + animation: mergeDeep({} as EChartsAnimationSettings, + echartsAnimationDefaultSettings), thresholds: [], showLegend: true, legendPosition: LegendPosition.top, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.html deleted file mode 100644 index 3465e80c15..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.html +++ /dev/null @@ -1,37 +0,0 @@ - -
-
- -
-
-
-
-
-
-
-
{{ legendItem.label }}
-
-
{{ legendItem.value }}
-
-
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts index d215f37a32..4b2eca3728 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts @@ -14,85 +14,32 @@ /// limitations under the License. /// -import { - AfterViewInit, - ChangeDetectorRef, - Component, - ElementRef, - Input, - OnDestroy, - OnInit, - Renderer2, - TemplateRef, - ViewChild, - ViewEncapsulation -} from '@angular/core'; +import { Component, Input, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; import { doughnutDefaultSettings, - DoughnutLayout, - DoughnutTooltipValueType, + doughnutPieChartSettings, DoughnutWidgetSettings } from '@home/components/widget/lib/chart/doughnut-widget.models'; import { WidgetContext } from '@home/models/widget-component.models'; -import { - backgroundStyle, - ColorProcessor, - ComponentStyle, - overlayStyle, - textStyle -} from '@shared/models/widget-settings.models'; -import { ResizeObserver } from '@juggle/resize-observer'; import { WidgetComponent } from '@home/components/widget/widget.component'; -import * as echarts from 'echarts/core'; import { TranslateService } from '@ngx-translate/core'; -import { PieDataItemOption } from 'echarts/types/src/chart/pie/PieSeries'; -import { formatValue, isDefinedAndNotNull, isNumeric } from '@core/utils'; -import { SVG, Svg, Text } from '@svgdotjs/svg.js'; -import { DataKey, LegendPosition } from '@shared/models/widget.models'; -import { Observable } from 'rxjs'; -import { ImagePipe } from '@shared/pipe/image.pipe'; -import { DomSanitizer } from '@angular/platform-browser'; -import { ECharts, echartsModule, EChartsOption } from '@home/components/widget/lib/chart/echarts-widget.models'; - -const shapeSize = 134; -const shapeSegmentWidth = 13.4; - -interface DoughnutDataItem { - id: number; - dataKey: DataKey; - value: number; - hasValue: boolean; - enabled: boolean; -} - -interface DoughnutLegendItem { - id: number; - color: string; - label: string; - value: string; - hasValue: boolean; - enabled: boolean; - total?: boolean; -} +import { isDefinedAndNotNull } from '@core/utils'; +import { TbPieChart } from '@home/components/widget/lib/chart/pie-chart'; +import { + LatestChartComponent, + LatestChartComponentCallbacks +} from '@home/components/widget/lib/chart/latest-chart.component'; @Component({ selector: 'tb-doughnut-widget', - templateUrl: './doughnut-widget.component.html', - styleUrls: ['./doughnut-widget.component.scss'], + templateUrl: './latest-chart-widget.component.html', + styleUrls: [], encapsulation: ViewEncapsulation.None }) -export class DoughnutWidgetComponent implements OnInit, OnDestroy, AfterViewInit { +export class DoughnutWidgetComponent implements OnInit { - @ViewChild('doughnutContent', {static: false}) - doughnutContent: ElementRef; - - @ViewChild('doughnutShape', {static: false}) - doughnutShape: ElementRef; - - @ViewChild('doughnutLegend', {static: false}) - doughnutLegend: ElementRef; - - settings: DoughnutWidgetSettings; + @ViewChild('latestChart') + latestChart: LatestChartComponent; @Input() ctx: WidgetContext; @@ -100,45 +47,12 @@ export class DoughnutWidgetComponent implements OnInit, OnDestroy, AfterViewInit @Input() widgetTitlePanel: TemplateRef; - showLegend: boolean; - legendClass: string; - - totalValueColor: ColorProcessor; - - backgroundStyle$: Observable; - overlayStyle: ComponentStyle = {}; - - legendItems: DoughnutLegendItem[]; - legendLabelStyle: ComponentStyle; - legendValueStyle: ComponentStyle; - disabledLegendLabelStyle: ComponentStyle; - disabledLegendValueStyle: ComponentStyle; - - private shapeResize$: ResizeObserver; - private legendHorizontal: boolean; - - private decimals = 0; - private units = ''; - - private total = 0; - private totalText = 'N/A'; - private scale = 1; - - private dataItems: DoughnutDataItem[] = []; + settings: DoughnutWidgetSettings; - private drawDoughnutPending = false; - private showTotal = false; - private doughnutChart: ECharts; - private doughnutOptions: EChartsOption; - private svgShape: Svg; - private totalTextNode: Text; + callbacks: LatestChartComponentCallbacks; - constructor(private imagePipe: ImagePipe, - private sanitizer: DomSanitizer, - private widgetComponent: WidgetComponent, - private renderer: Renderer2, - private translate: TranslateService, - private cd: ChangeDetectorRef) { + constructor(private widgetComponent: WidgetComponent, + private translate: TranslateService) { } ngOnInit(): void { @@ -146,390 +60,19 @@ export class DoughnutWidgetComponent implements OnInit, OnDestroy, AfterViewInit const horizontal = isDefinedAndNotNull(params.horizontal) ? params.horizontal : false; this.ctx.$scope.doughnutWidget = this; this.settings = {...doughnutDefaultSettings(horizontal), ...this.ctx.settings}; - - this.decimals = this.ctx.decimals; - this.units = this.ctx.units; - - this.showLegend = this.settings.showLegend; - this.showTotal = this.settings.layout === DoughnutLayout.with_total; - - if (this.showTotal) { - this.totalValueColor = ColorProcessor.fromSettings(this.settings.totalValueColor); - } - - this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); - this.overlayStyle = overlayStyle(this.settings.background.overlay); - - if (this.showLegend) { - this.legendItems = []; - this.legendClass = `legend-${this.settings.legendPosition}`; - this.legendHorizontal = [LegendPosition.left, LegendPosition.right].includes(this.settings.legendPosition); - this.legendLabelStyle = textStyle(this.settings.legendLabelFont); - this.disabledLegendLabelStyle = textStyle(this.settings.legendLabelFont); - this.legendLabelStyle.color = this.settings.legendLabelColor; - this.legendValueStyle = textStyle(this.settings.legendValueFont); - this.disabledLegendValueStyle = textStyle(this.settings.legendValueFont); - this.legendValueStyle.color = this.settings.legendValueColor; - } - let counter = 0; - if (this.ctx.datasources.length) { - for (const datasource of this.ctx.datasources) { - const dataKeys = datasource.dataKeys; - for (const dataKey of dataKeys) { - const id = counter++; - this.dataItems.push({ - id, - dataKey, - value: 0, - hasValue: false, - enabled: true - }); - if (this.showLegend) { - this.legendItems.push( - { - id, - value: '--', - label: dataKey.label, - color: dataKey.color, - enabled: true, - hasValue: false - } - ); - } - } + this.callbacks = { + createChart: (chartShape, renderer) => { + const settings = doughnutPieChartSettings(this.settings); + return new TbPieChart(this.ctx, settings, chartShape.nativeElement, renderer, this.translate, true); } - } - if (this.settings.sortSeries) { - this.dataItems.sort((a, b) => a.dataKey.label.localeCompare(b.dataKey.label)); - if (this.showLegend) { - this.legendItems.sort((a, b) => a.label.localeCompare(b.label)); - } - } - if (this.showLegend && !this.showTotal) { - this.legendItems.push( - { - id: null, - value: '--', - label: this.translate.instant('widgets.doughnut.total'), - color: 'rgba(0, 0, 0, 0.06)', - enabled: true, - hasValue: false, - total: true - } - ); - } - } - - ngAfterViewInit() { - if (this.drawDoughnutPending) { - this.drawDoughnut(); - } - } - - ngOnDestroy() { - if (this.shapeResize$) { - this.shapeResize$.disconnect(); - } - if (this.doughnutChart) { - this.doughnutChart.dispose(); - } + }; } public onInit() { - const borderRadius = this.ctx.$widgetElement.css('borderRadius'); - this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; - if (this.doughnutShape) { - this.drawDoughnut(); - } else { - this.drawDoughnutPending = true; - } - this.cd.detectChanges(); + this.latestChart?.onInit(); } public onDataUpdated() { - for (const dsData of this.ctx.data) { - let value = 0; - const tsValue = dsData.data[0]; - const dataItem = this.dataItems.find(item => item.dataKey === dsData.dataKey); - if (tsValue && isDefinedAndNotNull(tsValue[1]) && isNumeric(tsValue[1])) { - value = tsValue[1]; - dataItem.hasValue = true; - dataItem.value = Number(value); - } else { - dataItem.hasValue = false; - dataItem.value = 0; - } - } - this.updateSeriesData(); - if (this.showLegend) { - this.cd.detectChanges(); - if (this.legendHorizontal) { - setTimeout(() => { - this.onResize(); - }); - } - } - } - - private updateSeriesData(renderTotal = true) { - this.total = 0; - this.totalText = 'N/A'; - let hasValue = false; - const seriesData: PieDataItemOption[] = []; - const enabledDataItems = this.dataItems.filter(item => item.enabled && item.hasValue); - for (const dataItem of this.dataItems) { - if (dataItem.enabled && dataItem.hasValue) { - hasValue = true; - this.total += dataItem.value; - seriesData.push( - {id: dataItem.id, value: dataItem.value, name: dataItem.dataKey.label, itemStyle: {color: dataItem.dataKey.color}} - ); - if (enabledDataItems.length > 1) { - seriesData.push({ - value: 0, name: '', itemStyle: {color: 'transparent'}, emphasis: {disabled: true} - }); - } - } - if (this.showLegend) { - const legendItem = this.legendItems.find(item => item.id === dataItem.id); - if (dataItem.hasValue) { - legendItem.hasValue = true; - legendItem.value = formatValue(dataItem.value, this.decimals, this.units, false); - } else { - legendItem.hasValue = false; - legendItem.value = '--'; - } - } - } - for (let i= 1; i < seriesData.length; i+=2) { - seriesData[i].value = this.total / 100; - } - if (this.showTotal || this.showLegend) { - if (hasValue) { - this.totalText = formatValue(this.total, this.decimals, this.units, false); - if (this.showLegend && !this.showTotal) { - this.legendItems[this.legendItems.length - 1].hasValue = true; - this.legendItems[this.legendItems.length - 1].value = this.totalText; - } - } else if (this.showLegend && !this.showTotal) { - this.legendItems[this.legendItems.length - 1].hasValue = false; - this.legendItems[this.legendItems.length - 1].value = '--'; - } - } - this.doughnutOptions.series[0].data = seriesData; - this.doughnutChart.setOption(this.doughnutOptions); - if (this.showTotal) { - this.totalValueColor.update(this.total); - if (renderTotal) { - this.renderTotal(); - } - } - } - - public onLegendItemEnter(item: DoughnutLegendItem) { - if (!item.total && item.enabled && item.hasValue) { - const dataIndex = this.doughnutOptions.series[0].data.findIndex(d => d.id === item.id); - if (dataIndex > -1) { - this.doughnutChart.dispatchAction({ - type: 'highlight', - dataIndex - }); - } - } - } - - public onLegendItemLeave(item: DoughnutLegendItem) { - if (!item.total && item.enabled && item.hasValue) { - const dataIndex = this.doughnutOptions.series[0].data.findIndex(d => d.id === item.id); - if (dataIndex > -1) { - this.doughnutChart.dispatchAction({ - type: 'downplay', - dataIndex - }); - } - } + this.latestChart?.onDataUpdated(); } - - public toggleLegendItem(item: DoughnutLegendItem) { - if (!item.total && item.hasValue) { - const enable = !item.enabled; - const dataItem = this.dataItems.find(d => d.id === item.id); - if (dataItem) { - dataItem.enabled = enable; - this.updateSeriesData(); - item.enabled = enable; - if (enable) { - const dataIndex = this.doughnutOptions.series[0].data.findIndex(d => d.id === item.id); - if (dataIndex > -1) { - this.doughnutChart.dispatchAction({ - type: 'highlight', - dataIndex - }); - } - } - } - } - } - - private drawDoughnut() { - echartsModule.init(); - const shapeWidth = this.doughnutShape.nativeElement.getBoundingClientRect().width; - const shapeHeight = this.doughnutShape.nativeElement.getBoundingClientRect().height; - const size = this.settings.autoScale ? shapeSize : Math.min(shapeWidth, shapeHeight); - const innerRadius = size / 2 - shapeSegmentWidth; - const outerRadius = size / 2; - this.doughnutChart = echarts.init(this.doughnutShape.nativeElement, null, { - renderer: 'svg', - width: this.settings.autoScale ? shapeSize : undefined, - height: this.settings.autoScale ? shapeSize : undefined, - }); - this.doughnutOptions = { - tooltip: { - trigger: this.settings.showTooltip ? 'item' : 'none', - confine: false, - appendTo: 'body', - }, - series: [ - { - type: 'pie', - clockwise: this.settings.clockwise, - radius: [innerRadius, outerRadius], - avoidLabelOverlap: false, - itemStyle: { - borderRadius: '50%', - borderWidth: 0, - borderColor: '#fff' - }, - label: { - show: false - }, - emphasis: { - scale: false, - itemStyle: { - borderColor: '#fff', - borderWidth: 2, - shadowColor: 'rgba(0, 0, 0, 0.24)', - shadowBlur: 8 - }, - label: { - show: false - } - } - } - ] - }; - if (this.settings.showTooltip) { - this.doughnutOptions.series[0].tooltip = { - formatter: (params) => { - if (!params.name) { - return null; - } - let value: string; - if (this.settings.tooltipValueType === DoughnutTooltipValueType.percentage) { - const percents = params.value / this.total * 100; - value = formatValue(percents, this.settings.tooltipValueDecimals, '%', false); - } else { - value = formatValue(params.value, this.settings.tooltipValueDecimals, this.units, false); - } - const textElement: HTMLElement = this.renderer.createElement('div'); - this.renderer.setStyle(textElement, 'display', 'inline-flex'); - this.renderer.setStyle(textElement, 'align-items', 'center'); - this.renderer.setStyle(textElement, 'gap', '8px'); - const labelElement: HTMLElement = this.renderer.createElement('div'); - this.renderer.appendChild(labelElement, this.renderer.createText(params.name)); - this.renderer.setStyle(labelElement, 'font-family', 'Roboto'); - this.renderer.setStyle(labelElement, 'font-size', '11px'); - this.renderer.setStyle(labelElement, 'font-style', 'normal'); - this.renderer.setStyle(labelElement, 'font-weight', '400'); - this.renderer.setStyle(labelElement, 'line-height', '16px'); - this.renderer.setStyle(labelElement, 'letter-spacing', '0.25px'); - this.renderer.setStyle(labelElement, 'color', 'rgba(0, 0, 0, 0.38)'); - const valueElement: HTMLElement = this.renderer.createElement('div'); - this.renderer.appendChild(valueElement, this.renderer.createText(value)); - this.renderer.setStyle(valueElement, 'font-family', this.settings.tooltipValueFont.family); - this.renderer.setStyle(valueElement, 'font-size', this.settings.tooltipValueFont.size + this.settings.tooltipValueFont.sizeUnit); - this.renderer.setStyle(valueElement, 'font-style', this.settings.tooltipValueFont.style); - this.renderer.setStyle(valueElement, 'font-weight', this.settings.tooltipValueFont.weight); - this.renderer.setStyle(valueElement, 'line-height', this.settings.tooltipValueFont.lineHeight); - this.renderer.setStyle(valueElement, 'color', this.settings.tooltipValueColor); - this.renderer.appendChild(textElement, labelElement); - this.renderer.appendChild(textElement, valueElement); - return textElement; - }, - padding: [4, 8], - backgroundColor: this.settings.tooltipBackgroundColor, - extraCssText: `line-height: 1; backdrop-filter: blur(${this.settings.tooltipBackgroundBlur}px);` - }; - this.doughnutOptions.series[0].tooltip.position = (pos) => [pos[0] + 10, pos[1] + 10]; - } - this.updateSeriesData(false); - - this.renderer.setStyle(this.doughnutChart.getDom().firstChild, 'overflow', 'visible'); - if (this.settings.autoScale) { - this.renderer.setStyle(this.doughnutChart.getDom().firstChild, 'position', 'absolute'); - } - this.renderer.setStyle(this.doughnutChart.getDom().firstChild.firstChild, 'overflow', 'visible'); - - this.svgShape = SVG(this.doughnutChart.getDom().firstChild.firstChild).toRoot(); - - if (this.showTotal) { - this.totalTextNode = this.svgShape.text('').font({ - family: 'Roboto', - leading: 1 - }).attr({'text-anchor': 'middle'}); - this.renderTotal(); - } - - this.shapeResize$ = new ResizeObserver(() => { - this.onResize(); - }); - this.shapeResize$.observe(this.doughnutContent.nativeElement); - this.onResize(); - } - - private renderTotal() { - this.totalTextNode.text(add => { - add.tspan(this.translate.instant('widgets.doughnut.total')).font({size: '12px', weight: 400}).fill('rgba(0, 0, 0, 0.38)'); - add.tspan('').newLine().font({size: '4px'}); - add.tspan(this.totalText).newLine().font( - {family: this.settings.totalValueFont.family, - size: this.settings.totalValueFont.size + this.settings.totalValueFont.sizeUnit, - weight: this.settings.totalValueFont.weight, - style: this.settings.totalValueFont.style} - ).fill(this.totalValueColor.color); - }).center(this.svgShape.bbox().width / 2, this.svgShape.bbox().height / 2); - } - - private onResize() { - if (this.legendHorizontal) { - this.renderer.setStyle(this.doughnutShape.nativeElement, 'max-width', null); - this.renderer.setStyle(this.doughnutShape.nativeElement, 'min-width', null); - this.renderer.setStyle(this.doughnutLegend.nativeElement, 'flex', null); - } - const shapeWidth = this.doughnutShape.nativeElement.getBoundingClientRect().width; - const shapeHeight = this.doughnutShape.nativeElement.getBoundingClientRect().height; - const size = Math.min(shapeWidth, shapeHeight); - if (this.legendHorizontal) { - this.renderer.setStyle(this.doughnutShape.nativeElement, 'max-width', `${size}px`); - this.renderer.setStyle(this.doughnutShape.nativeElement, 'min-width', `${size}px`); - this.renderer.setStyle(this.doughnutLegend.nativeElement, 'flex', '1'); - } - if (!this.settings.autoScale) { - const innerRadius = size / 2 - shapeSegmentWidth; - const outerRadius = size / 2; - this.doughnutOptions.series[0].radius = [innerRadius, outerRadius]; - this.doughnutChart.setOption(this.doughnutOptions); - } else { - this.scale = size / shapeSize; - this.renderer.setStyle(this.doughnutChart.getDom().firstChild, 'transform', `scale(${this.scale})`); - } - if (!this.settings.autoScale) { - this.doughnutChart.resize(); - } - if (this.showTotal) { - this.totalTextNode.center((this.settings.autoScale ? shapeSize : shapeWidth) / 2, - (this.settings.autoScale ? shapeSize : shapeHeight) / 2); - } - } - } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.models.ts index 9110cfcd4d..d059e0186c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.models.ts @@ -14,14 +14,16 @@ /// limitations under the License. /// -import { - BackgroundSettings, - BackgroundType, - ColorSettings, - constantColor, - Font -} from '@shared/models/widget-settings.models'; +import { BackgroundType, ColorSettings, constantColor, Font } from '@shared/models/widget-settings.models'; import { LegendPosition } from '@shared/models/widget.models'; +import { pieChartAnimationDefaultSettings, PieChartSettings } from '@home/components/widget/lib/chart/pie-chart.models'; +import { DeepPartial } from '@shared/models/common'; +import { + LatestChartTooltipValueType, + LatestChartWidgetSettings +} from '@home/components/widget/lib/chart/latest-chart.models'; +import { mergeDeep } from '@core/utils'; +import { EChartsAnimationSettings } from '@home/components/widget/lib/chart/echarts-widget.models'; export enum DoughnutLayout { default = 'default', @@ -51,41 +53,11 @@ export const horizontalDoughnutLayoutImages = new Map( ] ); -export enum DoughnutTooltipValueType { - absolute = 'absolute', - percentage = 'percentage' -} - -export const doughnutTooltipValueTypes = Object.keys(DoughnutTooltipValueType) as DoughnutTooltipValueType[]; - -export const doughnutTooltipValueTypeTranslations = new Map( - [ - [DoughnutTooltipValueType.absolute, 'widgets.doughnut.tooltip-value-type-absolute'], - [DoughnutTooltipValueType.percentage, 'widgets.doughnut.tooltip-value-type-percentage'] - ] -); - -export interface DoughnutWidgetSettings { +export interface DoughnutWidgetSettings extends LatestChartWidgetSettings { layout: DoughnutLayout; - autoScale: boolean; clockwise: boolean; - sortSeries: boolean; totalValueFont: Font; totalValueColor: ColorSettings; - showLegend: boolean; - legendPosition: LegendPosition; - legendLabelFont: Font; - legendLabelColor: string; - legendValueFont: Font; - legendValueColor: string; - showTooltip: boolean; - tooltipValueType: DoughnutTooltipValueType; - tooltipValueDecimals: number; - tooltipValueFont: Font; - tooltipValueColor: string; - tooltipBackgroundColor: string; - tooltipBackgroundBlur: number; - background: BackgroundSettings; } export const doughnutDefaultSettings = (horizontal: boolean): DoughnutWidgetSettings => ({ @@ -102,6 +74,8 @@ export const doughnutDefaultSettings = (horizontal: boolean): DoughnutWidgetSett lineHeight: '1' }, totalValueColor: constantColor('rgba(0, 0, 0, 0.87)'), + animation: mergeDeep({} as EChartsAnimationSettings, + pieChartAnimationDefaultSettings), showLegend: true, legendPosition: horizontal ? LegendPosition.right : LegendPosition.bottom, legendLabelFont: { @@ -123,7 +97,7 @@ export const doughnutDefaultSettings = (horizontal: boolean): DoughnutWidgetSett }, legendValueColor: 'rgba(0, 0, 0, 0.87)', showTooltip: true, - tooltipValueType: DoughnutTooltipValueType.percentage, + tooltipValueType: LatestChartTooltipValueType.percentage, tooltipValueDecimals: 0, tooltipValueFont: { family: 'Roboto', @@ -146,3 +120,31 @@ export const doughnutDefaultSettings = (horizontal: boolean): DoughnutWidgetSett } } }); + +export const doughnutPieChartSettings = (settings: DoughnutWidgetSettings): DeepPartial => ({ + autoScale: settings.autoScale, + doughnut: true, + clockwise: settings.clockwise, + sortSeries: settings.sortSeries, + showTotal: settings.layout === DoughnutLayout.with_total, + animation: settings.animation, + showLegend: settings.showLegend, + totalValueFont: settings.totalValueFont, + totalValueColor: settings.totalValueColor, + showLabel: false, + borderWidth: 0, + borderColor: '#fff', + borderRadius: '50%', + emphasisScale: false, + emphasisBorderWidth: 2, + emphasisBorderColor: '#fff', + emphasisShadowColor: 'rgba(0, 0, 0, 0.24)', + emphasisShadowBlur: 8, + showTooltip: settings.showTooltip, + tooltipValueType: settings.tooltipValueType, + tooltipValueDecimals: settings.tooltipValueDecimals, + tooltipValueFont: settings.tooltipValueFont, + tooltipValueColor: settings.tooltipValueColor, + tooltipBackgroundColor: settings.tooltipBackgroundColor, + tooltipBackgroundBlur: settings.tooltipBackgroundBlur +}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts index 4d188e0a74..a225973b82 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts @@ -162,6 +162,63 @@ export const timeSeriesChartShapeOffsetFunctions = new Map { let interval: number; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart-widget.component.html new file mode 100644 index 0000000000..436687b23a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart-widget.component.html @@ -0,0 +1,24 @@ + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.html new file mode 100644 index 0000000000..bd85f19d81 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.html @@ -0,0 +1,37 @@ + +
+
+ +
+
+
+
+
+
+
+
{{ legendItem.label }}
+
+
{{ legendItem.value }}
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.scss similarity index 82% rename from ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.scss index 3ed90e1c2c..77b4f0fc08 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.scss @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -.tb-doughnut-panel { +.tb-latest-chart-panel { width: 100%; height: 100%; position: relative; @@ -21,10 +21,10 @@ flex-direction: column; gap: 16px; padding: 20px 24px 24px 24px; - > div:not(.tb-doughnut-overlay) { + > div:not(.tb-latest-chart-overlay) { z-index: 1; } - .tb-doughnut-overlay { + .tb-latest-chart-overlay { position: absolute; top: 12px; left: 12px; @@ -34,7 +34,7 @@ div.tb-widget-title { padding: 0; } - .tb-doughnut-content { + .tb-latest-chart-content { flex: 1; min-width: 0; min-height: 0; @@ -50,22 +50,22 @@ &.legend-left { flex-direction: row-reverse; } - .tb-doughnut-shape { + .tb-latest-chart-shape { flex: 1; - min-width: 0; - min-height: 0; + min-width: 10px; + min-height: 10px; display: flex; align-items: center; justify-content: center; } - .tb-doughnut-legend { + .tb-latest-chart-legend { display: flex; justify-content: space-around; align-items: center; align-self: stretch; flex-wrap: wrap; gap: 8px; - .tb-doughnut-legend-item { + .tb-latest-chart-legend-item { display: flex; flex-direction: column; justify-content: center; @@ -74,19 +74,19 @@ &.pointer { cursor: pointer; } - .tb-doughnut-legend-item-label { + .tb-latest-chart-legend-item-label { display: flex; align-items: center; gap: 4px; color: #ccc; - .tb-doughnut-legend-item-label-circle { + .tb-latest-chart-legend-item-label-circle { width: 8px; height: 8px; border-radius: 50%; background-color: #ccc; } } - .tb-doughnut-legend-item-value { + .tb-latest-chart-legend-item-value { padding-left: 12px; color: #ccc; } @@ -94,11 +94,11 @@ } &.legend-right, &.legend-left { gap: 24px; - .tb-doughnut-legend { + .tb-latest-chart-legend { flex-direction: column; justify-content: center; align-items: stretch; - .tb-doughnut-legend-item { + .tb-latest-chart-legend-item { flex-direction: row; justify-content: space-between; align-items: center; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.ts new file mode 100644 index 0000000000..21599dc94b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.ts @@ -0,0 +1,200 @@ +/// +/// 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 { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { + LatestChartDataItem, + LatestChartLegendItem, + LatestChartSettings, + LatestChartWidgetSettings +} from '@home/components/widget/lib/chart/latest-chart.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { Observable } from 'rxjs'; +import { backgroundStyle, ComponentStyle, overlayStyle, textStyle } from '@shared/models/widget-settings.models'; +import { TbLatestChart } from '@home/components/widget/lib/chart/latest-chart'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; +import { WidgetComponent } from '@home/components/widget/widget.component'; +import { TranslateService } from '@ngx-translate/core'; +import { LegendPosition } from '@shared/models/widget.models'; + +export interface LatestChartComponentCallbacks { + createChart: (chartShape: ElementRef, renderer: Renderer2) => TbLatestChart; + onItemClick?: ($event: Event, item: LatestChartDataItem) => void; +} + +@Component({ + selector: 'tb-latest-chart', + templateUrl: './latest-chart.component.html', + styleUrls: ['./latest-chart.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class LatestChartComponent implements OnInit, OnDestroy, AfterViewInit { + + @ViewChild('chartContent', {static: false}) + chartContent: ElementRef; + + @ViewChild('chartShape', {static: false}) + chartShape: ElementRef; + + @ViewChild('chartLegend', {static: false}) + chartLegend: ElementRef; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + @Input() + callbacks: LatestChartComponentCallbacks; + + @Input() + settings: LatestChartWidgetSettings; + + showLegend: boolean; + legendClass: string; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + get legendItems(): LatestChartLegendItem[] { + return this.latestChart ? this.latestChart.getLegendItems() : []; + } + + legendLabelStyle: ComponentStyle; + legendValueStyle: ComponentStyle; + disabledLegendLabelStyle: ComponentStyle; + disabledLegendValueStyle: ComponentStyle; + + private shapeResize$: ResizeObserver; + private legendHorizontal: boolean; + + private latestChart: TbLatestChart; + + constructor(private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private widgetComponent: WidgetComponent, + private renderer: Renderer2, + private translate: TranslateService, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.showLegend = this.settings.showLegend; + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + + if (this.showLegend) { + this.legendClass = `legend-${this.settings.legendPosition}`; + this.legendHorizontal = [LegendPosition.left, LegendPosition.right].includes(this.settings.legendPosition); + this.legendLabelStyle = textStyle(this.settings.legendLabelFont); + this.disabledLegendLabelStyle = textStyle(this.settings.legendLabelFont); + this.legendLabelStyle.color = this.settings.legendLabelColor; + this.legendValueStyle = textStyle(this.settings.legendValueFont); + this.disabledLegendValueStyle = textStyle(this.settings.legendValueFont); + this.legendValueStyle.color = this.settings.legendValueColor; + } + } + + ngAfterViewInit() { + this.latestChart = this.callbacks.createChart(this.chartShape, this.renderer); + this.latestChart.onItemClick(this.callbacks.onItemClick); + this.shapeResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.shapeResize$.observe(this.chartContent.nativeElement); + this.onResize(); + } + + ngOnDestroy() { + if (this.shapeResize$) { + this.shapeResize$.disconnect(); + } + if (this.latestChart) { + this.latestChart.destroy(); + } + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + this.cd.detectChanges(); + } + + public onDataUpdated() { + if (this.latestChart) { + this.latestChart.update(); + } + if (this.showLegend) { + this.cd.detectChanges(); + if (this.legendHorizontal) { + setTimeout(() => { + this.onResize(); + }); + } + } + } + + public onLegendItemEnter(item: LatestChartLegendItem) { + if (!item.total && item.hasValue) { + this.latestChart.keyEnter(item.dataKey); + } + } + + public onLegendItemLeave(item: LatestChartLegendItem) { + if (!item.total && item.hasValue) { + this.latestChart.keyLeave(item.dataKey); + } + } + + public toggleLegendItem(item: LatestChartLegendItem) { + if (!item.total && item.hasValue) { + this.latestChart.toggleKey(item.dataKey); + } + } + + private onResize() { + if (this.legendHorizontal) { + this.renderer.setStyle(this.chartShape.nativeElement, 'max-width', null); + this.renderer.setStyle(this.chartShape.nativeElement, 'min-width', null); + this.renderer.setStyle(this.chartLegend.nativeElement, 'flex', null); + } + const shapeWidth = this.chartShape.nativeElement.getBoundingClientRect().width; + const shapeHeight = this.chartShape.nativeElement.getBoundingClientRect().height; + const size = Math.min(shapeWidth, shapeHeight); + if (this.legendHorizontal) { + this.renderer.setStyle(this.chartShape.nativeElement, 'max-width', `${size}px`); + this.renderer.setStyle(this.chartShape.nativeElement, 'min-width', `${size}px`); + this.renderer.setStyle(this.chartLegend.nativeElement, 'flex', '1'); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.models.ts new file mode 100644 index 0000000000..c99cd5e773 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.models.ts @@ -0,0 +1,122 @@ +/// +/// 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 { DataKey, Datasource, LegendPosition } from '@shared/models/widget.models'; +import { BackgroundSettings, Font } from '@shared/models/widget-settings.models'; +import { Renderer2 } from '@angular/core'; +import { CallbackDataParams } from 'echarts/types/dist/shared'; +import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { EChartsAnimationSettings } from '@home/components/widget/lib/chart/echarts-widget.models'; + +export interface LatestChartDataItem { + id: number; + datasource: Datasource; + dataKey: DataKey; + value: number; + hasValue: boolean; + enabled: boolean; +} + +export interface LatestChartLegendItem { + dataKey?: DataKey; + color: string; + label: string; + value: string; + hasValue: boolean; + total?: boolean; +} + +export enum LatestChartTooltipValueType { + absolute = 'absolute', + percentage = 'percentage' +} + +export const latestChartTooltipValueTypes = Object.keys(LatestChartTooltipValueType) as LatestChartTooltipValueType[]; + +export const latestChartTooltipValueTypeTranslations = new Map( + [ + [LatestChartTooltipValueType.absolute, 'widgets.latest-chart.tooltip-value-type-absolute'], + [LatestChartTooltipValueType.percentage, 'widgets.latest-chart.tooltip-value-type-percentage'] + ] +); + +export interface LatestChartTooltipSettings { + showTooltip: boolean; + tooltipValueType: LatestChartTooltipValueType; + tooltipValueDecimals: number; + tooltipValueFont: Font; + tooltipValueColor: string; + tooltipBackgroundColor: string; + tooltipBackgroundBlur: number; +} + +export interface LatestChartSettings extends LatestChartTooltipSettings { + autoScale?: boolean; + sortSeries: boolean; + showTotal?: boolean; + showLegend: boolean; + animation: EChartsAnimationSettings; +} + +export interface LatestChartWidgetSettings extends LatestChartSettings { + legendPosition: LegendPosition; + legendLabelFont: Font; + legendLabelColor: string; + legendValueFont: Font; + legendValueColor: string; + background: BackgroundSettings; +} + +export const latestChartTooltipFormatter = (renderer: Renderer2, + settings: LatestChartTooltipSettings, + params: CallbackDataParams, + units: string, + total: number): null | HTMLElement => { + if (!params.name) { + return null; + } + let value: string; + if (settings.tooltipValueType === LatestChartTooltipValueType.percentage) { + const percents = isDefinedAndNotNull(params.percent) ? params.percent : (params.value as number) / total * 100; + value = formatValue(percents, settings.tooltipValueDecimals, '%', false); + } else { + value = formatValue(params.value, settings.tooltipValueDecimals, units, false); + } + const textElement: HTMLElement = renderer.createElement('div'); + renderer.setStyle(textElement, 'display', 'inline-flex'); + renderer.setStyle(textElement, 'align-items', 'center'); + renderer.setStyle(textElement, 'gap', '8px'); + const labelElement: HTMLElement = renderer.createElement('div'); + renderer.appendChild(labelElement, renderer.createText(params.name)); + renderer.setStyle(labelElement, 'font-family', 'Roboto'); + renderer.setStyle(labelElement, 'font-size', '11px'); + renderer.setStyle(labelElement, 'font-style', 'normal'); + renderer.setStyle(labelElement, 'font-weight', '400'); + renderer.setStyle(labelElement, 'line-height', '16px'); + renderer.setStyle(labelElement, 'letter-spacing', '0.25px'); + renderer.setStyle(labelElement, 'color', 'rgba(0, 0, 0, 0.38)'); + const valueElement: HTMLElement = renderer.createElement('div'); + renderer.appendChild(valueElement, renderer.createText(value)); + renderer.setStyle(valueElement, 'font-family', settings.tooltipValueFont.family); + renderer.setStyle(valueElement, 'font-size', settings.tooltipValueFont.size + settings.tooltipValueFont.sizeUnit); + renderer.setStyle(valueElement, 'font-style', settings.tooltipValueFont.style); + renderer.setStyle(valueElement, 'font-weight', settings.tooltipValueFont.weight); + renderer.setStyle(valueElement, 'line-height', settings.tooltipValueFont.lineHeight); + renderer.setStyle(valueElement, 'color', settings.tooltipValueColor); + renderer.appendChild(textElement, labelElement); + renderer.appendChild(textElement, valueElement); + return textElement; +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.ts new file mode 100644 index 0000000000..ddb6e47560 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.ts @@ -0,0 +1,374 @@ +/// +/// 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 { ResizeObserver } from '@juggle/resize-observer'; +import { ECharts, echartsModule, EChartsOption } from '@home/components/widget/lib/chart/echarts-widget.models'; +import { + LatestChartDataItem, + LatestChartLegendItem, + LatestChartSettings, + latestChartTooltipFormatter +} from '@home/components/widget/lib/chart/latest-chart.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { DeepPartial } from '@shared/models/common'; +import { Renderer2 } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { formatValue, isDefinedAndNotNull, isNumeric, mergeDeep } from '@core/utils'; +import { DataKey } from '@shared/models/widget.models'; +import * as echarts from 'echarts/core'; +import { CallbackDataParams } from 'echarts/types/dist/shared'; +import { SVG, Svg } from '@svgdotjs/svg.js'; + +export abstract class TbLatestChart { + + private readonly shapeResize$: ResizeObserver; + + protected readonly settings: S; + + protected readonly decimals: number; + protected readonly units: string; + + protected total = 0; + protected totalText = 'N/A'; + + protected latestChart: ECharts; + protected latestChartOption: EChartsOption; + + protected svgShape: Svg; + + protected dataItems: LatestChartDataItem[] = []; + private legendItems: LatestChartLegendItem[] = []; + + private itemClick: ($event: Event, item: LatestChartDataItem) => void; + + protected constructor(private ctx: WidgetContext, + private readonly inputSettings: DeepPartial, + protected chartElement: HTMLElement, + private renderer: Renderer2, + protected translate: TranslateService, + private autoResize = true) { + this.settings = mergeDeep({} as S, + this.defaultSettings(), + this.inputSettings as S); + + this.decimals = this.ctx.decimals; + this.units = this.ctx.units; + + this.initSettings(); + + this.setupData(); + + this.onResize(); + if (this.autoResize) { + this.shapeResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.shapeResize$.observe(this.chartElement); + } + } + + private setupData(): void { + let counter = 0; + if (this.ctx.datasources.length) { + for (const datasource of this.ctx.datasources) { + const dataKeys = datasource.dataKeys; + for (const dataKey of dataKeys) { + const id = counter++; + this.dataItems.push({ + id, + datasource, + dataKey, + value: 0, + hasValue: false, + enabled: true + }); + if (this.settings.showLegend) { + this.legendItems.push( + { + dataKey, + value: '--', + label: dataKey.label, + color: dataKey.color, + hasValue: false + } + ); + } + } + } + } + if (this.settings.sortSeries) { + this.dataItems.sort((a, b) => a.dataKey.label.localeCompare(b.dataKey.label)); + if (this.settings.showLegend) { + this.legendItems.sort((a, b) => a.label.localeCompare(b.label)); + } + } + if (this.settings.showLegend && !this.settings.showTotal) { + this.legendItems.push( + { + value: '--', + label: this.translate.instant('widgets.latest-chart.total'), + color: 'rgba(0, 0, 0, 0.06)', + hasValue: false, + total: true + } + ); + } + } + + public getLegendItems(): LatestChartLegendItem[] { + return this.legendItems; + } + + public update(): void { + for (const dsData of this.ctx.data) { + let value = 0; + const tsValue = dsData.data[0]; + const dataItem = this.dataItems.find(item => item.dataKey === dsData.dataKey); + if (tsValue && isDefinedAndNotNull(tsValue[1]) && isNumeric(tsValue[1])) { + value = tsValue[1]; + dataItem.hasValue = true; + dataItem.value = Number(value); + } else { + dataItem.hasValue = false; + dataItem.value = 0; + } + } + this.updateSeriesData(); + } + + public keyEnter(dataKey: DataKey): void { + const item = this.dataItems.find(d => d.dataKey === dataKey); + if (item) { + const dataIndex = this.latestChartOption.series[0].data.findIndex( + (d: any) => d.id === item.id); + if (dataIndex > -1) { + this.latestChart.dispatchAction({ + type: 'highlight', + dataIndex + }); + } + } + } + + public keyLeave(dataKey: DataKey): void { + const item = this.dataItems.find(d => d.dataKey === dataKey); + if (item) { + const dataIndex = this.latestChartOption.series[0].data.findIndex( + (d: any) => d.id === item.id); + if (dataIndex > -1) { + this.latestChart.dispatchAction({ + type: 'downplay', + dataIndex + }); + } + } + } + + public toggleKey(dataKey: DataKey): void { + const enable = dataKey.hidden; + const dataItem = this.dataItems.find(d => d.dataKey === dataKey); + if (dataItem) { + let dataIndex = this.latestChartOption.series[0].data.findIndex( + (d: any) => d.id === dataItem.id); + dataItem.enabled = enable; + if (!enable && dataIndex > -1) { + this.latestChart.dispatchAction({ + type: 'downplay', + dataIndex + }); + } + this.updateSeriesData(); + dataKey.hidden = !enable; + if (enable) { + dataIndex = this.latestChartOption.series[0].data.findIndex( + (d: any) => d.id === dataItem.id); + if (dataIndex > -1) { + this.latestChart.dispatchAction({ + type: 'highlight', + dataIndex + }); + } + } + } + } + + public destroy(): void { + if (this.shapeResize$) { + this.shapeResize$.disconnect(); + } + if (this.latestChart) { + this.latestChart.dispose(); + } + } + + public resize(): void { + this.onResize(); + } + + public onItemClick(itemClick: ($event: Event, item: LatestChartDataItem) => void) { + this.itemClick = itemClick; + } + + private updateSeriesData(initial = false) { + this.total = 0; + this.totalText = 'N/A'; + let hasValue = false; + for (const dataItem of this.dataItems) { + if (dataItem.enabled && dataItem.hasValue) { + hasValue = true; + this.total += dataItem.value; + } + if (this.settings.showLegend) { + const legendItem = this.legendItems.find(item => item.dataKey === dataItem.dataKey); + if (dataItem.hasValue) { + legendItem.hasValue = true; + legendItem.value = formatValue(dataItem.value, this.decimals, this.units, false); + } else { + legendItem.hasValue = false; + legendItem.value = '--'; + } + } + } + if (this.settings.showTotal || this.settings.showLegend) { + if (hasValue) { + this.totalText = formatValue(this.total, this.decimals, this.units, false); + if (this.settings.showLegend && !this.settings.showTotal) { + this.legendItems[this.legendItems.length - 1].hasValue = true; + this.legendItems[this.legendItems.length - 1].value = this.totalText; + } + } else if (this.settings.showLegend && !this.settings.showTotal) { + this.legendItems[this.legendItems.length - 1].hasValue = false; + this.legendItems[this.legendItems.length - 1].value = '--'; + } + } + this.doUpdateSeriesData(); + this.latestChart.setOption(this.latestChartOption); + this.afterUpdateSeriesData(initial); + } + + private drawChart() { + echartsModule.init(); + this.renderer.setStyle(this.chartElement, 'letterSpacing', 'normal'); + this.latestChart = echarts.init(this.chartElement, null, { + renderer: 'svg', + width: this.settings.autoScale ? this.initialShapeWidth() : undefined, + height: this.settings.autoScale ? this.initialShapeHeight() : undefined, + }); + this.latestChartOption = { + tooltip: { + trigger: this.settings.showTooltip ? 'item' : 'none', + confine: false, + appendTo: 'body', + formatter: (params: CallbackDataParams) => + this.settings.showTooltip + ? latestChartTooltipFormatter(this.renderer, this.settings, params, this.units, this.total) + : undefined, + padding: [4, 8], + backgroundColor: this.settings.tooltipBackgroundColor, + extraCssText: `line-height: 1; backdrop-filter: blur(${this.settings.tooltipBackgroundBlur}px);`, + position: (pos) => [pos[0] + 10, pos[1] + 10] + }, + animation: this.settings.animation.animation, + animationThreshold: this.settings.animation.animationThreshold, + animationDuration: this.settings.animation.animationDuration, + animationEasing: this.settings.animation.animationEasing, + animationDelay: this.settings.animation.animationDelay, + animationDurationUpdate: this.settings.animation.animationDurationUpdate, + animationEasingUpdate: this.settings.animation.animationEasingUpdate, + animationDelayUpdate: this.settings.animation.animationDelayUpdate + }; + this.prepareLatestChartOption(); + this.updateSeriesData(true); + this.renderer.setStyle(this.latestChart.getDom().firstChild, 'overflow', 'visible'); + if (this.settings.autoScale) { + this.renderer.setStyle(this.latestChart.getDom().firstChild, 'position', 'absolute'); + } + this.renderer.setStyle(this.latestChart.getDom().firstChild.firstChild, 'overflow', 'visible'); + this.svgShape = SVG(this.latestChart.getDom().firstChild.firstChild).toRoot(); + this.afterDrawChart(); + this.latestChart.on('click', (params) => { + this.onChartClick(params); + }); + } + + protected onChartClick(params: echarts.ECElementEvent) { + if (params.componentType === 'series') { + if (params.data) { + const data = params.data as any; + if (isDefinedAndNotNull(data?.id)) { + const item = this.dataItems.find(d => d.id === data.id); + if (item && this.itemClick) { + this.itemClick(params.event.event, item); + } + } + } + } + }; + + private onResize() { + const shapeWidth = this.chartElement.offsetWidth; + const shapeHeight = this.chartElement.offsetHeight; + if (shapeWidth && shapeHeight) { + if (!this.latestChart) { + this.drawChart(); + } else { + const width = this.latestChart.getWidth(); + const height = this.latestChart.getHeight(); + if (width !== shapeWidth || height !== shapeHeight) { + this.beforeResize(shapeWidth, shapeHeight); + if (!this.settings.autoScale) { + this.latestChart.resize(); + } else { + let scale: number; + if (shapeWidth < shapeHeight) { + scale = shapeWidth / this.initialShapeWidth(); + } else { + scale = shapeHeight / this.initialShapeHeight(); + } + this.renderer.setStyle(this.latestChart.getDom().firstChild, 'transform', `scale(${scale})`); + } + this.afterResize(shapeWidth, shapeHeight); + } + } + } + } + + protected initSettings() { + } + + protected initialShapeWidth(): number { + return 100; + } + + protected initialShapeHeight(): number { + return 100; + } + + protected beforeResize(_shapeWidth: number, _shapeHeight: number) {}; + + protected afterResize(_shapeWidth: number, _shapeHeight: number) {}; + + protected afterDrawChart() {}; + + protected afterUpdateSeriesData(_initial: boolean) {}; + + protected abstract defaultSettings(): S; + + protected abstract prepareLatestChartOption(): void; + + protected abstract doUpdateSeriesData(): void; + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart-widget.component.ts new file mode 100644 index 0000000000..41f36a43a5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart-widget.component.ts @@ -0,0 +1,86 @@ +/// +/// 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, Input, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { WidgetComponent } from '@home/components/widget/widget.component'; +import { TranslateService } from '@ngx-translate/core'; +import { TbPieChart } from '@home/components/widget/lib/chart/pie-chart'; +import { + LatestChartComponent, + LatestChartComponentCallbacks +} from '@home/components/widget/lib/chart/latest-chart.component'; +import { + pieChartWidgetDefaultSettings, + pieChartWidgetPieChartSettings, + PieChartWidgetSettings +} from '@home/components/widget/lib/chart/pie-chart-widget.models'; + +@Component({ + selector: 'tb-pie-chart-widget', + templateUrl: './latest-chart-widget.component.html', + styleUrls: [], + encapsulation: ViewEncapsulation.None +}) +export class PieChartWidgetComponent implements OnInit { + + @ViewChild('latestChart') + latestChart: LatestChartComponent; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + settings: PieChartWidgetSettings; + + callbacks: LatestChartComponentCallbacks; + + constructor(private widgetComponent: WidgetComponent, + private translate: TranslateService) { + } + + ngOnInit(): void { + this.ctx.$scope.pieChartWidget = this; + this.settings = {...pieChartWidgetDefaultSettings, ...this.ctx.settings}; + this.callbacks = { + createChart: (chartShape, renderer) => { + const settings = pieChartWidgetPieChartSettings(this.settings); + return new TbPieChart(this.ctx, settings, chartShape.nativeElement, renderer, this.translate, true); + }, + onItemClick: ($event: Event, item) => { + const descriptors = this.ctx.actionsApi.getActionDescriptors('sliceClick'); + if ($event && descriptors.length) { + $event.stopPropagation(); + const datasource = item.datasource; + const entityId = datasource ? datasource.entity?.id : null; + const entityName = datasource ? datasource.entityName : null; + const entityLabel = datasource ? datasource.entityLabel : null; + this.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName, item, entityLabel); + } + } + }; + } + + public onInit() { + this.latestChart?.onInit(); + } + + public onDataUpdated() { + this.latestChart?.onDataUpdated(); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart-widget.models.ts new file mode 100644 index 0000000000..05dbef0959 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart-widget.models.ts @@ -0,0 +1,131 @@ +/// +/// 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 { + LatestChartTooltipValueType, + LatestChartWidgetSettings +} from '@home/components/widget/lib/chart/latest-chart.models'; +import { BackgroundType, Font } from '@shared/models/widget-settings.models'; +import { LegendPosition } from '@shared/models/widget.models'; +import { DeepPartial } from '@shared/models/common'; +import { + pieChartAnimationDefaultSettings, + PieChartLabelPosition, + PieChartSettings +} from '@home/components/widget/lib/chart/pie-chart.models'; +import { isDefinedAndNotNull, mergeDeep } from '@core/utils'; +import { EChartsAnimationSettings } from '@home/components/widget/lib/chart/echarts-widget.models'; + +export interface PieChartWidgetSettings extends LatestChartWidgetSettings { + showLabel: boolean; + labelPosition: PieChartLabelPosition; + labelFont: Font; + labelColor: string; + borderWidth: number; + borderColor: string; + radius: number; + clockwise: boolean; +} + +export const pieChartWidgetDefaultSettings: PieChartWidgetSettings = { + showLabel: true, + labelPosition: PieChartLabelPosition.outside, + labelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: 'normal', + lineHeight: '1.2' + }, + labelColor: '#000', + borderWidth: 0, + borderColor: '#000', + radius: 80, + clockwise: false, + sortSeries: false, + animation: mergeDeep({} as EChartsAnimationSettings, + pieChartAnimationDefaultSettings), + showLegend: true, + legendPosition: LegendPosition.bottom, + legendLabelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '16px' + }, + legendLabelColor: 'rgba(0, 0, 0, 0.38)', + legendValueFont: { + family: 'Roboto', + size: 14, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '20px' + }, + legendValueColor: 'rgba(0, 0, 0, 0.87)', + showTooltip: true, + tooltipValueType: LatestChartTooltipValueType.percentage, + tooltipValueDecimals: 0, + tooltipValueFont: { + family: 'Roboto', + size: 13, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '16px' + }, + tooltipValueColor: 'rgba(0, 0, 0, 0.76)', + tooltipBackgroundColor: 'rgba(255, 255, 255, 0.76)', + tooltipBackgroundBlur: 4, + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}; + +export const pieChartWidgetPieChartSettings = (settings: PieChartWidgetSettings): DeepPartial => ({ + autoScale: false, + doughnut: false, + clockwise: settings.clockwise, + sortSeries: settings.sortSeries, + showTotal: false, + animation: settings.animation, + showLegend: settings.showLegend, + showLabel: settings.showLabel, + labelPosition: settings.labelPosition, + labelFont: settings.labelFont, + labelColor: settings.labelColor, + borderWidth: settings.borderWidth, + borderColor: settings.borderColor, + radius: isDefinedAndNotNull(settings.radius) ? settings.radius + '%' : undefined, + emphasisBorderWidth: settings.borderWidth, + emphasisBorderColor: settings.borderColor, + showTooltip: settings.showTooltip, + tooltipValueType: settings.tooltipValueType, + tooltipValueDecimals: settings.tooltipValueDecimals, + tooltipValueFont: settings.tooltipValueFont, + tooltipValueColor: settings.tooltipValueColor, + tooltipBackgroundColor: settings.tooltipBackgroundColor, + tooltipBackgroundBlur: settings.tooltipBackgroundBlur +}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart.models.ts new file mode 100644 index 0000000000..419c4b61bb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart.models.ts @@ -0,0 +1,126 @@ +/// +/// 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 { ColorSettings, constantColor, Font } from '@shared/models/widget-settings.models'; +import { + LatestChartSettings, + LatestChartTooltipValueType +} from '@home/components/widget/lib/chart/latest-chart.models'; +import { mergeDeep } from '@core/utils'; +import { + EChartsAnimationEasing, + EChartsAnimationSettings +} from '@home/components/widget/lib/chart/echarts-widget.models'; + +export enum PieChartLabelPosition { + outside = 'outside', + inside = 'inside' +} + +export const pieChartLabelPositions = Object.keys(PieChartLabelPosition) as PieChartLabelPosition[]; + +export const pieChartLabelPositionTranslations = new Map( + [ + [PieChartLabelPosition.outside, 'widgets.pie-chart.label-position-outside'], + [PieChartLabelPosition.inside, 'widgets.pie-chart.label-position-inside'] + ] +); + +export interface PieChartSettings extends LatestChartSettings { + doughnut: boolean; + radius: string; + clockwise: boolean; + totalValueFont: Font; + totalValueColor: ColorSettings; + showLabel: boolean; + labelPosition: PieChartLabelPosition; + labelFont: Font; + labelColor: string; + borderWidth: number; + borderColor: string; + borderRadius: string; + emphasisScale: boolean; + emphasisBorderWidth: number; + emphasisBorderColor: string; + emphasisShadowBlur: number; + emphasisShadowColor: string; +} + +export const pieChartAnimationDefaultSettings: EChartsAnimationSettings = { + animation: true, + animationThreshold: 2000, + animationDuration: 1000, + animationEasing: EChartsAnimationEasing.cubicOut, + animationDelay: 0, + animationDurationUpdate: 500, + animationEasingUpdate: EChartsAnimationEasing.cubicOut, + animationDelayUpdate: 0 +}; + +export const pieChartDefaultSettings: PieChartSettings = { + autoScale: false, + doughnut: false, + radius: '80%', + clockwise: false, + sortSeries: false, + showTotal: false, + animation: mergeDeep({} as EChartsAnimationSettings, + pieChartAnimationDefaultSettings), + showLegend: true, + totalValueFont: { + family: 'Roboto', + size: 24, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '1' + }, + totalValueColor: constantColor('rgba(0, 0, 0, 0.87)'), + showLabel: false, + labelPosition: PieChartLabelPosition.outside, + labelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: 'normal', + lineHeight: '1' + }, + labelColor: '#000', + borderWidth: 0, + borderColor: '#000', + borderRadius: '0%', + emphasisScale: true, + emphasisBorderWidth: 0, + emphasisBorderColor: '#000', + emphasisShadowBlur: 10, + emphasisShadowColor: 'rgba(0, 0, 0, 0.5)', + showTooltip: true, + tooltipValueType: LatestChartTooltipValueType.percentage, + tooltipValueDecimals: 0, + tooltipValueFont: { + family: 'Roboto', + size: 13, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '16px' + }, + tooltipValueColor: 'rgba(0, 0, 0, 0.76)', + tooltipBackgroundColor: 'rgba(255, 255, 255, 0.76)', + tooltipBackgroundBlur: 4 +}; + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart.ts new file mode 100644 index 0000000000..cfe003732e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart.ts @@ -0,0 +1,194 @@ +/// +/// 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 { pieChartDefaultSettings, PieChartSettings } from '@home/components/widget/lib/chart/pie-chart.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { DeepPartial } from '@shared/models/common'; +import { Renderer2 } from '@angular/core'; +import { ColorProcessor, textStyle } from '@shared/models/widget-settings.models'; +import { PieDataItemOption } from 'echarts/types/src/chart/pie/PieSeries'; +import { Text } from '@svgdotjs/svg.js'; +import { TranslateService } from '@ngx-translate/core'; +import { TbLatestChart } from '@home/components/widget/lib/chart/latest-chart'; +import { formatValue } from '@core/utils'; + +const shapeSize = 134; +const shapeSegmentWidth = 13.4; + +export class TbPieChart extends TbLatestChart { + + private totalValueColor: ColorProcessor; + private totalTextNode: Text; + + constructor(ctx: WidgetContext, + inputSettings: DeepPartial, + chartElement: HTMLElement, + renderer: Renderer2, + translate: TranslateService, + autoResize = true) { + + super(ctx, inputSettings, chartElement, renderer, translate, autoResize); + } + + protected defaultSettings(): PieChartSettings { + return pieChartDefaultSettings; + } + + protected initSettings() { + if (this.settings.showTotal) { + this.totalValueColor = ColorProcessor.fromSettings(this.settings.totalValueColor); + } + } + + protected prepareLatestChartOption() { + const shapeWidth = this.chartElement.offsetWidth; + const shapeHeight = this.chartElement.offsetHeight; + const size = this.settings.autoScale ? shapeSize : Math.min(shapeWidth, shapeHeight); + const innerRadius = size / 2 - shapeSegmentWidth; + const outerRadius = size / 2; + const labelStyle = textStyle(this.settings.labelFont); + labelStyle.fontSize = this.settings.labelFont.size; + labelStyle.lineHeight = labelStyle.fontSize * 1.2; + labelStyle.color = this.settings.labelColor; + this.latestChartOption.series = [ + { + type: 'pie', + clockwise: this.settings.clockwise, + radius: this.settings.doughnut ? [innerRadius, outerRadius] : this.settings.radius, + avoidLabelOverlap: false, + itemStyle: { + borderRadius: this.settings.borderRadius, + borderWidth: this.settings.borderWidth, + borderColor: this.settings.borderColor + }, + label: { + show: this.settings.showLabel, + position: this.settings.labelPosition, + formatter: (params) => { + const percents = params.percent; + const value = formatValue(percents, 0, '%', false); + return `{label|${params.name}\n${value}}`; + }, + rich: { + label: labelStyle + } + }, + emphasis: { + scale: this.settings.emphasisScale, + itemStyle: { + borderColor: this.settings.emphasisBorderColor, + borderWidth: this.settings.emphasisBorderWidth, + shadowColor: this.settings.emphasisShadowColor, + shadowBlur: this.settings.emphasisShadowBlur + }, + label: { + show: this.settings.showLabel + } + }, + animation: this.settings.animation.animation, + animationThreshold: this.settings.animation.animationThreshold, + animationDuration: this.settings.animation.animationDuration, + animationEasing: this.settings.animation.animationEasing, + animationDelay: this.settings.animation.animationDelay, + animationDurationUpdate: this.settings.animation.animationDurationUpdate, + animationEasingUpdate: this.settings.animation.animationEasingUpdate, + animationDelayUpdate: this.settings.animation.animationDelayUpdate + } + ]; + } + + protected afterDrawChart() { + if (this.settings.showTotal) { + this.totalTextNode = this.svgShape.text('').font({ + family: 'Roboto', + leading: 1 + }).attr({'text-anchor': 'middle'}); + this.renderTotal(); + } + }; + + protected doUpdateSeriesData() { + const seriesData: PieDataItemOption[] = []; + const enabledDataItems = this.dataItems.filter(item => item.enabled && item.hasValue); + for (const dataItem of this.dataItems) { + if (dataItem.enabled && dataItem.hasValue) { + seriesData.push( + {id: dataItem.id, value: dataItem.value, name: dataItem.dataKey.label, itemStyle: {color: dataItem.dataKey.color}} + ); + if (this.settings.doughnut && enabledDataItems.length > 1) { + seriesData.push({ + value: 0, name: '', itemStyle: {color: 'transparent'}, emphasis: {disabled: true} + }); + } + } + } + if (this.settings.doughnut) { + for (let i = 1; i < seriesData.length; i += 2) { + seriesData[i].value = this.total / 100; + } + } + this.latestChartOption.series[0].data = seriesData; + } + + protected afterUpdateSeriesData(initial: boolean) { + if (this.settings.showTotal) { + this.totalValueColor.update(this.total); + if (!initial) { + this.renderTotal(); + } + } + }; + + protected initialShapeWidth(): number { + return shapeSize; + } + + protected initialShapeHeight(): number { + return shapeSize; + } + + protected beforeResize(shapeWidth: number, shapeHeight: number) { + if (!this.settings.autoScale) { + if (this.settings.doughnut) { + const size = Math.min(shapeWidth, shapeHeight); + const innerRadius = size / 2 - shapeSegmentWidth; + const outerRadius = size / 2; + this.latestChartOption.series[0].radius = [innerRadius, outerRadius]; + this.latestChart.setOption(this.latestChartOption); + } + } + }; + + protected afterResize(shapeWidth: number, shapeHeight: number) { + if (this.settings.showTotal) { + this.totalTextNode.center((this.settings.autoScale ? shapeSize : shapeWidth) / 2, + (this.settings.autoScale ? shapeSize : shapeHeight) / 2); + } + }; + + private renderTotal() { + this.totalTextNode.text(add => { + add.tspan(this.translate.instant('widgets.latest-chart.total')).font({size: '12px', weight: 400}).fill('rgba(0, 0, 0, 0.38)'); + add.tspan('').newLine().font({size: '4px'}); + add.tspan(this.totalText).newLine().font( + {family: this.settings.totalValueFont.family, + size: this.settings.totalValueFont.size + this.settings.totalValueFont.sizeUnit, + weight: this.settings.totalValueFont.weight, + style: this.settings.totalValueFont.style} + ).fill(this.totalValueColor.color); + }).center(this.svgShape.bbox().width / 2, this.svgShape.bbox().height / 2); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts index c1221536ee..c411737761 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts @@ -24,15 +24,19 @@ import { sortedColorRange } from '@shared/models/widget-settings.models'; import { LegendPosition } from '@shared/models/widget.models'; -import { EChartsShape, EChartsTooltipWidgetSettings } from '@home/components/widget/lib/chart/echarts-widget.models'; +import { + echartsAnimationDefaultSettings, + EChartsAnimationSettings, + EChartsShape, + EChartsTooltipWidgetSettings +} from '@home/components/widget/lib/chart/echarts-widget.models'; import { createTimeSeriesChartVisualMapPiece, defaultTimeSeriesChartXAxisSettings, defaultTimeSeriesChartYAxisSettings, LineSeriesStepType, SeriesFillType, - SeriesLabelPosition, ThresholdLabelPosition, timeSeriesChartAnimationDefaultSettings, - TimeSeriesChartAnimationSettings, + SeriesLabelPosition, ThresholdLabelPosition, timeSeriesChartColorScheme, timeSeriesChartGridDefaultSettings, TimeSeriesChartGridSettings, TimeSeriesChartKeySettings, TimeSeriesChartLineType, @@ -84,7 +88,7 @@ export interface RangeChartWidgetSettings extends EChartsTooltipWidgetSettings { grid: TimeSeriesChartGridSettings; yAxis: TimeSeriesChartYAxisSettings; xAxis: TimeSeriesChartXAxisSettings; - animation: TimeSeriesChartAnimationSettings; + animation: EChartsAnimationSettings; thresholds: TimeSeriesChartThreshold[]; showLegend: boolean; legendPosition: LegendPosition; @@ -150,8 +154,8 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = { xAxis: mergeDeep({} as TimeSeriesChartXAxisSettings, defaultTimeSeriesChartXAxisSettings, {showSplitLines: false} as TimeSeriesChartXAxisSettings), - animation: mergeDeep({} as TimeSeriesChartAnimationSettings, - timeSeriesChartAnimationDefaultSettings), + animation: mergeDeep({} as EChartsAnimationSettings, + echartsAnimationDefaultSettings), thresholds: [], showLegend: true, legendPosition: LegendPosition.top, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts index e03c2a7158..0fd702a95f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts @@ -16,6 +16,8 @@ import { ECharts, + echartsAnimationDefaultSettings, + EChartsAnimationSettings, EChartsOption, EChartsSeriesItem, EChartsShape, @@ -571,64 +573,6 @@ export interface TimeSeriesChartBarWidthSettings { intervalGap: number; } -export enum TimeSeriesChartAnimationEasing { - linear = 'linear', - quadraticIn = 'quadraticIn', - quadraticOut = 'quadraticOut', - quadraticInOut = 'quadraticInOut', - cubicIn = 'cubicIn', - cubicOut = 'cubicOut', - cubicInOut = 'cubicInOut', - quarticIn = 'quarticIn', - quarticOut = 'quarticOut', - quarticInOut = 'quarticInOut', - quinticIn = 'quinticIn', - quinticOut = 'quinticOut', - quinticInOut = 'quinticInOut', - sinusoidalIn = 'sinusoidalIn', - sinusoidalOut = 'sinusoidalOut', - sinusoidalInOut = 'sinusoidalInOut', - exponentialIn = 'exponentialIn', - exponentialOut = 'exponentialOut', - exponentialInOut = 'exponentialInOut', - circularIn = 'circularIn', - circularOut = 'circularOut', - circularInOut = 'circularInOut', - elasticIn = 'elasticIn', - elasticOut = 'elasticOut', - elasticInOut = 'elasticInOut', - backIn = 'backIn', - backOut = 'backOut', - backInOut = 'backInOut', - bounceIn = 'bounceIn', - bounceOut = 'bounceOut', - bounceInOut = 'bounceInOut' -} - -export const timeSeriesChartAnimationEasings = Object.keys(TimeSeriesChartAnimationEasing) as TimeSeriesChartAnimationEasing[]; - -export interface TimeSeriesChartAnimationSettings { - animation: boolean; - animationThreshold: number; - animationDuration: number; - animationEasing: TimeSeriesChartAnimationEasing; - animationDelay: number; - animationDurationUpdate: number; - animationEasingUpdate: TimeSeriesChartAnimationEasing; - animationDelayUpdate: number; -} - -export const timeSeriesChartAnimationDefaultSettings: TimeSeriesChartAnimationSettings = { - animation: true, - animationThreshold: 2000, - animationDuration: 500, - animationEasing: TimeSeriesChartAnimationEasing.cubicOut, - animationDelay: 0, - animationDurationUpdate: 300, - animationEasingUpdate: TimeSeriesChartAnimationEasing.cubicOut, - animationDelayUpdate: 0 -}; - export interface TimeSeriesChartVisualMapPiece { lt?: number; gt?: number; @@ -721,7 +665,7 @@ export interface TimeSeriesChartSettings extends EChartsTooltipWidgetSettings, T grid: TimeSeriesChartGridSettings; yAxes: TimeSeriesChartYAxes; xAxis: TimeSeriesChartXAxisSettings; - animation: TimeSeriesChartAnimationSettings; + animation: EChartsAnimationSettings; barWidthSettings: TimeSeriesChartBarWidthSettings; noAggregationBarWidthSettings: TimeSeriesChartNoAggregationBarWidthSettings; visualMapSettings?: TimeSeriesChartVisualMapSettings; @@ -742,8 +686,8 @@ export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = { }, xAxis: mergeDeep({} as TimeSeriesChartXAxisSettings, defaultTimeSeriesChartXAxisSettings), - animation: mergeDeep({} as TimeSeriesChartAnimationSettings, - timeSeriesChartAnimationDefaultSettings), + animation: mergeDeep({} as EChartsAnimationSettings, + echartsAnimationDefaultSettings), barWidthSettings: { barGap: 0.3, intervalGap: 0.6 diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html index 04b2bff29c..920374ec00 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html @@ -227,9 +227,9 @@
- - +
widget-config.card-appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.html index ac99c1c42e..863663db08 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.html @@ -32,17 +32,17 @@
- {{ 'widgets.doughnut.auto-scale' | translate }} + {{ 'widgets.latest-chart.auto-scale' | translate }}
- {{ 'widgets.doughnut.clockwise-layout' | translate }} + {{ 'widgets.latest-chart.clockwise-layout' | translate }}
- {{ 'widgets.doughnut.sort-series' | translate }} + {{ 'widgets.latest-chart.sort-series' | translate }}
@@ -119,8 +119,8 @@
- - {{ doughnutTooltipValueTypeTranslationMap.get(type) | translate }} + + {{ latestChartTooltipValueTypeTranslationMap.get(type) | translate }} @@ -154,6 +154,9 @@
+ +
{{ 'widgets.background.background' | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.ts index 20910e36af..c71f4b8a8f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.ts @@ -24,19 +24,22 @@ import { import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { formatValue, isDefinedAndNotNull, mergeDeep } from '@core/utils'; import { doughnutDefaultSettings, DoughnutLayout, doughnutLayoutImages, doughnutLayouts, doughnutLayoutTranslations, - DoughnutTooltipValueType, - doughnutTooltipValueTypes, - doughnutTooltipValueTypeTranslations, + DoughnutWidgetSettings, horizontalDoughnutLayoutImages } from '@home/components/widget/lib/chart/doughnut-widget.models'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { + LatestChartTooltipValueType, + latestChartTooltipValueTypes, + latestChartTooltipValueTypeTranslations +} from '@home/components/widget/lib/chart/latest-chart.models'; @Component({ selector: 'tb-doughnut-widget-settings', @@ -62,9 +65,9 @@ export class DoughnutWidgetSettingsComponent extends WidgetSettingsComponent { legendPositionTranslationMap = legendPositionTranslationMap; - doughnutTooltipValueTypes = doughnutTooltipValueTypes; + latestChartTooltipValueTypes = latestChartTooltipValueTypes; - doughnutTooltipValueTypeTranslationMap = doughnutTooltipValueTypeTranslations; + latestChartTooltipValueTypeTranslationMap = latestChartTooltipValueTypeTranslations; doughnutWidgetSettingsForm: UntypedFormGroup; @@ -88,7 +91,7 @@ export class DoughnutWidgetSettingsComponent extends WidgetSettingsComponent { } protected defaultSettings(): WidgetSettings { - return doughnutDefaultSettings(this.horizontal); + return mergeDeep({} as DoughnutWidgetSettings, doughnutDefaultSettings(this.horizontal)); } protected onSettingsSet(settings: WidgetSettings) { @@ -101,6 +104,8 @@ export class DoughnutWidgetSettingsComponent extends WidgetSettingsComponent { totalValueFont: [settings.totalValueFont, []], totalValueColor: [settings.totalValueColor, []], + animation: [settings.animation, []], + showLegend: [settings.showLegend, []], legendPosition: [settings.legendPosition, []], legendLabelFont: [settings.legendLabelFont, []], @@ -175,9 +180,9 @@ export class DoughnutWidgetSettingsComponent extends WidgetSettingsComponent { } private _tooltipValuePreviewFn(): string { - const tooltipValueType: DoughnutTooltipValueType = this.doughnutWidgetSettingsForm.get('tooltipValueType').value; + const tooltipValueType: LatestChartTooltipValueType = this.doughnutWidgetSettingsForm.get('tooltipValueType').value; const decimals: number = this.doughnutWidgetSettingsForm.get('tooltipValueDecimals').value; - if (tooltipValueType === DoughnutTooltipValueType.percentage) { + if (tooltipValueType === LatestChartTooltipValueType.percentage) { return formatValue(35, decimals, '%', false); } else { const units: string = this.widgetConfig.config.units; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/pie-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/pie-chart-widget-settings.component.html new file mode 100644 index 0000000000..3f15f8af34 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/pie-chart-widget-settings.component.html @@ -0,0 +1,184 @@ + + +
+
widgets.pie-chart.pie-chart-card-style
+
+ + {{ 'widgets.pie-chart.label' | translate }} + +
+ + + + {{ pieChartLabelPositionTranslationMap.get(position) | translate }} + + + + + + + +
+
+
+
{{ 'widgets.pie-chart.border' | translate }}
+
+ + +
px
+
+ + +
+
+
+
{{ 'widgets.pie-chart.radius' | translate }}
+
+ + +
%
+
+
+
+
+ + {{ 'widgets.latest-chart.clockwise-layout' | translate }} + +
+
+ + {{ 'widgets.latest-chart.sort-series' | translate }} + +
+
+ + + + + {{ 'widget-config.legend' | translate }} + + + + +
+
{{ 'legend.position' | translate }}
+ + + + {{ legendPositionTranslationMap.get(pos) | translate }} + + + +
+
+
{{ 'legend.label' | translate }}
+
+ + + + +
+
+
+
{{ 'legend.value' | translate }}
+
+ + + + +
+
+
+
+
+
+ + + + + {{ 'widget-config.tooltip' | translate }} + + + + +
+
{{ 'tooltip.value' | translate }}
+
+ + + + {{ latestChartTooltipValueTypeTranslationMap.get(type) | translate }} + + + + + +
widget-config.decimals-suffix
+
+ + + + +
+
+
+
{{ 'tooltip.background-color' | translate }}
+ + +
+
+
{{ 'tooltip.background-blur' | translate }}
+ + +
px
+
+
+
+
+
+ + +
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/pie-chart-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/pie-chart-widget-settings.component.ts new file mode 100644 index 0000000000..ae96753264 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/pie-chart-widget-settings.component.ts @@ -0,0 +1,182 @@ +/// +/// 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 } from '@angular/core'; +import { + legendPositions, + legendPositionTranslationMap, + 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'; +import { formatValue, mergeDeep } from '@core/utils'; +import { + LatestChartTooltipValueType, + latestChartTooltipValueTypes, + latestChartTooltipValueTypeTranslations +} from '@home/components/widget/lib/chart/latest-chart.models'; +import { + pieChartLabelPositions, + pieChartLabelPositionTranslations +} from '@home/components/widget/lib/chart/pie-chart.models'; +import { + pieChartWidgetDefaultSettings, + PieChartWidgetSettings +} from '@home/components/widget/lib/chart/pie-chart-widget.models'; + +@Component({ + selector: 'tb-pie-chart-widget-settings', + templateUrl: './pie-chart-widget-settings.component.html', + styleUrls: [] +}) +export class PieChartWidgetSettingsComponent extends WidgetSettingsComponent { + + pieChartLabelPositions = pieChartLabelPositions; + + pieChartLabelPositionTranslationMap = pieChartLabelPositionTranslations; + + legendPositions = legendPositions; + + legendPositionTranslationMap = legendPositionTranslationMap; + + latestChartTooltipValueTypes = latestChartTooltipValueTypes; + + latestChartTooltipValueTypeTranslationMap = latestChartTooltipValueTypeTranslations; + + pieChartWidgetSettingsForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.pieChartWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return mergeDeep({} as PieChartWidgetSettings, pieChartWidgetDefaultSettings); + } + + protected onSettingsSet(settings: WidgetSettings) { + this.pieChartWidgetSettingsForm = this.fb.group({ + showLabel: [settings.showLabel, []], + labelPosition: [settings.labelPosition, []], + labelFont: [settings.labelFont, []], + labelColor: [settings.labelColor, []], + + borderWidth: [settings.borderWidth, [Validators.min(0)]], + borderColor: [settings.borderColor, []], + + radius: [settings.radius, [Validators.min(0), Validators.max(100)]], + + clockwise: [settings.clockwise, []], + sortSeries: [settings.sortSeries, []], + + animation: [settings.animation, []], + + showLegend: [settings.showLegend, []], + legendPosition: [settings.legendPosition, []], + legendLabelFont: [settings.legendLabelFont, []], + legendLabelColor: [settings.legendLabelColor, []], + legendValueFont: [settings.legendValueFont, []], + legendValueColor: [settings.legendValueColor, []], + + showTooltip: [settings.showTooltip, []], + tooltipValueType: [settings.tooltipValueType, []], + tooltipValueDecimals: [settings.tooltipValueDecimals, []], + tooltipValueFont: [settings.tooltipValueFont, []], + tooltipValueColor: [settings.tooltipValueColor, []], + tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], + tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], + + background: [settings.background, []] + }); + } + + protected validatorTriggers(): string[] { + return ['showLabel', 'showLegend', 'showTooltip']; + } + + protected updateValidators(emitEvent: boolean) { + const showLabel: boolean = this.pieChartWidgetSettingsForm.get('showLabel').value; + const showLegend: boolean = this.pieChartWidgetSettingsForm.get('showLegend').value; + const showTooltip: boolean = this.pieChartWidgetSettingsForm.get('showTooltip').value; + + if (showLabel) { + this.pieChartWidgetSettingsForm.get('labelPosition').enable(); + this.pieChartWidgetSettingsForm.get('labelFont').enable(); + this.pieChartWidgetSettingsForm.get('labelColor').enable(); + } else { + this.pieChartWidgetSettingsForm.get('labelPosition').disable(); + this.pieChartWidgetSettingsForm.get('labelFont').disable(); + this.pieChartWidgetSettingsForm.get('labelColor').disable(); + } + + if (showLegend) { + this.pieChartWidgetSettingsForm.get('legendPosition').enable(); + this.pieChartWidgetSettingsForm.get('legendLabelFont').enable(); + this.pieChartWidgetSettingsForm.get('legendLabelColor').enable(); + this.pieChartWidgetSettingsForm.get('legendValueFont').enable(); + this.pieChartWidgetSettingsForm.get('legendValueColor').enable(); + } else { + this.pieChartWidgetSettingsForm.get('legendPosition').disable(); + this.pieChartWidgetSettingsForm.get('legendLabelFont').disable(); + this.pieChartWidgetSettingsForm.get('legendLabelColor').disable(); + this.pieChartWidgetSettingsForm.get('legendValueFont').disable(); + this.pieChartWidgetSettingsForm.get('legendValueColor').disable(); + } + if (showTooltip) { + this.pieChartWidgetSettingsForm.get('tooltipValueType').enable(); + this.pieChartWidgetSettingsForm.get('tooltipValueDecimals').enable(); + this.pieChartWidgetSettingsForm.get('tooltipValueFont').enable(); + this.pieChartWidgetSettingsForm.get('tooltipValueColor').enable(); + this.pieChartWidgetSettingsForm.get('tooltipBackgroundColor').enable(); + this.pieChartWidgetSettingsForm.get('tooltipBackgroundBlur').enable(); + } else { + this.pieChartWidgetSettingsForm.get('tooltipValueType').disable(); + this.pieChartWidgetSettingsForm.get('tooltipValueDecimals').disable(); + this.pieChartWidgetSettingsForm.get('tooltipValueFont').disable(); + this.pieChartWidgetSettingsForm.get('tooltipValueColor').disable(); + this.pieChartWidgetSettingsForm.get('tooltipBackgroundColor').disable(); + this.pieChartWidgetSettingsForm.get('tooltipBackgroundBlur').disable(); + } + } + + private _valuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(110, decimals, units, false); + } + + private _tooltipValuePreviewFn(): string { + const tooltipValueType: LatestChartTooltipValueType = this.pieChartWidgetSettingsForm.get('tooltipValueType').value; + const decimals: number = this.pieChartWidgetSettingsForm.get('tooltipValueDecimals').value; + if (tooltipValueType === LatestChartTooltipValueType.percentage) { + return formatValue(35, decimals, '%', false); + } else { + const units: string = this.widgetConfig.config.units; + return formatValue(110, decimals, units, false); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html index 63b514c68b..30c5eea4c1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html @@ -303,9 +303,9 @@
- - +
widget-config.card-appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html index 737e2e2c21..6bf8abdb70 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html @@ -254,9 +254,9 @@
- - +
widget-config.card-appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/echarts-animation-settings.component.html similarity index 80% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/echarts-animation-settings.component.html index 5e0ad8e2ec..53246d2cfc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/echarts-animation-settings.component.html @@ -23,61 +23,61 @@ - {{ 'widgets.time-series-chart.animation.animation' | translate }} + {{ 'widgets.echarts.animation.animation' | translate }}
-
{{ 'widgets.time-series-chart.animation.animation-threshold' | translate }}
+
{{ 'widgets.echarts.animation.animation-threshold' | translate }}
ms
-
{{ 'widgets.time-series-chart.animation.animation-duration' | translate }}
+
{{ 'widgets.echarts.animation.animation-duration' | translate }}
ms
-
widgets.time-series-chart.animation.animation-easing
+
widgets.echarts.animation.animation-easing
- + {{ easing }}
-
{{ 'widgets.time-series-chart.animation.animation-delay' | translate }}
+
{{ 'widgets.echarts.animation.animation-delay' | translate }}
ms
-
{{ 'widgets.time-series-chart.animation.update-animation-duration' | translate }}
+
{{ 'widgets.echarts.animation.update-animation-duration' | translate }}
ms
-
widgets.time-series-chart.animation.update-animation-easing
+
widgets.echarts.animation.update-animation-easing
- + {{ easing }}
-
{{ 'widgets.time-series-chart.animation.update-animation-delay' | translate }}
+
{{ 'widgets.echarts.animation.update-animation-delay' | translate }}
ms
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/echarts-animation-settings.component.ts similarity index 83% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/echarts-animation-settings.component.ts index 5da5aa2fe9..b3a10c6298 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/echarts-animation-settings.component.ts @@ -22,34 +22,34 @@ import { UntypedFormGroup, Validators } from '@angular/forms'; -import { - timeSeriesChartAnimationEasings, - TimeSeriesChartAnimationSettings -} from '@home/components/widget/lib/chart/time-series-chart.models'; import { WidgetService } from '@core/http/widget.service'; +import { + echartsAnimationEasings, + EChartsAnimationSettings +} from '@home/components/widget/lib/chart/echarts-widget.models'; @Component({ - selector: 'tb-time-series-chart-animation-settings', - templateUrl: './time-series-chart-animation-settings.component.html', + selector: 'tb-echarts-animation-settings', + templateUrl: './echarts-animation-settings.component.html', styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => TimeSeriesChartAnimationSettingsComponent), + useExisting: forwardRef(() => EchartsAnimationSettingsComponent), multi: true } ] }) -export class TimeSeriesChartAnimationSettingsComponent implements OnInit, ControlValueAccessor { +export class EchartsAnimationSettingsComponent implements OnInit, ControlValueAccessor { settingsExpanded = false; - timeSeriesChartAnimationEasings = timeSeriesChartAnimationEasings; + echartsAnimationEasings = echartsAnimationEasings; @Input() disabled: boolean; - private modelValue: TimeSeriesChartAnimationSettings; + private modelValue: EChartsAnimationSettings; private propagateChange = null; @@ -96,7 +96,7 @@ export class TimeSeriesChartAnimationSettingsComponent implements OnInit, Contro } } - writeValue(value: TimeSeriesChartAnimationSettings): void { + writeValue(value: EChartsAnimationSettings): void { this.modelValue = value; this.animationSettingsFormGroup.patchValue( value, {emitEvent: false} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.html index d171833001..e897a9de8b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.html @@ -83,7 +83,7 @@
widgets.widget-font.preview
-
{{ previewText }}
+