fix(регистрация/iOS): перенос фокуса к первой ошибке при открытой клавиатуре
Some checks failed
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) Has been cancelled
Docker Public / Deploy :test → test.food-market.kz (push) Has been cancelled

iOS Safari: если набрать пароль и, НЕ скрывая клавиатуру, нажать кнопку —
фокус не переходил на первое поле с ошибкой (email), оставался в пароле.
Причина: focusFirstError откладывал el.focus() через setTimeout(150) — вне
пользовательского жеста iOS игнорирует программный перенос фокуса (тем более
из поля с открытой клавиатурой).

Теперь focus() вызывается СИНХРОННО в обработчике жеста (pointerup/click/Enter)
с preventScroll, а плавная прокрутка к полю — отдельно. Тап/свайп из 87a4666
не затронуты.

Проверено на WebKit-движке (Playwright webkit, hasTouch, iPhone-вьюпорт):
пароль в фокусе → тап по кнопке → фокус переходит на email; и на Chromium
(тап/свайп/долгое нажатие, первое касание, один запрос) — регрессий нет.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nns 2026-10-13 18:00:37 +05:00
parent 87a4666c25
commit b3daa889f4

View file

@ -103,13 +103,15 @@ export default function SignupForm({ defaultPlan = 'start' }: Props) {
const focusFirstError = (errs: FieldErrors) => {
const firstKey = FIELD_ORDER.find((k) => errs[k])
if (!firstKey) return
// На 1280×665 верхние поля часто выше вьюпорта — сначала проскроллим к полю,
// затем сфокусируем (focus сам по себе прокручивает неплавно/неточно).
const el = document.getElementById(`signup-${firstKey}`)
if (el) {
// Фокус СИНХРОННО, в рамках пользовательского жеста (pointerup/click/Enter).
// iOS Safari игнорирует программный перенос фокуса, отложенный через
// setTimeout или с уже открытой клавиатурой в другом поле, — поэтому
// focus() вызываем сразу. preventScroll убирает резкий прыжок, а плавную
// прокрутку к полю делаем отдельно.
el.focus({ preventScroll: true })
el.scrollIntoView({ behavior: 'smooth', block: 'center' })
// Фокус после старта прокрутки — иначе браузер перебьёт плавный скролл.
setTimeout(() => el.focus({ preventScroll: true }), 150)
}
}