Current section

Files

Jump to
moon lib moon components switch.ex
Raw

lib/moon/components/switch.ex

defmodule Moon.Components.Switch do
@moduledoc false
use Moon.StatelessComponent
alias Moon.Icon
alias Moon.Components.Switch.Caption
alias Phoenix.LiveView.JS
@icons_sizes %{
"small" => "0.75rem",
"medium" => "1rem",
"large" => "1.5rem"
}
prop(id, :string, required: true)
prop(field, :atom, from_context: {Surface.Components.Form.Field, :field})
prop(form, :form, from_context: {Surface.Components.Form, :form})
prop(checked, :boolean, default: false)
prop(icons, :boolean, default: false)
prop(size, :string, values: ["small", "medium", "large"], default: "medium")
prop(action, :string, default: "Toggle Setting")
prop(caption_unchecked, :string)
prop(caption_checked, :string)
prop(icon_name_off, :string, default: "other_moon")
prop(icon_name_on, :string, default: "other_sun")
prop(class, :css_class, default: "")
prop(on_click, :event)
def render(assigns) do
~F"""
<div
{=@id}
class={
"relative inline-flex items-center",
"space-x-1": @size != "large",
"space-x-2": @size == "large",
"#{@class}": @class
}
:on-click={@on_click}
>
<Caption :if={@caption_unchecked} label={@caption_unchecked} size={@size} active={!@checked} />
<Surface.Components.Form.RadioButton
{=@form}
{=@field}
class="hidden"
value="true"
opts={
id: "#{@id}_radio_true",
checked: is_selected(@checked, @form, @field)
}
/>
<Surface.Components.Form.RadioButton
{=@form}
{=@field}
class="hidden"
value="false"
opts={
id: "#{@id}_radio_false",
checked: !is_selected(@checked, @form, @field)
}
/>
<button
type="button"
aria-pressed="false"
class={
"relative inline-flex shrink-0 transition-colors duration-200 ease-in-out border-transparent rounded-full cursor-pointer outline-none focus:outline-none bg-piccolo shadow-md",
"bg-piccolo": is_selected(@checked, @form, @field),
"bg-gohan": !is_selected(@checked, @form, @field),
"border-2 w-7 focus:ring-1 focus:ring-offset-1": @size == "small",
"border-4 w-10 focus:ring-2 focus:ring-offset-1": @size == "medium",
"border-4 w-14 focus:ring-2 focus:ring-offset-2": @size == "large"
}
:on-click={!@on_click &&
JS.dispatch("moon:update-switch",
detail: %{
checked: !is_selected(@checked, @form, @field),
switch_id: @id
},
to: "##{@id}_radio_true"
)}
>
<span class="sr-only">{@action}</span>
<div :if={@icons} class="absolute flex">
<Icon name={@icon_name_off} class="scale-90" font_size={icon_size(@size)} />
<Icon name={@icon_name_on} class="scale-90" font_size={icon_size(@size)} />
</div>
<span
aria-hidden="true"
class={
"inline-block transition duration-200 ease-in-out translate-x-0 bg-gohan rounded-full shadow-md pointer-events-none ring-0 shadow-md",
"w-3 h-3": @size == "small",
"w-4 h-4": @size == "medium",
"w-6 h-6": @size == "large",
"ltr:translate-x-3 rtl:-translate-x-3":
@size == "small" and is_selected(@checked, @form, @field),
"ltr:translate-x-4 rtl:-translate-x-4":
@size == "medium" and is_selected(@checked, @form, @field),
"ltr:translate-x-6 rtl:-translate-x-6":
@size == "large" and is_selected(@checked, @form, @field)
}
/>
</button>
<Caption
:if={@caption_checked}
label={@caption_checked}
size={@size}
active={is_selected(@checked, @form, @field)}
/>
</div>
"""
end
def icon_size(size) do
@icons_sizes[size]
end
def is_true(val) do
Enum.member?([true, "true"], val)
end
def is_selected(checked, form, field) do
is_true(checked) || is_true(Phoenix.HTML.Form.input_value(form, field))
end
end