icon

Morainev0.5.0

InputNumber
formsinput-number

InputNumber

Numeric input with increment and decrement controls, step, and min/max constraints.

Import#

import { InputNumber } from 'moraine'

Slot Structure#

Numeric input with increment/decrement controls placed inline (horizontal) or stacked (vertical).

Horizontal#

root
├── decrement (optional)
├── input
└── increment (optional)

Vertical#

root
├── input
└── controls
├── increment (optional)
└── decrement (optional)

Examples#

Variants#

Visual style variants for the input shell.

function Variants() {
const VARIANTS = ['outline', 'subtle', 'ghost', 'none'] as const
return (
<div class="flex flex-wrap gap-6 items-start">
<For each={VARIANTS}>
{(variant) => (
<div class="space-y-1">
<label class="text-xs text-muted-foreground block">{variant}</label>
<InputNumber variant={variant} defaultValue={3} />
</div>
)}
</For>
</div>
)
}

Sizes#

Size scale from xs to xl for input height and control rail sizing.

function Sizes() {
const SIZES: InputNumberSize[] = ['xs', 'sm', 'md', 'lg', 'xl']
return (
<div class="max-w-md space-y-3">
<For each={SIZES}>
{(size) => (
<div class="space-y-1">
<p class="text-xs text-muted-foreground font-mono">{size}</p>
<InputNumber size={size} defaultValue={3} />
</div>
)}
</For>
</div>
)
}

Orientations#

Horizontal (default) and vertical button layouts.

function Orientations() {
return (
<div class="flex flex-wrap gap-6 items-start">
<div class="space-y-1">
<label class="text-xs text-muted-foreground block">Horizontal</label>
<InputNumber defaultValue={5} minValue={0} maxValue={20} />
</div>
<div class="space-y-1">
<label class="text-xs text-muted-foreground block">Vertical</label>
<InputNumber orientation="vertical" defaultValue={5} minValue={0} maxValue={20} />
</div>
</div>
)
}

Controlled#

Value bound to a signal with live readout.

function Controlled() {
const [controlledValue, setControlledValue] = createSignal(10)
return (
<div class="max-w-xs space-y-2">
<InputNumber
value={controlledValue()}
onRawValueChange={(v) => {
if (Number.isFinite(v)) {
setControlledValue(v)
}
}}
minValue={0}
maxValue={99}
variant="subtle"
/>
<p class="text-xs text-muted-foreground">Current value: {controlledValue()}</p>
</div>
)
}

Disabled#

Non-interactive disabled state.

function Disabled() {
return <InputNumber defaultValue={7} disabled />
}

Long Press#

Press and hold increment or decrement to continuously step the value. Set holdRepeat={false} to disable the repeat behavior.

function LongPress() {
const [repeatValue, setRepeatValue] = createSignal(12)
const [singleStepValue, setSingleStepValue] = createSignal(12)
return (
<div class="max-w-xs space-y-4">
<div class="space-y-2">
<InputNumber
value={repeatValue()}
onRawValueChange={(value) => {
if (Number.isFinite(value)) {
setRepeatValue(value)
}
}}
minValue={0}
maxValue={99}
step={1}
variant="subtle"
/>
<p class="text-xs text-muted-foreground">
Hold <span class="font-medium">+</span> or <span class="font-medium">−</span> to repeat.
Current value: {repeatValue()}
</p>
</div>
<div class="space-y-2">
<InputNumber
value={singleStepValue()}
holdRepeat={false}
onRawValueChange={(value) => {
if (Number.isFinite(value)) {
setSingleStepValue(value)
}
}}
minValue={0}
maxValue={99}
step={1}
variant="subtle"
/>
<p class="text-xs text-muted-foreground">
Set <code>holdRepeat=false</code> to keep press-and-hold at a single step. Current value:{' '}
{singleStepValue()}
</p>
</div>
</div>
)
}

Min / Max / Step#

Constrained ranges and custom step increments.

