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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"number.js","sourceRoot":"","sources":["../../src/number.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC;IACtE,CAAC;eAAO,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,GAAY,EAAE,GAAY;IACrE,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,aAAH,GAAG,cAAH,GAAG,GAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,aAAH,GAAG,cAAH,GAAG,GAAI,QAAQ,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;IACxE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,MAAM,SAAS,GAAG,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC;aAC7C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChF,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACzB,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,MAAc,EAAE,MAAc;IACvE,IAAI,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxE,IAAI,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,MAA2B,EAAE,SAAiB;IACtF,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxF,mFAAmF;IACnF,kFAAkF;IAClF,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,SAAS,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM;QACtC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;IACjE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAChC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,IAAY,EAAE,MAAc,EAAE,MAAc;IAC9F,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,KAAK,GAAG,QAAQ,CAAC,MAAM,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC;QAAE,KAAK,EAAE,CAAC;IAClG,IAAI,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC1B,IAAI,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC;IAC1B,OAAO,GAAG,GAAG,KAAK,IAAI,OAAO,GAAG,KAAK,IAAI,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC;QACjF,GAAG,EAAE,CAAC;QACN,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IACnD,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC;IACtF,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,IAAI,UAAU,IAAI,GAAG,GAAG,UAAU,CAAC;IAChF,OAAO,UAAU,IAAI,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;AACpD,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;;IAClC,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAA,MAAA,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,0CAAE,MAAM,KAAI,CAAC,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,KAAa,EAAE,KAAa;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACpC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,WAAW,GAAG,WAAW,CAAC;IACtC,IAAI,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;QACxG,OAAO,GAAG,GAAG,MAAM,CAAC;IACtB,CAAC;IACD,OAAO,KAAK,GAAG,KAAK,CAAC;AACvB,CAAC","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
- function resolveLocale(locale) {
41
- try {
42
- const supported = Intl.NumberFormat.supportedLocalesOf([locale]);
43
- return supported.length > 0 ? supported[0] : 'en-US';
44
- }
45
- catch (_a) {
46
- return 'en-US';
47
- }
48
- }
49
- function isValidNumber(value) {
50
- return Number.isFinite(value) && !Number.isNaN(value);
51
- }
52
- function clamp(value, min, max) {
53
- return Math.min(Math.max(value, min), max);
54
- }
55
- function clampToBounds(value, min, max) {
56
- let result = value;
57
- if (min !== undefined && result < min) {
58
- result = min;
59
- }
60
- if (max !== undefined && result > max) {
61
- result = max;
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 = (0, react_1.useMemo)(() => {
69
- if (typeof precision !== 'number' || Number.isNaN(precision))
70
- return 2;
71
- return clamp(Math.round(precision), 0, 20);
72
- }, [precision]);
73
- const safeStep = (0, react_1.useMemo)(() => {
74
- if (typeof step !== 'number' || Number.isNaN(step) || step <= 0)
75
- return 1;
76
- return step;
77
- }, [step]);
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
- if (minValue !== undefined && maxValue !== undefined && minValue > maxValue) {
80
- return [maxValue, minValue];
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 getPrecisionValue = (0, react_1.useCallback)((p) => {
85
- return Math.pow(10, p);
86
- }, []);
87
- const getNumberValue = (0, react_1.useCallback)((strValue) => {
88
- const inputValue = String(strValue || '');
89
- const hasNegativeSign = inputValue.startsWith('-');
90
- const onlyDigits = inputValue.replace(/[^0-9]/g, '');
91
- if (!onlyDigits) {
92
- return hasNegativeSign ? -0 : 0;
93
- }
94
- const numberValue = Number(onlyDigits);
95
- const parsedValue = numberValue / getPrecisionValue(safePrecision);
96
- return hasNegativeSign ? -parsedValue : parsedValue;
97
- }, [getPrecisionValue, safePrecision]);
98
- const numberFormatter = (0, react_1.useMemo)(() => {
99
- const options = {
100
- style: 'decimal',
101
- minimumFractionDigits: safePrecision,
102
- maximumFractionDigits: safePrecision,
103
- };
104
- const resolvedLocale = resolveLocale(locale);
105
- return new Intl.NumberFormat(resolvedLocale, options);
106
- }, [locale, safePrecision]);
107
- const formatNumber = (0, react_1.useCallback)((val) => {
108
- let numberValue;
109
- if (typeof val === 'string') {
110
- numberValue = getNumberValue(val);
111
- }
112
- else {
113
- numberValue = val;
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
- if (!isValidNumber(numberValue)) {
116
- numberValue = 0;
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
- numberValue = clampToBounds(numberValue, effectiveMin, effectiveMax);
119
- const formattedNumber = numberFormatter.format(numberValue);
120
- const safePrefix = sanitizeString(prefix);
121
- const safeSuffix = sanitizeString(suffix);
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
- return initial;
134
- }, [value, getNumberValue]);
135
- const [maskedValue, setMaskedValue] = (0, react_1.useState)(getInitialValue);
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
- }
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
- (0, react_1.useEffect)(() => {
159
- if (value === undefined)
148
+ }, [disabled, readOnly, normalize, formatNumber, controlled, onChange, maskedValue]);
149
+ const handleChange = (event) => {
150
+ var _a;
151
+ if (disabled || readOnly)
160
152
  return;
161
- const numericVal = typeof value === 'string' ? getNumberValue(value) : value;
162
- const clamped = clampToBounds(numericVal, effectiveMin, effectiveMax);
163
- setMaskedValue(formatNumber(clamped));
164
- numericValueRef.current = clamped;
165
- }, [value, formatNumber, getNumberValue, effectiveMin, effectiveMax]);
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 };
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
- const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);
178
- const newMaskedValue = formatNumber(clampedValue);
179
- setMaskedValue(newMaskedValue);
180
- numericValueRef.current = clampedValue;
181
- if (onChange) {
182
- onChange(event, clampedValue, newMaskedValue);
183
- }
184
- }, [getNumberValue, formatNumber, effectiveMin, effectiveMax, onChange]);
185
- const handleBlur = (0, react_1.useCallback)((event) => {
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
- }, [getNumberValue, formatNumber, effectiveMin, effectiveMax, onBlur]);
196
- const handleStep = (0, react_1.useCallback)((delta, multiplier = 1) => {
197
- if (disabled)
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 currentValue = numericValueRef.current;
200
- const stepValue = (safeStep / Math.pow(10, safePrecision)) * multiplier;
201
- const newValue = clampToBounds(currentValue + delta * stepValue, effectiveMin, effectiveMax);
202
- const newMaskedValue = formatNumber(newValue);
203
- setMaskedValue(newMaskedValue);
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)((newValue) => {
240
- const clampedValue = clampToBounds(newValue, effectiveMin, effectiveMax);
241
- const newMaskedValue = formatNumber(clampedValue);
242
- setMaskedValue(newMaskedValue);
243
- numericValueRef.current = clampedValue;
244
- const syntheticEvent = {
245
- target: {
246
- value: newMaskedValue,
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
- return safePrecision > 0 ? 'decimal' : 'numeric';
271
- }, [inputModeProp, safePrecision]);
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
- const controlsProps = {
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(controlsProps)));
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
- return (react_1.default.createElement("div", { style: { display: 'inline-flex', alignItems: 'center', gap: '4px' } },
294
- react_1.default.createElement("button", { type: "button", onClick: () => handleStep(-1), disabled: disabled, "aria-label": "Decrease value", style: {
295
- padding: '4px 8px',
296
- cursor: disabled ? 'not-allowed' : 'pointer',
297
- opacity: disabled ? 0.5 : 1,
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: () => handleStep(1), disabled: disabled, "aria-label": "Increase value", style: {
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":"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","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"]}
@@ -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;