Merge pull request '[1252] 일본의 전각 숫자, 반각 함수변경' (#304) from dev into dev-deploy
Reviewed-on: #304
This commit is contained in:
commit
6d540ad6f0
@ -1,122 +1,183 @@
|
||||
// 숫자만 입력 가능한 input onChange 함수
|
||||
export const onlyNumberInputChange = (e, callback) => {
|
||||
let value = e.target.value
|
||||
value = value.replace(/[^-0-9]/g, '')
|
||||
callback(value, e)
|
||||
// 간단한 IME 감지 함수
|
||||
function isIMEComposing(e) {
|
||||
// compositionstart ~ compositionend 사이의 입력은 IME 조합 중
|
||||
return e.nativeEvent?.isComposing || e.isComposing || false
|
||||
}
|
||||
|
||||
//소수점 둘째자리 숫자만 입력가능
|
||||
export const onlyNumberWithDotInputChange = (e, callback) => {
|
||||
const val = e.target.value
|
||||
|
||||
const pattern = /^-?(\d{1,4}([.]\d{0,2})?)?$/
|
||||
if (!pattern.test(val)) {
|
||||
// prev에서 마지막 자리 제거
|
||||
callback(val.slice(0, val.length - 1), e)
|
||||
// 숫자만 입력 가능한 input onChange 함수 (음수 포함)
|
||||
export const onlyNumberInputChange = (e, callback) => {
|
||||
// IME 조합 중이면 그대로 전달
|
||||
if (isIMEComposing(e)) {
|
||||
callback(e.target.value, e)
|
||||
return
|
||||
}
|
||||
|
||||
callback(val, e)
|
||||
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
|
||||
// =============================
|
||||
// 마지막으로 유효했던 값 추적
|
||||
let lastValidDigits = '';
|
||||
let lastValidDecimal = '';
|
||||
|
||||
/**
|
||||
* 숫자만 포함된 문자열로 정규화 (0-9)
|
||||
* - 전각 숫자 -> 반각
|
||||
* - 숫자 외 제거
|
||||
* - 결과가 유효하면 길이/증가폭과 무관하게 허용
|
||||
*/
|
||||
export function normalizeDigits(value) {
|
||||
if (value == null || value === '') {
|
||||
lastValidDigits = '';
|
||||
return '';
|
||||
}
|
||||
// 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');
|
||||
|
||||
const converted = String(value).replace(/[0-9]/g, s =>
|
||||
String.fromCharCode(s.charCodeAt(0) - 0xFEE0)
|
||||
);
|
||||
const normalized = converted.replace(/\D/g, '');
|
||||
|
||||
if (normalized === '') {
|
||||
lastValidDigits = '';
|
||||
return '';
|
||||
}
|
||||
|
||||
if (/^\d+$/.test(normalized)) {
|
||||
lastValidDigits = normalized;
|
||||
return normalized;
|
||||
}
|
||||
|
||||
return lastValidDigits || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 소수점이 포함된 숫자 문자열로 정규화
|
||||
* - 전각 숫자/점 -> 반각
|
||||
* - 숫자/점 외 제거
|
||||
* - 점은 첫 번째만 허용
|
||||
*/
|
||||
export function normalizeDecimal(value) {
|
||||
if (value == null || value === '') {
|
||||
lastValidDecimal = '';
|
||||
return '';
|
||||
}
|
||||
|
||||
let converted = String(value).replace(/[0-9.]/g, s =>
|
||||
s === '.' ? '.' : String.fromCharCode(s.charCodeAt(0) - 0xFEE0)
|
||||
);
|
||||
converted = converted.replace(/[^0-9.]/g, '');
|
||||
|
||||
const firstDot = converted.indexOf('.');
|
||||
let normalized;
|
||||
|
||||
if (firstDot !== -1) {
|
||||
const integerPart = converted.slice(0, firstDot).replace(/\D/g, '');
|
||||
const fractionPart = converted.slice(firstDot + 1).replace(/\D/g, '');
|
||||
normalized = integerPart + '.' + fractionPart;
|
||||
if (allowNegative) {
|
||||
// 음수 허용 시
|
||||
let result = normalized.replace(/[^-0-9]/g, '');
|
||||
// 음수 기호는 맨 앞에만 허용
|
||||
if (result.indexOf('-') > 0) {
|
||||
result = result.replace(/-/g, '');
|
||||
}
|
||||
// 연속된 음수 기호 제거
|
||||
result = result.replace(/^-+/, '-');
|
||||
return result;
|
||||
} else {
|
||||
normalized = converted.replace(/\D/g, '');
|
||||
// 양수만 허용
|
||||
return normalized.replace(/[^0-9]/g, '');
|
||||
}
|
||||
|
||||
if (normalized === '') {
|
||||
lastValidDecimal = '';
|
||||
return '';
|
||||
}
|
||||
|
||||
if (/^\d+(\.\d*)?$/.test(normalized) || /^\d+$/.test(normalized)) {
|
||||
lastValidDecimal = normalized;
|
||||
return normalized;
|
||||
}
|
||||
|
||||
return lastValidDecimal || '';
|
||||
}
|
||||
|
||||
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) {
|
||||
const s = normalizeDecimal(value)
|
||||
export function normalizeDecimalLimit(value, maxFractionDigits = 2, maxIntegerDigits = null, allowNegative = false) {
|
||||
const s = normalizeDecimal(value, allowNegative)
|
||||
if (!s) return s
|
||||
const [intPart, fracPart] = s.split('.')
|
||||
if (fracPart === undefined) return intPart
|
||||
return `${intPart}.${fracPart.slice(0, Math.max(0, maxFractionDigits))}`
|
||||
|
||||
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 (optional): mutate target.value and return normalized value
|
||||
export function sanitizeIntegerInputEvent(e) {
|
||||
const v = normalizeDigits(e?.target?.value)
|
||||
if (e?.target) e.target.value = v
|
||||
// 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) {
|
||||
const v = normalizeDecimal(e?.target?.value)
|
||||
if (e?.target) e.target.value = 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);
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user