Corex. PinInput
(Corex v0.1.0)
View Source
Phoenix implementation of Zag.js Pin Input.
Anatomy
Basic
<.pin_input count={4} class="pin-input">
<:label>Code</:label>
</.pin_input>API
Set a stable id when using imperative API helpers or on_* events. With field={@form[:code]}, the field id is used automatically.
| Function | Action | Returns |
|---|---|---|
set_value/2 | Set cell values (client) | %Phoenix.LiveView.JS{} |
set_value/3 | Set cell values (server) | socket |
clear/1 | Clear all cells (client) | %Phoenix.LiveView.JS{} |
clear/2 | Clear all cells (server) | socket |
value/1 | Read values (client) | %Phoenix.LiveView.JS{} |
value/2 | Read values (client, opts) | %Phoenix.LiveView.JS{} |
value/3 | Read values (server) | socket |
Events
Pick an event name and pass it to on_* on <.pin_input>.
Server events
| Event | When | Payload |
|---|---|---|
on_value_change="pin_changed" | Any cell changes | %{"id" => id, "value" => list} |
on_value_complete="pin_complete" | All cells filled | %{"id" => id, "value" => list} |
on_value_change
<.pin_input count={4} class="pin-input" on_value_change="pin_changed">
<:label>Code</:label>
</.pin_input>def handle_event("pin_changed", %{"id" => _id, "value" => value}, socket) do
{:noreply, assign(socket, :pin, value)}
endClient events
| Event | When | event.detail |
|---|---|---|
on_value_change_client="pin-changed" | Any cell changes | id, value |
on_value_complete_client="pin-complete" | All cells filled | id, value |
Form
Use field={f[:code]} inside <.form> so the hidden input name and validation align with Phoenix forms.
For cross-cutting invalid styling and error presentation, see the Forms guide. Pass invalid={Corex.FormField.invalid?(@form[:code])} when you want alert borders after validation.
<.form for={@form} phx-change="validate">
<.pin_input field={@form[:code]} count={4} class="pin-input">
<:label>Verification code</:label>
</.pin_input>
</.form>Style
Use data attributes to target elements:
[data-scope="pin-input"][data-part="root"] {}
[data-scope="pin-input"][data-part="label"] {}
[data-scope="pin-input"][data-part="control"] {}
[data-scope="pin-input"][data-part="input"] {}@import "../corex/main.css";
@import "../corex/tokens/themes/neo/light.css";
@import "../corex/components/pin-input.css";Stack modifiers on the host (class on <.pin_input>).
Color
| Modifier | Classes |
|---|---|
| Default | pin-input |
| Accent | pin-input pin-input--accent |
| Brand | pin-input pin-input--brand |
| Alert | pin-input pin-input--alert |
| Info | pin-input pin-input--info |
| Success | pin-input pin-input--success |
Size
| Modifier | Classes |
|---|---|
| SM | pin-input pin-input--sm |
| MD | pin-input pin-input--md |
| LG | pin-input pin-input--lg |
| XL | pin-input pin-input--xl |
Radius
| Modifier | Classes |
|---|---|
| None | pin-input pin-input--rounded-none |
| SM | pin-input pin-input--rounded-sm |
| MD | pin-input pin-input--rounded-md |
| LG | pin-input pin-input--rounded-lg |
| XL | pin-input pin-input--rounded-xl |
| Full | pin-input pin-input--rounded-full |
The value assign is the initial cell contents. Standalone mode uses data-default-value; field={@form[:code]} uses Zag controlled data-value and resyncs on patch via updateProps({ value }). Use the controlled assign only for non-form LiveView with on_value_change.
Summary
API
Clear all cells from phx-click. Dispatches corex:pin-input:clear.
Clear all cells from handle_event (pin_input_clear).
Replace cell values from phx-click. Dispatches corex:pin-input:set-value; value accepts a nonempty string list, a comma string, graphemes string, etc. (normalized like the form helper).
Replace cell values from handle_event (pin_input_set_value).
Same as value/2 with default respond_to:.
Read the current value from phx-click. Dispatches corex:pin-input:value. Optional respond_to: :server, :client, or :both.
Read the current value from handle_event (pin_input_value). Same replies as value/2.
Components
Attributes
id(:string)value(:list) - Initial or controlled value (list of single-character strings). Padded tocountfor the hook. Defaults to[].controlled(:boolean) - Opt-in LiveView controlled mode (data-value). Requires re-assigningvalueonon_value_change. Not used withfield={...}. Defaults tofalse.count(:integer) - Number of input boxes. Defaults to4.disabled(:boolean) - Defaults tofalse.invalid(:boolean) - Defaults tofalse.required(:boolean) - Defaults tofalse.read_only(:boolean) - Defaults tofalse.mask(:boolean) - Defaults tofalse.otp(:boolean) - Defaults tofalse.blur_on_complete(:boolean) - Defaults tofalse.select_on_focus(:boolean) - Defaults tofalse.name(:string) - Defaults tonil.form(:string) - Defaults tonil.dir(:string) - Defaults tonil.Must be one ofnil,"ltr", or"rtl".orientation(:string) - Defaults to"vertical". Must be one of"horizontal", or"vertical".type(:string) - Defaults to"numeric". Must be one of"alphanumeric","numeric", or"alphabetic".placeholder(:string) - Defaults to"○".on_value_change(:string) - Defaults tonil.on_value_change_client(:string) - Defaults tonil.on_value_complete(:string) - Defaults tonil.on_value_complete_client(:string) - Defaults tonil.translation(Corex.PinInput.Translation) - Override translatable strings. Defaults tonil.errors(:list) - Error messages to display (non-field API). Defaults to[].field(Phoenix.HTML.FormField) - A form field, e.g. f[:code] or @form[:code].- Global attributes are accepted.
Slots
label- Accepts attributes:class(:string)
error- Accepts attributes:class(:string)
API
Clear all cells from phx-click. Dispatches corex:pin-input:clear.
<.action phx-click={Corex.PinInput.clear("my-pin")}>Clear</.action>
<.pin_input id="my-pin" count={4} class="pin-input" />
Clear all cells from handle_event (pin_input_clear).
def handle_event("clear_pin", _, socket) do
{:noreply, Corex.PinInput.clear(socket, "my-pin")}
end
Replace cell values from phx-click. Dispatches corex:pin-input:set-value; value accepts a nonempty string list, a comma string, graphemes string, etc. (normalized like the form helper).
<.action phx-click={Corex.PinInput.set_value("my-pin", ["1", "2", "", ""])}>Prefill</.action>
<.pin_input id="my-pin" count={4} class="pin-input" />document.getElementById("my-pin")?.dispatchEvent(
new CustomEvent("corex:pin-input:set-value", {
bubbles: false,
detail: { value: ["1", "2", "", ""] },
})
);
Replace cell values from handle_event (pin_input_set_value).
def handle_event("fill_pin", _, socket) do
{:noreply, Corex.PinInput.set_value(socket, "my-pin", ["0", "0", "0", "0"])}
end
Same as value/2 with default respond_to:.
Read the current value from phx-click. Dispatches corex:pin-input:value. Optional respond_to: :server, :client, or :both.
| Reply | Payload | |
|---|---|---|
| Server | pin_input_value_response | %{"id" => id, "value" => cells, "valueAsString" => str} |
| Client | pin-input-value on the host | same fields in detail |
<.action phx-click={Corex.PinInput.value("my-pin")}>Read</.action>
<.pin_input id="my-pin" count={4} class="pin-input" />def handle_event("pin_input_value_response", %{"id" => _, "valueAsString" => s}, socket) do
{:noreply, assign(socket, :otp, s)}
end
Read the current value from handle_event (pin_input_value). Same replies as value/2.
| Reply | Payload |
|---|---|
pin_input_value_response | %{"id" => id, "value" => cells, "valueAsString" => str} |
def handle_event("read_pin", _, socket) do
{:noreply, Corex.PinInput.value(socket, "my-pin", respond_to: :server)}
end