react-intl-number-input 0.8.2 → 0.8.4
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 +125 -0
- package/CONTRIBUTING.md +120 -0
- package/README.md +42 -9
- package/dist/esm/index.js +273 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/index.js +52 -3
- package/dist/index.js.map +1 -0
- package/package.json +13 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
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.4] - 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.3] - 2026-09-03
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Dual build support: the package now ships both CommonJS (`dist/index.js`) and ESM (`dist/esm/index.js`) versions.
|
|
22
|
+
- Sourcemaps with inline sources included in the package for better debugging experience.
|
|
23
|
+
- `CHANGELOG.md` and `CONTRIBUTING.md` are now included in the npm package.
|
|
24
|
+
- `engines` field added to `package.json` specifying Node.js >= 20.
|
|
25
|
+
- Proper accessibility support: the input now has `role="spinbutton"` and relevant `aria-valuenow`, `aria-valuemin`, and `aria-valuemax` attributes.
|
|
26
|
+
- Full `forwardRef` support: `ref` now correctly points to the underlying `<input>` element and supports imperative methods via `useImperativeHandle`.
|
|
27
|
+
- Demo and build scripts migrated to TypeScript and modern ES modules.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- Improved cursor management: typing within the masked input now correctly preserves the cursor position instead of jumping to the end.
|
|
32
|
+
- Enhanced synthetic events: `onChange` events triggered by step buttons or `setValue` now include more standard event properties (`currentTarget`, `persist`, etc.) for better compatibility.
|
|
33
|
+
- Build process updated to use multiple `tsc` configurations for CJS and ESM.
|
|
34
|
+
- `package.json` `exports` updated to support modern ESM resolution while maintaining CJS compatibility.
|
|
35
|
+
|
|
36
|
+
## [0.8.2] - 2026-08-15
|
|
37
|
+
|
|
38
|
+
This is a maintainer/toolchain release. The published component API, peer dependency (`react >=16.8.0 <20`), and CommonJS entry are unchanged.
|
|
39
|
+
|
|
40
|
+
### Security
|
|
41
|
+
|
|
42
|
+
- Pinned transitive `js-yaml` to `4.3.1` ([GHSA-5p4m-2wfm-xmqj](https://github.com/advisories/GHSA-5p4m-2wfm-xmqj))
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- Replaced the Babel + Webpack toolchain with TypeScript (`tsc`) for the library and esbuild for the demo
|
|
47
|
+
- Dropped the `fast-uri` override (it only existed for webpack-dev-server)
|
|
48
|
+
- Contributor Node.js requirement is now 20+ (no longer tied to Babel 8)
|
|
49
|
+
|
|
50
|
+
## [0.8.1] - 2026-08-13
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- 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`
|
|
55
|
+
- Upgraded `@testing-library/jest-dom` from `6.9.1` to `7.0.1`
|
|
56
|
+
|
|
57
|
+
## [0.8.0] - 2026-07-27
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
|
|
61
|
+
- `renderControls` render prop with exported `ControlsRenderProps` for custom stepper UI (`increment`, `decrement`, `setValue`, bounds, and formatted value)
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
|
|
65
|
+
- Step increments now use display units: `step={1}` with `precision={2}` adds `0.01` (previously added `100` to the numeric value)
|
|
66
|
+
- `renderControls` renders the input and controls as siblings without an opinionated wrapper — layout is controlled by the consumer
|
|
67
|
+
- When both `renderControls` and `showStepButtons` are set, `renderControls` takes precedence
|
|
68
|
+
- Development toolchain upgraded: Babel 8, TypeScript 7, Webpack 5.108, webpack-cli 7.2, webpack-dev-server 6
|
|
69
|
+
- Babel config updated for Babel 8 defaults (`modules: "commonjs"`, classic JSX runtime) so the published CJS build remains compatible with React 16.8+
|
|
70
|
+
- Contributor Node.js requirement updated to `^22.18.0 || >=24.11.0` (Babel 8)
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
|
|
74
|
+
- 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)
|
|
75
|
+
- Controlled `value` prop updates now apply `minValue`/`maxValue` clamping before formatting
|
|
76
|
+
- Resolved transitive security advisories in the dev dependency tree (`npm audit` clean)
|
|
77
|
+
|
|
78
|
+
## [0.7.0] - 2026-06-20
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
|
|
82
|
+
- TypeScript source and published type declarations (`IntlNumberInputProps`)
|
|
83
|
+
- `forwardRef` support for the underlying `<input>`
|
|
84
|
+
- Standard HTML input attribute passthrough (`onFocus`, `aria-*`, `data-*`, `autoComplete`, etc.)
|
|
85
|
+
- Step buttons (`showStepButtons`, `step`)
|
|
86
|
+
- `minValue` / `maxValue` constraints
|
|
87
|
+
- `onBlur` callback
|
|
88
|
+
- `inputMode` prop
|
|
89
|
+
- Prefix/suffix HTML sanitization
|
|
90
|
+
- Redesigned interactive demo page with labeled examples and independent state
|
|
91
|
+
- `CONTRIBUTING.md` and `CHANGELOG.md` documentation
|
|
92
|
+
|
|
93
|
+
### Changed
|
|
94
|
+
|
|
95
|
+
- Migrated from JSX to TypeScript
|
|
96
|
+
- Removed runtime `prop-types` dependency (TypeScript types are the source of truth)
|
|
97
|
+
- `onBlur` now returns clamped numeric values consistent with `onChange`
|
|
98
|
+
- Prefix/suffix sanitization is SSR-safe (no `document` access)
|
|
99
|
+
- `peerDependencies` now require only `react` (not `react-dom`)
|
|
100
|
+
- Split consumer and maintainer documentation (`README.md`, `CONTRIBUTING.md`)
|
|
101
|
+
|
|
102
|
+
### Fixed
|
|
103
|
+
|
|
104
|
+
- SSR crash when rendering on the server
|
|
105
|
+
- `onBlur` returning unclamped values while display was clamped
|
|
106
|
+
|
|
107
|
+
## [0.6.0] - 2025
|
|
108
|
+
|
|
109
|
+
### Changed
|
|
110
|
+
|
|
111
|
+
- Updated build toolchain to Babel 7 and Webpack 5
|
|
112
|
+
- Added Jest test suite
|
|
113
|
+
- Updated demo to React 19
|
|
114
|
+
- Security updates for vulnerable dependencies
|
|
115
|
+
|
|
116
|
+
## [0.5.0] and earlier
|
|
117
|
+
|
|
118
|
+
See [git history](https://github.com/nandorip/react-intl-number-input/commits/master) for changes prior to 0.6.0.
|
|
119
|
+
|
|
120
|
+
[Unreleased]: https://github.com/nandorip/react-intl-number-input/compare/v0.8.2...HEAD
|
|
121
|
+
[0.8.2]: https://github.com/nandorip/react-intl-number-input/compare/v0.8.1...v0.8.2
|
|
122
|
+
[0.8.1]: https://github.com/nandorip/react-intl-number-input/compare/v0.8.0...v0.8.1
|
|
123
|
+
[0.8.0]: https://github.com/nandorip/react-intl-number-input/compare/v0.7.0...v0.8.0
|
|
124
|
+
[0.7.0]: https://github.com/nandorip/react-intl-number-input/compare/v0.6.0...v0.7.0
|
|
125
|
+
[0.6.0]: https://github.com/nandorip/react-intl-number-input/releases/tag/v0.6.0
|
package/CONTRIBUTING.md
ADDED
|
@@ -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
|
-
```
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
const react_1 = __importStar(require("react"));
|
|
37
37
|
function sanitizeString(str) {
|
|
38
|
-
return str.replace(/<[^>]*>/g, '');
|
|
38
|
+
return str.replace(/<[^>]*>/g, '').replace(/on\w+\s*=\s*["'][^"']*["']/g, '');
|
|
39
39
|
}
|
|
40
40
|
function resolveLocale(locale) {
|
|
41
41
|
try {
|
|
@@ -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;
|
|
@@ -84,7 +86,7 @@ const IntlNumberInput = (0, react_1.forwardRef)(function IntlNumberInput({ local
|
|
|
84
86
|
}, []);
|
|
85
87
|
const getNumberValue = (0, react_1.useCallback)((strValue) => {
|
|
86
88
|
const inputValue = String(strValue || '');
|
|
87
|
-
const hasNegativeSign = inputValue.
|
|
89
|
+
const hasNegativeSign = inputValue.startsWith('-');
|
|
88
90
|
const onlyDigits = inputValue.replace(/[^0-9]/g, '');
|
|
89
91
|
if (!onlyDigits) {
|
|
90
92
|
return hasNegativeSign ? -0 : 0;
|
|
@@ -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:
|
|
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
|
+
"version": "0.8.4",
|
|
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": "
|
|
25
|
-
"build:
|
|
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",
|