// 간단한 IME 감지 함수 function isIMEComposing(e) { // compositionstart ~ compositionend 사이의 입력은 IME 조합 중 return e.nativeEvent?.isComposing || e.isComposing || false } // 숫자만 입력 가능한 input onChange 함수 (음수 포함) export const onlyNumberInputChange = (e, callback) => { // IME 조합 중이면 그대로 전달 if (isIMEComposing(e)) { callback(e.target.value, e) return } let value = e.target.value value = value.replace(/[^-0-9]/g, '') // 음수 기호는 맨 앞에만 허용 if (value.indexOf('-') > 0) { value = value.replace(/-/g, '') } // 연속된 음수 기호 제거 value = value.replace(/^-+/, '-') callback(value, e) } // 소수점 둘째자리 숫자만 입력가능 (음수 포함, 개선된 로직) export const onlyNumberWithDotInputChange = (e, callback) => { // IME 조합 중이면 그대로 전달 if (isIMEComposing(e)) { callback(e.target.value, e) return } const val = e.target.value // 음수를 포함한 소수점 패턴 (최대 4자리 정수, 2자리 소수) const pattern = /^-?(\d{0,4}([.]\d{0,2})?)?$/ if (!pattern.test(val)) { // 패턴에 맞지 않으면 마지막 입력 문자 제거 const correctedValue = val.slice(0, val.length - 1) callback(correctedValue, e) return } // 음수 기호가 중간에 있으면 제거 let correctedValue = val if (val.indexOf('-') > 0) { correctedValue = val.replace(/-/g, '') } callback(correctedValue, e) } // ============================= // Number normalization utilities // ============================= // 1) Normalize any string to NFKC and keep only ASCII digits 0-9. export function normalizeDigits(value, allowNegative = false) { // 1. 전각 숫자를 반각으로 변환 const halfWidth = fullToHalf(String(value ?? '')); // 2. NFKC 정규화 const normalized = halfWidth.normalize('NFKC'); if (allowNegative) { // 음수 허용 시 let result = normalized.replace(/[^-0-9]/g, ''); // 음수 기호는 맨 앞에만 허용 if (result.indexOf('-') > 0) { result = result.replace(/-/g, ''); } // 연속된 음수 기호 제거 result = result.replace(/^-+/, '-'); return result; } else { // 양수만 허용 return normalized.replace(/[^0-9]/g, ''); } } export function normalizeDecimal(value, allowNegative = false) { // 1. 전각 숫자와 기호를 반각으로 변환 const halfWidth = fullToHalf(String(value ?? '')); // 2. NFKC 정규화 const normalized = halfWidth.normalize('NFKC'); let result; if (allowNegative) { // 음수와 소수점 허용 result = normalized.replace(/[^-0-9.]/g, ''); // 음수 기호는 맨 앞에만 허용 if (result.indexOf('-') > 0) { result = result.replace(/-/g, ''); } // 연속된 음수 기호 제거 result = result.replace(/^-+/, '-'); } else { // 양수만 허용 (소수점 포함) result = normalized.replace(/[^0-9.]/g, ''); } // 소수점은 하나만 허용 const [head, ...rest] = result.split('.'); return rest.length ? `${head}.${rest.join('').replace(/\./g, '')}` : head; } // 2-1) Limit fractional digits for decimal numbers. Default to 2 digits. export function normalizeDecimalLimit(value, maxFractionDigits = 2, maxIntegerDigits = null, allowNegative = false) { const s = normalizeDecimal(value, allowNegative) if (!s) return s const isNegative = s.startsWith('-') const absoluteValue = isNegative ? s.slice(1) : s const [intPart, fracPart] = absoluteValue.split('.') // 정수 부분 자릿수 제한 let limitedIntPart = intPart if (maxIntegerDigits && intPart.length > maxIntegerDigits) { limitedIntPart = intPart.slice(0, maxIntegerDigits) } // 소수 부분 자릿수 제한 let result = limitedIntPart if (fracPart !== undefined) { const limitedFracPart = fracPart.slice(0, Math.max(0, maxFractionDigits)) if (limitedFracPart.length > 0) { result = `${limitedIntPart}.${limitedFracPart}` } } return isNegative ? `-${result}` : result } // 3) DOM input event helpers: mutate target.value and return normalized value export function sanitizeIntegerInputEvent(e, allowNegative = false) { if (!e?.target) return '' // IME 조합 중이면 원본 값 반환 if (isIMEComposing(e)) { return e.target.value } const v = normalizeDigits(e.target.value, allowNegative) e.target.value = v return v } export function sanitizeDecimalInputEvent(e, allowNegative = false) { if (!e?.target) return '' // IME 조합 중이면 원본 값 반환 if (isIMEComposing(e)) { return e.target.value } const v = normalizeDecimal(e.target.value, allowNegative) e.target.value = v return v } export function sanitizeDecimalLimitInputEvent(e, maxFractionDigits = 2, maxIntegerDigits = null, allowNegative = false) { if (!e?.target) return '' // IME 조합 중이면 원본 값 반환 if (isIMEComposing(e)) { return e.target.value } const v = normalizeDecimalLimit(e.target.value, maxFractionDigits, maxIntegerDigits, allowNegative) e.target.value = v return v } export function fullToHalf(str) { if (!str) return ''; // Convert full-width numbers (0-9) to half-width (0-9) return str.replace(/[0-9]/g, function(s) { return String.fromCharCode(s.charCodeAt(0) - 0xFEE0); }); }