defmodule PhoenixKitWeb.Components.Dashboard.Badge do
@moduledoc """
Badge component for dashboard tab indicators.
Renders various badge types with support for:
- Count badges (numeric)
- Dot indicators
- Status badges
- "New" indicators
- Custom text badges
- Pulse and other animations
"""
use Phoenix.Component
alias PhoenixKit.Dashboard.Badge, as: BadgeStruct
@doc """
Renders a dashboard badge.
## Attributes
- `badge` - The Badge struct or nil
- `class` - Additional CSS classes
## Examples
<.dashboard_badge badge={@tab.badge} />
<.dashboard_badge badge={Badge.count(5)} />
"""
attr :badge, :any, default: nil
attr :class, :string, default: ""
def dashboard_badge(assigns) do
~H"""
<%= if @badge && BadgeStruct.visible?(@badge) do %>
<%= case @badge.type do %>
<% :dot -> %>
<.dot_badge badge={@badge} class={@class} />
<% :count -> %>
<.count_badge badge={@badge} class={@class} />
<% :status -> %>
<.status_badge badge={@badge} class={@class} />
<% :new -> %>
<.new_badge badge={@badge} class={@class} />
<% :text -> %>
<.text_badge badge={@badge} class={@class} />
<% :compound -> %>
<.compound_badge badge={@badge} class={@class} />
<% _ -> %>
<.count_badge badge={@badge} class={@class} />
<% end %>
<% end %>
"""
end
@doc """
Renders a count badge with optional max value display.
"""
attr :badge, :any, required: true
attr :class, :string, default: ""
def count_badge(assigns) do
value = BadgeStruct.display_value(assigns.badge)
color_class = BadgeStruct.color_class(assigns.badge)
assigns =
assigns
|> assign(:value, value)
|> assign(:color_class, color_class)
~H"""
{@value}
"""
end
@doc """
Renders a dot indicator badge.
"""
attr :badge, :any, required: true
attr :class, :string, default: ""
def dot_badge(assigns) do
color_class = BadgeStruct.dot_color_class(assigns.badge)
assigns = assign(assigns, :color_class, color_class)
~H"""
"""
end
@doc """
Renders a status badge with value and color.
"""
attr :badge, :any, required: true
attr :class, :string, default: ""
def status_badge(assigns) do
value = BadgeStruct.display_value(assigns.badge)
color_class = BadgeStruct.color_class(assigns.badge)
assigns =
assigns
|> assign(:value, value)
|> assign(:color_class, color_class)
~H"""
String.replace("badge-", "text-")}>
{String.capitalize(to_string(@value))}
"""
end
@doc """
Renders a "New" indicator badge.
"""
attr :badge, :any, required: true
attr :class, :string, default: ""
def new_badge(assigns) do
color_class = BadgeStruct.color_class(assigns.badge)
assigns = assign(assigns, :color_class, color_class)
~H"""
New
"""
end
@doc """
Renders a custom text badge.
"""
attr :badge, :any, required: true
attr :class, :string, default: ""
def text_badge(assigns) do
value = BadgeStruct.display_value(assigns.badge)
color_class = BadgeStruct.color_class(assigns.badge)
assigns =
assigns
|> assign(:value, value)
|> assign(:color_class, color_class)
~H"""
{@value}
"""
end
@doc """
Renders a compound badge with multiple colored segments.
Supports three styles:
- `:text` - Colored text values with separator (default)
- `:blocks` - Colored background pills side by side
- `:dots` - Colored dots with numbers
"""
attr :badge, :any, required: true
attr :class, :string, default: ""
def compound_badge(assigns) do
segments = BadgeStruct.visible_segments(assigns.badge)
style = assigns.badge.compound_style || :text
separator = assigns.badge.separator || "/"
assigns =
assigns
|> assign(:segments, segments)
|> assign(:style, style)
|> assign(:separator, separator)
~H"""
<%= case @style do %>
<% :text -> %>
<.compound_text_style segments={@segments} separator={@separator} />
<% :blocks -> %>
<.compound_blocks_style segments={@segments} />
<% :dots -> %>
<.compound_dots_style segments={@segments} />
<% _ -> %>
<.compound_text_style segments={@segments} separator={@separator} />
<% end %>
"""
end
# Text style: "10 / 5 / 2" with colored text
defp compound_text_style(assigns) do
~H"""
<%= for {segment, index} <- Enum.with_index(@segments) do %>
<%= if index > 0 do %>
{@separator}
<% end %>
{segment[:value] || segment["value"]}
<%= if segment[:label] || segment["label"] do %>
{segment[:label] || segment["label"]}
<% end %>
<% end %>
"""
end
# Blocks style: colored background pills
defp compound_blocks_style(assigns) do
~H"""
<%= for segment <- @segments do %>
{segment[:value] || segment["value"]}
<% end %>
"""
end
# Dots style: colored dots with numbers
defp compound_dots_style(assigns) do
~H"""
<%= for segment <- @segments do %>
{segment[:value] || segment["value"]}
<% end %>
"""
end
# Text color classes for compound badge text style
defp text_color_class(:primary), do: "text-primary"
defp text_color_class(:secondary), do: "text-secondary"
defp text_color_class(:accent), do: "text-accent"
defp text_color_class(:info), do: "text-info"
defp text_color_class(:success), do: "text-success"
defp text_color_class(:warning), do: "text-warning"
defp text_color_class(:error), do: "text-error"
defp text_color_class(:neutral), do: "text-neutral"
defp text_color_class(:base), do: "text-base-content"
defp text_color_class("primary"), do: "text-primary"
defp text_color_class("secondary"), do: "text-secondary"
defp text_color_class("accent"), do: "text-accent"
defp text_color_class("info"), do: "text-info"
defp text_color_class("success"), do: "text-success"
defp text_color_class("warning"), do: "text-warning"
defp text_color_class("error"), do: "text-error"
defp text_color_class("neutral"), do: "text-neutral"
defp text_color_class("base"), do: "text-base-content"
defp text_color_class(_), do: "text-base-content"
# Block color classes (background + text) for compound badge blocks style
defp block_color_class(:primary), do: "bg-primary text-primary-content"
defp block_color_class(:secondary), do: "bg-secondary text-secondary-content"
defp block_color_class(:accent), do: "bg-accent text-accent-content"
defp block_color_class(:info), do: "bg-info text-info-content"
defp block_color_class(:success), do: "bg-success text-success-content"
defp block_color_class(:warning), do: "bg-warning text-warning-content"
defp block_color_class(:error), do: "bg-error text-error-content"
defp block_color_class(:neutral), do: "bg-neutral text-neutral-content"
defp block_color_class(:base), do: "bg-base-300 text-base-content"
defp block_color_class("primary"), do: "bg-primary text-primary-content"
defp block_color_class("secondary"), do: "bg-secondary text-secondary-content"
defp block_color_class("accent"), do: "bg-accent text-accent-content"
defp block_color_class("info"), do: "bg-info text-info-content"
defp block_color_class("success"), do: "bg-success text-success-content"
defp block_color_class("warning"), do: "bg-warning text-warning-content"
defp block_color_class("error"), do: "bg-error text-error-content"
defp block_color_class("neutral"), do: "bg-neutral text-neutral-content"
defp block_color_class("base"), do: "bg-base-300 text-base-content"
defp block_color_class(_), do: "bg-base-300 text-base-content"
# Dot color classes for compound badge dots style
defp dot_color_class_for_segment(:primary), do: "bg-primary"
defp dot_color_class_for_segment(:secondary), do: "bg-secondary"
defp dot_color_class_for_segment(:accent), do: "bg-accent"
defp dot_color_class_for_segment(:info), do: "bg-info"
defp dot_color_class_for_segment(:success), do: "bg-success"
defp dot_color_class_for_segment(:warning), do: "bg-warning"
defp dot_color_class_for_segment(:error), do: "bg-error"
defp dot_color_class_for_segment(:neutral), do: "bg-neutral"
defp dot_color_class_for_segment(:base), do: "bg-base-300"
defp dot_color_class_for_segment("primary"), do: "bg-primary"
defp dot_color_class_for_segment("secondary"), do: "bg-secondary"
defp dot_color_class_for_segment("accent"), do: "bg-accent"
defp dot_color_class_for_segment("info"), do: "bg-info"
defp dot_color_class_for_segment("success"), do: "bg-success"
defp dot_color_class_for_segment("warning"), do: "bg-warning"
defp dot_color_class_for_segment("error"), do: "bg-error"
defp dot_color_class_for_segment("neutral"), do: "bg-neutral"
defp dot_color_class_for_segment("base"), do: "bg-base-300"
defp dot_color_class_for_segment(_), do: "bg-base-300"
@doc """
Renders a presence indicator showing user count.
"""
attr :count, :integer, default: 0
attr :show_text, :boolean, default: false
attr :class, :string, default: ""
def presence_indicator(assigns) do
~H"""
<%= if @count > 0 do %>
<%= if @show_text do %>
{@count}
<% end %>
<% end %>
"""
end
end