A numeric input component with increment/decrement controls and number validation
npm install @choice-ui/numeric-inputA sophisticated numeric input component that supports mathematical expression evaluation, unit formatting, and rich interaction patterns. Built for precision and flexibility in professional design tools.
``tsx`
import { NumericInput } from "@choice-ui/react"
- Mathematical Expressions - Evaluate math expressions like 1+2*3 or 50/2{value}px
- Unit Formatting - Display values with units using expression patterns like
- Multi-Value Support - Handle complex objects and arrays with formatted display
- Variable Binding - Support for dynamic variable values with visual indication
- Interactive Controls - Keyboard navigation and drag-to-adjust functionality
- Rich Composition - Prefix and suffix elements with dropdown menus and actions
- Step Controls - Configurable step increments with modifier key support
- Validation - Built-in min/max constraints and decimal precision
- Theme Support - Multiple variants (default, light, dark, reset) for different contexts
`tsx
import { useState } from "react"
function MyComponent() {
const [value, setValue] = useState(10)
return (
onChange={(newValue) => setValue(newValue as number)}
/>
)
}
`
`tsx`
import { HugWidth } from "@choiceform/icons-react"
;
onChange={setValue}
>
`tsx`
import { Relative } from "@choiceform/icons-react"
;
onChange={setValue}
>
`tsx
const [value, setValue] = useState(100)
expression="{value}px"
onChange={(newValue) => setValue(newValue as number)}
>
`
`tsx
const [dimensions, setDimensions] = useState({
width: 100,
height: 200
})
expression="{width}px, {height}px"
onChange={(newValue) => setDimensions(newValue as typeof dimensions)}
>
`
`tsx
import { Dropdown, IconButton } from "@choice-ui/react"
import { ChevronDownSmall, FixedHeight, HugHeight } from "@choiceform/icons-react"
const [value, setValue] = useState(10)
const [menuOpen, setMenuOpen] = useState(false)
value={value}
onChange={setValue}
>
onOpenChange={setMenuOpen}
placement="bottom"
>
Fixed height
Hug contents
`
`tsx
import { Select } from "@choice-ui/react"
import { ChevronDownSmall } from "@choiceform/icons-react"
const [value, setValue] = useState(10)
const [actionType, setActionType] = useState("fixed")
const [menuOpen, setMenuOpen] = useState(false)
value={value}
onChange={setValue}
>
{actionType !== "fixed" && (
{actionType}
)}
open={menuOpen}
onOpenChange={setMenuOpen}
value={actionType}
onChange={setActionType}
placement="bottom-end"
>
`
`tsx
import { Variable } from "@choiceform/icons-react"
const [value, setValue] = useState
const [variableValue, setVariableValue] = useState(10)
onChange={setValue}
>
{!value &&
setVariableValue(10)
setValue(undefined)
}}
>
Add variable...
`
`tsx
// Default - follows page theme
value={value}
onChange={setValue}
/>
// Light - fixed light appearance
value={value}
onChange={setValue}
/>
// Dark - fixed dark appearance
value={value}
onChange={setValue}
/>
// Reset - no variant styling
value={value}
onChange={setValue}
/>
`
`ts
interface NumericInputProps {
/* Current numeric value /
value?: NumericInputValue
/* Default value for uncontrolled usage /
defaultValue?: NumericInputValue
/* Callback when value changes /
onChange?: (value: NumericInputValue, detail: NumberResult) => void
/* Callback when input is cleared /
onEmpty?: () => void
/* Expression pattern for formatted display (e.g., "{value}px") /
expression?: string
/* Minimum allowed value /
min?: number
/* Maximum allowed value /
max?: number
/* Step increment for arrow keys and drag /
step?: number
/* Step increment when Shift key is pressed /
shiftStep?: number
/* Number of decimal places to display /
decimal?: number
/* Whether the input is disabled /
disabled?: boolean
/* Whether the input is read-only /
readOnly?: boolean
/* Whether the input appears selected /
selected?: boolean
/* Whether the input appears focused /
focused?: boolean
/* Visual theme variant /
variant?: "default" | "light" | "dark" | "reset"
/* Tooltip configuration /
tooltip?: {
content: string
}
/* Child elements (Prefix, Suffix, Variable, etc.) /
children?: React.ReactNode
}
type NumericInputValue = string | number | (string | number | undefined)[] | Record
interface NumberResult {
array: number[]
string: string
object: Record
}
`
- Defaults: step: 1, shiftStep: 10, variant: "default"
Container for prefix content like icons or labels.
Container for suffix content with different types for various use cases.
`ts`
interface SuffixProps {
type?: "default" | "menu" | "action"
children?: React.ReactNode
}
Displays a variable value when the main input is undefined.
`ts`
interface VariableProps {
value: number | string
}
Displays action-related text or labels.
A specialized button for triggering dropdown menus.
`ts`
interface MenuTriggerProps {
type?: "menu" | "action"
"aria-label"?: string
}
- Uses Tailwind CSS with tailwind-variants for consistent themingclassName
- Customize using the prop on individual sub-componentsdefault
- Variants support:
- : Follows the page theme dynamically (light/dark mode)light
- : Fixed light appearance regardless of themedark
- : Fixed dark appearance regardless of themereset
- : Removes variant styling, no variant settings applied
- Disabled state provides appropriate visual feedback
| Key | Action |
| ------------------------------- | -------------------------------------- |
| ↑ / ↓ | Increase/decrease by step value |Shift + ↑
| / Shift + ↓ | Increase/decrease by shiftStep value |Meta/Alt + ↑
| / Meta/Alt + ↓ | Increase/decrease by 1 (fine control) |Enter
| | Confirm the current value |Escape
| | Reset to previous value when editing |
| Pattern | Example Value | Display |
| --------------------------- | --------------------------- | ----------------------------------- |
| "{value}px" | 100 | "100px" |"{width}x{height}"
| | {width: 100, height: 200} | "100x200" |"{value1}, {value2}"
| | [10, 20] | "10, 20" |"{value1}{value2,hidden}"
| | [10, 10] | "10" (second hidden when identical) |
- Use appropriate constraints (min, max, step) for better user experiencefocused
- Provide clear prefix/suffix elements to indicate value type or units
- For complex formatted values, always use the expression pattern
- Handle undefined values appropriately when using variables
- Use the prop when integrating with dropdown menus
- Test mathematical expressions with your expected value ranges
`tsx
// User can input "1+2*3" and get result of 7
// Component compares calculated result with current value
const [value, setValue] = useState(2)
const [changeCount, setChangeCount] = useState(0)
const handleChange = (newValue, detail) => {
setValue(newValue as number)
setChangeCount((prev) => prev + 1)
console.log("Input text:", detail.string)
}
;
onChange={handleChange}
/>
`
`tsx
const [color, setColor] = useState({ r: 255, g: 128, b: 64 })
expression="rgb({r}, {g}, {b})"
min={0}
max={255}
step={1}
onChange={(newValue) => setColor(newValue as typeof color)}
/>
`
`tsx
const [size, setSize] = useState({ width: 100, height: 50 })
expression="{width}px × {height}px"
min={1}
step={1}
onChange={(newValue) => setSize(newValue as typeof size)}
>
``
- Full keyboard navigation support
- Screen reader announcements for value changes
- Proper ARIA labels and descriptions
- Focus management for complex interactions
- High contrast support in both light and dark themes
- Mathematical expressions are evaluated safely using a custom parser
- Variable values provide visual indication when active
- Component automatically formats display based on expression patterns
- Drag interactions work on both the input field and handler elements
- Expression parsing supports basic arithmetic operations: +, -, \*, /, ()