{#
bar.html
========
Bar chart widget renderer.
Emits the DOM shape `wrapper.html`'s `initCharts()` looks for:
<div data-umbral-chart="bar" data-chart-series="<name>">
<span data-chart-point data-label="..." data-value="..." hidden>
...
<div data-chart-canvas></div> ← where ApexCharts mounts
<div data-chart-unavailable hidden> ← shown only if JS fails
</div>
The SVG-rects rendering stays around as the
`data-chart-unavailable` fallback (graceful degradation: if the
CDN script fails to load, the widget still shows something
meaningful — just not animated).
Expects payload: { series: [{name, points: [{x, y}]}], x_type }.
Renders the first series only for the minimal v1 shape.
#}
{% macro bar_widget(title, payload) %}
<div class="bar-widget h-full flex flex-col p-md">
<p class="text-label-sm text-outline uppercase tracking-wider mb-sm flex-shrink-0">{{ title }}</p>
{% if payload.series and payload.series | length > 0 %}
{%- set series = payload.series[0] -%}
{%- set points = series.points -%}
{%- set n = points | length -%}
{% if n > 0 %}
{%- set max_y = points | map(attribute="y") | max -%}
{%- set max_y = max_y if max_y > 0 else 1.0 -%}
{%- set bar_w = 100 / n -%}
{%- set chart_h = 80 -%}
<div
class="flex-1 min-h-0 relative"
data-umbral-chart="bar"
data-chart-series="{{ series.name }}"
>
{# Data points — read by the JS, never visible. #}
{% for pt in points %}
<span
data-chart-point
data-label="{{ pt.x }}"
data-value="{{ pt.y }}"
hidden
></span>
{% endfor %}
{# ApexCharts mount target. Fills the data-umbral-chart parent. #}
<div data-chart-canvas class="absolute inset-0"></div>
{# Fallback — same SVG bars we had before. Initially visible
for no-JS clients; `initCharts()` hides it once it confirms
ApexCharts is available and the canvas has rendered. If
ApexCharts CAN'T load, the JS keeps the fallback shown. #}
<div data-chart-unavailable class="absolute inset-0 flex flex-col">
<div class="flex-1 overflow-hidden">
<svg viewBox="0 0 100 {{ chart_h }}" xmlns="http://www.w3.org/2000/svg"
class="w-full h-full" preserveAspectRatio="none">
{% for pt in points %}
{%- set bar_h = (pt.y / max_y * (chart_h - 8)) | round(1) -%}
{%- set x_pos = (loop.index0 * bar_w + bar_w * 0.1) | round(2) -%}
{%- set bar_width = (bar_w * 0.8) | round(2) -%}
<rect
x="{{ x_pos }}"
y="{{ (chart_h - bar_h - 2) | round(1) }}"
width="{{ bar_width }}"
height="{{ bar_h }}"
rx="1"
fill="var(--primary)"
opacity="0.8"
/>
{% endfor %}
</svg>
</div>
<div class="flex justify-between mt-xs flex-shrink-0">
{% for pt in points %}
<span class="text-label-sm text-outline truncate" style="max-width: {{ (100 / n) | round(0) }}%">{{ pt.x }}</span>
{% endfor %}
</div>
</div>
</div>
{% else %}
<p class="text-label-sm text-outline italic mt-auto">No data</p>
{% endif %}
{% else %}
<p class="text-label-sm text-outline italic mt-auto">No data</p>
{% endif %}
</div>
{% endmacro %}