qcast-front/src/util/input-utils.js

183 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 간단한 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 (-) to half-width (0-9)
return str.replace(/[-]/g, function(s) {
return String.fromCharCode(s.charCodeAt(0) - 0xFEE0);
});
}