Search documentation of phoenix_kit

# PhoenixKit.Dashboard.Tab (phoenix_kit v1.7.87)

Copy Markdown View Source

Defines the Tab struct and related types for the user dashboard navigation system.

Tabs can be configured with rich features including:

- Labels and icons
- Conditional visibility based on roles, feature flags, or custom logic
- Badge indicators with live updates via PubSub
- Attention animations (pulse, bounce, shake)
- Grouping with headers and dividers
- Subtabs with parent/child relationships
- Custom path matching logic
- Tooltips and accessibility features

## Basic Usage

    %Tab{
      id: :orders,
      label: "My Orders",
      icon: "hero-shopping-bag",
      path: "/dashboard/orders",
      priority: 100
    }

## With Badge

    %Tab{
      id: :notifications,
      label: "Notifications",
      icon: "hero-bell",
      path: "/dashboard/notifications",
      badge: %Badge{type: :count, value: 5, color: :error}
    }

## With Live Updates

    %Tab{
      id: :printers,
      label: "Printers",
      icon: "hero-cube",
      path: "/dashboard/printers",
      badge: %Badge{
        type: :count,
        subscribe: {"farm:stats", fn msg -> msg.printing_count end}
      }
    }

## Conditional Visibility

Use `visible` for non-permission conditional logic (feature flags, user data). For access control, use the `permission` field instead.

    %Tab{
      id: :beta_feature,
      label: "Beta",
      icon: "hero-beaker",
      path: "/dashboard/beta",
      visible: fn scope -> scope.user.features["beta_enabled"] == true end
    }

## Subtabs

Tabs can have parent/child relationships. Subtabs appear indented under their parent:

    # Parent tab
    %Tab{
      id: :orders,
      label: "Orders",
      icon: "hero-shopping-bag",
      path: "/dashboard/orders",
      subtab_display: :when_active  # Show subtabs only when this tab is active
    }

    # Subtabs
    %Tab{
      id: :pending_orders,
      label: "Pending",
      path: "/dashboard/orders/pending",
      parent: :orders
    }

    %Tab{
      id: :completed_orders,
      label: "Completed",
      path: "/dashboard/orders/completed",
      parent: :orders
    }

Subtab display modes:

- `:when_active` - Show subtabs only when parent tab is active (default)
- `:always` - Always show subtabs regardless of parent state

## Summary

### Types

dynamic_children_fn()
level()
match_type()
subtab_animation()
subtab_display()
t()
visibility()

### Functions

admin?(arg1)

Checks if this is an admin-level tab.

clear_attention(tab)

Clears the attention animation from the tab.

divider(opts \\ [])

Creates a divider pseudo-tab for visual separation.

divider?(arg1)

Checks if this tab is a divider.

group_header(opts)

Creates a group header pseudo-tab for organizing sections.

group_header?(arg1)

Checks if this tab is a group header.

matches_path?(arg1, current_path)

Checks if the current path matches this tab's path according to its match strategy.

module_enabled?(arg1)

Checks if the module associated with this tab is enabled.

navigable?(tab)

Checks if this tab is a regular navigable tab (not divider or header).

new(attrs)

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

new!(attrs)

Creates a new Tab struct, raising on error.

parent_id(tab)

Gets the parent ID of a subtab, or nil if it's a top-level tab.

permission_granted?(arg1, scope)

Checks if permission is granted for this tab given a scope.

resolve_path(tab, context)

Resolves a relative tab path to an absolute path based on context.

set_attention(tab, attention)

Sets an attention animation on the tab.

show_subtabs?(arg1, active)

Checks if subtabs should be shown for this tab based on its display setting and active state.

subtab?(arg1)

Checks if this tab is a subtab (has a parent).

top_level?(tab)

Checks if this tab is a top-level tab (not a subtab).

update_badge(tab, badge)

Updates a tab's badge value.

user?(arg1)

Checks if this is a user-level tab.

visible?(arg1, scope)

Evaluates the visibility of a tab given a scope.

## Types

# dynamic_children_fn()

    @type dynamic_children_fn() :: (map() -> [t()]) | nil

# level()

    @type level() :: :user | :admin | :all

