NumericInput

A numeric input component with increment/decrement controls and number validation

Installation

Terminal
pnpm add @choice-ui/numeric-input

Import

Component.tsx
import { NumericInput, NumericInputElement, NumericInputMenuTrigger, NumericInputVariable, useNumericLongPress, type NumericInputProps, type NumericInputElementProps, type NumericInputMenuTriggerProps, type NumericInputVariableProps, type NumberResult, type NumericChangeDetail, type NumericInputValue } from "@choice-ui/numeric-input"

Basic

# NumericInput Component
A sophisticated input component for handling numeric values with rich formatting, expression evaluation, and interactive features.
## Key Features
- **Flexible Value Handling**: Supports simple numbers, mathematical expressions, and formatted values (with units) - **Multiple Interaction Modes**: Keyboard navigation, drag adjustments, menu selection - **Variable Binding**: Support for displaying and toggling variable values - **Rich Expression Formats**: Unit formatting (px, %, etc.), multi-value inputs, conditional formatting - **Customizable Appearance**: Prefix, suffix elements, dark theme - **State Management**: Disabled state, selected state, error handling
## Expression Format Examples
- Unit format: `"{value}px"` → displays "100px" - Multiple properties: `"{width}px, {height}px"` → displays "10px, 20px" - Conditional hiding: `"{value1}{value2,hidden}"` → shows value2 only when needed
## Advanced Features
- **Math Evaluation**: Input mathematical expressions (e.g., "1+2*3") - **Keyboard Controls**: - ↑/↓: Increase/decrease by step value - Shift + ↑/↓: Use larger step - Meta/Alt + ↑/↓: Use smaller step - **Variable Support**: Display variable values with `<NumericInput.Variable />` component - **Custom Menus**: Add dropdown menus with suffix of type "menu" or "action"
## Best Practices
- Provide clear prefix or suffix to indicate value type or unit - Use appropriate constraints (min, max, step) for better user experience - Always use expression pattern for complex formatted values
## Style Variants
- Default (light theme) - Dark theme: `variant="dark"` - Disabled state: `disabled={true}` - Selected state: `selected={true}`

Prefix

Example showing NumericInput with a prefix element. Prefixes provide visual context for the input value, such as indicating the property type.

Suffix

Example showing NumericInput with a suffix element. Suffixes help indicate units or provide additional context after the value.

Long Press

Demonstrates the `useNumericLongPress` hook for continuous increment/decrement.
### Features
- **Single click**: Triggers one increment/decrement - **Long press**: After 400ms delay, continuously triggers at 50ms intervals - **Auto-stop**: Stops when mouse is released or leaves the button

Suffix Menu

Example demonstrating a NumericInput with a dropdown menu in the suffix. This pattern allows additional actions to be performed on the input value. The menu is triggered by a button in the suffix position.

Suffix Action

Example showing a NumericInput with an action in the suffix. Action suffixes allow changing the behavior or formatting of the input. This example demonstrates a dropdown selector that changes the height behavior.

Add Variable

Example demonstrating variable value support in NumericInput. Variables allow users to reference dynamic values instead of hardcoded numbers. This example shows how to toggle between a concrete value and a variable reference.

Expression

Example showing expression pattern support for formatting compound values. The expression "{width}px, {height}px" formats the value object with two properties. This pattern is ideal for dimension inputs or other multi-value scenarios.

Disabled

Example showing NumericInput in disabled state. Disabled inputs prevent user interaction but maintain visual consistency. This example shows both simple disabled inputs and complex ones with variables and menus.

Sizes

Sizes: Demonstrates different size variants of the numeric input component.
- default: Standard height (24px / h-6) - large: Larger height (32px / h-8)
This example shows all combinations of sizes with different features:
- Basic input - With prefix - With suffix - With prefix and suffix - With variable - With menu trigger - Disabled state

Default Size (h-6 / 24px)

Basic
With Prefix
With Suffix
Prefix + Suffix
With Variable
With Menu
Disabled

Large Size (h-8 / 32px)

Basic
With Prefix
With Suffix
Prefix + Suffix
With Variable
With Menu
Disabled

Variants

Variants: Demonstrates different visual variants of the numeric input component.
- default: Follows the page theme dynamically (light/dark mode) - light: Fixed light appearance regardless of theme - dark: Fixed dark appearance regardless of theme - reset: Removes variant styling, no variant settings applied

