react-intl-number-input 0.8.5 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,222 +1,254 @@
1
- # react-intl-number-input
2
-
3
- A React component for masked and formatted number input with `Intl.NumberFormat` locale support.
4
-
5
- [Demo](https://nandorip.github.io/react-intl-number-input/example/dist/)
6
-
7
- ## Requirements
8
-
9
- - React: `>=16.8.0 <20`
10
-
11
- ## Install
12
-
13
- ```bash
14
- npm install react-intl-number-input
15
- ```
16
-
17
- ## Usage
18
-
19
- ```javascript
20
- import React, { useState } from 'react';
21
- import IntlNumberInput from 'react-intl-number-input';
22
-
23
- function App() {
24
- const [value, setValue] = useState(0);
25
- const [maskedValue, setMaskedValue] = useState('0.00');
26
-
27
- const handleChange = (event, nextValue, nextMaskedValue) => {
28
- setValue(nextValue);
29
- setMaskedValue(nextMaskedValue);
30
- };
31
-
32
- return (
33
- <div>
34
- <IntlNumberInput onChange={handleChange} />
35
- <p>value: {value}</p>
36
- <p>maskedValue: {maskedValue}</p>
37
- </div>
38
- );
39
- }
40
- ```
41
-
42
- ### TypeScript
43
-
44
- ```tsx
45
- import React, { useState } from 'react';
46
- import IntlNumberInput, {
47
- IntlNumberInputProps,
48
- } from 'react-intl-number-input';
49
-
50
- function App() {
51
- const [value, setValue] = useState<number>(0);
52
-
53
- const handleChange: IntlNumberInputProps['onChange'] = (event, val, masked) => {
54
- setValue(val);
55
- console.log('Numeric:', val, 'Masked:', masked);
56
- };
57
-
58
- return <IntlNumberInput value={value} onChange={handleChange} locale="pt-BR" />;
59
- }
60
- ```
61
-
62
- React 18+:
63
-
64
- ```javascript
65
- import { createRoot } from 'react-dom/client';
66
-
67
- const root = createRoot(document.getElementById('root'));
68
- root.render(<App />);
69
- ```
70
-
71
- React 16/17:
72
-
73
- ```javascript
74
- import ReactDOM from 'react-dom';
75
-
76
- ReactDOM.render(<App />, document.getElementById('root'));
77
- ```
78
-
79
- ## Properties
80
-
81
- | Name | Type | Default | Description |
82
- | --- | --- | :---: | --- |
83
- | value | `number` \| `string` | `0` | Controlled numeric value |
84
- | locale | `string` | `'en-US'` | BCP 47 language tag ([Intl locales](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locale_identification_and_negotiation)) |
85
- | prefix | `string` | `''` | Prefix shown in the masked value |
86
- | suffix | `string` | `''` | Suffix shown in the masked value |
87
- | precision | `number` | `2` | Fraction digits (clamped to `0`–`20`) |
88
- | onChange | `function` | — | `(event, value, maskedValue) => void` |
89
- | onBlur | `function` | — | `(event, value, maskedValue) => void` |
90
- | disabled | `boolean` | `false` | Disables the input |
91
- | autoFocus | `boolean` | — | Native input autofocus |
92
- | minValue | `number` | — | Minimum allowed value |
93
- | maxValue | `number` | — | Maximum allowed value |
94
- | showStepButtons | `boolean` | `false` | Renders built-in +/- step buttons (ignored when `renderControls` is set) |
95
- | renderControls | `function` | — | `(props: ControlsRenderProps) => ReactNode` — custom stepper UI |
96
- | step | `number` | `1` | Step increment in display units (e.g. `1` with `precision={2}` adds `0.01`) |
97
- | inputMode | `'numeric'` \| `'decimal'` | auto | Overrides mobile keyboard mode |
98
- | className | `string` | — | Input CSS class |
99
- | style | `object` | — | Input inline styles |
100
- | id | `string` | — | Input id |
101
- | name | `string` | — | Input name |
102
- | placeholder | `string` | — | Input placeholder |
103
- | readOnly | `boolean` | — | Read-only input |
104
- | required | `boolean` | — | Required input |
105
- | tabIndex | `number` | — | Input tab index |
106
-
107
- The component also accepts standard `<input>` attributes such as `ref`, `onFocus`, `onKeyDown`, `aria-*`, `data-*`, and `autoComplete`. Custom `onChange` and `onBlur` callbacks receive the clamped numeric value and the formatted masked string.
108
-
109
- ## Accessibility (a11y)
110
-
1
+ # react-intl-number-input
2
+
3
+ A React number input with implied decimal places and locale formatting through `Intl.NumberFormat`. For money-style entry, typing `1234` with two decimal places produces `12.34`.
4
+
5
+ [Interactive demo](https://nandorip.github.io/react-intl-number-input/)
6
+
7
+ ## Requirements
8
+
9
+ - React: `>=16.8.0 <20`
10
+ - Node.js: `>=20` for installation and local development (declared in the npm package's `engines` field). The component runs in the browser.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install react-intl-number-input
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```javascript
21
+ import React, { useState } from 'react';
22
+ import IntlNumberInput from 'react-intl-number-input';
23
+
24
+ function App() {
25
+ const [value, setValue] = useState(0);
26
+ const [maskedValue, setMaskedValue] = useState('0.00');
27
+
28
+ const handleChange = (event, nextValue, nextMaskedValue) => {
29
+ setValue(nextValue);
30
+ setMaskedValue(nextMaskedValue);
31
+ };
32
+
33
+ return (
34
+ <div>
35
+ <IntlNumberInput onChange={handleChange} />
36
+ <p>value: {value}</p>
37
+ <p>maskedValue: {maskedValue}</p>
38
+ </div>
39
+ );
40
+ }
41
+ ```
42
+
43
+ ### TypeScript
44
+
45
+ ```tsx
46
+ import React, { useState } from 'react';
47
+ import IntlNumberInput, {
48
+ IntlNumberInputProps,
49
+ } from 'react-intl-number-input';
50
+
51
+ function App() {
52
+ const [value, setValue] = useState<number>(0);
53
+
54
+ const handleChange: IntlNumberInputProps['onChange'] = (event, val, masked) => {
55
+ setValue(val);
56
+ console.log('Numeric:', val, 'Masked:', masked);
57
+ };
58
+
59
+ return <IntlNumberInput controlled value={value} onChange={handleChange} locale="pt-BR" />;
60
+ }
61
+ ```
62
+
63
+ React 18+:
64
+
65
+ ```javascript
66
+ import { createRoot } from 'react-dom/client';
67
+
68
+ const root = createRoot(document.getElementById('root'));
69
+ root.render(<App />);
70
+ ```
71
+
72
+ React 16/17:
73
+
74
+ ```javascript
75
+ import ReactDOM from 'react-dom';
76
+
77
+ ReactDOM.render(<App />, document.getElementById('root'));
78
+ ```
79
+
80
+ ## Properties
81
+
82
+ | Name | Type | Default | Description |
83
+ | --- | --- | :---: | --- |
84
+ | value | `number` \| `string` | `0` | Initial/synchronized value; numbers use numeric units, strings use implied-decimal parsing |
85
+ | controlled | `boolean` | `false` | Keep `value` authoritative, including when the parent rejects an edit |
86
+ | locale | `string` | `'en-US'` | BCP 47 language tag ([Intl locales](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locale_identification_and_negotiation)) |
87
+ | prefix | `string` | `''` | Prefix shown in the masked value |
88
+ | suffix | `string` | `''` | Suffix shown in the masked value |
89
+ | precision | `number` | `2` | Fraction digits (clamped to `0`–`20`) |
90
+ | onChange | `function` | — | `(event, value, maskedValue) => void` |
91
+ | onBlur | `function` | — | `(event, value, maskedValue) => void` |
92
+ | disabled | `boolean` | `false` | Disables the input |
93
+ | autoFocus | `boolean` | — | Native input autofocus |
94
+ | minValue | `number` | — | Minimum allowed value |
95
+ | maxValue | `number` | — | Maximum allowed value |
96
+ | showStepButtons | `boolean` | `false` | Renders built-in +/- step buttons (ignored when `renderControls` is set) |
97
+ | renderControls | `function` | — | `(props: ControlsRenderProps) => ReactNode` — custom stepper UI |
98
+ | step | `number` | `1` | Step in scaled digit units: the increment is `step / 10 ** precision` (e.g. `1` with `precision={2}` adds `0.01`) |
99
+ | inputMode | `'numeric'` \| `'decimal'` | auto | Overrides mobile keyboard mode |
100
+ | className | `string` | — | Input CSS class |
101
+ | style | `object` | — | Input inline styles |
102
+ | id | `string` | — | Input id |
103
+ | name | `string` | — | Input name |
104
+ | placeholder | `string` | — | Input placeholder |
105
+ | readOnly | `boolean` | — | Read-only input |
106
+ | required | `boolean` | — | Required input |
107
+ | tabIndex | `number` | — | Input tab index |
108
+
109
+ The component also accepts standard `<input>` attributes such as `ref`, `onFocus`, `onKeyDown`, `aria-*`, `data-*`, and `autoComplete`. Custom `onChange` and `onBlur` callbacks receive the clamped numeric value and the formatted masked string.
110
+
111
+ ### Value modes and compatibility
112
+
113
+ By default, the component preserves its historical behavior: `value` initializes the field and a changed `value` updates it, while local edits remain visible if the parent leaves `value` unchanged. Without `value`, the component manages its own numeric state.
114
+
115
+ For strict controlled behavior, use `controlled` and update `value` from `onChange`. Rejected edits emit the proposed numeric value and mask, but the input and custom controls retain the parent's value:
116
+
117
+ ```jsx
118
+ <IntlNumberInput
119
+ controlled
120
+ value={value}
121
+ onChange={(_event, nextValue) => setValue(nextValue)}
122
+ />
123
+ ```
124
+
125
+ The default remains compatible with existing integrations, including those that use a fixed `value` as an initial amount. `controlled` requires no changes to callback signatures.
126
+
127
+ String values use the same implied-decimal parser as typing: with `precision={2}`, `value="1234"` means `12.34`, while `value={1234}` means `1,234.00`. Prefer numbers for programmatic values. Changing precision reformats existing internal numeric state without rescaling it; rounding for display does not discard hidden fraction digits.
128
+
129
+ Typing forwards the original React change event. Buttons and custom setters deliver a programmatic change event whose `target` and `currentTarget` are the actual input element, containing the proposed mask during the callback. Use the second and third callback arguments for numeric and formatted values consistently across both kinds of changes. These programmatic callbacks do not dispatch an additional DOM change event.
130
+
131
+ ## Accessibility (a11y)
132
+
111
133
  The component is built with accessibility in mind:
112
134
  - Uses `role="spinbutton"` to identify as a numeric input.
113
135
  - Provides `aria-valuenow`, `aria-valuemin`, and `aria-valuemax` attributes.
136
+ - Provides the formatted value as `aria-valuetext`, including any prefix and suffix.
137
+ - Supports Arrow Up and Arrow Down to adjust by one step; Shift plus an arrow adjusts by ten steps. Your `onKeyDown` handler runs first and can prevent the adjustment.
114
138
  - Support for `forwardRef` allows linking labels and managing focus programmatically.
115
139
  - Built-in step buttons have descriptive `aria-label` attributes.
116
-
117
- ## Examples
118
-
119
- ### Basic Usage
120
-
121
- ```javascript
122
- // maskedValue: 1,234,567.89
123
- <IntlNumberInput />
124
- ```
125
-
126
- ```javascript
127
- // maskedValue: 12,345.6789
128
- <IntlNumberInput precision={4} />
129
- ```
130
-
131
- ```javascript
132
- // maskedValue: $1,234,567.89
133
- <IntlNumberInput prefix="$" />
134
- ```
135
-
136
- ```javascript
137
- // maskedValue: 1,234%
138
- <IntlNumberInput suffix="%" precision={0} />
139
- ```
140
-
141
- ```javascript
142
- // maskedValue: 12.50% — type digits only; decimal is implied by precision
143
- <IntlNumberInput suffix="%" precision={2} />
144
- ```
145
-
146
- ```javascript
147
- // maskedValue: R$ 1.234.567,89
148
- <IntlNumberInput locale="pt-BR" prefix="R$ " precision={2} />
149
- ```
150
-
151
- ```javascript
152
- // With min/max and step buttons
153
- <IntlNumberInput
154
- value={50}
155
- minValue={0}
156
- maxValue={100}
157
- step={5}
158
- precision={0}
159
- showStepButtons
160
- onChange={(event, value) => console.log(value)}
161
- />
162
- ```
163
-
164
- ```javascript
165
- // Custom controls (layout is up to you — wrap in a parent div as needed)
166
- <div className="amount-field">
167
- <IntlNumberInput
168
- value={12.34}
169
- precision={2}
170
- step={1}
171
- minValue={0}
172
- maxValue={100}
173
- onChange={(event, value) => console.log(value)}
174
- renderControls={({ increment, decrement, setValue, value, formattedValue, min, max, disabled }) => (
175
- <div className="amount-controls">
176
- <button type="button" onClick={() => decrement()} disabled={disabled}>-</button>
177
- <span>{formattedValue}</span>
178
- <button type="button" onClick={() => increment()} disabled={disabled}>+</button>
179
- <button type="button" onClick={() => setValue(max ?? value)} disabled={disabled}>
180
- Max
181
- </button>
182
- </div>
183
- )}
184
- />
185
- </div>
186
- ```
187
-
188
- ### Programmatic Focus (ref)
189
-
190
- ```javascript
191
- import React, { useRef } from 'react';
192
- import IntlNumberInput from 'react-intl-number-input';
193
-
194
- function FocusExample() {
195
- const inputRef = useRef(null);
196
-
197
- return (
198
- <>
199
- <IntlNumberInput ref={inputRef} />
200
- <button onClick={() => inputRef.current?.focus()}>
201
- Focus Input
202
- </button>
203
- </>
204
- );
205
- }
206
- ```
207
-
208
- `renderControls` replaces `showStepButtons` when both are provided. The component renders the `<input>` and your controls as siblings (no wrapper), so you control layout in the parent.
209
-
210
- ## How input works
211
-
212
- Users type digits; the component applies locale formatting and optional prefix/suffix. For `precision={2}`, typing `1234` becomes `12.34`. Negative values are supported when `-` is present in the input.
213
-
214
- ## Contributing
215
-
216
- See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and publishing instructions.
217
-
218
- Local development requires Node.js 20+. The published package has no Node.js version constraint for consumers.
219
-
220
- ## Changelog
221
-
222
- See [CHANGELOG.md](CHANGELOG.md) for release history.
140
+
141
+ Associate each input with a visible `<label htmlFor="...">`, `aria-labelledby`, or `aria-label`. `readOnly` prevents changes from typing, arrow keys, built-in buttons and custom setters; custom controls also receive `readOnly`.
142
+
143
+ ## Examples
144
+
145
+ ### Basic Usage
146
+
147
+ ```javascript
148
+ // maskedValue: 1,234,567.89
149
+ <IntlNumberInput value={1234567.89} />
150
+ ```
151
+
152
+ ```javascript
153
+ // maskedValue: 12,345.6789
154
+ <IntlNumberInput value={12345.6789} precision={4} />
155
+ ```
156
+
157
+ ```javascript
158
+ // maskedValue: $1,234,567.89
159
+ <IntlNumberInput value={1234567.89} prefix="$" />
160
+ ```
161
+
162
+ ```javascript
163
+ // maskedValue: 1,234%
164
+ <IntlNumberInput value={1234} suffix="%" precision={0} />
165
+ ```
166
+
167
+ ```javascript
168
+ // maskedValue: 12.50% — type digits only; decimal is implied by precision
169
+ <IntlNumberInput value={12.5} suffix="%" precision={2} />
170
+ ```
171
+
172
+ ```javascript
173
+ // maskedValue: R$ 1.234.567,89
174
+ <IntlNumberInput value={1234567.89} locale="pt-BR" prefix="R$ " precision={2} />
175
+ ```
176
+
177
+ ```javascript
178
+ // With min/max and step buttons
179
+ <IntlNumberInput
180
+ value={50}
181
+ minValue={0}
182
+ maxValue={100}
183
+ step={5}
184
+ precision={0}
185
+ showStepButtons
186
+ onChange={(event, value) => console.log(value)}
187
+ />
188
+ ```
189
+
190
+ ```javascript
191
+ // Custom controls (layout is up to you — wrap in a parent div as needed)
192
+ <div className="amount-field">
193
+ <IntlNumberInput
194
+ value={12.34}
195
+ precision={2}
196
+ step={1}
197
+ minValue={0}
198
+ maxValue={100}
199
+ onChange={(event, value) => console.log(value)}
200
+ renderControls={({ increment, decrement, setValue, value, formattedValue, min, max, disabled, readOnly }) => (
201
+ <div className="amount-controls">
202
+ <button type="button" onClick={() => decrement()} disabled={disabled || readOnly}>-</button>
203
+ <span>{formattedValue}</span>
204
+ <button type="button" onClick={() => increment()} disabled={disabled || readOnly}>+</button>
205
+ <button type="button" onClick={() => setValue(max ?? value)} disabled={disabled || readOnly}>
206
+ Max
207
+ </button>
208
+ </div>
209
+ )}
210
+ />
211
+ </div>
212
+ ```
213
+
214
+ ### Programmatic Focus (ref)
215
+
216
+ ```javascript
217
+ import React, { useRef } from 'react';
218
+ import IntlNumberInput from 'react-intl-number-input';
219
+
220
+ function FocusExample() {
221
+ const inputRef = useRef(null);
222
+
223
+ return (
224
+ <>
225
+ <IntlNumberInput ref={inputRef} />
226
+ <button onClick={() => inputRef.current?.focus()}>
227
+ Focus Input
228
+ </button>
229
+ </>
230
+ );
231
+ }
232
+ ```
233
+
234
+ `renderControls` replaces `showStepButtons` when both are provided. The component renders the `<input>` and your controls as siblings (no wrapper), so you control layout in the parent.
235
+
236
+ ## How input works
237
+
238
+ Users type digits; the component applies locale formatting and optional prefix/suffix. For `precision={2}`, typing `1234` becomes `12.34`. Negative values are supported when `-` is present in the input.
239
+
240
+ ASCII digits and the locale's digits are accepted, including Arabic-Indic, Han decimal and supplementary-plane digits. Edits confined to the configured prefix or suffix preserve the amount and restore the affix. Prefixes and suffixes are display adornments; a `%` suffix does not multiply or divide the numeric value.
241
+
242
+ For compatibility, clearing the input produces zero (or the nearest allowed bound). Empty and missing values are not represented separately: `required` cannot detect a missing amount and `placeholder` is normally hidden. Validate absence separately in your form if needed. Bounds apply immediately; typing an intermediate value below the minimum may therefore clamp it before typing is complete.
243
+
244
+ The numeric API uses JavaScript `number`. Precision controls formatting, not arbitrary-precision arithmetic. Stepping avoids floating-point accumulation when the scaled operands fit safe integers; very large values or high precision retain the limitations of JavaScript numbers.
245
+
246
+ ## Contributing
247
+
248
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and publishing instructions.
249
+
250
+ Before publishing, run `npm run check`. It verifies tests, the demo's types, both builds and an installed npm tarball, including its type declarations and runtime entry points.
251
+
252
+ ## Changelog
253
+
254
+ See [CHANGELOG.md](CHANGELOG.md) for release history.
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ export interface ControlsRenderProps {
3
+ increment: (multiplier?: number) => void;
4
+ decrement: (multiplier?: number) => void;
5
+ setValue: (value: number) => void;
6
+ value: number;
7
+ min?: number;
8
+ max?: number;
9
+ disabled: boolean;
10
+ readOnly?: boolean;
11
+ step: number;
12
+ precision: number;
13
+ formattedValue: string;
14
+ }
15
+ type IntlNumberInputOwnProps = {
16
+ locale?: string;
17
+ prefix?: string;
18
+ suffix?: string;
19
+ precision?: number;
20
+ value?: number | string;
21
+ /** Opt in to keeping value authoritative when the parent rejects an edit. */
22
+ controlled?: boolean;
23
+ showStepButtons?: boolean;
24
+ step?: number;
25
+ minValue?: number;
26
+ maxValue?: number;
27
+ inputMode?: 'numeric' | 'decimal';
28
+ onChange?: (event: React.ChangeEvent<HTMLInputElement>, value: number, maskedValue: string) => void;
29
+ onBlur?: (event: React.FocusEvent<HTMLInputElement>, value: number, maskedValue: string) => void;
30
+ renderControls?: (props: ControlsRenderProps) => React.ReactNode;
31
+ };
32
+ export type IntlNumberInputProps = IntlNumberInputOwnProps & Omit<React.ComponentPropsWithoutRef<'input'>, keyof IntlNumberInputOwnProps | 'value' | 'type' | 'defaultValue' | 'children'>;
33
+ declare const _default: React.NamedExoticComponent<IntlNumberInputOwnProps & Omit<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "children" | "defaultValue" | "type" | keyof IntlNumberInputOwnProps> & React.RefAttributes<HTMLInputElement>>;
34
+ export default _default;