Files
Rivertalk/lib/river_connect_web/components/ui/switch.ex
T
2026-02-12 17:10:03 -06:00

95 lines
3.1 KiB
Elixir

defmodule RiverConnectWeb.Components.UI.Switch do
@moduledoc """
Implementation of a switch/toggle component.
A switch is a control that allows users to toggle between checked and not checked states.
"""
use RiverConnectWeb.Components.UI, :component
@doc """
Renders a switch component.
## Props
* `:id` - The id to be applied to the input element
* `:name` - The name to be applied to the input element
* `:class` - Additional CSS classes
* `:value` - The current value of the switch
* `:default-value` - The default value of the switch
* `:field` - Phoenix form field
* `:disabled` - Whether the switch is disabled
* `:on-checked-changed` - Handler for value change event
"""
attr :id, :string, default: nil
attr :name, :string, default: nil
attr :class, :string, default: nil
attr :checked, :boolean, default: false
attr :disabled, :boolean, default: false
attr :"on-checked-changed", :any, default: nil, doc: "Handler for value change event"
attr :field, Phoenix.HTML.FormField,
doc: "a form field struct retrieved from the form, for example: @form[:active]"
attr :rest, :global
def switch(assigns) do
assigns = prepare_assign(assigns)
# Normalize value for boolean
assigns = assign(assigns, :checked, normalize_boolean(assigns.checked))
# Collect event mappings
event_map =
add_event_mapping(%{}, assigns, "checked-changed", :"on-checked-changed")
assigns =
assigns
|> assign(:event_map, json(event_map))
|> assign(:initial_state, if(assigns.checked, do: "checked", else: "unchecked"))
|> assign(
:options,
json(%{
disabled: assigns.disabled
})
)
~H"""
<div
id={@id}
data-component="switch"
data-state={@initial_state}
data-options={@options}
data-event-mappings={@event_map}
data-part="root"
phx-hook="SaladUI"
data-disabled={@disabled}
class={
classes([
"switch-root inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
@class
])
}
tabindex={if @disabled, do: "-1", else: "0"}
{@rest}
>
<input type="hidden" name={@name} value="false" />
<input
type="checkbox"
id={"#{@id}-input"}
name={@name}
value="true"
class="sr-only"
checked={@checked}
disabled={@disabled}
aria-checked={@checked}
data-part="input"
/>
<span
data-part="thumb"
data-state={@initial_state}
class="pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
/>
</div>
"""
end
end