@lodev09/react-native-true-sheet 4.0.0-beta.3 → 4.0.0-beta.30

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 (244) hide show
  1. package/README.md +28 -5
  2. package/android/build.gradle +23 -1
  3. package/android/proguard-rules.pro +3 -0
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +50 -16
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +303 -184
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +38 -109
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +13 -0
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +127 -0
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
  11. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
  12. package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +29 -3
  13. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +234 -66
  14. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +491 -186
  15. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +64 -10
  16. package/android/src/main/java/com/lodev09/truesheet/core/RNScreensEventObserver.kt +31 -14
  17. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetBehavior.kt +135 -0
  18. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +23 -23
  19. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +130 -6
  20. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +104 -39
  21. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +14 -17
  22. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetGrabberView.kt +2 -4
  23. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +148 -0
  24. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +33 -40
  25. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
  26. package/android/src/main/java/com/lodev09/truesheet/utils/ColorUtils.kt +19 -0
  27. package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +15 -2
  28. package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
  29. package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
  30. package/android/src/main/jni/TrueSheetSpec.h +1 -0
  31. package/android/src/main/jni/TrueSheetStateUpdater.cpp +37 -6
  32. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +1 -9
  33. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +114 -30
  34. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +30 -7
  35. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +2 -2
  36. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +9 -8
  37. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
  38. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +14 -3
  39. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +5 -6
  40. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
  41. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +10 -6
  42. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
  43. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
  44. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +43 -0
  45. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
  46. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
  47. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +33 -0
  48. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
  49. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +7 -40
  50. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
  51. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
  52. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
  53. package/ios/TrueSheetContainerView.h +23 -7
  54. package/ios/TrueSheetContainerView.mm +45 -25
  55. package/ios/TrueSheetContentView.h +44 -15
  56. package/ios/TrueSheetContentView.mm +221 -190
  57. package/ios/TrueSheetFooterView.mm +133 -28
  58. package/ios/TrueSheetModule.h +2 -7
  59. package/ios/TrueSheetModule.mm +18 -18
  60. package/ios/TrueSheetOverlayView.h +42 -0
  61. package/ios/TrueSheetOverlayView.mm +175 -0
  62. package/ios/TrueSheetView.h +2 -0
  63. package/ios/TrueSheetView.mm +268 -52
  64. package/ios/TrueSheetViewController.h +36 -3
  65. package/ios/TrueSheetViewController.mm +797 -102
  66. package/ios/core/RNScreensEventObserver.mm +31 -28
  67. package/ios/core/TrueSheetBlurView.h +0 -2
  68. package/ios/core/TrueSheetBlurView.mm +3 -36
  69. package/ios/core/TrueSheetDetentCalculator.h +18 -1
  70. package/ios/core/TrueSheetDetentCalculator.mm +44 -9
  71. package/ios/core/TrueSheetDimView.h +27 -0
  72. package/ios/core/TrueSheetDimView.mm +41 -0
  73. package/ios/core/TrueSheetGrabberView.mm +13 -3
  74. package/ios/core/TrueSheetKeyboardObserver.h +7 -0
  75. package/ios/core/TrueSheetKeyboardObserver.mm +107 -15
  76. package/ios/core/TrueSheetOverlayContainerView.h +31 -0
  77. package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
  78. package/ios/events/TrueSheetLifecycleEvents.h +2 -0
  79. package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
  80. package/ios/tvos/TrueSheetStubs.mm +12 -0
  81. package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
  82. package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
  83. package/lib/module/TrueSheet.js +222 -54
  84. package/lib/module/TrueSheet.js.map +1 -1
  85. package/lib/module/TrueSheet.web.js +364 -275
  86. package/lib/module/TrueSheet.web.js.map +1 -1
  87. package/lib/module/TrueSheetBackground.js +15 -0
  88. package/lib/module/TrueSheetBackground.js.map +1 -0
  89. package/lib/module/TrueSheetOverlay.js +30 -0
  90. package/lib/module/TrueSheetOverlay.js.map +1 -0
  91. package/lib/module/TrueSheetOverlay.web.js +35 -0
  92. package/lib/module/TrueSheetOverlay.web.js.map +1 -0
  93. package/lib/module/TrueSheetPeek.web.js +20 -15
  94. package/lib/module/TrueSheetPeek.web.js.map +1 -1
  95. package/lib/module/TrueSheetProvider.web.js +13 -9
  96. package/lib/module/TrueSheetProvider.web.js.map +1 -1
  97. package/lib/module/detentBackgrounds.js +21 -0
  98. package/lib/module/detentBackgrounds.js.map +1 -0
  99. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  100. package/lib/module/fabric/TrueSheetFooterViewNativeComponent.ts +2 -3
  101. package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  102. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +89 -73
  103. package/lib/module/index.js +1 -0
  104. package/lib/module/index.js.map +1 -1
  105. package/lib/module/mocks/index.js +18 -2
  106. package/lib/module/mocks/index.js.map +1 -1
  107. package/lib/module/mocks/navigationExpoRouter.js.map +1 -1
  108. package/lib/module/navigation/base-types.js +2 -0
  109. package/lib/module/navigation/base-types.js.map +1 -0
  110. package/lib/module/navigation/createTrueSheetRouter.js +158 -100
  111. package/lib/module/navigation/createTrueSheetRouter.js.map +1 -1
  112. package/lib/module/navigation/expo-router/base-types.js +2 -0
  113. package/lib/module/navigation/expo-router/base-types.js.map +1 -0
  114. package/lib/module/navigation/expo-router/extendTrueSheetRouter.js +71 -0
  115. package/lib/module/navigation/expo-router/extendTrueSheetRouter.js.map +1 -0
  116. package/lib/module/navigation/expo-router/index.js +18 -7
  117. package/lib/module/navigation/expo-router/index.js.map +1 -1
  118. package/lib/module/navigation/expo-router/types.js +4 -0
  119. package/lib/module/navigation/expo-router/types.js.map +1 -0
  120. package/lib/module/navigation/index.js.map +1 -1
  121. package/lib/module/navigation/screen/useSheetScreenState.js +1 -0
  122. package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
  123. package/lib/module/scrollable.js +28 -0
  124. package/lib/module/scrollable.js.map +1 -0
  125. package/lib/module/web/constants.js +1 -1
  126. package/lib/module/web/constants.js.map +1 -1
  127. package/lib/module/web/keyboard.js +69 -0
  128. package/lib/module/web/keyboard.js.map +1 -0
  129. package/lib/module/web/layout.js +168 -0
  130. package/lib/module/web/layout.js.map +1 -0
  131. package/lib/module/web/vaul/constants.js +9 -0
  132. package/lib/module/web/vaul/constants.js.map +1 -1
  133. package/lib/module/web/vaul/context.js +4 -0
  134. package/lib/module/web/vaul/context.js.map +1 -1
  135. package/lib/module/web/vaul/helpers.js +1 -0
  136. package/lib/module/web/vaul/helpers.js.map +1 -1
  137. package/lib/module/web/vaul/index.js +83 -21
  138. package/lib/module/web/vaul/index.js.map +1 -1
  139. package/lib/module/web/vaul/use-snap-points.js +57 -28
  140. package/lib/module/web/vaul/use-snap-points.js.map +1 -1
  141. package/lib/typescript/src/TrueSheet.d.ts +18 -1
  142. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  143. package/lib/typescript/src/TrueSheet.types.d.ts +209 -35
  144. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  145. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  146. package/lib/typescript/src/TrueSheetBackground.d.ts +3 -0
  147. package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
  148. package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
  149. package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
  150. package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
  151. package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
  152. package/lib/typescript/src/TrueSheetPeek.web.d.ts +8 -4
  153. package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
  154. package/lib/typescript/src/TrueSheetProvider.web.d.ts +5 -1
  155. package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -1
  156. package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
  157. package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
  158. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts +2 -3
  159. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts.map +1 -1
  160. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
  161. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
  162. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +76 -65
  163. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  164. package/lib/typescript/src/index.d.ts +1 -0
  165. package/lib/typescript/src/index.d.ts.map +1 -1
  166. package/lib/typescript/src/mocks/index.d.ts +4 -0
  167. package/lib/typescript/src/mocks/index.d.ts.map +1 -1
  168. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +3 -3
  169. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -1
  170. package/lib/typescript/src/navigation/base-types.d.ts +9 -0
  171. package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
  172. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +36 -10
  173. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -1
  174. package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
  175. package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
  176. package/lib/typescript/src/navigation/expo-router/extendTrueSheetRouter.d.ts +33 -0
  177. package/lib/typescript/src/navigation/expo-router/extendTrueSheetRouter.d.ts.map +1 -0
  178. package/lib/typescript/src/navigation/expo-router/index.d.ts +9 -6
  179. package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -1
  180. package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
  181. package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
  182. package/lib/typescript/src/navigation/index.d.ts +3 -4
  183. package/lib/typescript/src/navigation/index.d.ts.map +1 -1
  184. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +2 -1
  185. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
  186. package/lib/typescript/src/navigation/types.d.ts +43 -6
  187. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  188. package/lib/typescript/src/scrollable.d.ts +2 -0
  189. package/lib/typescript/src/scrollable.d.ts.map +1 -0
  190. package/lib/typescript/src/web/constants.d.ts +1 -1
  191. package/lib/typescript/src/web/constants.d.ts.map +1 -1
  192. package/lib/typescript/src/web/keyboard.d.ts +15 -0
  193. package/lib/typescript/src/web/keyboard.d.ts.map +1 -0
  194. package/lib/typescript/src/web/layout.d.ts +20 -0
  195. package/lib/typescript/src/web/layout.d.ts.map +1 -0
  196. package/lib/typescript/src/web/vaul/constants.d.ts +3 -0
  197. package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
  198. package/lib/typescript/src/web/vaul/context.d.ts +5 -1
  199. package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
  200. package/lib/typescript/src/web/vaul/helpers.d.ts +1 -0
  201. package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
  202. package/lib/typescript/src/web/vaul/index.d.ts +20 -1
  203. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
  204. package/lib/typescript/src/web/vaul/types.d.ts +9 -0
  205. package/lib/typescript/src/web/vaul/types.d.ts.map +1 -1
  206. package/lib/typescript/src/web/vaul/use-snap-points.d.ts +6 -4
  207. package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
  208. package/package.json +18 -8
  209. package/react-native.config.js +1 -0
  210. package/src/TrueSheet.tsx +257 -62
  211. package/src/TrueSheet.types.ts +225 -35
  212. package/src/TrueSheet.web.tsx +445 -333
  213. package/src/TrueSheetBackground.tsx +17 -0
  214. package/src/TrueSheetOverlay.tsx +24 -0
  215. package/src/TrueSheetOverlay.web.tsx +33 -0
  216. package/src/TrueSheetPeek.web.tsx +25 -23
  217. package/src/TrueSheetProvider.web.tsx +22 -13
  218. package/src/detentBackgrounds.ts +24 -0
  219. package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  220. package/src/fabric/TrueSheetFooterViewNativeComponent.ts +2 -3
  221. package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  222. package/src/fabric/TrueSheetViewNativeComponent.ts +89 -73
  223. package/src/index.ts +1 -0
  224. package/src/mocks/index.ts +17 -2
  225. package/src/mocks/navigationExpoRouter.ts +6 -5
  226. package/src/navigation/base-types.ts +20 -0
  227. package/src/navigation/createTrueSheetRouter.ts +220 -144
  228. package/src/navigation/expo-router/base-types.ts +18 -0
  229. package/src/navigation/expo-router/extendTrueSheetRouter.ts +99 -0
  230. package/src/navigation/expo-router/index.ts +41 -30
  231. package/src/navigation/expo-router/types.ts +116 -0
  232. package/src/navigation/index.ts +6 -7
  233. package/src/navigation/screen/useSheetScreenState.ts +2 -0
  234. package/src/navigation/types.ts +81 -23
  235. package/src/scrollable.ts +25 -0
  236. package/src/web/constants.ts +1 -1
  237. package/src/web/keyboard.ts +79 -0
  238. package/src/web/layout.ts +196 -0
  239. package/src/web/vaul/constants.ts +9 -0
  240. package/src/web/vaul/context.ts +7 -1
  241. package/src/web/vaul/helpers.ts +5 -0
  242. package/src/web/vaul/index.tsx +113 -16
  243. package/src/web/vaul/types.ts +10 -0
  244. package/src/web/vaul/use-snap-points.ts +86 -33
