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 CHANGED
@@ -42,7 +42,10 @@ function App() {
42
42
  ### TypeScript
43
43
 
44
44
  ```typescript
45
- import IntlNumberInput, { IntlNumberInputProps } from 'react-intl-number-input';
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
- | step | `number` | `1` | Step increment for buttons |
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">, "type" | "defaultValue" | "children" | keyof IntlNumberInputOwnProps> & React.RefAttributes<HTMLInputElement>>;
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
- Object.defineProperty(exports, "__esModule", {
4
- value: true
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
- exports.default = void 0;
7
- var _react = _interopRequireWildcard(require("react"));
8
- const _excluded = ["locale", "prefix", "suffix", "precision", "value", "showStepButtons", "step", "minValue", "maxValue", "inputMode", "onChange", "onBlur", "disabled"];
9
- 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; }
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
- return str.replace(/<[^>]*>/g, '');
38
+ return str.replace(/<[^>]*>/g, '');
15
39
  }
16
40
  function resolveLocale(locale) {
17
- try {
18
- const supported = Intl.NumberFormat.supportedLocalesOf([locale]);
19
- return supported.length > 0 ? supported[0] : 'en-US';
20
- } catch {
21
- return 'en-US';
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
- return Number.isFinite(value) && !Number.isNaN(value);
50
+ return Number.isFinite(value) && !Number.isNaN(value);
26
51
  }
27
52
  function clamp(value, min, max) {
28
- return Math.min(Math.max(value, min), max);
53
+ return Math.min(Math.max(value, min), max);
29
54
  }
30
55
  function clampToBounds(value, min, max) {
31
- let result = value;
32
- if (min !== undefined && result < min) {
33
- result = min;
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 (!isValidNumber(numberValue)) {
102
- numberValue = 0;
60
+ if (max !== undefined && result > max) {
61
+ result = max;
103
62
  }
104
- numberValue = clampToBounds(numberValue, effectiveMin, effectiveMax);
105
- const formattedNumber = numberFormatter.format(numberValue);
106
- const safePrefix = sanitizeString(prefix);
107
- const safeSuffix = sanitizeString(suffix);
108
- return `${safePrefix}${formattedNumber}${safeSuffix}`;
109
- }, [getNumberValue, numberFormatter, prefix, suffix, effectiveMin, effectiveMax]);
110
- const getInitialValue = (0, _react.useCallback)(() => {
111
- const initial = value !== undefined ? value : 0;
112
- return formatNumber(initial);
113
- }, [value, formatNumber]);
114
- const [maskedValue, setMaskedValue] = (0, _react.useState)(getInitialValue);
115
- (0, _react.useEffect)(() => {
116
- setMaskedValue(formatNumber(value ?? 0));
117
- }, [value, formatNumber]);
118
- const handleChange = (0, _react.useCallback)(event => {
119
- const rawValue = event.target.value;
120
- const newValue = getNumberValue(rawValue);
121
- if (!isValidNumber(newValue)) {
122
- return;
123
- }
124
- const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);
125
- const newMaskedValue = formatNumber(clampedValue);
126
- setMaskedValue(newMaskedValue);
127
- if (onChange) {
128
- onChange(event, clampedValue, newMaskedValue);
129
- }
130
- }, [getNumberValue, formatNumber, effectiveMin, effectiveMax, onChange]);
131
- const handleBlur = (0, _react.useCallback)(event => {
132
- const rawValue = event.target.value;
133
- const currentValue = getNumberValue(rawValue);
134
- const clampedValue = clampToBounds(currentValue, effectiveMin, effectiveMax);
135
- const formattedValue = formatNumber(clampedValue);
136
- setMaskedValue(formattedValue);
137
- if (onBlur) {
138
- onBlur(event, clampedValue, formattedValue);
139
- }
140
- }, [getNumberValue, formatNumber, effectiveMin, effectiveMax, onBlur]);
141
- const handleStep = (0, _react.useCallback)(delta => {
142
- if (disabled) return;
143
- const currentValue = getNumberValue(maskedValue);
144
- const stepValue = safeStep * Math.pow(10, safePrecision);
145
- const newValue = clampToBounds(currentValue + delta * stepValue, effectiveMin, effectiveMax);
146
- const newMaskedValue = formatNumber(newValue);
147
- setMaskedValue(newMaskedValue);
148
- const syntheticEvent = {
149
- target: {
150
- value: newMaskedValue,
151
- name: inputProps.name || '',
152
- id: inputProps.id || ''
153
- },
154
- preventDefault: () => {}
155
- };
156
- if (onChange) {
157
- onChange(syntheticEvent, newValue, newMaskedValue);
158
- }
159
- }, [disabled, getNumberValue, maskedValue, safeStep, safePrecision, effectiveMin, effectiveMax, formatNumber, onChange, inputProps.name, inputProps.id]);
160
- const inputMode = (0, _react.useMemo)(() => {
161
- if (inputModeProp) {
162
- return inputModeProp;
163
- }
164
- return safePrecision > 0 ? 'decimal' : 'numeric';
165
- }, [inputModeProp, safePrecision]);
166
- const inputElement = /*#__PURE__*/_react.default.createElement("input", _extends({
167
- ref: ref,
168
- type: "text",
169
- inputMode: inputMode,
170
- value: maskedValue,
171
- disabled: disabled,
172
- onChange: handleChange,
173
- onBlur: handleBlur
174
- }, inputProps));
175
- if (!showStepButtons) {
176
- return inputElement;
177
- }
178
- return /*#__PURE__*/_react.default.createElement("div", {
179
- style: {
180
- display: 'inline-flex',
181
- alignItems: 'center',
182
- gap: '4px'
183
- }
184
- }, /*#__PURE__*/_react.default.createElement("button", {
185
- type: "button",
186
- onClick: () => handleStep(-1),
187
- disabled: disabled,
188
- "aria-label": "Decrease value",
189
- style: {
190
- padding: '4px 8px',
191
- cursor: disabled ? 'not-allowed' : 'pointer',
192
- opacity: disabled ? 0.5 : 1
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
- }, "-"), inputElement, /*#__PURE__*/_react.default.createElement("button", {
195
- type: "button",
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
- var _default = exports.default = /*#__PURE__*/(0, _react.memo)(IntlNumberInput);
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.7.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
- "start": "webpack serve --mode development",
28
- "transpile": "babel src -d dist --extensions \".ts,.tsx\" --ignore \"**/*.test.ts\" --ignore \"**/*.test.tsx\"",
29
- "build:types": "tsc --emitDeclarationOnly",
30
- "prepublishOnly": "npm test && npm run transpile && npm run build:types",
31
- "build": "webpack --mode production",
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
- "@babel/cli": "^7.29.7",
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.17",
70
- "babel-jest": "^30.4.1",
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.7",
77
- "react-dom": "^19.2.7",
78
- "typescript": "^6.0.3",
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
- "fast-uri": "^3.1.1",
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
  }