Tooltip

Expression Calculation

Example demonstrating the mathematical expression evaluation feature. This shows how NumericInput can:
1. Accept and evaluate mathematical expressions (e.g., 1+1) 2. Compare calculation results with current value 3. Only trigger onChange when the result differs from current value
For example:
- When value is 2, entering "2" won't trigger onChange - When value is 2, entering "1+1" won't trigger onChange (same result) - When value is 2, entering "3" or "1+2" will trigger onChange (different result)
Usage:
- Try entering different values and expressions to observe onChange event behavior - Current value won't trigger onChange when entered again - Expressions that result in the same value won't trigger onChange - Entering other values or expressions will trigger onChange - Try expressions like "1+1", "2*1", "4/2" that result in the current value

Current value: 2

onChange trigger count: 0

Unit Expression Calculation

Example demonstrating expression calculations with unit formatting. Shows how:
1. Expressions with units like "{value}px" display values with units 2. Input expressions are evaluated before comparison 3. Similar expressions resulting in the same value won't trigger onChange
For example:
- When value is "24px", entering "24" or "12+12" won't trigger onChange - When value is "24px", entering "25" or "12+13" will trigger onChange
Usage:
- Try entering values and expressions with a pixel unit pattern - Current value won't trigger onChange when entered again - Expressions that result in the same numerical value won't trigger onChange - Entering different values or expressions will trigger onChange - Try expressions like "12+12", "24*1" that result in the current numerical value

Current value: 24px

onChange trigger count: 0

Multi Value Expression Update

Example demonstrating multi-value expression update behavior. In previous versions, when only modifying the second value (e.g., height) without modifying the first value (e.g., width), the component could not update correctly. This fix ensures that even when only one property is modified, the entire object updates correctly.
Usage:
- Modify the first value (width) - Modify the second value (height) - Modify both values simultaneously After the fix, all three scenarios should correctly trigger updates.
Tips:
- Enter comma-separated values (e.g., "15, 20") to update both properties - Keep the value before the comma unchanged and only modify the value after (e.g., ", 25") to update only height

Current value: 10, 20

Update count: 0

Step And Shift Step

Step and Shift Step: Demonstrates the step functionality with modifier key support
This example shows how NumericInput responds to different step values based on modifier keys:
- Default: 1 step - Shift: 10 steps (larger increments) - Meta/Ctrl: 0.1 steps (finer control)
Usage:
- Focus the input and use arrow keys with modifier keys to see different step values - ↑/↓: Default step (1) - Shift + ↑/↓: Large step (10) - Try holding Shift while using arrow keys to see different step sizes

Current value: 100

Array Values

ArrayValues: Demonstrates support for array values.
Features:
- Array input/output - Comma-separated value parsing - Multiple value management - Useful for multi-value scenarios like coordinates or dimensions
Usage:
- NumericInput supports array values for multi-value inputs - Array values require an expression pattern to format display - Expression uses {value1}, {value2}, {value3} etc. (1-based indexing) - Enter comma-separated values like "10, 20, 30"

Current values: 10, 20, 30

Empty Value

EmptyValue: Demonstrates empty value handling.
Features:
- Support for undefined/null values - Value clearing functionality - Default value management - Useful for optional numeric inputs
Usage:
- NumericInput supports undefined values for optional inputs - Clear the input to see empty value handling

Current value: 100

Linked Inputs

LinkedInputs: Demonstrates coordinated multiple inputs with shared state.
Features:
- Coordinated multiple inputs - Shared state management - Visual result feedback - Practical example: RGB color picker
Usage:
- Example of coordinated multiple NumericInput components - Each input controls a different color channel (R, G, B) - Changes are reflected in real-time visual preview
#ff6432
rgb(255, 100, 50)

API reference

