PhoenixKit.Dashboard.Badge (phoenix_kit v1.7.71)

Copy Markdown View Source

Defines badge types for dashboard tab indicators.

Badges provide visual feedback on tabs to show counts, status, or draw attention. They can be static or update live via PubSub subscriptions.

Badge Types

  • :count - Numeric count badge (e.g., "5", "99+")
  • :dot - Simple colored dot indicator
  • :status - Status indicator with color (green/yellow/red)
  • :new - "New" text indicator
  • :text - Custom text badge

Static Badge

%Badge{type: :count, value: 5}
%Badge{type: :dot, color: :success}
%Badge{type: :status, value: :online, color: :success}

Live Badge with PubSub

%Badge{
  type: :count,
  subscribe: {"farm:stats", fn msg -> msg.printing_count end}
}

Context-Aware Badge

For badges that show different values per user/organization/context, use context_key and optionally loader. The badge value is stored per-context in socket assigns instead of globally.

# Badge depends on current organization context
%Badge{
  type: :count,
  context_key: :organization,
  loader: {MyApp.Alerts, :count_for_org},  # Called with (context)
  subscribe: "org:{id}:alerts"  # {id} replaced with context.id
}

Context Placeholders

Subscribe topics support {field} placeholders that are resolved from the current context:

  • {id} - context.id or context[:id]
  • {name} - context.name or context[:name]
  • Any field accessible on the context struct/map

Loader Function

The loader is called when the LiveView mounts to get the initial badge value:

  • {Module, :function} - Called as Module.function(context)
  • fn context -> value end - Anonymous function

Badge with Attention Animation

%Badge{
  type: :count,
  value: 3,
  color: :error,
  pulse: true
}

Summary

Functions

Returns the CSS color class for the badge.

Creates a compound badge with multiple colored segments.

Creates a context-aware compound badge that loads segments per-context.

Creates a context-aware badge that loads values per-context.

Checks if this badge is context-aware (requires per-context value storage).

Creates a count badge.

Formats the badge value for display.

Creates a dot badge.

Returns the CSS background color class for dot badges.

Extracts value from a PubSub message using the badge's subscription config.

Gets the resolved topic for this badge given a context.

Gets the PubSub topic for this badge, if it has a subscription.

Creates a live badge that subscribes to PubSub updates.

Checks if this badge has a live subscription.

Loads the initial badge value using the loader function.

Creates a new Badge struct from a map or keyword list.

Creates a new Badge struct, raising on error.

Creates a "New" badge.

Resolves placeholders in a topic string using context data.

Creates a status badge.

Creates a text badge with custom text.

Updates segments for a compound badge.

Updates the badge value.

Checks if the badge should be visible.

Returns visible segments for a compound badge.

Types

badge_color()

@type badge_color() ::
  :primary
  | :secondary
  | :accent
  | :info
  | :success
  | :warning
  | :error
  | :neutral

badge_type()

@type badge_type() :: :count | :dot | :status | :new | :text | :compound

compound_segment()

@type compound_segment() :: %{
  :value => integer() | String.t(),
  :color => badge_color(),
  optional(:label) => String.t()
}

compound_style()

@type compound_style() :: :text | :blocks | :dots

loader_config()

@type loader_config() :: {module(), atom()} | (any() -> any())

subscribe_config()

@type subscribe_config() ::
  {String.t(), (map() -> any())} | {String.t(), atom()} | String.t()

t()

@type t() :: %PhoenixKit.Dashboard.Badge{
  animate: boolean(),
  color: badge_color(),
  compound_style: compound_style(),
  context_key: atom() | nil,
  format: (any() -> String.t()) | nil,
  hidden_when_zero: boolean(),
  hide_zero_segments: boolean(),
  loader: loader_config() | nil,
  max: integer() | nil,
  metadata: map(),
  pulse: boolean(),
  segments: [compound_segment()],
  separator: String.t(),
  subscribe: subscribe_config() | nil,
  type: badge_type(),
  value: any()
}

Functions

color_class(arg1)

@spec color_class(t()) :: String.t()

Returns the CSS color class for the badge.

compound(segments, opts \\ [])

@spec compound(
  [compound_segment()],
  keyword()
) :: t()

Creates a compound badge with multiple colored segments.

A compound badge displays multiple values with different colors in a single badge. Useful for showing status breakdowns like "10 success / 5 pending / 2 error".

