defmodule MusicLibraryWeb.ChartComponents do
@moduledoc """
Chart components for the stats dashboard.
"""
use MusicLibraryWeb, :live_component
@doc """
Renders a horizontal bar chart using CSS Grid.
The label column auto-sizes to the widest label (up to 130px max),
bars fill remaining space proportionally, and values are displayed
to the right of each bar.
## Examples
<.vertical_bar_chart
data={[{"Artist 1", 5}, {"Artist 2", 3}]}
label_fn={&elem(&1, 0)}
value_fn={&elem(&1, 1)}
color_class="bg-red-500"
/>
"""
attr :data, :list, required: true
attr :label_fn, :any, required: true
attr :value_fn, :any, required: true
attr :color_class, :string, required: true
attr :class, :string, default: ""
attr :datum_click, :any, default: nil, doc: "the function for handling phx-click on each datum"
def vertical_bar_chart(assigns) when assigns.data != [] do
assigns =
assigns
|> assign(:max_value, max_value(assigns.data, assigns.value_fn))
~H"""
<%= for datum <- @data do %>
<% percentage = bar_percentage(@value_fn.(datum), @max_value) %>
<% label = to_string(@label_fn.(datum)) %>
<%= if @datum_click do %>
<.bar_row
label={label}
percentage={percentage}
value={@value_fn.(datum)}
color_class={@color_class}
grouped
/>
<% else %>
<.bar_row
label={label}
percentage={percentage}
value={@value_fn.(datum)}
color_class={@color_class}
/>
<% end %>
<% end %>
"""
end
def vertical_bar_chart(assigns) do
~H"""
{gettext("No data available")}
"""
end
attr :label, :string, required: true
attr :percentage, :float, required: true
attr :value, :any, required: true
attr :color_class, :string, required: true
attr :grouped, :boolean, default: false
defp bar_row(assigns) do
~H"""
{@label}
{@value}
"""
end
defp max_value(data, value_fn) do
max = Enum.max_by(data, value_fn)
value_fn.(max)
end
defp bar_percentage(_value, max_value) when max_value == 0, do: 0.0
defp bar_percentage(value, max_value), do: value / max_value * 100
end