fix(phone): не считать «7» из префикса как введённую цифру
Some checks failed
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 1m5s
CI / Web (React + Vite) (push) Failing after 37s
Docker Public / Build + push Public (push) Successful in 27s
Docker Web / Build + push Web (push) Failing after 27s
Docker Web / Deploy Web on stage (push) Has been skipped
Docker Public / Deploy Public on stage (push) Successful in 11s

Баг: при нажатии любой не-цифры (буквы, пробела) поле подставляло «7». Корень: extractDigits парсил полное e.target.value включая префикс «+7», и его «7» попадала в d, добавляясь как введённый юзером символ. Backspace на пустом поле не работал по той же причине.

Фикс: новая функция parseRawInput отделяет префикс «+7 » ДО извлечения цифр. Применена в handleChange обоих PhoneInput (web + public). Кейс paste без префикса (8XXXXXXXXXX / 7XXXXXXXXXX из 11 цифр) сохранён.
This commit is contained in:
nurdotnet 2026-05-03 15:56:27 +05:00
parent 2301446b06
commit 47c349818f
2 changed files with 30 additions and 6 deletions

View file

@ -7,6 +7,17 @@ function extractDigits(value: string): string {
return d.slice(0, 10) return d.slice(0, 10)
} }
/** Парсит сырой ввод инпута, отбрасывая префикс «+7 ». Без этого «7» из
* префикса попадает в подсчёт и при нажатии не-цифры появляется лишняя «7». */
function parseRawInput(raw: string): string {
if (raw.startsWith('+7 ')) raw = raw.slice(3)
else if (raw.startsWith('+7')) raw = raw.slice(2)
else if (raw.startsWith('+')) raw = raw.slice(1)
let d = raw.replace(/\D/g, '')
if (d.length === 11 && (d[0] === '7' || d[0] === '8')) d = d.slice(1)
return d.slice(0, 10)
}
function formatLocal(d: string): string { function formatLocal(d: string): string {
if (d.length === 0) return '' if (d.length === 0) return ''
if (d.length <= 3) return d if (d.length <= 3) return d
@ -27,7 +38,7 @@ export function PhoneInput({ value, onChange, className, disabled, ...rest }: Ph
const display = `+7 ${formatLocal(digits)}`.trimEnd() const display = `+7 ${formatLocal(digits)}`.trimEnd()
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const d = extractDigits(e.target.value) const d = parseRawInput(e.target.value)
onChange(d.length > 0 ? `+7${d}` : '') onChange(d.length > 0 ? `+7${d}` : '')
} }

View file

@ -3,12 +3,25 @@ import { cn } from '@/lib/utils'
const inputClass = 'w-full h-10 rounded-md border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 px-3 text-sm leading-none focus:outline-none focus:ring-2 focus:ring-[var(--color-brand)] disabled:opacity-60 disabled:bg-slate-50 dark:disabled:bg-slate-800/60 tabular-nums' const inputClass = 'w-full h-10 rounded-md border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 px-3 text-sm leading-none focus:outline-none focus:ring-2 focus:ring-[var(--color-brand)] disabled:opacity-60 disabled:bg-slate-50 dark:disabled:bg-slate-800/60 tabular-nums'
/** Извлекает 10 цифр KZ-номера (после +7) из любого формата. /** Извлекает 10 цифр KZ-номера (после +7) из каноничного значения «+7XXXXXXXXXX». */
* "+7 700 123 45 67" "7001234567", "8(707)1234567" "0712345670" */
function extractDigits(value: string): string { function extractDigits(value: string): string {
if (!value) return '' if (!value) return ''
let d = value.replace(/\D/g, '') let d = value.replace(/\D/g, '')
// 11 цифр и ведущая 7 или 8 — это код страны, отрезаем. if (d.length === 11 && (d[0] === '7' || d[0] === '8')) d = d.slice(1)
return d.slice(0, 10)
}
/** Парсит сырой ввод инпута (то что показано на экране, например «+7 700 1»)
* и достаёт реально введённые юзером цифры (после «+7 »). Критично: «+7 »
* префикс отбрасывается ДО извлечения цифр, иначе «7» из префикса попадает
* в подсчёт и при нажатии не-цифры на пустом поле появляется «7». */
function parseRawInput(raw: string): string {
// Снимаем именно наш префикс — в любом из трёх возможных промежуточных видов.
if (raw.startsWith('+7 ')) raw = raw.slice(3)
else if (raw.startsWith('+7')) raw = raw.slice(2)
else if (raw.startsWith('+')) raw = raw.slice(1)
let d = raw.replace(/\D/g, '')
// Кейс paste «8 700 …» или «7 700 …» без нашего префикса.
if (d.length === 11 && (d[0] === '7' || d[0] === '8')) d = d.slice(1) if (d.length === 11 && (d[0] === '7' || d[0] === '8')) d = d.slice(1)
return d.slice(0, 10) return d.slice(0, 10)
} }
@ -41,8 +54,8 @@ export function PhoneInput({ value, onChange, className, disabled, ...rest }: Ph
const display = `+7 ${formatLocal(digits)}`.trimEnd() const display = `+7 ${formatLocal(digits)}`.trimEnd()
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const d = extractDigits(e.target.value) const d = parseRawInput(e.target.value)
onChange(d.length === 10 ? `+7${d}` : d.length > 0 ? `+7${d}` : '') onChange(d.length > 0 ? `+7${d}` : '')
} }
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {