Skip to content

fix(swmansion): report 0 as prevIndex for the programmatic open - #48

Merged
arekkubaczkowski merged 1 commit into
mainfrom
fix/swmansion-onindexchange-prev-on-open
Aug 19, 2026
Merged

fix(swmansion): report 0 as prevIndex for the programmatic open#48
arekkubaczkowski merged 1 commit into
mainfrom
fix/swmansion-onindexchange-prev-on-open

Conversation

@arekkubaczkowski

Copy link
Copy Markdown
Owner

Problem

onIndexChange(nextIndex, prevIndex) never reports the 0 -> open transition for portal and inline sheets, so a consumer handler like this one never fires:

if (index > 0 && prevIndex === 0) Haptics.impactAsync(...);

lastIndexRef — the source of prevIndex — was seeded from the index state, and that state does not start at 0 everywhere:

mode defaultIndex initial index expand() emits
inline (no provider) 0 openIndex (openIndex, openIndex)
BottomSheetPortal 0 openIndex (openIndex, openIndex)
BottomSheetPersistent -1 0 (openIndex, 0)

For portal/inline sheets mounting is the open — native animates in from the collapsed detent — so the initial index is the animation's target, not the sheet's current position. Seeding prevIndex from it hides the very transition the second argument exists to expose.

It fails on every open, not just the first: close() does resync the tracker to 0, but a portal sheet unmounts on close, so the ref is recreated at openIndex on the next mount.

Fix

Seed the tracker at 0. The sheet is off screen until expand() lands, in every mode, so 0 is the truthful previous position.

Verified against a rendered adapter tree in both modes — before: defaultIndex 0 -> [[1, 1]], defaultIndex -1 -> [[1, 0]]; after: [[1, 0]] in both.

Note

Side effect worth a look: on a switch restore the coordinator drives close() then expand() again, so the restored sheet underneath now also reports (openIndex, 0) and would re-fire an open-haptic. That reads as correct to me — it is a fresh open from the consumer's side — but it is a behavior change.

No regression test added yet; say the word and I'll add one to src/__tests__/.

yarn lint, yarn typecheck, yarn test (122/122) all pass.

🤖 Generated with Claude Code

`lastIndexRef` seeded from the `index` state, which starts at `openIndex`
for portal and inline sheets — mounting *is* the open there, and native
animates in from the collapsed detent. So `expand()` emitted
`onIndexChange(openIndex, openIndex)` and consumers branching on the
0 -> open transition (haptics on open, for one) never fired. Portal
sheets unmount on close, so the ref reset every cycle and the transition
was never observable at all; only persistent sheets (`defaultIndex: -1`,
so `index` starts at 0) got it right.

Seed the tracker at 0 instead: the sheet is off screen until `expand()`
lands, in every mode.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@arekkubaczkowski
arekkubaczkowski merged commit 6477c83 into main Aug 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant