@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.
Files changed (203) hide show
  1. package/README.md +25 -4
  2. package/android/build.gradle +22 -1
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +50 -16
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +302 -184
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +21 -110
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +116 -0
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +18 -0
  11. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +119 -50
  12. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +360 -163
  13. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +54 -12
  14. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +22 -9
  15. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +84 -5
  16. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +104 -39
  17. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +3 -0
  18. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +137 -0
  19. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
  20. package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +15 -2
  21. package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
  22. package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
  23. package/android/src/main/jni/TrueSheetSpec.h +1 -0
  24. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +1 -9
  25. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +110 -30
  26. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +30 -7
  27. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +2 -2
  28. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +9 -8
  29. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
  30. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +9 -3
  31. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +3 -6
  32. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
  33. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +4 -6
  34. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
  35. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
  36. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +43 -0
  37. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
  38. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
  39. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +33 -0
  40. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
  41. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +7 -40
  42. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
  43. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
  44. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
  45. package/ios/TrueSheetContainerView.h +27 -7
  46. package/ios/TrueSheetContainerView.mm +31 -11
  47. package/ios/TrueSheetContentView.h +43 -15
  48. package/ios/TrueSheetContentView.mm +211 -189
  49. package/ios/TrueSheetFooterView.h +6 -0
  50. package/ios/TrueSheetFooterView.mm +93 -17
  51. package/ios/TrueSheetModule.h +2 -7
  52. package/ios/TrueSheetModule.mm +7 -16
  53. package/ios/TrueSheetOverlayView.h +32 -0
  54. package/ios/TrueSheetOverlayView.mm +161 -0
  55. package/ios/TrueSheetView.mm +212 -55
  56. package/ios/TrueSheetViewController.h +31 -3
  57. package/ios/TrueSheetViewController.mm +393 -94
  58. package/ios/core/TrueSheetBlurView.h +0 -2
  59. package/ios/core/TrueSheetBlurView.mm +3 -36
  60. package/ios/core/TrueSheetDetentCalculator.h +7 -1
  61. package/ios/core/TrueSheetDetentCalculator.mm +27 -4
  62. package/ios/core/TrueSheetGrabberView.mm +13 -3
  63. package/ios/core/TrueSheetKeyboardObserver.mm +2 -0
  64. package/ios/core/TrueSheetOverlayContainerView.h +31 -0
  65. package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
  66. package/ios/events/TrueSheetLifecycleEvents.h +2 -0
  67. package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
  68. package/ios/tvos/TrueSheetStubs.mm +12 -0
  69. package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
  70. package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
  71. package/lib/module/TrueSheet.js +173 -39
  72. package/lib/module/TrueSheet.js.map +1 -1
  73. package/lib/module/TrueSheet.web.js +218 -243
  74. package/lib/module/TrueSheet.web.js.map +1 -1
  75. package/lib/module/TrueSheetBackground.js +15 -0
  76. package/lib/module/TrueSheetBackground.js.map +1 -0
  77. package/lib/module/TrueSheetOverlay.js +30 -0
  78. package/lib/module/TrueSheetOverlay.js.map +1 -0
  79. package/lib/module/TrueSheetOverlay.web.js +35 -0
  80. package/lib/module/TrueSheetOverlay.web.js.map +1 -0
  81. package/lib/module/TrueSheetPeek.web.js +20 -15
  82. package/lib/module/TrueSheetPeek.web.js.map +1 -1
  83. package/lib/module/detentBackgrounds.js +21 -0
  84. package/lib/module/detentBackgrounds.js.map +1 -0
  85. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  86. package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  87. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +14 -10
  88. package/lib/module/index.js +1 -0
  89. package/lib/module/index.js.map +1 -1
  90. package/lib/module/mocks/index.js +18 -2
  91. package/lib/module/mocks/index.js.map +1 -1
  92. package/lib/module/mocks/navigationExpoRouter.js.map +1 -1
  93. package/lib/module/navigation/base-types.js +2 -0
  94. package/lib/module/navigation/base-types.js.map +1 -0
  95. package/lib/module/navigation/createTrueSheetRouter.js +0 -9
  96. package/lib/module/navigation/createTrueSheetRouter.js.map +1 -1
  97. package/lib/module/navigation/expo-router/base-types.js +2 -0
  98. package/lib/module/navigation/expo-router/base-types.js.map +1 -0
  99. package/lib/module/navigation/expo-router/index.js +10 -7
  100. package/lib/module/navigation/expo-router/index.js.map +1 -1
  101. package/lib/module/navigation/expo-router/types.js +4 -0
  102. package/lib/module/navigation/expo-router/types.js.map +1 -0
  103. package/lib/module/navigation/index.js.map +1 -1
  104. package/lib/module/navigation/screen/useSheetScreenState.js +1 -0
  105. package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
  106. package/lib/module/web/constants.js +1 -1
  107. package/lib/module/web/constants.js.map +1 -1
  108. package/lib/module/web/layout.js +168 -0
  109. package/lib/module/web/layout.js.map +1 -0
  110. package/lib/module/web/vaul/constants.js +4 -0
  111. package/lib/module/web/vaul/constants.js.map +1 -1
  112. package/lib/module/web/vaul/context.js.map +1 -1
  113. package/lib/module/web/vaul/helpers.js +1 -0
  114. package/lib/module/web/vaul/helpers.js.map +1 -1
  115. package/lib/module/web/vaul/index.js +44 -6
  116. package/lib/module/web/vaul/index.js.map +1 -1
  117. package/lib/module/web/vaul/use-snap-points.js +6 -8
  118. package/lib/module/web/vaul/use-snap-points.js.map +1 -1
  119. package/lib/typescript/src/TrueSheet.d.ts +6 -0
  120. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  121. package/lib/typescript/src/TrueSheet.types.d.ts +135 -34
  122. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  123. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  124. package/lib/typescript/src/TrueSheetBackground.d.ts +3 -0
  125. package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
  126. package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
  127. package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
  128. package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
  129. package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
  130. package/lib/typescript/src/TrueSheetPeek.web.d.ts +8 -4
  131. package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
  132. package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
  133. package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
  134. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
  135. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
  136. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +11 -7
  137. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  138. package/lib/typescript/src/index.d.ts +1 -0
  139. package/lib/typescript/src/index.d.ts.map +1 -1
  140. package/lib/typescript/src/mocks/index.d.ts +4 -0
  141. package/lib/typescript/src/mocks/index.d.ts.map +1 -1
  142. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +3 -3
  143. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -1
  144. package/lib/typescript/src/navigation/base-types.d.ts +9 -0
  145. package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
  146. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +2 -13
  147. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -1
  148. package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
  149. package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
  150. package/lib/typescript/src/navigation/expo-router/index.d.ts +8 -6
  151. package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -1
  152. package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
  153. package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
  154. package/lib/typescript/src/navigation/index.d.ts +3 -4
  155. package/lib/typescript/src/navigation/index.d.ts.map +1 -1
  156. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +2 -1
  157. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
  158. package/lib/typescript/src/navigation/types.d.ts +41 -5
  159. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  160. package/lib/typescript/src/web/constants.d.ts +1 -1
  161. package/lib/typescript/src/web/constants.d.ts.map +1 -1
  162. package/lib/typescript/src/web/layout.d.ts +19 -0
  163. package/lib/typescript/src/web/layout.d.ts.map +1 -0
  164. package/lib/typescript/src/web/vaul/constants.d.ts +1 -0
  165. package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
  166. package/lib/typescript/src/web/vaul/context.d.ts +1 -0
  167. package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
  168. package/lib/typescript/src/web/vaul/helpers.d.ts +1 -0
  169. package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
  170. package/lib/typescript/src/web/vaul/index.d.ts +7 -1
  171. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
  172. package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
  173. package/package.json +17 -8
  174. package/react-native.config.js +1 -0
  175. package/src/TrueSheet.tsx +206 -48
  176. package/src/TrueSheet.types.ts +142 -35
  177. package/src/TrueSheet.web.tsx +270 -290
  178. package/src/TrueSheetBackground.tsx +17 -0
  179. package/src/TrueSheetOverlay.tsx +24 -0
  180. package/src/TrueSheetOverlay.web.tsx +33 -0
  181. package/src/TrueSheetPeek.web.tsx +25 -23
  182. package/src/detentBackgrounds.ts +24 -0
  183. package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  184. package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  185. package/src/fabric/TrueSheetViewNativeComponent.ts +14 -10
  186. package/src/index.ts +1 -0
  187. package/src/mocks/index.ts +17 -2
  188. package/src/mocks/navigationExpoRouter.ts +6 -5
  189. package/src/navigation/base-types.ts +20 -0
  190. package/src/navigation/createTrueSheetRouter.ts +5 -33
  191. package/src/navigation/expo-router/base-types.ts +18 -0
  192. package/src/navigation/expo-router/index.ts +28 -30
  193. package/src/navigation/expo-router/types.ts +116 -0
  194. package/src/navigation/index.ts +6 -7
  195. package/src/navigation/screen/useSheetScreenState.ts +2 -0
  196. package/src/navigation/types.ts +75 -22
  197. package/src/web/constants.ts +1 -1
  198. package/src/web/layout.ts +196 -0
  199. package/src/web/vaul/constants.ts +4 -0
  200. package/src/web/vaul/context.ts +1 -0
  201. package/src/web/vaul/helpers.ts +5 -0
  202. package/src/web/vaul/index.tsx +67 -5
  203. package/src/web/vaul/use-snap-points.ts +6 -14