Styles

  • :text (default) - Colored text values with separator (e.g., "10 / 5 / 2")
  • :blocks - Colored background pills side by side
  • :dots - Colored dots with numbers beside them

Options

  • :style - Display style: :text, :blocks, :dots (default: :text)
  • :separator - Separator string for :text style (default: "/")
  • :hide_zero_segments - Hide segments with value 0 (default: false)
  • :pulse - Enable pulse animation (default: false)

Segment Format

Each segment is a map with:

  • :value (required) - Integer or string value to display
  • :color (required) - Badge color atom (:success, :warning, :error, etc.)
  • :label (optional) - Text label shown after value (e.g., "done", "pending")

Examples

# Simple compound badge
Badge.compound([
  %{value: 10, color: :success},
  %{value: 5, color: :warning},
  %{value: 2, color: :error}
])

# With labels and blocks style
Badge.compound([
  %{value: 10, color: :success, label: "done"},
  %{value: 5, color: :warning, label: "pending"}
], style: :blocks)

# Hide zero segments
Badge.compound([
  %{value: 10, color: :success},
  %{value: 0, color: :error}
], hide_zero_segments: true)
# Only shows: "10"

compound_context(context_key, loader, opts \\ [])

@spec compound_context(atom(), loader_config(), keyword()) :: t()

Creates a context-aware compound badge that loads segments per-context.

The loader function should return a list of segment maps.

Examples

# Loader returns list of segments for current organization
Badge.compound_context(:organization, {MyApp.Tasks, :get_status_counts},
  style: :blocks
)

# In MyApp.Tasks
def get_status_counts(org) do
  [
    %{value: count_completed(org.id), color: :success},
    %{value: count_pending(org.id), color: :warning},
    %{value: count_overdue(org.id), color: :error}
  ]
end

context(context_key, loader, opts \\ [])

@spec context(atom(), loader_config(), keyword()) :: t()

Creates a context-aware badge that loads values per-context.

Examples

# Badge that shows alert count for current organization
Badge.context(:organization, {MyApp.Alerts, :count_for_org}, color: :error)

# With live updates via context-specific PubSub topic
Badge.context(:farm, {MyApp.Farms, :printing_count},
  subscribe: "farm:{id}:stats",
  color: :info
)

context_aware?(badge)

@spec context_aware?(t()) :: boolean()

Checks if this badge is context-aware (requires per-context value storage).

count(value, opts \\ [])

@spec count(
  integer(),
  keyword()
) :: t()

Creates a count badge.

Examples

Badge.count(5)
Badge.count(10, color: :error, max: 99)

display_value(badge)

@spec display_value(t()) :: String.t() | nil

Formats the badge value for display.

Examples

iex> badge = Badge.count(5)
iex> Badge.display_value(badge)
"5"

iex> badge = Badge.count(150, max: 99)
iex> Badge.display_value(badge)
"99+"

dot(opts \\ [])

@spec dot(keyword()) :: t()

Creates a dot badge.

Examples

Badge.dot()
Badge.dot(color: :success)
Badge.dot(color: :error, pulse: true)

dot_color_class(arg1)

@spec dot_color_class(t()) :: String.t()

Returns the CSS background color class for dot badges.

extract_value(arg1, message)

@spec extract_value(t(), map()) :: any()

Extracts value from a PubSub message using the badge's subscription config.

get_resolved_topic(badge, context)

@spec get_resolved_topic(t(), map() | struct() | nil) :: String.t() | nil

Gets the resolved topic for this badge given a context.

For context-aware badges, resolves placeholders. For regular badges, returns the topic as-is.

get_topic(arg1)

@spec get_topic(t()) :: String.t() | nil

Gets the PubSub topic for this badge, if it has a subscription.

live(topic, extractor, opts \\ [])

@spec live(String.t(), atom() | (map() -> any()), keyword()) :: t()

Creates a live badge that subscribes to PubSub updates.

Examples

Badge.live("user:notifications", :count)
Badge.live("farm:stats", fn msg -> msg.printing_count end, color: :info)

live?(badge)

@spec live?(t()) :: boolean()

Checks if this badge has a live subscription.

load_value(arg1, context)

@spec load_value(t(), map() | struct() | nil) :: any()

Loads the initial badge value using the loader function.

Examples

