@lodev09/react-native-true-sheet 4.0.0-beta.2 → 4.0.0-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -4
- package/android/build.gradle +22 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +50 -16
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +302 -184
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +21 -110
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +116 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +18 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +119 -50
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +360 -163
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +54 -12
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +22 -9
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +84 -5
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +104 -39
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +3 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +137 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +15 -2
- package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
- package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
- package/android/src/main/jni/TrueSheetSpec.h +1 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +1 -9
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +110 -30
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +30 -7
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +2 -2
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +9 -8
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +9 -3
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +3 -6
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +4 -6
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +43 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +33 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +7 -40
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
- package/ios/TrueSheetContainerView.h +27 -7
- package/ios/TrueSheetContainerView.mm +31 -11
- package/ios/TrueSheetContentView.h +43 -15
- package/ios/TrueSheetContentView.mm +211 -189
- package/ios/TrueSheetFooterView.h +6 -0
- package/ios/TrueSheetFooterView.mm +93 -17
- package/ios/TrueSheetModule.h +2 -7
- package/ios/TrueSheetModule.mm +7 -16
- package/ios/TrueSheetOverlayView.h +32 -0
- package/ios/TrueSheetOverlayView.mm +161 -0
- package/ios/TrueSheetView.mm +212 -55
- package/ios/TrueSheetViewController.h +31 -3
- package/ios/TrueSheetViewController.mm +393 -94
- package/ios/core/TrueSheetBlurView.h +0 -2
- package/ios/core/TrueSheetBlurView.mm +3 -36
- package/ios/core/TrueSheetDetentCalculator.h +7 -1
- package/ios/core/TrueSheetDetentCalculator.mm +27 -4
- package/ios/core/TrueSheetGrabberView.mm +13 -3
- package/ios/core/TrueSheetKeyboardObserver.mm +2 -0
- package/ios/core/TrueSheetOverlayContainerView.h +31 -0
- package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
- package/ios/events/TrueSheetLifecycleEvents.h +2 -0
- package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
- package/ios/tvos/TrueSheetStubs.mm +12 -0
- package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
- package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
- package/lib/module/TrueSheet.js +173 -39
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +218 -243
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetBackground.js +15 -0
- package/lib/module/TrueSheetBackground.js.map +1 -0
- package/lib/module/TrueSheetOverlay.js +30 -0
- package/lib/module/TrueSheetOverlay.js.map +1 -0
- package/lib/module/TrueSheetOverlay.web.js +35 -0
- package/lib/module/TrueSheetOverlay.web.js.map +1 -0
- package/lib/module/TrueSheetPeek.web.js +20 -15
- package/lib/module/TrueSheetPeek.web.js.map +1 -1
- package/lib/module/detentBackgrounds.js +21 -0
- package/lib/module/detentBackgrounds.js.map +1 -0
- package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +14 -10
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/mocks/index.js +18 -2
- package/lib/module/mocks/index.js.map +1 -1
- package/lib/module/mocks/navigationExpoRouter.js.map +1 -1
- package/lib/module/navigation/base-types.js +2 -0
- package/lib/module/navigation/base-types.js.map +1 -0
- package/lib/module/navigation/createTrueSheetRouter.js +0 -9
- package/lib/module/navigation/createTrueSheetRouter.js.map +1 -1
- package/lib/module/navigation/expo-router/base-types.js +2 -0
- package/lib/module/navigation/expo-router/base-types.js.map +1 -0
- package/lib/module/navigation/expo-router/index.js +10 -7
- package/lib/module/navigation/expo-router/index.js.map +1 -1
- package/lib/module/navigation/expo-router/types.js +4 -0
- package/lib/module/navigation/expo-router/types.js.map +1 -0
- package/lib/module/navigation/index.js.map +1 -1
- package/lib/module/navigation/screen/useSheetScreenState.js +1 -0
- package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
- package/lib/module/web/constants.js +1 -1
- package/lib/module/web/constants.js.map +1 -1
- package/lib/module/web/layout.js +168 -0
- package/lib/module/web/layout.js.map +1 -0
- package/lib/module/web/vaul/constants.js +4 -0
- package/lib/module/web/vaul/constants.js.map +1 -1
- package/lib/module/web/vaul/context.js.map +1 -1
- package/lib/module/web/vaul/helpers.js +1 -0
- package/lib/module/web/vaul/helpers.js.map +1 -1
- package/lib/module/web/vaul/index.js +44 -6
- package/lib/module/web/vaul/index.js.map +1 -1
- package/lib/module/web/vaul/use-snap-points.js +6 -8
- package/lib/module/web/vaul/use-snap-points.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts +6 -0
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +135 -34
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetBackground.d.ts +3 -0
- package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
- package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
- package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetPeek.web.d.ts +8 -4
- package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
- package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
- package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
- package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +11 -7
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/index.d.ts +4 -0
- package/lib/typescript/src/mocks/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +3 -3
- package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -1
- package/lib/typescript/src/navigation/base-types.d.ts +9 -0
- package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +2 -13
- package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -1
- package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
- package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/expo-router/index.d.ts +8 -6
- package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
- package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/index.d.ts +3 -4
- package/lib/typescript/src/navigation/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +2 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
- package/lib/typescript/src/navigation/types.d.ts +41 -5
- package/lib/typescript/src/navigation/types.d.ts.map +1 -1
- package/lib/typescript/src/web/constants.d.ts +1 -1
- package/lib/typescript/src/web/constants.d.ts.map +1 -1
- package/lib/typescript/src/web/layout.d.ts +19 -0
- package/lib/typescript/src/web/layout.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/constants.d.ts +1 -0
- package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/context.d.ts +1 -0
- package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/helpers.d.ts +1 -0
- package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/index.d.ts +7 -1
- package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
- package/package.json +17 -8
- package/react-native.config.js +1 -0
- package/src/TrueSheet.tsx +206 -48
- package/src/TrueSheet.types.ts +142 -35
- package/src/TrueSheet.web.tsx +270 -290
- package/src/TrueSheetBackground.tsx +17 -0
- package/src/TrueSheetOverlay.tsx +24 -0
- package/src/TrueSheetOverlay.web.tsx +33 -0
- package/src/TrueSheetPeek.web.tsx +25 -23
- package/src/detentBackgrounds.ts +24 -0
- package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/src/fabric/TrueSheetViewNativeComponent.ts +14 -10
- package/src/index.ts +1 -0
- package/src/mocks/index.ts +17 -2
- package/src/mocks/navigationExpoRouter.ts +6 -5
- package/src/navigation/base-types.ts +20 -0
- package/src/navigation/createTrueSheetRouter.ts +5 -33
- package/src/navigation/expo-router/base-types.ts +18 -0
- package/src/navigation/expo-router/index.ts +28 -30
- package/src/navigation/expo-router/types.ts +116 -0
- package/src/navigation/index.ts +6 -7
- package/src/navigation/screen/useSheetScreenState.ts +2 -0
- package/src/navigation/types.ts +75 -22
- package/src/web/constants.ts +1 -1
- package/src/web/layout.ts +196 -0
- package/src/web/vaul/constants.ts +4 -0
- package/src/web/vaul/context.ts +1 -0
- package/src/web/vaul/helpers.ts +5 -0
- package/src/web/vaul/index.tsx +67 -5
- package/src/web/vaul/use-snap-points.ts +6 -14
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.lodev09.truesheet
|
|
2
2
|
|
|
3
|
+
import android.animation.ValueAnimator
|
|
3
4
|
import android.annotation.SuppressLint
|
|
4
5
|
import android.graphics.Canvas
|
|
5
6
|
import android.os.Build
|
|
@@ -37,6 +38,7 @@ import com.lodev09.truesheet.core.TrueSheetDimViewDelegate
|
|
|
37
38
|
import com.lodev09.truesheet.core.TrueSheetKeyboardObserver
|
|
38
39
|
import com.lodev09.truesheet.core.TrueSheetKeyboardObserverDelegate
|
|
39
40
|
import com.lodev09.truesheet.core.TrueSheetStackManager
|
|
41
|
+
import com.lodev09.truesheet.utils.Insets
|
|
40
42
|
import com.lodev09.truesheet.utils.KeyboardUtils
|
|
41
43
|
import com.lodev09.truesheet.utils.ScreenUtils
|
|
42
44
|
import com.lodev09.truesheet.utils.TouchEventDeduper
|
|
@@ -54,6 +56,7 @@ interface TrueSheetViewControllerDelegate {
|
|
|
54
56
|
fun viewControllerDidPresent(index: Int, position: Float, detent: Float)
|
|
55
57
|
fun viewControllerWillDismiss()
|
|
56
58
|
fun viewControllerDidDismiss(parent: TrueSheetView?)
|
|
59
|
+
fun viewControllerDidAttemptDismiss()
|
|
57
60
|
fun viewControllerDidChangeDetent(index: Int, position: Float, detent: Float)
|
|
58
61
|
fun viewControllerDidDragBegin(index: Int, position: Float, detent: Float)
|
|
59
62
|
fun viewControllerDidDragChange(index: Int, position: Float, detent: Float)
|
|
@@ -78,17 +81,19 @@ interface TrueSheetViewControllerDelegate {
|
|
|
78
81
|
* enabling touch pass-through to underlying views. Handles detent configuration, drag interactions,
|
|
79
82
|
* keyboard avoidance, dimmed backgrounds, back button, and lifecycle events for stacked sheets.
|
|
80
83
|
*/
|
|
81
|
-
enum class
|
|
82
|
-
|
|
84
|
+
enum class TrueSheetPlacement {
|
|
85
|
+
AUTOMATIC,
|
|
86
|
+
LEADING,
|
|
83
87
|
CENTER,
|
|
84
|
-
|
|
88
|
+
TRAILING;
|
|
85
89
|
|
|
86
90
|
companion object {
|
|
87
|
-
fun fromString(value: String?):
|
|
91
|
+
fun fromString(value: String?): TrueSheetPlacement =
|
|
88
92
|
when (value) {
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
|
|
93
|
+
"leading" -> LEADING
|
|
94
|
+
"center" -> CENTER
|
|
95
|
+
"trailing" -> TRAILING
|
|
96
|
+
else -> AUTOMATIC
|
|
92
97
|
}
|
|
93
98
|
}
|
|
94
99
|
}
|
|
@@ -118,10 +123,14 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
118
123
|
companion object {
|
|
119
124
|
private const val DEFAULT_MAX_WIDTH = 640 // dp
|
|
120
125
|
private const val DEFAULT_CORNER_RADIUS = 16 // dp
|
|
121
|
-
private const val
|
|
126
|
+
private const val DEFAULT_PLACEMENT_OFFSET = 16 // dp
|
|
122
127
|
private const val TRANSLATE_ANIMATION_DURATION = 200L
|
|
123
128
|
private const val DISMISS_DURATION = 200L
|
|
124
129
|
private const val SCREEN_FADE_DURATION = 150L
|
|
130
|
+
|
|
131
|
+
// Mirrors BottomSheetBehavior.HIDE_THRESHOLD — how far past the lowest stop,
|
|
132
|
+
// as a fraction of its height, a release hides a dismissible sheet
|
|
133
|
+
private const val HIDE_THRESHOLD = 0.5f
|
|
125
134
|
}
|
|
126
135
|
|
|
127
136
|
// =============================================================================
|
|
@@ -162,6 +171,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
162
171
|
private var pendingDetentIndex: Int = -1
|
|
163
172
|
|
|
164
173
|
private var interactionState: InteractionState = InteractionState.Idle
|
|
174
|
+
|
|
175
|
+
// A content/header/footer size change arrived mid-drag — reconfiguring then
|
|
176
|
+
// would reset behavior state and kill the gesture, so it's applied on settle.
|
|
177
|
+
private var hasPendingSizeChange = false
|
|
165
178
|
internal var isBeingDismissed = false
|
|
166
179
|
private set
|
|
167
180
|
var wasHiddenByScreen = false
|
|
@@ -175,6 +188,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
175
188
|
// Keyboard State
|
|
176
189
|
private var detentIndexBeforeKeyboard: Int = -1
|
|
177
190
|
private var isKeyboardDismissProgrammatic = false
|
|
191
|
+
|
|
192
|
+
// Sheet top when a keyboard-floor drag was released; -1 once the settle target is known
|
|
193
|
+
private var keyboardSettleStartTop = -1
|
|
178
194
|
private var focusedViewBeforeBlur: View? = null
|
|
179
195
|
|
|
180
196
|
// Promises
|
|
@@ -194,12 +210,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
194
210
|
// Touch Dispatchers
|
|
195
211
|
private val jsTouchDispatcher = JSTouchDispatcher(this)
|
|
196
212
|
private val jsPointerDispatcher = JSPointerDispatcher(this)
|
|
197
|
-
|
|
198
|
-
// True when the active touch stream began inside the footer. Latched on
|
|
199
|
-
// ACTION_DOWN so subsequent MOVE events keep routing to the footer even if
|
|
200
|
-
// the finger drifts outside the footer's rect mid-gesture.
|
|
201
|
-
private var footerOwnsTouchStream = false
|
|
202
|
-
|
|
203
213
|
private val touchDeduper = TouchEventDeduper()
|
|
204
214
|
|
|
205
215
|
private val eventDispatcher
|
|
@@ -208,8 +218,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
208
218
|
// Detent Configuration
|
|
209
219
|
override var maxContentHeight: Int? = null
|
|
210
220
|
override var maxContentWidth: Int? = null
|
|
211
|
-
override var
|
|
212
|
-
override var
|
|
221
|
+
override var placement: TrueSheetPlacement = TrueSheetPlacement.AUTOMATIC
|
|
222
|
+
override var placementOffset: Int = DEFAULT_PLACEMENT_OFFSET.dpToPx().toInt()
|
|
213
223
|
override var detents: MutableList<Double> = mutableListOf(0.5, 1.0)
|
|
214
224
|
|
|
215
225
|
// Appearance Configuration
|
|
@@ -218,7 +228,13 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
218
228
|
override var grabber: Boolean = true
|
|
219
229
|
override var grabberOptions: GrabberOptions? = null
|
|
220
230
|
override var accessibilityOptions: AccessibilityOptions? = null
|
|
231
|
+
var backgroundColor: Int? = null
|
|
232
|
+
var detentBackgrounds: List<Int?> = emptyList()
|
|
233
|
+
private var displayedBackgroundIndex = 0
|
|
234
|
+
private var backgroundAnimator: ValueAnimator? = null
|
|
235
|
+
private var targetBackgroundColor: Int? = null
|
|
221
236
|
override var sheetBackgroundColor: Int? = null
|
|
237
|
+
private set
|
|
222
238
|
var insetAdjustment: TrueSheetInsetAdjustment = TrueSheetInsetAdjustment.AUTOMATIC
|
|
223
239
|
|
|
224
240
|
var scrollableOptions: ScrollableOptions? = null
|
|
@@ -227,6 +243,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
227
243
|
behavior?.scrollingExpandsSheet = value?.scrollingExpandsSheet ?: true
|
|
228
244
|
}
|
|
229
245
|
|
|
246
|
+
// React tag of the user-provided scrollable within the content (see the scrollableRef prop)
|
|
247
|
+
var scrollableHandle: Int = -1
|
|
248
|
+
|
|
230
249
|
override var sheetCornerRadius: Float = DEFAULT_CORNER_RADIUS.dpToPx()
|
|
231
250
|
set(value) {
|
|
232
251
|
field = if (value < 0) DEFAULT_CORNER_RADIUS.dpToPx() else value
|
|
@@ -270,9 +289,29 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
270
289
|
val screenWidth: Int
|
|
271
290
|
get() = ScreenUtils.getScreenWidth(reactContext)
|
|
272
291
|
|
|
292
|
+
// Cached per layout/configuration pass — the WindowManager and
|
|
293
|
+
// rootWindowInsets queries behind these allocate, and they're read many
|
|
294
|
+
// times per animation frame (position emission, detent interpolation,
|
|
295
|
+
// stacked sheet sync)
|
|
296
|
+
private var cachedRealScreenHeight: Int = 0
|
|
297
|
+
private var cachedInsets: Insets? = null
|
|
298
|
+
|
|
299
|
+
private fun invalidateScreenMetrics() {
|
|
300
|
+
cachedRealScreenHeight = 0
|
|
301
|
+
cachedInsets = null
|
|
302
|
+
}
|
|
303
|
+
|
|
273
304
|
// Includes system bars for accurate positioning
|
|
274
305
|
override val realScreenHeight: Int
|
|
275
|
-
get()
|
|
306
|
+
get() {
|
|
307
|
+
if (cachedRealScreenHeight == 0) {
|
|
308
|
+
cachedRealScreenHeight = ScreenUtils.getRealScreenHeight(reactContext)
|
|
309
|
+
}
|
|
310
|
+
return cachedRealScreenHeight
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private val insets: Insets
|
|
314
|
+
get() = cachedInsets ?: ScreenUtils.getInsets(reactContext).also { cachedInsets = it }
|
|
276
315
|
|
|
277
316
|
// Content Measurements
|
|
278
317
|
// Cached values used during dismiss when container is unmounted
|
|
@@ -294,6 +333,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
294
333
|
|
|
295
334
|
override var absoluteFooter: Boolean = false
|
|
296
335
|
|
|
336
|
+
override val footerInsetAdjustment: Boolean
|
|
337
|
+
get() = absoluteFooter && scrollableHandle > 0 && scrollableOptions?.contentInsetAdjustment?.footer != false
|
|
338
|
+
|
|
297
339
|
override val peekContentHeight: Int
|
|
298
340
|
get() = containerView?.peekContentHeight ?: cachedPeekContentHeight
|
|
299
341
|
|
|
@@ -309,16 +351,30 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
309
351
|
private val isKeyboardTransitioning: Boolean
|
|
310
352
|
get() = keyboardObserver?.isTransitioning ?: false
|
|
311
353
|
|
|
354
|
+
// Keyboard is up or animating — detents are (or are about to be) keyboard-shifted
|
|
355
|
+
val isKeyboardActive: Boolean
|
|
356
|
+
get() = keyboardInset > 0 || isKeyboardTransitioning
|
|
357
|
+
|
|
358
|
+
// While the keyboard holds the sheet up, the keyboard-free first detent is the
|
|
359
|
+
// collapsed stop — landing there dismisses the keyboard, and (when dismissible)
|
|
360
|
+
// dragging more than half its height below it hides the sheet too, like iOS.
|
|
361
|
+
override val hasKeyboardFloor: Boolean
|
|
362
|
+
get() = keyboardInset > 0
|
|
363
|
+
|
|
364
|
+
// The detent JS last heard about — the keyboard expansion is never reported
|
|
365
|
+
private val knownDetentIndex: Int
|
|
366
|
+
get() = if (detentIndexBeforeKeyboard >= 0) detentIndexBeforeKeyboard else currentDetentIndex
|
|
367
|
+
|
|
312
368
|
fun isFocusedViewWithinSheet(): Boolean {
|
|
313
369
|
val sheet = sheetView ?: return false
|
|
314
370
|
return keyboardObserver?.isFocusedViewWithinSheet(sheet) ?: false
|
|
315
371
|
}
|
|
316
372
|
|
|
317
373
|
val bottomInset: Int
|
|
318
|
-
get() = if (edgeToEdgeEnabled)
|
|
374
|
+
get() = if (edgeToEdgeEnabled) insets.bottom else 0
|
|
319
375
|
|
|
320
376
|
override val topInset: Int
|
|
321
|
-
get() = if (edgeToEdgeEnabled)
|
|
377
|
+
get() = if (edgeToEdgeEnabled) insets.top else 0
|
|
322
378
|
|
|
323
379
|
override val contentBottomInset: Int
|
|
324
380
|
get() = if (insetAdjustment == TrueSheetInsetAdjustment.AUTOMATIC) bottomInset else 0
|
|
@@ -331,6 +387,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
331
387
|
}
|
|
332
388
|
|
|
333
389
|
// Sheet State
|
|
390
|
+
val isSettling: Boolean
|
|
391
|
+
get() = behavior?.state == BottomSheetBehavior.STATE_SETTLING
|
|
392
|
+
|
|
334
393
|
val isExpanded: Boolean
|
|
335
394
|
get() {
|
|
336
395
|
val sheetTop = sheetView?.top ?: return false
|
|
@@ -373,6 +432,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
373
432
|
|
|
374
433
|
private fun cleanupSheet() {
|
|
375
434
|
cleanupKeyboardObserver()
|
|
435
|
+
backgroundAnimator?.cancel()
|
|
436
|
+
backgroundAnimator = null
|
|
437
|
+
targetBackgroundColor = null
|
|
376
438
|
sheetView?.animate()?.cancel()
|
|
377
439
|
|
|
378
440
|
// Cleanup dim views
|
|
@@ -392,6 +454,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
392
454
|
sheetView = null
|
|
393
455
|
|
|
394
456
|
interactionState = InteractionState.Idle
|
|
457
|
+
hasPendingSizeChange = false
|
|
395
458
|
isBeingDismissed = false
|
|
396
459
|
isPresented = false
|
|
397
460
|
isSheetVisible = false
|
|
@@ -440,13 +503,15 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
440
503
|
// =============================================================================
|
|
441
504
|
|
|
442
505
|
override fun coordinatorLayoutDidLayout(changed: Boolean) {
|
|
443
|
-
|
|
506
|
+
if (changed) invalidateScreenMetrics()
|
|
507
|
+
|
|
444
508
|
if (isPresented && changed) {
|
|
445
|
-
|
|
509
|
+
setupSheetDetentsForSizeChange()
|
|
446
510
|
}
|
|
447
511
|
}
|
|
448
512
|
|
|
449
513
|
override fun coordinatorLayoutDidChangeConfiguration() {
|
|
514
|
+
invalidateScreenMetrics()
|
|
450
515
|
if (!isPresented) return
|
|
451
516
|
|
|
452
517
|
sheetView?.updateGravity()
|
|
@@ -458,6 +523,28 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
458
523
|
override fun findScrollView(): ViewGroup? = containerView?.contentView?.findScrollView()
|
|
459
524
|
override fun findSheetView(): TrueSheetBottomSheetView? = sheetView
|
|
460
525
|
|
|
526
|
+
override fun coordinatorLayoutDidPullDown(overdragPx: Int, keyboardWasVisible: Boolean) {
|
|
527
|
+
if (!isPresented || dismissible || !draggable) return
|
|
528
|
+
val sheet = sheetView ?: return
|
|
529
|
+
|
|
530
|
+
// Only a pull clamped at the lowest stop is a blocked drag-to-dismiss — a
|
|
531
|
+
// child that swallowed the gesture higher up isn't
|
|
532
|
+
val lowestTop = detentCalculator.getLowestSheetTop()
|
|
533
|
+
if (sheet.top < lowestTop) return
|
|
534
|
+
|
|
535
|
+
if (keyboardWasVisible) {
|
|
536
|
+
// A sheet that couldn't move at all (first detent already full height) never
|
|
537
|
+
// began a drag, so the keyboard is still up — dismiss it here
|
|
538
|
+
if (hasKeyboardFloor && !isKeyboardDismissProgrammatic) commitDetentAndDismissKeyboard(0, sheet)
|
|
539
|
+
|
|
540
|
+
// Past the floor by the distance that would hide a dismissible sheet, like iOS
|
|
541
|
+
val floorHeight = realScreenHeight - lowestTop
|
|
542
|
+
if (overdragPx < floorHeight * HIDE_THRESHOLD) return
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
delegate?.viewControllerDidAttemptDismiss()
|
|
546
|
+
}
|
|
547
|
+
|
|
461
548
|
// =============================================================================
|
|
462
549
|
// MARK: - TrueSheetDimViewDelegate
|
|
463
550
|
// =============================================================================
|
|
@@ -495,24 +582,30 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
495
582
|
|
|
496
583
|
override fun bottomSheetViewDidTapGrabber() {
|
|
497
584
|
val nextIndex = (currentDetentIndex + 1) % detents.size
|
|
498
|
-
if (nextIndex == 0 && detents.size == 1
|
|
499
|
-
|
|
585
|
+
if (nextIndex == 0 && detents.size == 1) {
|
|
586
|
+
if (dismissible) {
|
|
587
|
+
dismiss(animated = true)
|
|
588
|
+
} else {
|
|
589
|
+
delegate?.viewControllerDidAttemptDismiss()
|
|
590
|
+
}
|
|
500
591
|
} else {
|
|
501
|
-
|
|
592
|
+
resize(nextIndex)
|
|
502
593
|
}
|
|
503
594
|
}
|
|
504
595
|
|
|
505
596
|
override fun bottomSheetViewDidAccessibilityIncrement() {
|
|
506
597
|
if (currentDetentIndex < detents.size - 1) {
|
|
507
|
-
|
|
598
|
+
resize(currentDetentIndex + 1)
|
|
508
599
|
}
|
|
509
600
|
}
|
|
510
601
|
|
|
511
602
|
override fun bottomSheetViewDidAccessibilityDecrement() {
|
|
512
603
|
if (currentDetentIndex > 0) {
|
|
513
|
-
|
|
604
|
+
resize(currentDetentIndex - 1)
|
|
514
605
|
} else if (dismissible) {
|
|
515
606
|
dismiss(animated = true)
|
|
607
|
+
} else {
|
|
608
|
+
delegate?.viewControllerDidAttemptDismiss()
|
|
516
609
|
}
|
|
517
610
|
}
|
|
518
611
|
|
|
@@ -526,7 +619,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
526
619
|
}
|
|
527
620
|
|
|
528
621
|
override fun onSlide(sheetView: View, slideOffset: Float) {
|
|
529
|
-
handleSlide(sheetView
|
|
622
|
+
handleSlide(sheetView)
|
|
530
623
|
}
|
|
531
624
|
}
|
|
532
625
|
|
|
@@ -555,27 +648,44 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
555
648
|
}
|
|
556
649
|
}
|
|
557
650
|
|
|
558
|
-
private fun handleSlide(sheetView: View
|
|
651
|
+
private fun handleSlide(sheetView: View) {
|
|
559
652
|
// Skip during dismiss animation
|
|
560
653
|
if (isBeingDismissed) return
|
|
561
654
|
|
|
562
655
|
val behavior = behavior ?: return
|
|
563
656
|
|
|
657
|
+
if (interactionState is InteractionState.Dragging || pendingDetentIndex >= 0) {
|
|
658
|
+
updateStateDimensionsForPosition(sheetView.top)
|
|
659
|
+
}
|
|
660
|
+
|
|
564
661
|
when (behavior.state) {
|
|
565
|
-
BottomSheetBehavior.STATE_DRAGGING
|
|
566
|
-
|
|
662
|
+
BottomSheetBehavior.STATE_DRAGGING -> handleDragChange(sheetView)
|
|
663
|
+
|
|
664
|
+
BottomSheetBehavior.STATE_SETTLING -> {
|
|
665
|
+
handleDragChange(sheetView)
|
|
666
|
+
dismissKeyboardForSettleTarget(sheetView)
|
|
667
|
+
}
|
|
567
668
|
|
|
568
669
|
else -> { }
|
|
569
670
|
}
|
|
570
671
|
|
|
571
672
|
emitChangePositionDelegate(sheetView.top)
|
|
572
673
|
|
|
674
|
+
// Drive the parent's stack translation from our actual frame position —
|
|
675
|
+
// an independently-timed animation lags behind (e.g. auto detent shrink)
|
|
676
|
+
// and reveals the parent above this sheet's top edge. Skipped while
|
|
677
|
+
// dragging so the parent stays put during drag-to-dismiss, and while the
|
|
678
|
+
// keyboard is active so growing for it doesn't move the parent (like iOS).
|
|
679
|
+
if (!isPresentAnimating && interactionState !is InteractionState.Dragging && !isKeyboardActive) {
|
|
680
|
+
syncParentTranslation(sheetView.top)
|
|
681
|
+
}
|
|
682
|
+
|
|
573
683
|
// Position on every slide frame — during keyboard transitions the IME
|
|
574
684
|
// animation callbacks are sparser than the sheet's settle frames, and the
|
|
575
685
|
// footer rides the sheet between them, jittering against the keyboard.
|
|
576
686
|
// positionFooter derives from current state, so double-driving it with
|
|
577
687
|
// keyboardDidChangeHeight is harmless.
|
|
578
|
-
positionFooter(
|
|
688
|
+
positionFooter()
|
|
579
689
|
|
|
580
690
|
if (!isKeyboardTransitioning) {
|
|
581
691
|
updateDimAmount(sheetView.top)
|
|
@@ -585,11 +695,22 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
585
695
|
private fun handleStateSettled(sheetView: View, newState: Int) {
|
|
586
696
|
if (interactionState is InteractionState.Reconfiguring) return
|
|
587
697
|
|
|
698
|
+
// A release already at a stop settles without a slide frame
|
|
699
|
+
keyboardSettleStartTop = -1
|
|
700
|
+
|
|
588
701
|
// Sheet has reached a stable state — any in-flight resize target is now stale,
|
|
589
702
|
// whether settled at the target, interrupted by drag, or superseded.
|
|
590
703
|
pendingDetentIndex = -1
|
|
591
704
|
|
|
592
|
-
val index = detentCalculator.getDetentIndexForState(newState) ?:
|
|
705
|
+
val index = detentCalculator.getDetentIndexForState(newState) ?: run {
|
|
706
|
+
// Unmapped settle — end any drag anyway so deferred size changes aren't
|
|
707
|
+
// dropped forever (a leaked Dragging state skips every reconfigure)
|
|
708
|
+
if (interactionState is InteractionState.Dragging) {
|
|
709
|
+
interactionState = InteractionState.Idle
|
|
710
|
+
}
|
|
711
|
+
flushPendingSizeChange()
|
|
712
|
+
return
|
|
713
|
+
}
|
|
593
714
|
val position = getPositionDpForView(sheetView)
|
|
594
715
|
val detentInfo = DetentInfo(index, position)
|
|
595
716
|
|
|
@@ -597,21 +718,30 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
597
718
|
if (isPresentAnimating) {
|
|
598
719
|
isPresentAnimating = false
|
|
599
720
|
finishPresent()
|
|
721
|
+
flushPendingSizeChange()
|
|
600
722
|
return
|
|
601
723
|
}
|
|
602
724
|
|
|
725
|
+
val wasDragging = interactionState is InteractionState.Dragging
|
|
726
|
+
|
|
603
727
|
when (interactionState) {
|
|
604
728
|
is InteractionState.Dragging -> {
|
|
605
729
|
val detent = detentCalculator.getDetentValueForIndex(detentInfo.index)
|
|
606
730
|
delegate?.viewControllerDidDragEnd(detentInfo.index, detentInfo.position, detent)
|
|
607
731
|
|
|
608
|
-
// Skip detent change if keyboard inset is still active —
|
|
609
|
-
//
|
|
610
|
-
if (
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
732
|
+
// Skip detent change if keyboard inset is still active — the keyboard floor
|
|
733
|
+
// landing below commits the detent instead.
|
|
734
|
+
if (keyboardInset == 0) {
|
|
735
|
+
val changed = detentInfo.index != knownDetentIndex
|
|
736
|
+
detentIndexBeforeKeyboard = -1
|
|
737
|
+
if (detentInfo.index != currentDetentIndex) {
|
|
738
|
+
currentDetentIndex = detentInfo.index
|
|
739
|
+
setupDimmedBackground()
|
|
740
|
+
}
|
|
741
|
+
if (changed) {
|
|
742
|
+
delegate?.viewControllerDidChangeDetent(detentInfo.index, detentInfo.position, detent)
|
|
743
|
+
this@TrueSheetViewController.sheetView?.updateGrabberAccessibilityValue(detentInfo.index, detents.size)
|
|
744
|
+
}
|
|
615
745
|
}
|
|
616
746
|
|
|
617
747
|
interactionState = InteractionState.Idle
|
|
@@ -629,14 +759,28 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
629
759
|
}
|
|
630
760
|
}
|
|
631
761
|
|
|
632
|
-
|
|
633
|
-
|
|
762
|
+
if (hasKeyboardFloor && wasDragging && newState != BottomSheetBehavior.STATE_EXPANDED) {
|
|
763
|
+
// Dragged down onto a keyboard-free stop (the floor or the keyboard detent) —
|
|
764
|
+
// dismiss the keyboard and commit that detent. keyboardWillHide reconfigures
|
|
765
|
+
// the detents (and the container) to it.
|
|
766
|
+
commitDetentAndDismissKeyboard(index, sheetView)
|
|
767
|
+
} else {
|
|
768
|
+
updateStateDimensions()
|
|
769
|
+
}
|
|
634
770
|
|
|
635
771
|
if (!isKeyboardTransitioning) {
|
|
636
772
|
updateDimAmount(animated = true)
|
|
637
773
|
}
|
|
638
774
|
|
|
639
775
|
TrueSheetStackManager.updateBackgroundAccessibility()
|
|
776
|
+
|
|
777
|
+
flushPendingSizeChange()
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
private fun flushPendingSizeChange() {
|
|
781
|
+
if (!hasPendingSizeChange) return
|
|
782
|
+
hasPendingSizeChange = false
|
|
783
|
+
setupSheetDetentsForSizeChange()
|
|
640
784
|
}
|
|
641
785
|
|
|
642
786
|
// =============================================================================
|
|
@@ -698,6 +842,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
698
842
|
val sheet = this.sheetView ?: return
|
|
699
843
|
if (isPresented) return
|
|
700
844
|
|
|
845
|
+
invalidateScreenMetrics()
|
|
701
846
|
shouldAnimatePresent = animated
|
|
702
847
|
currentDetentIndex = detentIndex
|
|
703
848
|
interactionState = InteractionState.Idle
|
|
@@ -711,6 +856,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
711
856
|
setupDimmedBackground()
|
|
712
857
|
setupKeyboardObserver()
|
|
713
858
|
|
|
859
|
+
displayedBackgroundIndex = detentIndex
|
|
860
|
+
setupBackground(animated = false)
|
|
714
861
|
sheet.setupBackground()
|
|
715
862
|
sheet.setupElevation()
|
|
716
863
|
sheet.setupGrabber()
|
|
@@ -735,13 +882,18 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
735
882
|
fun resize(detentIndex: Int) {
|
|
736
883
|
if (!isPresented) return
|
|
737
884
|
|
|
738
|
-
// Commit the new index so keyboardWillHide restores to it instead of the stale
|
|
885
|
+
// Commit the new index so keyboardWillHide restores to it instead of the stale
|
|
886
|
+
// one; the sheet keeps avoiding the keyboard meanwhile
|
|
739
887
|
if (detentIndexBeforeKeyboard >= 0) {
|
|
740
888
|
detentIndexBeforeKeyboard = detentIndex
|
|
889
|
+
if (hasKeyboardFloor) {
|
|
890
|
+
resizePromise?.invoke()
|
|
891
|
+
resizePromise = null
|
|
892
|
+
return
|
|
893
|
+
}
|
|
741
894
|
}
|
|
742
895
|
|
|
743
896
|
pendingDetentIndex = detentIndex
|
|
744
|
-
updateStateDimensions(deferShrink = true)
|
|
745
897
|
setupDimmedBackground()
|
|
746
898
|
setStateForDetentIndex(detentIndex)
|
|
747
899
|
resizePromise?.invoke()
|
|
@@ -866,11 +1018,15 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
866
1018
|
|
|
867
1019
|
val maxAvailableHeight = realScreenHeight - topInset
|
|
868
1020
|
|
|
869
|
-
|
|
1021
|
+
// With a keyboard floor only the last detent stays keyboard-shifted; the
|
|
1022
|
+
// collapsed and half stops are the keyboard-free first and second detents,
|
|
1023
|
+
// so a short drag down from the keyboard-expanded sheet rests at the second
|
|
1024
|
+
// and a long one at the first, like iOS
|
|
1025
|
+
val peekHeight = minOf(detentCalculator.getDetentHeight(detents[0], includeKeyboard = !hasKeyboardFloor), maxAvailableHeight)
|
|
870
1026
|
|
|
871
1027
|
val halfExpandedDetentHeight = when (detents.size) {
|
|
872
1028
|
1 -> peekHeight
|
|
873
|
-
else -> detentCalculator.getDetentHeight(detents[1])
|
|
1029
|
+
else -> detentCalculator.getDetentHeight(detents[1], includeKeyboard = !hasKeyboardFloor)
|
|
874
1030
|
}
|
|
875
1031
|
|
|
876
1032
|
val maxDetentHeight = minOf(detentCalculator.getDetentHeight(detents.last()), maxAvailableHeight)
|
|
@@ -880,8 +1036,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
880
1036
|
|
|
881
1037
|
val expandedOffset = realScreenHeight - maxDetentHeight
|
|
882
1038
|
|
|
883
|
-
// fitToContents works better with <= 2 detents when no expanded offset
|
|
884
|
-
|
|
1039
|
+
// fitToContents works better with <= 2 detents when no expanded offset, but
|
|
1040
|
+
// it drops the half stop the keyboard floor relies on
|
|
1041
|
+
val fitToContents = !hasKeyboardFloor && detents.size < 3 && expandedOffset == 0
|
|
885
1042
|
|
|
886
1043
|
configureDetents(
|
|
887
1044
|
behavior = behavior,
|
|
@@ -892,13 +1049,25 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
892
1049
|
animate = isPresented
|
|
893
1050
|
)
|
|
894
1051
|
|
|
895
|
-
updateStateDimensions()
|
|
896
|
-
|
|
897
1052
|
if (isPresented && applyState) {
|
|
898
1053
|
// Prefer the pending target while a resize animation is in flight so a
|
|
899
1054
|
// layout-driven reconfigure doesn't revert to the stale currentDetentIndex.
|
|
900
1055
|
val targetIndex = if (pendingDetentIndex >= 0) pendingDetentIndex else currentDetentIndex
|
|
1056
|
+
|
|
1057
|
+
// Track the settle like resize() — the container (and the footer pinned
|
|
1058
|
+
// to its bottom) follows the sheet's visible height instead of jumping
|
|
1059
|
+
// to the target and riding the animation
|
|
1060
|
+
pendingDetentIndex = targetIndex
|
|
1061
|
+
sheetView?.let { updateStateDimensionsForPosition(it.top) }
|
|
901
1062
|
setStateForDetentIndex(targetIndex)
|
|
1063
|
+
|
|
1064
|
+
// Already at the target — no slide frames will follow
|
|
1065
|
+
if (!isSettling) {
|
|
1066
|
+
pendingDetentIndex = -1
|
|
1067
|
+
updateStateDimensions()
|
|
1068
|
+
}
|
|
1069
|
+
} else {
|
|
1070
|
+
updateStateDimensions()
|
|
902
1071
|
}
|
|
903
1072
|
|
|
904
1073
|
interactionState = InteractionState.Idle
|
|
@@ -922,10 +1091,13 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
922
1091
|
}
|
|
923
1092
|
|
|
924
1093
|
fun setupSheetDetentsForSizeChange() {
|
|
925
|
-
//
|
|
926
|
-
//
|
|
927
|
-
|
|
928
|
-
|
|
1094
|
+
// Reconfiguring while the container tracks the sheet can interrupt its
|
|
1095
|
+
// motion. Apply content size changes once the drag or resize settles.
|
|
1096
|
+
if (interactionState is InteractionState.Dragging || isSettling) {
|
|
1097
|
+
hasPendingSizeChange = true
|
|
1098
|
+
return
|
|
1099
|
+
}
|
|
1100
|
+
hasPendingSizeChange = false
|
|
929
1101
|
|
|
930
1102
|
setupSheetDetents()
|
|
931
1103
|
positionFooter()
|
|
@@ -939,6 +1111,30 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
939
1111
|
// MARK: - Dimmed Background
|
|
940
1112
|
// =============================================================================
|
|
941
1113
|
|
|
1114
|
+
fun setupBackground(animated: Boolean = true) {
|
|
1115
|
+
val sheet = sheetView ?: return
|
|
1116
|
+
val color = detentBackgrounds.getOrNull(displayedBackgroundIndex)
|
|
1117
|
+
?: backgroundColor ?: sheet.getDefaultBackgroundColor()
|
|
1118
|
+
if (animated && color == targetBackgroundColor) return
|
|
1119
|
+
|
|
1120
|
+
backgroundAnimator?.cancel()
|
|
1121
|
+
targetBackgroundColor = color
|
|
1122
|
+
val startColor = sheetBackgroundColor ?: color
|
|
1123
|
+
if (!animated || !isPresented || startColor == color) {
|
|
1124
|
+
sheetBackgroundColor = color
|
|
1125
|
+
sheet.updateBackgroundColor(color)
|
|
1126
|
+
return
|
|
1127
|
+
}
|
|
1128
|
+
backgroundAnimator = ValueAnimator.ofArgb(startColor, color).apply {
|
|
1129
|
+
duration = 200L
|
|
1130
|
+
addUpdateListener {
|
|
1131
|
+
sheetBackgroundColor = it.animatedValue as Int
|
|
1132
|
+
sheet.updateBackgroundColor(it.animatedValue as Int)
|
|
1133
|
+
}
|
|
1134
|
+
start()
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
942
1138
|
fun setupDimmedBackground() {
|
|
943
1139
|
val coordinator = this.coordinatorLayout ?: run {
|
|
944
1140
|
RNLog.e(reactContext, "TrueSheet: coordinatorLayout is null in setupDimmedBackground")
|
|
@@ -978,10 +1174,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
978
1174
|
|
|
979
1175
|
fun updateDimAmount(sheetTop: Int? = null, animated: Boolean = false) {
|
|
980
1176
|
if (!dimmed) return
|
|
981
|
-
if (contentHeight == 0) return
|
|
982
1177
|
|
|
983
1178
|
// While keyboard is active or transitioning, use the target detent position for dim
|
|
984
|
-
val top = if (
|
|
1179
|
+
val top = if (isKeyboardActive) {
|
|
985
1180
|
detentCalculator.getSheetTopForDetentIndex(currentDetentIndex)
|
|
986
1181
|
} else {
|
|
987
1182
|
val keyboardOffset = if (isBeingDismissed) 0 else currentKeyboardInset
|
|
@@ -1006,13 +1201,17 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1006
1201
|
|
|
1007
1202
|
var footerKeyboardOffset: Int = 0
|
|
1008
1203
|
|
|
1009
|
-
|
|
1204
|
+
/**
|
|
1205
|
+
* false keeps an absolute footer pinned to the sheet's bottom edge behind
|
|
1206
|
+
* the keyboard instead of rising above it.
|
|
1207
|
+
*/
|
|
1208
|
+
override var footerAvoidsKeyboard: Boolean = true
|
|
1209
|
+
|
|
1210
|
+
fun positionFooter() {
|
|
1010
1211
|
if (!isPresented) return
|
|
1011
1212
|
val footerView = containerView?.footerView ?: return
|
|
1012
1213
|
val sheet = sheetView ?: return
|
|
1013
1214
|
|
|
1014
|
-
val keyboardShift = if (currentKeyboardInset > 0) maxOf(0, currentKeyboardInset + footerKeyboardOffset) else 0
|
|
1015
|
-
|
|
1016
1215
|
// A relative footer is laid out by Yoga (pinned to the container's bottom
|
|
1017
1216
|
// edge) and stays in the layout flow behind the keyboard — only an
|
|
1018
1217
|
// absolute footer floats above it
|
|
@@ -1021,24 +1220,31 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1021
1220
|
return
|
|
1022
1221
|
}
|
|
1023
1222
|
|
|
1223
|
+
// The footer's absorbed safe-area inset rides below the keyboard's top
|
|
1224
|
+
// edge — its content stays flush with the keyboard without dropping the
|
|
1225
|
+
// inset padding from the layout, which would jump at the transition edges
|
|
1226
|
+
val keyboardShift = if (currentKeyboardInset > 0 && footerAvoidsKeyboard) {
|
|
1227
|
+
maxOf(0, currentKeyboardInset + footerKeyboardOffset - footerView.bottomInset)
|
|
1228
|
+
} else {
|
|
1229
|
+
0
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1024
1232
|
val footerHeight = footerView.height
|
|
1025
1233
|
val sheetHeight = sheet.height
|
|
1026
|
-
val sheetTop = sheet.top
|
|
1027
1234
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
//
|
|
1031
|
-
//
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
// when the keyboard detents reset on release.
|
|
1035
|
-
if (slideOffset != null && slideOffset < 0 && keyboardShift == 0) {
|
|
1036
|
-
footerY -= (footerHeight * slideOffset)
|
|
1037
|
-
}
|
|
1235
|
+
// Pinned to the screen bottom (above the keyboard) while the sheet sits at
|
|
1236
|
+
// or above its lowest stop; dragged past it the footer rides down with the
|
|
1237
|
+
// sheet instead, like iOS. A resize settle always pins — the sheet may be
|
|
1238
|
+
// animating up from below a stop that just grew
|
|
1239
|
+
val anchorTop = if (pendingDetentIndex >= 0) sheet.top else minOf(sheet.top, detentCalculator.getLowestSheetTop())
|
|
1240
|
+
val footerY = (sheetHeight - anchorTop - footerHeight - keyboardShift).toFloat()
|
|
1038
1241
|
|
|
1039
1242
|
// Clamp to prevent footer going above safe area
|
|
1040
1243
|
val maxAllowedY = (sheetHeight - topInset - footerHeight).toFloat()
|
|
1041
1244
|
footerView.y = minOf(footerY, maxAllowedY)
|
|
1245
|
+
if (!footerAvoidsKeyboard) {
|
|
1246
|
+
containerView?.contentView?.updateContentInset()
|
|
1247
|
+
}
|
|
1042
1248
|
}
|
|
1043
1249
|
|
|
1044
1250
|
// =============================================================================
|
|
@@ -1064,7 +1270,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1064
1270
|
if (!viewToFocus.isAttachedToWindow) return
|
|
1065
1271
|
if (viewToFocus.requestFocus()) {
|
|
1066
1272
|
viewToFocus.postDelayed({
|
|
1067
|
-
KeyboardUtils.show(viewToFocus)
|
|
1273
|
+
if (viewToFocus.isFocused) KeyboardUtils.show(viewToFocus)
|
|
1068
1274
|
}, 100)
|
|
1069
1275
|
}
|
|
1070
1276
|
}
|
|
@@ -1079,9 +1285,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1079
1285
|
delegate = object : TrueSheetKeyboardObserverDelegate {
|
|
1080
1286
|
override fun keyboardWillShow(height: Int) {
|
|
1081
1287
|
if (!shouldHandleKeyboard()) return
|
|
1082
|
-
// An absolute footer rises above the keyboard, so it drops the bottom
|
|
1083
|
-
// inset padding — before detents are configured against its height
|
|
1084
|
-
if (absoluteFooter) containerView?.footerView?.keyboardVisible = true
|
|
1085
1288
|
// If a resize is in flight, restore to its target — not the stale current
|
|
1086
1289
|
detentIndexBeforeKeyboard = if (pendingDetentIndex >= 0) pendingDetentIndex else currentDetentIndex
|
|
1087
1290
|
pendingDetentIndex = -1
|
|
@@ -1094,17 +1297,22 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1094
1297
|
|
|
1095
1298
|
override fun keyboardWillHide() {
|
|
1096
1299
|
if (!shouldHandleKeyboard(checkFocus = false)) return
|
|
1097
|
-
|
|
1300
|
+
// Mid-drag (the drag itself dismissed the keyboard) the release decides
|
|
1301
|
+
// the detent and a reconfigure would kill the gesture — the floor stops
|
|
1302
|
+
// stay put until the settle flushes the pending reconfigure
|
|
1303
|
+
if (interactionState is InteractionState.Dragging) {
|
|
1304
|
+
hasPendingSizeChange = true
|
|
1305
|
+
return
|
|
1306
|
+
}
|
|
1098
1307
|
val restoring = !isBeingDismissed && detentIndexBeforeKeyboard >= 0
|
|
1099
1308
|
|
|
1100
1309
|
// Skip reconfigure during interactive keyboard dismiss (e.g. keyboardDismissMode="on-drag")
|
|
1101
1310
|
// to prevent the sheet from jumping. keyboardDidHide will reconfigure after.
|
|
1102
1311
|
if (restoring || isKeyboardDismissProgrammatic) {
|
|
1103
|
-
setupSheetDetents()
|
|
1104
1312
|
if (restoring) {
|
|
1105
1313
|
currentDetentIndex = detentIndexBeforeKeyboard
|
|
1106
|
-
setStateForDetentIndex(currentDetentIndex)
|
|
1107
1314
|
}
|
|
1315
|
+
setupSheetDetents()
|
|
1108
1316
|
}
|
|
1109
1317
|
|
|
1110
1318
|
// Same-frame reposition — the reconfigure above shrinks the container
|
|
@@ -1119,9 +1327,13 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1119
1327
|
|
|
1120
1328
|
override fun keyboardDidHide() {
|
|
1121
1329
|
if (!shouldHandleKeyboard(checkFocus = false)) return
|
|
1122
|
-
containerView?.footerView?.keyboardVisible = false
|
|
1123
|
-
detentIndexBeforeKeyboard = -1
|
|
1124
1330
|
isKeyboardDismissProgrammatic = false
|
|
1331
|
+
// Keep detentIndexBeforeKeyboard for the settle's detent-change check
|
|
1332
|
+
if (interactionState is InteractionState.Dragging) {
|
|
1333
|
+
hasPendingSizeChange = true
|
|
1334
|
+
return
|
|
1335
|
+
}
|
|
1336
|
+
detentIndexBeforeKeyboard = -1
|
|
1125
1337
|
setupSheetDetents(applyState = false)
|
|
1126
1338
|
positionFooter()
|
|
1127
1339
|
updateDimAmount(
|
|
@@ -1141,7 +1353,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1141
1353
|
// Handle case where keyboard is already visible and focus moves into the sheet
|
|
1142
1354
|
if (!shouldHandleKeyboard()) return
|
|
1143
1355
|
if (detentIndexBeforeKeyboard < 0 && (keyboardObserver?.currentHeight ?: 0) > 0) {
|
|
1144
|
-
if (absoluteFooter) containerView?.footerView?.keyboardVisible = true
|
|
1145
1356
|
detentIndexBeforeKeyboard = currentDetentIndex
|
|
1146
1357
|
currentDetentIndex = detents.size - 1
|
|
1147
1358
|
setupSheetDetents()
|
|
@@ -1169,8 +1380,28 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1169
1380
|
detentIndexBeforeKeyboard = -1
|
|
1170
1381
|
}
|
|
1171
1382
|
|
|
1383
|
+
private fun handleSettling(sheetView: View) {
|
|
1384
|
+
// A keyboard-floor drag was released — the first settle frame reveals the target
|
|
1385
|
+
keyboardSettleStartTop = if (hasKeyboardFloor && interactionState is InteractionState.Dragging) sheetView.top else -1
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
/**
|
|
1389
|
+
* Dismisses the keyboard as soon as a released drag is known to land on a
|
|
1390
|
+
* keyboard-free stop, rather than waiting for the landing. BottomSheetBehavior
|
|
1391
|
+
* keeps its target private, so read it off the first settle frame: heading
|
|
1392
|
+
* down means the half stop or the floor; heading up from below the half stop
|
|
1393
|
+
* means the half stop; only heading up from above it means the expanded stop.
|
|
1394
|
+
*/
|
|
1395
|
+
private fun dismissKeyboardForSettleTarget(sheetView: View) {
|
|
1396
|
+
if (keyboardSettleStartTop < 0) return
|
|
1397
|
+
val startTop = keyboardSettleStartTop
|
|
1398
|
+
keyboardSettleStartTop = -1
|
|
1399
|
+
|
|
1400
|
+
val headingToKeyboardFreeStop = sheetView.top > startTop || startTop > detentCalculator.getKeyboardHalfStopTop()
|
|
1401
|
+
if (headingToKeyboardFreeStop && shouldHandleKeyboard()) dismissKeyboard()
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1172
1404
|
private fun handleDragBegin(sheetView: View) {
|
|
1173
|
-
detentIndexBeforeKeyboard = -1
|
|
1174
1405
|
pendingDetentIndex = -1
|
|
1175
1406
|
|
|
1176
1407
|
val position = getPositionDpForView(sheetView)
|
|
@@ -1179,43 +1410,25 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1179
1410
|
interactionState = InteractionState.Dragging(startTop = sheetView.top)
|
|
1180
1411
|
}
|
|
1181
1412
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1413
|
+
// Commits a non-keyboard detent and hides the keyboard; keyboardWillHide then
|
|
1414
|
+
// reconfigures the detents so the sheet settles at the committed index.
|
|
1415
|
+
private fun commitDetentAndDismissKeyboard(index: Int, sheetView: View) {
|
|
1416
|
+
val changed = index != knownDetentIndex
|
|
1417
|
+
detentIndexBeforeKeyboard = -1
|
|
1418
|
+
currentDetentIndex = index
|
|
1419
|
+
dismissKeyboard()
|
|
1420
|
+
setupDimmedBackground()
|
|
1185
1421
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
for (i in detents.indices) {
|
|
1193
|
-
val nonKeyboardHeight = minOf(detentCalculator.getDetentHeight(detents[i], includeKeyboard = false), maxAvailableHeight)
|
|
1194
|
-
val nonKeyboardTop = realScreenHeight - nonKeyboardHeight
|
|
1195
|
-
val midpoint = keyboardTop + (nonKeyboardTop - keyboardTop) / 2
|
|
1196
|
-
|
|
1197
|
-
if (sheetView.top >= midpoint) {
|
|
1198
|
-
// Target position matches the keyboard-expanded position — keep keyboard open
|
|
1199
|
-
if (nonKeyboardTop == keyboardTop) break
|
|
1200
|
-
|
|
1201
|
-
detentIndexBeforeKeyboard = -1
|
|
1202
|
-
currentDetentIndex = i
|
|
1203
|
-
dismissKeyboard()
|
|
1204
|
-
setupDimmedBackground()
|
|
1205
|
-
|
|
1206
|
-
val position = getPositionDpForView(sheetView)
|
|
1207
|
-
val detent = detentCalculator.getDetentValueForIndex(i)
|
|
1208
|
-
delegate?.viewControllerDidChangeDetent(i, position, detent)
|
|
1209
|
-
break
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1422
|
+
if (!changed) return
|
|
1423
|
+
val position = getPositionDpForView(sheetView)
|
|
1424
|
+
val detent = detentCalculator.getDetentValueForIndex(index)
|
|
1425
|
+
delegate?.viewControllerDidChangeDetent(index, position, detent)
|
|
1426
|
+
this.sheetView?.updateGrabberAccessibilityValue(index, detents.size)
|
|
1212
1427
|
}
|
|
1213
1428
|
|
|
1214
1429
|
private fun handleDragChange(sheetView: View) {
|
|
1215
1430
|
if (interactionState !is InteractionState.Dragging) return
|
|
1216
1431
|
|
|
1217
|
-
updateStateDimensionsForDrag(sheetView.top)
|
|
1218
|
-
|
|
1219
1432
|
val position = getPositionDpForView(sheetView)
|
|
1220
1433
|
val detent = detentCalculator.getDetentValueForIndex(currentDetentIndex)
|
|
1221
1434
|
delegate?.viewControllerDidDragChange(currentDetentIndex, position, detent)
|
|
@@ -1250,14 +1463,20 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1250
1463
|
}
|
|
1251
1464
|
|
|
1252
1465
|
private fun emitChangePositionDelegate(currentTop: Int, realtime: Boolean = true) {
|
|
1466
|
+
if (isPresented && !isPresentAnimating && !isBeingDismissed) {
|
|
1467
|
+
val index = detentCalculator.getBackgroundIndex(currentTop, displayedBackgroundIndex)
|
|
1468
|
+
if (index != displayedBackgroundIndex) {
|
|
1469
|
+
displayedBackgroundIndex = index
|
|
1470
|
+
setupBackground()
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1253
1473
|
// Dedupe emissions for same position
|
|
1254
1474
|
if (currentTop == lastEmittedPositionPx) return
|
|
1255
1475
|
|
|
1256
1476
|
lastEmittedPositionPx = currentTop
|
|
1257
1477
|
val visibleHeight = realScreenHeight - currentTop
|
|
1258
1478
|
val position = detentCalculator.getPositionDp(visibleHeight)
|
|
1259
|
-
val interpolatedIndex = detentCalculator.
|
|
1260
|
-
val detent = detentCalculator.getInterpolatedDetentForPosition(currentTop)
|
|
1479
|
+
val (interpolatedIndex, detent) = detentCalculator.getInterpolatedPositionInfo(currentTop)
|
|
1261
1480
|
delegate?.viewControllerDidChangePosition(interpolatedIndex, position, detent, realtime)
|
|
1262
1481
|
}
|
|
1263
1482
|
|
|
@@ -1265,9 +1484,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1265
1484
|
* Updates position emission, footer, and dim amount together.
|
|
1266
1485
|
* This pattern is commonly used during animations and state changes.
|
|
1267
1486
|
*/
|
|
1268
|
-
private fun updateSheetVisuals(effectiveTop: Int
|
|
1487
|
+
private fun updateSheetVisuals(effectiveTop: Int) {
|
|
1269
1488
|
emitChangePositionDelegate(effectiveTop)
|
|
1270
|
-
positionFooter(
|
|
1489
|
+
positionFooter()
|
|
1271
1490
|
updateDimAmount(effectiveTop)
|
|
1272
1491
|
}
|
|
1273
1492
|
|
|
@@ -1287,13 +1506,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1287
1506
|
/**
|
|
1288
1507
|
* Updates the Fabric state with the current/target detent size so Yoga sizes
|
|
1289
1508
|
* the container to the sheet's visible height.
|
|
1290
|
-
*
|
|
1291
|
-
* Unlike iOS, resize animations run over multiple frames (ViewDragHelper), so
|
|
1292
|
-
* shrinking is deferred to settle ([deferShrink]) when a resize animation is
|
|
1293
|
-
* about to run — growing applies immediately so content is laid out before
|
|
1294
|
-
* the sheet reveals it.
|
|
1295
1509
|
*/
|
|
1296
|
-
private fun updateStateDimensions(
|
|
1510
|
+
private fun updateStateDimensions() {
|
|
1297
1511
|
if (detents.isEmpty()) return
|
|
1298
1512
|
|
|
1299
1513
|
val targetIndex = (if (pendingDetentIndex >= 0) pendingDetentIndex else currentDetentIndex)
|
|
@@ -1302,21 +1516,21 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1302
1516
|
val newHeight = minOf(detentCalculator.getDetentHeight(detents[targetIndex]), maxAvailableHeight)
|
|
1303
1517
|
val newWidth = getStateWidth()
|
|
1304
1518
|
|
|
1305
|
-
if (deferShrink && newWidth == lastStateWidth && newHeight < lastStateHeight) return
|
|
1306
|
-
|
|
1307
1519
|
setStateDimensions(newWidth, newHeight)
|
|
1308
1520
|
}
|
|
1309
1521
|
|
|
1310
1522
|
/**
|
|
1311
|
-
* Tracks the sheet's visible height during
|
|
1312
|
-
* animation) so the container resizes with the finger, like iOS. Clamped to the
|
|
1523
|
+
* Tracks the sheet's visible height during drags and resize animations. Clamped to the
|
|
1313
1524
|
* smallest detent — dragging below it slides the sheet out without resizing.
|
|
1314
1525
|
*/
|
|
1315
|
-
private fun
|
|
1526
|
+
private fun updateStateDimensionsForPosition(sheetTop: Int) {
|
|
1316
1527
|
if (detents.isEmpty()) return
|
|
1317
1528
|
|
|
1318
1529
|
val maxAvailableHeight = realScreenHeight - topInset
|
|
1319
|
-
val minHeight = minOf(
|
|
1530
|
+
val minHeight = minOf(
|
|
1531
|
+
detentCalculator.getDetentHeight(detents.first(), includeKeyboard = !hasKeyboardFloor),
|
|
1532
|
+
maxAvailableHeight
|
|
1533
|
+
)
|
|
1320
1534
|
val newHeight = detentCalculator.getVisibleSheetHeight(sheetTop).coerceIn(minHeight, maxAvailableHeight)
|
|
1321
1535
|
|
|
1322
1536
|
setStateDimensions(getStateWidth(), newHeight)
|
|
@@ -1336,9 +1550,27 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1336
1550
|
delegate?.viewControllerDidChangeSize(width, height)
|
|
1337
1551
|
}
|
|
1338
1552
|
|
|
1339
|
-
|
|
1553
|
+
/**
|
|
1554
|
+
* Realtime (non-animated) counterpart of TrueSheetStackManager.updateParentTranslation,
|
|
1555
|
+
* driven by onSlide so the parent tracks this sheet's actual position.
|
|
1556
|
+
*/
|
|
1557
|
+
private fun syncParentTranslation(sheetTop: Int) {
|
|
1558
|
+
val parent = parentSheetView ?: return
|
|
1559
|
+
val minSheetTop = detentCalculator.getSheetTopForDetentIndex(0)
|
|
1560
|
+
parent.syncTranslationForChild(sheetTop, minSheetTop)
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
fun translateSheet(translationY: Int, animated: Boolean = true, onEnd: (() -> Unit)? = null) {
|
|
1340
1564
|
val sheet = sheetView ?: return
|
|
1341
1565
|
|
|
1566
|
+
if (!animated) {
|
|
1567
|
+
sheet.animate().cancel()
|
|
1568
|
+
sheet.translationY = translationY.toFloat()
|
|
1569
|
+
emitChangePositionDelegate(sheet.top + translationY)
|
|
1570
|
+
onEnd?.invoke()
|
|
1571
|
+
return
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1342
1574
|
sheet.animate()
|
|
1343
1575
|
.translationY(translationY.toFloat())
|
|
1344
1576
|
.setDuration(TRANSLATE_ANIMATION_DURATION)
|
|
@@ -1385,41 +1617,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1385
1617
|
// MARK: - RootView Touch Handling
|
|
1386
1618
|
// =============================================================================
|
|
1387
1619
|
|
|
1388
|
-
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
|
1389
|
-
val footer = containerView?.footerView
|
|
1390
|
-
val action = event.actionMasked
|
|
1391
|
-
|
|
1392
|
-
if (footer != null && footer.isShown) {
|
|
1393
|
-
if (action == MotionEvent.ACTION_DOWN) {
|
|
1394
|
-
val loc = ScreenUtils.getScreenLocation(footer)
|
|
1395
|
-
val x = event.rawX.toInt()
|
|
1396
|
-
val y = event.rawY.toInt()
|
|
1397
|
-
footerOwnsTouchStream = x >= loc[0] &&
|
|
1398
|
-
x <= loc[0] + footer.width &&
|
|
1399
|
-
y >= loc[1] &&
|
|
1400
|
-
y <= loc[1] + footer.height
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
if (footerOwnsTouchStream) {
|
|
1404
|
-
val loc = ScreenUtils.getScreenLocation(footer)
|
|
1405
|
-
val localEvent = MotionEvent.obtain(event)
|
|
1406
|
-
localEvent.setLocation(event.rawX - loc[0], event.rawY - loc[1])
|
|
1407
|
-
val handled = footer.dispatchTouchEvent(localEvent)
|
|
1408
|
-
localEvent.recycle()
|
|
1409
|
-
|
|
1410
|
-
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
|
1411
|
-
footerOwnsTouchStream = false
|
|
1412
|
-
}
|
|
1413
|
-
if (handled) return true
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
|
1418
|
-
footerOwnsTouchStream = false
|
|
1419
|
-
}
|
|
1420
|
-
return super.dispatchTouchEvent(event)
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
1620
|
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
|
|
1424
1621
|
eventDispatcher?.let {
|
|
1425
1622
|
if (touchDeduper.shouldDispatch(event)) {
|