From 3cd6b6b5555468f33755608bc12eea9f04e22068 Mon Sep 17 00:00:00 2001 From: Jason Yu Date: Tue, 12 May 2026 20:39:51 +0100 Subject: [PATCH 1/2] fix: let user-initiated snaps interrupt a forceClose handleSnapToIndex, handleSnapToPosition, handleExpand and handleCollapse all early-exit when isForcedClosing is true, which swallows the snap that BottomSheetModal.handlePresent issues when present() is called during an in-flight dismiss animation. The modal ends up unmounted under a still-visible consumer. animateToPosition already cancels any in-flight animation via stopAnimation() and clears isForcedClosing as a side effect of replacing animatedAnimationState, and the cancelled animation's onComplete fires with isFinished=false so animateToPositionCompleted early-returns (no stale handleOnChange(-1) / handleOnClose). The guard is therefore unnecessarily defensive for user-initiated snaps; the cleanup policy in evaluatePosition already gates on source !== ANIMATION_SOURCE.USER for the same reason. handleClose and handleForceClose keep their isForcedClosing guards intentionally (same-target redundancy and double-fire prevention). --- src/components/bottomSheet/BottomSheet.tsx | 30 ++++++---------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/src/components/bottomSheet/BottomSheet.tsx b/src/components/bottomSheet/BottomSheet.tsx index fc520337..575394ec 100644 --- a/src/components/bottomSheet/BottomSheet.tsx +++ b/src/components/bottomSheet/BottomSheet.tsx @@ -1126,15 +1126,12 @@ const BottomSheetComponent = forwardRef( * exit method if : * - layout is not calculated. * - already animating to next position. - * - sheet is forced closing. */ - const { nextPosition, nextIndex, isForcedClosing } = - animatedAnimationState.get(); + const { nextPosition, nextIndex } = animatedAnimationState.get(); if ( !isLayoutCalculated.value || index === nextIndex || - targetPosition === nextPosition || - isForcedClosing + targetPosition === nextPosition ) { return; } @@ -1177,14 +1174,9 @@ const BottomSheetComponent = forwardRef( * exit method if : * - layout is not calculated. * - already animating to next position. - * - sheet is forced closing. */ - const { nextPosition, isForcedClosing } = animatedAnimationState.get(); - if ( - !isLayoutCalculated || - targetPosition === nextPosition || - isForcedClosing - ) { + const { nextPosition } = animatedAnimationState.get(); + if (!isLayoutCalculated || targetPosition === nextPosition) { return; } @@ -1344,15 +1336,12 @@ const BottomSheetComponent = forwardRef( * exit method if : * - layout is not calculated. * - already animating to next position. - * - sheet is forced closing. */ - const { nextPosition, nextIndex, isForcedClosing } = - animatedAnimationState.get(); + const { nextPosition, nextIndex } = animatedAnimationState.get(); if ( !isLayoutCalculated.value || targetIndex === nextIndex || - targetPosition === nextPosition || - isForcedClosing + targetPosition === nextPosition ) { return; } @@ -1399,15 +1388,12 @@ const BottomSheetComponent = forwardRef( * exit method if : * - layout is not calculated. * - already animating to next position. - * - sheet is forced closing. */ - const { nextPosition, nextIndex, isForcedClosing } = - animatedAnimationState.get(); + const { nextPosition, nextIndex } = animatedAnimationState.get(); if ( !isLayoutCalculated || nextIndex === 0 || - targetPosition === nextPosition || - isForcedClosing + targetPosition === nextPosition ) { return; } From 35942a152ea98eedf4f04df0bd0d11f66e5569c4 Mon Sep 17 00:00:00 2001 From: Jason Yu Date: Tue, 12 May 2026 20:50:04 +0100 Subject: [PATCH 2/2] fix: use .value when checking isLayoutCalculated in handleSnapToPosition and handleCollapse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isLayoutCalculated is a SharedValue from useDerivedValue, so the bare `!isLayoutCalculated` check was `!` — always false — and the layout-readiness guard was dead code in these two methods. The other public methods (handleSnapToIndex, handleClose, handleExpand) already use .value correctly. Spotted by Copilot during review of the previous commit. Pre-existing latent bug; surfaced by the previous commit removing an adjacent term from the same condition. Folded in here rather than a separate PR to keep the file consistent across all four public methods. --- src/components/bottomSheet/BottomSheet.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/bottomSheet/BottomSheet.tsx b/src/components/bottomSheet/BottomSheet.tsx index 575394ec..63964ae7 100644 --- a/src/components/bottomSheet/BottomSheet.tsx +++ b/src/components/bottomSheet/BottomSheet.tsx @@ -1176,7 +1176,7 @@ const BottomSheetComponent = forwardRef( * - already animating to next position. */ const { nextPosition } = animatedAnimationState.get(); - if (!isLayoutCalculated || targetPosition === nextPosition) { + if (!isLayoutCalculated.value || targetPosition === nextPosition) { return; } @@ -1391,7 +1391,7 @@ const BottomSheetComponent = forwardRef( */ const { nextPosition, nextIndex } = animatedAnimationState.get(); if ( - !isLayoutCalculated || + !isLayoutCalculated.value || nextIndex === 0 || targetPosition === nextPosition ) {