defmodule CobblemonUiWeb.PokemonComponents do
use CobblemonUiWeb, :html
attr :pokemon, :map, required: true
attr :index, :integer, required: true
attr :compact, :boolean, default: false
attr :tier, :string, default: nil
def pokemon_card(%{pokemon: nil} = assigns) do
~H"""
Empty
"""
end
def pokemon_card(assigns) do
~H"""
{@pokemon.species || "Unknown"}
Lv. {@pokemon.level || "?"}
<.tier_badge :if={@tier} tier={@tier} species={@pokemon.species} compact={@compact} />
<.icon
name="hero-sparkles"
class={[
"text-warning",
if(@compact, do: "size-3", else: "size-4")
]}
/>
{@pokemon.nature}
{gender_symbol(@pokemon.gender)}
"""
end
attr :tier, :string, required: true
attr :species, :string, required: true
attr :compact, :boolean, default: false
def tier_badge(assigns) do
~H"""
"bg-red-500/20 text-red-400 ring-1 ring-red-500/30 hover:bg-red-500/30"
"A" -> "bg-orange-500/20 text-orange-400 ring-1 ring-orange-500/30 hover:bg-orange-500/30"
"B" -> "bg-yellow-500/20 text-yellow-400 ring-1 ring-yellow-500/30 hover:bg-yellow-500/30"
"C" -> "bg-green-500/20 text-green-400 ring-1 ring-green-500/30 hover:bg-green-500/30"
"D" -> "bg-blue-500/20 text-blue-400 ring-1 ring-blue-500/30 hover:bg-blue-500/30"
_ -> "bg-base-300/30 text-base-content/40 ring-1 ring-base-300/40"
end
]}
>
{@tier}
"""
end
attr :pokemon, :map, required: true
attr :tier, :string, default: nil
def pokemon_detail(assigns) do
~H"""
<%!-- Detail header --%>
<.icon name="hero-bolt" class="size-4 text-primary/70" />
{@pokemon.species || "Unknown"}
<.tier_badge :if={@tier} tier={@tier} species={@pokemon.species} />
★
Level {@pokemon.level || "?"} · {String.capitalize(@pokemon.form || "default")} form
<%!-- Info Column --%>
Details
<.stat_pill label="Nature" value={@pokemon.nature} />
<.stat_pill label="Ability" value={@pokemon.ability} />
<.stat_pill label="Gender" value={@pokemon.gender} />
<.stat_pill label="Friendship" value={@pokemon.friendship} />
<%!-- Moves --%>
Moves
{format_move(move)}
No moves
<%!-- Stats Column --%>
<%!-- IVs --%>
IVs
<.stat_bar
:for={{stat, val} <- stat_list(@pokemon.ivs)}
label={format_stat(stat)}
value={val}
max={31}
/>
<%!-- EVs --%>
EVs
({ev_total(@pokemon.evs)}/510)
<.stat_bar
:for={{stat, val} <- stat_list(@pokemon.evs)}
label={format_stat(stat)}
value={val}
max={252}
/>
"""
end
attr :label, :string, required: true
attr :value, :any, required: true
def stat_pill(assigns) do
~H"""
"""
end
attr :label, :string, required: true
attr :value, :integer, required: true
attr :max, :integer, required: true
def stat_bar(assigns) do
pct = if assigns.max > 0, do: min(assigns.value / assigns.max * 100, 100), else: 0
color =
cond do
pct >= 90 -> "bg-success"
pct >= 60 -> "bg-info"
pct >= 30 -> "bg-warning"
true -> "bg-error/70"
end
assigns = assign(assigns, pct: pct, color: color)
~H"""
"""
end
# --- Helpers ---
defp gender_symbol("male"), do: "♂"
defp gender_symbol("female"), do: "♀"
defp gender_symbol(_), do: "—"
defp gender_color("male"), do: "text-info"
defp gender_color("female"), do: "text-error"
defp gender_color(_), do: "text-base-content/40"
defp format_move(move) when is_binary(move), do: String.replace(move, "_", " ")
defp format_move(_), do: "—"
defp format_stat(:hp), do: "HP"
defp format_stat(:attack), do: "ATK"
defp format_stat(:defense), do: "DEF"
defp format_stat(:special_attack), do: "SPA"
defp format_stat(:special_defense), do: "SPD"
defp format_stat(:speed), do: "SPE"
defp format_stat(other), do: to_string(other)
defp stat_list(stats) when is_map(stats) do
[:hp, :attack, :defense, :special_attack, :special_defense, :speed]
|> Enum.map(fn key -> {key, Map.get(stats, key, 0)} end)
end
defp stat_list(_), do: []
defp ev_total(evs) when is_map(evs), do: evs |> Map.values() |> Enum.sum()
defp ev_total(_), do: 0
end