Corex.NumberInput (Corex v0.1.0-beta.5)

View Source

Phoenix implementation of Zag.js Number Input.

Examples

Basic

<.number_input id="num" 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>

Slots :decrement_trigger and :increment_trigger are required and render the button content (e.g. icons).

Phoenix Form Integration

Use field={f[:key]} or field={@form[:key]} with a form built from an Ecto changeset. Set the form id in to_form/2 and use id={@form.id} on <.form>. Pass invalid={...} when you want Zag's invalid styling; the component does not infer it from field errors.

Controller

Build the form from a changeset and pass it to the template:

def form_page(conn, _params) do
  form =
    %MyApp.Form.Quantity{}
    |> MyApp.Form.Quantity.changeset(%{})
    |> Phoenix.Component.to_form(as: :quantity, id: "quantity-form")
  render(conn, :form_page, form: form)
end
<.form :let={f} for={@form} id={@form.id} action={@action} method="post">
  <.number_input field={f[: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>
  <button type="submit">Submit</button>
</.form>

Live View with Ecto changeset

With field={@form[:key]}, the input stays uncontrolled in the Zag sense so increment and decrement work locally; the hidden field still updates for phx-change and submit. The controlled attribute is ignored when field is set.

For a standalone server-owned value (no field), use controlled, value, and on_value_change so each change is applied on the server (see Number input · Patterns in the demo app).

Use a changeset and standard form events; the field value is submitted with the form.

def mount(_params, _session, socket) do
  form =
    %MyApp.Form.Quantity{}
    |> MyApp.Form.Quantity.changeset(%{})
    |> Phoenix.Component.to_form(as: :quantity, id: "quantity-form")
  {:ok, assign(socket, :form, form)}
end

def handle_event("validate", %{"quantity" => params}, socket) do
  changeset =
    %MyApp.Form.Quantity{}
    |> MyApp.Form.Quantity.changeset(params)
    |> Map.put(:action, :validate)
  {:noreply, assign(socket, :form, Phoenix.Component.to_form(changeset, as: :quantity, id: "quantity-form"))}
end
<.form for={@form} id={@form.id} phx-change="validate" phx-submit="save">
  <.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>
  <button type="submit">Submit</button>
</.form>

Styling

Use data attributes to target elements:

[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"] {}

If you wish to use the default Corex styling, you can use the class number-input on the component. This requires to install Mix.Tasks.Corex.Design first and import the component css file.

@import "../corex/main.css";
@import "../corex/tokens/themes/neo/light.css";
@import "../corex/components/number-input.css";

You can then use modifiers

<.number_input class="number-input number-input--accent number-input--lg">
  <:decrement_trigger><.heroicon name="hero-chevron-down" class="icon" /></:decrement_trigger>
  <:increment_trigger><.heroicon name="hero-chevron-up" class="icon" /></:increment_trigger>
</.number_input>

Summary

Components

number_input(assigns)

Attributes

  • id (:string)
  • value (:string) - Defaults to nil.
  • default_value (:string) - Defaults to nil.
  • min (:float) - Defaults to nil.
  • max (:float) - Defaults to nil.
  • step (:float) - Defaults to 1.0.
  • disabled (:boolean) - Defaults to false.
  • read_only (:boolean) - Defaults to false.
  • invalid (:boolean) - Defaults to false.
  • required (:boolean) - Defaults to false.
  • allow_mouse_wheel (:boolean) - Defaults to false.
  • controlled (:boolean) - Server-driven value; use with value and on_value_change. Ignored when field is set (forms stay uncontrolled for working steppers). Defaults to false.
  • name (:string) - Defaults to nil.
  • form (:string) - Defaults to nil.
  • on_value_change (:string) - Defaults to nil.
  • on_value_change_client (:string) - Defaults to nil.
  • dir (:string) - Defaults to nil.Must be one of nil, "ltr", or "rtl".
  • orientation (:string) - Defaults to "vertical". Must be one of "horizontal", or "vertical".
  • translation (Corex.NumberInput.Translation) - Override translatable strings. Defaults to nil.
  • 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)