# match_type()

    @type match_type() :: :exact | :prefix | :regex | (String.t() -> boolean())

# subtab_animation()

    @type subtab_animation() :: :none | :slide | :fade | :collapse

# subtab_display()

    @type subtab_display() :: :when_active | :always

# t()

    @type t() :: %PhoenixKit.Dashboard.Tab{
      attention: atom() | nil,
      badge: PhoenixKit.Dashboard.Badge.t() | nil,
      dynamic_children: dynamic_children_fn(),
      external: boolean(),
      group: atom() | nil,
      highlight_with_subtabs: boolean(),
      icon: String.t() | nil,
      id: atom(),
      inserted_at: DateTime.t() | nil,
      label: String.t(),
      level: level(),
      live_view: {module(), atom()} | nil,
      match: match_type(),
      metadata: map(),
      new_tab: boolean(),
      parent: atom() | nil,
      path: String.t(),
      permission: String.t() | nil,
      priority: integer(),
      redirect_to_first_subtab: boolean(),
      subtab_animation: subtab_animation() | nil,
      subtab_display: subtab_display(),
      subtab_icon_size: String.t() | nil,
      subtab_indent: String.t() | nil,
      subtab_text_size: String.t() | nil,
      tooltip: String.t() | nil,
      visible: visibility()
    }

# visibility()

    @type visibility() :: boolean() | (map() -> boolean())

## Functions

# admin?(arg1)

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

Checks if this is an admin-level tab.

# clear_attention(tab)

    @spec clear_attention(t()) :: t()

Clears the attention animation from the tab.

# divider(opts \\ [])

    @spec divider(keyword()) :: t()

Creates a divider pseudo-tab for visual separation.

## Options

- `:id` - Unique identifier (default: auto-generated)
- `:priority` - Sort order (required to position the divider)
- `:group` - Group this divider belongs to (optional)
- `:label` - Optional label text for the divider (shows as a header)

## Examples

    Tab.divider(priority: 150)
    Tab.divider(priority: 200, label: "Account")

# divider?(arg1)

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

Checks if this tab is a divider.

# group_header(opts)

    @spec group_header(keyword()) :: t()

Creates a group header pseudo-tab for organizing sections.

## Options

- `:id` - Unique identifier (required)
- `:label` - Header text (required)
- `:priority` - Sort order (required)
- `:icon` - Optional icon for the header
- `:collapsible` - Whether the group can be collapsed (default: false)
- `:collapsed` - Initial collapsed state (default: false)

## Examples

    Tab.group_header(id: :farm_section, label: "Farm Management", priority: 100)
    Tab.group_header(id: :account_section, label: "Account", priority: 200, collapsible: true)

# group_header?(arg1)

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

Checks if this tab is a group header.

# matches_path?(arg1, current_path)

    @spec matches_path?(t(), String.t()) :: boolean()

Checks if the current path matches this tab's path according to its match strategy.

## Examples

    iex> tab = %Tab{path: "/dashboard", match: :exact}
    iex> Tab.matches_path?(tab, "/dashboard")
    true
    iex> Tab.matches_path?(tab, "/dashboard/orders")
    false

    iex> tab = %Tab{path: "/dashboard", match: :prefix}
    iex> Tab.matches_path?(tab, "/dashboard/orders")
    true

# module_enabled?(arg1)

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

Checks if the module associated with this tab is enabled.

Returns true if:

- The tab has no permission requirement (permission is nil)
- The feature module for the permission key is enabled

# navigable?(tab)

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

Checks if this tab is a regular navigable tab (not divider or header).

# new(attrs)

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

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

## Options

