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
Functions
Checks if this is an admin-level tab.
Clears the attention animation from the tab.
Creates a divider pseudo-tab for visual separation.
Checks if this tab is a divider.
Creates a group header pseudo-tab for organizing sections.
Checks if this tab is a group header.
Checks if the current path matches this tab's path according to its match strategy.
Checks if the module associated with this tab is enabled.
Checks if this tab is a regular navigable tab (not divider or header).
Creates a new Tab struct from a map or keyword list.
Creates a new Tab struct, raising on error.
Gets the parent ID of a subtab, or nil if it's a top-level tab.
Checks if permission is granted for this tab given a scope.
Resolves a relative tab path to an absolute path based on context.
Sets an attention animation on the tab.
Checks if subtabs should be shown for this tab based on its display setting and active state.
Checks if this tab is a subtab (has a parent).
Checks if this tab is a top-level tab (not a subtab).
Updates a tab's badge value.
Checks if this is a user-level tab.
Evaluates the visibility of a tab given a scope.
Types
@type level() :: :user | :admin | :all
@type subtab_animation() :: :none | :slide | :fade | :collapse
@type subtab_display() :: :when_active | :always
@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() }
Functions
Checks if this is an admin-level tab.
Clears the attention animation from the tab.
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")
Checks if this tab is a divider.
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)
Checks if this tab is a group header.
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
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
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:permissioninstead.: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}}
Creates a new Tab struct, raising on error.
Gets the parent ID of a subtab, or nil if it's a top-level tab.
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
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").
Sets an attention animation on the tab.
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
Checks if this tab is a subtab (has a parent).
Checks if this tab is a top-level tab (not a subtab).
@spec update_badge(t(), PhoenixKit.Dashboard.Badge.t() | map() | nil) :: t()
Updates a tab's badge value.
Checks if this is a user-level tab.
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