@@ -1,10 +1,13 @@
1
1
  package com.lodev09.truesheet
2
2
 
3
3
  import android.view.WindowManager
4
+ import com.facebook.react.bridge.ColorPropConverter
4
5
  import com.facebook.react.bridge.ReadableArray
5
6
  import com.facebook.react.bridge.ReadableMap
7
+ import com.facebook.react.bridge.ReadableType
6
8
  import com.facebook.react.module.annotations.ReactModule
7
9
  import com.facebook.react.uimanager.PixelUtil.dpToPx
10
+ import com.facebook.react.uimanager.PointerEvents
8
11
  import com.facebook.react.uimanager.ReactStylesDiffMap
9
12
  import com.facebook.react.uimanager.StateWrapper
10
13
  import com.facebook.react.uimanager.ThemedReactContext
@@ -55,6 +58,20 @@ class TrueSheetViewManager :
55
58
 
56
59
  override fun getDelegate(): ViewManagerDelegate<TrueSheetView> = delegate
57
60
 
61
+ // The codegen delegate drops props outside the spec (pointerEvents comes from the
62
+ // host's style), so apply it here after the delegate pass.
63
+ override fun updateProperties(viewToUpdate: TrueSheetView, props: ReactStylesDiffMap) {
64
+ super.updateProperties(viewToUpdate, props)
65
+ if (props.hasKey("pointerEvents")) {
66
+ setPointerEvents(viewToUpdate, props.getString("pointerEvents"))
67
+ }
68
+ }
69
+
70
+ @ReactProp(name = "pointerEvents")
71
+ fun setPointerEvents(view: TrueSheetView, pointerEventsStr: String?) {
72
+ view.pointerEvents = PointerEvents.parsePointerEvents(pointerEventsStr)
73
+ }
74
+
58
75
  /**
59
76
  * Export custom direct event types for Fabric
60
77
  * Uses Kotlin native collections with decoupled event classes
@@ -66,6 +83,7 @@ class TrueSheetViewManager :
66
83
  DidPresentEvent.EVENT_NAME to hashMapOf("registrationName" to DidPresentEvent.REGISTRATION_NAME),
67
84
  WillDismissEvent.EVENT_NAME to hashMapOf("registrationName" to WillDismissEvent.REGISTRATION_NAME),
68
85
  DidDismissEvent.EVENT_NAME to hashMapOf("registrationName" to DidDismissEvent.REGISTRATION_NAME),
86
+ DismissAttemptEvent.EVENT_NAME to hashMapOf("registrationName" to DismissAttemptEvent.REGISTRATION_NAME),
69
87
  DetentChangeEvent.EVENT_NAME to hashMapOf("registrationName" to DetentChangeEvent.REGISTRATION_NAME),
70
88
  DragBeginEvent.EVENT_NAME to hashMapOf("registrationName" to DragBeginEvent.REGISTRATION_NAME),
71
89
  DragChangeEvent.EVENT_NAME to hashMapOf("registrationName" to DragChangeEvent.REGISTRATION_NAME),
@@ -98,6 +116,21 @@ class TrueSheetViewManager :
98
116
  view.setDetents(detents)
99
117
  }
100
118
 
119
+ @ReactProp(name = "detentBackgrounds")
120
+ override fun setDetentBackgrounds(view: TrueSheetView, value: ReadableArray?) {
121
+ view.setDetentBackgrounds(
122
+ (0 until (value?.size() ?: 0)).map { index ->
123
+ val entry = value?.getMap(index)
124
+ if (entry != null && entry.hasKey("color") && !entry.isNull("color")) {
125
+ val color = if (entry.getType("color") == ReadableType.Map) entry.getMap("color") else entry.getDouble("color")
126
+ ColorPropConverter.getColor(color, view.context)
127
+ } else {
128
+ null
129
+ }
130
+ }
131
+ )
132
+ }
133
+
101
134
  @ReactProp(name = "backgroundColor", customType = "Color")
102
135
  override fun setBackgroundColor(view: TrueSheetView, color: Int?) {
103
136
  view.setSheetBackgroundColor(color)
@@ -179,6 +212,7 @@ class TrueSheetViewManager :
179
212
  @ReactProp(name = "initialDetentIndex", defaultInt = -1)
180
213
  override fun setInitialDetentIndex(view: TrueSheetView, index: Int) {
181
214
  view.initialDetentIndex = index
215
+ view.presentInitialIfNeeded()
182
216
  }
183
217
 
184
218
  @ReactProp(name = "initialDetentAnimated", defaultBoolean = true)
@@ -196,14 +230,14 @@ class TrueSheetViewManager :
196
230
  view.setMaxContentWidth(if (width > 0) width.dpToPx().toInt() else null)
197
231
  }
198
232
 
199
- @ReactProp(name = "anchor")
200
- override fun setAnchor(view: TrueSheetView, anchor: String?) {
201
- view.setAnchor(anchor)
233
+ @ReactProp(name = "placement")
234
+ override fun setPlacement(view: TrueSheetView, placement: String?) {
235
+ view.setPlacement(placement)
202
236
  }
203
237
 
204
- @ReactProp(name = "anchorOffset", defaultDouble = 0.0)
205
- override fun setAnchorOffset(view: TrueSheetView, offset: Double) {
206
- view.setAnchorOffset(if (offset > 0) offset.dpToPx().toInt() else 0)
238
+ @ReactProp(name = "placementOffset", defaultDouble = 0.0)
239
+ override fun setPlacementOffset(view: TrueSheetView, offset: Double) {
240
+ view.setPlacementOffset(if (offset > 0) offset.dpToPx().toInt() else 0)
207
241
  }
208
242
 
209
243
  @ReactProp(name = "backgroundBlur")
@@ -211,11 +245,6 @@ class TrueSheetViewManager :
211
245
  // iOS-specific prop - no-op on Android
212
246
  }
213
247
 
214
- @ReactProp(name = "blurOptions")
215
- override fun setBlurOptions(view: TrueSheetView, options: ReadableMap?) {
216
- // iOS-specific prop - no-op on Android
217
- }
218
-
219
248
  @ReactProp(name = "insetAdjustment")
220
249
  override fun setInsetAdjustment(view: TrueSheetView, insetAdjustment: String?) {
221
250
  view.setInsetAdjustment(insetAdjustment ?: "automatic")
@@ -231,6 +260,11 @@ class TrueSheetViewManager :
231
260
  view.setSheetElevation(elevation.toFloat())
232
261
  }
233
262
 
263
+ @ReactProp(name = "scrollableHandle", defaultInt = -1)
264
+ override fun setScrollableHandle(view: TrueSheetView, handle: Int) {
265
+ view.setScrollableHandle(handle)
266
+ }
267
+
234
268
  @ReactProp(name = "scrollableOptions")
235
269
  override fun setScrollableOptions(view: TrueSheetView, options: ReadableMap?) {
236
270
  if (options == null) {
@@ -240,7 +274,11 @@ class TrueSheetViewManager :
240
274
 
241
275
  val scrollableOptions = ScrollableOptions(
242
276
  keyboardScrollOffset = if (options.hasKey("keyboardScrollOffset")) options.getDouble("keyboardScrollOffset").toFloat() else 0f,
243
- scrollingExpandsSheet = if (options.hasKey("scrollingExpandsSheet")) options.getBoolean("scrollingExpandsSheet") else true
277
+ keyboardOffset = if (options.hasKey("keyboardOffset")) options.getDouble("keyboardOffset").toFloat() else 0f,
278
+ scrollingExpandsSheet = if (options.hasKey("scrollingExpandsSheet")) options.getBoolean("scrollingExpandsSheet") else true,
279
+ contentInsetAdjustment = TrueSheetContentInsetAdjustment.fromString(
280
+ if (options.hasKey("contentInsetAdjustment")) options.getString("contentInsetAdjustment") else null
281
+ )
244
282
  )
245
283
  view.setScrollableOptions(scrollableOptions)
246
284
  }
@@ -259,6 +297,10 @@ class TrueSheetViewManager :
259
297
  val keyboardOffset =
260
298
  if (options != null && options.hasKey("keyboardOffset")) options.getDouble("keyboardOffset").toFloat() else 0f
261
299
  view.setFooterKeyboardOffset(keyboardOffset)
300
+
301
+ val avoidKeyboard =
302
+ if (options != null && options.hasKey("avoidKeyboard")) options.getBoolean("avoidKeyboard") else true
303
+ view.setFooterAvoidsKeyboard(avoidKeyboard)
262
304
  }
263
305
 
264
306
  companion object {
@@ -18,7 +18,7 @@ import androidx.core.view.ViewCompat
18
18
  import com.facebook.react.uimanager.PixelUtil.dpToPx
19
19
  import com.facebook.react.uimanager.ThemedReactContext
20
20
  import com.google.android.material.bottomsheet.BottomSheetBehavior
21
- import com.lodev09.truesheet.TrueSheetAnchor
21
+ import com.lodev09.truesheet.TrueSheetPlacement
22
22
  import com.lodev09.truesheet.utils.ScreenUtils
23
23
 
24
24
  interface TrueSheetBottomSheetViewDelegate {
@@ -27,8 +27,8 @@ interface TrueSheetBottomSheetViewDelegate {
27
27
  val sheetElevation: Float
28
28
  val sheetBackgroundColor: Int?
29
29
  val maxContentWidth: Int?
30
- val anchor: TrueSheetAnchor
31
- val anchorOffset: Int
30
+ val placement: TrueSheetPlacement
31
+ val placementOffset: Int
32
32
  val grabber: Boolean
33
33
  val grabberOptions: GrabberOptions?
34
34
  val accessibilityOptions: AccessibilityOptions?
@@ -94,13 +94,19 @@ class TrueSheetBottomSheetView(private val reactContext: ThemedReactContext) : F
94
94
  // =============================================================================
95
95
 
96
96
  private fun resolveAnchor(): Pair<Int, Int> {
97
- val anchor = if (ScreenUtils.isPortraitPhone(reactContext)) null else delegate?.anchor
98
- val gravity = when (anchor) {
99
- TrueSheetAnchor.LEFT -> Gravity.START
100
- TrueSheetAnchor.RIGHT -> Gravity.END
97
+ val placement = if (ScreenUtils.isPortraitPhone(reactContext)) null else delegate?.placement
98
+ val gravity = when (placement) {
99
+ TrueSheetPlacement.LEADING -> Gravity.START
100
+ TrueSheetPlacement.TRAILING -> Gravity.END
101
101
  else -> Gravity.CENTER_HORIZONTAL
102
102
  } or Gravity.BOTTOM
103
- val margin = if (anchor == TrueSheetAnchor.LEFT || anchor == TrueSheetAnchor.RIGHT) delegate?.anchorOffset ?: 0 else 0
103
+ val margin = if (placement == TrueSheetPlacement.LEADING ||
104
+ placement == TrueSheetPlacement.TRAILING
105
+ ) {
106
+ delegate?.placementOffset ?: 0
107
+ } else {
108
+ 0
109
+ }
104
110
  return Pair(gravity, margin)
105
111
  }
106
112
 
@@ -173,7 +179,14 @@ class TrueSheetBottomSheetView(private val reactContext: ThemedReactContext) : F
173
179
  clipToOutline = true
174
180
  }
175
181
 
176
- private fun getDefaultBackgroundColor(): Int {
182
+ fun updateBackgroundColor(color: Int) {
183
+ (background as? ShapeDrawable)?.let {
184
+ it.paint.color = color
185
+ it.invalidateSelf()
186
+ }
187
+ }
188
+
189
+ fun getDefaultBackgroundColor(): Int {
177
190
  val isNight = (
178
191
  reactContext.resources.configuration.uiMode and
179
192
  Configuration.UI_MODE_NIGHT_MASK
@@ -10,9 +10,11 @@ import android.view.ViewGroup
10
10
  import android.widget.EditText
11
11
  import androidx.coordinatorlayout.widget.CoordinatorLayout
12
12
  import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
13
+ import com.facebook.react.uimanager.PixelUtil.dpToPx
13
14
  import com.facebook.react.uimanager.PointerEvents
14
15
  import com.facebook.react.uimanager.ReactPointerEventsView
15
16
  import com.facebook.react.uimanager.TouchTargetHelper
17
+ import com.lodev09.truesheet.utils.KeyboardUtils
16
18
  import com.lodev09.truesheet.utils.isDescendantOf
17
19
 
18
20
  interface TrueSheetCoordinatorLayoutDelegate {
@@ -20,6 +22,12 @@ interface TrueSheetCoordinatorLayoutDelegate {
20
22
  fun coordinatorLayoutDidChangeConfiguration()
21
23
  fun findScrollView(): ViewGroup?
22
24
  fun findSheetView(): TrueSheetBottomSheetView?
25
+
26
+ /**
27
+ * A downward pull the sheet couldn't follow ended; `overdragPx` is the travel it
28
+ * didn't follow, `keyboardWasVisible` whether the keyboard was up when it began.
29
+ */
30
+ fun coordinatorLayoutDidPullDown(overdragPx: Int, keyboardWasVisible: Boolean)
23
31
  }