function MinMaxStep() {
return (
<div class="flex flex-wrap gap-6 items-start">
<div class="space-y-1">
<label class="text-xs text-muted-foreground block">Step 5 (0–100)</label>
<InputNumber defaultValue={25} minValue={0} maxValue={100} step={5} />
</div>
<div class="space-y-1">
<label class="text-xs text-muted-foreground block">Step 0.1 (0–1)</label>
<InputNumber defaultValue={0.5} minValue={0} maxValue={1} step={0.1} />
</div>
</div>
)
}

API Reference#

Attributes#

Slotroot4 attributes
Number input wrapper that owns the input and step controls.

Data Attributes

3
Data AttributeDescription
data-orientation
Stores the rendered orientation.
data-size
Stores the resolved size variant.
data-variant
Stores the resolved visual variant.

ARIA Attributes

1
ARIA AttributeDescription
role
Defines the semantic role exposed to assistive technology.

Props#

PropTypeDefaultDescription
autofocusboolean | undefinedfalse
Whether to automatically focus the input on mount.
autofocusDelaynumber | undefined0
Delay in milliseconds before focusing the input.
classClassValue
Class applied to the component root or trigger element.
classesInputNumberT.Classes | undefined
decrementboolean | undefinedtrue
Whether to show the decrement button.
decrementDisabledboolean | undefined
Whether the decrement button is disabled.
decrementIconIconT.Nameorientation === 'vertical' ? 'icon-chevron-down' : 'icon-minus
Icon for the decrement button.
defaultValuestring | number | undefined
Default displayed value for uncontrolled usage.
disabledboolean | undefinedfalse
Whether the input is disabled.
holdRepeatboolean | undefinedtrue
Whether press-and-hold should trigger repeated value changes.
idstring | undefined
The ID of the input element.
incrementboolean | undefinedtrue
Whether to show the increment button.
incrementDisabledboolean | undefined
Whether the increment button is disabled.
incrementIconIconT.Nameorientation === 'vertical' ? 'icon-chevron-up' : 'icon-plus
Icon for the increment button.
largeStepnumber | undefinedstep * 10
The step size used for PageUp/PageDown.
localestring | undefined
Locale for number formatting and parsing. Uses browser default if not specified.
maxValuenumber | undefined
Maximum allowed numeric value.
minValuenumber | undefined
Minimum allowed numeric value.
namestring | undefined
The name of the input element, used for form submission.
onBlurJSX.FocusEventHandlerUnion<HTMLInputElement, FocusEvent> | undefined
Callback when the input loses focus.
onChange((value: string) => void) | undefined
Callback when the formatted string value changes.
onDecrementClickJSX.EventHandlerUnion<HTMLButtonElement, MouseEvent> | undefined
Callback when the decrement button is clicked.
onFocusJSX.FocusEventHandlerUnion<HTMLInputElement, FocusEvent> | undefined
Callback when the input gains focus.
onIncrementClickJSX.EventHandlerUnion<HTMLButtonElement, MouseEvent> | undefined
Callback when the increment button is clicked.
onRawValueChange((value: number) => void) | undefined
Callback when the numeric value changes.
orientationInputNumberOrientation | undefinedhorizontal
The orientation of the control buttons.
placeholderstring | undefined
Placeholder text for the input.
rawValuenumber | undefined
Controlled numeric value. Takes precedence over `value`.
readOnlyboolean | undefinedfalse
Whether the input is read-only.
refJSX.HTMLElementTags["div"] extends { ref?: infer Ref; } ? Ref : never | undefined
repeatDelayMsnumber | undefined500
Delay in milliseconds before repeated value changes start.
repeatIntervalMsnumber | undefined80
Interval in milliseconds between repeated value changes.
repeatPointerTypes"all" | PointerType | undefinedall
Pointer types that can trigger press-and-hold repeat.
repeatThrottleMsnumber | undefined0
Minimum elapsed time in milliseconds between repeat triggers.
requiredboolean | undefinedfalse
Whether the input is required.
size"xs" | "sm" | "md" | "lg" | "xl" | undefined
stepnumber | undefined1
The increment/decrement step size.
styleJSX.CSSProperties | undefined
stylesInputNumberT.Styles | undefined
valuestring | number | undefined
Controlled displayed value.
variant"none" | "outline" | "ghost" | "subtle" | undefined
wheelboolean | undefinedfalse
Whether mouse wheel changes the value while the input is focused.