Corex. NumberInput
(Corex v0.1.2)
View Source
Phoenix implementation of Zag.js Number Input.
Anatomy
Minimal
<.number_input class="number-input">
<:label>Quantity</:label>
<:decrement_trigger><.heroicon name="hero-chevron-down" class="icon" /></:decrement_trigger>
<:increment_trigger><.heroicon name="hero-chevron-up" class="icon" /></:increment_trigger>
</.number_input>Min, max, step
<.number_input
class="number-input"
min={0.0}
max={100.0}
step={5.0}
value="10"
>
<:label>Amount</:label>
<:decrement_trigger><.heroicon name="hero-chevron-down" class="icon" /></:decrement_trigger>
<:increment_trigger><.heroicon name="hero-chevron-up" class="icon" /></:increment_trigger>
</.number_input>min, max, and step are floats. Use step for granularity: step={1} for whole-number steps (display omits .0), step={0.01} for two decimal places. The visible value is formatted on the server to match Zag after hydration.
Slots :decrement_trigger and :increment_trigger are required.
API
Requires a stable id on <.number_input>. For forms, use field; use the API for imperative updates and reading machine state.
| Function | Action | Returns |
|---|---|---|
set_value/2 | Set value (client) | %Phoenix.LiveView.JS{} |
set_value/3 | Set value (server) | socket |
clear_value/1 | Clear value (client) | %Phoenix.LiveView.JS{} |
clear_value/2 | Clear value (server) | socket |
increment/1 | Increment by step (client) | %Phoenix.LiveView.JS{} |
increment/2 | Increment by step (server) | socket |
decrement/1 | Decrement by step (client) | %Phoenix.LiveView.JS{} |
decrement/2 | Decrement by step (server) | socket |
set_to_min/1 | Set to min (client) | %Phoenix.LiveView.JS{} |
set_to_min/2 | Set to min (server) | socket |
set_to_max/1 | Set to max (client) | %Phoenix.LiveView.JS{} |
set_to_max/2 | Set to max (server) | socket |
focus/1 | Focus input (client) | %Phoenix.LiveView.JS{} |
focus/2 | Focus input (server) | socket |
state/1 | Read machine state (client) | %Phoenix.LiveView.JS{} |
state/2 | Read machine state (client, opts) | %Phoenix.LiveView.JS{} |
state/3 | Read machine state (server) | socket |
Machine state (state/2, state/3)
Replies with number_input_state_response (server) or number-input-state on the host (client). Payload includes Zag machine fields:
| Field | Type | Meaning |
|---|---|---|
focused | boolean | Input is focused |
invalid | boolean | Input is invalid |
empty | boolean | Value is empty |
value | string | Formatted value |
valueAsNumber | number | Numeric value |
Events
Pick an event name and pass it to on_* on <.number_input>.
Server events
| Event | When | Payload |
|---|---|---|
on_value_change="number_input_changed" | Value changes | %{"id" => id, "value" => string, "valueAsNumber" => number} |
on_value_change
<.number_input
class="number-input"
on_value_change="number_input_changed"
>
<:label>Quantity</:label>
<:decrement_trigger><.heroicon name="hero-chevron-down" class="icon" /></:decrement_trigger>
<:increment_trigger><.heroicon name="hero-chevron-up" class="icon" /></:increment_trigger>
</.number_input>def handle_event("number_input_changed", %{"id" => _id, "value" => value}, socket) do
{:noreply, assign(socket, :quantity, value)}
endClient events
| Event | When | event.detail |
|---|---|---|
on_value_change_client="number-input-changed" | Value changes | id, value, valueAsNumber |
Patterns
Pass value for the initial number on mount. The machine owns updates after that unless you use set_value/2 or form field.
Form
Use field={f[:value]} inside <.form>. With a form field, increment and decrement stay local; the hidden input updates for submit.
For cross-cutting invalid styling and error presentation, see the Forms guide. Pass invalid={Corex.FormField.invalid?(@form[:value])} when you want alert borders after validation.
<.form for={@form} phx-change="validate">
<.number_input field={@form[:value]} class="number-input">
<:label>Quantity</:label>
<:decrement_trigger><.heroicon name="hero-chevron-down" class="icon" /></:decrement_trigger>
<:increment_trigger><.heroicon name="hero-chevron-up" class="icon" /></:increment_trigger>
<:error :let={msg}>
<.heroicon name="hero-exclamation-circle" class="icon" />
{msg}
</:error>
</.number_input>
</.form>Style
Target parts with data-scope and data-part, or use Corex Design: import tokens and number-input.css, then set class="number-input" on <.number_input>.
[data-scope="number-input"][data-part="root"] {}
[data-scope="number-input"][data-part="control"] {}
[data-scope="number-input"][data-part="input"] {}
[data-scope="number-input"][data-part="trigger-group"] {}
[data-scope="number-input"][data-part="decrement-trigger"] {}
[data-scope="number-input"][data-part="increment-trigger"] {}@import "../corex/main.css";
@import "../corex/tokens/themes/neo/light.css";
@import "../corex/components/number-input.css";Stack modifiers on the host (class on <.number_input>).
Color
| Modifier | Classes |
|---|---|
| Default | number-input |
| Accent | number-input number-input--accent |
| Brand | number-input number-input--brand |
| Alert | number-input number-input--alert |
| Info | number-input number-input--info |
| Success | number-input number-input--success |
Size
| Modifier | Classes |
|---|---|
| SM | number-input number-input--sm |
| MD | number-input number-input--md |
| LG | number-input number-input--lg |
| XL | number-input number-input--xl |
Summary
API
Clear the value from phx-click. Dispatches corex:number-input:clear-value.
Clear the value from handle_event (number_input_clear_value).
Decrement from phx-click. Dispatches corex:number-input:decrement.
Decrement from handle_event (number_input_decrement).
Focus the value field from phx-click. Dispatches corex:number-input:focus.
Focus the value field from handle_event (number_input_focus).
Increment from phx-click. Dispatches corex:number-input:increment.
Increment from handle_event (number_input_increment).
Snap to maximum from phx-click. Dispatches corex:number-input:set-to-max.
Snap to maximum from handle_event (number_input_set_to_max).
Snap to minimum from phx-click. Dispatches corex:number-input:set-to-min.
Snap to minimum from handle_event (number_input_set_to_min).
Replace the formatted value from phx-click. Dispatches corex:number-input:set-value with a numeric value.
Replace the formatted value from handle_event (number_input_set_value).
Same as state/2 with default respond_to:.
Read machine state from phx-click. Dispatches corex:number-input:state. Optional respond_to: :server, :client, or :both.
Components
Attributes
id(:string)value(:string) - Defaults tonil.min(:float) - Defaults tonil.max(:float) - Defaults tonil.step(:float) - Defaults to1.0.disabled(:boolean) - Defaults tofalse.read_only(:boolean) - Defaults tofalse.invalid(:boolean) - Defaults tofalse.required(:boolean) - Defaults tofalse.allow_mouse_wheel(:boolean) - Defaults tofalse.name(:string) - Defaults tonil.form(:string) - Defaults tonil.on_value_change(:string) - Defaults tonil.on_value_change_client(: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".translation(Corex.NumberInput.Translation) - Override translatable strings. Defaults tonil.errors(:list) - List of error messages to display. Defaults to[].field(Phoenix.HTML.FormField) - A form field struct, e.g. f[:age] or @form[:age].- Global attributes are accepted.
Slots
label- Accepts attributes:class(:string)
decrement_trigger(required) - Accepts attributes:class(:string)
increment_trigger(required) - Accepts attributes:class(:string)
error- Accepts attributes:class(:string)
API
Clear the value from phx-click. Dispatches corex:number-input:clear-value.
<.action phx-click={Corex.NumberInput.clear_value("my-num")}>Clear</.action>
<.number_input id="my-num" class="number-input">
<:increment_trigger><span>+</span></:increment_trigger>
<:decrement_trigger><span>-</span></:decrement_trigger>
</.number_input>
Clear the value from handle_event (number_input_clear_value).
def handle_event("clear", _, socket) do
{:noreply, Corex.NumberInput.clear_value(socket, "my-num")}
end
Decrement from phx-click. Dispatches corex:number-input:decrement.
<.action phx-click={Corex.NumberInput.decrement("my-num")}>Decrement</.action>
<.number_input id="my-num" class="number-input">
<:increment_trigger><span>+</span></:increment_trigger>
<:decrement_trigger><span>-</span></:decrement_trigger>
</.number_input>
Decrement from handle_event (number_input_decrement).
def handle_event("dec", _, socket) do
{:noreply, Corex.NumberInput.decrement(socket, "my-num")}
end
Focus the value field from phx-click. Dispatches corex:number-input:focus.
<.action phx-click={Corex.NumberInput.focus("my-num")}>Focus</.action>
<.number_input id="my-num" class="number-input">
<:increment_trigger><span>+</span></:increment_trigger>
<:decrement_trigger><span>-</span></:decrement_trigger>
</.number_input>
Focus the value field from handle_event (number_input_focus).
def handle_event("focus", _, socket) do
{:noreply, Corex.NumberInput.focus(socket, "my-num")}
end
Increment from phx-click. Dispatches corex:number-input:increment.
<.action phx-click={Corex.NumberInput.increment("my-num")}>Increment</.action>
<.number_input id="my-num" class="number-input">
<:increment_trigger><span>+</span></:increment_trigger>
<:decrement_trigger><span>-</span></:decrement_trigger>
</.number_input>
Increment from handle_event (number_input_increment).
def handle_event("inc", _, socket) do
{:noreply, Corex.NumberInput.increment(socket, "my-num")}
end
Snap to maximum from phx-click. Dispatches corex:number-input:set-to-max.
<.action phx-click={Corex.NumberInput.set_to_max("my-num")}>Max</.action>
<.number_input id="my-num" max={100} class="number-input">
<:increment_trigger><span>+</span></:increment_trigger>
<:decrement_trigger><span>-</span></:decrement_trigger>
</.number_input>
Snap to maximum from handle_event (number_input_set_to_max).
def handle_event("max", _, socket) do
{:noreply, Corex.NumberInput.set_to_max(socket, "my-num")}
end
Snap to minimum from phx-click. Dispatches corex:number-input:set-to-min.
<.action phx-click={Corex.NumberInput.set_to_min("my-num")}>Min</.action>
<.number_input id="my-num" min={0} class="number-input">
<:increment_trigger><span>+</span></:increment_trigger>
<:decrement_trigger><span>-</span></:decrement_trigger>
</.number_input>
Snap to minimum from handle_event (number_input_set_to_min).
def handle_event("min", _, socket) do
{:noreply, Corex.NumberInput.set_to_min(socket, "my-num")}
end
Replace the formatted value from phx-click. Dispatches corex:number-input:set-value with a numeric value.
<.action phx-click={Corex.NumberInput.set_value("my-num", 42)}>42</.action>
<.number_input id="my-num" value={40} step={1} class="number-input">
<:increment_trigger><span>+</span></:increment_trigger>
<:decrement_trigger><span>-</span></:decrement_trigger>
</.number_input>document.getElementById("my-num")?.dispatchEvent(
new CustomEvent("corex:number-input:set-value", {
bubbles: false,
detail: { value: 42 },
})
);
Replace the formatted value from handle_event (number_input_set_value).
def handle_event("set_answer", _, socket) do
{:noreply, Corex.NumberInput.set_value(socket, "my-num", 7)}
end
Same as state/2 with default respond_to:.
Read machine state from phx-click. Dispatches corex:number-input:state. Optional respond_to: :server, :client, or :both.
| Reply | Payload | |
|---|---|---|
| Server | number_input_state_response | %{"id" => id, "focused" => bool, "invalid" => bool, "empty" => bool, "value" => str, "valueAsNumber" => num} |
| Client | number-input-state on the input root | same fields in detail |
<.action phx-click={Corex.NumberInput.state("my-num")}>Snapshot</.action>
<.number_input id="my-num" class="number-input">
<:increment_trigger><span>+</span></:increment_trigger>
<:decrement_trigger><span>-</span></:decrement_trigger>
</.number_input>def handle_event("number_input_state_response", %{"id" => _, "valueAsNumber" => n}, socket) do
{:noreply, assign(socket, :n, n)}
end
Read machine state from handle_event (number_input_state). Same replies as state/2; server-only unless you also use state/2 for a DOM reply.
| Reply | Payload |
|---|---|
number_input_state_response | %{"id" => id} plus focused/invalid/value fields |
def handle_event("snapshot", _, socket) do
{:noreply, Corex.NumberInput.state(socket, "my-num", respond_to: :server)}
end