24
32
 
25
33
  /**
@@ -57,13 +65,26 @@ class TrueSheetCoordinatorLayout(context: Context) :
57
65
  private var streamGestureClaimed = false
58
66
 
59
67
  // Whether the sheet owns vertical drags for this stream: the touch target is inside
60
- // the sheet but outside the pinned scrollable (e.g. a header overlaying it), with
68
+ // the sheet but outside the detected scrollable (e.g. a header overlaying it), with
61
69
  // nothing vertically scrollable along its chain. Such targets consume the stream
62
70
  // natively (no nested scroll), and BottomSheetBehavior won't intercept because the
63
71
  // scrollable's bounds still contain the point (touchingScrollingChild) — intercept
64
72
  // manually.
65
73
  private var streamSheetDraggable = false
66
74
  private var streamDraggableEditText = false
75
+ private var streamScrollsIndependently = false
76
+
77
+ // Pull-down tracking. BottomSheetBehavior gives no callback when a downward drag
78
+ // is clamped by `isHideable = false` — the sheet stays put (or the scrollable
79
+ // overscrolls) and no state change fires. Accumulate the downward finger travel
80
+ // neither the sheet nor its scrollable followed and report it on release so the
81
+ // controller can surface a blocked dismiss attempt.
82
+ private var pullTracking = false
83
+ private var pullStartX = 0f
84
+ private var pullStartY = 0f
85
+ private var pullLastY = 0f
86
+ private var pullOverdrag = 0f
87
+ private var pullKeyboardVisible = false
67
88
 
68
89
  init {
69
90
  layoutParams = LayoutParams(
@@ -94,6 +115,49 @@ class TrueSheetCoordinatorLayout(context: Context) :
94
115
  override val pointerEvents: PointerEvents
95
116
  get() = PointerEvents.BOX_NONE
96
117
 
118
+ override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
119
+ when (ev.actionMasked) {
120
+ MotionEvent.ACTION_DOWN -> {
121
+ val sheet = delegate?.findSheetView()
122
+ pullTracking = sheet != null && isTouchInside(sheet, ev)
123
+ pullStartX = ev.rawX
124
+ pullStartY = ev.rawY
125
+ pullLastY = ev.rawY
126
+ pullOverdrag = 0f
127
+ pullKeyboardVisible = pullTracking && KeyboardUtils.isKeyboardVisible(this)
128
+ }
129
+
130
+ MotionEvent.ACTION_MOVE -> if (pullTracking) {
131
+ val sheet = delegate?.findSheetView()
132
+ val topBefore = sheet?.top
133
+ val handled = super.dispatchTouchEvent(ev)
134
+ val step = ev.rawY - pullLastY
135
+ pullLastY = ev.rawY
136
+ // Finger moved down but the sheet stayed put and the scrollable is at
137
+ // its top — the pull was clamped
138
+ if (step > 0 && sheet != null && sheet.top == topBefore && delegate?.findScrollView()?.canScrollVertically(-1) != true) {
139
+ pullOverdrag += step
140
+ }
141
+ return handled
142
+ }
143
+
144
+ MotionEvent.ACTION_UP -> if (pullTracking) {
145
+ pullTracking = false
146
+ val dx = kotlin.math.abs(ev.rawX - pullStartX)
147
+ val dy = ev.rawY - pullStartY
148
+ // Let the behavior settle first so the controller sees the resting position.
149
+ val handled = super.dispatchTouchEvent(ev)
150
+ if (dy > dx && pullOverdrag > PULL_DOWN_THRESHOLD_DP.dpToPx()) {
151
+ delegate?.coordinatorLayoutDidPullDown(pullOverdrag.toInt(), pullKeyboardVisible)
152
+ }
153
+ return handled
154
+ }
155
+
156
+ MotionEvent.ACTION_CANCEL -> pullTracking = false
157
+ }
158
+ return super.dispatchTouchEvent(ev)
159
+ }
160
+
97
161
  /**
98
162
  * Clears stale `nestedScrollingChildRef` from BottomSheetBehavior.
99
163
  *
@@ -127,19 +191,20 @@ class TrueSheetCoordinatorLayout(context: Context) :
127
191
 
128
192
  /**
129
193
  * Resolves the stream's touch target (respecting zIndex and pointerEvents) and
130
- * decides whether the sheet may drag from it — inside the sheet, outside the pinned
194
+ * decides whether the sheet may drag from it — inside the sheet, outside the detected
131
195
  * scrollable, and nothing along the target's chain scrolls vertically.
132
196
  */