iex> badge = Badge.context(:org, {MyApp.Alerts, :count_for_org})
iex> Badge.load_value(badge, %{id: 123})
5  # Result from MyApp.Alerts.count_for_org(%{id: 123})

new(attrs)

@spec new(map() | keyword()) :: {:ok, t()} | {:error, String.t()}

Creates a new Badge struct from a map or keyword list.

Options

  • :type - Badge type: :count, :dot, :status, :new, :text (default: :count)
  • :value - The value to display (number for count, atom/string for status/text)
  • :color - Badge color: :primary, :secondary, :accent, :info, :success, :warning, :error, :neutral (default: :primary)
  • :max - Maximum display value for count badges (e.g., 99 shows "99+") (optional)
  • :pulse - Enable pulse animation (default: false)
  • :animate - Enable value change animation (default: true)
  • :hidden_when_zero - Hide count badge when value is 0 (default: true)
  • :subscribe - PubSub subscription config for live updates (optional)
  • :format - Custom formatter function for the value (optional)
  • :metadata - Custom metadata map (default: %{})
  • :context_key - Context selector key for per-context badges (optional, e.g., :organization)
  • :loader - Function to load initial value for context: {Module, :function} or fn context -> value end

Subscribe Configuration

The :subscribe option can be:

  1. A tuple of {topic, extractor_function}: {"farm:stats", fn msg -> msg.printing_count end}

  2. A tuple of {topic, key_atom} to extract from message: {"farm:stats", :printing_count}

  3. Just a topic string (uses full message as value): "user:notifications:count"

Topics support {field} placeholders for context-aware badges: "org:{id}:alerts" - resolves to "org:123:alerts" when context.id is 123

Examples

iex> Badge.new(type: :count, value: 5)
{:ok, %Badge{type: :count, value: 5}}

iex> Badge.new(type: :dot, color: :error, pulse: true)
{:ok, %Badge{type: :dot, color: :error, pulse: true}}

iex> Badge.new(type: :count, subscribe: {"orders:count", :count})
{:ok, %Badge{type: :count, subscribe: {"orders:count", :count}}}

# Context-aware badge
iex> Badge.new(type: :count, context_key: :organization, loader: {MyApp.Alerts, :count_for_org})
{:ok, %Badge{type: :count, context_key: :organization, loader: {MyApp.Alerts, :count_for_org}}}

new!(attrs)

@spec new!(map() | keyword()) :: t()

Creates a new Badge struct, raising on error.

new_indicator(opts \\ [])

@spec new_indicator(keyword()) :: t()

Creates a "New" badge.

Examples

Badge.new_indicator()
Badge.new_indicator(color: :accent)

resolve_topic(topic, context)

@spec resolve_topic(String.t() | nil, map() | struct()) :: String.t() | nil

Resolves placeholders in a topic string using context data.

Supports {field} syntax where field is accessed from the context.

Examples

iex> Badge.resolve_topic("org:{id}:alerts", %{id: 123})
"org:123:alerts"

iex> Badge.resolve_topic("farm:{farm_id}:stats", %{farm_id: "abc"})
"farm:abc:stats"

status(value, opts \\ [])

@spec status(
  atom() | String.t(),
  keyword()
) :: t()

Creates a status badge.

Examples

Badge.status(:online, color: :success)
Badge.status(:busy, color: :warning)
Badge.status(:offline, color: :error)

text(value, opts \\ [])

@spec text(
  String.t(),
  keyword()
) :: t()

Creates a text badge with custom text.

Examples

Badge.text("Beta")
Badge.text("Pro", color: :accent)

update_segments(badge, segments)

@spec update_segments(t(), [compound_segment()]) :: t()

Updates segments for a compound badge.

update_value(badge, value)

@spec update_value(t(), any()) :: t()

Updates the badge value.

visible?(badge)

@spec visible?(t()) :: boolean()

Checks if the badge should be visible.

Count badges with value 0 are hidden by default when hidden_when_zero is true. Compound badges are hidden when all segments have zero value (and hide_zero_segments is true).

visible_segments(arg1)

@spec visible_segments(t()) :: [compound_segment()]

Returns visible segments for a compound badge.

If hide_zero_segments is true, filters out segments with value 0 or nil.

Examples

badge = Badge.compound([
  %{value: 10, color: :success},
  %{value: 0, color: :error}
], hide_zero_segments: true)

Badge.visible_segments(badge)
# => [%{value: 10, color: :success}]