PhoenixKit.Dashboard.ContextSelector (phoenix_kit v1.7.33)

Copy Markdown View Source

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: :id,
  label: "Farm",
  icon: "hero-building-office",
  position: :sidebar,
  sub_position: :end,
  empty_behavior: :hide,
  session_key: "dashboard_context_id",
  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_id, 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_id, parent_context) instead of just user_id.

  • :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 to position: :header, sub_position: :start). Defaults to "/". Set to false or nil to 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 ID. Defaults to "dashboard_context_id" for single selector, or "dashboard_context_ids" (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 when tab_loader is 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)}
end

Or use the helper functions:

context_id = PhoenixKit.Dashboard.current_context_id(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

on_parent_change()

@type on_parent_change() :: :reset | :keep | {:redirect, String.t()}

sub_position()

@type sub_position() :: :start | :end | {:priority, integer()}

t()

@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

enabled?()

@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

find_by_id(contexts, id)

@spec find_by_id(list(), any()) :: any() | nil

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, ...}

get_all_configs()

@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, ...}]

get_config()

@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}

get_dependent_keys(configs, parent_key)

@spec get_dependent_keys([t()], atom()) :: [atom()]

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]

get_display_name(item)

@spec get_display_name(any()) :: String.t()

Gets the display name for a context item using the configured function.

Examples

iex> PhoenixKit.Dashboard.ContextSelector.get_display_name(farm)
"My Farm"

get_display_name_for_config(arg1, item)

@spec get_display_name_for_config(t(), any()) :: String.t()

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"

get_id(item)

@spec get_id(any()) :: any()

Gets the ID for a context item using the configured id_field.

Examples

iex> PhoenixKit.Dashboard.ContextSelector.get_id(%{id: 123})
123

get_id_for_config(arg1, item)

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

Gets the ID for a context item using a specific config.

load_contexts(user_id)

@spec load_contexts(any()) :: list()

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_id)
[%Farm{id: 1, name: "My Farm"}, %Farm{id: 2, name: "Other Farm"}]

load_contexts_for_config(config, user_id, parent_context \\ nil)

@spec load_contexts_for_config(t(), any(), any()) :: list()

Loads contexts for a specific selector config.

For independent selectors, calls loader(user_id). For dependent selectors, calls loader(user_id, parent_context).

Parameters

  • config - The selector configuration
  • user_id - The user ID to load contexts for
  • parent_context - The parent context (required for dependent selectors)

load_tabs(context)

@spec load_tabs(any()) :: list()

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", ...}, ...]

multi_selector_enabled?()

@spec multi_selector_enabled?() :: boolean()

Checks if multiple selectors are configured.

Returns true if the plural :dashboard_context_selectors config is set.

multi_session_key()

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

Gets the session key for storing multiple context IDs.

Returns the configured multi-selector session key.

order_by_dependencies(configs)

@spec order_by_dependencies([t()]) :: [t()]

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}]

session_key()

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

Gets the session key for storing the context ID.