133
197
  private fun updateStreamTargetFlags(ev: MotionEvent) {
134
198
  streamSheetDraggable = false
135
199
  streamDraggableEditText = false
200
+ streamScrollsIndependently = false
136
201
 
137
202
  val sheet = delegate?.findSheetView() ?: return
138
203
  val target = TouchTargetHelper.findTargetPathAndCoordinatesForTouch(ev.x, ev.y, this, FloatArray(2))
139
204
  .firstNotNullOfOrNull { it.getView() } ?: return
140
205
  if (target !== sheet && !target.isDescendantOf(sheet)) return
141
206
 
142
- // A pinned scrollable that can scroll (or hosts pull-to-refresh) owns its
207
+ // A detected scrollable that can scroll (or hosts pull-to-refresh) owns its
143
208
  // touches — nested scrolling drags the sheet. When it can't scroll (e.g.
144
209
  // the sheet is fully expanded and the content fits) it eats the gesture
145
210
  // instead, so fall through and let the sheet drag from it — an EditText
@@ -152,7 +217,10 @@ class TrueSheetCoordinatorLayout(context: Context) :
152
217
 
153
218
  var view: View? = target
154
219
  while (view != null && view !== this) {
155
- if (view.canScrollVertically(1) || view.canScrollVertically(-1)) return
220
+ if (view.canScrollVertically(1) || view.canScrollVertically(-1)) {
221
+ streamScrollsIndependently = !view.isNestedScrollingEnabled
222
+ return
223
+ }
156
224
  view = view.parent as? View
157
225
  }
158
226
 
@@ -160,6 +228,14 @@ class TrueSheetCoordinatorLayout(context: Context) :
160
228
  streamDraggableEditText = target is EditText
161
229
  }