- `:id` - Unique identifier for the tab (required, atom)
- `:label` - Display text for the tab (required, string)
- `:icon` - Heroicon name, e.g., "hero-home" (optional)
- `:path` - URL path for the tab (required)
- `:priority` - Sort order, lower numbers appear first (default: 500)
- `:group` - Group identifier for organizing tabs (optional, atom)
- `:parent` - Parent tab ID for subtabs (optional, atom)
- `:subtab_display` - When to show subtabs: :when_active or :always (default: :when_active)
- `:subtab_indent` - Tailwind padding class for subtab indentation (e.g., "pl-6", "pl-12")
- `:subtab_icon_size` - Icon size class for subtabs (e.g., "w-3 h-3", "w-5 h-5")
- `:subtab_text_size` - Text size class for subtabs (e.g., "text-xs", "text-base")
- `:subtab_animation` - Animation when subtabs appear: :none, :slide, :fade, :collapse
- `:redirect_to_first_subtab` - Navigate to first subtab when clicking parent (default: false)
- `:highlight_with_subtabs` - Highlight parent when subtab is active (default: false)
- `:match` - Path matching strategy: :exact, :prefix, :regex, or function (default: :prefix)
- `:visible` - Boolean or function(scope) -> boolean for non-permission conditional visibility, e.g. feature flags (default: true). For access control, use `:permission` instead.
- `:badge` - Badge struct or map for displaying indicators (optional)
- `:tooltip` - Hover text for the tab (optional)
- `:external` - Whether this links to an external site (default: false)
- `:new_tab` - Whether to open in a new tab (default: false)
- `:attention` - Attention animation: :pulse, :bounce, :shake, :glow (optional)
- `:metadata` - Custom metadata map for advanced use cases (default: %{})

## Examples

    iex> Tab.new(id: :home, label: "Home", path: "/dashboard", icon: "hero-home")
    {:ok, %Tab{id: :home, label: "Home", path: "/dashboard", icon: "hero-home"}}

    iex> Tab.new(%{id: :orders, label: "Orders", path: "/orders", priority: 100})
    {:ok, %Tab{id: :orders, label: "Orders", path: "/orders", priority: 100}}

# new!(attrs)

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

Creates a new Tab struct, raising on error.

# parent_id(tab)

    @spec parent_id(t()) :: atom() | nil

Gets the parent ID of a subtab, or nil if it's a top-level tab.

# permission_granted?(arg1, scope)

    @spec permission_granted?(t(), map()) :: boolean()

Checks if permission is granted for this tab given a scope.

Returns true if:

- The tab has no permission requirement (permission is nil)
- The scope has module access for the tab's permission key

# resolve_path(tab, context)

    @spec resolve_path(t(), :admin | :settings | :user_dashboard) :: t()

Resolves a relative tab path to an absolute path based on context.

Modules define short relative paths (e.g., `"hello-world"`) and the core prepends the appropriate prefix based on which callback returned the tab:

- `:admin` — prepends `/admin`
- `:settings` — prepends `/admin/settings`
- `:user_dashboard` — prepends `/dashboard`

Absolute paths (starting with `/`) pass through unchanged. Empty strings resolve to the context root (e.g., `""` + `:admin` → `"/admin"`).

# set_attention(tab, attention)

    @spec set_attention(t(), atom() | nil) :: t()

Sets an attention animation on the tab.

# show_subtabs?(arg1, active)

    @spec show_subtabs?(t(), boolean()) :: boolean()

Checks if subtabs should be shown for this tab based on its display setting and active state.

## Examples

    iex> tab = %Tab{subtab_display: :always}
    iex> Tab.show_subtabs?(tab, false)
    true

    iex> tab = %Tab{subtab_display: :when_active}
    iex> Tab.show_subtabs?(tab, false)
    false

    iex> tab = %Tab{subtab_display: :when_active}
    iex> Tab.show_subtabs?(tab, true)
    true

# subtab?(arg1)

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

Checks if this tab is a subtab (has a parent).

# top_level?(tab)

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

Checks if this tab is a top-level tab (not a subtab).

# update_badge(tab, badge)

    @spec update_badge(t(), PhoenixKit.Dashboard.Badge.t() | map() | nil) :: t()

Updates a tab's badge value.

# user?(arg1)

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

Checks if this is a user-level tab.

# visible?(arg1, scope)

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

Evaluates the visibility of a tab given a scope.

## Examples

    iex> tab = %Tab{visible: true}
    iex> Tab.visible?(tab, %{})
    true

    iex> tab = %Tab{visible: fn scope -> scope.user.beta_enabled end}
    iex> Tab.visible?(tab, %{user: %{beta_enabled: true}})
    true