Skip to content

Commit 79dc43f

Browse files
feat(highcharts): implement gauge-basic (#9538)
## Implementation: `gauge-basic` - javascript/highcharts Implements the **javascript/highcharts** version of `gauge-basic`. **File:** `plots/gauge-basic/implementations/javascript/highcharts.js` **Parent Issue:** #857 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/28477104839)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent b3cd06d commit 79dc43f

2 files changed

Lines changed: 401 additions & 0 deletions

File tree

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// anyplot.ai
2+
// gauge-basic: Basic Gauge Chart
3+
// Library: highcharts 12.6.0 | JavaScript 22.23.0
4+
// Quality: 91/100 | Created: 2026-06-30
5+
//# anyplot-orientation: square
6+
// anyplot.ai
7+
// gauge-basic: Basic Gauge Chart
8+
// Library: Highcharts 12.6.0 | Node 22
9+
// License: Highcharts — commercial license, free for non-commercial use (highcharts.com/license)
10+
// Quality: pending | Created: 2026-06-30
11+
12+
const t = window.ANYPLOT_TOKENS;
13+
14+
// --- Data (Customer Satisfaction Score) ------------------------------------
15+
const MIN_VAL = 0;
16+
const MAX_VAL = 100;
17+
const CUR_VAL = 72;
18+
const THRESH_LO = 30;
19+
const THRESH_HI = 70;
20+
21+
const arcRed = THRESH_LO - MIN_VAL; // 30
22+
const arcAmber = THRESH_HI - THRESH_LO; // 40
23+
const arcGreen = MAX_VAL - THRESH_HI; // 30
24+
25+
const TITLE = "gauge-basic · javascript · highcharts · anyplot.ai";
26+
const titleFs = Math.max(15, Math.round(22 * Math.min(1, 67 / TITLE.length))) + "px";
27+
28+
// --- Chart -----------------------------------------------------------------
29+
const chart = Highcharts.chart("container", {
30+
chart: {
31+
backgroundColor: "transparent",
32+
animation: false,
33+
style: { fontFamily: "inherit" },
34+
margin: [60, 40, 140, 40]
35+
},
36+
credits: { enabled: false },
37+
colors: t.palette,
38+
title: {
39+
text: TITLE,
40+
style: { color: t.ink, fontSize: titleFs, fontWeight: "600" }
41+
},
42+
plotOptions: {
43+
pie: {
44+
animation: false,
45+
startAngle: -90,
46+
endAngle: 90,
47+
center: ["50%", "70%"],
48+
dataLabels: { enabled: false },
49+
borderWidth: 0
50+
},
51+
series: { animation: false }
52+
},
53+
tooltip: { enabled: false },
54+
legend: { enabled: false },
55+
series: [{
56+
type: "pie",
57+
name: "CSAT Zones",
58+
innerSize: "62%",
59+
size: "90%",
60+
data: [
61+
{ y: arcRed, color: "#AE3030" },
62+
{ y: arcAmber, color: "#DDCC77" },
63+
{ y: arcGreen, color: "#009E73" }
64+
]
65+
}]
66+
});
67+
68+
// --- SVG overlays (needle, value, legend) ----------------------------------
69+
const ps = chart.series[0];
70+
const cx = chart.plotLeft + ps.center[0];
71+
const cy = chart.plotTop + ps.center[1];
72+
const outerR = ps.center[2] / 2;
73+
const innerR = ps.center[3] / 2;
74+
75+
// Needle angle: -90° (left/MIN) to +90° (right/MAX), clockwise from 12 o'clock
76+
const pct = (CUR_VAL - MIN_VAL) / (MAX_VAL - MIN_VAL);
77+
const angleDeg = -90 + pct * 180;
78+
const angleRad = angleDeg * Math.PI / 180;
79+
80+
// SVG coords: x = cx + r·sin(θ), y = cy - r·cos(θ)
81+
const needleLen = outerR * 0.82;
82+
const tipX = cx + needleLen * Math.sin(angleRad);
83+
const tipY = cy - needleLen * Math.cos(angleRad);
84+
85+
// Needle line
86+
chart.renderer.path(["M", cx, cy, "L", tipX, tipY])
87+
.attr({ stroke: t.ink, "stroke-width": 5, "stroke-linecap": "round", zIndex: 5 })
88+
.add();
89+
90+
// Hub dot at pivot
91+
chart.renderer.circle(cx, cy, outerR * 0.065)
92+
.attr({ fill: t.ink, zIndex: 6 })
93+
.add();
94+
95+
// Large value inside donut hole
96+
const valY = cy - innerR * 0.45;
97+
chart.renderer.text(CUR_VAL.toString(), cx, valY)
98+
.attr({ align: "center", zIndex: 5 })
99+
.css({ fontSize: "66px", fontWeight: "700", color: t.ink, fontFamily: "inherit" })
100+
.add();
101+
102+
chart.renderer.text("/ 100", cx, valY + 38)
103+
.attr({ align: "center", zIndex: 5 })
104+
.css({ fontSize: "18px", color: t.inkSoft, fontFamily: "inherit" })
105+
.add();
106+
107+
// Metric name below gauge
108+
chart.renderer.text("Customer Satisfaction Score", cx, cy + 52)
109+
.attr({ align: "center", zIndex: 5 })
110+
.css({ fontSize: "18px", fontWeight: "600", color: t.ink, fontFamily: "inherit" })
111+
.add();
112+
113+
// Zone legend (3 columns)
114+
const zoneBaseY = cy + 112;
115+
const colW = chart.chartWidth / 3;
116+
[
117+
{ range: "0 – 30", label: "Needs Improvement", color: "#AE3030" },
118+
{ range: "30 – 70", label: "Satisfactory", color: "#DDCC77" },
119+
{ range: "70 – 100", label: "Excellent", color: "#009E73" }
120+
].forEach((z, i) => {
121+
const zx = colW * i + colW / 2;
122+
chart.renderer.text(z.range, zx, zoneBaseY)
123+
.attr({ align: "center", zIndex: 5 })
124+
.css({ fontSize: "15px", fontWeight: "700", color: z.color, fontFamily: "inherit" })
125+
.add();
126+
chart.renderer.text(z.label, zx, zoneBaseY + 24)
127+
.attr({ align: "center", zIndex: 5 })
128+
.css({ fontSize: "14px", color: t.inkSoft, fontFamily: "inherit" })
129+
.add();
130+
});

0 commit comments

Comments
 (0)