162
230
 
231
+ private fun isTouchInside(view: View, ev: MotionEvent): Boolean {
232
+ val loc = IntArray(2)
233
+ view.getLocationOnScreen(loc)
234
+ val x = ev.rawX.toInt()
235
+ val y = ev.rawY.toInt()
236
+ return x >= loc[0] && x <= loc[0] + view.width && y >= loc[1] && y <= loc[1] + view.height
237
+ }
238
+
163
239
  private fun findDescendantViewAt(view: View, rawX: Int, rawY: Int, predicate: (View) -> Boolean): View? {
164
240
  val loc = IntArray(2)
165
241
  view.getLocationOnScreen(loc)
@@ -174,7 +250,7 @@ class TrueSheetCoordinatorLayout(context: Context) :
174
250
  }
175
251
 
176
252
  /**
177
- * Called by descendant RootView implementations (controller, footer) when a child
253
+ * Called by the descendant RootView (controller) when a child
178
254
  * starts a native gesture — e.g. an RNGH handler activated or a scrollable began
179
255
  * scrolling. Yields sheet interception for the rest of the touch stream.
180
256
  */
@@ -226,6 +302,8 @@ class TrueSheetCoordinatorLayout(context: Context) :
226
302
  return false
227
303
  }
228
304
 
305
+ if (streamScrollsIndependently) return false
306
+
229
307
  if (streamGestureClaimed) {
230
308
  if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
231
309
  streamGestureClaimed = false
@@ -299,5 +377,6 @@ class TrueSheetCoordinatorLayout(context: Context) :
299
377
  companion object {
300
378
  private const val GESTURE_HANDLER_ROOT_VIEW_CLASS = "com.swmansion.gesturehandler.react.RNGestureHandlerRootView"
301
379
  private const val GESTURE_CLAIM_SLOP_FACTOR = 3
380
+ private const val PULL_DOWN_THRESHOLD_DP = 24f
302
381
  }
303
382
  }
