defmodule PineUi.Select do
@moduledoc """
Provides select components for dropdown selections.
The Select module offers three main variants:
- `basic/1` - Standard select dropdown
- `grouped/1` - Select with option groups
- `searchable/1` - Enhanced select with search functionality
"""
use Phoenix.Component
@doc """
Renders a basic select dropdown component.
## Examples
<.basic
id="country"
label="Country"
options={[{"us", "United States"}, {"ca", "Canada"}]}
selected="us"
/>
## Options
* `:id` - The ID for the select element (required)
* `:name` - The name attribute (optional, defaults to ID)
* `:label` - Label text (optional)
* `:options` - List of {value, label} tuples for the options (required)
* `:selected` - The currently selected value (optional)
* `:placeholder` - Placeholder text for empty selection (optional)
* `:hint` - Help text displayed below the select (optional)
* `:error` - Error message displayed below the select (optional)
* `:required` - Whether the field is required (optional, defaults to false)
* `:disabled` - Whether the field is disabled (optional, defaults to false)
* `:phx_change` - Phoenix change event name (optional)
* `:class` - Additional CSS classes for the select element (optional)
* `:container_class` - CSS classes for the container div (optional)
"""
def basic(assigns) do
assigns =
assigns
|> assign_new(:container_class, fn -> "" end)
|> assign_new(:class, fn -> "" end)
|> assign_new(:name, fn -> assigns.id end)
|> assign_new(:required, fn -> false end)
|> assign_new(:disabled, fn -> false end)
|> assign_new(:phx_change, fn -> nil end)
|> assign_new(:selected, fn -> nil end)
~H"""
<%= if Map.get(assigns, :label, nil) do %>
<% end %>
<%= if Map.get(assigns, :hint, nil) do %>
<%= @hint %>
<% end %>
<%= if Map.get(assigns, :error, nil) do %>
<%= @error %>
<% end %>
"""
end
@doc """
Renders a select dropdown with option groups.
## Examples
<.grouped
id="continent"
label="Country"
option_groups={[
{"North America", [{"us", "United States"}, {"ca", "Canada"}]},
{"Europe", [{"fr", "France"}, {"de", "Germany"}]}
]}
/>
## Options
* `:id` - The ID for the select element (required)
* `:name` - The name attribute (optional, defaults to ID)
* `:label` - Label text (optional)
* `:option_groups` - List of {group_label, options} tuples (required)
* `:selected` - The currently selected value (optional)
* `:placeholder` - Placeholder text for empty selection (optional)
* `:hint` - Help text displayed below the select (optional)
* `:error` - Error message displayed below the select (optional)
* `:required` - Whether the field is required (optional, defaults to false)
* `:disabled` - Whether the field is disabled (optional, defaults to false)
* `:phx_change` - Phoenix change event name (optional)
* `:class` - Additional CSS classes for the select element (optional)
* `:container_class` - CSS classes for the container div (optional)
"""
def grouped(assigns) do
assigns =
assigns
|> assign_new(:container_class, fn -> "" end)
|> assign_new(:class, fn -> "" end)
|> assign_new(:name, fn -> assigns.id end)
|> assign_new(:required, fn -> false end)
|> assign_new(:disabled, fn -> false end)
|> assign_new(:phx_change, fn -> nil end)
|> assign_new(:selected, fn -> nil end)
~H"""
<%= if Map.get(assigns, :label, nil) do %>
<% end %>
<%= if Map.get(assigns, :hint, nil) do %>
<%= @hint %>
<% end %>
<%= if Map.get(assigns, :error, nil) do %>
<%= @error %>
<% end %>
"""
end
@doc """
Renders a searchable select dropdown with filtering.
## Examples
<.searchable
id="country"
label="Country"
options={[{"us", "United States"}, {"ca", "Canada"}]}
placeholder="Search..."
/>
## Options
* `:id` - The ID for the select element (required)
* `:name` - The name attribute (optional, defaults to ID)
* `:label` - Label text (optional)
* `:options` - List of {value, label} tuples for the options (required)
* `:selected` - The currently selected value (optional)
* `:selected_label` - The label for the selected value (optional)
* `:placeholder` - Placeholder text for the search (optional)
* `:hint` - Help text displayed below the select (optional)
* `:error` - Error message displayed below the select (optional)
* `:required` - Whether the field is required (optional, defaults to false)
* `:disabled` - Whether the field is disabled (optional, defaults to false)
* `:phx_change` - Phoenix change event name (optional)
* `:class` - Additional CSS classes for the select element (optional)
* `:container_class` - CSS classes for the container div (optional)
"""
def searchable(assigns) do
assigns =
assigns
|> assign_new(:container_class, fn -> "" end)
|> assign_new(:class, fn -> "" end)
|> assign_new(:name, fn -> assigns.id end)
|> assign_new(:placeholder, fn -> "Select an option" end)
|> assign_new(:required, fn -> false end)
|> assign_new(:disabled, fn -> false end)
|> assign_new(:phx_change, fn -> nil end)
|> assign_new(:selected, fn -> nil end)
|> assign_new(:selected_label, fn -> nil end)
options_json = Jason.encode!(Enum.map(assigns.options, fn {value, label} -> %{value: value, label: label} end))
~H"""