fix(rtl): horizontal initialScrollIndex near the end settles on the mirror index#478
Conversation
… RTL initialContentOffset seed A horizontal RTL list opened with initialScrollIndex near the end can settle on the RTL mirror index. The initial-scroll watchdog/retry (scrollToFallbackOffset) dispatched the *unconverted* logical offset, and the initialContentOffset seed was also taken unconverted for RTL. Convert both, mirroring the normal doScrollTo dispatch path. Fixes LegendApp#476 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 405cb2c8d1
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| isHorizontalRTLProps({ horizontal, rtl }) | ||
| ? undefined |
There was a problem hiding this comment.
Don't drop offset-only initial scrolls in RTL
When a horizontal RTL list uses initialScrollOffset without initialScrollIndex, this branch returns undefined even though the initial-scroll session is offset-only. initializeInitialScrollOnMount then treats that as 0, and because alwaysDispatchInitialScroll is false when an offset prop is present, it finishes and clears the initial scroll at the origin instead of dispatching the converted offset; the initialScrollOffset prop is effectively ignored for this case. Limit this skip to bootstrap sessions or force a converted dispatch for offset-only sessions.
Useful? React with 👍 / 👎.
Addresses Codex review: the previous skip applied to all horizontal RTL lists, so an offset-only RTL initial scroll (initialScrollOffset without initialScrollIndex) dropped its offset and finished at 0. Scope the RTL skip to bootstrap sessions (initialScrollIndex / initialScrollAtEnd), where the converting bootstrap dispatch re-applies the position; offset-only sessions keep their resolved offset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes #476.
Problem
A horizontal RTL
LegendListopened with aninitialScrollIndexnear the end can settle on the RTL mirror index instead of the requested one (e.g. request item 595 of 604 → settles on item 10 =total − 1 − 594).Legend List keeps logical (LTR) scroll offsets and converts to the native RTL coordinate via
toNativeHorizontalOffset. Two spots on the initial-scroll path skipped that conversion:scrollToFallbackOffset(the initial-scroll watchdog/retry incheckFinishedScroll.ts) dispatched the unconverted logical offset directly to the nativescrollTo. When the watchdog wins the race against the bootstrap scroll's nativeonScrollround-trip (more likely on a large jump / slow device / withrecycleItems), the list lands on the mirror.initialContentOffsetseed (the first nativecontentOffset) was also taken unconverted for RTL.Fix
Convert the offset in
scrollToFallbackOffset(mirroring the normaldoScrollTodispatch), and skip the unconverted RTLinitialContentOffsetseed so the converting initial-scroll dispatch positions the list.Verification
bun run tsc:src✅,bunx biome check✅,bun test✅ (no new failures; one pre-existing old-arch bootstrap test fails onmainunchanged).@legendapp/list@3.0.6, RN 0.85.3, Android): without the fix the list settles on the mirror index (deterministic, 5/5 cold starts); with the fix it settles on the requested item. Repro: https://github.com/Mohamed-kassim/legend-list-rtl-reproScoped to horizontal RTL — non-RTL and vertical paths are unaffected (
isHorizontalRTLProps/horizontalguards).