@@ -18,11 +18,27 @@ interface TrueSheetDetentCalculatorDelegate {
18
18
  val absoluteHeader: Boolean
19
19
  val footerHeight: Int
20
20
  val absoluteFooter: Boolean
21
+
22
+ /**
23
+ * True while the plugged scrollable is padded by the absolute footer's
24
+ * height (see ScrollableOptions.footerInsetAdjustment) — the footer then
25
+ * counts toward the auto detent like a relative one, so the content ends
26
+ * above it.
27
+ */
28
+ val footerInsetAdjustment: Boolean
29
+ val footerAvoidsKeyboard: Boolean
21
30
  val peekContentHeight: Int
22
31
  val contentBottomInset: Int
23
32
  val maxContentHeight: Int?
24
33
  val keyboardInset: Int
25
34
  val topInset: Int
35
+
36
+ /**
37
+ * True while the keyboard holds the sheet up: the collapsed stop then sits at
38
+ * the keyboard-free first detent (the "floor") so a drag-down dismisses the
39
+ * keyboard before it can dismiss the sheet.
40
+ */
41
+ val hasKeyboardFloor: Boolean
26
42
  }
27
43
 
28
44
  /**
@@ -44,6 +60,15 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
44
60
  private val maxContentHeight: Int? get() = delegate?.maxContentHeight
45
61
  private val keyboardInset: Int get() = delegate?.keyboardInset ?: 0
46
62
  private val topInset: Int get() = delegate?.topInset ?: 0
63
+ private val hasKeyboardFloor: Boolean get() = delegate?.hasKeyboardFloor == true
64
+
65
+ /**
66
+ * An absolute footer floats over the content and contributes no height to
67
+ * the auto detent — unless it pads the scrollable, then the content ends
68
+ * above it like a relative footer.
69
+ */
70
+ private val footerInAuto: Boolean
71
+ get() = delegate?.absoluteFooter == false || delegate?.footerInsetAdjustment == true
47
72
 
