fix: gamepad input field navigation — exit at cursor edges

- Up/Down from input: try containers as fallback when spatial nav fails
- Left/Right from input: exit field when cursor is at start/end
  (e.g. Left from search bar at position 0 → category buttons)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-30 09:17:39 +01:00
parent ed9fd5c823
commit 7d61fc1790

View File

@@ -246,14 +246,38 @@ export function useControllerNav(containerRef?: { value: HTMLElement | null }) {
if (nearest) {
focusEl(nearest)
} else {
// Fallback: tab order when spatial navigation fails
const idx = all.indexOf(target)
const fallback = dir === 'down' ? all[idx + 1] : all[idx - 1]
if (fallback) focusEl(fallback)
// Spatial nav failed — try containers directly (e.g. search bar → first container)
const containers = getContainers()
const containerNearest = containers.length
? findNearestInDirection(target, containers, dir)
: null
if (containerNearest) {
focusEl(containerNearest)
} else {
// Last fallback: tab order
const idx = all.indexOf(target)
const fallback = dir === 'down' ? all[idx + 1] : all[idx - 1]
if (fallback) focusEl(fallback)
}
}
return
}
// Left/Right: stay in field (cursor movement). Escape: handled below.
// Left/Right: cursor movement in field, but exit at edges
if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
const input = target as HTMLInputElement
const atStart = input.selectionStart === 0 && input.selectionEnd === 0
const atEnd = input.selectionStart === (input.value?.length ?? 0)
if ((e.key === 'ArrowLeft' && atStart) || (e.key === 'ArrowRight' && atEnd)) {
e.preventDefault()
const dir = e.key === 'ArrowLeft' ? 'left' as const : 'right' as const
const all = getFocusableElements(containerRef?.value ?? document)
const candidates = all.filter(el => el !== target)
const nearest = findNearestInDirection(target, candidates, dir)
if (nearest) focusEl(nearest)
}
return
}
// Other keys (Escape): handled below.
if (e.key !== 'Escape') return
}