Packages

phoenix_kit

1.7.113
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib phoenix_kit_web components dashboard context_selector.ex
Raw

lib/phoenix_kit_web/components/dashboard/context_selector.ex

defmodule PhoenixKitWeb.Components.Dashboard.ContextSelector do
@moduledoc """
Context selector dropdown component for dashboard navigation.
Allows users with multiple contexts (organizations, farms, teams, etc.)
to switch between them. Only renders when the user has 2+ contexts.
## Components
- `context_selector/1` - Header dropdown (for `position: :header` with `sub_position: :start` or `:end`)
- `sidebar_context_selector/1` - Sidebar dropdown (for `position: :sidebar`)
- `mobile_context_selector/1` - Mobile menu variant
## Usage
Header position (default):
<.context_selector
contexts={@dashboard_contexts}
current={@current_context}
config={@context_selector_config}
/>
Sidebar position:
<.sidebar_context_selector
contexts={@dashboard_contexts}
current={@current_context}
config={@context_selector_config}
/>
Or use the convenience wrappers that check visibility:
<.context_selector_if_enabled
show={@show_context_selector}
contexts={@dashboard_contexts}
current={@current_context}
config={@context_selector_config}
/>
<.sidebar_context_selector_if_enabled
show={@show_context_selector}
contexts={@dashboard_contexts}
current={@current_context}
config={@context_selector_config}
/>
"""
use Phoenix.Component
alias PhoenixKit.Dashboard.ContextSelector
import PhoenixKitWeb.Components.Core.Icon, only: [icon: 1]
@doc """
Renders the context selector dropdown.
Only renders if there are contexts to display.
## Attributes
- `contexts` - List of context items
- `current` - The currently selected context
- `config` - The ContextSelector config struct
- `class` - Additional CSS classes
"""
attr :contexts, :list, required: true
attr :current, :any, default: nil
attr :config, :any, required: true
attr :class, :string, default: ""
def context_selector(assigns) do
~H"""
<div class={["dropdown dropdown-end", @class]}>
<label tabindex="0" class="btn btn-ghost gap-2 normal-case font-normal">
<%= if @config.icon do %>
<.icon name={@config.icon} class="w-4 h-4" />
<% end %>
<span class="max-w-[150px] truncate">
{get_current_display_name(@current, @config)}
</span>
<.icon name="hero-chevron-down" class="w-4 h-4 opacity-50" />
</label>
<ul
tabindex="0"
class="dropdown-content z-50 menu p-2 shadow-lg bg-base-100 rounded-box w-56 border border-base-300"
>
<li class="menu-title">
<span>Switch {@config.label}</span>
</li>
<%= for context <- @contexts do %>
<li>
<.link
href={context_switch_path(context, @config)}
method="post"
class={context_item_classes(context, @current, @config)}
>
<%= if @config.icon do %>
<.icon name={@config.icon} class="w-4 h-4" />
<% end %>
<span class="flex-1 truncate">
{get_display_name(context, @config)}
</span>
<%= if current?(context, @current, @config) do %>
<.icon name="hero-check" class="w-4 h-4 text-success" />
<% end %>
</.link>
</li>
<% end %>
</ul>
</div>
"""
end
@doc """
Conditionally renders the context selector based on visibility flag.
Use this wrapper to avoid checking `@show_context_selector` manually.
## Attributes
- `show` - Boolean flag from `@show_context_selector`
- All other attributes are passed to `context_selector/1`
"""
attr :show, :boolean, default: false
attr :contexts, :list, required: true
attr :current, :any, default: nil
attr :config, :any, required: true
attr :class, :string, default: ""
def context_selector_if_enabled(assigns) do
~H"""
<%= if @show and @config.enabled do %>
<.context_selector
contexts={@contexts}
current={@current}
config={@config}
class={@class}
/>
<% end %>
"""
end
@doc """
Renders a mobile-friendly context selector for mobile menus.
Shows as a list section at the top of mobile navigation.
## Attributes
- `contexts` - List of context items
- `current` - The currently selected context
- `config` - The ContextSelector config struct
"""
attr :contexts, :list, required: true
attr :current, :any, default: nil
attr :config, :any, required: true
def mobile_context_selector(assigns) do
~H"""
<div class="px-2 py-3 border-b border-base-300">
<div class="text-xs font-semibold text-base-content/60 uppercase tracking-wider mb-2 px-2">
Current {@config.label}
</div>
<ul class="menu menu-sm">
<%= for context <- @contexts do %>
<li>
<.link
href={context_switch_path(context, @config)}
method="post"
class={mobile_context_item_classes(context, @current, @config)}
>
<%= if @config.icon do %>
<.icon name={@config.icon} class="w-4 h-4" />
<% end %>
<span class="flex-1 truncate">
{get_display_name(context, @config)}
</span>
<%= if current?(context, @current, @config) do %>
<.icon name="hero-check" class="w-4 h-4 text-success" />
<% end %>
</.link>
</li>
<% end %>
</ul>
</div>
"""
end
@doc """
Conditionally renders the mobile context selector.
"""
attr :show, :boolean, default: false
attr :contexts, :list, required: true
attr :current, :any, default: nil
attr :config, :any, required: true
def mobile_context_selector_if_enabled(assigns) do
~H"""
<%= if @show and @config.enabled do %>
<.mobile_context_selector
contexts={@contexts}
current={@current}
config={@config}
/>
<% end %>
"""
end
@doc """
Renders a context selector for the sidebar.
Displays as a dropdown at the top of the sidebar navigation.
## Attributes
- `contexts` - List of context items
- `current` - The currently selected context
- `config` - The ContextSelector config struct
- `class` - Additional CSS classes
"""
attr :contexts, :list, required: true
attr :current, :any, default: nil
attr :config, :any, required: true
attr :class, :string, default: ""
def sidebar_context_selector(assigns) do
~H"""
<div class={["mb-4 px-2", @class]}>
<div class="text-xs font-semibold text-base-content/50 uppercase tracking-wider mb-2">
{@config.label}
</div>
<div class="dropdown dropdown-bottom w-full">
<label
tabindex="0"
class="btn btn-ghost btn-sm w-full justify-between gap-2 normal-case font-normal bg-base-200 hover:bg-base-300"
>
<span class="flex items-center gap-2 truncate">
<%= if @config.icon do %>
<.icon name={@config.icon} class="w-4 h-4 shrink-0" />
<% end %>
<span class="truncate">
{get_current_display_name(@current, @config)}
</span>
</span>
<.icon name="hero-chevron-up-down" class="w-4 h-4 opacity-50 shrink-0" />
</label>
<ul
tabindex="0"
class="dropdown-content z-50 menu p-2 shadow-lg bg-base-100 rounded-box w-full border border-base-300 mt-1"
>
<%= for context <- @contexts do %>
<li>
<.link
href={context_switch_path(context, @config)}
method="post"
class={context_item_classes(context, @current, @config)}
>
<%= if @config.icon do %>
<.icon name={@config.icon} class="w-4 h-4" />
<% end %>
<span class="flex-1 truncate">
{get_display_name(context, @config)}
</span>
<%= if current?(context, @current, @config) do %>
<.icon name="hero-check" class="w-4 h-4 text-success" />
<% end %>
</.link>
</li>
<% end %>
</ul>
</div>
</div>
"""
end
@doc """
Conditionally renders the sidebar context selector.
"""
attr :show, :boolean, default: false
attr :contexts, :list, required: true
attr :current, :any, default: nil
attr :config, :any, required: true
attr :class, :string, default: ""
def sidebar_context_selector_if_enabled(assigns) do
~H"""
<%= if @show and @config.enabled do %>
<.sidebar_context_selector
contexts={@contexts}
current={@current}
config={@config}
class={@class}
/>
<% end %>
"""
end
# Private helpers
defp get_current_display_name(nil, config), do: "Select #{config.label}"
defp get_current_display_name(current, config) do
ContextSelector.get_display_name_for_config(config, current)
end
defp get_display_name(context, config) do
ContextSelector.get_display_name_for_config(config, context)
end
defp context_switch_path(context, config) do
id = ContextSelector.get_id_for_config(config, context)
url_prefix = PhoenixKit.Config.get_url_prefix()
# Use keyed path for multi-selector, legacy path for single selector
if config.key && config.key != :default do
"#{url_prefix}/context/#{config.key}/#{id}"
else
"#{url_prefix}/context/#{id}"
end
end
defp current?(context, current, config) do
context_id = ContextSelector.get_id_for_config(config, context)
current_id = ContextSelector.get_id_for_config(config, current)
context_id == current_id
end
defp context_item_classes(context, current, config) do
base_classes = "flex items-center gap-2"
if current?(context, current, config) do
"#{base_classes} active"
else
base_classes
end
end
defp mobile_context_item_classes(context, current, config) do
base_classes = "flex items-center gap-2"
if current?(context, current, config) do
"#{base_classes} bg-primary/10 text-primary"
else
base_classes
end
end
end