react-intl-number-input 0.7.0 → 0.8.1
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 +35 -3
- package/dist/index.d.ts +14 -1
- package/dist/index.js +76 -30
- package/package.json +17 -19
package/README.md
CHANGED
|
@@ -42,7 +42,10 @@ function App() {
|
|
|
42
42
|
### TypeScript
|
|
43
43
|
|
|
44
44
|
```typescript
|
|
45
|
-
import IntlNumberInput, {
|
|
45
|
+
import IntlNumberInput, {
|
|
46
|
+
IntlNumberInputProps,
|
|
47
|
+
ControlsRenderProps,
|
|
48
|
+
} from 'react-intl-number-input';
|
|
46
49
|
|
|
47
50
|
const props: IntlNumberInputProps = {
|
|
48
51
|
locale: 'pt-BR',
|
|
@@ -85,8 +88,9 @@ ReactDOM.render(<App />, document.getElementById('root'));
|
|
|
85
88
|
| autoFocus | `boolean` | — | Native input autofocus |
|
|
86
89
|
| minValue | `number` | — | Minimum allowed value |
|
|
87
90
|
| maxValue | `number` | — | Maximum allowed value |
|
|
88
|
-
| showStepButtons | `boolean` | `false` | Renders +/- step buttons |
|
|
89
|
-
|
|
|
91
|
+
| showStepButtons | `boolean` | `false` | Renders built-in +/- step buttons (ignored when `renderControls` is set) |
|
|
92
|
+
| renderControls | `function` | — | `(props: ControlsRenderProps) => ReactNode` — custom stepper UI |
|
|
93
|
+
| step | `number` | `1` | Step increment in display units (e.g. `1` with `precision={2}` adds `0.01`) |
|
|
90
94
|
| inputMode | `'numeric'` \| `'decimal'` | auto | Overrides mobile keyboard mode |
|
|
91
95
|
| className | `string` | — | Input CSS class |
|
|
92
96
|
| style | `object` | — | Input inline styles |
|
|
@@ -144,6 +148,32 @@ The component also accepts standard `<input>` attributes such as `ref`, `onFocus
|
|
|
144
148
|
/>
|
|
145
149
|
```
|
|
146
150
|
|
|
151
|
+
```javascript
|
|
152
|
+
// Custom controls (layout is up to you — wrap in a parent div as needed)
|
|
153
|
+
<div className="amount-field">
|
|
154
|
+
<IntlNumberInput
|
|
155
|
+
value={12.34}
|
|
156
|
+
precision={2}
|
|
157
|
+
step={1}
|
|
158
|
+
minValue={0}
|
|
159
|
+
maxValue={100}
|
|
160
|
+
onChange={(event, value) => console.log(value)}
|
|
161
|
+
renderControls={({ increment, decrement, setValue, value, formattedValue, min, max, disabled }) => (
|
|
162
|
+
<div className="amount-controls">
|
|
163
|
+
<button type="button" onClick={() => decrement()} disabled={disabled}>-</button>
|
|
164
|
+
<span>{formattedValue}</span>
|
|
165
|
+
<button type="button" onClick={() => increment()} disabled={disabled}>+</button>
|
|
166
|
+
<button type="button" onClick={() => setValue(max ?? value)} disabled={disabled}>
|
|
167
|
+
Max
|
|
168
|
+
</button>
|
|
169
|
+
</div>
|
|
170
|
+
)}
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`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.
|
|
176
|
+
|
|
147
177
|
## How input works
|
|
148
178
|
|
|
149
179
|
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.
|
|
@@ -152,6 +182,8 @@ Users type digits; the component applies locale formatting and optional prefix/s
|
|
|
152
182
|
|
|
153
183
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and publishing instructions.
|
|
154
184
|
|
|
185
|
+
Local development requires Node.js `^22.18.0` or `>=24.11.0` (Babel 8). The published package has no Node.js version constraint for consumers.
|
|
186
|
+
|
|
155
187
|
## Changelog
|
|
156
188
|
|
|
157
189
|
See [CHANGELOG.md](CHANGELOG.md) for release history.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
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
|
+
step: number;
|
|
11
|
+
precision: number;
|
|
12
|
+
formattedValue: string;
|
|
13
|
+
}
|
|
2
14
|
type IntlNumberInputOwnProps = {
|
|
3
15
|
locale?: string;
|
|
4
16
|
prefix?: string;
|
|
@@ -12,7 +24,8 @@ type IntlNumberInputOwnProps = {
|
|
|
12
24
|
inputMode?: 'numeric' | 'decimal';
|
|
13
25
|
onChange?: (event: React.ChangeEvent<HTMLInputElement>, value: number, maskedValue: string) => void;
|
|
14
26
|
onBlur?: (event: React.FocusEvent<HTMLInputElement>, value: number, maskedValue: string) => void;
|
|
27
|
+
renderControls?: (props: ControlsRenderProps) => React.ReactNode;
|
|
15
28
|
};
|
|
16
29
|
export type IntlNumberInputProps = IntlNumberInputOwnProps & Omit<React.ComponentPropsWithoutRef<'input'>, keyof IntlNumberInputOwnProps | 'value' | 'type' | 'defaultValue' | 'children'>;
|
|
17
|
-
declare const _default: React.NamedExoticComponent<IntlNumberInputOwnProps & Omit<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "
|
|
30
|
+
declare const _default: React.NamedExoticComponent<IntlNumberInputOwnProps & Omit<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "children" | "defaultValue" | "type" | keyof IntlNumberInputOwnProps> & React.RefAttributes<HTMLInputElement>>;
|
|
18
31
|
export default _default;
|
package/dist/index.js
CHANGED
|
@@ -5,11 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
-
const _excluded = ["locale", "prefix", "suffix", "precision", "value", "showStepButtons", "step", "minValue", "maxValue", "inputMode", "onChange", "onBlur", "disabled"];
|
|
9
8
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
10
|
-
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
11
|
-
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
|
|
12
|
-
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
13
9
|
function sanitizeString(str) {
|
|
14
10
|
return str.replace(/<[^>]*>/g, '');
|
|
15
11
|
}
|
|
@@ -37,23 +33,23 @@ function clampToBounds(value, min, max) {
|
|
|
37
33
|
}
|
|
38
34
|
return result;
|
|
39
35
|
}
|
|
40
|
-
const IntlNumberInput = /*#__PURE__*/(0, _react.forwardRef)(function IntlNumberInput(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
36
|
+
const IntlNumberInput = /*#__PURE__*/(0, _react.forwardRef)(function IntlNumberInput({
|
|
37
|
+
locale = 'en-US',
|
|
38
|
+
prefix = '',
|
|
39
|
+
suffix = '',
|
|
40
|
+
precision = 2,
|
|
41
|
+
value,
|
|
42
|
+
showStepButtons = false,
|
|
43
|
+
step = 1,
|
|
44
|
+
minValue,
|
|
45
|
+
maxValue,
|
|
46
|
+
inputMode: inputModeProp,
|
|
47
|
+
onChange,
|
|
48
|
+
onBlur,
|
|
49
|
+
disabled = false,
|
|
50
|
+
renderControls,
|
|
51
|
+
...inputProps
|
|
52
|
+
}, ref) {
|
|
57
53
|
const safePrecision = (0, _react.useMemo)(() => {
|
|
58
54
|
if (typeof precision !== 'number' || Number.isNaN(precision)) return 2;
|
|
59
55
|
return clamp(Math.round(precision), 0, 20);
|
|
@@ -111,10 +107,22 @@ const IntlNumberInput = /*#__PURE__*/(0, _react.forwardRef)(function IntlNumberI
|
|
|
111
107
|
const initial = value !== undefined ? value : 0;
|
|
112
108
|
return formatNumber(initial);
|
|
113
109
|
}, [value, formatNumber]);
|
|
110
|
+
const getInitialNumericValue = (0, _react.useCallback)(() => {
|
|
111
|
+
const initial = value !== undefined ? value : 0;
|
|
112
|
+
if (typeof initial === 'string') {
|
|
113
|
+
return getNumberValue(initial);
|
|
114
|
+
}
|
|
115
|
+
return initial;
|
|
116
|
+
}, [value, getNumberValue]);
|
|
114
117
|
const [maskedValue, setMaskedValue] = (0, _react.useState)(getInitialValue);
|
|
118
|
+
const numericValueRef = (0, _react.useRef)(getInitialNumericValue());
|
|
115
119
|
(0, _react.useEffect)(() => {
|
|
116
|
-
|
|
117
|
-
|
|
120
|
+
if (value === undefined) return;
|
|
121
|
+
const numericVal = typeof value === 'string' ? getNumberValue(value) : value;
|
|
122
|
+
const clamped = clampToBounds(numericVal, effectiveMin, effectiveMax);
|
|
123
|
+
setMaskedValue(formatNumber(clamped));
|
|
124
|
+
numericValueRef.current = clamped;
|
|
125
|
+
}, [value, formatNumber, getNumberValue, effectiveMin, effectiveMax]);
|
|
118
126
|
const handleChange = (0, _react.useCallback)(event => {
|
|
119
127
|
const rawValue = event.target.value;
|
|
120
128
|
const newValue = getNumberValue(rawValue);
|
|
@@ -124,6 +132,7 @@ const IntlNumberInput = /*#__PURE__*/(0, _react.forwardRef)(function IntlNumberI
|
|
|
124
132
|
const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);
|
|
125
133
|
const newMaskedValue = formatNumber(clampedValue);
|
|
126
134
|
setMaskedValue(newMaskedValue);
|
|
135
|
+
numericValueRef.current = clampedValue;
|
|
127
136
|
if (onChange) {
|
|
128
137
|
onChange(event, clampedValue, newMaskedValue);
|
|
129
138
|
}
|
|
@@ -134,17 +143,19 @@ const IntlNumberInput = /*#__PURE__*/(0, _react.forwardRef)(function IntlNumberI
|
|
|
134
143
|
const clampedValue = clampToBounds(currentValue, effectiveMin, effectiveMax);
|
|
135
144
|
const formattedValue = formatNumber(clampedValue);
|
|
136
145
|
setMaskedValue(formattedValue);
|
|
146
|
+
numericValueRef.current = clampedValue;
|
|
137
147
|
if (onBlur) {
|
|
138
148
|
onBlur(event, clampedValue, formattedValue);
|
|
139
149
|
}
|
|
140
150
|
}, [getNumberValue, formatNumber, effectiveMin, effectiveMax, onBlur]);
|
|
141
|
-
const handleStep = (0, _react.useCallback)(delta => {
|
|
151
|
+
const handleStep = (0, _react.useCallback)((delta, multiplier = 1) => {
|
|
142
152
|
if (disabled) return;
|
|
143
|
-
const currentValue =
|
|
144
|
-
const stepValue = safeStep
|
|
153
|
+
const currentValue = numericValueRef.current;
|
|
154
|
+
const stepValue = safeStep / Math.pow(10, safePrecision) * multiplier;
|
|
145
155
|
const newValue = clampToBounds(currentValue + delta * stepValue, effectiveMin, effectiveMax);
|
|
146
156
|
const newMaskedValue = formatNumber(newValue);
|
|
147
157
|
setMaskedValue(newMaskedValue);
|
|
158
|
+
numericValueRef.current = newValue;
|
|
148
159
|
const syntheticEvent = {
|
|
149
160
|
target: {
|
|
150
161
|
value: newMaskedValue,
|
|
@@ -156,22 +167,57 @@ const IntlNumberInput = /*#__PURE__*/(0, _react.forwardRef)(function IntlNumberI
|
|
|
156
167
|
if (onChange) {
|
|
157
168
|
onChange(syntheticEvent, newValue, newMaskedValue);
|
|
158
169
|
}
|
|
159
|
-
}, [disabled,
|
|
170
|
+
}, [disabled, safeStep, safePrecision, effectiveMin, effectiveMax, formatNumber, onChange, inputProps.name, inputProps.id]);
|
|
171
|
+
const increment = (0, _react.useCallback)((multiplier = 1) => handleStep(1, multiplier), [handleStep]);
|
|
172
|
+
const decrement = (0, _react.useCallback)((multiplier = 1) => handleStep(-1, multiplier), [handleStep]);
|
|
173
|
+
const setValue = (0, _react.useCallback)(newValue => {
|
|
174
|
+
const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);
|
|
175
|
+
const newMaskedValue = formatNumber(clampedValue);
|
|
176
|
+
setMaskedValue(newMaskedValue);
|
|
177
|
+
numericValueRef.current = clampedValue;
|
|
178
|
+
const syntheticEvent = {
|
|
179
|
+
target: {
|
|
180
|
+
value: newMaskedValue,
|
|
181
|
+
name: inputProps.name || '',
|
|
182
|
+
id: inputProps.id || ''
|
|
183
|
+
},
|
|
184
|
+
preventDefault: () => {}
|
|
185
|
+
};
|
|
186
|
+
if (onChange) {
|
|
187
|
+
onChange(syntheticEvent, clampedValue, newMaskedValue);
|
|
188
|
+
}
|
|
189
|
+
}, [effectiveMin, effectiveMax, formatNumber, onChange, inputProps.name, inputProps.id]);
|
|
160
190
|
const inputMode = (0, _react.useMemo)(() => {
|
|
161
191
|
if (inputModeProp) {
|
|
162
192
|
return inputModeProp;
|
|
163
193
|
}
|
|
164
194
|
return safePrecision > 0 ? 'decimal' : 'numeric';
|
|
165
195
|
}, [inputModeProp, safePrecision]);
|
|
166
|
-
const inputElement = /*#__PURE__*/_react.default.createElement("input",
|
|
196
|
+
const inputElement = /*#__PURE__*/_react.default.createElement("input", {
|
|
167
197
|
ref: ref,
|
|
168
198
|
type: "text",
|
|
169
199
|
inputMode: inputMode,
|
|
170
200
|
value: maskedValue,
|
|
171
201
|
disabled: disabled,
|
|
172
202
|
onChange: handleChange,
|
|
173
|
-
onBlur: handleBlur
|
|
174
|
-
|
|
203
|
+
onBlur: handleBlur,
|
|
204
|
+
...inputProps
|
|
205
|
+
});
|
|
206
|
+
if (renderControls) {
|
|
207
|
+
const controlsProps = {
|
|
208
|
+
increment,
|
|
209
|
+
decrement,
|
|
210
|
+
setValue,
|
|
211
|
+
value: numericValueRef.current,
|
|
212
|
+
min: effectiveMin,
|
|
213
|
+
max: effectiveMax,
|
|
214
|
+
disabled,
|
|
215
|
+
step: safeStep,
|
|
216
|
+
precision: safePrecision,
|
|
217
|
+
formattedValue: maskedValue
|
|
218
|
+
};
|
|
219
|
+
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, inputElement, renderControls(controlsProps));
|
|
220
|
+
}
|
|
175
221
|
if (!showStepButtons) {
|
|
176
222
|
return inputElement;
|
|
177
223
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-intl-number-input",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "A React component for masked and formatted number input.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"sideEffects": false,
|
|
25
25
|
"scripts": {
|
|
26
26
|
"test": "jest --runInBand",
|
|
27
|
+
"dev": "webpack serve --mode development",
|
|
27
28
|
"start": "webpack serve --mode development",
|
|
28
29
|
"transpile": "babel src -d dist --extensions \".ts,.tsx\" --ignore \"**/*.test.ts\" --ignore \"**/*.test.tsx\"",
|
|
29
30
|
"build:types": "tsc --emitDeclarationOnly",
|
|
@@ -58,32 +59,29 @@
|
|
|
58
59
|
"react": ">=16.8.0 <20"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|
|
61
|
-
"@babel/cli": "^
|
|
62
|
-
"@babel/core": "^
|
|
63
|
-
"@babel/
|
|
64
|
-
"@babel/preset-
|
|
65
|
-
"@babel/preset-
|
|
66
|
-
"@
|
|
67
|
-
"@testing-library/jest-dom": "^6.9.1",
|
|
62
|
+
"@babel/cli": "^8.0.4",
|
|
63
|
+
"@babel/core": "^8.0.1",
|
|
64
|
+
"@babel/preset-env": "^8.0.2",
|
|
65
|
+
"@babel/preset-react": "^8.0.1",
|
|
66
|
+
"@babel/preset-typescript": "^8.0.1",
|
|
67
|
+
"@testing-library/jest-dom": "^7.0.1",
|
|
68
68
|
"@testing-library/react": "^16.3.2",
|
|
69
|
-
"@types/react": "^19.2.
|
|
69
|
+
"@types/react": "^19.2.18",
|
|
70
70
|
"babel-jest": "^30.4.1",
|
|
71
71
|
"babel-loader": "^10.1.1",
|
|
72
72
|
"gh-pages": "^6.3.0",
|
|
73
|
-
"html-webpack-plugin": "^5.6.
|
|
73
|
+
"html-webpack-plugin": "^5.6.8",
|
|
74
74
|
"jest": "^30.4.2",
|
|
75
75
|
"jest-environment-jsdom": "^30.4.1",
|
|
76
|
-
"react": "^19.2.
|
|
77
|
-
"react-dom": "^19.2.
|
|
78
|
-
"typescript": "^
|
|
79
|
-
"webpack": "^5.
|
|
80
|
-
"webpack-cli": "^7.
|
|
81
|
-
"webpack-dev-server": "^
|
|
76
|
+
"react": "^19.2.8",
|
|
77
|
+
"react-dom": "^19.2.8",
|
|
78
|
+
"typescript": "^7.0.2",
|
|
79
|
+
"webpack": "^5.109.2",
|
|
80
|
+
"webpack-cli": "^7.2.2",
|
|
81
|
+
"webpack-dev-server": "^6.0.0"
|
|
82
82
|
},
|
|
83
83
|
"overrides": {
|
|
84
84
|
"fast-uri": "^3.1.1",
|
|
85
|
-
"
|
|
86
|
-
"js-yaml": "^4.1.1",
|
|
87
|
-
"uuid": "^11.1.1"
|
|
85
|
+
"js-yaml": "^4.1.1"
|
|
88
86
|
}
|
|
89
87
|
}
|