fix(phone): блокировать ввод не-цифр на уровне keyDown
Some checks failed
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 1m4s
CI / Web (React + Vite) (push) Failing after 37s
Docker Public / Build + push Public (push) Successful in 26s
Docker Web / Build + push Web (push) Failing after 26s
Docker Web / Deploy Web on stage (push) Has been skipped
Docker Public / Deploy Public on stage (push) Successful in 10s

Предыдущий fix не покрывал кейс когда курсор оказывался не сразу после «+7 » (в начале строки или в середине). parseRawInput не находил префикс на нужной позиции и «7» снова попадала в подсчёт.

Корректное решение: блокировать любой не-цифровой символ через preventDefault в onKeyDown — тогда он вообще не доходит до input, парсер видит только валидные цифры. Сохранены: Backspace/Delete (с защитой префикса), стрелки и навигация, Cmd/Ctrl+A/C/V/X для копи-пасты, Enter для submit. Paste произвольной строки по-прежнему работает через onChange.
This commit is contained in:
nurdotnet 2026-05-03 16:15:27 +05:00
parent 47c349818f
commit 16fe7580af
2 changed files with 34 additions and 6 deletions

View file

@ -43,11 +43,21 @@ export function PhoneInput({ value, onChange, className, disabled, ...rest }: Ph
}
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.metaKey || e.ctrlKey || e.altKey) return
if (['Tab', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End', 'Enter', 'Escape'].includes(e.key)) return
const el = e.currentTarget
const start = el.selectionStart ?? 0
const end = el.selectionEnd ?? 0
if (e.key === 'Backspace' && start <= 3 && end === start) e.preventDefault()
if (e.key === 'Delete' && start < 3) e.preventDefault()
if (e.key === 'Backspace') {
if (start <= 3 && end === start) e.preventDefault()
return
}
if (e.key === 'Delete') {
if (start < 3) e.preventDefault()
return
}
// Только цифры — иначе появляется фантомная «7» из префикса.
if (!/^\d$/.test(e.key)) e.preventDefault()
}
const handleFocus = () => {

View file

@ -59,13 +59,31 @@ export function PhoneInput({ value, onChange, className, disabled, ...rest }: Ph
}
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
// Cmd/Ctrl + что-то — copy/paste/select-all и прочие шорткаты, пропускаем.
if (e.metaKey || e.ctrlKey || e.altKey) return
// Навигация и форм-сабмит — пропускаем.
if (['Tab', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End', 'Enter', 'Escape'].includes(e.key)) return
const el = e.currentTarget
const start = el.selectionStart ?? 0
const end = el.selectionEnd ?? 0
// Не позволяем удалить префикс "+7 " (3 символа). Backspace на позиции
// ≤3 без выделения, либо Delete на позиции <3.
if (e.key === 'Backspace' && start <= 3 && end === start) e.preventDefault()
if (e.key === 'Delete' && start < 3) e.preventDefault()
// Backspace/Delete — разрешаем, но не даём удалить префикс «+7 » (3 символа).
if (e.key === 'Backspace') {
if (start <= 3 && end === start) e.preventDefault()
return
}
if (e.key === 'Delete') {
if (start < 3) e.preventDefault()
return
}
// Дальше — только цифры. Любой другой символ блокируем, чтобы он не
// попал в input и не запутал парсер. Без этого нажатие буквы при курсоре
// в любом месте ломает раскладку и приводит к появлению фантомной «7».
if (!/^\d$/.test(e.key)) {
e.preventDefault()
}
}
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {