Corex. RadioGroup
(Corex v0.1.0)
View Source
Phoenix implementation of Zag.js Radio Group.
Anatomy
Minimal
<.radio_group
name="rg-minimal"
class="radio-group"
items={[
%{value: "lorem", label: "Lorem ipsum dolor sit amet"},
%{value: "duis", label: "Duis dictum gravida odio ac pharetra?"},
%{value: "donec", label: "Donec condimentum ex mi"}
]}
>
<:label>Choose one</:label>
</.radio_group>With indicator
<.radio_group
name="rg-indicator"
class="radio-group"
items={[
%{value: "lorem", label: "Lorem ipsum dolor sit amet"},
%{value: "duis", label: "Duis dictum gravida odio ac pharetra?"},
%{value: "donec", label: "Donec condimentum ex mi"}
]}
>
<:label>Choose one</:label>
<:item_control><.heroicon name="hero-check" class="data-checked" /></:item_control>
</.radio_group>Invalid
<.radio_group
name="rg-invalid"
class="radio-group"
invalid
errors={["Required"]}
items={[
%{value: "lorem", label: "Lorem ipsum dolor sit amet"},
%{value: "duis", label: "Duis dictum gravida odio ac pharetra?"},
%{value: "donec", label: "Donec condimentum ex mi"}
]}
>
<:label>Choose one</:label>
<:item_control><.heroicon name="hero-check" class="data-checked" /></:item_control>
<:error :let={msg}>
<.heroicon name="hero-exclamation-circle" class="icon" />
{msg}
</:error>
</.radio_group>Read-only
<.radio_group
name="rg-readonly"
class="radio-group"
read_only
value="lorem"
items={[
%{value: "lorem", label: "Lorem ipsum dolor sit amet"},
%{value: "duis", label: "Duis dictum gravida odio ac pharetra?"},
%{value: "donec", label: "Donec condimentum ex mi"}
]}
>
<:label>Choose one</:label>
<:item_control><.heroicon name="hero-check" class="data-checked" /></:item_control>
</.radio_group>Items can be {value, label} tuples or maps with :value, :label, and optional :disabled, :invalid.
Events
Pick an event name and pass it to on_* on <.radio_group>.
Server events
| Event | When | Payload |
|---|---|---|
on_value_change="radio_group_changed" | Selected value changes | %{"id" => id, "value" => value} |
on_value_change
<.radio_group
name="rg-events"
class="radio-group"
on_value_change="radio_group_changed"
items={[
%{value: "lorem", label: "Lorem ipsum dolor sit amet"},
%{value: "duis", label: "Duis dictum gravida odio ac pharetra?"},
%{value: "donec", label: "Donec condimentum ex mi"}
]}
>
<:label>Choose one</:label>
<:item_control><.heroicon name="hero-check" class="data-checked" /></:item_control>
</.radio_group>def handle_event("radio_group_changed", %{"id" => _id, "value" => value}, socket) do
{:noreply, assign(socket, :choice, value)}
endClient events
| Event | When | event.detail |
|---|---|---|
on_value_change_client="radio-group-changed" | Selected value changes | id, value |
on_value_change_client
<.radio_group
id="radio-group-events-client"
name="rg-events-client"
class="radio-group"
on_value_change_client="radio-group-changed"
items={[
%{value: "lorem", label: "Lorem ipsum dolor sit amet"},
%{value: "duis", label: "Duis dictum gravida odio ac pharetra?"},
%{value: "donec", label: "Donec condimentum ex mi"}
]}
>
<:label>Choose one</:label>
<:item_control><.heroicon name="hero-check" class="data-checked" /></:item_control>
</.radio_group>const el = document.getElementById("radio-group-events-client");
el?.addEventListener("radio-group-changed", (event) => console.log(event.detail));API
Requires a stable id on <.radio_group>. Imperative helpers set the selected value.
| Function | Action | Returns |
|---|---|---|
set_value/2 | Set value (client) | %Phoenix.LiveView.JS{} |
set_value/3 | Set value (server) | socket |
clear_value/1 | Clear selection (client) | %Phoenix.LiveView.JS{} |
clear_value/2 | Clear selection (server) | socket |
focus/1 | Focus the group (client) | %Phoenix.LiveView.JS{} |
focus/2 | Focus the group (server) | socket |
value/1 | Read current value (client) | %Phoenix.LiveView.JS{} |
value/2 | Read current value (client, options) | %Phoenix.LiveView.JS{} |
value/3 | Read current value (server) | socket |
set_value
<.action phx-click={Corex.RadioGroup.set_value("radio-group-api-server", "duis")} class="button button--sm">
Set Duis
</.action>
<.radio_group
id="radio-group-api-server"
name="rg-api-server"
class="radio-group"
value="lorem"
items={[
%{value: "lorem", label: "Lorem ipsum dolor sit amet"},
%{value: "duis", label: "Duis dictum gravida odio ac pharetra?"},
%{value: "donec", label: "Donec condimentum ex mi"}
]}
>
<:label>Pick</:label>
<:item_control><.heroicon name="hero-check" class="data-checked" /></:item_control>
</.radio_group>Patterns
Controlled
For server-owned selection, set controlled, bind value, and handle on_value_change in LiveView.
<.radio_group
id="radio-group-api-controlled"
name="rg-controlled"
class="radio-group"
controlled
value={@api_controlled_value}
on_value_change="radio_group_api_controlled"
items={[
%{value: "lorem", label: "Lorem ipsum dolor sit amet"},
%{value: "duis", label: "Duis dictum gravida odio ac pharetra?"},
%{value: "donec", label: "Donec condimentum ex mi"}
]}
>
<:label>Pick</:label>
<:item_control><.heroicon name="hero-check" class="data-checked" /></:item_control>
</.radio_group>def handle_event("radio_group_api_controlled", %{"value" => v}, socket) do
{:noreply, assign(socket, :api_controlled_value, v)}
endStream
Use Phoenix.LiveView.stream/3 to add or remove items at runtime. Keep @items_list in sync with the stream and pass it as items. Configure dom_id to match each item (radio-group:stream-radio-group:item:#{value}).
<.radio_group
name="stream-rg"
class="radio-group"
items={@items_list}
value={@stream_value}
controlled
on_value_change="patterns_stream_value"
>
<:label>Choose one</:label>
<:item_control><.heroicon name="hero-check" class="data-checked" /></:item_control>
</.radio_group>Form
Use field={@form[:choice]} inside <.form> so the hidden input name and validation align with Phoenix forms. Pass invalid only when you want invalid styling.
For cross-cutting invalid styling and error presentation, see the Forms guide. Pass invalid={Corex.FormField.invalid?(@form[:choice])} when you want alert borders after validation.
<.form for={@form} phx-change="validate">
<.radio_group
field={@form[:choice]}
class="radio-group"
items={[
%{value: "lorem", label: "Lorem ipsum dolor sit amet"},
%{value: "duis", label: "Duis dictum gravida odio ac pharetra?"},
%{value: "donec", label: "Donec condimentum ex mi"}
]}
>
<:label>Choose one</:label>
<:item_control><.heroicon name="hero-check" class="data-checked" /></:item_control>
<:error :let={msg}>
<.heroicon name="hero-exclamation-circle" class="icon" />
{msg}
</:error>
</.radio_group>
</.form>Style
Target parts with data-scope and data-part, or use Corex Design: import tokens and radio-group.css, then set class="radio-group" on <.radio_group>.
[data-scope="radio-group"][data-part="root"] {}
[data-scope="radio-group"][data-part="label"] {}
[data-scope="radio-group"][data-part="item"] {}
[data-scope="radio-group"][data-part="item-text"] {}
[data-scope="radio-group"][data-part="item-control"] {}
[data-scope="radio-group"][data-part="item-hidden-input"] {}@import "../corex/main.css";
@import "../corex/tokens/themes/neo/light.css";
@import "../corex/components/radio-group.css";Stack modifiers on the host (class on <.radio_group>).
Color
| Modifier | Classes |
|---|---|
| Default | radio-group |
| Accent | radio-group radio-group--accent |
| Brand | radio-group radio-group--brand |
| Alert | radio-group radio-group--alert |
| Info | radio-group radio-group--info |
| Success | radio-group radio-group--success |
Size
| Modifier | Classes |
|---|---|
| SM | radio-group radio-group--sm |
| MD | radio-group radio-group--md |
| LG | radio-group radio-group--lg |
| XL | radio-group radio-group--xl |
Summary
API
Clear selection from phx-click. Dispatches corex:radio-group:clear-value.
Clear selection from handle_event (radio_group_clear_value).
Move focus onto the radio group from phx-click. Dispatches corex:radio-group:focus.
Focus the radio group from handle_event (radio_group_focus).
Choose a radio option from phx-click. Dispatches corex:radio-group:set-value with detail.value.
Choose a radio option from handle_event (radio_group_set_value).
Same as value/2 with default respond_to:.
Read the current value from phx-click. Dispatches corex:radio-group:value. Optional respond_to: :server, :client, or :both.
Read the current value from handle_event (radio_group_value). Same replies as value/2.
Components
Attributes
id(:string)value(:string) - Defaults tonil.controlled(:boolean) - Defaults tofalse.name(:string) - Defaults tonil.form(:string) - Defaults tonil.disabled(:boolean) - Defaults tofalse.invalid(:boolean) - Defaults tofalse.required(:boolean) - Defaults tofalse.read_only(:boolean) - Defaults tofalse.dir(:string) - Defaults tonil.Must be one ofnil,"ltr", or"rtl".orientation(:string) - Defaults to"vertical". Must be one of"horizontal", or"vertical".on_value_change(:string) - Defaults tonil.on_value_change_client(:string) - Defaults tonil.items(:list) (required) - List of [value, label] or %{value: ..., label: ..., disabled: ..., invalid: ...}.errors(:list) - Error messages to display (non-field API). Defaults to[].field(Phoenix.HTML.FormField) - A form field, e.g. f[:choice] or @form[:choice].- Global attributes are accepted.
Slots
label- Accepts attributes:class(:string)
item_control- Accepts attributes:class(:string)
item- Accepts attributes:class(:string)
error- Accepts attributes:class(:string)
API
Clear selection from phx-click. Dispatches corex:radio-group:clear-value.
<.action phx-click={Corex.RadioGroup.clear_value("my-rg")}>Clear</.action>
<.radio_group id="my-rg" name="rg-api" class="radio-group" items={[%{value: "lorem", label: "Lorem"}]}>
<:label>Pick</:label>
</.radio_group>
Clear selection from handle_event (radio_group_clear_value).
def handle_event("clear_rg", _, socket) do
{:noreply, Corex.RadioGroup.clear_value(socket, "my-rg")}
end
Move focus onto the radio group from phx-click. Dispatches corex:radio-group:focus.
<.action phx-click={Corex.RadioGroup.focus("my-rg")}>Focus</.action>
<.radio_group id="my-rg" name="rg-api" class="radio-group" items={[%{value: "a", label: "A"}]}>
<:label>Pick</:label>
</.radio_group>
Focus the radio group from handle_event (radio_group_focus).
def handle_event("focus_rg", _, socket) do
{:noreply, Corex.RadioGroup.focus(socket, "my-rg")}
end
Choose a radio option from phx-click. Dispatches corex:radio-group:set-value with detail.value.
<.action phx-click={Corex.RadioGroup.set_value("my-rg", "lorem")}>Pick Lorem</.action>
<.radio_group id="my-rg" name="rg-api" class="radio-group" items={[%{value: "lorem", label: "Lorem"}, %{value: "duis", label: "Duis"}]}>
<:label>One</:label>
</.radio_group>document.getElementById("my-rg")?.dispatchEvent(
new CustomEvent("corex:radio-group:set-value", {
bubbles: false,
detail: { value: "lorem" },
})
);
Choose a radio option from handle_event (radio_group_set_value).
def handle_event("pick", %{"v" => v}, socket) do
{:noreply, Corex.RadioGroup.set_value(socket, "my-rg", v)}
end
Same as value/2 with default respond_to:.
Read the current value from phx-click. Dispatches corex:radio-group:value. Optional respond_to: :server, :client, or :both.
| Reply | Payload | |
|---|---|---|
| Server | radio_group_value_response | %{"id" => id, "value" => selection} |
| Client | radio-group-value on the root | same fields in detail |
<.action phx-click={Corex.RadioGroup.value("my-rg")}>Read</.action>
<.radio_group id="my-rg" name="rg-api" class="radio-group" items={[%{value: "a", label: "A"}]}>
<:label>Pick</:label>
</.radio_group>def handle_event("radio_group_value_response", %{"id" => _, "value" => v}, socket) do
{:noreply, assign(socket, :rg, v)}
end
Read the current value from handle_event (radio_group_value). Same replies as value/2.
| Reply | Payload |
|---|---|
radio_group_value_response | %{"id" => id, "value" => selection} |
def handle_event("read_rg", _, socket) do
{:noreply, Corex.RadioGroup.value(socket, "my-rg", respond_to: :server)}
end