@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.
Files changed (40) hide show
  1. package/__mf/css/async/263.d3010b86.css +1 -0
  2. package/__mf/css/async/341.d3010b86.css +1 -0
  3. package/__mf/css/async/759.d3010b86.css +1 -0
  4. package/__mf/js/{GaugeChart.0b56a463.js → GaugeChart.83b324ad.js} +3 -3
  5. package/__mf/js/async/177.651d64b7.js +111 -0
  6. package/__mf/js/async/{238.16f121d4.js → 238.d2cb634f.js} +1 -1
  7. package/__mf/js/async/{116.577ccfb6.js → 292.0f2f97fb.js} +3 -3
  8. package/__mf/js/async/301.7224e983.js +1 -0
  9. package/__mf/js/async/656.e85be6c5.js +1 -0
  10. package/__mf/js/async/711.9605ad11.js +22 -0
  11. package/__mf/js/async/{308.b768a783.js → 740.8d6cccb6.js} +1 -1
  12. package/__mf/js/async/{75.b72fd8c7.js → 75.9648e446.js} +1 -1
  13. package/__mf/js/async/85.9f00bd55.js +7 -0
  14. package/__mf/js/async/{292.149a1005.js → 999.2b4fe1ef.js} +1 -1
  15. package/__mf/js/async/__federation_expose_GaugeChart.0894679a.js +1 -0
  16. package/__mf/js/{main.6a2260a5.js → main.674f4b65.js} +3 -3
  17. package/lib/GaugeChartBase.d.ts +3 -5
  18. package/lib/GaugeChartBase.d.ts.map +1 -1
  19. package/lib/GaugeChartBase.js +50 -74
  20. package/lib/GaugeChartBase.js.map +1 -1
  21. package/lib/GaugeChartPanel.d.ts.map +1 -1
  22. package/lib/GaugeChartPanel.js +58 -10
  23. package/lib/GaugeChartPanel.js.map +1 -1
  24. package/lib/cjs/GaugeChartBase.js +52 -81
  25. package/lib/cjs/GaugeChartPanel.js +57 -9
  26. package/mf-manifest.json +19 -19
  27. package/mf-stats.json +19 -19
  28. package/package.json +5 -5
  29. package/__mf/css/async/263.683bd428.css +0 -1
  30. package/__mf/css/async/341.683bd428.css +0 -1
  31. package/__mf/css/async/759.683bd428.css +0 -1
  32. package/__mf/js/async/109.38fe9d18.js +0 -73
  33. package/__mf/js/async/119.be08efba.js +0 -111
  34. package/__mf/js/async/288.6d46d8e7.js +0 -7
  35. package/__mf/js/async/298.bad26699.js +0 -1
  36. package/__mf/js/async/740.87850b80.js +0 -1
  37. package/__mf/js/async/__federation_expose_GaugeChart.4c068ee6.js +0 -1
  38. /package/__mf/js/async/{119.be08efba.js.LICENSE.txt → 177.651d64b7.js.LICENSE.txt} +0 -0
  39. /package/__mf/js/async/{109.38fe9d18.js.LICENSE.txt → 711.9605ad11.js.LICENSE.txt} +0 -0
  40. /package/__mf/js/async/{288.6d46d8e7.js.LICENSE.txt → 85.9f00bd55.js.LICENSE.txt} +0 -0
@@ -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, useDeepMemo } from '@perses-dev/core';
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
- // adjusts fontSize depending on number of characters
37
- const valueSizeClamp = getResponsiveValueSize(data.value, format, width, height);
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
- type: 'gauge',
48
- center: [
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: PROGRESS_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: PROGRESS_WIDTH
89
+ width: progressWidth
77
90
  }
78
91
  },
79
- axisTick: {
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
- type: 'gauge',
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
- axisTick: {
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: valueSizeClamp,
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: 12,
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
- sx: {
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,EAAwC,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAW,MAAM,OAAO,CAAC;AAC9C,OAAO,EAIL,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAQ7B,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;AAEjF,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,GAAG,IAAI,CA0GhF;AAED,wBAAgB,iBAAiB,CAAC,EAAE,iBAAiB,EAAE,EAAE,oBAAoB,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAUxG"}
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"}
@@ -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: 5,
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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"}