@@ -5,6 +5,7 @@ import com.facebook.react.bridge.ReadableArray
5
5
  import com.facebook.react.bridge.ReadableMap
6
6
  import com.facebook.react.module.annotations.ReactModule
7
7
  import com.facebook.react.uimanager.PixelUtil.dpToPx
8
+ import com.facebook.react.uimanager.PointerEvents
8
9
  import com.facebook.react.uimanager.ReactStylesDiffMap
9
10
  import com.facebook.react.uimanager.StateWrapper
10
11
  import com.facebook.react.uimanager.ThemedReactContext
@@ -17,6 +18,7 @@ import com.facebook.react.viewmanagers.TrueSheetViewManagerInterface
17
18
  import com.lodev09.truesheet.core.AccessibilityOptions
18
19
  import com.lodev09.truesheet.core.GrabberOptions
19
20
  import com.lodev09.truesheet.events.*
21
+ import com.lodev09.truesheet.utils.getColorOrNull
20
22
 
21
23
  /**
22
24
  * ViewManager for TrueSheetView - Fabric architecture
@@ -55,6 +57,20 @@ class TrueSheetViewManager :
55
57
 
56
58
  override fun getDelegate(): ViewManagerDelegate<TrueSheetView> = delegate
57
59
 
60
+ // The codegen delegate drops props outside the spec (pointerEvents comes from the
61
+ // host's style), so apply it here after the delegate pass.
62
+ override fun updateProperties(viewToUpdate: TrueSheetView, props: ReactStylesDiffMap) {
63
+ super.updateProperties(viewToUpdate, props)
64
+ if (props.hasKey("pointerEvents")) {
65
+ setPointerEvents(viewToUpdate, props.getString("pointerEvents"))
66
+ }
67
+ }
68
+
69
+ @ReactProp(name = "pointerEvents")
70
+ fun setPointerEvents(view: TrueSheetView, pointerEventsStr: String?) {
71
+ view.pointerEvents = PointerEvents.parsePointerEvents(pointerEventsStr)
72
+ }
73
+
58
74
  /**
59
75
  * Export custom direct event types for Fabric
60
76
  * Uses Kotlin native collections with decoupled event classes
@@ -66,6 +82,7 @@ class TrueSheetViewManager :
66
82
  DidPresentEvent.EVENT_NAME to hashMapOf("registrationName" to DidPresentEvent.REGISTRATION_NAME),
67
83
  WillDismissEvent.EVENT_NAME to hashMapOf("registrationName" to WillDismissEvent.REGISTRATION_NAME),
68
84
  DidDismissEvent.EVENT_NAME to hashMapOf("registrationName" to DidDismissEvent.REGISTRATION_NAME),
85
+ DismissAttemptEvent.EVENT_NAME to hashMapOf("registrationName" to DismissAttemptEvent.REGISTRATION_NAME),
69
86
  DetentChangeEvent.EVENT_NAME to hashMapOf("registrationName" to DetentChangeEvent.REGISTRATION_NAME),
70
87
  DragBeginEvent.EVENT_NAME to hashMapOf("registrationName" to DragBeginEvent.REGISTRATION_NAME),
71
88
  DragChangeEvent.EVENT_NAME to hashMapOf("registrationName" to DragChangeEvent.REGISTRATION_NAME),
@@ -98,6 +115,15 @@ class TrueSheetViewManager :
98
115
  view.setDetents(detents)
99
116
  }
100
117
 
118
+ @ReactProp(name = "detentBackgrounds")
119
+ override fun setDetentBackgrounds(view: TrueSheetView, value: ReadableArray?) {
120
+ view.setDetentBackgrounds(
121
+ (0 until (value?.size() ?: 0)).map { index ->
122
+ value?.getMap(index)?.getColorOrNull("color", view.context)
123
+ }
124
+ )
125
+ }
126
+
101
127
  @ReactProp(name = "backgroundColor", customType = "Color")
102
128
  override fun setBackgroundColor(view: TrueSheetView, color: Int?) {
103
129
  view.setSheetBackgroundColor(color)
@@ -131,7 +157,7 @@ class TrueSheetViewManager :
131
157
  } else {
132
158
  null
133
159
  },
134
- color = if (options.hasKey("color") && !options.isNull("color")) options.getInt("color") else null,
160
+ color = options.getColorOrNull("color", view.context),
135
161
  adaptive = if (options.hasKey("adaptive")) options.getBoolean("adaptive") else true
136
162
  )
137
163
  view.setGrabberOptions(grabberOptions)
@@ -166,11 +192,26 @@ class TrueSheetViewManager :
166
192
  view.setDraggable(draggable)
167
193
  }
168
194
 
195
+ @ReactProp(name = "dismissThreshold")
196
+ override fun setDismissThreshold(view: TrueSheetView, value: String?) {
197
+ view.setDismissThreshold(value)
198
+ }
199
+
169
200
  @ReactProp(name = "dimmed", defaultBoolean = true)
170
201
  override fun setDimmed(view: TrueSheetView, dimmed: Boolean) {
171
202
  view.setDimmed(dimmed)
172
203
  }
173
204
 
205
+ @ReactProp(name = "dimColor", customType = "Color")
206
+ override fun setDimColor(view: TrueSheetView, color: Int?) {
207
+ view.setDimColor(color)
208
+ }
209
+
210
+ @ReactProp(name = "dimOpacity", defaultDouble = -1.0)
211
+ override fun setDimOpacity(view: TrueSheetView, opacity: Double) {
212
+ view.setDimOpacity(opacity.toFloat())
213
+ }
214
+
174
215
  @ReactProp(name = "dimmedDetentIndex", defaultInt = 0)
175
216
  override fun setDimmedDetentIndex(view: TrueSheetView, index: Int) {
176
217
  view.setDimmedDetentIndex(index)
@@ -197,14 +238,14 @@ class TrueSheetViewManager :
197
238
  view.setMaxContentWidth(if (width > 0) width.dpToPx().toInt() else null)
198
239
  }
199
240
 
200
- @ReactProp(name = "anchor")
201
- override fun setAnchor(view: TrueSheetView, anchor: String?) {
202
- view.setAnchor(anchor)
241
+ @ReactProp(name = "placement")
242
+ override fun setPlacement(view: TrueSheetView, placement: String?) {
243
+ view.setPlacement(placement)
203
244
  }
204
245
 
205
- @ReactProp(name = "anchorOffset", defaultDouble = 0.0)
206
- override fun setAnchorOffset(view: TrueSheetView, offset: Double) {
207
- view.setAnchorOffset(if (offset > 0) offset.dpToPx().toInt() else 0)
246
+ @ReactProp(name = "placementOffset", defaultDouble = 0.0)
247
+ override fun setPlacementOffset(view: TrueSheetView, offset: Double) {
248
+ view.setPlacementOffset(if (offset > 0) offset.dpToPx().toInt() else 0)
208
249
  }
209
250
 
210
251
  @ReactProp(name = "backgroundBlur")
@@ -212,8 +253,8 @@ class TrueSheetViewManager :
212
253
  // iOS-specific prop - no-op on Android
213
254
  }
214
255
 
215
- @ReactProp(name = "blurOptions")
216
- override fun setBlurOptions(view: TrueSheetView, options: ReadableMap?) {
256
+ @ReactProp(name = "foldOptions")
257
+ override fun setFoldOptions(view: TrueSheetView, options: ReadableMap?) {
217
258
  // iOS-specific prop - no-op on Android
218
259
  }
219
260
 
@@ -232,6 +273,11 @@ class TrueSheetViewManager :
232
273
  view.setSheetElevation(elevation.toFloat())
233
274
  }
234
275
 
276
+ @ReactProp(name = "scrollableHandle", defaultInt = -1)
277
+ override fun setScrollableHandle(view: TrueSheetView, handle: Int) {
278
+ view.setScrollableHandle(handle)
279
+ }
280
+
235
281
  @ReactProp(name = "scrollableOptions")
236
282
  override fun setScrollableOptions(view: TrueSheetView, options: ReadableMap?) {
237
283
  if (options == null) {
@@ -241,7 +287,11 @@ class TrueSheetViewManager :
241
287
 
242
288
  val scrollableOptions = ScrollableOptions(
243
289
  keyboardScrollOffset = if (options.hasKey("keyboardScrollOffset")) options.getDouble("keyboardScrollOffset").toFloat() else 0f,
244
- scrollingExpandsSheet = if (options.hasKey("scrollingExpandsSheet")) options.getBoolean("scrollingExpandsSheet") else true
290
+ keyboardOffset = if (options.hasKey("keyboardOffset")) options.getDouble("keyboardOffset").toFloat() else 0f,
291
+ scrollingExpandsSheet = if (options.hasKey("scrollingExpandsSheet")) options.getBoolean("scrollingExpandsSheet") else true,
292
+ contentInsetAdjustment = TrueSheetContentInsetAdjustment.fromString(
293
+ if (options.hasKey("contentInsetAdjustment")) options.getString("contentInsetAdjustment") else null
294
+ )
245
295
  )
246
296
  view.setScrollableOptions(scrollableOptions)
247
297
  }
@@ -260,6 +310,10 @@ class TrueSheetViewManager :
260
310
  val keyboardOffset =
261
311
  if (options != null && options.hasKey("keyboardOffset")) options.getDouble("keyboardOffset").toFloat() else 0f
262
312
  view.setFooterKeyboardOffset(keyboardOffset)
313
+
314
+ val avoidKeyboard =
315
+ if (options != null && options.hasKey("avoidKeyboard")) options.getBoolean("avoidKeyboard") else true
316
+ view.setFooterAvoidsKeyboard(avoidKeyboard)
263
317
  }
264
318
 
265
319
  companion object {
@@ -22,6 +22,15 @@ class RNScreensEventObserver : EventDispatcherListener {
22
22
  private var eventDispatcher: EventDispatcher? = null
23
23
  var presenterScreenTag: Int = 0
24
24
 
25
+ // Screens enclosing the presenter. react-native-screens only dispatches lifecycle
26
+ // events for stack screens, so a presenter inside a plain screen container (e.g. a
27
+ // bottom tab) never receives them and an enclosing stack screen has to stand in.
28
+ var ancestorScreenTags: Set<Int> = emptySet()
29
+
30
+ // Only the screen that hid the sheet brings it back. An enclosing screen reappearing
31
+ // must not reveal a sheet whose own screen is still covered inside it.
32
+ private var disappearedScreenTag: Int = 0
33
+
25
34
  fun startObserving(dispatcher: EventDispatcher?) {
26
35
  if (eventDispatcher != null || dispatcher == null) return
27
36
 
@@ -35,29 +44,37 @@ class RNScreensEventObserver : EventDispatcherListener {
35
44
  }
36
45
 
37
46
  fun capturePresenterScreenFromView(view: View?) {
38
- presenterScreenTag = 0
39
-
40
- var current: View? = view
41
- while (current != null) {
42
- if (isScreenView(current)) {
43
- presenterScreenTag = current.id
44
- break
45
- }
46
- current = (current.parent as? View)
47
- }
47
+ val tags = screenTags(view).toList()
48
+ presenterScreenTag = tags.firstOrNull() ?: 0
49
+ ancestorScreenTags = tags.drop(1).toSet()
48
50
  }
49
51
 
50
52
  override fun onEventDispatch(event: Event<*>) {
51
- // Only process events for the presenter screen
52
- if (presenterScreenTag == 0 || event.viewTag != presenterScreenTag) return
53
+ // Only process events for the presenter screen and the screens enclosing it
54
+ if (presenterScreenTag == 0) return
55
+ if (event.viewTag != presenterScreenTag && event.viewTag !in ancestorScreenTags) return
53
56
 
54
57
  when (event.eventName) {
55
- "topWillDisappear" -> delegate?.presenterScreenWillDisappear()
56
- "topWillAppear" -> delegate?.presenterScreenWillAppear()
58
+ "topWillDisappear" -> {
59
+ if (disappearedScreenTag != 0) return
60
+ disappearedScreenTag = event.viewTag
61
+ delegate?.presenterScreenWillDisappear()
62
+ }
63
+
64
+ "topWillAppear" -> {
65
+ if (event.viewTag != disappearedScreenTag) return
66
+ disappearedScreenTag = 0
67
+ delegate?.presenterScreenWillAppear()
68
+ }
57
69
  }
58
70
  }
59
71
 
60
72
  companion object {
61
73
  private fun isScreenView(view: View): Boolean = view.javaClass.name == RN_SCREENS_VIEW_CLASS
74
+
75
+ // Tags of the screens enclosing the view, innermost first
76
+ private fun screenTags(view: View?): Sequence<Int> = generateSequence(view) { it.parent as? View }.filter(::isScreenView).map { it.id }
77
+
78
+ fun findPresenterScreenTag(view: View?): Int = screenTags(view).firstOrNull() ?: 0
62
79
  }
63
80
  }
@@ -1,11 +1,82 @@
1
1
  package com.lodev09.truesheet.core
2
2
 
3
+ import android.view.MotionEvent
4
+ import android.view.VelocityTracker
3
5
  import android.view.View
4
6
  import androidx.coordinatorlayout.widget.CoordinatorLayout
7
+ import androidx.core.view.ViewCompat
8
+ import com.facebook.react.uimanager.PixelUtil.dpToPx
5
9
  import com.google.android.material.bottomsheet.BottomSheetBehavior
10
+ import kotlin.math.abs
11
+
12
+ enum class TrueSheetDismissThreshold {
13
+ HALF,
14
+ SHORT;
15
+
16
+ companion object {
17
+ fun fromString(value: String?): TrueSheetDismissThreshold =
18
+ when (value) {
19
+ "short" -> SHORT
20
+ else -> HALF
21
+ }
22
+ }
23
+ }
6
24
 
7
25
  class TrueSheetBottomSheetBehavior<V : View> : BottomSheetBehavior<V>() {
8
26
  var scrollingExpandsSheet: Boolean = true
27
+ var dismissThreshold: TrueSheetDismissThreshold = TrueSheetDismissThreshold.HALF
28
+ var lowestSheetTop: (() -> Int)? = null
29
+
30
+ private var nestedDragged = false
31
+ private var nestedReleaseVelocity = 0f
32
+ private var touchPointerId = MotionEvent.INVALID_POINTER_ID
33
+ private var trackedEventTime = -1L
34
+ private var trackedAction = -1
35
+ private var velocityTracker: VelocityTracker? = null
36
+
37
+ init {
38
+ // Material hides once the release point, projected by velocity * friction, passes half
39
+ // the lowest detent. A higher friction lets short flicks dismiss, closer to iOS sheets.
40
+ hideFriction = HIDE_FRICTION
41
+ }
42
+
43
+ // Material's first layout registers an IME callback that rides the sheet's translationY
44
+ // with the keyboard. TrueSheet positions sheets for the keyboard itself, and the callback
45
+ // overwrites a parent's stack translation when the keyboard rises mid-translation.
46
+ override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
47
+ val handled = super.onLayoutChild(parent, child, layoutDirection)
48
+ ViewCompat.setWindowInsetsAnimationCallback(child, null)
49
+ return handled
50
+ }
51
+
52
+ // With the short threshold, settle at or below the lowest detent like
53
+ // Compose Material3's sheet instead of Material's half-height rule.
54
+ private fun releaseState(child: V, dragged: Boolean, velocityY: Float): Int? {
55
+ if (dismissThreshold != TrueSheetDismissThreshold.SHORT || !isDraggable || !dragged) return null
56
+ val lowestTop = lowestSheetTop?.invoke() ?: return null
57
+ if (child.top < lowestTop) return null
58
+ val hides = if (abs(velocityY) > DISMISS_VELOCITY_DP.dpToPx()) {
59
+ velocityY > 0
60
+ } else {
61
+ child.top - lowestTop > DISMISS_DISTANCE_DP.dpToPx()
62
+ }
63
+ return if (hides && isHideable) STATE_HIDDEN else STATE_COLLAPSED
64
+ }
65
+
66
+ override fun onStartNestedScroll(
67
+ coordinatorLayout: CoordinatorLayout,
68
+ child: V,
69
+ directTargetChild: View,
70
+ target: View,
71
+ axes: Int,
72
+ type: Int
73
+ ): Boolean {
74
+ if (type == ViewCompat.TYPE_TOUCH) {
75
+ nestedDragged = false
76
+ nestedReleaseVelocity = 0f
77
+ }
78
+ return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
79
+ }
9
80
 
10
81
  override fun onNestedPreScroll(
11
82
  coordinatorLayout: CoordinatorLayout,
@@ -20,6 +91,13 @@ class TrueSheetBottomSheetBehavior<V : View> : BottomSheetBehavior<V>() {
20
91
  // Block expansion from scroll, but allow if sheet is already being dragged
21
92
  if (!scrollingExpandsSheet && dy > 0 && state != STATE_DRAGGING) return
22
93
  super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
94
+ if (type == ViewCompat.TYPE_TOUCH) {
95
+ if (state == STATE_EXPANDED) {
96
+ nestedDragged = false
97
+ } else if (consumed[1] != 0) {
98
+ nestedDragged = true
99
+ }
100
+ }
23
101
  }
24
102
 
25
103
  override fun onNestedPreFling(
@@ -29,8 +107,65 @@ class TrueSheetBottomSheetBehavior<V : View> : BottomSheetBehavior<V>() {
29
107
  velocityX: Float,
30
108
  velocityY: Float
31
109
  ): Boolean {
110
+ nestedReleaseVelocity = -velocityY
111
+ if (releaseState(child, nestedDragged, nestedReleaseVelocity) != null) return true
32
112
  // Don't consume flings — let the ScrollView decelerate naturally
33
113
  if (!scrollingExpandsSheet) return false
34
114
  return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY)
35
115
  }
116
+
117
+ override fun onStopNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, type: Int) {
118
+ if (type == ViewCompat.TYPE_TOUCH) {
119
+ releaseState(child, nestedDragged, nestedReleaseVelocity)?.let {
120
+ nestedDragged = false
121
+ state = it
122
+ return
123
+ }
124
+ }
125
+ super.onStopNestedScroll(coordinatorLayout, child, target, type)
126
+ }
127
+
128
+ private fun trackTouch(event: MotionEvent) {
129
+ if (dismissThreshold != TrueSheetDismissThreshold.SHORT) return
130
+ if (event.eventTime == trackedEventTime && event.actionMasked == trackedAction) return
131
+ trackedEventTime = event.eventTime
132
+ trackedAction = event.actionMasked
133
+ if (event.actionMasked == MotionEvent.ACTION_DOWN) {
134
+ velocityTracker?.recycle()
135
+ velocityTracker = VelocityTracker.obtain()
136
+ touchPointerId = event.getPointerId(0)
137
+ }
138
+ velocityTracker?.addMovement(event)
139
+ }
140
+
141
+ override fun onInterceptTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean {
142
+ trackTouch(event)
143
+ return super.onInterceptTouchEvent(parent, child, event)
144
+ }
145
+
146
+ override fun onTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean {
147
+ trackTouch(event)
148
+ val wasDragging = state == STATE_DRAGGING
149
+ val handled = super.onTouchEvent(parent, child, event)
150
+ if (event.actionMasked == MotionEvent.ACTION_UP) {
151
+ val velocityY = velocityTracker?.run {
152
+ computeCurrentVelocity(1000)
153
+ getYVelocity(touchPointerId)
154
+ } ?: 0f
155
+ releaseState(child, wasDragging, velocityY)?.let { state = it }
156
+ }
157
+ if (event.actionMasked == MotionEvent.ACTION_UP || event.actionMasked == MotionEvent.ACTION_CANCEL) {
158
+ velocityTracker?.recycle()
159
+ velocityTracker = null
160
+ }
161
+ return handled
162
+ }
163
+
164
+ companion object {
165
+ private const val HIDE_FRICTION = 0.3f
166
+
167
+ // Compose Material3 SheetState's velocity and positional thresholds
168
+ private const val DISMISS_VELOCITY_DP = 125f
169
+ private const val DISMISS_DISTANCE_DP = 56f
170
+ }
36
171
  }
@@ -1,7 +1,6 @@
1
1
  package com.lodev09.truesheet.core
2
2
 
3
3
  import android.annotation.SuppressLint
4
- import android.content.res.Configuration
5
4
  import android.graphics.Color
6
5
  import android.graphics.Outline
7
6
  import android.graphics.Rect
@@ -18,8 +17,9 @@ import androidx.core.view.ViewCompat
18
17
  import com.facebook.react.uimanager.PixelUtil.dpToPx
19
18
  import com.facebook.react.uimanager.ThemedReactContext
20
19
  import com.google.android.material.bottomsheet.BottomSheetBehavior
21
- import com.lodev09.truesheet.TrueSheetAnchor
20
+ import com.lodev09.truesheet.TrueSheetPlacement
22
21
  import com.lodev09.truesheet.utils.ScreenUtils
22
+ import com.lodev09.truesheet.utils.isDarkMode
23
23
 
24
24
  interface TrueSheetBottomSheetViewDelegate {
25
25
  val isTopmostSheet: Boolean
@@ -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?
@@ -81,26 +81,24 @@ class TrueSheetBottomSheetView(private val reactContext: ThemedReactContext) : F
81
81
  clipToPadding = false
82
82
  }
83
83
 
84
- override fun setTranslationY(translationY: Float) {
85
- // This prevents keyboard inset animations from resetting parent sheet translation
86
- if (translationY == 0f && this.translationY != 0f) {
87
- return
88
- }
89
- super.setTranslationY(translationY)
90
- }
91
-
92
84
  // =============================================================================
93
85
  // MARK: - Layout
94
86
  // =============================================================================
95
87
 
96
88
  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
89
+ val placement = if (ScreenUtils.isPortraitPhone(reactContext)) null else delegate?.placement
90
+ val gravity = when (placement) {
91
+ TrueSheetPlacement.LEADING -> Gravity.START
92
+ TrueSheetPlacement.TRAILING -> Gravity.END
101
93
  else -> Gravity.CENTER_HORIZONTAL
102
94
  } or Gravity.BOTTOM
103
- val margin = if (anchor == TrueSheetAnchor.LEFT || anchor == TrueSheetAnchor.RIGHT) delegate?.anchorOffset ?: 0 else 0
95
+ val margin = if (placement == TrueSheetPlacement.LEADING ||
96
+ placement == TrueSheetPlacement.TRAILING
97
+ ) {
98
+ delegate?.placementOffset ?: 0
99
+ } else {
100
+ 0
101
+ }
104
102
  return Pair(gravity, margin)
105
103
  }
106
104
 
@@ -173,14 +171,16 @@ class TrueSheetBottomSheetView(private val reactContext: ThemedReactContext) : F
173
171
  clipToOutline = true
174
172
  }
175
173
 
176
- private fun getDefaultBackgroundColor(): Int {
177
- val isNight = (
178
- reactContext.resources.configuration.uiMode and
179
- Configuration.UI_MODE_NIGHT_MASK
180
- ) == Configuration.UI_MODE_NIGHT_YES
181
- return if (isNight) COLOR_SURFACE_CONTAINER_LOW_DARK else COLOR_SURFACE_CONTAINER_LOW_LIGHT
174
+ fun updateBackgroundColor(color: Int) {
175
+ (background as? ShapeDrawable)?.let {
176
+ it.paint.color = color
177
+ it.invalidateSelf()
178
+ }
182
179
  }
183
180
 
181
+ fun getDefaultBackgroundColor(): Int =
182
+ if (reactContext.isDarkMode) COLOR_SURFACE_CONTAINER_LOW_DARK else COLOR_SURFACE_CONTAINER_LOW_LIGHT
183
+
184
184
  fun setupElevation() {
185
185
  val value = delegate?.sheetElevation ?: DEFAULT_ELEVATION
186
186
  val effectiveElevation = if (value < 0) DEFAULT_ELEVATION else value