Fix season browser focus: land on first episode on down, ignore parked-cursor hover

- Add data-nav-entry-col-from-above so moving down from the season
  selector always focuses the first episode instead of the tile
  visually closest to the selected season poster; moving back up
  still returns to the current season.
- Require recent real mouse movement before treating mouseover as
  mouse intent: scroll/re-render under a stationary cursor no longer
  activates mouse mode, so sideways season browsing no longer loses
  focus to the episode tile under the parked pointer.
This commit is contained in:
2026-09-23 21:30:17 +00:00
parent 9041061e86
commit 931f00f647
3 changed files with 33 additions and 1 deletions
@@ -36,6 +36,10 @@ const FOCUSABLE_ATTR = "data-nav-focusable"
const ROW_ATTR = "data-nav-row"
const COL_ATTR = "data-nav-col"
const ENTRY_COL_ATTR = "data-nav-entry-col"
// Like ENTRY_COL_ATTR, but only applies when entering the row from above
// (ArrowDown). Lets a row declare a fixed landing column for downward entry
// without hijacking upward or horizontal moves.
const ENTRY_COL_FROM_ABOVE_ATTR = "data-nav-entry-col-from-above"
const SYNC_SCROLL_ROW_ATTR = "data-sync-scroll-row"
const SYNC_SCROLL_GROUP_ATTR = "data-sync-scroll-group"
const DEFAULT_SYNC_SCROLL_GROUP = "browse"
@@ -672,8 +676,23 @@ function findNextElement(
desiredCol.value = currentCol
}
const entryTarget = findElementAt(targetRow, targetCol, true)
const targetRowElements = byRow.get(targetRow) ?? []
if (direction === "down") {
for (const el of targetRowElements) {
const fromAboveCol = el.element.getAttribute(ENTRY_COL_FROM_ABOVE_ATTR)
if (fromAboveCol === null) continue
const fromAboveTarget = targetRowElements.find(
(e) => e.col === parseInt(fromAboveCol, 10),
)
if (fromAboveTarget) {
desiredCol.value = fromAboveTarget.col
return fromAboveTarget.element
}
}
}
const entryTarget = findElementAt(targetRow, targetCol, true)
const hasEntryOverride = targetRowElements.some((el) => el.element.hasAttribute(ENTRY_COL_ATTR))
if (hasEntryOverride) {
return entryTarget?.element || null