48
73
  /**
49
74
  * Height for auto (-1.0) detents: content + header + footer height.
@@ -52,14 +77,14 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
52
77
  private val autoDetentHeight: Int
53
78
  get() = contentHeight +
54
79
  (if (delegate?.absoluteHeader == true) 0 else headerHeight) +
55
- (if (delegate?.absoluteFooter == true) 0 else footerHeight)
80
+ (if (footerInAuto) footerHeight else 0)
56
81
 
57
82
  /**
58
- * Bottom inset for auto detents. A relative footer owns the sheet's bottom
59
- * edge, so it absorbs the inset instead of adding it to the auto height.
83
+ * Bottom inset for auto detents. A footer counted in the auto height owns
84
+ * the sheet's bottom edge, so it absorbs the inset instead of adding it.
60
85
  */
61
86
  private val autoDetentBottomInset: Int
62
- get() = if (footerHeight > 0 && delegate?.absoluteFooter == false) 0 else contentBottomInset
87
+ get() = if (footerHeight > 0 && footerInAuto) 0 else contentBottomInset
63
88
 
64
89
  /**
65
90
  * Bottom inset for peek detents. An absolute footer counts toward the peek
@@ -97,11 +122,37 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
97
122
  (detent * screenHeight).toInt() + contentBottomInset
98
123
  }
99
124
 
100
- val height = if (includeKeyboard) baseHeight + keyboardInset else baseHeight
125
+ val keyboardHeight = if (includeKeyboard) keyboardInset else 0
126
+ val footerKeyboardOverlap = if (detent == -1.0 &&
127
+ delegate?.absoluteFooter == true &&
128
+ footerInAuto &&
129
+ delegate?.footerAvoidsKeyboard == false
130
+ ) {
131
+ minOf(keyboardHeight, maxOf(0, footerHeight - contentBottomInset))
132
+ } else {
133
+ 0
134
+ }
135
+ val height = baseHeight + keyboardHeight - footerKeyboardOverlap
101
136
  val maxAllowedHeight = screenHeight + contentBottomInset
102
137
  return maxContentHeight?.let { minOf(height, it, maxAllowedHeight) } ?: minOf(height, maxAllowedHeight)
103
138
  }
104
139
 
140
+ fun getBackgroundIndex(sheetTop: Int, displayedIndex: Int): Int {
141
+ if (detents.isEmpty()) return -1
142
+
143
+ val height = getVisibleSheetHeight(sheetTop)
144
+ var nextIndex = displayedIndex.coerceIn(0, detents.size - 1)
145
+ var distance = kotlin.math.abs(height - getVisibleSheetHeight(getSheetTopForDetentIndex(nextIndex)))
146
+ for (index in detents.indices) {
147
+ val nextDistance = kotlin.math.abs(height - getVisibleSheetHeight(getSheetTopForDetentIndex(index)))
148
+ if (nextDistance < distance) {
149
+ nextIndex = index
150
+ distance = nextDistance
151
+ }
152
+ }
153
+ return nextIndex
154
+ }
155
+
105
156
  /**
106
157
  * Get the expected sheet top position for a detent index.
107
158
  */
@@ -110,12 +161,31 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
110
161
  RNLog.w(reactContext, "TrueSheet: Detent index ($index) is out of bounds (0..${detents.size - 1})")
111
162
  return realScreenHeight
112
163
  }
113
- // Clamp to the space the sheet can actually occupy — matching
114
- // setupSheetDetents. A keyboard-inflated detent height can exceed it,
115
- // placing the expected top above the screen while the real sheet stops
116
- // at the top inset.
164
+ // With a keyboard floor only the last detent stays keyboard-shifted — it's
165
+ // the keyboard-expanded resting position; the stops below it are keyboard-free
166
+ val isKeyboardFreeStop = hasKeyboardFloor && index != detents.size - 1
167
+ return getSheetTop(detents[index], includeKeyboard = !isKeyboardFreeStop)
168
+ }
169
+
170
+ /**
171
+ * Top of the lowest stop the sheet can rest at: the keyboard floor while it's
172
+ * active, else the first detent.
173
+ */
174
+ fun getLowestSheetTop(): Int = if (hasKeyboardFloor) getSheetTop(detents[0], includeKeyboard = false) else getSheetTopForDetentIndex(0)
175
+
176
+ /**
177
+ * Top of the half stop while the keyboard floor is active: the keyboard-free
178
+ * second detent, or the floor itself for a single detent.
179
+ */
180
+ fun getKeyboardHalfStopTop(): Int = getSheetTop(detents[minOf(1, detents.size - 1)], includeKeyboard = false)
181
+
182
+ // Clamp to the space the sheet can actually occupy — matching
183
+ // setupSheetDetents. A keyboard-inflated detent height can exceed it,
184
+ // placing the expected top above the screen while the real sheet stops
185
+ // at the top inset.
186
+ private fun getSheetTop(detent: Double, includeKeyboard: Boolean = true): Int {
117
187
  val maxAvailableHeight = realScreenHeight - topInset
118
- return realScreenHeight - minOf(getDetentHeight(detents[index]), maxAvailableHeight)
188
+ return realScreenHeight - minOf(getDetentHeight(detent, includeKeyboard), maxAvailableHeight)
119
189
  }
120
190
 
121
191
  /**
@@ -152,6 +222,9 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
152
222
  */
