react-intl-number-input 0.8.3 → 0.8.5

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/CHANGELOG.md ADDED
@@ -0,0 +1,131 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.8.5] - 2026-09-03
11
+
12
+ ### Fixed
13
+
14
+ - Finalized demo and build scripts migration to TypeScript.
15
+ - Corrected package entry points for better ESM compatibility.
16
+
17
+ ## [0.8.4] - 2026-09-03
18
+
19
+ ### Fixed
20
+
21
+ - Attempted publish (version skipped due to registry timeout).
22
+
23
+ ## [0.8.3] - 2026-09-03
24
+
25
+ ### Added
26
+
27
+ - Dual build support: the package now ships both CommonJS (`dist/index.js`) and ESM (`dist/esm/index.js`) versions.
28
+ - Sourcemaps with inline sources included in the package for better debugging experience.
29
+ - `CHANGELOG.md` and `CONTRIBUTING.md` are now included in the npm package.
30
+ - `engines` field added to `package.json` specifying Node.js >= 20.
31
+ - Proper accessibility support: the input now has `role="spinbutton"` and relevant `aria-valuenow`, `aria-valuemin`, and `aria-valuemax` attributes.
32
+ - Full `forwardRef` support: `ref` now correctly points to the underlying `<input>` element and supports imperative methods via `useImperativeHandle`.
33
+ - Demo and build scripts migrated to TypeScript and modern ES modules.
34
+
35
+ ### Changed
36
+
37
+ - Improved cursor management: typing within the masked input now correctly preserves the cursor position instead of jumping to the end.
38
+ - Enhanced synthetic events: `onChange` events triggered by step buttons or `setValue` now include more standard event properties (`currentTarget`, `persist`, etc.) for better compatibility.
39
+ - Build process updated to use multiple `tsc` configurations for CJS and ESM.
40
+ - `package.json` `exports` updated to support modern ESM resolution while maintaining CJS compatibility.
41
+
42
+ ## [0.8.2] - 2026-08-15
43
+
44
+ This is a maintainer/toolchain release. The published component API, peer dependency (`react >=16.8.0 <20`), and CommonJS entry are unchanged.
45
+
46
+ ### Security
47
+
48
+ - Pinned transitive `js-yaml` to `4.3.1` ([GHSA-5p4m-2wfm-xmqj](https://github.com/advisories/GHSA-5p4m-2wfm-xmqj))
49
+
50
+ ### Changed
51
+
52
+ - Replaced the Babel + Webpack toolchain with TypeScript (`tsc`) for the library and esbuild for the demo
53
+ - Dropped the `fast-uri` override (it only existed for webpack-dev-server)
54
+ - Contributor Node.js requirement is now 20+ (no longer tied to Babel 8)
55
+
56
+ ## [0.8.1] - 2026-08-13
57
+
58
+ ### Changed
59
+
60
+ - Updated development dependencies: React `19.2.8`, webpack `5.109.2`, webpack-cli `7.2.2`, html-webpack-plugin `5.6.8`, `@types/react` `19.2.18`
61
+ - Upgraded `@testing-library/jest-dom` from `6.9.1` to `7.0.1`
62
+
63
+ ## [0.8.0] - 2026-07-27
64
+
65
+ ### Added
66
+
67
+ - `renderControls` render prop with exported `ControlsRenderProps` for custom stepper UI (`increment`, `decrement`, `setValue`, bounds, and formatted value)
68
+
69
+ ### Changed
70
+
71
+ - Step increments now use display units: `step={1}` with `precision={2}` adds `0.01` (previously added `100` to the numeric value)
72
+ - `renderControls` renders the input and controls as siblings without an opinionated wrapper — layout is controlled by the consumer
73
+ - When both `renderControls` and `showStepButtons` are set, `renderControls` takes precedence
74
+ - Development toolchain upgraded: Babel 8, TypeScript 7, Webpack 5.108, webpack-cli 7.2, webpack-dev-server 6
75
+ - Babel config updated for Babel 8 defaults (`modules: "commonjs"`, classic JSX runtime) so the published CJS build remains compatible with React 16.8+
76
+ - Contributor Node.js requirement updated to `^22.18.0 || >=24.11.0` (Babel 8)
77
+
78
+ ### Fixed
79
+
80
+ - Step buttons and custom controls now read the numeric value from an internal ref instead of re-parsing the masked string (fixes incorrect steps with prefix/suffix or locale formatting)
81
+ - Controlled `value` prop updates now apply `minValue`/`maxValue` clamping before formatting
82
+ - Resolved transitive security advisories in the dev dependency tree (`npm audit` clean)
83
+
84
+ ## [0.7.0] - 2026-06-20
85
+
86
+ ### Added
87
+
88
+ - TypeScript source and published type declarations (`IntlNumberInputProps`)
89
+ - `forwardRef` support for the underlying `<input>`
90
+ - Standard HTML input attribute passthrough (`onFocus`, `aria-*`, `data-*`, `autoComplete`, etc.)
91
+ - Step buttons (`showStepButtons`, `step`)
92
+ - `minValue` / `maxValue` constraints
93
+ - `onBlur` callback
94
+ - `inputMode` prop
95
+ - Prefix/suffix HTML sanitization
96
+ - Redesigned interactive demo page with labeled examples and independent state
97
+ - `CONTRIBUTING.md` and `CHANGELOG.md` documentation
98
+
99
+ ### Changed
100
+
101
+ - Migrated from JSX to TypeScript
102
+ - Removed runtime `prop-types` dependency (TypeScript types are the source of truth)
103
+ - `onBlur` now returns clamped numeric values consistent with `onChange`
104
+ - Prefix/suffix sanitization is SSR-safe (no `document` access)
105
+ - `peerDependencies` now require only `react` (not `react-dom`)
106
+ - Split consumer and maintainer documentation (`README.md`, `CONTRIBUTING.md`)
107
+
108
+ ### Fixed
109
+
110
+ - SSR crash when rendering on the server
111
+ - `onBlur` returning unclamped values while display was clamped
112
+
113
+ ## [0.6.0] - 2025
114
+
115
+ ### Changed
116
+
117
+ - Updated build toolchain to Babel 7 and Webpack 5
118
+ - Added Jest test suite
119
+ - Updated demo to React 19
120
+ - Security updates for vulnerable dependencies
121
+
122
+ ## [0.5.0] and earlier
123
+
124
+ See [git history](https://github.com/nandorip/react-intl-number-input/commits/master) for changes prior to 0.6.0.
125
+
126
+ [Unreleased]: https://github.com/nandorip/react-intl-number-input/compare/v0.8.2...HEAD
127
+ [0.8.2]: https://github.com/nandorip/react-intl-number-input/compare/v0.8.1...v0.8.2
128
+ [0.8.1]: https://github.com/nandorip/react-intl-number-input/compare/v0.8.0...v0.8.1
129
+ [0.8.0]: https://github.com/nandorip/react-intl-number-input/compare/v0.7.0...v0.8.0
130
+ [0.7.0]: https://github.com/nandorip/react-intl-number-input/compare/v0.6.0...v0.7.0
131
+ [0.6.0]: https://github.com/nandorip/react-intl-number-input/releases/tag/v0.6.0
@@ -0,0 +1,120 @@
1
+ # Contributing
2
+
3
+ Thank you for your interest in contributing to `react-intl-number-input`.
4
+
5
+ ## Requirements
6
+
7
+ - Node.js 20+ (recommended: current Active LTS)
8
+ - npm 10+
9
+
10
+ These versions are required to build and test the library. Consumers of the published npm package are not constrained by this.
11
+
12
+ ## Getting started
13
+
14
+ ```bash
15
+ git clone https://github.com/nandorip/react-intl-number-input.git
16
+ cd react-intl-number-input
17
+ npm install
18
+ ```
19
+
20
+ ## Local development
21
+
22
+ Start the demo app with live rebuild:
23
+
24
+ ```bash
25
+ npm start
26
+ ```
27
+
28
+ Open [http://localhost:3001](http://localhost:3001). The demo lives in `example/` and imports the component from `src/`.
29
+
30
+ ## Scripts
31
+
32
+ | Command | Description |
33
+ | --- | --- |
34
+ | `npm start` | esbuild dev server for the demo |
35
+ | `npm test` | Run unit tests with Jest |
36
+ | `npm run build:lib` | Compile `src/` to CommonJS, ESM, and types in `dist/` |
37
+ | `npm run build` | Build the demo bundle in `example/dist` |
38
+ | `npm run publish-demo` | Build and deploy the demo to GitHub Pages |
39
+
40
+ ## Project structure
41
+
42
+ ```
43
+ src/ Library source (TypeScript)
44
+ dist/ Published build output (generated, not committed)
45
+ example/ Demo app and GitHub Pages bundle
46
+ ```
47
+
48
+ The npm package ships `dist/`, `README.md`, `LICENSE`, `CHANGELOG.md`, and `CONTRIBUTING.md` (see `files` in `package.json`).
49
+
50
+ ## Testing
51
+
52
+ Run the full test suite before opening a pull request:
53
+
54
+ ```bash
55
+ npm test
56
+ ```
57
+
58
+ Tests live alongside the source in `src/index.test.tsx`.
59
+
60
+ ## Building the library
61
+
62
+ To produce the publishable artifacts locally:
63
+
64
+ ```bash
65
+ npm run build:lib
66
+ ```
67
+
68
+ This generates:
69
+
70
+ - `dist/index.js` — CommonJS build (`tsc`)
71
+ - `dist/esm/index.js` — ESM build (`tsc`)
72
+ - `dist/index.d.ts` — TypeScript declarations
73
+
74
+ ## Publishing to npm
75
+
76
+ ### Prerequisites
77
+
78
+ - npm account with publish access to `react-intl-number-input`
79
+ - logged in: `npm login`
80
+
81
+ ### Release checklist
82
+
83
+ 1. Update `CHANGELOG.md` with the new version and changes.
84
+ 2. Bump the version in `package.json` following [semver](https://semver.org/):
85
+ ```bash
86
+ npm version patch # 0.7.0 → 0.7.1
87
+ npm version minor # 0.7.0 → 0.8.0
88
+ npm version major # 0.7.0 → 1.0.0
89
+ ```
90
+ 3. Publish:
91
+ ```bash
92
+ npm publish
93
+ ```
94
+
95
+ The `prepublishOnly` script runs automatically before publish:
96
+
97
+ ```
98
+ npm test → npm run build:lib
99
+ ```
100
+
101
+ Do not commit `dist/` — it is built during publish and excluded via `.gitignore`.
102
+
103
+ ### Deploying the demo
104
+
105
+ To update the live demo on GitHub Pages:
106
+
107
+ ```bash
108
+ npm run publish-demo
109
+ ```
110
+
111
+ ## Pull requests
112
+
113
+ 1. Fork the repository and create a feature branch.
114
+ 2. Make your changes with tests where applicable.
115
+ 3. Run `npm test` and ensure it passes.
116
+ 4. Open a pull request with a clear description of the change.
117
+
118
+ ## Reporting issues
119
+
120
+ Open an issue at [github.com/nandorip/react-intl-number-input/issues](https://github.com/nandorip/react-intl-number-input/issues).
package/README.md CHANGED
@@ -41,19 +41,22 @@ function App() {
41
41
 
42
42
  ### TypeScript
43
43
 
44
- ```typescript
44
+ ```tsx
45
+ import React, { useState } from 'react';
45
46
  import IntlNumberInput, {
46
47
  IntlNumberInputProps,
47
- ControlsRenderProps,
48
48
  } from 'react-intl-number-input';
49
49
 
50
- const props: IntlNumberInputProps = {
51
- locale: 'pt-BR',
52
- precision: 2,
53
- onChange: (event, value, maskedValue) => {
54
- console.log(value, maskedValue);
55
- },
56
- };
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
+ }
57
60
  ```
58
61
 
59
62
  React 18+:
@@ -103,8 +106,18 @@ ReactDOM.render(<App />, document.getElementById('root'));
103
106
 
104
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.
105
108
 
109
+ ## Accessibility (a11y)
110
+
111
+ The component is built with accessibility in mind:
112
+ - Uses `role="spinbutton"` to identify as a numeric input.
113
+ - Provides `aria-valuenow`, `aria-valuemin`, and `aria-valuemax` attributes.
114
+ - Support for `forwardRef` allows linking labels and managing focus programmatically.
115
+ - Built-in step buttons have descriptive `aria-label` attributes.
116
+
106
117
  ## Examples
107
118
 
119
+ ### Basic Usage
120
+
108
121
  ```javascript
109
122
  // maskedValue: 1,234,567.89
110
123
  <IntlNumberInput />
@@ -172,6 +185,26 @@ The component also accepts standard `<input>` attributes such as `ref`, `onFocus
172
185
  </div>
173
186
  ```
174
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
+
175
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.
176
209
 
177
210
  ## How input works
@@ -0,0 +1,273 @@
1
+ import React, { useState, useMemo, useCallback, useEffect, useLayoutEffect, useRef, useImperativeHandle, memo, forwardRef, } from 'react';
2
+ function sanitizeString(str) {
3
+ return str.replace(/<[^>]*>/g, '').replace(/on\w+\s*=\s*["'][^"']*["']/g, '');
4
+ }
5
+ function resolveLocale(locale) {
6
+ try {
7
+ const supported = Intl.NumberFormat.supportedLocalesOf([locale]);
8
+ return supported.length > 0 ? supported[0] : 'en-US';
9
+ }
10
+ catch (_a) {
11
+ return 'en-US';
12
+ }
13
+ }
14
+ function isValidNumber(value) {
15
+ return Number.isFinite(value) && !Number.isNaN(value);
16
+ }
17
+ function clamp(value, min, max) {
18
+ return Math.min(Math.max(value, min), max);
19
+ }
20
+ function clampToBounds(value, min, max) {
21
+ let result = value;
22
+ if (min !== undefined && result < min) {
23
+ result = min;
24
+ }
25
+ if (max !== undefined && result > max) {
26
+ result = max;
27
+ }
28
+ return result;
29
+ }
30
+ const IntlNumberInput = 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) {
31
+ const innerRef = useRef(null);
32
+ useImperativeHandle(ref, () => innerRef.current);
33
+ const safePrecision = useMemo(() => {
34
+ if (typeof precision !== 'number' || Number.isNaN(precision))
35
+ return 2;
36
+ return clamp(Math.round(precision), 0, 20);
37
+ }, [precision]);
38
+ const safeStep = useMemo(() => {
39
+ if (typeof step !== 'number' || Number.isNaN(step) || step <= 0)
40
+ return 1;
41
+ return step;
42
+ }, [step]);
43
+ const [effectiveMin, effectiveMax] = useMemo(() => {
44
+ if (minValue !== undefined && maxValue !== undefined && minValue > maxValue) {
45
+ return [maxValue, minValue];
46
+ }
47
+ return [minValue, maxValue];
48
+ }, [minValue, maxValue]);
49
+ const getPrecisionValue = useCallback((p) => {
50
+ return Math.pow(10, p);
51
+ }, []);
52
+ const getNumberValue = useCallback((strValue) => {
53
+ const inputValue = String(strValue || '');
54
+ const hasNegativeSign = inputValue.startsWith('-');
55
+ const onlyDigits = inputValue.replace(/[^0-9]/g, '');
56
+ if (!onlyDigits) {
57
+ return hasNegativeSign ? -0 : 0;
58
+ }
59
+ const numberValue = Number(onlyDigits);
60
+ const parsedValue = numberValue / getPrecisionValue(safePrecision);
61
+ return hasNegativeSign ? -parsedValue : parsedValue;
62
+ }, [getPrecisionValue, safePrecision]);
63
+ const numberFormatter = useMemo(() => {
64
+ const options = {
65
+ style: 'decimal',
66
+ minimumFractionDigits: safePrecision,
67
+ maximumFractionDigits: safePrecision,
68
+ };
69
+ const resolvedLocale = resolveLocale(locale);
70
+ return new Intl.NumberFormat(resolvedLocale, options);
71
+ }, [locale, safePrecision]);
72
+ const formatNumber = useCallback((val) => {
73
+ let numberValue;
74
+ if (typeof val === 'string') {
75
+ numberValue = getNumberValue(val);
76
+ }
77
+ else {
78
+ numberValue = val;
79
+ }
80
+ if (!isValidNumber(numberValue)) {
81
+ numberValue = 0;
82
+ }
83
+ numberValue = clampToBounds(numberValue, effectiveMin, effectiveMax);
84
+ const formattedNumber = numberFormatter.format(numberValue);
85
+ const safePrefix = sanitizeString(prefix);
86
+ const safeSuffix = sanitizeString(suffix);
87
+ return `${safePrefix}${formattedNumber}${safeSuffix}`;
88
+ }, [getNumberValue, numberFormatter, prefix, suffix, effectiveMin, effectiveMax]);
89
+ const getInitialValue = useCallback(() => {
90
+ const initial = value !== undefined ? value : 0;
91
+ return formatNumber(initial);
92
+ }, [value, formatNumber]);
93
+ const getInitialNumericValue = useCallback(() => {
94
+ const initial = value !== undefined ? value : 0;
95
+ if (typeof initial === 'string') {
96
+ return getNumberValue(initial);
97
+ }
98
+ return initial;
99
+ }, [value, getNumberValue]);
100
+ const [maskedValue, setMaskedValue] = useState(getInitialValue);
101
+ const numericValueRef = useRef(getInitialNumericValue());
102
+ const selectionRef = useRef(null);
103
+ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
104
+ useIsomorphicLayoutEffect(() => {
105
+ if (selectionRef.current && innerRef.current) {
106
+ const { digitsBefore } = selectionRef.current;
107
+ const newValue = innerRef.current.value;
108
+ let newPos = 0;
109
+ let digitsFound = 0;
110
+ for (let i = 0; i < newValue.length; i++) {
111
+ if (/\d/.test(newValue[i])) {
112
+ digitsFound++;
113
+ }
114
+ newPos = i + 1;
115
+ if (digitsFound >= digitsBefore) {
116
+ break;
117
+ }
118
+ }
119
+ innerRef.current.setSelectionRange(newPos, newPos);
120
+ selectionRef.current = null;
121
+ }
122
+ }, [maskedValue]);
123
+ useEffect(() => {
124
+ if (value === undefined)
125
+ return;
126
+ const numericVal = typeof value === 'string' ? getNumberValue(value) : value;
127
+ const clamped = clampToBounds(numericVal, effectiveMin, effectiveMax);
128
+ setMaskedValue(formatNumber(clamped));
129
+ numericValueRef.current = clamped;
130
+ }, [value, formatNumber, getNumberValue, effectiveMin, effectiveMax]);
131
+ const handleChange = useCallback((event) => {
132
+ const input = event.target;
133
+ const selectionStart = input.selectionStart || 0;
134
+ const valueBefore = input.value;
135
+ const digitsBefore = valueBefore.substring(0, selectionStart).replace(/\D/g, '').length;
136
+ selectionRef.current = { digitsBefore };
137
+ const rawValue = event.target.value;
138
+ const newValue = getNumberValue(rawValue);
139
+ if (!isValidNumber(newValue)) {
140
+ return;
141
+ }
142
+ const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);
143
+ const newMaskedValue = formatNumber(clampedValue);
144
+ setMaskedValue(newMaskedValue);
145
+ numericValueRef.current = clampedValue;
146
+ if (onChange) {
147
+ onChange(event, clampedValue, newMaskedValue);
148
+ }
149
+ }, [getNumberValue, formatNumber, effectiveMin, effectiveMax, onChange]);
150
+ const handleBlur = useCallback((event) => {
151
+ const rawValue = event.target.value;
152
+ const currentValue = getNumberValue(rawValue);
153
+ const clampedValue = clampToBounds(currentValue, effectiveMin, effectiveMax);
154
+ const formattedValue = formatNumber(clampedValue);
155
+ setMaskedValue(formattedValue);
156
+ numericValueRef.current = clampedValue;
157
+ if (onBlur) {
158
+ onBlur(event, clampedValue, formattedValue);
159
+ }
160
+ }, [getNumberValue, formatNumber, effectiveMin, effectiveMax, onBlur]);
161
+ const handleStep = useCallback((delta, multiplier = 1) => {
162
+ if (disabled)
163
+ return;
164
+ const currentValue = numericValueRef.current;
165
+ const stepValue = (safeStep / Math.pow(10, safePrecision)) * multiplier;
166
+ const newValue = clampToBounds(currentValue + delta * stepValue, effectiveMin, effectiveMax);
167
+ const newMaskedValue = formatNumber(newValue);
168
+ setMaskedValue(newMaskedValue);
169
+ numericValueRef.current = newValue;
170
+ const syntheticEvent = {
171
+ target: {
172
+ value: newMaskedValue,
173
+ name: inputProps.name || '',
174
+ id: inputProps.id || '',
175
+ },
176
+ currentTarget: {
177
+ value: newMaskedValue,
178
+ name: inputProps.name || '',
179
+ id: inputProps.id || '',
180
+ },
181
+ persist: () => { },
182
+ preventDefault: () => { },
183
+ stopPropagation: () => { },
184
+ bubble: true,
185
+ cancelable: false,
186
+ type: 'change',
187
+ };
188
+ if (onChange) {
189
+ onChange(syntheticEvent, newValue, newMaskedValue);
190
+ }
191
+ }, [
192
+ disabled,
193
+ safeStep,
194
+ safePrecision,
195
+ effectiveMin,
196
+ effectiveMax,
197
+ formatNumber,
198
+ onChange,
199
+ inputProps.name,
200
+ inputProps.id,
201
+ ]);
202
+ const increment = useCallback((multiplier = 1) => handleStep(1, multiplier), [handleStep]);
203
+ const decrement = useCallback((multiplier = 1) => handleStep(-1, multiplier), [handleStep]);
204
+ const setValue = useCallback((newValue) => {
205
+ const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);
206
+ const newMaskedValue = formatNumber(clampedValue);
207
+ setMaskedValue(newMaskedValue);
208
+ numericValueRef.current = clampedValue;
209
+ const syntheticEvent = {
210
+ target: {
211
+ value: newMaskedValue,
212
+ name: inputProps.name || '',
213
+ id: inputProps.id || '',
214
+ },
215
+ currentTarget: {
216
+ value: newMaskedValue,
217
+ name: inputProps.name || '',
218
+ id: inputProps.id || '',
219
+ },
220
+ persist: () => { },
221
+ preventDefault: () => { },
222
+ stopPropagation: () => { },
223
+ bubble: true,
224
+ cancelable: false,
225
+ type: 'change',
226
+ };
227
+ if (onChange) {
228
+ onChange(syntheticEvent, clampedValue, newMaskedValue);
229
+ }
230
+ }, [effectiveMin, effectiveMax, formatNumber, onChange, inputProps.name, inputProps.id]);
231
+ const inputMode = useMemo(() => {
232
+ if (inputModeProp) {
233
+ return inputModeProp;
234
+ }
235
+ return safePrecision > 0 ? 'decimal' : 'numeric';
236
+ }, [inputModeProp, safePrecision]);
237
+ const inputElement = (React.createElement("input", { ref: innerRef, type: "text", role: "spinbutton", "aria-valuenow": isValidNumber(numericValueRef.current) ? numericValueRef.current : 0, "aria-valuemin": effectiveMin, "aria-valuemax": effectiveMax, inputMode: inputMode, value: maskedValue, disabled: disabled, onChange: handleChange, onBlur: handleBlur, ...inputProps }));
238
+ if (renderControls) {
239
+ const controlsProps = {
240
+ increment,
241
+ decrement,
242
+ setValue,
243
+ value: numericValueRef.current,
244
+ min: effectiveMin,
245
+ max: effectiveMax,
246
+ disabled,
247
+ step: safeStep,
248
+ precision: safePrecision,
249
+ formattedValue: maskedValue,
250
+ };
251
+ return (React.createElement(React.Fragment, null,
252
+ inputElement,
253
+ renderControls(controlsProps)));
254
+ }
255
+ if (!showStepButtons) {
256
+ return inputElement;
257
+ }
258
+ return (React.createElement("div", { style: { display: 'inline-flex', alignItems: 'center', gap: '4px' } },
259
+ React.createElement("button", { type: "button", onClick: () => handleStep(-1), disabled: disabled, "aria-label": "Decrease value", style: {
260
+ padding: '4px 8px',
261
+ cursor: disabled ? 'not-allowed' : 'pointer',
262
+ opacity: disabled ? 0.5 : 1,
263
+ } }, "-"),
264
+ inputElement,
265
+ React.createElement("button", { type: "button", onClick: () => handleStep(1), disabled: disabled, "aria-label": "Increase value", style: {
266
+ padding: '4px 8px',
267
+ cursor: disabled ? 'not-allowed' : 'pointer',
268
+ opacity: disabled ? 0.5 : 1,
269
+ } }, "+")));
270
+ });
271
+ IntlNumberInput.displayName = 'IntlNumberInput';
272
+ export default memo(IntlNumberInput);
273
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {\n useState,\n useMemo,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useImperativeHandle,\n memo,\n forwardRef,\n} from 'react';\n\nexport interface ControlsRenderProps {\n increment: (multiplier?: number) => void;\n decrement: (multiplier?: number) => void;\n setValue: (value: number) => void;\n value: number;\n min?: number;\n max?: number;\n disabled: boolean;\n step: number;\n precision: number;\n formattedValue: string;\n}\n\ntype IntlNumberInputOwnProps = {\n locale?: string;\n prefix?: string;\n suffix?: string;\n precision?: number;\n value?: number | string;\n showStepButtons?: boolean;\n step?: number;\n minValue?: number;\n maxValue?: number;\n inputMode?: 'numeric' | 'decimal';\n onChange?: (\n event: React.ChangeEvent<HTMLInputElement>,\n value: number,\n maskedValue: string\n ) => void;\n onBlur?: (\n event: React.FocusEvent<HTMLInputElement>,\n value: number,\n maskedValue: string\n ) => void;\n renderControls?: (props: ControlsRenderProps) => React.ReactNode;\n};\n\nexport type IntlNumberInputProps = IntlNumberInputOwnProps &\n Omit<\n React.ComponentPropsWithoutRef<'input'>,\n keyof IntlNumberInputOwnProps | 'value' | 'type' | 'defaultValue' | 'children'\n >;\n\nfunction sanitizeString(str: string): string {\n return str.replace(/<[^>]*>/g, '').replace(/on\\w+\\s*=\\s*[\"'][^\"']*[\"']/g, '');\n}\n\nfunction resolveLocale(locale: string): string {\n try {\n const supported = Intl.NumberFormat.supportedLocalesOf([locale]);\n return supported.length > 0 ? supported[0] : 'en-US';\n } catch {\n return 'en-US';\n }\n}\n\nfunction isValidNumber(value: number): boolean {\n return Number.isFinite(value) && !Number.isNaN(value);\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction clampToBounds(\n value: number,\n min: number | undefined,\n max: number | undefined\n): number {\n let result = value;\n\n if (min !== undefined && result < min) {\n result = min;\n }\n\n if (max !== undefined && result > max) {\n result = max;\n }\n\n return result;\n}\n\nconst IntlNumberInput = forwardRef<HTMLInputElement, IntlNumberInputProps>(\n function IntlNumberInput(\n {\n locale = 'en-US',\n prefix = '',\n suffix = '',\n precision = 2,\n value,\n showStepButtons = false,\n step = 1,\n minValue,\n maxValue,\n inputMode: inputModeProp,\n onChange,\n onBlur,\n disabled = false,\n renderControls,\n ...inputProps\n },\n ref\n ) {\n const innerRef = useRef<HTMLInputElement>(null);\n useImperativeHandle(ref, () => innerRef.current!);\n\n const safePrecision = useMemo(() => {\n if (typeof precision !== 'number' || Number.isNaN(precision)) return 2;\n return clamp(Math.round(precision), 0, 20);\n }, [precision]);\n\n const safeStep = useMemo(() => {\n if (typeof step !== 'number' || Number.isNaN(step) || step <= 0) return 1;\n return step;\n }, [step]);\n\n const [effectiveMin, effectiveMax] = useMemo(() => {\n if (minValue !== undefined && maxValue !== undefined && minValue > maxValue) {\n return [maxValue, minValue] as const;\n }\n return [minValue, maxValue] as const;\n }, [minValue, maxValue]);\n\n const getPrecisionValue = useCallback((p: number) => {\n return Math.pow(10, p);\n }, []);\n\n const getNumberValue = useCallback(\n (strValue: string): number => {\n const inputValue = String(strValue || '');\n const hasNegativeSign = inputValue.startsWith('-');\n\n const onlyDigits = inputValue.replace(/[^0-9]/g, '');\n\n if (!onlyDigits) {\n return hasNegativeSign ? -0 : 0;\n }\n\n const numberValue = Number(onlyDigits);\n const parsedValue = numberValue / getPrecisionValue(safePrecision);\n\n return hasNegativeSign ? -parsedValue : parsedValue;\n },\n [getPrecisionValue, safePrecision]\n );\n\n const numberFormatter = useMemo(() => {\n const options = {\n style: 'decimal' as const,\n minimumFractionDigits: safePrecision,\n maximumFractionDigits: safePrecision,\n };\n\n const resolvedLocale = resolveLocale(locale);\n return new Intl.NumberFormat(resolvedLocale, options);\n }, [locale, safePrecision]);\n\n const formatNumber = useCallback(\n (val: number | string): string => {\n let numberValue: number;\n\n if (typeof val === 'string') {\n numberValue = getNumberValue(val);\n } else {\n numberValue = val;\n }\n\n if (!isValidNumber(numberValue)) {\n numberValue = 0;\n }\n\n numberValue = clampToBounds(numberValue, effectiveMin, effectiveMax);\n\n const formattedNumber = numberFormatter.format(numberValue);\n const safePrefix = sanitizeString(prefix);\n const safeSuffix = sanitizeString(suffix);\n\n return `${safePrefix}${formattedNumber}${safeSuffix}`;\n },\n [getNumberValue, numberFormatter, prefix, suffix, effectiveMin, effectiveMax]\n );\n\n const getInitialValue = useCallback(() => {\n const initial = value !== undefined ? value : 0;\n return formatNumber(initial);\n }, [value, formatNumber]);\n\n const getInitialNumericValue = useCallback(() => {\n const initial = value !== undefined ? value : 0;\n if (typeof initial === 'string') {\n return getNumberValue(initial);\n }\n return initial;\n }, [value, getNumberValue]);\n\n const [maskedValue, setMaskedValue] = useState<string>(getInitialValue);\n const numericValueRef = useRef<number>(getInitialNumericValue());\n const selectionRef = useRef<{ digitsBefore: number } | null>(null);\n\n const useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n\n useIsomorphicLayoutEffect(() => {\n if (selectionRef.current && innerRef.current) {\n const { digitsBefore } = selectionRef.current;\n const newValue = innerRef.current.value;\n let newPos = 0;\n let digitsFound = 0;\n\n for (let i = 0; i < newValue.length; i++) {\n if (/\\d/.test(newValue[i])) {\n digitsFound++;\n }\n newPos = i + 1;\n if (digitsFound >= digitsBefore) {\n break;\n }\n }\n\n innerRef.current.setSelectionRange(newPos, newPos);\n selectionRef.current = null;\n }\n }, [maskedValue]);\n\n useEffect(() => {\n if (value === undefined) return;\n const numericVal = typeof value === 'string' ? getNumberValue(value) : value;\n const clamped = clampToBounds(numericVal, effectiveMin, effectiveMax);\n setMaskedValue(formatNumber(clamped));\n numericValueRef.current = clamped;\n }, [value, formatNumber, getNumberValue, effectiveMin, effectiveMax]);\n\n const handleChange = useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n const input = event.target;\n const selectionStart = input.selectionStart || 0;\n const valueBefore = input.value;\n const digitsBefore = valueBefore.substring(0, selectionStart).replace(/\\D/g, '').length;\n\n selectionRef.current = { digitsBefore };\n\n const rawValue = event.target.value;\n const newValue = getNumberValue(rawValue);\n\n if (!isValidNumber(newValue)) {\n return;\n }\n\n const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);\n const newMaskedValue = formatNumber(clampedValue);\n setMaskedValue(newMaskedValue);\n numericValueRef.current = clampedValue;\n\n if (onChange) {\n onChange(event, clampedValue, newMaskedValue);\n }\n },\n [getNumberValue, formatNumber, effectiveMin, effectiveMax, onChange]\n );\n\n const handleBlur = useCallback(\n (event: React.FocusEvent<HTMLInputElement>) => {\n const rawValue = event.target.value;\n const currentValue = getNumberValue(rawValue);\n const clampedValue = clampToBounds(currentValue, effectiveMin, effectiveMax);\n const formattedValue = formatNumber(clampedValue);\n\n setMaskedValue(formattedValue);\n numericValueRef.current = clampedValue;\n\n if (onBlur) {\n onBlur(event, clampedValue, formattedValue);\n }\n },\n [getNumberValue, formatNumber, effectiveMin, effectiveMax, onBlur]\n );\n\n const handleStep = useCallback(\n (delta: number, multiplier: number = 1) => {\n if (disabled) return;\n\n const currentValue = numericValueRef.current;\n const stepValue = (safeStep / Math.pow(10, safePrecision)) * multiplier;\n const newValue = clampToBounds(\n currentValue + delta * stepValue,\n effectiveMin,\n effectiveMax\n );\n\n const newMaskedValue = formatNumber(newValue);\n setMaskedValue(newMaskedValue);\n numericValueRef.current = newValue;\n\n const syntheticEvent = {\n target: {\n value: newMaskedValue,\n name: inputProps.name || '',\n id: inputProps.id || '',\n },\n currentTarget: {\n value: newMaskedValue,\n name: inputProps.name || '',\n id: inputProps.id || '',\n },\n persist: () => {},\n preventDefault: () => {},\n stopPropagation: () => {},\n bubble: true,\n cancelable: false,\n type: 'change',\n } as unknown as React.ChangeEvent<HTMLInputElement>;\n\n if (onChange) {\n onChange(syntheticEvent, newValue, newMaskedValue);\n }\n },\n [\n disabled,\n safeStep,\n safePrecision,\n effectiveMin,\n effectiveMax,\n formatNumber,\n onChange,\n inputProps.name,\n inputProps.id,\n ]\n );\n\n const increment = useCallback(\n (multiplier: number = 1) => handleStep(1, multiplier),\n [handleStep]\n );\n\n const decrement = useCallback(\n (multiplier: number = 1) => handleStep(-1, multiplier),\n [handleStep]\n );\n\n const setValue = useCallback(\n (newValue: number) => {\n const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);\n const newMaskedValue = formatNumber(clampedValue);\n setMaskedValue(newMaskedValue);\n numericValueRef.current = clampedValue;\n\n const syntheticEvent = {\n target: {\n value: newMaskedValue,\n name: inputProps.name || '',\n id: inputProps.id || '',\n },\n currentTarget: {\n value: newMaskedValue,\n name: inputProps.name || '',\n id: inputProps.id || '',\n },\n persist: () => {},\n preventDefault: () => {},\n stopPropagation: () => {},\n bubble: true,\n cancelable: false,\n type: 'change',\n } as unknown as React.ChangeEvent<HTMLInputElement>;\n\n if (onChange) {\n onChange(syntheticEvent, clampedValue, newMaskedValue);\n }\n },\n [effectiveMin, effectiveMax, formatNumber, onChange, inputProps.name, inputProps.id]\n );\n\n const inputMode = useMemo(() => {\n if (inputModeProp) {\n return inputModeProp;\n }\n return safePrecision > 0 ? 'decimal' : 'numeric';\n }, [inputModeProp, safePrecision]);\n\n const inputElement = (\n <input\n ref={innerRef}\n type=\"text\"\n role=\"spinbutton\"\n aria-valuenow={isValidNumber(numericValueRef.current) ? numericValueRef.current : 0}\n aria-valuemin={effectiveMin}\n aria-valuemax={effectiveMax}\n inputMode={inputMode}\n value={maskedValue}\n disabled={disabled}\n onChange={handleChange}\n onBlur={handleBlur}\n {...inputProps}\n />\n );\n\n if (renderControls) {\n const controlsProps: ControlsRenderProps = {\n increment,\n decrement,\n setValue,\n value: numericValueRef.current,\n min: effectiveMin,\n max: effectiveMax,\n disabled,\n step: safeStep,\n precision: safePrecision,\n formattedValue: maskedValue,\n };\n\n return (\n <>\n {inputElement}\n {renderControls(controlsProps)}\n </>\n );\n }\n\n if (!showStepButtons) {\n return inputElement;\n }\n\n return (\n <div style={{ display: 'inline-flex', alignItems: 'center', gap: '4px' }}>\n <button\n type=\"button\"\n onClick={() => handleStep(-1)}\n disabled={disabled}\n aria-label=\"Decrease value\"\n style={{\n padding: '4px 8px',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n }}\n >\n -\n </button>\n {inputElement}\n <button\n type=\"button\"\n onClick={() => handleStep(1)}\n disabled={disabled}\n aria-label=\"Increase value\"\n style={{\n padding: '4px 8px',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n }}\n >\n +\n </button>\n </div>\n );\n }\n);\n\nIntlNumberInput.displayName = 'IntlNumberInput';\n\nexport default memo(IntlNumberInput);"]}
@@ -0,0 +1 @@
1
+ {"type": "module"}
package/dist/index.js CHANGED
@@ -63,6 +63,8 @@ function clampToBounds(value, min, max) {
63
63
  return result;
64
64
  }
65
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 innerRef = (0, react_1.useRef)(null);
67
+ (0, react_1.useImperativeHandle)(ref, () => innerRef.current);
66
68
  const safePrecision = (0, react_1.useMemo)(() => {
67
69
  if (typeof precision !== 'number' || Number.isNaN(precision))
68
70
  return 2;
@@ -132,6 +134,27 @@ const IntlNumberInput = (0, react_1.forwardRef)(function IntlNumberInput({ local
132
134
  }, [value, getNumberValue]);
133
135
  const [maskedValue, setMaskedValue] = (0, react_1.useState)(getInitialValue);
134
136
  const numericValueRef = (0, react_1.useRef)(getInitialNumericValue());
137
+ const selectionRef = (0, react_1.useRef)(null);
138
+ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? react_1.useLayoutEffect : react_1.useEffect;
139
+ useIsomorphicLayoutEffect(() => {
140
+ if (selectionRef.current && innerRef.current) {
141
+ const { digitsBefore } = selectionRef.current;
142
+ const newValue = innerRef.current.value;
143
+ let newPos = 0;
144
+ let digitsFound = 0;
145
+ for (let i = 0; i < newValue.length; i++) {
146
+ if (/\d/.test(newValue[i])) {
147
+ digitsFound++;
148
+ }
149
+ newPos = i + 1;
150
+ if (digitsFound >= digitsBefore) {
151
+ break;
152
+ }
153
+ }
154
+ innerRef.current.setSelectionRange(newPos, newPos);
155
+ selectionRef.current = null;
156
+ }
157
+ }, [maskedValue]);
135
158
  (0, react_1.useEffect)(() => {
136
159
  if (value === undefined)
137
160
  return;
@@ -141,6 +164,11 @@ const IntlNumberInput = (0, react_1.forwardRef)(function IntlNumberInput({ local
141
164
  numericValueRef.current = clamped;
142
165
  }, [value, formatNumber, getNumberValue, effectiveMin, effectiveMax]);
143
166
  const handleChange = (0, react_1.useCallback)((event) => {
167
+ const input = event.target;
168
+ const selectionStart = input.selectionStart || 0;
169
+ const valueBefore = input.value;
170
+ const digitsBefore = valueBefore.substring(0, selectionStart).replace(/\D/g, '').length;
171
+ selectionRef.current = { digitsBefore };
144
172
  const rawValue = event.target.value;
145
173
  const newValue = getNumberValue(rawValue);
146
174
  if (!isValidNumber(newValue)) {
@@ -180,7 +208,17 @@ const IntlNumberInput = (0, react_1.forwardRef)(function IntlNumberInput({ local
180
208
  name: inputProps.name || '',
181
209
  id: inputProps.id || '',
182
210
  },
211
+ currentTarget: {
212
+ value: newMaskedValue,
213
+ name: inputProps.name || '',
214
+ id: inputProps.id || '',
215
+ },
216
+ persist: () => { },
183
217
  preventDefault: () => { },
218
+ stopPropagation: () => { },
219
+ bubble: true,
220
+ cancelable: false,
221
+ type: 'change',
184
222
  };
185
223
  if (onChange) {
186
224
  onChange(syntheticEvent, newValue, newMaskedValue);
@@ -209,7 +247,17 @@ const IntlNumberInput = (0, react_1.forwardRef)(function IntlNumberInput({ local
209
247
  name: inputProps.name || '',
210
248
  id: inputProps.id || '',
211
249
  },
250
+ currentTarget: {
251
+ value: newMaskedValue,
252
+ name: inputProps.name || '',
253
+ id: inputProps.id || '',
254
+ },
255
+ persist: () => { },
212
256
  preventDefault: () => { },
257
+ stopPropagation: () => { },
258
+ bubble: true,
259
+ cancelable: false,
260
+ type: 'change',
213
261
  };
214
262
  if (onChange) {
215
263
  onChange(syntheticEvent, clampedValue, newMaskedValue);
@@ -221,7 +269,7 @@ const IntlNumberInput = (0, react_1.forwardRef)(function IntlNumberInput({ local
221
269
  }
222
270
  return safePrecision > 0 ? 'decimal' : 'numeric';
223
271
  }, [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 }));
272
+ const inputElement = (react_1.default.createElement("input", { ref: innerRef, type: "text", role: "spinbutton", "aria-valuenow": isValidNumber(numericValueRef.current) ? numericValueRef.current : 0, "aria-valuemin": effectiveMin, "aria-valuemax": effectiveMax, inputMode: inputMode, value: maskedValue, disabled: disabled, onChange: handleChange, onBlur: handleBlur, ...inputProps }));
225
273
  if (renderControls) {
226
274
  const controlsProps = {
227
275
  increment,
@@ -257,3 +305,4 @@ const IntlNumberInput = (0, react_1.forwardRef)(function IntlNumberInput({ local
257
305
  });
258
306
  IntlNumberInput.displayName = 'IntlNumberInput';
259
307
  exports.default = (0, react_1.memo)(IntlNumberInput);
308
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {\n useState,\n useMemo,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useImperativeHandle,\n memo,\n forwardRef,\n} from 'react';\n\nexport interface ControlsRenderProps {\n increment: (multiplier?: number) => void;\n decrement: (multiplier?: number) => void;\n setValue: (value: number) => void;\n value: number;\n min?: number;\n max?: number;\n disabled: boolean;\n step: number;\n precision: number;\n formattedValue: string;\n}\n\ntype IntlNumberInputOwnProps = {\n locale?: string;\n prefix?: string;\n suffix?: string;\n precision?: number;\n value?: number | string;\n showStepButtons?: boolean;\n step?: number;\n minValue?: number;\n maxValue?: number;\n inputMode?: 'numeric' | 'decimal';\n onChange?: (\n event: React.ChangeEvent<HTMLInputElement>,\n value: number,\n maskedValue: string\n ) => void;\n onBlur?: (\n event: React.FocusEvent<HTMLInputElement>,\n value: number,\n maskedValue: string\n ) => void;\n renderControls?: (props: ControlsRenderProps) => React.ReactNode;\n};\n\nexport type IntlNumberInputProps = IntlNumberInputOwnProps &\n Omit<\n React.ComponentPropsWithoutRef<'input'>,\n keyof IntlNumberInputOwnProps | 'value' | 'type' | 'defaultValue' | 'children'\n >;\n\nfunction sanitizeString(str: string): string {\n return str.replace(/<[^>]*>/g, '').replace(/on\\w+\\s*=\\s*[\"'][^\"']*[\"']/g, '');\n}\n\nfunction resolveLocale(locale: string): string {\n try {\n const supported = Intl.NumberFormat.supportedLocalesOf([locale]);\n return supported.length > 0 ? supported[0] : 'en-US';\n } catch {\n return 'en-US';\n }\n}\n\nfunction isValidNumber(value: number): boolean {\n return Number.isFinite(value) && !Number.isNaN(value);\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction clampToBounds(\n value: number,\n min: number | undefined,\n max: number | undefined\n): number {\n let result = value;\n\n if (min !== undefined && result < min) {\n result = min;\n }\n\n if (max !== undefined && result > max) {\n result = max;\n }\n\n return result;\n}\n\nconst IntlNumberInput = forwardRef<HTMLInputElement, IntlNumberInputProps>(\n function IntlNumberInput(\n {\n locale = 'en-US',\n prefix = '',\n suffix = '',\n precision = 2,\n value,\n showStepButtons = false,\n step = 1,\n minValue,\n maxValue,\n inputMode: inputModeProp,\n onChange,\n onBlur,\n disabled = false,\n renderControls,\n ...inputProps\n },\n ref\n ) {\n const innerRef = useRef<HTMLInputElement>(null);\n useImperativeHandle(ref, () => innerRef.current!);\n\n const safePrecision = useMemo(() => {\n if (typeof precision !== 'number' || Number.isNaN(precision)) return 2;\n return clamp(Math.round(precision), 0, 20);\n }, [precision]);\n\n const safeStep = useMemo(() => {\n if (typeof step !== 'number' || Number.isNaN(step) || step <= 0) return 1;\n return step;\n }, [step]);\n\n const [effectiveMin, effectiveMax] = useMemo(() => {\n if (minValue !== undefined && maxValue !== undefined && minValue > maxValue) {\n return [maxValue, minValue] as const;\n }\n return [minValue, maxValue] as const;\n }, [minValue, maxValue]);\n\n const getPrecisionValue = useCallback((p: number) => {\n return Math.pow(10, p);\n }, []);\n\n const getNumberValue = useCallback(\n (strValue: string): number => {\n const inputValue = String(strValue || '');\n const hasNegativeSign = inputValue.startsWith('-');\n\n const onlyDigits = inputValue.replace(/[^0-9]/g, '');\n\n if (!onlyDigits) {\n return hasNegativeSign ? -0 : 0;\n }\n\n const numberValue = Number(onlyDigits);\n const parsedValue = numberValue / getPrecisionValue(safePrecision);\n\n return hasNegativeSign ? -parsedValue : parsedValue;\n },\n [getPrecisionValue, safePrecision]\n );\n\n const numberFormatter = useMemo(() => {\n const options = {\n style: 'decimal' as const,\n minimumFractionDigits: safePrecision,\n maximumFractionDigits: safePrecision,\n };\n\n const resolvedLocale = resolveLocale(locale);\n return new Intl.NumberFormat(resolvedLocale, options);\n }, [locale, safePrecision]);\n\n const formatNumber = useCallback(\n (val: number | string): string => {\n let numberValue: number;\n\n if (typeof val === 'string') {\n numberValue = getNumberValue(val);\n } else {\n numberValue = val;\n }\n\n if (!isValidNumber(numberValue)) {\n numberValue = 0;\n }\n\n numberValue = clampToBounds(numberValue, effectiveMin, effectiveMax);\n\n const formattedNumber = numberFormatter.format(numberValue);\n const safePrefix = sanitizeString(prefix);\n const safeSuffix = sanitizeString(suffix);\n\n return `${safePrefix}${formattedNumber}${safeSuffix}`;\n },\n [getNumberValue, numberFormatter, prefix, suffix, effectiveMin, effectiveMax]\n );\n\n const getInitialValue = useCallback(() => {\n const initial = value !== undefined ? value : 0;\n return formatNumber(initial);\n }, [value, formatNumber]);\n\n const getInitialNumericValue = useCallback(() => {\n const initial = value !== undefined ? value : 0;\n if (typeof initial === 'string') {\n return getNumberValue(initial);\n }\n return initial;\n }, [value, getNumberValue]);\n\n const [maskedValue, setMaskedValue] = useState<string>(getInitialValue);\n const numericValueRef = useRef<number>(getInitialNumericValue());\n const selectionRef = useRef<{ digitsBefore: number } | null>(null);\n\n const useIsomorphicLayoutEffect =\n typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n\n useIsomorphicLayoutEffect(() => {\n if (selectionRef.current && innerRef.current) {\n const { digitsBefore } = selectionRef.current;\n const newValue = innerRef.current.value;\n let newPos = 0;\n let digitsFound = 0;\n\n for (let i = 0; i < newValue.length; i++) {\n if (/\\d/.test(newValue[i])) {\n digitsFound++;\n }\n newPos = i + 1;\n if (digitsFound >= digitsBefore) {\n break;\n }\n }\n\n innerRef.current.setSelectionRange(newPos, newPos);\n selectionRef.current = null;\n }\n }, [maskedValue]);\n\n useEffect(() => {\n if (value === undefined) return;\n const numericVal = typeof value === 'string' ? getNumberValue(value) : value;\n const clamped = clampToBounds(numericVal, effectiveMin, effectiveMax);\n setMaskedValue(formatNumber(clamped));\n numericValueRef.current = clamped;\n }, [value, formatNumber, getNumberValue, effectiveMin, effectiveMax]);\n\n const handleChange = useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n const input = event.target;\n const selectionStart = input.selectionStart || 0;\n const valueBefore = input.value;\n const digitsBefore = valueBefore.substring(0, selectionStart).replace(/\\D/g, '').length;\n\n selectionRef.current = { digitsBefore };\n\n const rawValue = event.target.value;\n const newValue = getNumberValue(rawValue);\n\n if (!isValidNumber(newValue)) {\n return;\n }\n\n const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);\n const newMaskedValue = formatNumber(clampedValue);\n setMaskedValue(newMaskedValue);\n numericValueRef.current = clampedValue;\n\n if (onChange) {\n onChange(event, clampedValue, newMaskedValue);\n }\n },\n [getNumberValue, formatNumber, effectiveMin, effectiveMax, onChange]\n );\n\n const handleBlur = useCallback(\n (event: React.FocusEvent<HTMLInputElement>) => {\n const rawValue = event.target.value;\n const currentValue = getNumberValue(rawValue);\n const clampedValue = clampToBounds(currentValue, effectiveMin, effectiveMax);\n const formattedValue = formatNumber(clampedValue);\n\n setMaskedValue(formattedValue);\n numericValueRef.current = clampedValue;\n\n if (onBlur) {\n onBlur(event, clampedValue, formattedValue);\n }\n },\n [getNumberValue, formatNumber, effectiveMin, effectiveMax, onBlur]\n );\n\n const handleStep = useCallback(\n (delta: number, multiplier: number = 1) => {\n if (disabled) return;\n\n const currentValue = numericValueRef.current;\n const stepValue = (safeStep / Math.pow(10, safePrecision)) * multiplier;\n const newValue = clampToBounds(\n currentValue + delta * stepValue,\n effectiveMin,\n effectiveMax\n );\n\n const newMaskedValue = formatNumber(newValue);\n setMaskedValue(newMaskedValue);\n numericValueRef.current = newValue;\n\n const syntheticEvent = {\n target: {\n value: newMaskedValue,\n name: inputProps.name || '',\n id: inputProps.id || '',\n },\n currentTarget: {\n value: newMaskedValue,\n name: inputProps.name || '',\n id: inputProps.id || '',\n },\n persist: () => {},\n preventDefault: () => {},\n stopPropagation: () => {},\n bubble: true,\n cancelable: false,\n type: 'change',\n } as unknown as React.ChangeEvent<HTMLInputElement>;\n\n if (onChange) {\n onChange(syntheticEvent, newValue, newMaskedValue);\n }\n },\n [\n disabled,\n safeStep,\n safePrecision,\n effectiveMin,\n effectiveMax,\n formatNumber,\n onChange,\n inputProps.name,\n inputProps.id,\n ]\n );\n\n const increment = useCallback(\n (multiplier: number = 1) => handleStep(1, multiplier),\n [handleStep]\n );\n\n const decrement = useCallback(\n (multiplier: number = 1) => handleStep(-1, multiplier),\n [handleStep]\n );\n\n const setValue = useCallback(\n (newValue: number) => {\n const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);\n const newMaskedValue = formatNumber(clampedValue);\n setMaskedValue(newMaskedValue);\n numericValueRef.current = clampedValue;\n\n const syntheticEvent = {\n target: {\n value: newMaskedValue,\n name: inputProps.name || '',\n id: inputProps.id || '',\n },\n currentTarget: {\n value: newMaskedValue,\n name: inputProps.name || '',\n id: inputProps.id || '',\n },\n persist: () => {},\n preventDefault: () => {},\n stopPropagation: () => {},\n bubble: true,\n cancelable: false,\n type: 'change',\n } as unknown as React.ChangeEvent<HTMLInputElement>;\n\n if (onChange) {\n onChange(syntheticEvent, clampedValue, newMaskedValue);\n }\n },\n [effectiveMin, effectiveMax, formatNumber, onChange, inputProps.name, inputProps.id]\n );\n\n const inputMode = useMemo(() => {\n if (inputModeProp) {\n return inputModeProp;\n }\n return safePrecision > 0 ? 'decimal' : 'numeric';\n }, [inputModeProp, safePrecision]);\n\n const inputElement = (\n <input\n ref={innerRef}\n type=\"text\"\n role=\"spinbutton\"\n aria-valuenow={isValidNumber(numericValueRef.current) ? numericValueRef.current : 0}\n aria-valuemin={effectiveMin}\n aria-valuemax={effectiveMax}\n inputMode={inputMode}\n value={maskedValue}\n disabled={disabled}\n onChange={handleChange}\n onBlur={handleBlur}\n {...inputProps}\n />\n );\n\n if (renderControls) {\n const controlsProps: ControlsRenderProps = {\n increment,\n decrement,\n setValue,\n value: numericValueRef.current,\n min: effectiveMin,\n max: effectiveMax,\n disabled,\n step: safeStep,\n precision: safePrecision,\n formattedValue: maskedValue,\n };\n\n return (\n <>\n {inputElement}\n {renderControls(controlsProps)}\n </>\n );\n }\n\n if (!showStepButtons) {\n return inputElement;\n }\n\n return (\n <div style={{ display: 'inline-flex', alignItems: 'center', gap: '4px' }}>\n <button\n type=\"button\"\n onClick={() => handleStep(-1)}\n disabled={disabled}\n aria-label=\"Decrease value\"\n style={{\n padding: '4px 8px',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n }}\n >\n -\n </button>\n {inputElement}\n <button\n type=\"button\"\n onClick={() => handleStep(1)}\n disabled={disabled}\n aria-label=\"Increase value\"\n style={{\n padding: '4px 8px',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n }}\n >\n +\n </button>\n </div>\n );\n }\n);\n\nIntlNumberInput.displayName = 'IntlNumberInput';\n\nexport default memo(IntlNumberInput);"]}
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "react-intl-number-input",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "A React component for masked and formatted number input.",
5
5
  "main": "dist/index.js",
6
+ "module": "dist/esm/index.js",
6
7
  "types": "dist/index.d.ts",
7
8
  "exports": {
8
9
  ".": {
9
10
  "types": "./dist/index.d.ts",
11
+ "import": "./dist/esm/index.js",
10
12
  "require": "./dist/index.js",
11
13
  "default": "./dist/index.js"
12
14
  }
@@ -14,15 +16,22 @@
14
16
  "files": [
15
17
  "dist",
16
18
  "README.md",
17
- "LICENSE"
19
+ "LICENSE",
20
+ "CHANGELOG.md",
21
+ "CONTRIBUTING.md"
18
22
  ],
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
19
26
  "sideEffects": false,
20
27
  "scripts": {
21
28
  "test": "jest --runInBand",
22
29
  "dev": "node scripts/demo.mjs",
23
30
  "start": "node scripts/demo.mjs",
24
- "transpile": "tsc",
25
- "build:lib": "tsc",
31
+ "transpile": "npm run build:lib",
32
+ "build:cjs": "tsc",
33
+ "build:esm": "tsc -p tsconfig.esm.json",
34
+ "build:lib": "npm run build:cjs && npm run build:esm && node -e \"require('fs').writeFileSync('dist/esm/package.json', '{\\\"type\\\": \\\"module\\\"}')\"",
26
35
  "prepublishOnly": "npm test && npm run build:lib",
27
36
  "build": "node scripts/demo.mjs --production",
28
37
  "deploy": "gh-pages -d example/dist",