react-intl-number-input 0.8.6 → 0.9.0
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 +39 -0
- package/CONTRIBUTING.md +49 -24
- package/README.md +54 -22
- package/dist/esm/index.d.ts +34 -0
- package/dist/esm/index.js +151 -242
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/number.d.ts +7 -0
- package/dist/esm/number.js +73 -0
- package/dist/esm/number.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +151 -242
- package/dist/index.js.map +1 -1
- package/dist/number.d.ts +7 -0
- package/dist/number.js +82 -0
- package/dist/number.js.map +1 -0
- package/package.json +18 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"number.js","sourceRoot":"","sources":["../../src/number.ts"],"names":[],"mappings":"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","sourcesContent":["export function resolveLocale(locale: string): string {\n try {\n return Intl.NumberFormat.supportedLocalesOf([locale])[0] || 'en-US';\n } catch {\n return 'en-US';\n }\n}\n\nexport function clampToBounds(value: number, min?: number, max?: number): number {\n return Math.min(Math.max(value, min ?? -Infinity), max ?? Infinity);\n}\n\nexport function createDigitMap(locale: string): Map<string, string> {\n const formatter = new Intl.NumberFormat(locale, { useGrouping: false });\n const digits = new Map<string, string>();\n for (let digit = 0; digit <= 9; digit++) {\n const ascii = String(digit);\n const localized = formatter.formatToParts(digit)\n .filter((part) => part.type === 'integer').map((part) => part.value).join('');\n digits.set(ascii, ascii);\n digits.set(localized, ascii);\n }\n return digits;\n}\n\nexport function stripAffixes(text: string, prefix: string, suffix: string): string {\n if (prefix && text.startsWith(prefix)) text = text.slice(prefix.length);\n if (suffix && text.endsWith(suffix)) text = text.slice(0, -suffix.length);\n return text;\n}\n\nexport function parseNumber(text: string, digits: Map<string, string>, precision: number): number {\n const negative = /[-\\u2212]/.test(text);\n const ascii = Array.from(text).map((character) => digits.get(character) || '').join('');\n // Insert the implied decimal before conversion to avoid overflowing a digit string\n // that represents a finite number after scaling, and unnecessary double rounding.\n const padded = ascii.padStart(precision + 1, '0');\n const decimal = precision === 0 ? padded\n : `${padded.slice(0, -precision)}.${padded.slice(-precision)}`;\n const numeric = Number(decimal);\n return negative ? -numeric : numeric;\n}\n\nexport function preserveAffixEdit(previous: string, next: string, prefix: string, suffix: string): string {\n let start = 0;\n while (start < previous.length && start < next.length && previous[start] === next[start]) start++;\n let end = previous.length;\n let nextEnd = next.length;\n while (end > start && nextEnd > start && previous[end - 1] === next[nextEnd - 1]) {\n end--;\n nextEnd--;\n }\n const numericEnd = previous.length - suffix.length;\n const onlyPrefix = prefix.length > 0 && start < prefix.length && end <= prefix.length;\n const onlySuffix = suffix.length > 0 && start >= numericEnd && end > numericEnd;\n return onlyPrefix || onlySuffix ? previous : next;\n}\n\nfunction decimalPlaces(value: number): number {\n const [mantissa, exponent = '0'] = String(value).toLowerCase().split('e');\n return Math.max(0, (mantissa.split('.')[1]?.length || 0) - Number(exponent));\n}\n\nexport function addStep(value: number, delta: number): number {\n const places = Math.max(decimalPlaces(value), decimalPlaces(delta));\n const factor = Math.pow(10, places);\n const scaledValue = Math.round(value * factor);\n const scaledDelta = Math.round(delta * factor);\n const sum = scaledValue + scaledDelta;\n if (Number.isSafeInteger(scaledValue) && Number.isSafeInteger(scaledDelta) && Number.isSafeInteger(sum)) {\n return sum / factor;\n }\n return value + delta;\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export interface ControlsRenderProps {
|
|
|
7
7
|
min?: number;
|
|
8
8
|
max?: number;
|
|
9
9
|
disabled: boolean;
|
|
10
|
+
readOnly?: boolean;
|
|
10
11
|
step: number;
|
|
11
12
|
precision: number;
|
|
12
13
|
formattedValue: string;
|
|
@@ -17,6 +18,8 @@ type IntlNumberInputOwnProps = {
|
|
|
17
18
|
suffix?: string;
|
|
18
19
|
precision?: number;
|
|
19
20
|
value?: number | string;
|
|
21
|
+
/** Opt in to keeping value authoritative when the parent rejects an edit. */
|
|
22
|
+
controlled?: boolean;
|
|
20
23
|
showStepButtons?: boolean;
|
|
21
24
|
step?: number;
|
|
22
25
|
minValue?: number;
|
package/dist/index.js
CHANGED
|
@@ -34,274 +34,183 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
const react_1 = __importStar(require("react"));
|
|
37
|
+
const number_js_1 = require("./number.js");
|
|
38
|
+
// Keep the historical affix normalization for compatibility.
|
|
37
39
|
function sanitizeString(str) {
|
|
38
40
|
return str.replace(/<[^>]*>/g, '').replace(/on\w+\s*=\s*["'][^"']*["']/g, '');
|
|
39
41
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
return result;
|
|
42
|
+
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? react_1.useLayoutEffect : react_1.useEffect;
|
|
43
|
+
function createChangeEvent(input) {
|
|
44
|
+
const nativeEvent = new Event('change', { bubbles: true, cancelable: true });
|
|
45
|
+
const event = {
|
|
46
|
+
target: input,
|
|
47
|
+
currentTarget: input,
|
|
48
|
+
nativeEvent,
|
|
49
|
+
bubbles: true,
|
|
50
|
+
bubble: true,
|
|
51
|
+
cancelable: nativeEvent.cancelable,
|
|
52
|
+
get defaultPrevented() { return nativeEvent.defaultPrevented; },
|
|
53
|
+
eventPhase: nativeEvent.eventPhase,
|
|
54
|
+
isTrusted: false,
|
|
55
|
+
timeStamp: nativeEvent.timeStamp,
|
|
56
|
+
type: 'change',
|
|
57
|
+
preventDefault: () => nativeEvent.preventDefault(),
|
|
58
|
+
stopPropagation: () => nativeEvent.stopPropagation(),
|
|
59
|
+
isDefaultPrevented: () => nativeEvent.defaultPrevented,
|
|
60
|
+
isPropagationStopped: () => nativeEvent.cancelBubble,
|
|
61
|
+
persist: () => { },
|
|
62
|
+
};
|
|
63
|
+
return event;
|
|
64
64
|
}
|
|
65
|
-
const IntlNumberInput = (0, react_1.forwardRef)(function IntlNumberInput({ locale = 'en-US', prefix = '', suffix = '', precision = 2, value, showStepButtons = false, step = 1, minValue, maxValue, inputMode: inputModeProp, onChange, onBlur, disabled = false, renderControls, ...inputProps }, ref) {
|
|
65
|
+
const IntlNumberInput = (0, react_1.forwardRef)(function IntlNumberInput({ locale = 'en-US', prefix = '', suffix = '', precision = 2, value, controlled = false, showStepButtons = false, step = 1, minValue, maxValue, inputMode: inputModeProp, onChange, onBlur, onKeyDown: onKeyDownProp, disabled = false, renderControls, ...inputProps }, ref) {
|
|
66
66
|
const innerRef = (0, react_1.useRef)(null);
|
|
67
67
|
(0, react_1.useImperativeHandle)(ref, () => innerRef.current);
|
|
68
|
-
const safePrecision =
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}, [
|
|
68
|
+
const safePrecision = typeof precision !== 'number' || Number.isNaN(precision)
|
|
69
|
+
? 2 : Math.min(Math.max(Math.round(precision), 0), 20);
|
|
70
|
+
const safeStep = Number.isFinite(step) && step > 0 ? step : 1;
|
|
71
|
+
const safePrefix = (0, react_1.useMemo)(() => sanitizeString(prefix), [prefix]);
|
|
72
|
+
const safeSuffix = (0, react_1.useMemo)(() => sanitizeString(suffix), [suffix]);
|
|
73
|
+
const resolvedLocale = (0, react_1.useMemo)(() => (0, number_js_1.resolveLocale)(locale), [locale]);
|
|
74
|
+
const digitMap = (0, react_1.useMemo)(() => (0, number_js_1.createDigitMap)(resolvedLocale), [resolvedLocale]);
|
|
75
|
+
const numberFormatter = (0, react_1.useMemo)(() => new Intl.NumberFormat(resolvedLocale, {
|
|
76
|
+
minimumFractionDigits: safePrecision, maximumFractionDigits: safePrecision,
|
|
77
|
+
}), [resolvedLocale, safePrecision]);
|
|
78
78
|
const [effectiveMin, effectiveMax] = (0, react_1.useMemo)(() => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return [minValue, maxValue];
|
|
79
|
+
const min = Number.isFinite(minValue) ? minValue : undefined;
|
|
80
|
+
const max = Number.isFinite(maxValue) ? maxValue : undefined;
|
|
81
|
+
return min !== undefined && max !== undefined && min > max ? [max, min] : [min, max];
|
|
83
82
|
}, [minValue, maxValue]);
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
83
|
+
const normalize = (0, react_1.useCallback)((numeric) => (0, number_js_1.clampToBounds)(Number.isFinite(numeric) ? numeric : 0, effectiveMin, effectiveMax), [effectiveMin, effectiveMax]);
|
|
84
|
+
const getNumberValue = (0, react_1.useCallback)((text) => (0, number_js_1.parseNumber)((0, number_js_1.stripAffixes)(text, safePrefix, safeSuffix), digitMap, safePrecision), [safePrefix, safeSuffix, digitMap, safePrecision]);
|
|
85
|
+
const fromProp = () => normalize(typeof value === 'string' ? getNumberValue(value) : value !== null && value !== void 0 ? value : 0);
|
|
86
|
+
const [state, setState] = (0, react_1.useState)(() => ({ source: value, numeric: fromProp() }));
|
|
87
|
+
const [editVersion, setEditVersion] = (0, react_1.useState)(0);
|
|
88
|
+
// Synchronize changed external values before rendering. An unchanged value retains
|
|
89
|
+
// the optimistic editing behavior of releases before controlled was introduced.
|
|
90
|
+
let numericValue = controlled ? fromProp() : normalize(state.numeric);
|
|
91
|
+
if (!Object.is(state.source, value)) {
|
|
92
|
+
numericValue = value === undefined && !controlled ? normalize(state.numeric) : fromProp();
|
|
93
|
+
setState({ source: value, numeric: numericValue });
|
|
94
|
+
}
|
|
95
|
+
else if (!controlled && !Object.is(state.numeric, numericValue)) {
|
|
96
|
+
setState({ source: value, numeric: numericValue });
|
|
97
|
+
}
|
|
98
|
+
const formatNumber = (0, react_1.useCallback)((numeric) => `${safePrefix}${numberFormatter.format(numeric)}${safeSuffix}`, [safePrefix, safeSuffix, numberFormatter]);
|
|
99
|
+
const maskedValue = formatNumber(numericValue);
|
|
100
|
+
// Cache for several custom-control calls in one event; rendered state is numericValue.
|
|
101
|
+
const numericValueRef = (0, react_1.useRef)(numericValue);
|
|
102
|
+
const selectionRef = (0, react_1.useRef)(null);
|
|
103
|
+
const countDigits = (text) => Array.from(text).filter((char) => digitMap.has(char)).length;
|
|
104
|
+
useIsomorphicLayoutEffect(() => {
|
|
105
|
+
numericValueRef.current = numericValue;
|
|
106
|
+
if (!selectionRef.current || !innerRef.current)
|
|
107
|
+
return;
|
|
108
|
+
const input = innerRef.current;
|
|
109
|
+
const { digitsBefore } = selectionRef.current;
|
|
110
|
+
const numericText = (0, number_js_1.stripAffixes)(input.value, safePrefix, safeSuffix);
|
|
111
|
+
let position = safePrefix.length;
|
|
112
|
+
let found = 0;
|
|
113
|
+
for (const character of numericText) {
|
|
114
|
+
if (found >= digitsBefore)
|
|
115
|
+
break;
|
|
116
|
+
position += character.length;
|
|
117
|
+
if (digitMap.has(character))
|
|
118
|
+
found++;
|
|
114
119
|
}
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
input.setSelectionRange(position, position);
|
|
121
|
+
selectionRef.current = null;
|
|
122
|
+
}, [numericValue, maskedValue, editVersion, safePrefix, safeSuffix, digitMap]);
|
|
123
|
+
const readOnly = !!inputProps.readOnly;
|
|
124
|
+
const commitValue = (0, react_1.useCallback)((next, event) => {
|
|
125
|
+
if (disabled || readOnly || !Number.isFinite(next))
|
|
126
|
+
return;
|
|
127
|
+
const nextValue = normalize(next);
|
|
128
|
+
const nextMasked = formatNumber(nextValue);
|
|
129
|
+
if (!controlled) {
|
|
130
|
+
numericValueRef.current = nextValue;
|
|
131
|
+
setState((previous) => ({ ...previous, numeric: nextValue }));
|
|
117
132
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return `${safePrefix}${formattedNumber}${safeSuffix}`;
|
|
123
|
-
}, [getNumberValue, numberFormatter, prefix, suffix, effectiveMin, effectiveMax]);
|
|
124
|
-
const getInitialValue = (0, react_1.useCallback)(() => {
|
|
125
|
-
const initial = value !== undefined ? value : 0;
|
|
126
|
-
return formatNumber(initial);
|
|
127
|
-
}, [value, formatNumber]);
|
|
128
|
-
const getInitialNumericValue = (0, react_1.useCallback)(() => {
|
|
129
|
-
const initial = value !== undefined ? value : 0;
|
|
130
|
-
if (typeof initial === 'string') {
|
|
131
|
-
return getNumberValue(initial);
|
|
133
|
+
// Reapply the caret even if normalization leaves the formatted value unchanged.
|
|
134
|
+
setEditVersion((previous) => previous + 1);
|
|
135
|
+
if (event) {
|
|
136
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(event, nextValue, nextMasked);
|
|
132
137
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
}
|
|
138
|
+
else if (innerRef.current) {
|
|
139
|
+
const input = innerRef.current;
|
|
140
|
+
input.value = nextMasked;
|
|
141
|
+
try {
|
|
142
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(createChangeEvent(input), nextValue, nextMasked);
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
input.value = controlled ? maskedValue : nextMasked;
|
|
153
146
|
}
|
|
154
|
-
innerRef.current.setSelectionRange(newPos, newPos);
|
|
155
|
-
selectionRef.current = null;
|
|
156
147
|
}
|
|
157
|
-
}, [maskedValue]);
|
|
158
|
-
|
|
159
|
-
|
|
148
|
+
}, [disabled, readOnly, normalize, formatNumber, controlled, onChange, maskedValue]);
|
|
149
|
+
const handleChange = (event) => {
|
|
150
|
+
var _a;
|
|
151
|
+
if (disabled || readOnly)
|
|
160
152
|
return;
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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 };
|
|
172
|
-
const rawValue = event.target.value;
|
|
173
|
-
const newValue = getNumberValue(rawValue);
|
|
174
|
-
if (!isValidNumber(newValue)) {
|
|
153
|
+
const raw = (0, number_js_1.preserveAffixEdit)(maskedValue, event.target.value, safePrefix, safeSuffix);
|
|
154
|
+
const numericTextBeforeCaret = (0, number_js_1.stripAffixes)(raw.slice(0, (_a = event.target.selectionStart) !== null && _a !== void 0 ? _a : 0), safePrefix, '');
|
|
155
|
+
selectionRef.current = { digitsBefore: countDigits(numericTextBeforeCaret) };
|
|
156
|
+
const next = getNumberValue(raw);
|
|
157
|
+
if (!Number.isFinite(next)) {
|
|
158
|
+
selectionRef.current = null;
|
|
175
159
|
return;
|
|
176
160
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const rawValue = event.target.value;
|
|
187
|
-
const currentValue = getNumberValue(rawValue);
|
|
188
|
-
const clampedValue = clampToBounds(currentValue, effectiveMin, effectiveMax);
|
|
189
|
-
const formattedValue = formatNumber(clampedValue);
|
|
190
|
-
setMaskedValue(formattedValue);
|
|
191
|
-
numericValueRef.current = clampedValue;
|
|
192
|
-
if (onBlur) {
|
|
193
|
-
onBlur(event, clampedValue, formattedValue);
|
|
161
|
+
commitValue(next, event);
|
|
162
|
+
};
|
|
163
|
+
const handleBlur = (event) => {
|
|
164
|
+
const raw = event.target.value;
|
|
165
|
+
const parsed = raw === maskedValue || disabled || readOnly ? numericValue : getNumberValue(raw);
|
|
166
|
+
const nextValue = normalize(Number.isFinite(parsed) ? parsed : numericValue);
|
|
167
|
+
if (!controlled && !disabled && !readOnly) {
|
|
168
|
+
numericValueRef.current = nextValue;
|
|
169
|
+
setState((previous) => ({ ...previous, numeric: nextValue }));
|
|
194
170
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
171
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur(event, nextValue, formatNumber(nextValue));
|
|
172
|
+
};
|
|
173
|
+
const handleStep = (0, react_1.useCallback)((direction, multiplier = 1) => {
|
|
174
|
+
if (!Number.isFinite(multiplier))
|
|
198
175
|
return;
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
numericValueRef.current = newValue;
|
|
205
|
-
const syntheticEvent = {
|
|
206
|
-
target: {
|
|
207
|
-
value: newMaskedValue,
|
|
208
|
-
name: inputProps.name || '',
|
|
209
|
-
id: inputProps.id || '',
|
|
210
|
-
},
|
|
211
|
-
currentTarget: {
|
|
212
|
-
value: newMaskedValue,
|
|
213
|
-
name: inputProps.name || '',
|
|
214
|
-
id: inputProps.id || '',
|
|
215
|
-
},
|
|
216
|
-
persist: () => { },
|
|
217
|
-
preventDefault: () => { },
|
|
218
|
-
stopPropagation: () => { },
|
|
219
|
-
bubble: true,
|
|
220
|
-
cancelable: false,
|
|
221
|
-
type: 'change',
|
|
222
|
-
};
|
|
223
|
-
if (onChange) {
|
|
224
|
-
onChange(syntheticEvent, newValue, newMaskedValue);
|
|
225
|
-
}
|
|
226
|
-
}, [
|
|
227
|
-
disabled,
|
|
228
|
-
safeStep,
|
|
229
|
-
safePrecision,
|
|
230
|
-
effectiveMin,
|
|
231
|
-
effectiveMax,
|
|
232
|
-
formatNumber,
|
|
233
|
-
onChange,
|
|
234
|
-
inputProps.name,
|
|
235
|
-
inputProps.id,
|
|
236
|
-
]);
|
|
176
|
+
const delta = direction * (safeStep / Math.pow(10, safePrecision)) * multiplier;
|
|
177
|
+
if (!Number.isFinite(delta))
|
|
178
|
+
return;
|
|
179
|
+
commitValue((0, number_js_1.addStep)(numericValueRef.current, delta));
|
|
180
|
+
}, [safeStep, safePrecision, commitValue]);
|
|
237
181
|
const increment = (0, react_1.useCallback)((multiplier = 1) => handleStep(1, multiplier), [handleStep]);
|
|
238
182
|
const decrement = (0, react_1.useCallback)((multiplier = 1) => handleStep(-1, multiplier), [handleStep]);
|
|
239
|
-
const setValue = (0, react_1.useCallback)((
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
name: inputProps.name || '',
|
|
248
|
-
id: inputProps.id || '',
|
|
249
|
-
},
|
|
250
|
-
currentTarget: {
|
|
251
|
-
value: newMaskedValue,
|
|
252
|
-
name: inputProps.name || '',
|
|
253
|
-
id: inputProps.id || '',
|
|
254
|
-
},
|
|
255
|
-
persist: () => { },
|
|
256
|
-
preventDefault: () => { },
|
|
257
|
-
stopPropagation: () => { },
|
|
258
|
-
bubble: true,
|
|
259
|
-
cancelable: false,
|
|
260
|
-
type: 'change',
|
|
261
|
-
};
|
|
262
|
-
if (onChange) {
|
|
263
|
-
onChange(syntheticEvent, clampedValue, newMaskedValue);
|
|
264
|
-
}
|
|
265
|
-
}, [effectiveMin, effectiveMax, formatNumber, onChange, inputProps.name, inputProps.id]);
|
|
266
|
-
const inputMode = (0, react_1.useMemo)(() => {
|
|
267
|
-
if (inputModeProp) {
|
|
268
|
-
return inputModeProp;
|
|
183
|
+
const setValue = (0, react_1.useCallback)((next) => commitValue(next), [commitValue]);
|
|
184
|
+
const handleKeyDown = (event) => {
|
|
185
|
+
onKeyDownProp === null || onKeyDownProp === void 0 ? void 0 : onKeyDownProp(event);
|
|
186
|
+
if (event.defaultPrevented || disabled || readOnly)
|
|
187
|
+
return;
|
|
188
|
+
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
|
|
189
|
+
event.preventDefault();
|
|
190
|
+
handleStep(event.key === 'ArrowUp' ? 1 : -1, event.shiftKey ? 10 : 1);
|
|
269
191
|
}
|
|
270
|
-
|
|
271
|
-
|
|
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 }));
|
|
192
|
+
};
|
|
193
|
+
const inputElement = react_1.default.createElement("input", { ref: innerRef, type: "text", role: "spinbutton", "aria-valuenow": numericValue, "aria-valuemin": effectiveMin, "aria-valuemax": effectiveMax, "aria-valuetext": maskedValue, inputMode: inputModeProp || (safePrecision > 0 ? 'decimal' : 'numeric'), value: maskedValue, disabled: disabled, onChange: handleChange, onBlur: handleBlur, onKeyDown: handleKeyDown, ...inputProps });
|
|
273
194
|
if (renderControls) {
|
|
274
|
-
|
|
275
|
-
increment,
|
|
276
|
-
decrement,
|
|
277
|
-
setValue,
|
|
278
|
-
value: numericValueRef.current,
|
|
279
|
-
min: effectiveMin,
|
|
280
|
-
max: effectiveMax,
|
|
281
|
-
disabled,
|
|
282
|
-
step: safeStep,
|
|
283
|
-
precision: safePrecision,
|
|
284
|
-
formattedValue: maskedValue,
|
|
285
|
-
};
|
|
286
|
-
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
195
|
+
return react_1.default.createElement(react_1.default.Fragment, null,
|
|
287
196
|
inputElement,
|
|
288
|
-
renderControls(
|
|
197
|
+
renderControls({
|
|
198
|
+
increment, decrement, setValue, value: numericValue,
|
|
199
|
+
min: effectiveMin, max: effectiveMax, disabled, readOnly,
|
|
200
|
+
step: safeStep, precision: safePrecision, formattedValue: maskedValue,
|
|
201
|
+
}));
|
|
289
202
|
}
|
|
290
|
-
if (!showStepButtons)
|
|
203
|
+
if (!showStepButtons)
|
|
291
204
|
return inputElement;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
205
|
+
const controlsDisabled = disabled || readOnly;
|
|
206
|
+
const buttonStyle = {
|
|
207
|
+
padding: '4px 8px', cursor: controlsDisabled ? 'not-allowed' : 'pointer',
|
|
208
|
+
opacity: controlsDisabled ? 0.5 : 1,
|
|
209
|
+
};
|
|
210
|
+
return react_1.default.createElement("div", { style: { display: 'inline-flex', alignItems: 'center', gap: '4px' } },
|
|
211
|
+
react_1.default.createElement("button", { type: "button", onClick: () => decrement(), disabled: controlsDisabled, "aria-label": "Decrease value", style: buttonStyle }, "-"),
|
|
299
212
|
inputElement,
|
|
300
|
-
react_1.default.createElement("button", { type: "button", onClick: () =>
|
|
301
|
-
padding: '4px 8px',
|
|
302
|
-
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
303
|
-
opacity: disabled ? 0.5 : 1,
|
|
304
|
-
} }, "+")));
|
|
213
|
+
react_1.default.createElement("button", { type: "button", onClick: () => increment(), disabled: controlsDisabled, "aria-label": "Increase value", style: buttonStyle }, "+"));
|
|
305
214
|
});
|
|
306
215
|
IntlNumberInput.displayName = 'IntlNumberInput';
|
|
307
216
|
exports.default = (0, react_1.memo)(IntlNumberInput);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAUe;AA6Cf,SAAS,cAAc,CAAC,GAAW;IACjC,OAAO,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,6BAA6B,EAAE,EAAE,CAAC,CAAC;AAChF,CAAC;AAED,SAAS,aAAa,CAAC,MAAc;IACnC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QACjE,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACvD,CAAC;eAAO,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;IAClC,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,KAAK,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;IACpD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,aAAa,CACpB,KAAa,EACb,GAAuB,EACvB,GAAuB;IAEvB,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,IAAI,GAAG,KAAK,SAAS,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,CAAC;IACf,CAAC;IAED,IAAI,GAAG,KAAK,SAAS,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,CAAC;IACf,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,eAAe,GAAG,IAAA,kBAAU,EAChC,SAAS,eAAe,CACtB,EACE,MAAM,GAAG,OAAO,EAChB,MAAM,GAAG,EAAE,EACX,MAAM,GAAG,EAAE,EACX,SAAS,GAAG,CAAC,EACb,KAAK,EACL,eAAe,GAAG,KAAK,EACvB,IAAI,GAAG,CAAC,EACR,QAAQ,EACR,QAAQ,EACR,SAAS,EAAE,aAAa,EACxB,QAAQ,EACR,MAAM,EACN,QAAQ,GAAG,KAAK,EAChB,cAAc,EACd,GAAG,UAAU,EACd,EACD,GAAG;IAEH,MAAM,QAAQ,GAAG,IAAA,cAAM,EAAmB,IAAI,CAAC,CAAC;IAChD,IAAA,2BAAmB,EAAC,GAAG,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAQ,CAAC,CAAC;IAElD,MAAM,aAAa,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE;QACjC,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;YAAE,OAAO,CAAC,CAAC;QACvE,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7C,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,MAAM,QAAQ,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE;QAC5B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,MAAM,CAAC,YAAY,EAAE,YAAY,CAAC,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE;QAChD,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,GAAG,QAAQ,EAAE,CAAC;YAC5E,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAU,CAAC;QACvC,CAAC;QACD,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAU,CAAC;IACvC,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEzB,MAAM,iBAAiB,GAAG,IAAA,mBAAW,EAAC,CAAC,CAAS,EAAE,EAAE;QAClD,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACzB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,cAAc,GAAG,IAAA,mBAAW,EAChC,CAAC,QAAgB,EAAU,EAAE;QAC3B,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;QAC1C,MAAM,eAAe,GAAG,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAEnD,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAErD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QACvC,MAAM,WAAW,GAAG,WAAW,GAAG,iBAAiB,CAAC,aAAa,CAAC,CAAC;QAEnE,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC;IACtD,CAAC,EACD,CAAC,iBAAiB,EAAE,aAAa,CAAC,CACnC,CAAC;IAEF,MAAM,eAAe,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE;QACnC,MAAM,OAAO,GAAG;YACd,KAAK,EAAE,SAAkB;YACzB,qBAAqB,EAAE,aAAa;YACpC,qBAAqB,EAAE,aAAa;SACrC,CAAC;QAEF,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QAC7C,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;IAE5B,MAAM,YAAY,GAAG,IAAA,mBAAW,EAC9B,CAAC,GAAoB,EAAU,EAAE;QAC/B,IAAI,WAAmB,CAAC;QAExB,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,WAAW,GAAG,GAAG,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,WAAW,GAAG,CAAC,CAAC;QAClB,CAAC;QAED,WAAW,GAAG,aAAa,CAAC,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;QAErE,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAC5D,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QAE1C,OAAO,GAAG,UAAU,GAAG,eAAe,GAAG,UAAU,EAAE,CAAC;IACxD,CAAC,EACD,CAAC,cAAc,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,CAAC,CAC9E,CAAC;IAEF,MAAM,eAAe,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;QACvC,MAAM,OAAO,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC;IAE1B,MAAM,sBAAsB,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;QAC9C,MAAM,OAAO,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC;IAE5B,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,IAAA,gBAAQ,EAAS,eAAe,CAAC,CAAC;IACxE,MAAM,eAAe,GAAG,IAAA,cAAM,EAAS,sBAAsB,EAAE,CAAC,CAAC;IACjE,MAAM,YAAY,GAAG,IAAA,cAAM,EAAkC,IAAI,CAAC,CAAC;IAEnE,MAAM,yBAAyB,GAC7B,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,uBAAe,CAAC,CAAC,CAAC,iBAAS,CAAC;IAE9D,yBAAyB,CAAC,GAAG,EAAE;QAC7B,IAAI,YAAY,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YAC7C,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC;YAC9C,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC;YACxC,IAAI,MAAM,GAAG,CAAC,CAAC;YACf,IAAI,WAAW,GAAG,CAAC,CAAC;YAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACzC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC3B,WAAW,EAAE,CAAC;gBAChB,CAAC;gBACD,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC;gBACf,IAAI,WAAW,IAAI,YAAY,EAAE,CAAC;oBAChC,MAAM;gBACR,CAAC;YACH,CAAC;YAED,QAAQ,CAAC,OAAO,CAAC,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACnD,YAAY,CAAC,OAAO,GAAG,IAAI,CAAC;QAC9B,CAAC;IACH,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAElB,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO;QAChC,MAAM,UAAU,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC7E,MAAM,OAAO,GAAG,aAAa,CAAC,UAAU,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;QACtE,cAAc,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;QACtC,eAAe,CAAC,OAAO,GAAG,OAAO,CAAC;IACpC,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;IAEtE,MAAM,YAAY,GAAG,IAAA,mBAAW,EAC9B,CAAC,KAA0C,EAAE,EAAE;QAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3B,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,IAAI,CAAC,CAAC;QACjD,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC;QAChC,MAAM,YAAY,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC;QAExF,YAAY,CAAC,OAAO,GAAG,EAAE,YAAY,EAAE,CAAC;QAExC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC;QACpC,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QAE1C,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO;QACT,CAAC;QAED,MAAM,YAAY,GAAG,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;QACzE,MAAM,cAAc,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;QAClD,cAAc,CAAC,cAAc,CAAC,CAAC;QAC/B,eAAe,CAAC,OAAO,GAAG,YAAY,CAAC;QAEvC,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;QAChD,CAAC;IACH,CAAC,EACD,CAAC,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,CAAC,CACrE,CAAC;IAEF,MAAM,UAAU,GAAG,IAAA,mBAAW,EAC5B,CAAC,KAAyC,EAAE,EAAE;QAC5C,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC;QACpC,MAAM,YAAY,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,YAAY,GAAG,aAAa,CAAC,YAAY,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;QAC7E,MAAM,cAAc,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;QAElD,cAAc,CAAC,cAAc,CAAC,CAAC;QAC/B,eAAe,CAAC,OAAO,GAAG,YAAY,CAAC;QAEvC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC,EACD,CAAC,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,CAAC,CACnE,CAAC;IAEF,MAAM,UAAU,GAAG,IAAA,mBAAW,EAC5B,CAAC,KAAa,EAAE,UAAU,GAAW,CAAC,EAAE,EAAE;QACxC,IAAI,QAAQ;YAAE,OAAO;QAErB,MAAM,YAAY,GAAG,eAAe,CAAC,OAAO,CAAC;QAC7C,MAAM,SAAS,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC,GAAG,UAAU,CAAC;QACxE,MAAM,QAAQ,GAAG,aAAa,CAC5B,YAAY,GAAG,KAAK,GAAG,SAAS,EAChC,YAAY,EACZ,YAAY,CACb,CAAC;QAEF,MAAM,cAAc,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC9C,cAAc,CAAC,cAAc,CAAC,CAAC;QAC/B,eAAe,CAAC,OAAO,GAAG,QAAQ,CAAC;QAEnC,MAAM,cAAc,GAAG;YACrB,MAAM,EAAE;gBACN,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,UAAU,CAAC,IAAI,IAAI,EAAE;gBAC3B,EAAE,EAAE,UAAU,CAAC,EAAE,IAAI,EAAE;aACxB;YACD,aAAa,EAAE;gBACb,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,UAAU,CAAC,IAAI,IAAI,EAAE;gBAC3B,EAAE,EAAE,UAAU,CAAC,EAAE,IAAI,EAAE;aACxB;YACD,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;YACjB,cAAc,EAAE,GAAG,EAAE,GAAE,CAAC;YACxB,eAAe,EAAE,GAAG,EAAE,GAAE,CAAC;YACzB,MAAM,EAAE,IAAI;YACZ,UAAU,EAAE,KAAK;YACjB,IAAI,EAAE,QAAQ;SACmC,CAAC;QAEpD,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,cAAc,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;QACrD,CAAC;IACH,CAAC,EACD;QACE,QAAQ;QACR,QAAQ;QACR,aAAa;QACb,YAAY;QACZ,YAAY;QACZ,YAAY;QACZ,QAAQ;QACR,UAAU,CAAC,IAAI;QACf,UAAU,CAAC,EAAE;KACd,CACF,CAAC;IAEF,MAAM,SAAS,GAAG,IAAA,mBAAW,EAC3B,CAAC,UAAU,GAAW,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,UAAU,CAAC,EACrD,CAAC,UAAU,CAAC,CACb,CAAC;IAEF,MAAM,SAAS,GAAG,IAAA,mBAAW,EAC3B,CAAC,UAAU,GAAW,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,EACtD,CAAC,UAAU,CAAC,CACb,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAA,mBAAW,EAC1B,CAAC,QAAgB,EAAE,EAAE;QACnB,MAAM,YAAY,GAAG,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;QACzE,MAAM,cAAc,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;QAClD,cAAc,CAAC,cAAc,CAAC,CAAC;QAC/B,eAAe,CAAC,OAAO,GAAG,YAAY,CAAC;QAEvC,MAAM,cAAc,GAAG;YACrB,MAAM,EAAE;gBACN,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,UAAU,CAAC,IAAI,IAAI,EAAE;gBAC3B,EAAE,EAAE,UAAU,CAAC,EAAE,IAAI,EAAE;aACxB;YACD,aAAa,EAAE;gBACb,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,UAAU,CAAC,IAAI,IAAI,EAAE;gBAC3B,EAAE,EAAE,UAAU,CAAC,EAAE,IAAI,EAAE;aACxB;YACD,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;YACjB,cAAc,EAAE,GAAG,EAAE,GAAE,CAAC;YACxB,eAAe,EAAE,GAAG,EAAE,GAAE,CAAC;YACzB,MAAM,EAAE,IAAI;YACZ,UAAU,EAAE,KAAK;YACjB,IAAI,EAAE,QAAQ;SACmC,CAAC;QAEpD,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,cAAc,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;QACzD,CAAC;IACH,CAAC,EACD,CAAC,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC,CACrF,CAAC;IAEF,MAAM,SAAS,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE;QAC7B,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,aAAa,CAAC;QACvB,CAAC;QACD,OAAO,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACnD,CAAC,EAAE,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC;IAEnC,MAAM,YAAY,GAAG,CACnB,yCACE,GAAG,EAAE,QAAQ,EACb,IAAI,EAAC,MAAM,EACX,IAAI,EAAC,YAAY,mBACF,aAAa,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,mBACpE,YAAY,mBACZ,YAAY,EAC3B,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,UAAU,KACd,UAAU,GACd,CACH,CAAC;IAEF,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,aAAa,GAAwB;YACzC,SAAS;YACT,SAAS;YACT,QAAQ;YACR,KAAK,EAAE,eAAe,CAAC,OAAO;YAC9B,GAAG,EAAE,YAAY;YACjB,GAAG,EAAE,YAAY;YACjB,QAAQ;YACR,IAAI,EAAE,QAAQ;YACd,SAAS,EAAE,aAAa;YACxB,cAAc,EAAE,WAAW;SAC5B,CAAC;QAEF,OAAO,CACL;YACG,YAAY;YACZ,cAAc,CAAC,aAAa,CAAC,CAC7B,CACJ,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,OAAO,CACL,uCAAK,KAAK,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE;QACtE,0CACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAC7B,QAAQ,EAAE,QAAQ,gBACP,gBAAgB,EAC3B,KAAK,EAAE;gBACL,OAAO,EAAE,SAAS;gBAClB,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;gBAC5C,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;aAC5B,QAGM;QACR,YAAY;QACb,0CACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAC5B,QAAQ,EAAE,QAAQ,gBACP,gBAAgB,EAC3B,KAAK,EAAE;gBACL,OAAO,EAAE,SAAS;gBAClB,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;gBAC5C,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;aAC5B,QAGM,CACL,CACP,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,eAAe,CAAC,WAAW,GAAG,iBAAiB,CAAC;kBAEjC,IAAA,YAAI,EAAC,eAAe,CAAC","sourcesContent":["import React, {\r\n useState,\r\n useMemo,\r\n useCallback,\r\n useEffect,\r\n useLayoutEffect,\r\n useRef,\r\n useImperativeHandle,\r\n memo,\r\n forwardRef,\r\n} from 'react';\r\n\r\nexport interface ControlsRenderProps {\r\n increment: (multiplier?: number) => void;\r\n decrement: (multiplier?: number) => void;\r\n setValue: (value: number) => void;\r\n value: number;\r\n min?: number;\r\n max?: number;\r\n disabled: boolean;\r\n step: number;\r\n precision: number;\r\n formattedValue: string;\r\n}\r\n\r\ntype IntlNumberInputOwnProps = {\r\n locale?: string;\r\n prefix?: string;\r\n suffix?: string;\r\n precision?: number;\r\n value?: number | string;\r\n showStepButtons?: boolean;\r\n step?: number;\r\n minValue?: number;\r\n maxValue?: number;\r\n inputMode?: 'numeric' | 'decimal';\r\n onChange?: (\r\n event: React.ChangeEvent<HTMLInputElement>,\r\n value: number,\r\n maskedValue: string\r\n ) => void;\r\n onBlur?: (\r\n event: React.FocusEvent<HTMLInputElement>,\r\n value: number,\r\n maskedValue: string\r\n ) => void;\r\n renderControls?: (props: ControlsRenderProps) => React.ReactNode;\r\n};\r\n\r\nexport type IntlNumberInputProps = IntlNumberInputOwnProps &\r\n Omit<\r\n React.ComponentPropsWithoutRef<'input'>,\r\n keyof IntlNumberInputOwnProps | 'value' | 'type' | 'defaultValue' | 'children'\r\n >;\r\n\r\nfunction sanitizeString(str: string): string {\r\n return str.replace(/<[^>]*>/g, '').replace(/on\\w+\\s*=\\s*[\"'][^\"']*[\"']/g, '');\r\n}\r\n\r\nfunction resolveLocale(locale: string): string {\r\n try {\r\n const supported = Intl.NumberFormat.supportedLocalesOf([locale]);\r\n return supported.length > 0 ? supported[0] : 'en-US';\r\n } catch {\r\n return 'en-US';\r\n }\r\n}\r\n\r\nfunction isValidNumber(value: number): boolean {\r\n return Number.isFinite(value) && !Number.isNaN(value);\r\n}\r\n\r\nfunction clamp(value: number, min: number, max: number): number {\r\n return Math.min(Math.max(value, min), max);\r\n}\r\n\r\nfunction clampToBounds(\r\n value: number,\r\n min: number | undefined,\r\n max: number | undefined\r\n): number {\r\n let result = value;\r\n\r\n if (min !== undefined && result < min) {\r\n result = min;\r\n }\r\n\r\n if (max !== undefined && result > max) {\r\n result = max;\r\n }\r\n\r\n return result;\r\n}\r\n\r\nconst IntlNumberInput = forwardRef<HTMLInputElement, IntlNumberInputProps>(\r\n function IntlNumberInput(\r\n {\r\n locale = 'en-US',\r\n prefix = '',\r\n suffix = '',\r\n precision = 2,\r\n value,\r\n showStepButtons = false,\r\n step = 1,\r\n minValue,\r\n maxValue,\r\n inputMode: inputModeProp,\r\n onChange,\r\n onBlur,\r\n disabled = false,\r\n renderControls,\r\n ...inputProps\r\n },\r\n ref\r\n ) {\r\n const innerRef = useRef<HTMLInputElement>(null);\r\n useImperativeHandle(ref, () => innerRef.current!);\r\n\r\n const safePrecision = useMemo(() => {\r\n if (typeof precision !== 'number' || Number.isNaN(precision)) return 2;\r\n return clamp(Math.round(precision), 0, 20);\r\n }, [precision]);\r\n\r\n const safeStep = useMemo(() => {\r\n if (typeof step !== 'number' || Number.isNaN(step) || step <= 0) return 1;\r\n return step;\r\n }, [step]);\r\n\r\n const [effectiveMin, effectiveMax] = useMemo(() => {\r\n if (minValue !== undefined && maxValue !== undefined && minValue > maxValue) {\r\n return [maxValue, minValue] as const;\r\n }\r\n return [minValue, maxValue] as const;\r\n }, [minValue, maxValue]);\r\n\r\n const getPrecisionValue = useCallback((p: number) => {\r\n return Math.pow(10, p);\r\n }, []);\r\n\r\n const getNumberValue = useCallback(\r\n (strValue: string): number => {\r\n const inputValue = String(strValue || '');\r\n const hasNegativeSign = inputValue.startsWith('-');\r\n\r\n const onlyDigits = inputValue.replace(/[^0-9]/g, '');\r\n\r\n if (!onlyDigits) {\r\n return hasNegativeSign ? -0 : 0;\r\n }\r\n\r\n const numberValue = Number(onlyDigits);\r\n const parsedValue = numberValue / getPrecisionValue(safePrecision);\r\n\r\n return hasNegativeSign ? -parsedValue : parsedValue;\r\n },\r\n [getPrecisionValue, safePrecision]\r\n );\r\n\r\n const numberFormatter = useMemo(() => {\r\n const options = {\r\n style: 'decimal' as const,\r\n minimumFractionDigits: safePrecision,\r\n maximumFractionDigits: safePrecision,\r\n };\r\n\r\n const resolvedLocale = resolveLocale(locale);\r\n return new Intl.NumberFormat(resolvedLocale, options);\r\n }, [locale, safePrecision]);\r\n\r\n const formatNumber = useCallback(\r\n (val: number | string): string => {\r\n let numberValue: number;\r\n\r\n if (typeof val === 'string') {\r\n numberValue = getNumberValue(val);\r\n } else {\r\n numberValue = val;\r\n }\r\n\r\n if (!isValidNumber(numberValue)) {\r\n numberValue = 0;\r\n }\r\n\r\n numberValue = clampToBounds(numberValue, effectiveMin, effectiveMax);\r\n\r\n const formattedNumber = numberFormatter.format(numberValue);\r\n const safePrefix = sanitizeString(prefix);\r\n const safeSuffix = sanitizeString(suffix);\r\n\r\n return `${safePrefix}${formattedNumber}${safeSuffix}`;\r\n },\r\n [getNumberValue, numberFormatter, prefix, suffix, effectiveMin, effectiveMax]\r\n );\r\n\r\n const getInitialValue = useCallback(() => {\r\n const initial = value !== undefined ? value : 0;\r\n return formatNumber(initial);\r\n }, [value, formatNumber]);\r\n\r\n const getInitialNumericValue = useCallback(() => {\r\n const initial = value !== undefined ? value : 0;\r\n if (typeof initial === 'string') {\r\n return getNumberValue(initial);\r\n }\r\n return initial;\r\n }, [value, getNumberValue]);\r\n\r\n const [maskedValue, setMaskedValue] = useState<string>(getInitialValue);\r\n const numericValueRef = useRef<number>(getInitialNumericValue());\r\n const selectionRef = useRef<{ digitsBefore: number } | null>(null);\r\n\r\n const useIsomorphicLayoutEffect =\r\n typeof window !== 'undefined' ? useLayoutEffect : useEffect;\r\n\r\n useIsomorphicLayoutEffect(() => {\r\n if (selectionRef.current && innerRef.current) {\r\n const { digitsBefore } = selectionRef.current;\r\n const newValue = innerRef.current.value;\r\n let newPos = 0;\r\n let digitsFound = 0;\r\n\r\n for (let i = 0; i < newValue.length; i++) {\r\n if (/\\d/.test(newValue[i])) {\r\n digitsFound++;\r\n }\r\n newPos = i + 1;\r\n if (digitsFound >= digitsBefore) {\r\n break;\r\n }\r\n }\r\n\r\n innerRef.current.setSelectionRange(newPos, newPos);\r\n selectionRef.current = null;\r\n }\r\n }, [maskedValue]);\r\n\r\n useEffect(() => {\r\n if (value === undefined) return;\r\n const numericVal = typeof value === 'string' ? getNumberValue(value) : value;\r\n const clamped = clampToBounds(numericVal, effectiveMin, effectiveMax);\r\n setMaskedValue(formatNumber(clamped));\r\n numericValueRef.current = clamped;\r\n }, [value, formatNumber, getNumberValue, effectiveMin, effectiveMax]);\r\n\r\n const handleChange = useCallback(\r\n (event: React.ChangeEvent<HTMLInputElement>) => {\r\n const input = event.target;\r\n const selectionStart = input.selectionStart || 0;\r\n const valueBefore = input.value;\r\n const digitsBefore = valueBefore.substring(0, selectionStart).replace(/\\D/g, '').length;\r\n\r\n selectionRef.current = { digitsBefore };\r\n\r\n const rawValue = event.target.value;\r\n const newValue = getNumberValue(rawValue);\r\n\r\n if (!isValidNumber(newValue)) {\r\n return;\r\n }\r\n\r\n const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);\r\n const newMaskedValue = formatNumber(clampedValue);\r\n setMaskedValue(newMaskedValue);\r\n numericValueRef.current = clampedValue;\r\n\r\n if (onChange) {\r\n onChange(event, clampedValue, newMaskedValue);\r\n }\r\n },\r\n [getNumberValue, formatNumber, effectiveMin, effectiveMax, onChange]\r\n );\r\n\r\n const handleBlur = useCallback(\r\n (event: React.FocusEvent<HTMLInputElement>) => {\r\n const rawValue = event.target.value;\r\n const currentValue = getNumberValue(rawValue);\r\n const clampedValue = clampToBounds(currentValue, effectiveMin, effectiveMax);\r\n const formattedValue = formatNumber(clampedValue);\r\n\r\n setMaskedValue(formattedValue);\r\n numericValueRef.current = clampedValue;\r\n\r\n if (onBlur) {\r\n onBlur(event, clampedValue, formattedValue);\r\n }\r\n },\r\n [getNumberValue, formatNumber, effectiveMin, effectiveMax, onBlur]\r\n );\r\n\r\n const handleStep = useCallback(\r\n (delta: number, multiplier: number = 1) => {\r\n if (disabled) return;\r\n\r\n const currentValue = numericValueRef.current;\r\n const stepValue = (safeStep / Math.pow(10, safePrecision)) * multiplier;\r\n const newValue = clampToBounds(\r\n currentValue + delta * stepValue,\r\n effectiveMin,\r\n effectiveMax\r\n );\r\n\r\n const newMaskedValue = formatNumber(newValue);\r\n setMaskedValue(newMaskedValue);\r\n numericValueRef.current = newValue;\r\n\r\n const syntheticEvent = {\r\n target: {\r\n value: newMaskedValue,\r\n name: inputProps.name || '',\r\n id: inputProps.id || '',\r\n },\r\n currentTarget: {\r\n value: newMaskedValue,\r\n name: inputProps.name || '',\r\n id: inputProps.id || '',\r\n },\r\n persist: () => {},\r\n preventDefault: () => {},\r\n stopPropagation: () => {},\r\n bubble: true,\r\n cancelable: false,\r\n type: 'change',\r\n } as unknown as React.ChangeEvent<HTMLInputElement>;\r\n\r\n if (onChange) {\r\n onChange(syntheticEvent, newValue, newMaskedValue);\r\n }\r\n },\r\n [\r\n disabled,\r\n safeStep,\r\n safePrecision,\r\n effectiveMin,\r\n effectiveMax,\r\n formatNumber,\r\n onChange,\r\n inputProps.name,\r\n inputProps.id,\r\n ]\r\n );\r\n\r\n const increment = useCallback(\r\n (multiplier: number = 1) => handleStep(1, multiplier),\r\n [handleStep]\r\n );\r\n\r\n const decrement = useCallback(\r\n (multiplier: number = 1) => handleStep(-1, multiplier),\r\n [handleStep]\r\n );\r\n\r\n const setValue = useCallback(\r\n (newValue: number) => {\r\n const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);\r\n const newMaskedValue = formatNumber(clampedValue);\r\n setMaskedValue(newMaskedValue);\r\n numericValueRef.current = clampedValue;\r\n\r\n const syntheticEvent = {\r\n target: {\r\n value: newMaskedValue,\r\n name: inputProps.name || '',\r\n id: inputProps.id || '',\r\n },\r\n currentTarget: {\r\n value: newMaskedValue,\r\n name: inputProps.name || '',\r\n id: inputProps.id || '',\r\n },\r\n persist: () => {},\r\n preventDefault: () => {},\r\n stopPropagation: () => {},\r\n bubble: true,\r\n cancelable: false,\r\n type: 'change',\r\n } as unknown as React.ChangeEvent<HTMLInputElement>;\r\n\r\n if (onChange) {\r\n onChange(syntheticEvent, clampedValue, newMaskedValue);\r\n }\r\n },\r\n [effectiveMin, effectiveMax, formatNumber, onChange, inputProps.name, inputProps.id]\r\n );\r\n\r\n const inputMode = useMemo(() => {\r\n if (inputModeProp) {\r\n return inputModeProp;\r\n }\r\n return safePrecision > 0 ? 'decimal' : 'numeric';\r\n }, [inputModeProp, safePrecision]);\r\n\r\n const inputElement = (\r\n <input\r\n ref={innerRef}\r\n type=\"text\"\r\n role=\"spinbutton\"\r\n aria-valuenow={isValidNumber(numericValueRef.current) ? numericValueRef.current : 0}\r\n aria-valuemin={effectiveMin}\r\n aria-valuemax={effectiveMax}\r\n inputMode={inputMode}\r\n value={maskedValue}\r\n disabled={disabled}\r\n onChange={handleChange}\r\n onBlur={handleBlur}\r\n {...inputProps}\r\n />\r\n );\r\n\r\n if (renderControls) {\r\n const controlsProps: ControlsRenderProps = {\r\n increment,\r\n decrement,\r\n setValue,\r\n value: numericValueRef.current,\r\n min: effectiveMin,\r\n max: effectiveMax,\r\n disabled,\r\n step: safeStep,\r\n precision: safePrecision,\r\n formattedValue: maskedValue,\r\n };\r\n\r\n return (\r\n <>\r\n {inputElement}\r\n {renderControls(controlsProps)}\r\n </>\r\n );\r\n }\r\n\r\n if (!showStepButtons) {\r\n return inputElement;\r\n }\r\n\r\n return (\r\n <div style={{ display: 'inline-flex', alignItems: 'center', gap: '4px' }}>\r\n <button\r\n type=\"button\"\r\n onClick={() => handleStep(-1)}\r\n disabled={disabled}\r\n aria-label=\"Decrease value\"\r\n style={{\r\n padding: '4px 8px',\r\n cursor: disabled ? 'not-allowed' : 'pointer',\r\n opacity: disabled ? 0.5 : 1,\r\n }}\r\n >\r\n -\r\n </button>\r\n {inputElement}\r\n <button\r\n type=\"button\"\r\n onClick={() => handleStep(1)}\r\n disabled={disabled}\r\n aria-label=\"Increase value\"\r\n style={{\r\n padding: '4px 8px',\r\n cursor: disabled ? 'not-allowed' : 'pointer',\r\n opacity: disabled ? 0.5 : 1,\r\n }}\r\n >\r\n +\r\n </button>\r\n </div>\r\n );\r\n }\r\n);\r\n\r\nIntlNumberInput.displayName = 'IntlNumberInput';\r\n\r\nexport default memo(IntlNumberInput);"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {\n useState, useMemo, useCallback, useEffect, useLayoutEffect, useRef,\n useImperativeHandle, memo, forwardRef,\n} from 'react';\nimport {\n resolveLocale, clampToBounds, createDigitMap, stripAffixes,\n parseNumber, preserveAffixEdit, addStep,\n} from './number.js';\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 readOnly?: 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 /** Opt in to keeping value authoritative when the parent rejects an edit. */\n controlled?: boolean;\n showStepButtons?: boolean;\n step?: number;\n minValue?: number;\n maxValue?: number;\n inputMode?: 'numeric' | 'decimal';\n onChange?: (event: React.ChangeEvent<HTMLInputElement>, value: number, maskedValue: string) => void;\n onBlur?: (event: React.FocusEvent<HTMLInputElement>, value: number, maskedValue: string) => void;\n renderControls?: (props: ControlsRenderProps) => React.ReactNode;\n};\n\nexport type IntlNumberInputProps = IntlNumberInputOwnProps &\n Omit<React.ComponentPropsWithoutRef<'input'>,\n keyof IntlNumberInputOwnProps | 'value' | 'type' | 'defaultValue' | 'children'>;\n\n// Keep the historical affix normalization for compatibility.\nfunction sanitizeString(str: string): string {\n return str.replace(/<[^>]*>/g, '').replace(/on\\w+\\s*=\\s*[\"'][^\"']*[\"']/g, '');\n}\n\nconst useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n\nfunction createChangeEvent(input: HTMLInputElement): React.ChangeEvent<HTMLInputElement> {\n const nativeEvent = new Event('change', { bubbles: true, cancelable: true });\n const event: React.ChangeEvent<HTMLInputElement> & { bubble: boolean } = {\n target: input,\n currentTarget: input,\n nativeEvent,\n bubbles: true,\n bubble: true,\n cancelable: nativeEvent.cancelable,\n get defaultPrevented() { return nativeEvent.defaultPrevented; },\n eventPhase: nativeEvent.eventPhase,\n isTrusted: false,\n timeStamp: nativeEvent.timeStamp,\n type: 'change',\n preventDefault: () => nativeEvent.preventDefault(),\n stopPropagation: () => nativeEvent.stopPropagation(),\n isDefaultPrevented: () => nativeEvent.defaultPrevented,\n isPropagationStopped: () => nativeEvent.cancelBubble,\n persist: () => {},\n };\n return event;\n}\n\nconst IntlNumberInput = forwardRef<HTMLInputElement, IntlNumberInputProps>(\n function IntlNumberInput({\n locale = 'en-US', prefix = '', suffix = '', precision = 2, value,\n controlled = false, showStepButtons = false, step = 1, minValue, maxValue,\n inputMode: inputModeProp, onChange, onBlur, onKeyDown: onKeyDownProp,\n disabled = false, renderControls, ...inputProps\n }, ref) {\n const innerRef = useRef<HTMLInputElement>(null);\n useImperativeHandle(ref, () => innerRef.current!);\n const safePrecision = typeof precision !== 'number' || Number.isNaN(precision)\n ? 2 : Math.min(Math.max(Math.round(precision), 0), 20);\n const safeStep = Number.isFinite(step) && step > 0 ? step : 1;\n const safePrefix = useMemo(() => sanitizeString(prefix), [prefix]);\n const safeSuffix = useMemo(() => sanitizeString(suffix), [suffix]);\n const resolvedLocale = useMemo(() => resolveLocale(locale), [locale]);\n const digitMap = useMemo(() => createDigitMap(resolvedLocale), [resolvedLocale]);\n const numberFormatter = useMemo(() => new Intl.NumberFormat(resolvedLocale, {\n minimumFractionDigits: safePrecision, maximumFractionDigits: safePrecision,\n }), [resolvedLocale, safePrecision]);\n const [effectiveMin, effectiveMax] = useMemo(() => {\n const min = Number.isFinite(minValue) ? minValue : undefined;\n const max = Number.isFinite(maxValue) ? maxValue : undefined;\n return min !== undefined && max !== undefined && min > max ? [max, min] : [min, max];\n }, [minValue, maxValue]);\n const normalize = useCallback((numeric: number) =>\n clampToBounds(Number.isFinite(numeric) ? numeric : 0, effectiveMin, effectiveMax),\n [effectiveMin, effectiveMax]);\n const getNumberValue = useCallback((text: string) =>\n parseNumber(stripAffixes(text, safePrefix, safeSuffix), digitMap, safePrecision),\n [safePrefix, safeSuffix, digitMap, safePrecision]);\n const fromProp = () => normalize(typeof value === 'string' ? getNumberValue(value) : value ?? 0);\n const [state, setState] = useState(() => ({ source: value, numeric: fromProp() }));\n const [editVersion, setEditVersion] = useState(0);\n // Synchronize changed external values before rendering. An unchanged value retains\n // the optimistic editing behavior of releases before controlled was introduced.\n let numericValue = controlled ? fromProp() : normalize(state.numeric);\n if (!Object.is(state.source, value)) {\n numericValue = value === undefined && !controlled ? normalize(state.numeric) : fromProp();\n setState({ source: value, numeric: numericValue });\n } else if (!controlled && !Object.is(state.numeric, numericValue)) {\n setState({ source: value, numeric: numericValue });\n }\n const formatNumber = useCallback((numeric: number) =>\n `${safePrefix}${numberFormatter.format(numeric)}${safeSuffix}`,\n [safePrefix, safeSuffix, numberFormatter]);\n const maskedValue = formatNumber(numericValue);\n // Cache for several custom-control calls in one event; rendered state is numericValue.\n const numericValueRef = useRef(numericValue);\n const selectionRef = useRef<{ digitsBefore: number } | null>(null);\n const countDigits = (text: string) => Array.from(text).filter((char) => digitMap.has(char)).length;\n\n useIsomorphicLayoutEffect(() => {\n numericValueRef.current = numericValue;\n if (!selectionRef.current || !innerRef.current) return;\n const input = innerRef.current;\n const { digitsBefore } = selectionRef.current;\n const numericText = stripAffixes(input.value, safePrefix, safeSuffix);\n let position = safePrefix.length;\n let found = 0;\n for (const character of numericText) {\n if (found >= digitsBefore) break;\n position += character.length;\n if (digitMap.has(character)) found++;\n }\n input.setSelectionRange(position, position);\n selectionRef.current = null;\n }, [numericValue, maskedValue, editVersion, safePrefix, safeSuffix, digitMap]);\n\n const readOnly = !!inputProps.readOnly;\n const commitValue = useCallback((next: number, event?: React.ChangeEvent<HTMLInputElement>) => {\n if (disabled || readOnly || !Number.isFinite(next)) return;\n const nextValue = normalize(next);\n const nextMasked = formatNumber(nextValue);\n if (!controlled) {\n numericValueRef.current = nextValue;\n setState((previous) => ({ ...previous, numeric: nextValue }));\n }\n // Reapply the caret even if normalization leaves the formatted value unchanged.\n setEditVersion((previous) => previous + 1);\n if (event) {\n onChange?.(event, nextValue, nextMasked);\n } else if (innerRef.current) {\n const input = innerRef.current;\n input.value = nextMasked;\n try {\n onChange?.(createChangeEvent(input), nextValue, nextMasked);\n } finally {\n input.value = controlled ? maskedValue : nextMasked;\n }\n }\n }, [disabled, readOnly, normalize, formatNumber, controlled, onChange, maskedValue]);\n\n const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n if (disabled || readOnly) return;\n const raw = preserveAffixEdit(maskedValue, event.target.value, safePrefix, safeSuffix);\n const numericTextBeforeCaret = stripAffixes(raw.slice(0, event.target.selectionStart ?? 0), safePrefix, '');\n selectionRef.current = { digitsBefore: countDigits(numericTextBeforeCaret) };\n const next = getNumberValue(raw);\n if (!Number.isFinite(next)) {\n selectionRef.current = null;\n return;\n }\n commitValue(next, event);\n };\n\n const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {\n const raw = event.target.value;\n const parsed = raw === maskedValue || disabled || readOnly ? numericValue : getNumberValue(raw);\n const nextValue = normalize(Number.isFinite(parsed) ? parsed : numericValue);\n if (!controlled && !disabled && !readOnly) {\n numericValueRef.current = nextValue;\n setState((previous) => ({ ...previous, numeric: nextValue }));\n }\n onBlur?.(event, nextValue, formatNumber(nextValue));\n };\n\n const handleStep = useCallback((direction: number, multiplier = 1) => {\n if (!Number.isFinite(multiplier)) return;\n const delta = direction * (safeStep / Math.pow(10, safePrecision)) * multiplier;\n if (!Number.isFinite(delta)) return;\n commitValue(addStep(numericValueRef.current, delta));\n }, [safeStep, safePrecision, commitValue]);\n const increment = useCallback((multiplier = 1) => handleStep(1, multiplier), [handleStep]);\n const decrement = useCallback((multiplier = 1) => handleStep(-1, multiplier), [handleStep]);\n const setValue = useCallback((next: number) => commitValue(next), [commitValue]);\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n onKeyDownProp?.(event);\n if (event.defaultPrevented || disabled || readOnly) return;\n if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {\n event.preventDefault();\n handleStep(event.key === 'ArrowUp' ? 1 : -1, event.shiftKey ? 10 : 1);\n }\n };\n\n const inputElement = <input\n ref={innerRef} type=\"text\" role=\"spinbutton\"\n aria-valuenow={numericValue} aria-valuemin={effectiveMin} aria-valuemax={effectiveMax}\n aria-valuetext={maskedValue}\n inputMode={inputModeProp || (safePrecision > 0 ? 'decimal' : 'numeric')}\n value={maskedValue} disabled={disabled}\n onChange={handleChange} onBlur={handleBlur} onKeyDown={handleKeyDown}\n {...inputProps}\n />;\n if (renderControls) {\n return <>{inputElement}{renderControls({\n increment, decrement, setValue, value: numericValue,\n min: effectiveMin, max: effectiveMax, disabled, readOnly,\n step: safeStep, precision: safePrecision, formattedValue: maskedValue,\n })}</>;\n }\n if (!showStepButtons) return inputElement;\n const controlsDisabled = disabled || readOnly;\n const buttonStyle = {\n padding: '4px 8px', cursor: controlsDisabled ? 'not-allowed' : 'pointer',\n opacity: controlsDisabled ? 0.5 : 1,\n };\n return <div style={{ display: 'inline-flex', alignItems: 'center', gap: '4px' }}>\n <button type=\"button\" onClick={() => decrement()} disabled={controlsDisabled}\n aria-label=\"Decrease value\" style={buttonStyle}>-</button>\n {inputElement}\n <button type=\"button\" onClick={() => increment()} disabled={controlsDisabled}\n aria-label=\"Increase value\" style={buttonStyle}>+</button>\n </div>;\n }\n);\n\nIntlNumberInput.displayName = 'IntlNumberInput';\nexport default memo(IntlNumberInput);\r\n"]}
|
package/dist/number.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function resolveLocale(locale: string): string;
|
|
2
|
+
export declare function clampToBounds(value: number, min?: number, max?: number): number;
|
|
3
|
+
export declare function createDigitMap(locale: string): Map<string, string>;
|
|
4
|
+
export declare function stripAffixes(text: string, prefix: string, suffix: string): string;
|
|
5
|
+
export declare function parseNumber(text: string, digits: Map<string, string>, precision: number): number;
|
|
6
|
+
export declare function preserveAffixEdit(previous: string, next: string, prefix: string, suffix: string): string;
|
|
7
|
+
export declare function addStep(value: number, delta: number): number;
|