@perses-dev/gauge-chart-plugin 0.9.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__mf/css/async/263.d3010b86.css +1 -0
- package/__mf/css/async/341.d3010b86.css +1 -0
- package/__mf/css/async/759.d3010b86.css +1 -0
- package/__mf/js/{GaugeChart.0b56a463.js → GaugeChart.83b324ad.js} +3 -3
- package/__mf/js/async/177.651d64b7.js +111 -0
- package/__mf/js/async/{238.16f121d4.js → 238.d2cb634f.js} +1 -1
- package/__mf/js/async/{116.577ccfb6.js → 292.0f2f97fb.js} +3 -3
- package/__mf/js/async/301.7224e983.js +1 -0
- package/__mf/js/async/656.e85be6c5.js +1 -0
- package/__mf/js/async/711.9605ad11.js +22 -0
- package/__mf/js/async/{308.b768a783.js → 740.8d6cccb6.js} +1 -1
- package/__mf/js/async/{75.b72fd8c7.js → 75.9648e446.js} +1 -1
- package/__mf/js/async/85.9f00bd55.js +7 -0
- package/__mf/js/async/{292.149a1005.js → 999.2b4fe1ef.js} +1 -1
- package/__mf/js/async/__federation_expose_GaugeChart.0894679a.js +1 -0
- package/__mf/js/{main.6a2260a5.js → main.674f4b65.js} +3 -3
- package/lib/GaugeChartBase.d.ts +3 -5
- package/lib/GaugeChartBase.d.ts.map +1 -1
- package/lib/GaugeChartBase.js +50 -74
- package/lib/GaugeChartBase.js.map +1 -1
- package/lib/GaugeChartPanel.d.ts.map +1 -1
- package/lib/GaugeChartPanel.js +58 -10
- package/lib/GaugeChartPanel.js.map +1 -1
- package/lib/cjs/GaugeChartBase.js +52 -81
- package/lib/cjs/GaugeChartPanel.js +57 -9
- package/mf-manifest.json +19 -19
- package/mf-stats.json +19 -19
- package/package.json +5 -5
- package/__mf/css/async/263.683bd428.css +0 -1
- package/__mf/css/async/341.683bd428.css +0 -1
- package/__mf/css/async/759.683bd428.css +0 -1
- package/__mf/js/async/109.38fe9d18.js +0 -73
- package/__mf/js/async/119.be08efba.js +0 -111
- package/__mf/js/async/288.6d46d8e7.js +0 -7
- package/__mf/js/async/298.bad26699.js +0 -1
- package/__mf/js/async/740.87850b80.js +0 -1
- package/__mf/js/async/__federation_expose_GaugeChart.4c068ee6.js +0 -1
- /package/__mf/js/async/{119.be08efba.js.LICENSE.txt → 177.651d64b7.js.LICENSE.txt} +0 -0
- /package/__mf/js/async/{109.38fe9d18.js.LICENSE.txt → 711.9605ad11.js.LICENSE.txt} +0 -0
- /package/__mf/js/async/{288.6d46d8e7.js.LICENSE.txt → 85.9f00bd55.js.LICENSE.txt} +0 -0
package/lib/GaugeChartBase.js
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
14
|
-
import { EChart, useChartsTheme } from '@perses-dev/components';
|
|
15
|
-
import { formatValue
|
|
14
|
+
import { EChart, useChartsTheme, useDeepMemo } from '@perses-dev/components';
|
|
15
|
+
import { formatValue } from '@perses-dev/core';
|
|
16
16
|
import { use } from 'echarts/core';
|
|
17
17
|
import { GaugeChart as EChartsGaugeChart } from 'echarts/charts';
|
|
18
18
|
import { GridComponent, TitleComponent, TooltipComponent } from 'echarts/components';
|
|
@@ -24,17 +24,40 @@ use([
|
|
|
24
24
|
TooltipComponent,
|
|
25
25
|
CanvasRenderer
|
|
26
26
|
]);
|
|
27
|
-
const PROGRESS_WIDTH = 16;
|
|
28
27
|
// adjusts when to show pointer icon
|
|
29
28
|
const GAUGE_SMALL_BREAKPOINT = 170;
|
|
30
29
|
export function GaugeChartBase(props) {
|
|
31
|
-
const { width, height, data, format, axisLine, max } = props;
|
|
30
|
+
const { width, height, data, format, axisLine, max, valueFontSize, progressWidth, titleFontSize } = props;
|
|
32
31
|
const chartsTheme = useChartsTheme();
|
|
33
32
|
// useDeepMemo ensures value size util does not rerun everytime you hover on the chart
|
|
34
33
|
const option = useDeepMemo(()=>{
|
|
35
34
|
if (data.value === undefined) return chartsTheme.noDataOption;
|
|
36
|
-
//
|
|
37
|
-
const
|
|
35
|
+
// Base configuration shared by both series (= progress & scale)
|
|
36
|
+
const baseGaugeConfig = {
|
|
37
|
+
type: 'gauge',
|
|
38
|
+
center: [
|
|
39
|
+
'50%',
|
|
40
|
+
'65%'
|
|
41
|
+
],
|
|
42
|
+
startAngle: 200,
|
|
43
|
+
endAngle: -20,
|
|
44
|
+
min: 0,
|
|
45
|
+
max: max,
|
|
46
|
+
axisTick: {
|
|
47
|
+
show: false
|
|
48
|
+
},
|
|
49
|
+
splitLine: {
|
|
50
|
+
show: false
|
|
51
|
+
},
|
|
52
|
+
axisLabel: {
|
|
53
|
+
show: false
|
|
54
|
+
},
|
|
55
|
+
data: [
|
|
56
|
+
{
|
|
57
|
+
value: data.value
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
};
|
|
38
61
|
return {
|
|
39
62
|
title: {
|
|
40
63
|
show: false
|
|
@@ -43,28 +66,18 @@ export function GaugeChartBase(props) {
|
|
|
43
66
|
show: false
|
|
44
67
|
},
|
|
45
68
|
series: [
|
|
69
|
+
// Inner gauge (progress)
|
|
46
70
|
{
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
'50%',
|
|
50
|
-
'65%'
|
|
51
|
-
],
|
|
52
|
-
radius: '86%',
|
|
53
|
-
startAngle: 200,
|
|
54
|
-
endAngle: -20,
|
|
55
|
-
min: 0,
|
|
56
|
-
max,
|
|
71
|
+
...baseGaugeConfig,
|
|
72
|
+
radius: '90%',
|
|
57
73
|
silent: true,
|
|
58
74
|
progress: {
|
|
59
75
|
show: true,
|
|
60
|
-
width:
|
|
76
|
+
width: progressWidth,
|
|
61
77
|
itemStyle: {
|
|
62
78
|
color: 'auto'
|
|
63
79
|
}
|
|
64
80
|
},
|
|
65
|
-
pointer: {
|
|
66
|
-
show: false
|
|
67
|
-
},
|
|
68
81
|
axisLine: {
|
|
69
82
|
lineStyle: {
|
|
70
83
|
color: [
|
|
@@ -73,22 +86,12 @@ export function GaugeChartBase(props) {
|
|
|
73
86
|
'rgba(127,127,127,0.35)'
|
|
74
87
|
]
|
|
75
88
|
],
|
|
76
|
-
width:
|
|
89
|
+
width: progressWidth
|
|
77
90
|
}
|
|
78
91
|
},
|
|
79
|
-
|
|
80
|
-
show: false,
|
|
81
|
-
distance: 0
|
|
82
|
-
},
|
|
83
|
-
splitLine: {
|
|
92
|
+
pointer: {
|
|
84
93
|
show: false
|
|
85
94
|
},
|
|
86
|
-
axisLabel: {
|
|
87
|
-
show: false,
|
|
88
|
-
distance: -18,
|
|
89
|
-
color: '#999',
|
|
90
|
-
fontSize: 12
|
|
91
|
-
},
|
|
92
95
|
anchor: {
|
|
93
96
|
show: false
|
|
94
97
|
},
|
|
@@ -97,24 +100,12 @@ export function GaugeChartBase(props) {
|
|
|
97
100
|
},
|
|
98
101
|
detail: {
|
|
99
102
|
show: false
|
|
100
|
-
}
|
|
101
|
-
data: [
|
|
102
|
-
{
|
|
103
|
-
value: data.value
|
|
104
|
-
}
|
|
105
|
-
]
|
|
103
|
+
}
|
|
106
104
|
},
|
|
105
|
+
// Outer gauge (scale & display)
|
|
107
106
|
{
|
|
108
|
-
|
|
109
|
-
center: [
|
|
110
|
-
'50%',
|
|
111
|
-
'65%'
|
|
112
|
-
],
|
|
107
|
+
...baseGaugeConfig,
|
|
113
108
|
radius: '100%',
|
|
114
|
-
startAngle: 200,
|
|
115
|
-
endAngle: -20,
|
|
116
|
-
min: 0,
|
|
117
|
-
max,
|
|
118
109
|
pointer: {
|
|
119
110
|
show: true,
|
|
120
111
|
// pointer hidden for small panels, path taken from ex: https://echarts.apache.org/examples/en/editor.html?c=gauge-grade
|
|
@@ -129,16 +120,8 @@ export function GaugeChartBase(props) {
|
|
|
129
120
|
color: 'auto'
|
|
130
121
|
}
|
|
131
122
|
},
|
|
132
|
-
axisLine,
|
|
133
|
-
|
|
134
|
-
show: false
|
|
135
|
-
},
|
|
136
|
-
splitLine: {
|
|
137
|
-
show: false
|
|
138
|
-
},
|
|
139
|
-
axisLabel: {
|
|
140
|
-
show: false
|
|
141
|
-
},
|
|
123
|
+
axisLine: axisLine,
|
|
124
|
+
// `detail` is the text displayed in the middle
|
|
142
125
|
detail: {
|
|
143
126
|
show: true,
|
|
144
127
|
width: '60%',
|
|
@@ -148,7 +131,7 @@ export function GaugeChartBase(props) {
|
|
|
148
131
|
'-9%'
|
|
149
132
|
],
|
|
150
133
|
color: 'inherit',
|
|
151
|
-
fontSize:
|
|
134
|
+
fontSize: valueFontSize,
|
|
152
135
|
formatter: data.value === null ? // at this level because the `formatter` function argument is `NaN`
|
|
153
136
|
// when the value is `null`, making it difficult to differentiate
|
|
154
137
|
// `null` from a true `NaN` case.
|
|
@@ -170,7 +153,7 @@ export function GaugeChartBase(props) {
|
|
|
170
153
|
'55%'
|
|
171
154
|
],
|
|
172
155
|
overflow: 'truncate',
|
|
173
|
-
fontSize:
|
|
156
|
+
fontSize: titleFontSize,
|
|
174
157
|
width: width * 0.8
|
|
175
158
|
}
|
|
176
159
|
}
|
|
@@ -185,29 +168,22 @@ export function GaugeChartBase(props) {
|
|
|
185
168
|
chartsTheme,
|
|
186
169
|
format,
|
|
187
170
|
axisLine,
|
|
188
|
-
max
|
|
171
|
+
max,
|
|
172
|
+
valueFontSize,
|
|
173
|
+
progressWidth,
|
|
174
|
+
titleFontSize
|
|
189
175
|
]);
|
|
190
176
|
return /*#__PURE__*/ _jsx(EChart, {
|
|
191
|
-
|
|
177
|
+
style: {
|
|
192
178
|
width: width,
|
|
193
|
-
height: height
|
|
179
|
+
height: height
|
|
180
|
+
},
|
|
181
|
+
sx: {
|
|
194
182
|
padding: `${chartsTheme.container.padding.default}px`
|
|
195
183
|
},
|
|
196
184
|
option: option,
|
|
197
185
|
theme: chartsTheme.echartsTheme
|
|
198
186
|
});
|
|
199
187
|
}
|
|
200
|
-
/**
|
|
201
|
-
* Responsive font size depending on number of characters, clamp used
|
|
202
|
-
* to ensure size stays within given range
|
|
203
|
-
*/ export function getResponsiveValueSize(value, format, width, height) {
|
|
204
|
-
const MIN_SIZE = 3;
|
|
205
|
-
const MAX_SIZE = 24;
|
|
206
|
-
const SIZE_MULTIPLIER = 0.7;
|
|
207
|
-
const formattedValue = typeof value === 'number' ? formatValue(value, format) : `${value}`;
|
|
208
|
-
const valueCharacters = formattedValue.length ?? 2;
|
|
209
|
-
const valueSize = Math.min(width, height) / valueCharacters * SIZE_MULTIPLIER;
|
|
210
|
-
return `clamp(${MIN_SIZE}px, ${valueSize}px, ${MAX_SIZE}px)`;
|
|
211
|
-
}
|
|
212
188
|
|
|
213
189
|
//# sourceMappingURL=GaugeChartBase.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/GaugeChartBase.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { EChart, useChartsTheme } from '@perses-dev/components';\nimport { formatValue, useDeepMemo, FormatOptions } from '@perses-dev/core';\nimport { use, EChartsCoreOption } from 'echarts/core';\nimport { GaugeChart as EChartsGaugeChart, GaugeSeriesOption } from 'echarts/charts';\nimport { GridComponent, TitleComponent, TooltipComponent } from 'echarts/components';\nimport { CanvasRenderer } from 'echarts/renderers';\nimport { ReactElement } from 'react';\n\nuse([EChartsGaugeChart, GridComponent, TitleComponent, TooltipComponent, CanvasRenderer]);\n\nconst PROGRESS_WIDTH = 16;\n\n// adjusts when to show pointer icon\nconst GAUGE_SMALL_BREAKPOINT = 170;\n\nexport type GaugeChartValue = number | null | undefined;\n\nexport type GaugeSeries = {\n value: GaugeChartValue;\n label: string;\n};\n\nexport interface GaugeChartBaseProps {\n width: number;\n height: number;\n data: GaugeSeries;\n format: FormatOptions;\n axisLine: GaugeSeriesOption['axisLine'];\n max?: number;\n}\n\nexport function GaugeChartBase(props: GaugeChartBaseProps): ReactElement {\n const { width, height, data, format, axisLine, max } = props;\n const chartsTheme = useChartsTheme();\n\n // useDeepMemo ensures value size util does not rerun everytime you hover on the chart\n const option: EChartsCoreOption = useDeepMemo(() => {\n if (data.value === undefined) return chartsTheme.noDataOption;\n\n // adjusts fontSize depending on number of characters\n const valueSizeClamp = getResponsiveValueSize(data.value, format, width, height);\n\n return {\n title: {\n show: false,\n },\n tooltip: {\n show: false,\n },\n series: [\n {\n type: 'gauge',\n center: ['50%', '65%'],\n radius: '86%',\n startAngle: 200,\n endAngle: -20,\n min: 0,\n max,\n silent: true,\n progress: {\n show: true,\n width: PROGRESS_WIDTH,\n itemStyle: {\n color: 'auto',\n },\n },\n pointer: {\n show: false,\n },\n axisLine: {\n lineStyle: {\n color: [[1, 'rgba(127,127,127,0.35)']], // TODO (sjcobb): use future chart theme colors\n width: PROGRESS_WIDTH,\n },\n },\n axisTick: {\n show: false,\n distance: 0,\n },\n splitLine: {\n show: false,\n },\n axisLabel: {\n show: false,\n distance: -18,\n color: '#999',\n fontSize: 12,\n },\n anchor: {\n show: false,\n },\n title: {\n show: false,\n },\n detail: {\n show: false,\n },\n data: [\n {\n value: data.value,\n },\n ],\n },\n {\n type: 'gauge',\n center: ['50%', '65%'],\n radius: '100%',\n startAngle: 200,\n endAngle: -20,\n min: 0,\n max,\n pointer: {\n show: true,\n // pointer hidden for small panels, path taken from ex: https://echarts.apache.org/examples/en/editor.html?c=gauge-grade\n icon: width > GAUGE_SMALL_BREAKPOINT ? 'path://M12.8,0.7l12,40.1H0.7L12.8,0.7z' : 'none',\n length: 10,\n width: 5,\n offsetCenter: [0, '-49%'],\n itemStyle: {\n color: 'auto',\n },\n },\n axisLine,\n axisTick: {\n show: false,\n },\n splitLine: {\n show: false,\n },\n axisLabel: {\n show: false,\n },\n detail: {\n show: true,\n width: '60%',\n borderRadius: 8,\n offsetCenter: [0, '-9%'],\n color: 'inherit', // allows value color to match active threshold color\n fontSize: valueSizeClamp,\n formatter:\n data.value === null\n ? // We use a different function when we *know* the value is null\n // at this level because the `formatter` function argument is `NaN`\n // when the value is `null`, making it difficult to differentiate\n // `null` from a true `NaN` case.\n (): string => 'null'\n : (value: number): string | undefined => {\n return formatValue(value, format);\n },\n },\n data: [\n {\n value: data.value,\n name: data.label,\n // TODO: new UX for series names, create separate React component or reuse ListLegendItem\n // https://echarts.apache.org/en/option.html#series-gauge.data.title\n title: {\n show: true,\n color: chartsTheme.echartsTheme.textStyle?.color ?? 'inherit', // series name font color\n offsetCenter: [0, '55%'],\n overflow: 'truncate',\n fontSize: 12,\n width: width * 0.8,\n },\n },\n ],\n },\n ],\n };\n }, [data, width, height, chartsTheme, format, axisLine, max]);\n\n return (\n <EChart\n sx={{\n width: width,\n height: height,\n padding: `${chartsTheme.container.padding.default}px`,\n }}\n option={option}\n theme={chartsTheme.echartsTheme}\n />\n );\n}\n\n/**\n * Responsive font size depending on number of characters, clamp used\n * to ensure size stays within given range\n */\nexport function getResponsiveValueSize(\n value: number | null,\n format: FormatOptions,\n width: number,\n height: number\n): string {\n const MIN_SIZE = 3;\n const MAX_SIZE = 24;\n const SIZE_MULTIPLIER = 0.7;\n const formattedValue = typeof value === 'number' ? formatValue(value, format) : `${value}`;\n const valueCharacters = formattedValue.length ?? 2;\n const valueSize = (Math.min(width, height) / valueCharacters) * SIZE_MULTIPLIER;\n return `clamp(${MIN_SIZE}px, ${valueSize}px, ${MAX_SIZE}px)`;\n}\n"],"names":["EChart","useChartsTheme","formatValue","useDeepMemo","use","GaugeChart","EChartsGaugeChart","GridComponent","TitleComponent","TooltipComponent","CanvasRenderer","PROGRESS_WIDTH","GAUGE_SMALL_BREAKPOINT","GaugeChartBase","props","width","height","data","format","axisLine","max","chartsTheme","option","value","undefined","noDataOption","valueSizeClamp","getResponsiveValueSize","title","show","tooltip","series","type","center","radius","startAngle","endAngle","min","silent","progress","itemStyle","color","pointer","lineStyle","axisTick","distance","splitLine","axisLabel","fontSize","anchor","detail","icon","length","offsetCenter","borderRadius","formatter","name","label","echartsTheme","textStyle","overflow","sx","padding","container","default","theme","MIN_SIZE","MAX_SIZE","SIZE_MULTIPLIER","formattedValue","valueCharacters","valueSize","Math"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;;AAEjC,SAASA,MAAM,EAAEC,cAAc,QAAQ,yBAAyB;AAChE,SAASC,WAAW,EAAEC,WAAW,QAAuB,mBAAmB;AAC3E,SAASC,GAAG,QAA2B,eAAe;AACtD,SAASC,cAAcC,iBAAiB,QAA2B,iBAAiB;AACpF,SAASC,aAAa,EAAEC,cAAc,EAAEC,gBAAgB,QAAQ,qBAAqB;AACrF,SAASC,cAAc,QAAQ,oBAAoB;AAGnDN,IAAI;IAACE;IAAmBC;IAAeC;IAAgBC;IAAkBC;CAAe;AAExF,MAAMC,iBAAiB;AAEvB,oCAAoC;AACpC,MAAMC,yBAAyB;AAkB/B,OAAO,SAASC,eAAeC,KAA0B;IACvD,MAAM,EAAEC,KAAK,EAAEC,MAAM,EAAEC,IAAI,EAAEC,MAAM,EAAEC,QAAQ,EAAEC,GAAG,EAAE,GAAGN;IACvD,MAAMO,cAAcpB;IAEpB,sFAAsF;IACtF,MAAMqB,SAA4BnB,YAAY;QAC5C,IAAIc,KAAKM,KAAK,KAAKC,WAAW,OAAOH,YAAYI,YAAY;QAE7D,qDAAqD;QACrD,MAAMC,iBAAiBC,uBAAuBV,KAAKM,KAAK,EAAEL,QAAQH,OAAOC;QAEzE,OAAO;YACLY,OAAO;gBACLC,MAAM;YACR;YACAC,SAAS;gBACPD,MAAM;YACR;YACAE,QAAQ;gBACN;oBACEC,MAAM;oBACNC,QAAQ;wBAAC;wBAAO;qBAAM;oBACtBC,QAAQ;oBACRC,YAAY;oBACZC,UAAU,CAAC;oBACXC,KAAK;oBACLjB;oBACAkB,QAAQ;oBACRC,UAAU;wBACRV,MAAM;wBACNd,OAAOJ;wBACP6B,WAAW;4BACTC,OAAO;wBACT;oBACF;oBACAC,SAAS;wBACPb,MAAM;oBACR;oBACAV,UAAU;wBACRwB,WAAW;4BACTF,OAAO;gCAAC;oCAAC;oCAAG;iCAAyB;6BAAC;4BACtC1B,OAAOJ;wBACT;oBACF;oBACAiC,UAAU;wBACRf,MAAM;wBACNgB,UAAU;oBACZ;oBACAC,WAAW;wBACTjB,MAAM;oBACR;oBACAkB,WAAW;wBACTlB,MAAM;wBACNgB,UAAU,CAAC;wBACXJ,OAAO;wBACPO,UAAU;oBACZ;oBACAC,QAAQ;wBACNpB,MAAM;oBACR;oBACAD,OAAO;wBACLC,MAAM;oBACR;oBACAqB,QAAQ;wBACNrB,MAAM;oBACR;oBACAZ,MAAM;wBACJ;4BACEM,OAAON,KAAKM,KAAK;wBACnB;qBACD;gBACH;gBACA;oBACES,MAAM;oBACNC,QAAQ;wBAAC;wBAAO;qBAAM;oBACtBC,QAAQ;oBACRC,YAAY;oBACZC,UAAU,CAAC;oBACXC,KAAK;oBACLjB;oBACAsB,SAAS;wBACPb,MAAM;wBACN,wHAAwH;wBACxHsB,MAAMpC,QAAQH,yBAAyB,2CAA2C;wBAClFwC,QAAQ;wBACRrC,OAAO;wBACPsC,cAAc;4BAAC;4BAAG;yBAAO;wBACzBb,WAAW;4BACTC,OAAO;wBACT;oBACF;oBACAtB;oBACAyB,UAAU;wBACRf,MAAM;oBACR;oBACAiB,WAAW;wBACTjB,MAAM;oBACR;oBACAkB,WAAW;wBACTlB,MAAM;oBACR;oBACAqB,QAAQ;wBACNrB,MAAM;wBACNd,OAAO;wBACPuC,cAAc;wBACdD,cAAc;4BAAC;4BAAG;yBAAM;wBACxBZ,OAAO;wBACPO,UAAUtB;wBACV6B,WACEtC,KAAKM,KAAK,KAAK,OAEX,mEAAmE;wBACnE,iEAAiE;wBACjE,iCAAiC;wBACjC,IAAc,SACd,CAACA;4BACC,OAAOrB,YAAYqB,OAAOL;wBAC5B;oBACR;oBACAD,MAAM;wBACJ;4BACEM,OAAON,KAAKM,KAAK;4BACjBiC,MAAMvC,KAAKwC,KAAK;4BAChB,yFAAyF;4BACzF,oEAAoE;4BACpE7B,OAAO;gCACLC,MAAM;gCACNY,OAAOpB,YAAYqC,YAAY,CAACC,SAAS,EAAElB,SAAS;gCACpDY,cAAc;oCAAC;oCAAG;iCAAM;gCACxBO,UAAU;gCACVZ,UAAU;gCACVjC,OAAOA,QAAQ;4BACjB;wBACF;qBACD;gBACH;aACD;QACH;IACF,GAAG;QAACE;QAAMF;QAAOC;QAAQK;QAAaH;QAAQC;QAAUC;KAAI;IAE5D,qBACE,KAACpB;QACC6D,IAAI;YACF9C,OAAOA;YACPC,QAAQA;YACR8C,SAAS,GAAGzC,YAAY0C,SAAS,CAACD,OAAO,CAACE,OAAO,CAAC,EAAE,CAAC;QACvD;QACA1C,QAAQA;QACR2C,OAAO5C,YAAYqC,YAAY;;AAGrC;AAEA;;;CAGC,GACD,OAAO,SAAS/B,uBACdJ,KAAoB,EACpBL,MAAqB,EACrBH,KAAa,EACbC,MAAc;IAEd,MAAMkD,WAAW;IACjB,MAAMC,WAAW;IACjB,MAAMC,kBAAkB;IACxB,MAAMC,iBAAiB,OAAO9C,UAAU,WAAWrB,YAAYqB,OAAOL,UAAU,GAAGK,OAAO;IAC1F,MAAM+C,kBAAkBD,eAAejB,MAAM,IAAI;IACjD,MAAMmB,YAAY,AAACC,KAAKnC,GAAG,CAACtB,OAAOC,UAAUsD,kBAAmBF;IAChE,OAAO,CAAC,MAAM,EAAEF,SAAS,IAAI,EAAEK,UAAU,IAAI,EAAEJ,SAAS,GAAG,CAAC;AAC9D"}
|
|
1
|
+
{"version":3,"sources":["../../src/GaugeChartBase.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { EChart, useChartsTheme, useDeepMemo } from '@perses-dev/components';\nimport { formatValue, FormatOptions } from '@perses-dev/core';\nimport { use, EChartsCoreOption } from 'echarts/core';\nimport { GaugeChart as EChartsGaugeChart, GaugeSeriesOption } from 'echarts/charts';\nimport { GridComponent, TitleComponent, TooltipComponent } from 'echarts/components';\nimport { CanvasRenderer } from 'echarts/renderers';\nimport { ReactElement } from 'react';\n\nuse([EChartsGaugeChart, GridComponent, TitleComponent, TooltipComponent, CanvasRenderer]);\n\n// adjusts when to show pointer icon\nconst GAUGE_SMALL_BREAKPOINT = 170;\n\nexport type GaugeChartValue = number | null | undefined;\n\nexport type GaugeSeries = {\n value: GaugeChartValue;\n label: string;\n};\n\nexport interface GaugeChartBaseProps {\n width: number;\n height: number;\n data: GaugeSeries;\n format: FormatOptions;\n axisLine: GaugeSeriesOption['axisLine'];\n max?: number;\n valueFontSize: string;\n progressWidth: number;\n titleFontSize: number;\n}\n\nexport function GaugeChartBase(props: GaugeChartBaseProps): ReactElement {\n const { width, height, data, format, axisLine, max, valueFontSize, progressWidth, titleFontSize } = props;\n const chartsTheme = useChartsTheme();\n\n // useDeepMemo ensures value size util does not rerun everytime you hover on the chart\n const option: EChartsCoreOption = useDeepMemo(() => {\n if (data.value === undefined) return chartsTheme.noDataOption;\n\n // Base configuration shared by both series (= progress & scale)\n const baseGaugeConfig = {\n type: 'gauge' as const,\n center: ['50%', '65%'] as [string, string],\n startAngle: 200,\n endAngle: -20,\n min: 0,\n max: max,\n axisTick: {\n show: false,\n },\n splitLine: {\n show: false,\n },\n axisLabel: {\n show: false,\n },\n data: [\n {\n value: data.value,\n },\n ],\n };\n\n return {\n title: {\n show: false,\n },\n tooltip: {\n show: false,\n },\n series: [\n // Inner gauge (progress)\n {\n ...baseGaugeConfig,\n radius: '90%',\n silent: true,\n progress: {\n show: true,\n width: progressWidth,\n itemStyle: {\n color: 'auto',\n },\n },\n axisLine: {\n lineStyle: {\n color: [[1, 'rgba(127,127,127,0.35)']], // TODO (sjcobb): use future chart theme colors\n width: progressWidth,\n },\n },\n pointer: {\n show: false,\n },\n anchor: {\n show: false,\n },\n title: {\n show: false,\n },\n detail: {\n show: false,\n },\n },\n // Outer gauge (scale & display)\n {\n ...baseGaugeConfig,\n radius: '100%',\n pointer: {\n show: true,\n // pointer hidden for small panels, path taken from ex: https://echarts.apache.org/examples/en/editor.html?c=gauge-grade\n icon: width > GAUGE_SMALL_BREAKPOINT ? 'path://M12.8,0.7l12,40.1H0.7L12.8,0.7z' : 'none',\n length: 10,\n width: 5,\n offsetCenter: [0, '-49%'],\n itemStyle: {\n color: 'auto',\n },\n },\n axisLine: axisLine,\n // `detail` is the text displayed in the middle\n detail: {\n show: true,\n width: '60%',\n borderRadius: 8,\n offsetCenter: [0, '-9%'],\n color: 'inherit', // allows value color to match active threshold color\n fontSize: valueFontSize,\n formatter:\n data.value === null\n ? // We use a different function when we *know* the value is null\n // at this level because the `formatter` function argument is `NaN`\n // when the value is `null`, making it difficult to differentiate\n // `null` from a true `NaN` case.\n (): string => 'null'\n : (value: number): string | undefined => {\n return formatValue(value, format);\n },\n },\n data: [\n {\n value: data.value,\n name: data.label,\n // TODO: new UX for series names, create separate React component or reuse ListLegendItem\n // https://echarts.apache.org/en/option.html#series-gauge.data.title\n title: {\n show: true,\n color: chartsTheme.echartsTheme.textStyle?.color ?? 'inherit', // series name font color\n offsetCenter: [0, '55%'],\n overflow: 'truncate',\n fontSize: titleFontSize,\n width: width * 0.8,\n },\n },\n ],\n },\n ],\n };\n }, [data, width, height, chartsTheme, format, axisLine, max, valueFontSize, progressWidth, titleFontSize]);\n\n return (\n <EChart\n style={{\n width: width,\n height: height,\n }}\n sx={{\n padding: `${chartsTheme.container.padding.default}px`,\n }}\n option={option}\n theme={chartsTheme.echartsTheme}\n />\n );\n}\n"],"names":["EChart","useChartsTheme","useDeepMemo","formatValue","use","GaugeChart","EChartsGaugeChart","GridComponent","TitleComponent","TooltipComponent","CanvasRenderer","GAUGE_SMALL_BREAKPOINT","GaugeChartBase","props","width","height","data","format","axisLine","max","valueFontSize","progressWidth","titleFontSize","chartsTheme","option","value","undefined","noDataOption","baseGaugeConfig","type","center","startAngle","endAngle","min","axisTick","show","splitLine","axisLabel","title","tooltip","series","radius","silent","progress","itemStyle","color","lineStyle","pointer","anchor","detail","icon","length","offsetCenter","borderRadius","fontSize","formatter","name","label","echartsTheme","textStyle","overflow","style","sx","padding","container","default","theme"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GaugeChartPanel.d.ts","sourceRoot":"","sources":["../../src/GaugeChartPanel.tsx"],"names":[],"mappings":"AAeA,OAAO,
|
|
1
|
+
{"version":3,"file":"GaugeChartPanel.d.ts","sourceRoot":"","sources":["../../src/GaugeChartPanel.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAoE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACpH,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAW,MAAM,OAAO,CAAC;AAC9C,OAAO,EAIL,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAyD7B,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;AAEjF,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,GAAG,IAAI,CAgIhF;AAED,wBAAgB,iBAAiB,CAAC,EAAE,iBAAiB,EAAE,EAAE,oBAAoB,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAUxG"}
|
package/lib/GaugeChartPanel.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
14
14
|
import { Box, Skeleton, Stack } from '@mui/material';
|
|
15
15
|
import { useChartsTheme } from '@perses-dev/components';
|
|
16
|
-
import { CalculationsMap, DEFAULT_CALCULATION } from '@perses-dev/core';
|
|
16
|
+
import { CalculationsMap, DEFAULT_CALCULATION, formatValue } from '@perses-dev/core';
|
|
17
17
|
import merge from 'lodash/merge';
|
|
18
18
|
import { useMemo } from 'react';
|
|
19
19
|
import { DEFAULT_FORMAT, DEFAULT_MAX_PERCENT, DEFAULT_MAX_PERCENT_DECIMAL } from './gauge-chart-model';
|
|
@@ -25,6 +25,40 @@ const EMPTY_GAUGE_SERIES = {
|
|
|
25
25
|
};
|
|
26
26
|
const GAUGE_MIN_WIDTH = 90;
|
|
27
27
|
const PANEL_PADDING_OFFSET = 20;
|
|
28
|
+
/**
|
|
29
|
+
* Calculate responsive progress width based on panel dimensions
|
|
30
|
+
*/ function getResponsiveProgressWidth(width, height) {
|
|
31
|
+
const MIN_WIDTH = 10;
|
|
32
|
+
const MAX_WIDTH = 48;
|
|
33
|
+
const RATIO = 0.1; // 10% of the smaller dimension
|
|
34
|
+
const minSize = Math.min(width, height);
|
|
35
|
+
// Use RATIO of the smaller dimension as base, with reasonable min/max bounds
|
|
36
|
+
return Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, Math.round(minSize * RATIO)));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Responsive font size depending on number of characters and panel dimensions.
|
|
40
|
+
* Uses clamp to ensure the text never overflows and scales appropriately with panel size.
|
|
41
|
+
* (Value refers to the main number value displayed inside the gauge)
|
|
42
|
+
*/ function getResponsiveValueFontSize(value, format, width, height) {
|
|
43
|
+
const MIN_SIZE = 8;
|
|
44
|
+
const MAX_SIZE = 64;
|
|
45
|
+
const formattedValue = typeof value === 'number' ? formatValue(value, format) : `${value}`;
|
|
46
|
+
const valueTextLength = Math.max(formattedValue.length, 6); // Ensure a minimum length to avoid overly large text for short values
|
|
47
|
+
const availableSpace = Math.min(width, height);
|
|
48
|
+
const fontSize = availableSpace / valueTextLength;
|
|
49
|
+
return `clamp(${MIN_SIZE}px, ${fontSize}px, ${MAX_SIZE}px)`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Calculate responsive title font size based on panel dimensions
|
|
53
|
+
* (Title refers to the text displayed below the gauge as a legend)
|
|
54
|
+
*/ function getResponsiveTitleFontSize(width, height) {
|
|
55
|
+
const MIN_SIZE = 10;
|
|
56
|
+
const MAX_SIZE = 16;
|
|
57
|
+
const RATIO = 0.06; // Use 6% of the smaller dimension as base
|
|
58
|
+
const size = Math.round(Math.min(width, height) * RATIO);
|
|
59
|
+
// Scale based on panel size, with reasonable min/max bounds
|
|
60
|
+
return Math.max(MIN_SIZE, Math.min(MAX_SIZE, size));
|
|
61
|
+
}
|
|
28
62
|
export function GaugeChartPanel(props) {
|
|
29
63
|
const { spec: pluginSpec, contentDimensions, queryResults } = props;
|
|
30
64
|
const { calculation, max, legend } = pluginSpec;
|
|
@@ -64,30 +98,40 @@ export function GaugeChartPanel(props) {
|
|
|
64
98
|
thresholdMax = format.unit === 'percent' ? DEFAULT_MAX_PERCENT : DEFAULT_MAX_PERCENT_DECIMAL;
|
|
65
99
|
}
|
|
66
100
|
const axisLineColors = convertThresholds(thresholds, format, thresholdMax, thresholdsColors);
|
|
101
|
+
// accounts for showing a separate chart for each time series
|
|
102
|
+
let chartWidth = contentDimensions.width / gaugeData.length - PANEL_PADDING_OFFSET;
|
|
103
|
+
if (chartWidth < GAUGE_MIN_WIDTH && gaugeData.length > 1) {
|
|
104
|
+
// enables horizontal scroll when charts overflow outside of panel
|
|
105
|
+
chartWidth = GAUGE_MIN_WIDTH;
|
|
106
|
+
}
|
|
107
|
+
// Calculate responsive values based on chart dimensions
|
|
108
|
+
const progressWidth = getResponsiveProgressWidth(chartWidth, contentDimensions.height);
|
|
109
|
+
const axisLineWidth = Math.round(progressWidth * 0.2); // Axis line width is 20% of progress width
|
|
110
|
+
const titleFontSize = getResponsiveTitleFontSize(chartWidth, contentDimensions.height);
|
|
67
111
|
const axisLine = {
|
|
68
112
|
show: true,
|
|
69
113
|
lineStyle: {
|
|
70
|
-
width:
|
|
114
|
+
width: axisLineWidth,
|
|
71
115
|
color: axisLineColors
|
|
72
116
|
}
|
|
73
117
|
};
|
|
74
118
|
// no data message handled inside chart component
|
|
75
119
|
if (!gaugeData.length) {
|
|
120
|
+
const emptyValueFontSize = getResponsiveValueFontSize(null, format, contentDimensions.width, contentDimensions.height);
|
|
121
|
+
const emptyProgressWidth = getResponsiveProgressWidth(contentDimensions.width, contentDimensions.height);
|
|
122
|
+
const emptyTitleFontSize = getResponsiveTitleFontSize(contentDimensions.width, contentDimensions.height);
|
|
76
123
|
return /*#__PURE__*/ _jsx(GaugeChartBase, {
|
|
77
124
|
width: contentDimensions.width,
|
|
78
125
|
height: contentDimensions.height,
|
|
79
126
|
data: EMPTY_GAUGE_SERIES,
|
|
80
127
|
format: format,
|
|
81
128
|
axisLine: axisLine,
|
|
82
|
-
max: thresholdMax
|
|
129
|
+
max: thresholdMax,
|
|
130
|
+
valueFontSize: emptyValueFontSize,
|
|
131
|
+
progressWidth: emptyProgressWidth,
|
|
132
|
+
titleFontSize: emptyTitleFontSize
|
|
83
133
|
});
|
|
84
134
|
}
|
|
85
|
-
// accounts for showing a separate chart for each time series
|
|
86
|
-
let chartWidth = contentDimensions.width / gaugeData.length - PANEL_PADDING_OFFSET;
|
|
87
|
-
if (chartWidth < GAUGE_MIN_WIDTH && gaugeData.length > 1) {
|
|
88
|
-
// enables horizontal scroll when charts overflow outside of panel
|
|
89
|
-
chartWidth = GAUGE_MIN_WIDTH;
|
|
90
|
-
}
|
|
91
135
|
const hasMultipleCharts = gaugeData.length > 1;
|
|
92
136
|
return /*#__PURE__*/ _jsx(Stack, {
|
|
93
137
|
direction: "row",
|
|
@@ -99,6 +143,7 @@ export function GaugeChartPanel(props) {
|
|
|
99
143
|
overflowX: gaugeData.length > 1 ? 'scroll' : 'auto'
|
|
100
144
|
},
|
|
101
145
|
children: gaugeData.map((series, seriesIndex)=>{
|
|
146
|
+
const fontSize = getResponsiveValueFontSize(series.value ?? null, format, chartWidth, contentDimensions.height);
|
|
102
147
|
return /*#__PURE__*/ _jsx(Box, {
|
|
103
148
|
children: /*#__PURE__*/ _jsx(GaugeChartBase, {
|
|
104
149
|
width: chartWidth,
|
|
@@ -106,7 +151,10 @@ export function GaugeChartPanel(props) {
|
|
|
106
151
|
data: series,
|
|
107
152
|
format: format,
|
|
108
153
|
axisLine: axisLine,
|
|
109
|
-
max: thresholdMax
|
|
154
|
+
max: thresholdMax,
|
|
155
|
+
valueFontSize: fontSize,
|
|
156
|
+
progressWidth: progressWidth,
|
|
157
|
+
titleFontSize: titleFontSize
|
|
110
158
|
})
|
|
111
159
|
}, `gauge-series-${seriesIndex}`);
|
|
112
160
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/GaugeChartPanel.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { Box, Skeleton, Stack } from '@mui/material';\nimport { useChartsTheme } from '@perses-dev/components';\nimport { CalculationsMap, DEFAULT_CALCULATION, TimeSeriesData } from '@perses-dev/core';\nimport { PanelProps } from '@perses-dev/plugin-system';\nimport type { GaugeSeriesOption } from 'echarts';\nimport merge from 'lodash/merge';\nimport { ReactElement, useMemo } from 'react';\nimport {\n DEFAULT_FORMAT,\n DEFAULT_MAX_PERCENT,\n DEFAULT_MAX_PERCENT_DECIMAL,\n GaugeChartOptions,\n} from './gauge-chart-model';\nimport { convertThresholds, defaultThresholdInput } from './thresholds';\nimport { GaugeChartBase, GaugeSeries } from './GaugeChartBase';\n\nconst EMPTY_GAUGE_SERIES: GaugeSeries = { label: '', value: undefined };\nconst GAUGE_MIN_WIDTH = 90;\nconst PANEL_PADDING_OFFSET = 20;\n\nexport type GaugeChartPanelProps = PanelProps<GaugeChartOptions, TimeSeriesData>;\n\nexport function GaugeChartPanel(props: GaugeChartPanelProps): ReactElement | null {\n const { spec: pluginSpec, contentDimensions, queryResults } = props;\n const { calculation, max, legend } = pluginSpec;\n\n const { thresholds: thresholdsColors } = useChartsTheme();\n\n /* Legend setting just added to the cue schema\n This line assures that if legend setting doesn't exist (for old gauge setting records),\n The legend shows normally as before. If it exists, then it checks the show property.\n */\n const showLegend = legend?.show ?? true;\n\n // ensures all default format properties set if undef\n const format = merge({}, DEFAULT_FORMAT, pluginSpec.format);\n\n const thresholds = pluginSpec.thresholds ?? defaultThresholdInput;\n\n const gaugeData = useMemo((): GaugeSeries[] => {\n const seriesData: GaugeSeries[] = [];\n\n if (!queryResults[0]?.data?.series?.length) {\n return seriesData;\n }\n\n if (!CalculationsMap[calculation]) {\n console.warn(`Invalid GaugeChart panel calculation ${calculation}, fallback to ${DEFAULT_CALCULATION}`);\n }\n\n const calculate = CalculationsMap[calculation] ?? CalculationsMap[DEFAULT_CALCULATION];\n\n for (const timeSeries of queryResults[0].data.series) {\n seriesData.push({\n value: calculate(timeSeries.values),\n label: showLegend ? (timeSeries.formattedName ?? '') : '',\n });\n }\n return seriesData;\n }, [queryResults, calculation, showLegend]);\n\n if (!contentDimensions) return null;\n\n // needed for end value of last threshold color segment\n let thresholdMax = max;\n if (thresholdMax === undefined) {\n thresholdMax = format.unit === 'percent' ? DEFAULT_MAX_PERCENT : DEFAULT_MAX_PERCENT_DECIMAL;\n }\n const axisLineColors = convertThresholds(thresholds, format, thresholdMax, thresholdsColors);\n\n const axisLine: GaugeSeriesOption['axisLine'] = {\n show: true,\n lineStyle: {\n width: 5,\n color: axisLineColors,\n },\n };\n\n // no data message handled inside chart component\n if (!gaugeData.length) {\n return (\n <GaugeChartBase\n width={contentDimensions.width}\n height={contentDimensions.height}\n data={EMPTY_GAUGE_SERIES}\n format={format}\n axisLine={axisLine}\n max={thresholdMax}\n />\n );\n }\n\n // accounts for showing a separate chart for each time series\n let chartWidth = contentDimensions.width / gaugeData.length - PANEL_PADDING_OFFSET;\n if (chartWidth < GAUGE_MIN_WIDTH && gaugeData.length > 1) {\n // enables horizontal scroll when charts overflow outside of panel\n chartWidth = GAUGE_MIN_WIDTH;\n }\n\n const hasMultipleCharts = gaugeData.length > 1;\n\n return (\n <Stack\n direction=\"row\"\n spacing={hasMultipleCharts ? 2 : 0}\n justifyContent={hasMultipleCharts ? 'left' : 'center'}\n alignItems=\"center\"\n sx={{\n // so scrollbar only shows when necessary\n overflowX: gaugeData.length > 1 ? 'scroll' : 'auto',\n }}\n >\n {gaugeData.map((series, seriesIndex) => {\n return (\n <Box key={`gauge-series-${seriesIndex}`}>\n <GaugeChartBase\n width={chartWidth}\n height={contentDimensions.height}\n data={series}\n format={format}\n axisLine={axisLine}\n max={thresholdMax}\n />\n </Box>\n );\n })}\n </Stack>\n );\n}\n\nexport function GaugeChartLoading({ contentDimensions }: GaugeChartPanelProps): React.ReactElement | null {\n if (!contentDimensions) return null;\n return (\n <Skeleton\n sx={{ margin: '0 auto' }}\n variant=\"circular\"\n width={contentDimensions.width > contentDimensions.height ? contentDimensions.height : contentDimensions.width}\n height={contentDimensions.height}\n />\n );\n}\n"],"names":["Box","Skeleton","Stack","useChartsTheme","CalculationsMap","DEFAULT_CALCULATION","merge","useMemo","DEFAULT_FORMAT","DEFAULT_MAX_PERCENT","DEFAULT_MAX_PERCENT_DECIMAL","convertThresholds","defaultThresholdInput","GaugeChartBase","EMPTY_GAUGE_SERIES","label","value","undefined","GAUGE_MIN_WIDTH","PANEL_PADDING_OFFSET","GaugeChartPanel","props","spec","pluginSpec","contentDimensions","queryResults","calculation","max","legend","thresholds","thresholdsColors","showLegend","show","format","gaugeData","seriesData","data","series","length","console","warn","calculate","timeSeries","push","values","formattedName","thresholdMax","unit","axisLineColors","axisLine","lineStyle","width","color","height","chartWidth","hasMultipleCharts","direction","spacing","justifyContent","alignItems","sx","overflowX","map","seriesIndex","GaugeChartLoading","margin","variant"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/GaugeChartPanel.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { Box, Skeleton, Stack } from '@mui/material';\nimport { useChartsTheme } from '@perses-dev/components';\nimport { CalculationsMap, DEFAULT_CALCULATION, FormatOptions, formatValue, TimeSeriesData } from '@perses-dev/core';\nimport { PanelProps } from '@perses-dev/plugin-system';\nimport type { GaugeSeriesOption } from 'echarts';\nimport merge from 'lodash/merge';\nimport { ReactElement, useMemo } from 'react';\nimport {\n DEFAULT_FORMAT,\n DEFAULT_MAX_PERCENT,\n DEFAULT_MAX_PERCENT_DECIMAL,\n GaugeChartOptions,\n} from './gauge-chart-model';\nimport { convertThresholds, defaultThresholdInput } from './thresholds';\nimport { GaugeChartBase, GaugeSeries } from './GaugeChartBase';\n\nconst EMPTY_GAUGE_SERIES: GaugeSeries = { label: '', value: undefined };\nconst GAUGE_MIN_WIDTH = 90;\nconst PANEL_PADDING_OFFSET = 20;\n\n/**\n * Calculate responsive progress width based on panel dimensions\n */\nfunction getResponsiveProgressWidth(width: number, height: number): number {\n const MIN_WIDTH = 10;\n const MAX_WIDTH = 48;\n const RATIO = 0.1; // 10% of the smaller dimension\n\n const minSize = Math.min(width, height);\n // Use RATIO of the smaller dimension as base, with reasonable min/max bounds\n return Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, Math.round(minSize * RATIO)));\n}\n\n/**\n * Responsive font size depending on number of characters and panel dimensions.\n * Uses clamp to ensure the text never overflows and scales appropriately with panel size.\n * (Value refers to the main number value displayed inside the gauge)\n */\nfunction getResponsiveValueFontSize(\n value: number | null,\n format: FormatOptions,\n width: number,\n height: number\n): string {\n const MIN_SIZE = 8;\n const MAX_SIZE = 64;\n const formattedValue = typeof value === 'number' ? formatValue(value, format) : `${value}`;\n\n const valueTextLength = Math.max(formattedValue.length, 6); // Ensure a minimum length to avoid overly large text for short values\n const availableSpace = Math.min(width, height);\n const fontSize = availableSpace / valueTextLength;\n\n return `clamp(${MIN_SIZE}px, ${fontSize}px, ${MAX_SIZE}px)`;\n}\n\n/**\n * Calculate responsive title font size based on panel dimensions\n * (Title refers to the text displayed below the gauge as a legend)\n */\nfunction getResponsiveTitleFontSize(width: number, height: number): number {\n const MIN_SIZE = 10;\n const MAX_SIZE = 16;\n const RATIO = 0.06; // Use 6% of the smaller dimension as base\n\n const size = Math.round(Math.min(width, height) * RATIO);\n // Scale based on panel size, with reasonable min/max bounds\n return Math.max(MIN_SIZE, Math.min(MAX_SIZE, size));\n}\n\nexport type GaugeChartPanelProps = PanelProps<GaugeChartOptions, TimeSeriesData>;\n\nexport function GaugeChartPanel(props: GaugeChartPanelProps): ReactElement | null {\n const { spec: pluginSpec, contentDimensions, queryResults } = props;\n const { calculation, max, legend } = pluginSpec;\n\n const { thresholds: thresholdsColors } = useChartsTheme();\n\n /* Legend setting just added to the cue schema\n This line assures that if legend setting doesn't exist (for old gauge setting records),\n The legend shows normally as before. If it exists, then it checks the show property.\n */\n const showLegend = legend?.show ?? true;\n\n // ensures all default format properties set if undef\n const format = merge({}, DEFAULT_FORMAT, pluginSpec.format);\n\n const thresholds = pluginSpec.thresholds ?? defaultThresholdInput;\n\n const gaugeData = useMemo((): GaugeSeries[] => {\n const seriesData: GaugeSeries[] = [];\n\n if (!queryResults[0]?.data?.series?.length) {\n return seriesData;\n }\n\n if (!CalculationsMap[calculation]) {\n console.warn(`Invalid GaugeChart panel calculation ${calculation}, fallback to ${DEFAULT_CALCULATION}`);\n }\n\n const calculate = CalculationsMap[calculation] ?? CalculationsMap[DEFAULT_CALCULATION];\n\n for (const timeSeries of queryResults[0].data.series) {\n seriesData.push({\n value: calculate(timeSeries.values),\n label: showLegend ? (timeSeries.formattedName ?? '') : '',\n });\n }\n return seriesData;\n }, [queryResults, calculation, showLegend]);\n\n if (!contentDimensions) return null;\n\n // needed for end value of last threshold color segment\n let thresholdMax = max;\n if (thresholdMax === undefined) {\n thresholdMax = format.unit === 'percent' ? DEFAULT_MAX_PERCENT : DEFAULT_MAX_PERCENT_DECIMAL;\n }\n const axisLineColors = convertThresholds(thresholds, format, thresholdMax, thresholdsColors);\n\n // accounts for showing a separate chart for each time series\n let chartWidth = contentDimensions.width / gaugeData.length - PANEL_PADDING_OFFSET;\n if (chartWidth < GAUGE_MIN_WIDTH && gaugeData.length > 1) {\n // enables horizontal scroll when charts overflow outside of panel\n chartWidth = GAUGE_MIN_WIDTH;\n }\n\n // Calculate responsive values based on chart dimensions\n const progressWidth = getResponsiveProgressWidth(chartWidth, contentDimensions.height);\n const axisLineWidth = Math.round(progressWidth * 0.2); // Axis line width is 20% of progress width\n const titleFontSize = getResponsiveTitleFontSize(chartWidth, contentDimensions.height);\n\n const axisLine: GaugeSeriesOption['axisLine'] = {\n show: true,\n lineStyle: {\n width: axisLineWidth,\n color: axisLineColors,\n },\n };\n\n // no data message handled inside chart component\n if (!gaugeData.length) {\n const emptyValueFontSize = getResponsiveValueFontSize(\n null,\n format,\n contentDimensions.width,\n contentDimensions.height\n );\n const emptyProgressWidth = getResponsiveProgressWidth(contentDimensions.width, contentDimensions.height);\n const emptyTitleFontSize = getResponsiveTitleFontSize(contentDimensions.width, contentDimensions.height);\n\n return (\n <GaugeChartBase\n width={contentDimensions.width}\n height={contentDimensions.height}\n data={EMPTY_GAUGE_SERIES}\n format={format}\n axisLine={axisLine}\n max={thresholdMax}\n valueFontSize={emptyValueFontSize}\n progressWidth={emptyProgressWidth}\n titleFontSize={emptyTitleFontSize}\n />\n );\n }\n\n const hasMultipleCharts = gaugeData.length > 1;\n\n return (\n <Stack\n direction=\"row\"\n spacing={hasMultipleCharts ? 2 : 0}\n justifyContent={hasMultipleCharts ? 'left' : 'center'}\n alignItems=\"center\"\n sx={{\n // so scrollbar only shows when necessary\n overflowX: gaugeData.length > 1 ? 'scroll' : 'auto',\n }}\n >\n {gaugeData.map((series, seriesIndex) => {\n const fontSize = getResponsiveValueFontSize(series.value ?? null, format, chartWidth, contentDimensions.height);\n\n return (\n <Box key={`gauge-series-${seriesIndex}`}>\n <GaugeChartBase\n width={chartWidth}\n height={contentDimensions.height}\n data={series}\n format={format}\n axisLine={axisLine}\n max={thresholdMax}\n valueFontSize={fontSize}\n progressWidth={progressWidth}\n titleFontSize={titleFontSize}\n />\n </Box>\n );\n })}\n </Stack>\n );\n}\n\nexport function GaugeChartLoading({ contentDimensions }: GaugeChartPanelProps): React.ReactElement | null {\n if (!contentDimensions) return null;\n return (\n <Skeleton\n sx={{ margin: '0 auto' }}\n variant=\"circular\"\n width={contentDimensions.width > contentDimensions.height ? contentDimensions.height : contentDimensions.width}\n height={contentDimensions.height}\n />\n );\n}\n"],"names":["Box","Skeleton","Stack","useChartsTheme","CalculationsMap","DEFAULT_CALCULATION","formatValue","merge","useMemo","DEFAULT_FORMAT","DEFAULT_MAX_PERCENT","DEFAULT_MAX_PERCENT_DECIMAL","convertThresholds","defaultThresholdInput","GaugeChartBase","EMPTY_GAUGE_SERIES","label","value","undefined","GAUGE_MIN_WIDTH","PANEL_PADDING_OFFSET","getResponsiveProgressWidth","width","height","MIN_WIDTH","MAX_WIDTH","RATIO","minSize","Math","min","max","round","getResponsiveValueFontSize","format","MIN_SIZE","MAX_SIZE","formattedValue","valueTextLength","length","availableSpace","fontSize","getResponsiveTitleFontSize","size","GaugeChartPanel","props","spec","pluginSpec","contentDimensions","queryResults","calculation","legend","thresholds","thresholdsColors","showLegend","show","gaugeData","seriesData","data","series","console","warn","calculate","timeSeries","push","values","formattedName","thresholdMax","unit","axisLineColors","chartWidth","progressWidth","axisLineWidth","titleFontSize","axisLine","lineStyle","color","emptyValueFontSize","emptyProgressWidth","emptyTitleFontSize","valueFontSize","hasMultipleCharts","direction","spacing","justifyContent","alignItems","sx","overflowX","map","seriesIndex","GaugeChartLoading","margin","variant"],"mappings":"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"}
|