// Gauge component template
#let gauge(data) = {
let label = data.label
let value = data.value
let min_val = data.at("min", default: 0.0)
let max_val = data.at("max", default: 100.0)
let thresholds = data.at("thresholds", default: ())
let style = data.at("style", default: "circular")
// Calculate percentage
let pct = if max_val > min_val {
((value - min_val) / (max_val - min_val)) * 100
} else {
0
}
// Determine color based on thresholds
let gauge_color = rgb("#3b82f6")
for threshold in thresholds {
if value >= threshold.value {
gauge_color = rgb(threshold.color)
}
}
block(
width: 100%,
breakable: false,
{
if style == "circular" {
// Circular gauge
align(center, box(width: 84pt, height: 84pt, {
place(center + horizon, circle(
radius: 40pt,
stroke: 3pt + color-border,
fill: none
))
place(center + horizon, circle(
radius: 35pt,
stroke: 5pt + gauge_color,
fill: none
))
place(center + horizon,
text(weight: "bold", size: 16pt, fill: color-text, str(calc.round(value, digits: 1)))
)
}))
} else if style == "vertical" {
// Thermometer style
align(center, {
rect(
width: 30pt,
height: 120pt,
stroke: 1pt + color-border,
fill: color-surface,
radius: 15pt,
{
place(
bottom,
rect(
width: 100%,
height: pct * 1%,
fill: gauge_color,
stroke: none,
radius: (bottom: 15pt)
)
)
}
)
})
} else {
// Horizontal bar
rect(
width: 200pt,
height: 20pt,
stroke: 1pt + color-border,
fill: color-surface,
radius: 10pt,
{
place(
left,
rect(
width: pct * 1%,
height: 100%,
fill: gauge_color,
stroke: none,
radius: (left: 10pt)
)
)
}
)
}
v(8pt)
align(center, text(size: 10pt, weight: "medium", fill: color-text, label))
align(center, text(size: 9pt, fill: color-text-muted, str(value) + " / " + str(max_val)))
}
)
}