glacier-web-cli 0.5.0

Command-line tool for creating and running Glacier applications.
{# One series drawn as kind="bar" | "line" | "area" from (x label, value) pairs; label says what the chart shows, name what the values are. #}
{% props label: String, series: Vec<(String, f64)>, kind: String = "bar", name: String = "Value", unit: String = "" %}
{%- let top = series.iter().map(|(_, v)| *v).fold(0.0, f64::max) %}
{%- let step = if top <= 0.0 { 1.0 } else { let raw = top / 4.0; let mag = 10f64.powf(raw.log10().floor()); let f = raw / mag; mag * if f <= 1.0 { 1.0 } else if f <= 2.0 { 2.0 } else if f <= 2.5 { 2.5 } else if f <= 5.0 { 5.0 } else { 10.0 } } %}
{%- let band = 280.0 / series.len().max(1) as f64 %}
{%- let num = |v: f64| if v.fract() == 0.0 { format!("{}", v as i64) } else { format!("{v:.1}") } %}
{%- let y = |v: f64| 192.0 - v / (step * 4.0) * 176.0 %}
{%- let x = |i: usize| 40.0 + band * (i as f64 + 0.5) %}
{%- let line = series.iter().enumerate().map(|(i, (_, v))| format!("{}{:.1},{:.1}", if i == 0 { "M" } else { " L" }, x(i), y(*v))).collect::<String>() %}
<div class="ui-chart">
  <svg viewBox="0 0 330 220" role="img" aria-label="{{ label }}">
    <g class="grid">{% for k in 1..=4 %}<line x1="40" x2="320" y1="{{ format!("{:.1}", y(step * k as f64)) }}" y2="{{ format!("{:.1}", y(step * k as f64)) }}"/>{% end %}</g>
    <line class="baseline" x1="40" x2="320" y1="192" y2="192"/>
    <g class="axis">
      {%- for k in 0..=4 %}<text x="32" y="{{ format!("{:.1}", y(step * k as f64) + 4.0) }}" text-anchor="end">{{ num(step * k as f64) }}</text>{% end %}
      {%- for (i, (key, _)) in series.iter().enumerate() %}<text x="{{ format!("{:.1}", x(i)) }}" y="210" text-anchor="middle">{{ key }}</text>{% end %}
    </g>
    <g class="s1">
      {%- if kind == "bar" %}
      {%- let w = (band - 14.0).max(2.0) %}
      {%- for (i, (key, v)) in series.iter().enumerate() %}
      {%- let (left, h) = (x(i) - w / 2.0, 192.0 - y(*v)) %}
      {%- let r = 4f64.min(h).min(w / 2.0) %}
      <path class="bar" data-i="{{ i }}" d="{{ format!("M{left:.1} 192 V{:.1} q0 -{r:.1} {r:.1} -{r:.1} h{:.1} q{r:.1} 0 {r:.1} {r:.1} V192 Z", 192.0 - h + r, w - 2.0 * r) }}" aria-label="{{ key }}: {{ num(*v) }}{% if !unit.is_empty() %} {{ unit }}{% end %}"/>
      {%- end %}
      {%- else %}
      {%- if kind == "area" && !series.is_empty() %}
      <path class="area" d="{{ line }} L{{ format!("{:.1}", x(series.len() - 1)) }},192 L{{ format!("{:.1}", x(0)) }},192 Z"/>
      {%- end %}
      <path class="line" d="{{ line }}"/>
      {%- end %}
    </g>
  </svg>
</div>
<details class="ui-details">
  <summary><svg class="ui-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m9 18 6-6-6-6"/></svg>View as table</summary>
  <div class="ui-table-wrap"><table class="ui-table"><thead><tr><th></th><th class="ui-num">{{ name }}</th></tr></thead><tbody>{% for (key, v) in series.iter() %}<tr><td>{{ key }}</td><td class="ui-num">{{ num(*v) }}</td></tr>{% end %}</tbody></table></div>
</details>