153
223
  fun getStateForDetentIndex(index: Int): Int {
154
224
  val stateMap = getDetentStateMap() ?: return BottomSheetBehavior.STATE_HIDDEN
225
+ // With a keyboard floor the keyboard-expanded last detent lives at the
226
+ // expanded stop — the half stop (which may share its index) is keyboard-free
227
+ if (hasKeyboardFloor && index == detents.size - 1) return BottomSheetBehavior.STATE_EXPANDED
155
228
  return stateMap.entries.find { it.value == index }?.key ?: BottomSheetBehavior.STATE_HIDDEN
156
229
  }
157
230
 
@@ -169,8 +242,12 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
169
242
  */
170
243
  private fun getDetentStateMap(): Map<Int, Int>? =
171
244
  when (detents.size) {
245
+ // With one detent halfExpandedRatio matches the peek height, so the
246
+ // behavior can settle HALF_EXPANDED at the same position (e.g. an upward
247
+ // drag release) — map it too, or the settle is unmappable
172
248
  1 -> mapOf(
173
249
  BottomSheetBehavior.STATE_COLLAPSED to 0,
250
+ BottomSheetBehavior.STATE_HALF_EXPANDED to 0,
174
251
  BottomSheetBehavior.STATE_EXPANDED to 0
175
252
  )
176
253
 
@@ -197,16 +274,18 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
197
274
  * Find which segment the position falls into for interpolation.
198
275
  * @return Triple(fromIndex, toIndex, progress) where progress is 0-1, or null if no detents
199
276
  */
200
- fun findSegmentForPosition(positionPx: Int): Triple<Int, Int, Float>? {
277
+ private fun findSegmentForPosition(positionPx: Int): Triple<Int, Int, Float>? {
201
278
  val count = detents.size
202
279
  if (count == 0) return null
203
280
 
204
- val firstPos = getSheetTopForDetentIndex(0)
281
+ // A single keyboard-shifted detent can rest down at its keyboard floor —
282
+ // that range is still index 0, not a dismissal
283
+ val floorPos = getLowestSheetTop()
205
284
 
206
- // Position is below first detent (sheet is being dragged down to dismiss)
207
- if (positionPx > firstPos) {
208
- val range = realScreenHeight - firstPos
209
- val progress = if (range > 0) (positionPx - firstPos).toFloat() / range else 0f
285
+ // Position is below the lowest stop (sheet is being dragged down to dismiss)
286
+ if (positionPx > floorPos) {
287
+ val range = realScreenHeight - floorPos
288
+ val progress = if (range > 0) (positionPx - floorPos).toFloat() / range else 0f
210
289
  return Triple(-1, 0, progress)
211
290
  }
212
291
 
@@ -238,37 +317,23 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
238
317
  }
239
318
 
240
319
  /**
241
- * Returns continuous index (e.g., 0.5 = halfway between detent 0 and 1).
320
+ * Returns interpolated continuous index (e.g., 0.5 = halfway between detent
321
+ * 0 and 1) and screen fraction for a position, sharing a single segment
322
+ * lookup — this runs on every position frame.
242
323
  */
243
- fun getInterpolatedIndexForPosition(positionPx: Int): Float {
244
- val count = detents.size
245
- if (count == 0) return -1f
246
-
247
- val segment = findSegmentForPosition(positionPx) ?: return 0f
248
- val (fromIndex, _, progress) = segment
249
-
250
- if (fromIndex == -1) return -progress
251
- return fromIndex + progress
252
- }
253
-
254
- /**
255
- * Returns interpolated screen fraction for position.
256
- */
257
- fun getInterpolatedDetentForPosition(positionPx: Int): Float {
258
- val count = detents.size
259
- if (count == 0) return 0f
324
+ fun getInterpolatedPositionInfo(positionPx: Int): Pair<Float, Float> {
325
+ if (detents.isEmpty()) return Pair(-1f, 0f)
260
326
 
261
- val segment = findSegmentForPosition(positionPx) ?: return getDetentValueForIndex(0)
262
- val (fromIndex, toIndex, progress) = segment
327
+ val (fromIndex, toIndex, progress) = findSegmentForPosition(positionPx)
328
+ ?: return Pair(0f, getDetentValueForIndex(0))
263
329
 
264
330
  if (fromIndex == -1) {
265
- val firstDetent = getDetentValueForIndex(0)
266
- return maxOf(0f, firstDetent * (1 - progress))
331
+ return Pair(-progress, maxOf(0f, getDetentValueForIndex(0) * (1 - progress)))
267
332
  }
268
333
 
269
334
  val fromDetent = getDetentValueForIndex(fromIndex)
270
335
  val toDetent = getDetentValueForIndex(toIndex)
271
- return fromDetent + progress * (toDetent - fromDetent)
336
+ return Pair(fromIndex + progress, fromDetent + progress * (toDetent - fromDetent))
272
337
  }
273
338
 
274
339
  companion object {
@@ -139,6 +139,9 @@ class TrueSheetDimView(private val reactContext: ThemedReactContext) :
139
139
 
140
140
  override fun onTouchEvent(event: MotionEvent): Boolean {
141
141
  if (blockGestures) {
142
+ if (event.actionMasked == MotionEvent.ACTION_DOWN) {
143
+ parent?.requestDisallowInterceptTouchEvent(true)
144
+ }
142
145
  // When dimmed, consume touch and trigger click on ACTION_UP
143
146
  if (event.action == MotionEvent.ACTION_UP) {
144
147
  performClick()