fix(регистрация): телефон — нестираемый префикс «+7 »
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker Public / Build + push Public (test + latest) (push) Waiting to run
Docker Public / Deploy :test → test.food-market.kz (push) Blocked by required conditions
Docker Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker Public / Build + push Public (test + latest) (push) Waiting to run
Docker Public / Deploy :test → test.food-market.kz (push) Blocked by required conditions
Docker Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
Стирание больше не превращает «7» кода страны в цифру номера. Причина: при Backspace в префикс raw становился «+7», и парсер считал это номером «7» («+7 7», далее набор давал мусор «+7 010 000 00 07»). Теперь: - извлечение тела номера идёт через stripPrefix (проход по совпадающим символам префикса) — повреждённый префикс даёт пустое тело, а не номер; - onKeyDown глушит Backspace на границе префикса и Delete перед префиксом; выделение+стирание (Ctrl+A/Ctrl+X) очищает поле до «+7 ». Полностью стёртое поле = «+7 » и пустой номер. Обе копии (сайт + кабинет). Проверено настоящим вводом в Chromium (Playwright): Ctrl+A+Backspace, Backspace до упора, Delete в начале, выделение+набор, Ctrl+A+Ctrl+X — после каждого набор «7010000000» ⇒ «+7 701 000 00 00»; регрессия набора и вставки тоже зелёная. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
6510ccafb5
commit
043d5c0417
|
|
@ -25,6 +25,16 @@ function valueToNational(value: string): string {
|
||||||
return toNational(value)
|
return toNational(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Снимаем наш визуальный префикс «+7 », проходя по совпадающим символам. Если
|
||||||
|
// пользователь стёр ЧАСТЬ префикса (raw="+7" или "+"), совпавший кусок всё равно
|
||||||
|
// отбрасывается, а тело оказывается пустым — цифра «7» кода страны НИКОГДА не
|
||||||
|
// утекает в номер при стирании. Остаток (тело) нормализуется отдельно.
|
||||||
|
function stripPrefix(raw: string): string {
|
||||||
|
let i = 0
|
||||||
|
while (i < PREFIX.length && i < raw.length && raw[i] === PREFIX[i]) i++
|
||||||
|
return raw.slice(i)
|
||||||
|
}
|
||||||
|
|
||||||
function nationalToDisplay(d: string): string {
|
function nationalToDisplay(d: string): string {
|
||||||
if (d.length === 0) return PREFIX
|
if (d.length === 0) return PREFIX
|
||||||
let s = PREFIX + d.slice(0, 3)
|
let s = PREFIX + d.slice(0, 3)
|
||||||
|
|
@ -88,14 +98,27 @@ export function PhoneInput({ value, onChange, className, disabled, ...rest }: Ph
|
||||||
const raw = e.target.value
|
const raw = e.target.value
|
||||||
const cursorPos = e.target.selectionStart ?? raw.length
|
const cursorPos = e.target.selectionStart ?? raw.length
|
||||||
pendingRef.current = natDigitsBeforeCursor(raw, cursorPos)
|
pendingRef.current = natDigitsBeforeCursor(raw, cursorPos)
|
||||||
// При посимвольном вводе значение начинается с «+7 » — берём часть после
|
// Тело = всё после нашего префикса «+7 » (с защитой от частично стёртого
|
||||||
// префикса, чтобы его «7» не попала в номер. При полной вставке (replace)
|
// префикса, см. stripPrefix). Нормализуем тело: вставка полного номера
|
||||||
// значения без префикса нормализуем целиком (снимем код страны/«8»).
|
// (с 8/кодом страны) приводится к 10 цифрам; при наборе первая «7» — это
|
||||||
const src = raw.startsWith(PREFIX) ? raw.slice(PREFIX_LEN) : raw
|
// начало оператора 7XX и не снимается.
|
||||||
const nat = toNational(src)
|
const nat = toNational(stripPrefix(raw))
|
||||||
onChange(nat.length > 0 ? '+7' + nat : '')
|
onChange(nat.length > 0 ? '+7' + nat : '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Префикс «+7 » нестираемый: Backspace на границе префикса (в номере стирать
|
||||||
|
// нечего) и Delete внутри/перед префиксом — глушим, чтобы «7» кода страны не
|
||||||
|
// превратилась в цифру номера. Выделение (Ctrl+A) + стирание не трогаем —
|
||||||
|
// браузер очистит всё, а value вернётся к «+7 ».
|
||||||
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
const el = e.currentTarget
|
||||||
|
const start = el.selectionStart ?? 0
|
||||||
|
const end = el.selectionEnd ?? 0
|
||||||
|
if (start !== end) return // есть выделение — пусть удаляет, нормализуется в handleChange
|
||||||
|
if (e.key === 'Backspace' && start <= PREFIX_LEN) { e.preventDefault(); return }
|
||||||
|
if (e.key === 'Delete' && start < PREFIX_LEN) { e.preventDefault() }
|
||||||
|
}
|
||||||
|
|
||||||
const guardCursor = (el: HTMLInputElement) => {
|
const guardCursor = (el: HTMLInputElement) => {
|
||||||
const start = el.selectionStart ?? 0
|
const start = el.selectionStart ?? 0
|
||||||
const end = el.selectionEnd ?? 0
|
const end = el.selectionEnd ?? 0
|
||||||
|
|
@ -118,6 +141,7 @@ export function PhoneInput({ value, onChange, className, disabled, ...rest }: Ph
|
||||||
value={display}
|
value={display}
|
||||||
onBeforeInput={handleBeforeInput}
|
onBeforeInput={handleBeforeInput}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
onSelect={handleSelect}
|
onSelect={handleSelect}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,16 @@ function valueToNational(value: string): string {
|
||||||
return toNational(value)
|
return toNational(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Снимаем наш визуальный префикс «+7 », проходя по совпадающим символам. Если
|
||||||
|
// пользователь стёр ЧАСТЬ префикса (raw="+7" или "+"), совпавший кусок всё равно
|
||||||
|
// отбрасывается, тело пустеет — цифра «7» кода страны НИКОГДА не утекает в номер
|
||||||
|
// при стирании. Остаток (тело) нормализуется отдельно.
|
||||||
|
function stripPrefix(raw: string): string {
|
||||||
|
let i = 0
|
||||||
|
while (i < PREFIX.length && i < raw.length && raw[i] === PREFIX[i]) i++
|
||||||
|
return raw.slice(i)
|
||||||
|
}
|
||||||
|
|
||||||
// Display из нац. номера. Формат: «+7 7XX XXX XX XX» — после «+7 » идут
|
// Display из нац. номера. Формат: «+7 7XX XXX XX XX» — после «+7 » идут
|
||||||
// 3+3+2+2 = 10 цифр через пробелы.
|
// 3+3+2+2 = 10 цифр через пробелы.
|
||||||
function nationalToDisplay(d: string): string {
|
function nationalToDisplay(d: string): string {
|
||||||
|
|
@ -118,14 +128,27 @@ export function PhoneInput({ value, onChange, className, disabled, ...rest }: Ph
|
||||||
const raw = e.target.value
|
const raw = e.target.value
|
||||||
const cursorPos = e.target.selectionStart ?? raw.length
|
const cursorPos = e.target.selectionStart ?? raw.length
|
||||||
pendingDigitCountRef.current = natDigitsBeforeCursor(raw, cursorPos)
|
pendingDigitCountRef.current = natDigitsBeforeCursor(raw, cursorPos)
|
||||||
// При посимвольном вводе значение начинается с «+7 » — берём часть после
|
// Тело = всё после нашего префикса «+7 » (с защитой от частично стёртого
|
||||||
// префикса, чтобы его «7» не попала в номер. При полной вставке значения
|
// префикса, см. stripPrefix). Нормализуем тело: вставка полного номера
|
||||||
// без префикса нормализуем целиком (снимем код страны/«8»).
|
// (с 8/кодом страны) приводится к 10 цифрам; при наборе первая «7» — это
|
||||||
const src = raw.startsWith(PREFIX) ? raw.slice(PREFIX_LEN) : raw
|
// начало оператора 7XX и не снимается.
|
||||||
const nat = toNational(src)
|
const nat = toNational(stripPrefix(raw))
|
||||||
onChange(nat.length > 0 ? '+7' + nat : '')
|
onChange(nat.length > 0 ? '+7' + nat : '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Префикс «+7 » нестираемый: Backspace на границе префикса (в номере стирать
|
||||||
|
// нечего) и Delete внутри/перед префиксом — глушим, чтобы «7» кода страны не
|
||||||
|
// превратилась в цифру номера. Выделение + стирание не трогаем — браузер
|
||||||
|
// очистит всё, value вернётся к «+7 ».
|
||||||
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
const el = e.currentTarget
|
||||||
|
const start = el.selectionStart ?? 0
|
||||||
|
const end = el.selectionEnd ?? 0
|
||||||
|
if (start !== end) return
|
||||||
|
if (e.key === 'Backspace' && start <= PREFIX_LEN) { e.preventDefault(); return }
|
||||||
|
if (e.key === 'Delete' && start < PREFIX_LEN) { e.preventDefault() }
|
||||||
|
}
|
||||||
|
|
||||||
// Не пускаем курсор/selection в префикс. onSelect ловит любые попытки
|
// Не пускаем курсор/selection в префикс. onSelect ловит любые попытки
|
||||||
// (клик, стрелки, Cmd+A) и мягко двигает на digit-зону.
|
// (клик, стрелки, Cmd+A) и мягко двигает на digit-зону.
|
||||||
const guardCursor = (el: HTMLInputElement) => {
|
const guardCursor = (el: HTMLInputElement) => {
|
||||||
|
|
@ -152,6 +175,7 @@ export function PhoneInput({ value, onChange, className, disabled, ...rest }: Ph
|
||||||
value={display}
|
value={display}
|
||||||
onBeforeInput={handleBeforeInput}
|
onBeforeInput={handleBeforeInput}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
onSelect={handleSelect}
|
onSelect={handleSelect}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue