diff --git a/src/util/input-utils.js b/src/util/input-utils.js index 7578e95e..86c03fe4 100644 --- a/src/util/input-utils.js +++ b/src/util/input-utils.js @@ -25,37 +25,75 @@ export const onlyNumberWithDotInputChange = (e, callback) => { // ============================= // 1) Normalize any string to NFKC and keep only ASCII digits 0-9. // 1) Normalize any string to keep only ASCII digits 0-9 +// IME composition state tracking +let isComposingDigits = false; +let lastDigitsValue = ''; +let isComposingDecimal = false; +let lastDecimalValue = ''; + +/** + * 숫자만 포함된 문자열로 정규화 (0-9) + * - 전각 숫자를 반각으로 변환 + * - 숫자가 아닌 문자 제거 + * - IME 입력 대응 + */ export function normalizeDigits(value) { if (value == null) return ''; + if (isComposingDigits) return value; - // First convert full-width numbers to half-width - const str = String(value).replace(/[0-9]/g, s => + // 전각 숫자를 반각으로 변환 + const normalized = String(value).replace(/[0-9]/g, s => String.fromCharCode(s.charCodeAt(0) - 0xFEE0) ); - // Then remove all non-digit characters - return str.replace(/\D/g, ''); + // IME 조합 중인지 확인 + if (lastDigitsValue && normalized.startsWith(lastDigitsValue) && + normalized.length > lastDigitsValue.length + 1) { + isComposingDigits = true; + setTimeout(() => { isComposingDigits = false; }, 0); + return value; + } + + // 숫자만 남기기 + const result = normalized.replace(/\D/g, ''); + lastDigitsValue = result; + return result; } -// 2) Normalize decimal numbers (allow one dot) +/** + * 소수점이 포함된 숫자 문자열로 정규화 + * - 전각 숫자와 소수점을 반각으로 변환 + * - 소수점은 한 개만 유지 + * - IME 입력 대응 + */ export function normalizeDecimal(value) { if (value == null) return ''; + if (isComposingDecimal) return value; - // Convert full-width numbers and dot to half-width - let str = String(value).replace(/[0-9.]/g, s => + // 전각 숫자와 소수점을 반각으로 변환 + const normalized = String(value).replace(/[0-9.]/g, s => s === '.' ? '.' : String.fromCharCode(s.charCodeAt(0) - 0xFEE0) ); - // Handle multiple dots by keeping only the first one - const parts = str.split('.'); - if (parts.length > 1) { - str = parts[0] + '.' + parts.slice(1).join(''); + // IME 조합 중인지 확인 + if (lastDecimalValue && normalized.startsWith(lastDecimalValue) && + normalized.length > lastDecimalValue.length + 1) { + isComposingDecimal = true; + setTimeout(() => { isComposingDecimal = false; }, 0); + return value; } - // Remove any remaining non-digit and non-dot characters - // and ensure only one dot remains - return str.replace(/[^\d.]/g, '') - .replace(/^(\d*\.\d*).*$/, '$1'); + // 소수점 처리 + const parts = normalized.split('.'); + let result; + if (parts.length > 1) { + result = parts[0].replace(/\D/g, '') + '.' + parts[1].replace(/\D/g, ''); + } else { + result = normalized.replace(/\D/g, ''); + } + + lastDecimalValue = result; + return result; } // 2-1) Limit fractional digits for decimal numbers. Default to 2 digits.