Vize

Number Field

Locale-aware APG spinbutton with formatting, stepping, press-and-hold triggers, and form association.

Package @vizejs/ui/number-field
Maturity stable
Own the source vize lib pull number-field
Requires context, controllable-state, id, locale
Aliases number input, spinbutton, stepper input, currency input, numeric field
Covers HTML number input, WAI-ARIA Spinbutton, React Aria NumberField, Radix/Reka NumberField

Usage

import { NumberField, NumberFieldInput, NumberFieldIncrement, NumberFieldDecrement } from "@vizejs/ui/number-field";

Or copy the source into your project with vize lib pull number-field (see Source Distribution).

API

NumberField

Source: src/families/form/number-field/number-field.vue

Props

Prop Type Default Description
id string | null undefined Id of the spinbutton input. null and undefined select a deterministic fallback. Binding a Field's fieldProps here wires the label, description, and error.
name string undefined Native form field name. When set, a hidden input submits the raw number (not the formatted text) with the owning form.
form string undefined Id of a form owner outside the component tree.
modelValue NumberFieldValue undefined Controlled value. undefined selects uncontrolled behavior; null is empty.
defaultValue NumberFieldValue null Initial uncontrolled value and the value restored by form reset.
min number undefined Lower bound. Omit for an unbounded field.
max number undefined Upper bound. Omit for an unbounded field.
step number 1 (0.01 when formatOptions.style is "percent") Positive step used by arrow keys, triggers, and the wheel.
largeStep number step * 10 Positive step used by Page Up and Page Down.
locale string undefined BCP 47 locale for formatting and parsing. Falls back to the nearest LocaleProvider, then the document language, then en-US.
formatOptions NumberFieldFormatOptions undefined Intl.NumberFormat options, for example currency, percent, or unit styles.
clampOnCommit boolean true Clamp typed values into [min, max] when they are committed.
snapOnCommit boolean false Snap typed values to the nearest step when they are committed.
allowWheel boolean false Opt in to stepping with the mouse wheel while the input is focused.
holdDelay number 400 Delay before press-and-hold on a trigger starts repeating, in milliseconds.
holdInterval number 60 Interval between repeated steps while a trigger is held, in milliseconds.
disabled boolean false Disable editing, focus, triggers, and native form submission.
readOnly boolean false Keep the input focusable while preventing user changes.
required boolean false Mark the input as required for native constraint validation.
ariaLabel string undefined Accessible name when no label or aria-labelledby supplies one.
ariaLabelledby string undefined Space-separated ids that label the input.
ariaDescribedby string undefined Space-separated ids that describe the input.
ariaErrormessage string undefined Id of the validation error message used while invalid.
ariaInvalid NumberFieldAriaInvalid false Invalid state announced to assistive technology.

Events

Event Payload Description
update:modelValue [value: NumberFieldValue] Fired when a commit or step requests a new controlled value.
change [value: NumberFieldValue, previous: NumberFieldValue, source: NumberFieldChangeSource] Fired after a distinct value is committed, with the previous value and its source.

Slots

Slot Slot props Description
default NumberFieldSlotState Renders the input, triggers, and any adornments with normalized state.

Exposed

Member Type Description
value NumberFieldValue Committed numeric value, or null while empty.
formattedValue string Locale-formatted committed value, or "" while empty.
inputText string Text currently shown in the input, including uncommitted typing.
min number Normalized lower bound (-Infinity when unbounded).
max number Normalized upper bound (Infinity when unbounded).
step number Normalized positive step.
largeStep number Normalized positive large step.
locale string Resolved locale used for formatting and parsing.
canIncrement boolean Whether an increment can still change the value.
canDecrement boolean Whether a decrement can still change the value.
disabled boolean Whether editing and triggers are disabled.
readOnly boolean Whether user edits are locked while focus remains available.
required boolean Whether native required validation is requested.
invalid boolean Whether assistive technology announces the field as invalid.
state NumberFieldState Stable state token for styling and tests.
root HTMLDivElement | null Rendered root element.
input HTMLInputElement | null Registered spinbutton input, once mounted.
focus (options?: FocusOptions) => void Focus the spinbutton input.
setValue (value: NumberFieldValue) => boolean Request a value (clamped, and snapped when snapOnCommit) and report whether it changed.
increment (count?: number) => boolean Step up by count steps (default 1) and report whether the value changed.
decrement (count?: number) => boolean Step down by count steps (default 1) and report whether the value changed.
commit () => boolean Parse and commit the current input text; returns whether the value changed.
reset () => boolean Restore the default value and report whether it changed.

NumberFieldInput

Source: src/families/form/number-field/number-field-input.vue

Props

Prop Type Default Description
placeholder string undefined Native placeholder text shown while empty.
autocomplete string "off" Native autocomplete hint.

Events

Event Payload Description
reject [text: string, nativeEvent: Event] Fired when typed text is rejected because it cannot become a number.

NumberFieldIncrement

Source: src/families/form/number-field/number-field-increment.vue

Props

Prop Type Default Description
ariaLabel string "Increase" or "Decrease" Accessible name of the trigger.

Slots

