Packages
corex
0.1.0-alpha.23
0.1.2
0.1.1
0.1.0
0.1.0-rc.1
0.1.0-rc.0
0.1.0-beta.5
0.1.0-beta.4
0.1.0-beta.3
0.1.0-beta.2
0.1.0-beta.1
0.1.0-alpha.33
0.1.0-alpha.32
0.1.0-alpha.31
0.1.0-alpha.30
0.1.0-alpha.29
0.1.0-alpha.28
0.1.0-alpha.27
0.1.0-alpha.26
0.1.0-alpha.25
0.1.0-alpha.24
0.1.0-alpha.23
0.1.0-alpha.22
0.1.0-alpha.21
0.1.0-alpha.20
0.1.0-alpha.19
0.1.0-alpha.18
0.1.0-alpha.17
0.1.0-alpha.16
0.1.0-alpha.15
0.1.0-alpha.14
0.1.0-alpha.13
0.1.0-alpha.12
0.1.0-alpha.11
0.1.0-alpha.10
0.1.0-alpha.9
0.1.0-alpha.8
0.1.0-alpha.7
0.1.0-alpha.6
0.1.0-alpha.5
0.1.0-alpha.4
0.1.0-alpha.3
0.1.0-alpha.2
0.1.0-alpha.1
Accessible and unstyled UI components library written in Elixir and TypeScript that integrates Zag.js state machines into the Phoenix Framework.
Current section
Files
Jump to
Current section
Files
lib/components/number_input.ex
defmodule Corex.NumberInput do
@moduledoc ~S'''
Phoenix implementation of [Zag.js Number Input](https://zagjs.com/components/react/number-input).
## Examples
<!-- tabs-open -->
### Basic
```heex
<.number_input id="num" class="number-input">
<:label>Quantity</:label>
</.number_input>
```
### With triggers
```heex
<.number_input id="num" class="number-input">
<:label>Quantity</:label>
<:decrement_trigger><.icon name="hero-chevron-down" class="icon" /></:decrement_trigger>
<:increment_trigger><.icon name="hero-chevron-up" class="icon" /></:increment_trigger>
</.number_input>
```
### With scrubber
```heex
<.number_input id="num" scrubber class="number-input">
<:label>Quantity</:label>
<:scrubber_trigger><.icon name="hero-arrows-up-down" class="icon rotate-90" /></:scrubber_trigger>
</.number_input>
```
<!-- tabs-close -->
Optional slots `:decrement_trigger`, `:increment_trigger`, and `:scrubber_trigger` render the button content (e.g. icons). When omitted, no content is shown.
## Styling
Use data attributes: `[data-scope="number-input"][data-part="root"]`, `control`, `input`, `trigger-group`, `decrement-trigger`, `increment-trigger`, `scrubber`.
'''
@doc type: :component
use Phoenix.Component
alias Corex.NumberInput.Anatomy.{
Props,
Root,
Label,
Control,
Input,
TriggerGroup,
DecrementTrigger,
IncrementTrigger,
Scrubber
}
alias Corex.NumberInput.Connect
attr(:id, :string, required: false)
attr(:value, :string, default: nil)
attr(:default_value, :string, default: nil)
attr(:controlled, :boolean, default: false)
attr(:min, :float, default: nil)
attr(:max, :float, default: nil)
attr(:step, :float, default: 1.0)
attr(:disabled, :boolean, default: false)
attr(:read_only, :boolean, default: false)
attr(:invalid, :boolean, default: false)
attr(:required, :boolean, default: false)
attr(:allow_mouse_wheel, :boolean, default: false)
attr(:name, :string, default: nil)
attr(:form, :string, default: nil)
attr(:on_value_change, :string, default: nil)
attr(:on_value_change_client, :string, default: nil)
attr(:scrubber, :boolean,
default: false,
doc: "When true, show scrubber instead of increment/decrement buttons"
)
attr(:rest, :global)
slot(:label, required: false)
slot(:decrement_trigger, required: false)
slot(:increment_trigger, required: false)
slot(:scrubber_trigger, required: false)
def number_input(assigns) do
assigns =
assigns
|> assign_new(:id, fn -> "number-input-#{System.unique_integer([:positive])}" end)
~H"""
<div
id={@id}
phx-hook="NumberInput"
{@rest}
{Connect.props(%Props{
id: @id,
value: @value,
default_value: @default_value,
controlled: @controlled,
min: @min,
max: @max,
step: @step,
disabled: @disabled,
read_only: @read_only,
invalid: @invalid,
required: @required,
allow_mouse_wheel: @allow_mouse_wheel,
name: @name,
form: @form,
on_value_change: @on_value_change,
on_value_change_client: @on_value_change_client
})}
>
<div phx-update="ignore" {Connect.root(%Root{id: @id})}>
<label :if={@label != []} {Connect.label(%Label{id: @id})}>
{render_slot(@label)}
</label>
<div {Connect.control(%Control{id: @id})}>
<input type="text" inputmode="decimal" {Connect.input(%Input{id: @id, disabled: @disabled})} />
<div {Connect.trigger_group(%TriggerGroup{})}>
<button :if={!@scrubber} type="button" {Connect.increment_trigger(%IncrementTrigger{id: @id})}>
{render_slot(@increment_trigger)}
</button>
<button :if={!@scrubber} type="button" {Connect.decrement_trigger(%DecrementTrigger{id: @id})}>
{render_slot(@decrement_trigger)}
</button>
<button :if={@scrubber} type="button" {Connect.scrubber(%Scrubber{id: @id})}>
{render_slot(@scrubber_trigger)}
</button>
</div>
</div>
</div>
</div>
"""
end
end