react-intl-number-input 0.7.0 → 0.8.2
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 +242 -190
- package/package.json +14 -32
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 20+. 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
|
@@ -1,207 +1,259 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
5
17
|
});
|
|
6
|
-
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const react_1 = __importStar(require("react"));
|
|
13
37
|
function sanitizeString(str) {
|
|
14
|
-
|
|
38
|
+
return str.replace(/<[^>]*>/g, '');
|
|
15
39
|
}
|
|
16
40
|
function resolveLocale(locale) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
41
|
+
try {
|
|
42
|
+
const supported = Intl.NumberFormat.supportedLocalesOf([locale]);
|
|
43
|
+
return supported.length > 0 ? supported[0] : 'en-US';
|
|
44
|
+
}
|
|
45
|
+
catch (_a) {
|
|
46
|
+
return 'en-US';
|
|
47
|
+
}
|
|
23
48
|
}
|
|
24
49
|
function isValidNumber(value) {
|
|
25
|
-
|
|
50
|
+
return Number.isFinite(value) && !Number.isNaN(value);
|
|
26
51
|
}
|
|
27
52
|
function clamp(value, min, max) {
|
|
28
|
-
|
|
53
|
+
return Math.min(Math.max(value, min), max);
|
|
29
54
|
}
|
|
30
55
|
function clampToBounds(value, min, max) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
if (max !== undefined && result > max) {
|
|
36
|
-
result = max;
|
|
37
|
-
}
|
|
38
|
-
return result;
|
|
39
|
-
}
|
|
40
|
-
const IntlNumberInput = /*#__PURE__*/(0, _react.forwardRef)(function IntlNumberInput(_ref, ref) {
|
|
41
|
-
let {
|
|
42
|
-
locale = 'en-US',
|
|
43
|
-
prefix = '',
|
|
44
|
-
suffix = '',
|
|
45
|
-
precision = 2,
|
|
46
|
-
value,
|
|
47
|
-
showStepButtons = false,
|
|
48
|
-
step = 1,
|
|
49
|
-
minValue,
|
|
50
|
-
maxValue,
|
|
51
|
-
inputMode: inputModeProp,
|
|
52
|
-
onChange,
|
|
53
|
-
onBlur,
|
|
54
|
-
disabled = false
|
|
55
|
-
} = _ref,
|
|
56
|
-
inputProps = _objectWithoutProperties(_ref, _excluded);
|
|
57
|
-
const safePrecision = (0, _react.useMemo)(() => {
|
|
58
|
-
if (typeof precision !== 'number' || Number.isNaN(precision)) return 2;
|
|
59
|
-
return clamp(Math.round(precision), 0, 20);
|
|
60
|
-
}, [precision]);
|
|
61
|
-
const safeStep = (0, _react.useMemo)(() => {
|
|
62
|
-
if (typeof step !== 'number' || Number.isNaN(step) || step <= 0) return 1;
|
|
63
|
-
return step;
|
|
64
|
-
}, [step]);
|
|
65
|
-
const [effectiveMin, effectiveMax] = (0, _react.useMemo)(() => {
|
|
66
|
-
if (minValue !== undefined && maxValue !== undefined && minValue > maxValue) {
|
|
67
|
-
return [maxValue, minValue];
|
|
68
|
-
}
|
|
69
|
-
return [minValue, maxValue];
|
|
70
|
-
}, [minValue, maxValue]);
|
|
71
|
-
const getPrecisionValue = (0, _react.useCallback)(p => {
|
|
72
|
-
return Math.pow(10, p);
|
|
73
|
-
}, []);
|
|
74
|
-
const getNumberValue = (0, _react.useCallback)(strValue => {
|
|
75
|
-
const inputValue = String(strValue || '');
|
|
76
|
-
const hasNegativeSign = inputValue.includes('-');
|
|
77
|
-
const onlyDigits = inputValue.replace(/[^0-9]/g, '');
|
|
78
|
-
if (!onlyDigits) {
|
|
79
|
-
return hasNegativeSign ? -0 : 0;
|
|
80
|
-
}
|
|
81
|
-
const numberValue = Number(onlyDigits);
|
|
82
|
-
const parsedValue = numberValue / getPrecisionValue(safePrecision);
|
|
83
|
-
return hasNegativeSign ? -parsedValue : parsedValue;
|
|
84
|
-
}, [getPrecisionValue, safePrecision]);
|
|
85
|
-
const numberFormatter = (0, _react.useMemo)(() => {
|
|
86
|
-
const options = {
|
|
87
|
-
style: 'decimal',
|
|
88
|
-
minimumFractionDigits: safePrecision,
|
|
89
|
-
maximumFractionDigits: safePrecision
|
|
90
|
-
};
|
|
91
|
-
const resolvedLocale = resolveLocale(locale);
|
|
92
|
-
return new Intl.NumberFormat(resolvedLocale, options);
|
|
93
|
-
}, [locale, safePrecision]);
|
|
94
|
-
const formatNumber = (0, _react.useCallback)(val => {
|
|
95
|
-
let numberValue;
|
|
96
|
-
if (typeof val === 'string') {
|
|
97
|
-
numberValue = getNumberValue(val);
|
|
98
|
-
} else {
|
|
99
|
-
numberValue = val;
|
|
56
|
+
let result = value;
|
|
57
|
+
if (min !== undefined && result < min) {
|
|
58
|
+
result = min;
|
|
100
59
|
}
|
|
101
|
-
if (
|
|
102
|
-
|
|
60
|
+
if (max !== undefined && result > max) {
|
|
61
|
+
result = max;
|
|
103
62
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
const IntlNumberInput = (0, react_1.forwardRef)(function IntlNumberInput({ locale = 'en-US', prefix = '', suffix = '', precision = 2, value, showStepButtons = false, step = 1, minValue, maxValue, inputMode: inputModeProp, onChange, onBlur, disabled = false, renderControls, ...inputProps }, ref) {
|
|
66
|
+
const safePrecision = (0, react_1.useMemo)(() => {
|
|
67
|
+
if (typeof precision !== 'number' || Number.isNaN(precision))
|
|
68
|
+
return 2;
|
|
69
|
+
return clamp(Math.round(precision), 0, 20);
|
|
70
|
+
}, [precision]);
|
|
71
|
+
const safeStep = (0, react_1.useMemo)(() => {
|
|
72
|
+
if (typeof step !== 'number' || Number.isNaN(step) || step <= 0)
|
|
73
|
+
return 1;
|
|
74
|
+
return step;
|
|
75
|
+
}, [step]);
|
|
76
|
+
const [effectiveMin, effectiveMax] = (0, react_1.useMemo)(() => {
|
|
77
|
+
if (minValue !== undefined && maxValue !== undefined && minValue > maxValue) {
|
|
78
|
+
return [maxValue, minValue];
|
|
79
|
+
}
|
|
80
|
+
return [minValue, maxValue];
|
|
81
|
+
}, [minValue, maxValue]);
|
|
82
|
+
const getPrecisionValue = (0, react_1.useCallback)((p) => {
|
|
83
|
+
return Math.pow(10, p);
|
|
84
|
+
}, []);
|
|
85
|
+
const getNumberValue = (0, react_1.useCallback)((strValue) => {
|
|
86
|
+
const inputValue = String(strValue || '');
|
|
87
|
+
const hasNegativeSign = inputValue.includes('-');
|
|
88
|
+
const onlyDigits = inputValue.replace(/[^0-9]/g, '');
|
|
89
|
+
if (!onlyDigits) {
|
|
90
|
+
return hasNegativeSign ? -0 : 0;
|
|
91
|
+
}
|
|
92
|
+
const numberValue = Number(onlyDigits);
|
|
93
|
+
const parsedValue = numberValue / getPrecisionValue(safePrecision);
|
|
94
|
+
return hasNegativeSign ? -parsedValue : parsedValue;
|
|
95
|
+
}, [getPrecisionValue, safePrecision]);
|
|
96
|
+
const numberFormatter = (0, react_1.useMemo)(() => {
|
|
97
|
+
const options = {
|
|
98
|
+
style: 'decimal',
|
|
99
|
+
minimumFractionDigits: safePrecision,
|
|
100
|
+
maximumFractionDigits: safePrecision,
|
|
101
|
+
};
|
|
102
|
+
const resolvedLocale = resolveLocale(locale);
|
|
103
|
+
return new Intl.NumberFormat(resolvedLocale, options);
|
|
104
|
+
}, [locale, safePrecision]);
|
|
105
|
+
const formatNumber = (0, react_1.useCallback)((val) => {
|
|
106
|
+
let numberValue;
|
|
107
|
+
if (typeof val === 'string') {
|
|
108
|
+
numberValue = getNumberValue(val);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
numberValue = val;
|
|
112
|
+
}
|
|
113
|
+
if (!isValidNumber(numberValue)) {
|
|
114
|
+
numberValue = 0;
|
|
115
|
+
}
|
|
116
|
+
numberValue = clampToBounds(numberValue, effectiveMin, effectiveMax);
|
|
117
|
+
const formattedNumber = numberFormatter.format(numberValue);
|
|
118
|
+
const safePrefix = sanitizeString(prefix);
|
|
119
|
+
const safeSuffix = sanitizeString(suffix);
|
|
120
|
+
return `${safePrefix}${formattedNumber}${safeSuffix}`;
|
|
121
|
+
}, [getNumberValue, numberFormatter, prefix, suffix, effectiveMin, effectiveMax]);
|
|
122
|
+
const getInitialValue = (0, react_1.useCallback)(() => {
|
|
123
|
+
const initial = value !== undefined ? value : 0;
|
|
124
|
+
return formatNumber(initial);
|
|
125
|
+
}, [value, formatNumber]);
|
|
126
|
+
const getInitialNumericValue = (0, react_1.useCallback)(() => {
|
|
127
|
+
const initial = value !== undefined ? value : 0;
|
|
128
|
+
if (typeof initial === 'string') {
|
|
129
|
+
return getNumberValue(initial);
|
|
130
|
+
}
|
|
131
|
+
return initial;
|
|
132
|
+
}, [value, getNumberValue]);
|
|
133
|
+
const [maskedValue, setMaskedValue] = (0, react_1.useState)(getInitialValue);
|
|
134
|
+
const numericValueRef = (0, react_1.useRef)(getInitialNumericValue());
|
|
135
|
+
(0, react_1.useEffect)(() => {
|
|
136
|
+
if (value === undefined)
|
|
137
|
+
return;
|
|
138
|
+
const numericVal = typeof value === 'string' ? getNumberValue(value) : value;
|
|
139
|
+
const clamped = clampToBounds(numericVal, effectiveMin, effectiveMax);
|
|
140
|
+
setMaskedValue(formatNumber(clamped));
|
|
141
|
+
numericValueRef.current = clamped;
|
|
142
|
+
}, [value, formatNumber, getNumberValue, effectiveMin, effectiveMax]);
|
|
143
|
+
const handleChange = (0, react_1.useCallback)((event) => {
|
|
144
|
+
const rawValue = event.target.value;
|
|
145
|
+
const newValue = getNumberValue(rawValue);
|
|
146
|
+
if (!isValidNumber(newValue)) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);
|
|
150
|
+
const newMaskedValue = formatNumber(clampedValue);
|
|
151
|
+
setMaskedValue(newMaskedValue);
|
|
152
|
+
numericValueRef.current = clampedValue;
|
|
153
|
+
if (onChange) {
|
|
154
|
+
onChange(event, clampedValue, newMaskedValue);
|
|
155
|
+
}
|
|
156
|
+
}, [getNumberValue, formatNumber, effectiveMin, effectiveMax, onChange]);
|
|
157
|
+
const handleBlur = (0, react_1.useCallback)((event) => {
|
|
158
|
+
const rawValue = event.target.value;
|
|
159
|
+
const currentValue = getNumberValue(rawValue);
|
|
160
|
+
const clampedValue = clampToBounds(currentValue, effectiveMin, effectiveMax);
|
|
161
|
+
const formattedValue = formatNumber(clampedValue);
|
|
162
|
+
setMaskedValue(formattedValue);
|
|
163
|
+
numericValueRef.current = clampedValue;
|
|
164
|
+
if (onBlur) {
|
|
165
|
+
onBlur(event, clampedValue, formattedValue);
|
|
166
|
+
}
|
|
167
|
+
}, [getNumberValue, formatNumber, effectiveMin, effectiveMax, onBlur]);
|
|
168
|
+
const handleStep = (0, react_1.useCallback)((delta, multiplier = 1) => {
|
|
169
|
+
if (disabled)
|
|
170
|
+
return;
|
|
171
|
+
const currentValue = numericValueRef.current;
|
|
172
|
+
const stepValue = (safeStep / Math.pow(10, safePrecision)) * multiplier;
|
|
173
|
+
const newValue = clampToBounds(currentValue + delta * stepValue, effectiveMin, effectiveMax);
|
|
174
|
+
const newMaskedValue = formatNumber(newValue);
|
|
175
|
+
setMaskedValue(newMaskedValue);
|
|
176
|
+
numericValueRef.current = newValue;
|
|
177
|
+
const syntheticEvent = {
|
|
178
|
+
target: {
|
|
179
|
+
value: newMaskedValue,
|
|
180
|
+
name: inputProps.name || '',
|
|
181
|
+
id: inputProps.id || '',
|
|
182
|
+
},
|
|
183
|
+
preventDefault: () => { },
|
|
184
|
+
};
|
|
185
|
+
if (onChange) {
|
|
186
|
+
onChange(syntheticEvent, newValue, newMaskedValue);
|
|
187
|
+
}
|
|
188
|
+
}, [
|
|
189
|
+
disabled,
|
|
190
|
+
safeStep,
|
|
191
|
+
safePrecision,
|
|
192
|
+
effectiveMin,
|
|
193
|
+
effectiveMax,
|
|
194
|
+
formatNumber,
|
|
195
|
+
onChange,
|
|
196
|
+
inputProps.name,
|
|
197
|
+
inputProps.id,
|
|
198
|
+
]);
|
|
199
|
+
const increment = (0, react_1.useCallback)((multiplier = 1) => handleStep(1, multiplier), [handleStep]);
|
|
200
|
+
const decrement = (0, react_1.useCallback)((multiplier = 1) => handleStep(-1, multiplier), [handleStep]);
|
|
201
|
+
const setValue = (0, react_1.useCallback)((newValue) => {
|
|
202
|
+
const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);
|
|
203
|
+
const newMaskedValue = formatNumber(clampedValue);
|
|
204
|
+
setMaskedValue(newMaskedValue);
|
|
205
|
+
numericValueRef.current = clampedValue;
|
|
206
|
+
const syntheticEvent = {
|
|
207
|
+
target: {
|
|
208
|
+
value: newMaskedValue,
|
|
209
|
+
name: inputProps.name || '',
|
|
210
|
+
id: inputProps.id || '',
|
|
211
|
+
},
|
|
212
|
+
preventDefault: () => { },
|
|
213
|
+
};
|
|
214
|
+
if (onChange) {
|
|
215
|
+
onChange(syntheticEvent, clampedValue, newMaskedValue);
|
|
216
|
+
}
|
|
217
|
+
}, [effectiveMin, effectiveMax, formatNumber, onChange, inputProps.name, inputProps.id]);
|
|
218
|
+
const inputMode = (0, react_1.useMemo)(() => {
|
|
219
|
+
if (inputModeProp) {
|
|
220
|
+
return inputModeProp;
|
|
221
|
+
}
|
|
222
|
+
return safePrecision > 0 ? 'decimal' : 'numeric';
|
|
223
|
+
}, [inputModeProp, safePrecision]);
|
|
224
|
+
const inputElement = (react_1.default.createElement("input", { ref: ref, type: "text", inputMode: inputMode, value: maskedValue, disabled: disabled, onChange: handleChange, onBlur: handleBlur, ...inputProps }));
|
|
225
|
+
if (renderControls) {
|
|
226
|
+
const controlsProps = {
|
|
227
|
+
increment,
|
|
228
|
+
decrement,
|
|
229
|
+
setValue,
|
|
230
|
+
value: numericValueRef.current,
|
|
231
|
+
min: effectiveMin,
|
|
232
|
+
max: effectiveMax,
|
|
233
|
+
disabled,
|
|
234
|
+
step: safeStep,
|
|
235
|
+
precision: safePrecision,
|
|
236
|
+
formattedValue: maskedValue,
|
|
237
|
+
};
|
|
238
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
239
|
+
inputElement,
|
|
240
|
+
renderControls(controlsProps)));
|
|
193
241
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
onClick: () => handleStep(1),
|
|
197
|
-
disabled: disabled,
|
|
198
|
-
"aria-label": "Increase value",
|
|
199
|
-
style: {
|
|
200
|
-
padding: '4px 8px',
|
|
201
|
-
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
202
|
-
opacity: disabled ? 0.5 : 1
|
|
242
|
+
if (!showStepButtons) {
|
|
243
|
+
return inputElement;
|
|
203
244
|
}
|
|
204
|
-
|
|
245
|
+
return (react_1.default.createElement("div", { style: { display: 'inline-flex', alignItems: 'center', gap: '4px' } },
|
|
246
|
+
react_1.default.createElement("button", { type: "button", onClick: () => handleStep(-1), disabled: disabled, "aria-label": "Decrease value", style: {
|
|
247
|
+
padding: '4px 8px',
|
|
248
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
249
|
+
opacity: disabled ? 0.5 : 1,
|
|
250
|
+
} }, "-"),
|
|
251
|
+
inputElement,
|
|
252
|
+
react_1.default.createElement("button", { type: "button", onClick: () => handleStep(1), disabled: disabled, "aria-label": "Increase value", style: {
|
|
253
|
+
padding: '4px 8px',
|
|
254
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
255
|
+
opacity: disabled ? 0.5 : 1,
|
|
256
|
+
} }, "+")));
|
|
205
257
|
});
|
|
206
258
|
IntlNumberInput.displayName = 'IntlNumberInput';
|
|
207
|
-
|
|
259
|
+
exports.default = (0, react_1.memo)(IntlNumberInput);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-intl-number-input",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.2",
|
|
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",
|
|
@@ -11,11 +11,6 @@
|
|
|
11
11
|
"default": "./dist/index.js"
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
|
-
"browserslist": [
|
|
15
|
-
"> 0.5%",
|
|
16
|
-
"last 2 versions",
|
|
17
|
-
"not dead"
|
|
18
|
-
],
|
|
19
14
|
"files": [
|
|
20
15
|
"dist",
|
|
21
16
|
"README.md",
|
|
@@ -24,11 +19,12 @@
|
|
|
24
19
|
"sideEffects": false,
|
|
25
20
|
"scripts": {
|
|
26
21
|
"test": "jest --runInBand",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
22
|
+
"dev": "node scripts/demo.mjs",
|
|
23
|
+
"start": "node scripts/demo.mjs",
|
|
24
|
+
"transpile": "tsc",
|
|
25
|
+
"build:lib": "tsc",
|
|
26
|
+
"prepublishOnly": "npm test && npm run build:lib",
|
|
27
|
+
"build": "node scripts/demo.mjs --production",
|
|
32
28
|
"deploy": "gh-pages -d example/dist",
|
|
33
29
|
"publish-demo": "npm run build && npm run deploy"
|
|
34
30
|
},
|
|
@@ -58,32 +54,18 @@
|
|
|
58
54
|
"react": ">=16.8.0 <20"
|
|
59
55
|
},
|
|
60
56
|
"devDependencies": {
|
|
61
|
-
"@
|
|
62
|
-
"@babel/core": "^7.29.7",
|
|
63
|
-
"@babel/plugin-transform-object-rest-spread": "^7.29.7",
|
|
64
|
-
"@babel/preset-env": "^7.29.7",
|
|
65
|
-
"@babel/preset-react": "^7.29.7",
|
|
66
|
-
"@babel/preset-typescript": "^7.29.7",
|
|
67
|
-
"@testing-library/jest-dom": "^6.9.1",
|
|
57
|
+
"@testing-library/jest-dom": "^7.0.1",
|
|
68
58
|
"@testing-library/react": "^16.3.2",
|
|
69
|
-
"@types/react": "^19.2.
|
|
70
|
-
"
|
|
71
|
-
"babel-loader": "^10.1.1",
|
|
59
|
+
"@types/react": "^19.2.18",
|
|
60
|
+
"esbuild": "^0.28.2",
|
|
72
61
|
"gh-pages": "^6.3.0",
|
|
73
|
-
"html-webpack-plugin": "^5.6.7",
|
|
74
62
|
"jest": "^30.4.2",
|
|
75
63
|
"jest-environment-jsdom": "^30.4.1",
|
|
76
|
-
"react": "^19.2.
|
|
77
|
-
"react-dom": "^19.2.
|
|
78
|
-
"typescript": "^
|
|
79
|
-
"webpack": "^5.107.2",
|
|
80
|
-
"webpack-cli": "^7.0.3",
|
|
81
|
-
"webpack-dev-server": "^5.2.5"
|
|
64
|
+
"react": "^19.2.8",
|
|
65
|
+
"react-dom": "^19.2.8",
|
|
66
|
+
"typescript": "^7.0.2"
|
|
82
67
|
},
|
|
83
68
|
"overrides": {
|
|
84
|
-
"
|
|
85
|
-
"follow-redirects": "^1.16.0",
|
|
86
|
-
"js-yaml": "^4.1.1",
|
|
87
|
-
"uuid": "^11.1.1"
|
|
69
|
+
"js-yaml": "4.3.1"
|
|
88
70
|
}
|
|
89
71
|
}
|