Slot Slot props Description
default NumberFieldTriggerSlotState Trigger contents, typically an icon.

NumberFieldDecrement

Source: src/families/form/number-field/number-field-decrement.vue

Props

Prop Type Default Description
ariaLabel string "Increase" or "Decrease" Accessible name of the trigger.

Slots

Slot Slot props Description
default NumberFieldTriggerSlotState Trigger contents, typically an icon.

Behavior

Normative state x input -> outcome table for number-field.vue, number-field-input.vue, number-field-increment.vue, and number-field-decrement.vue (@vizejs/ui/number-field). The input follows the WAI-ARIA APG spinbutton pattern. Every row is proven by the named test in number-field.test.ts, number-field-parser.test.ts, or number-field-ssr.test.ts; compile-only assertions live in number-field.types.test-d.ts.

# State Input Outcome Proven by
N1 named, bounded render role="spinbutton" text input with deterministic id, locale text, aria-valuenow/min/max/valuetext, inputmode, required state, hidden raw-number form value, parts renders an APG spinbutton with locale text, bounds, and form hooks
N2 editable type, then blur partial text (-, 1,234.) is kept without emitting; blur parses, commits, re-formats, emits update:modelValue then change(value, previous, "blur"); empty -> null typing keeps partial text and commits the parsed value on blur
N3 editable type an impossible character the previous text is restored and reject(text, event) fires; a minus sign is rejected while min >= 0 rejects characters that can never form a number
N4 bounded ArrowUp/Down, PageUp/Down, Home/End arrows move to the adjacent step-grid value, pages move by largeStep (default step * 10), Home/End jump to finite bounds, all clamped; keys are prevented arrow, page, home, and end keys step along the grid within bounds
N5 unbounded Home / End native caret movement is kept (not prevented); no aria-valuemin/max; signed fields use a text keyboard unbounded fields keep native Home and End caret movement
N6 uncommitted typing arrow key stepping starts from the typed text; decimal steps never accumulate floating-point error typed text is the stepping origin and precision stays decimal-safe
N7 commit blur / Enter typed values clamp into bounds unless clampOnCommit=false; snapOnCommit snaps to the step grid; Enter commits with source "enter" commits clamp by default and snap when requested
N8 locale + formatOptions render / type currency, percent (1% default step), and unit styles format and parse per locale; zero-fraction currencies reject decimals and use a numeric keyboard formats and parses currency, percent, and unit styles per locale
N9 triggers click / press-and-hold AT click steps once; pointer press keeps focus in the input, steps, then repeats after holdDelay every holdInterval until release/leave/cancel or a bound disables it triggers step on click, disable at bounds, and repeat while held
N10 wheel wheel ignored unless allowWheel; when opted in, only a focused input steps and cancels page scrolling (non-passive listener) wheel stepping is opt-in and requires focus
N11 controlled step request emits the request; the input keeps showing modelValue until the parent accepts it controlled value wins until the parent accepts the request
N12 in a form submit / reset the hidden input submits the raw number (not formatted text); form reset restores defaultValue and its formatted text submits the raw number and restores the default on form reset
N13 disabled / read-only keys, typing, triggers disabled: native disabled input, triggers, and hidden value; read-only: focusable with aria-readonly, triggers disabled, no edits or emits disabled and read-only fields keep availability semantics
N14 imperative expose focus, setValue (clamped, NaN -> null), increment(count), decrement(count), commit, reset, and normalized state exposes focus, setValue, increment, decrement, commit, and reset
N15 inside Field v-bind="fieldProps" on root the spinbutton receives the Field id, aria-labelledby, aria-describedby, aria-errormessage, and aria-invalid binds Field fieldProps to wire the label, description, and error
N16 part without provider setup throws the stable VIZE_UI_CONTEXT_MISSING: NumberField diagnostic parts require a NumberField provider
N17 parser format -> parse round-trips across locales, native numbering systems, accounting currency, percent, and unit styles; partial prefixes are accepted while typing round-trips formatted text across locales, numbering systems, and styles
N18 SSR / hydration isolated requests byte-identical markup, no NaN/Infinity, and hydration without diagnostics; hydrated triggers remain interactive renders byte-identical spinbutton markup and hydrates without mismatches
N19 inside LocaleProvider render without a locale prop the field formats and parses with the provider locale inherits the locale from the nearest LocaleProvider

Public extension contract

Surface Contract
Parts root (<div>), input (spinbutton), increment, decrement (<button tabindex="-1">).
Data attributes data-vize-ui (number-field, number-field-input, number-field-increment, number-field-decrement, number-field-value), data-state on root/input.
Boolean hooks Root: data-disabled, data-readonly, data-required, data-invalid, data-empty. Triggers: data-disabled, data-holding.
Slots Root default slot receives NumberFieldSlotState; trigger slots receive { direction, disabled, holding }.
Field wiring Bind a Field's fieldProps to NumberField; the root forwards the id and ARIA relations to the spinbutton and triggers reference it via aria-controls.
Locale locale prop, else the nearest LocaleProvider, else the document language, else en-US.

The subpath is tree-shakable and ships no CSS; those package contracts are pinned by distribution.test.ts, check:size, and check:tree-shaking.