Packages
moon
2.76.4
2.90.5
retired
2.90.4
2.90.3
2.90.2
2.90.1
2.90.0
2.89.4
2.89.1
2.89.0
2.88.0
2.87.11
2.87.10
2.87.9
2.87.8
2.87.7
2.87.6
2.87.5
2.87.4
2.87.3
2.87.2
2.87.1
2.87.0
2.86.1
2.86.0
2.85.1
2.85.0
2.84.0
2.83.0
2.82.0
2.81.4
2.81.3
2.81.2
2.81.1
2.81.0
2.80.2
2.80.1
2.80.0
2.79.13
2.79.12
2.79.11
2.79.10
2.79.9
2.79.8
2.79.7
2.79.6
2.79.5
2.79.4
2.79.3
2.79.2
2.79.1
2.79.0
2.78.4
2.78.3
2.78.2
2.78.1
2.78.0
2.77.0
2.76.5
2.76.4
2.76.3
2.76.2
2.76.1
2.76.0
2.75.0
2.74.1
2.74.0
2.73.8
2.73.7
2.73.6
2.73.5
2.73.4
2.73.3
2.73.2
2.73.1
2.73.0
2.72.5
2.72.4
2.72.3
2.72.2
2.72.1
2.72.0
2.71.1
2.71.0
2.70.0
2.69.2
2.69.1
2.69.0
2.68.11
2.68.10
Components-based design system written in elixir
Current section
Files
Jump to
Current section
Files
lib/moon/components/select/single_select.ex
defmodule Moon.Components.Select.SingleSelect do
@moduledoc false
use Moon.StatefulComponent
alias Moon.Autolayouts.PullAside
alias Moon.Autolayouts.TopToDown
alias Moon.Components.FieldBorder
alias Moon.Components.FieldLabel
alias Moon.Components.Popover
alias Moon.Components.Select.Dropdown
alias Moon.Components.Select.Helpers, as: SelectHelpers
alias Moon.Components.Select.SingleSelect.Value.SelectedValue
alias Moon.Components.Select.SingleSelect.HintText
alias Moon.Components.ErrorTag
prop(label, :string)
prop(options, :any, default: [])
prop(value, :any)
prop(disabled, :boolean, default: false)
prop(size, :string, values: ~w(sm md lg xl), default: "md")
prop(popover_placement, :string, default: "bottom-start")
prop(popover_class, :string)
prop(placeholder, :string)
prop(background_color, :string, values: Moon.colors(), default: "goku")
prop(has_error, :boolean)
prop(use_error_tag, :boolean)
prop(selected_value_class, :css_class, default: "")
prop(searched_options, :any, default: nil)
prop(on_search_change, :event)
prop(search_string, :string, default: nil)
prop(with, :string, default: nil)
prop(search_min_width, :css_class, default: "min-w-[144px]")
data(open, :boolean, default: false)
data(form, :form, from_context: {Surface.Components.Form, :form})
data(field, :atom, from_context: {Surface.Components.Form.Field, :field})
slot(hint_text_slot)
def render(assigns) do
~F"""
{#if @size != "xl"}
<FieldLabel class="block text-trunks text-moon-16 font-normal mb-2">
{@label}
</FieldLabel>
{/if}
<Popover placement={@popover_placement} show={@open} on_close="close" class={@popover_class}>
{Phoenix.HTML.Form.select(
@form,
@field,
SelectHelpers.get_formatted_options(
@options,
SelectHelpers.get_normalized_value(@form, @field, false, value: @value)
),
class: "hidden",
id: @id,
prompt: @label
)}
<FieldBorder
testid={"#{@id}-toggle_open"}
click="toggle_open"
class={
"w-full",
"bg-#{@background_color}": @background_color
}
has_error={has_error(@has_error, @form, @field)}
>
<PullAside class={
"leading-6",
SelectHelpers.get_padding(@size),
get_disabled_class(@disabled)
}>
<:left>
<SelectedValue
class={@selected_value_class}
{=@size}
{=@label}
{=@placeholder}
select_id={@id}
option={get_option_selected(@form, @field, @options, @value)}
/>
</:left>
<:right>
<Moon.Icons.ControlsChevronDown />
</:right>
</PullAside>
</FieldBorder>
<:content>
<TopToDown>
<div class={
"overflow-auto rounded-moon-i-md box-border border border-solid",
"border-beerus min-h-[20px] max-h-[200px] drop-shadow-2xl",
"#{@search_min_width}": not is_nil(@on_search_change)
}>
<Dropdown
id={"#{@id}-dropdown"}
select_id={@id}
available_options={@options}
options={@searched_options || @options}
{=@disabled}
{=@on_search_change}
{=@search_string}
{=@with}
/>
</div>
</TopToDown>
</:content>
</Popover>
<HintText :if={slot_assigned?(:hint_text_slot)} {=@has_error}>
<#slot {@hint_text_slot} />
</HintText>
<div
class="inline-block mt-2 text-moon-12"
:if={@use_error_tag && has_error(@has_error, @form, @field)}
>
<ErrorTag {=@field} />
</div>
"""
end
def handle_event("close", _params, socket) do
socket = assign(socket, open: false)
{:noreply, socket}
end
def handle_event("toggle_open", _params, socket) do
if socket.assigns.disabled == false do
{:noreply, assign(socket, open: !socket.assigns.open)}
else
{:noreply, socket}
end
end
#
# Private methods
#
defp get_disabled_class(disabled) do
if disabled do
"opacity-60 cursor-not-allowed"
end
end
defp get_option_selected(form, field, options, value) do
normalized_value = SelectHelpers.get_normalized_value(form, field, false, value: value)
if normalized_value != "" do
SelectHelpers.get_option(options, normalized_value)
else
""
end
end
defp has_error(has_error, form, field) do
has_error || (field && form && Keyword.has_key?(form.errors, field))
end
end