NumericInputPropsTypeDefault
value
NumericInputValue
|
undefined
-
defaultValue
NumericInputValue
|
undefined
-
onChange
((value: NumericInputValue, detail: NumberResult) => void)
|
undefined
-
max
number
|
undefined
-
min
number
|
undefined
-
step
number
|
undefined
-
disabled
boolean
|
undefined
-
id
string
|
undefined
-
size
undefined
|
"default"
|
"large"
-
readOnly
boolean
|
undefined
-
selected
boolean
|
undefined
-
className
string
|
undefined
-
classNames
{ container?: string
|
undefined; input?: string
|
undefined; }
|
undefined
-
tooltip
TooltipProps
|
undefined
-
triggerRef
RefObject<HTMLDivElement>
|
((el: HTMLDivElement
|
null) => void)
|
undefined
-
decimal
number
|
undefined
-
expression
string
|
undefined
-
focused
boolean
|
undefined
-
handlerPressed
boolean
|
undefined
-
handlerProps
Record<string, unknown>
|
undefined
-
onEmpty
(() => void)
|
undefined
-
onIsEditingChange
((isEditing: boolean) => void)
|
undefined
-
onPressEnd
(((event: PressEvent) => void) & ((e: PointerEvent) => void))
|
undefined
-
onPressStart
(((event: PressEvent) => void) & ((e: PointerEvent) => void))
|
undefined
-
shiftStep
number
|
undefined
-
variant
undefined
|
"default"
|
"light"
|
"dark"
|
"reset"
-
NumericInputElementPropsTypeDefault
className
string
|
undefined
-
position
undefined
|
"prefix"
|
"suffix"
-
type
undefined
|
"action"
|
"handler"
|
"menu"
-
ref
((instance: HTMLDivElement
|
null) => void)
|
RefObject<HTMLDivElement>
|
null
|
undefined
-
NumericInputMenuTriggerPropsTypeDefault
className
string
|
undefined
-
type
undefined
|
"action"
|
"menu"
-
size
undefined
|
"default"
|
"large"
|
"reset"
-
as
ElementType<any, keyof IntrinsicElements>
|
undefined
-
disabled
boolean
|
undefined
-
readOnly
boolean
|
undefined
-
active
boolean
|
undefined
-
asChild
boolean
|
undefined
-
focused
boolean
|
undefined
-
loading
boolean
|
undefined
-
tooltip
TooltipProps
|
undefined
-
variant
undefined
|
"default"
|
"reset"
|
"secondary"
|
"solid"
|
"highlight"
|
"ghost"
|
"dark"
|
"submit"
-
NumericInputMenuActionPromptTypeDefault
className
string
|
undefined
-
NumericInputVariablePropsTypeDefault
className
string
|
undefined
-
hasPrefixElement
boolean
|
undefined
-
onClick
((e: MouseEvent<HTMLDivElement, MouseEvent>) => void)
|
undefined
-
value
number
|
null
|
undefined
-
useNumericInputTypeDefault
containerRef
RefObject<HTMLElement>
|
undefined
-
decimal
number
|
undefined
-
defaultValue
NumericInputValue
|
undefined
-
disabled
boolean
|
undefined
-
expression
string
|
undefined
-
max
number
|
undefined
-
min
number
|
undefined
-
onChange
((value: T, obj: NumberResult) => void)
|
undefined
-
onEmpty
(() => void)
|
undefined
-
onPressEnd
(((event: PressEvent) => void) & ((e: PointerEvent) => void))
|
undefined
-
onPressStart
(((event: PressEvent) => void) & ((e: PointerEvent) => void))
|
undefined
-
readOnly
boolean
|
undefined
-
ref
Ref<HTMLInputElement>
|
undefined
-
shiftStep
number
|
undefined
-
step
number
|
undefined
-
value
NumericInputValue
|
undefined
-
useNumericValueProcessingTypeDefault
decimal
number
|
undefined
-
defaultValue
NumericInputValue
|
undefined
-
expression
string -
max
number
|
undefined
-
min
number
|
undefined
-
value
NumericInputValue
|
undefined
-
useInputInteractionsTypeDefault
decimal
number
|
undefined
-
disabled
boolean
|
undefined
-
displayValue
string -
expression
string -
getCurrentStep
() => number -
innerValue
NumberResult
|
undefined
-
inputRef
RefObject<HTMLInputElement>
-
isFocused
boolean -
max
number
|
undefined
-
min
number
|
undefined
-
onChange
((value: T, detail: NumberResult) => void)
|
undefined
-
onEmpty
(() => void)
|
undefined
-
readOnly
boolean
|
undefined
-
setDisplayValue
(value: string) => void -
setIsFocused
(focused: boolean) => void -
setValue
(value: NumberResult
|
((prev: NumberResult
|
undefined) => NumberResult)) => void
-
updateValue
(updateFn?: ((value: number) => number)
|
undefined) => void
-
value
NumericInputValue
|
undefined
-