Use label_fn and value_fn

This commit is contained in:
Claudio Ortolina
2025-04-06 17:45:44 +01:00
parent 79def8d053
commit 4fdd2e7ea6
2 changed files with 23 additions and 13 deletions
@@ -7,12 +7,16 @@ defmodule MusicLibraryWeb.ChartComponents do
## Examples ## Examples
<.vertical_bar_chart <.vertical_bar_chart
data={[{"Artist 1", 5}, {"Artist 2", 3}]} data={[{"Artist 1", 5}, {"Artist 2", 3}]}
label_fn={&elem(&1, 0)}
value_fn={&elem(&1, 1)}
width={400} width={400}
height={300} height={300}
bar_color="rgb(79, 70, 229)" bar_color="rgb(79, 70, 229)"
/> />
""" """
attr :data, :list, required: true, doc: "List of {label, value} tuples" attr :data, :list, required: true
attr :label_fn, :any, required: true
attr :value_fn, :any, required: true
attr :width, :integer, default: 400 attr :width, :integer, default: 400
attr :height, :integer, default: 300 attr :height, :integer, default: 300
attr :bar_color, :string, default: "rgb(79, 70, 229)" attr :bar_color, :string, default: "rgb(79, 70, 229)"
@@ -23,7 +27,7 @@ defmodule MusicLibraryWeb.ChartComponents do
assigns = assigns =
assigns assigns
|> assign(:padding, 40) |> assign(:padding, 40)
|> assign(:max_value, max_value(assigns.data)) |> assign(:max_value, max_value(assigns.data, assigns.value_fn))
# Account for labels # Account for labels
|> assign(:chart_width, assigns.width - 150) |> assign(:chart_width, assigns.width - 150)
# Account for bottom padding # Account for bottom padding
@@ -54,8 +58,8 @@ defmodule MusicLibraryWeb.ChartComponents do
<% end %> <% end %>
<%!-- Bars and labels --%> <%!-- Bars and labels --%>
<%= for {{label, value}, index} <- Enum.with_index(@data) do %> <%= for {datum, index} <- Enum.with_index(@data) do %>
<% bar_width = @chart_width * value / @max_value %> <% bar_width = @chart_width * @value_fn.(datum) / @max_value %>
<% y = @padding / 2 + index * (@bar_height + 4) %> <% y = @padding / 2 + index * (@bar_height + 4) %>
<%!-- Label --%> <%!-- Label --%>
@@ -64,9 +68,9 @@ defmodule MusicLibraryWeb.ChartComponents do
y={y + @bar_height / 2 + 4} y={y + @bar_height / 2 + 4}
text-anchor="end" text-anchor="end"
class={["text-xs fill-zinc-500 dark:fill-zinc-400", @label_click && "cursor-pointer"]} class={["text-xs fill-zinc-500 dark:fill-zinc-400", @label_click && "cursor-pointer"]}
phx-click={@label_click && @label_click.(label)} phx-click={@label_click && @label_click.(datum)}
> >
{truncate_label(label, 20)} {truncate_label(@label_fn.(datum), 20)}
</text> </text>
<%!-- Bar --%> <%!-- Bar --%>
@@ -78,7 +82,7 @@ defmodule MusicLibraryWeb.ChartComponents do
fill={@bar_color} fill={@bar_color}
class="opacity-80 hover:opacity-100 transition-opacity" class="opacity-80 hover:opacity-100 transition-opacity"
> >
<title>{label}: {value}</title> <title>{@label_fn.(datum)}: {@value_fn.(datum)}</title>
</rect> </rect>
<%!-- Value label --%> <%!-- Value label --%>
@@ -87,16 +91,16 @@ defmodule MusicLibraryWeb.ChartComponents do
y={y + @bar_height / 2 + 4} y={y + @bar_height / 2 + 4}
class="text-xs fill-zinc-500 dark:fill-zinc-400" class="text-xs fill-zinc-500 dark:fill-zinc-400"
> >
{value} {@value_fn.(datum)}
</text> </text>
<% end %> <% end %>
</svg> </svg>
""" """
end end
defp max_value(data) do defp max_value(data, value_fn) do
{_, max} = Enum.max_by(data, fn {_, value} -> value end) max = Enum.max_by(data, value_fn)
max value_fn.(max)
end end
defp calculate_bar_height(data, available_height) do defp calculate_bar_height(data, available_height) do
@@ -79,8 +79,12 @@
width={600} width={600}
height={35 * length(@records_by_artist)} height={35 * length(@records_by_artist)}
bar_color="rgb(79, 70, 229)" bar_color="rgb(79, 70, 229)"
label_fn={fn {artist, _count} -> artist end}
value_fn={fn {_artist, count} -> count end}
label_click={ label_click={
fn label -> JS.navigate(~p"/collection?#{%{query: ~s(artist:"#{label}")}}") end fn {artist, _count} ->
JS.navigate(~p"/collection?#{%{query: ~s(artist:"#{artist}")}}")
end
} }
class="w-full" class="w-full"
/> />
@@ -97,8 +101,10 @@
width={600} width={600}
height={35 * length(@records_by_genre)} height={35 * length(@records_by_genre)}
bar_color="rgb(244, 63, 94)" bar_color="rgb(244, 63, 94)"
label_fn={fn {genre, _count} -> genre end}
value_fn={fn {_genre, count} -> count end}
label_click={ label_click={
fn label -> JS.navigate(~p"/collection?#{%{query: ~s(genre:"#{label}")}}") end fn {genre, _count} -> JS.navigate(~p"/collection?#{%{query: ~s(genre:"#{genre}")}}") end
} }
class="w-full" class="w-full"
/> />