Configuration and helpers for the dashboard context selector.
The context selector allows users to switch between multiple contexts (organizations, farms, teams, workspaces, etc.) in the dashboard. Users with only one context won't see the selector.
Single Selector Configuration (Legacy)
Configure in your config/config.exs:
config :phoenix_kit, :dashboard_context_selector,
loader: {MyApp.Farms, :list_for_user},
display_name: fn farm -> farm.name end,
id_field: :uuid,
label: "Farm",
icon: "hero-building-office",
position: :sidebar,
sub_position: :end,
empty_behavior: :hide,
session_key: "dashboard_context_uuid",
tab_loader: {MyApp.Farms, :get_tabs_for_context}Multiple Selectors Configuration
Configure multiple independent or dependent selectors:
config :phoenix_kit, :dashboard_context_selectors, [
%{
key: :organization,
loader: {MyApp.Orgs, :list_for_user},
display_name: fn org -> org.name end,
label: "Organization",
icon: "hero-building-office",
position: :header,
sub_position: :start,
priority: 100
},
%{
key: :project,
depends_on: :organization,
loader: {MyApp.Projects, :list_for_org},
display_name: fn p -> p.name end,
label: "Project",
icon: "hero-folder",
position: :header,
sub_position: :end,
priority: 200,
on_parent_change: :reset
}
]Configuration Options
:key- Required for multi-selector. Unique atom identifier (e.g.,:organization). Used in session storage and routes.:loader- Required. A{Module, :function}tuple that takes a user ID and returns a list of context items. Example:{MyApp.Farms, :list_for_user}For dependent selectors, the function receives(user_uuid, parent_context).:display_name- Required. A function that takes a context item and returns the display string. Example:fn farm -> farm.name end:id_field- Optional. The field to use as the unique identifier. Defaults to:id. Can be an atom or a function.:label- Optional. The label shown in the UI (e.g., "Farm", "Organization"). Defaults to"Context".:icon- Optional. Heroicon name for the selector. Defaults to"hero-building-office".:position- Optional. Which area to show the selector in. Options::header(default),:sidebar.:sub_position- Optional. Where within the area to place the selector. For header::start(left, after logo),:end(right, before user menu), or{:priority, N}to sort among other header items. For sidebar::start(top),:end(pinned to very bottom), or{:priority, N}to sort among tabs. Defaults to:start.:priority- Optional. Integer for ordering within same position/sub_position. Lower values come first. Defaults to 500.:depends_on- Optional. Key of parent selector (for dependent selectors). When set, the loader receives(user_uuid, parent_context)instead of justuser_uuid.:on_parent_change- Optional. What to do when parent selector changes. Options::reset(default, select first),:keep,{:redirect, "/path"}.:empty_behavior- Optional. What to do when user has no contexts. Options::hide(default),:show_empty,{:redirect, "/path"}.:separator- Optional. Separator shown between logo and selector in header (only applies toposition: :header, sub_position: :start). Defaults to"/". Set tofalseornilto disable. Can be any string like"›","|", or"·".Note: The separator may appear slightly off-center due to internal padding in the selector dropdown. This is a visual quirk and can be adjusted by customizing the layout template if precise alignment is required.
:session_key- Optional. The session key for storing the selected context UUID. Defaults to"dashboard_context_uuid"for single selector, or"dashboard_context_uuids"(map) for multiple selectors.:tab_loader- Optional. A{Module, :function}tuple that takes a context item and returns a list of tab definitions. Enables dynamic tabs that change based on the selected context. Example:{MyApp.Farms, :get_tabs_for_context}
Usage in LiveViews
The ContextProvider on_mount hook automatically sets these assigns:
Single Selector (Legacy)
@dashboard_contexts- List of all contexts for the user@current_context- The currently selected context item@show_context_selector- Boolean, true only if user has 2+ contexts@dashboard_tabs- (Optional) List of Tab structs whentab_loaderis configured
Multiple Selectors
@dashboard_contexts_map- Map of key => list of contexts@current_contexts_map- Map of key => current context item@show_context_selectors_map- Map of key => boolean@context_selector_configs- List of all ContextSelector configs
Access the current context in your LiveView:
def mount(_params, _session, socket) do
context = socket.assigns.current_context
items = MyApp.Items.list_for_context(context.id)
{:ok, assign(socket, items: items)}
endOr use the helper functions:
context_uuid = PhoenixKit.Dashboard.current_context_uuid(socket)
Summary
Functions
Checks if the context selector feature is enabled.
Finds a context by ID from a list of contexts.
Gets all context selector configurations.
Gets the context selector configuration.
Gets the keys of all selectors that depend on the given key.
Gets the display name for a context item using the configured function.
Gets the display name for a context item using a specific config.
Gets the ID for a context item using the configured id_field.
Gets the ID for a context item using a specific config.
Loads contexts for a user using the configured loader.
Loads contexts for a specific selector config.
Loads tabs for the given context using the configured tab_loader.
Checks if multiple selectors are configured.
Gets the session key for storing multiple context IDs.
Orders selectors by their dependencies using topological sort.
Gets the session key for storing the context ID.
Types
@type on_parent_change() :: :reset | :keep | {:redirect, String.t()}
@type sub_position() :: :start | :end | {:priority, integer()}
@type t() :: %PhoenixKit.Dashboard.ContextSelector{ depends_on: atom() | nil, display_name: (any() -> String.t()) | nil, empty_behavior: :hide | :show_empty | {:redirect, String.t()}, enabled: boolean(), icon: String.t() | nil, id_field: atom() | (any() -> any()), key: atom() | nil, label: String.t(), loader: {module(), atom()} | nil, on_parent_change: on_parent_change(), position: :header | :sidebar, priority: integer(), separator: String.t() | false | nil, session_key: String.t(), sub_position: sub_position() | nil, tab_loader: {module(), atom()} | nil }
Functions
@spec enabled?() :: boolean()
Checks if the context selector feature is enabled.
Returns true if the feature is configured with a valid loader.
Examples
iex> PhoenixKit.Dashboard.ContextSelector.enabled?()
true
Finds a context by ID from a list of contexts.
Handles both string and integer ID comparison.
Examples
iex> PhoenixKit.Dashboard.ContextSelector.find_by_id(contexts, "123")
%Farm{id: 123, ...}
@spec get_all_configs() :: [t()]
Gets all context selector configurations.
Returns a list of validated %ContextSelector{} structs.
Handles both the legacy single selector config (:dashboard_context_selector)
and the new multi-selector config (:dashboard_context_selectors).
For legacy single selectors, assigns the default key :default.
Examples
iex> PhoenixKit.Dashboard.ContextSelector.get_all_configs()
[%ContextSelector{key: :organization, ...}, %ContextSelector{key: :project, ...}]
@spec get_config() :: t()
Gets the context selector configuration.
Returns a validated %ContextSelector{} struct if configured,
or a disabled struct if not configured.
Examples
iex> PhoenixKit.Dashboard.ContextSelector.get_config()
%ContextSelector{enabled: true, loader: {MyApp.Farms, :list_for_user}, ...}
iex> PhoenixKit.Dashboard.ContextSelector.get_config()
%ContextSelector{enabled: false}
Gets the keys of all selectors that depend on the given key.
Useful for determining which selectors need to be reset when a parent changes.
Examples
iex> configs = [%{key: :org}, %{key: :project, depends_on: :org}]
iex> PhoenixKit.Dashboard.ContextSelector.get_dependent_keys(configs, :org)
[:project]
Gets the display name for a context item using the configured function.
Examples
iex> PhoenixKit.Dashboard.ContextSelector.get_display_name(farm)
"My Farm"
Gets the display name for a context item using a specific config.
Examples
iex> PhoenixKit.Dashboard.ContextSelector.get_display_name_for_config(config, item)
"My Organization"
Gets the ID for a context item using the configured id_field.
Examples
iex> PhoenixKit.Dashboard.ContextSelector.get_id(%{id: 123})
123
Gets the ID for a context item using a specific config.
Loads contexts for a user using the configured loader.
Returns an empty list if the feature is not enabled or the loader fails.
Examples
iex> PhoenixKit.Dashboard.ContextSelector.load_contexts(user_uuid)
[%Farm{id: 1, name: "My Farm"}, %Farm{id: 2, name: "Other Farm"}]
Loads contexts for a specific selector config.
For independent selectors, calls loader(user_uuid).
For dependent selectors, calls loader(user_uuid, parent_context).
Parameters
config- The selector configurationuser_uuid- The user UUID to load contexts forparent_context- The parent context (required for dependent selectors)
Loads tabs for the given context using the configured tab_loader.
Returns an empty list if no tab_loader is configured or if the loader fails.
Examples
iex> PhoenixKit.Dashboard.ContextSelector.load_tabs(context)
[%{id: :overview, label: "Overview", ...}, ...]
@spec multi_selector_enabled?() :: boolean()
Checks if multiple selectors are configured.
Returns true if the plural :dashboard_context_selectors config is set.
@spec multi_session_key() :: String.t()
Gets the session key for storing multiple context IDs.
Returns the configured multi-selector session key.
Orders selectors by their dependencies using topological sort.
Independent selectors come first, then dependent selectors in order of their dependency chain. Selectors with the same dependency level are sorted by priority (lower first).
Examples
iex> configs = [%{key: :project, depends_on: :org}, %{key: :org}]
iex> PhoenixKit.Dashboard.ContextSelector.order_by_dependencies(configs)
[%{key: :org}, %{key: :project, depends_on: :org}]
@spec session_key() :: String.t()
Gets the session key for storing the context ID.