@lodev09/react-native-true-sheet 4.0.0-beta.0 → 4.0.0-beta.1

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 (367) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +42 -31
  3. package/RNTrueSheet.podspec +10 -2
  4. package/android/build.gradle +2 -1
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +174 -49
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerViewManager.kt +7 -0
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +371 -18
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +14 -0
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +150 -21
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterViewManager.kt +14 -0
  11. package/android/src/main/java/com/lodev09/truesheet/TrueSheetHeaderView.kt +38 -0
  12. package/android/src/main/java/com/lodev09/truesheet/TrueSheetHeaderViewManager.kt +28 -0
  13. package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +84 -24
  14. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +5 -4
  15. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPeekView.kt +72 -0
  16. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPeekViewManager.kt +33 -0
  17. package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +45 -0
  18. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +526 -231
  19. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +1217 -603
  20. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +136 -29
  21. package/android/src/main/java/com/lodev09/truesheet/core/RNScreensEventObserver.kt +63 -0
  22. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetBehavior.kt +36 -0
  23. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +255 -0
  24. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +303 -0
  25. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +277 -0
  26. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +162 -0
  27. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetGrabberView.kt +180 -0
  28. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetKeyboardObserver.kt +207 -0
  29. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +220 -0
  30. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetDragEvents.kt +71 -0
  31. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetFocusEvents.kt +65 -0
  32. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +112 -0
  33. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetStateEvents.kt +56 -0
  34. package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +84 -0
  35. package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +93 -67
  36. package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +30 -0
  37. package/android/src/main/jni/CMakeLists.txt +63 -0
  38. package/android/src/main/jni/TrueSheetSpec.h +19 -0
  39. package/android/src/main/jni/TrueSheetStateUpdater.cpp +96 -0
  40. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +24 -0
  41. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +43 -0
  42. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +28 -0
  43. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +11 -0
  44. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +37 -0
  45. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +24 -0
  46. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +56 -0
  47. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +28 -0
  48. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +11 -0
  49. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +41 -0
  50. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +18 -0
  51. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +16 -0
  52. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +28 -0
  53. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +61 -0
  54. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +45 -0
  55. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +23 -0
  56. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +52 -0
  57. package/ios/TrueSheetContainerView.h +81 -6
  58. package/ios/TrueSheetContainerView.mm +275 -21
  59. package/ios/TrueSheetContentView.h +38 -1
  60. package/ios/TrueSheetContentView.mm +413 -43
  61. package/ios/TrueSheetFooterView.h +27 -2
  62. package/ios/TrueSheetFooterView.mm +141 -35
  63. package/ios/{events/OnDidDismissEvent.h → TrueSheetHeaderView.h} +10 -7
  64. package/ios/TrueSheetHeaderView.mm +64 -0
  65. package/ios/TrueSheetModule.mm +92 -5
  66. package/ios/TrueSheetPeekView.h +32 -0
  67. package/ios/TrueSheetPeekView.mm +99 -0
  68. package/ios/TrueSheetView.h +7 -15
  69. package/ios/TrueSheetView.mm +633 -215
  70. package/ios/TrueSheetViewController.h +84 -16
  71. package/ios/TrueSheetViewController.mm +1136 -333
  72. package/ios/core/RNScreensEventObserver.h +46 -0
  73. package/ios/core/RNScreensEventObserver.mm +280 -0
  74. package/ios/core/TrueSheetBlurView.h +27 -0
  75. package/ios/core/TrueSheetBlurView.mm +82 -0
  76. package/ios/core/TrueSheetDetentCalculator.h +107 -0
  77. package/ios/core/TrueSheetDetentCalculator.mm +197 -0
  78. package/ios/core/TrueSheetGrabberView.h +72 -0
  79. package/ios/core/TrueSheetGrabberView.mm +178 -0
  80. package/ios/core/TrueSheetKeyboardObserver.h +39 -0
  81. package/ios/core/TrueSheetKeyboardObserver.mm +112 -0
  82. package/ios/events/TrueSheetDragEvents.h +39 -0
  83. package/ios/events/TrueSheetDragEvents.mm +62 -0
  84. package/ios/events/{OnPositionChangeEvent.h → TrueSheetFocusEvents.h} +8 -5
  85. package/ios/events/TrueSheetFocusEvents.mm +49 -0
  86. package/ios/events/TrueSheetLifecycleEvents.h +40 -0
  87. package/ios/events/TrueSheetLifecycleEvents.mm +71 -0
  88. package/ios/events/TrueSheetStateEvents.h +35 -0
  89. package/ios/events/TrueSheetStateEvents.mm +49 -0
  90. package/ios/tvos/TrueSheetStubs.mm +154 -0
  91. package/ios/{events/OnMountEvent.h → utils/BlurUtil.h} +5 -7
  92. package/ios/utils/BlurUtil.mm +69 -0
  93. package/ios/utils/GestureUtil.h +7 -0
  94. package/ios/utils/GestureUtil.mm +12 -0
  95. package/ios/utils/PlatformUtil.h +15 -0
  96. package/ios/utils/UIView+FirstResponder.h +15 -0
  97. package/ios/utils/UIView+FirstResponder.mm +26 -0
  98. package/ios/{events/OnWillDismissEvent.h → utils/UIView+ScrollEdgeInteraction.h} +6 -7
  99. package/ios/utils/UIView+ScrollEdgeInteraction.mm +62 -0
  100. package/ios/utils/WindowUtil.mm +17 -1
  101. package/lib/module/TrueSheet.js +224 -64
  102. package/lib/module/TrueSheet.js.map +1 -1
  103. package/lib/module/TrueSheet.web.js +1201 -0
  104. package/lib/module/TrueSheet.web.js.map +1 -0
  105. package/lib/module/TrueSheetPeek.js +15 -0
  106. package/lib/module/TrueSheetPeek.js.map +1 -0
  107. package/lib/module/TrueSheetPeek.web.js +55 -0
  108. package/lib/module/TrueSheetPeek.web.js.map +1 -0
  109. package/lib/module/TrueSheetProvider.js +28 -0
  110. package/lib/module/TrueSheetProvider.js.map +1 -0
  111. package/lib/module/TrueSheetProvider.web.js +157 -0
  112. package/lib/module/TrueSheetProvider.web.js.map +1 -0
  113. package/lib/module/fabric/TrueSheetContainerViewNativeComponent.ts +1 -1
  114. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +5 -1
  115. package/lib/module/fabric/TrueSheetFooterViewNativeComponent.ts +10 -2
  116. package/lib/module/fabric/TrueSheetHeaderViewNativeComponent.ts +8 -0
  117. package/lib/module/fabric/TrueSheetPeekViewNativeComponent.ts +8 -0
  118. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +105 -18
  119. package/lib/module/index.js +3 -3
  120. package/lib/module/index.js.map +1 -1
  121. package/lib/module/mocks/index.js +93 -0
  122. package/lib/module/mocks/index.js.map +1 -0
  123. package/lib/module/mocks/navigation.js +85 -0
  124. package/lib/module/mocks/navigation.js.map +1 -0
  125. package/lib/module/mocks/reanimated.js +91 -0
  126. package/lib/module/mocks/reanimated.js.map +1 -0
  127. package/lib/module/navigation/TrueSheetRouter.js +170 -0
  128. package/lib/module/navigation/TrueSheetRouter.js.map +1 -0
  129. package/lib/module/navigation/TrueSheetView.js +71 -0
  130. package/lib/module/navigation/TrueSheetView.js.map +1 -0
  131. package/lib/module/navigation/createTrueSheetNavigator.js +63 -0
  132. package/lib/module/navigation/createTrueSheetNavigator.js.map +1 -0
  133. package/lib/module/navigation/index.js +6 -0
  134. package/lib/module/navigation/index.js.map +1 -0
  135. package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js +56 -0
  136. package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js.map +1 -0
  137. package/lib/module/navigation/screen/TrueSheetScreen.js +49 -0
  138. package/lib/module/navigation/screen/TrueSheetScreen.js.map +1 -0
  139. package/lib/module/navigation/screen/index.js +5 -0
  140. package/lib/module/navigation/screen/index.js.map +1 -0
  141. package/lib/module/navigation/screen/types.js +4 -0
  142. package/lib/module/navigation/screen/types.js.map +1 -0
  143. package/lib/module/navigation/screen/useSheetScreenState.js +68 -0
  144. package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -0
  145. package/lib/module/navigation/types.js +4 -0
  146. package/lib/module/navigation/types.js.map +1 -0
  147. package/lib/module/navigation/useTrueSheetNavigation.js +24 -0
  148. package/lib/module/navigation/useTrueSheetNavigation.js.map +1 -0
  149. package/lib/module/reanimated/ReanimatedTrueSheet.js +15 -9
  150. package/lib/module/reanimated/ReanimatedTrueSheet.js.map +1 -1
  151. package/lib/module/reanimated/ReanimatedTrueSheet.web.js +81 -0
  152. package/lib/module/reanimated/ReanimatedTrueSheet.web.js.map +1 -0
  153. package/lib/module/reanimated/ReanimatedTrueSheetProvider.js +4 -2
  154. package/lib/module/reanimated/ReanimatedTrueSheetProvider.js.map +1 -1
  155. package/lib/module/reanimated/index.js +2 -2
  156. package/lib/module/reanimated/index.js.map +1 -1
  157. package/lib/module/reanimated/useReanimatedPositionChangeHandler.web.js +20 -0
  158. package/lib/module/reanimated/useReanimatedPositionChangeHandler.web.js.map +1 -0
  159. package/lib/module/specs/NativeTrueSheetModule.js.map +1 -1
  160. package/lib/module/web/constants.js +16 -0
  161. package/lib/module/web/constants.js.map +1 -0
  162. package/lib/module/web/vaul/README.md +9 -0
  163. package/lib/module/web/vaul/browser.js +30 -0
  164. package/lib/module/web/vaul/browser.js.map +1 -0
  165. package/lib/module/web/vaul/constants.js +15 -0
  166. package/lib/module/web/vaul/constants.js.map +1 -0
  167. package/lib/module/web/vaul/context.js +59 -0
  168. package/lib/module/web/vaul/context.js.map +1 -0
  169. package/lib/module/web/vaul/helpers.js +93 -0
  170. package/lib/module/web/vaul/helpers.js.map +1 -0
  171. package/lib/module/web/vaul/index.js +1309 -0
  172. package/lib/module/web/vaul/index.js.map +1 -0
  173. package/lib/module/web/vaul/style.css +287 -0
  174. package/lib/module/web/vaul/style.css.d.js +2 -0
  175. package/lib/module/web/vaul/style.css.d.js.map +1 -0
  176. package/lib/module/web/vaul/types.js +2 -0
  177. package/lib/module/web/vaul/types.js.map +1 -0
  178. package/lib/module/web/vaul/use-composed-refs.js +34 -0
  179. package/lib/module/web/vaul/use-composed-refs.js.map +1 -0
  180. package/lib/module/web/vaul/use-controllable-state.js +54 -0
  181. package/lib/module/web/vaul/use-controllable-state.js.map +1 -0
  182. package/lib/module/web/vaul/use-position-fixed.js +123 -0
  183. package/lib/module/web/vaul/use-position-fixed.js.map +1 -0
  184. package/lib/module/web/vaul/use-prevent-scroll.js +258 -0
  185. package/lib/module/web/vaul/use-prevent-scroll.js.map +1 -0
  186. package/lib/module/web/vaul/use-scale-background.js +57 -0
  187. package/lib/module/web/vaul/use-scale-background.js.map +1 -0
  188. package/lib/module/web/vaul/use-snap-points.js +305 -0
  189. package/lib/module/web/vaul/use-snap-points.js.map +1 -0
  190. package/lib/typescript/src/TrueSheet.d.ts +51 -14
  191. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  192. package/lib/typescript/src/TrueSheet.types.d.ts +463 -54
  193. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  194. package/lib/typescript/src/TrueSheet.web.d.ts +5 -0
  195. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -0
  196. package/lib/typescript/src/TrueSheetPeek.d.ts +9 -0
  197. package/lib/typescript/src/TrueSheetPeek.d.ts.map +1 -0
  198. package/lib/typescript/src/TrueSheetPeek.web.d.ts +28 -0
  199. package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -0
  200. package/lib/typescript/src/TrueSheetProvider.d.ts +18 -0
  201. package/lib/typescript/src/TrueSheetProvider.d.ts.map +1 -0
  202. package/lib/typescript/src/TrueSheetProvider.web.d.ts +27 -0
  203. package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -0
  204. package/lib/typescript/src/fabric/TrueSheetContainerViewNativeComponent.d.ts.map +1 -1
  205. package/lib/typescript/src/fabric/TrueSheetContentViewNativeComponent.d.ts.map +1 -1
  206. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts +2 -0
  207. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts.map +1 -1
  208. package/lib/typescript/src/fabric/TrueSheetHeaderViewNativeComponent.d.ts +6 -0
  209. package/lib/typescript/src/fabric/TrueSheetHeaderViewNativeComponent.d.ts.map +1 -0
  210. package/lib/typescript/src/fabric/TrueSheetPeekViewNativeComponent.d.ts +6 -0
  211. package/lib/typescript/src/fabric/TrueSheetPeekViewNativeComponent.d.ts.map +1 -0
  212. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +67 -16
  213. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  214. package/lib/typescript/src/index.d.ts +2 -2
  215. package/lib/typescript/src/index.d.ts.map +1 -1
  216. package/lib/typescript/src/mocks/index.d.ts +43 -0
  217. package/lib/typescript/src/mocks/index.d.ts.map +1 -0
  218. package/lib/typescript/src/mocks/navigation.d.ts +66 -0
  219. package/lib/typescript/src/mocks/navigation.d.ts.map +1 -0
  220. package/lib/typescript/src/mocks/reanimated.d.ts +48 -0
  221. package/lib/typescript/src/mocks/reanimated.d.ts.map +1 -0
  222. package/lib/typescript/src/navigation/TrueSheetRouter.d.ts +57 -0
  223. package/lib/typescript/src/navigation/TrueSheetRouter.d.ts.map +1 -0
  224. package/lib/typescript/src/navigation/TrueSheetView.d.ts +10 -0
  225. package/lib/typescript/src/navigation/TrueSheetView.d.ts.map +1 -0
  226. package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts +35 -0
  227. package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts.map +1 -0
  228. package/lib/typescript/src/navigation/index.d.ts +6 -0
  229. package/lib/typescript/src/navigation/index.d.ts.map +1 -0
  230. package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts +3 -0
  231. package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts.map +1 -0
  232. package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts +3 -0
  233. package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts.map +1 -0
  234. package/lib/typescript/src/navigation/screen/index.d.ts +4 -0
  235. package/lib/typescript/src/navigation/screen/index.d.ts.map +1 -0
  236. package/lib/typescript/src/navigation/screen/types.d.ts +15 -0
  237. package/lib/typescript/src/navigation/screen/types.d.ts.map +1 -0
  238. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +35 -0
  239. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -0
  240. package/lib/typescript/src/navigation/types.d.ts +153 -0
  241. package/lib/typescript/src/navigation/types.d.ts.map +1 -0
  242. package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts +23 -0
  243. package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts.map +1 -0
  244. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.d.ts.map +1 -1
  245. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.web.d.ts +41 -0
  246. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.web.d.ts.map +1 -0
  247. package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts +8 -2
  248. package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts.map +1 -1
  249. package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.web.d.ts +15 -0
  250. package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.web.d.ts.map +1 -0
  251. package/lib/typescript/src/specs/NativeTrueSheetModule.d.ts +25 -3
  252. package/lib/typescript/src/specs/NativeTrueSheetModule.d.ts.map +1 -1
  253. package/lib/typescript/src/web/constants.d.ts +14 -0
  254. package/lib/typescript/src/web/constants.d.ts.map +1 -0
  255. package/lib/typescript/src/web/vaul/browser.d.ts +8 -0
  256. package/lib/typescript/src/web/vaul/browser.d.ts.map +1 -0
  257. package/lib/typescript/src/web/vaul/constants.d.ts +13 -0
  258. package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -0
  259. package/lib/typescript/src/web/vaul/context.d.ts +53 -0
  260. package/lib/typescript/src/web/vaul/context.d.ts.map +1 -0
  261. package/lib/typescript/src/web/vaul/helpers.d.ts +17 -0
  262. package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -0
  263. package/lib/typescript/src/web/vaul/index.d.ts +228 -0
  264. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -0
  265. package/lib/typescript/src/web/vaul/types.d.ts +7 -0
  266. package/lib/typescript/src/web/vaul/types.d.ts.map +1 -0
  267. package/lib/typescript/src/web/vaul/use-composed-refs.d.ts +14 -0
  268. package/lib/typescript/src/web/vaul/use-composed-refs.d.ts.map +1 -0
  269. package/lib/typescript/src/web/vaul/use-controllable-state.d.ts +9 -0
  270. package/lib/typescript/src/web/vaul/use-controllable-state.d.ts.map +1 -0
  271. package/lib/typescript/src/web/vaul/use-position-fixed.d.ts +19 -0
  272. package/lib/typescript/src/web/vaul/use-position-fixed.d.ts.map +1 -0
  273. package/lib/typescript/src/web/vaul/use-prevent-scroll.d.ts +18 -0
  274. package/lib/typescript/src/web/vaul/use-prevent-scroll.d.ts.map +1 -0
  275. package/lib/typescript/src/web/vaul/use-scale-background.d.ts +2 -0
  276. package/lib/typescript/src/web/vaul/use-scale-background.d.ts.map +1 -0
  277. package/lib/typescript/src/web/vaul/use-snap-points.d.ts +38 -0
  278. package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -0
  279. package/package.json +78 -34
  280. package/react-native.config.js +9 -2
  281. package/src/TrueSheet.tsx +276 -60
  282. package/src/TrueSheet.types.ts +525 -71
  283. package/src/TrueSheet.web.tsx +1366 -0
  284. package/src/TrueSheetPeek.tsx +11 -0
  285. package/src/TrueSheetPeek.web.tsx +63 -0
  286. package/src/TrueSheetProvider.tsx +33 -0
  287. package/src/TrueSheetProvider.web.tsx +220 -0
  288. package/src/fabric/TrueSheetContainerViewNativeComponent.ts +1 -1
  289. package/src/fabric/TrueSheetContentViewNativeComponent.ts +5 -1
  290. package/src/fabric/TrueSheetFooterViewNativeComponent.ts +10 -2
  291. package/src/fabric/TrueSheetHeaderViewNativeComponent.ts +8 -0
  292. package/src/fabric/TrueSheetPeekViewNativeComponent.ts +8 -0
  293. package/src/fabric/TrueSheetViewNativeComponent.ts +105 -18
  294. package/src/index.ts +2 -2
  295. package/src/mocks/index.ts +89 -0
  296. package/src/mocks/navigation.ts +72 -0
  297. package/src/mocks/reanimated.ts +101 -0
  298. package/src/navigation/TrueSheetRouter.ts +234 -0
  299. package/src/navigation/TrueSheetView.tsx +99 -0
  300. package/src/navigation/createTrueSheetNavigator.tsx +93 -0
  301. package/src/navigation/index.ts +14 -0
  302. package/src/navigation/screen/ReanimatedTrueSheetScreen.tsx +61 -0
  303. package/src/navigation/screen/TrueSheetScreen.tsx +54 -0
  304. package/src/navigation/screen/index.ts +3 -0
  305. package/src/navigation/screen/types.ts +21 -0
  306. package/src/navigation/screen/useSheetScreenState.ts +107 -0
  307. package/src/navigation/types.ts +219 -0
  308. package/src/navigation/useTrueSheetNavigation.ts +26 -0
  309. package/src/reanimated/ReanimatedTrueSheet.tsx +12 -7
  310. package/src/reanimated/ReanimatedTrueSheet.web.tsx +75 -0
  311. package/src/reanimated/ReanimatedTrueSheetProvider.tsx +12 -8
  312. package/src/reanimated/useReanimatedPositionChangeHandler.web.ts +31 -0
  313. package/src/specs/NativeTrueSheetModule.ts +28 -3
  314. package/src/web/constants.ts +13 -0
  315. package/src/web/vaul/README.md +9 -0
  316. package/src/web/vaul/browser.ts +38 -0
  317. package/src/web/vaul/constants.ts +20 -0
  318. package/src/web/vaul/context.ts +101 -0
  319. package/src/web/vaul/helpers.ts +124 -0
  320. package/src/web/vaul/index.tsx +1740 -0
  321. package/src/web/vaul/style.css +287 -0
  322. package/src/web/vaul/style.css.d.ts +1 -0
  323. package/src/web/vaul/types.ts +7 -0
  324. package/src/web/vaul/use-composed-refs.ts +35 -0
  325. package/src/web/vaul/use-controllable-state.ts +66 -0
  326. package/src/web/vaul/use-position-fixed.ts +147 -0
  327. package/src/web/vaul/use-prevent-scroll.ts +309 -0
  328. package/src/web/vaul/use-scale-background.ts +64 -0
  329. package/src/web/vaul/use-snap-points.ts +455 -0
  330. package/android/src/main/java/com/lodev09/truesheet/events/DetentChangeEvent.kt +0 -26
  331. package/android/src/main/java/com/lodev09/truesheet/events/DidDismissEvent.kt +0 -20
  332. package/android/src/main/java/com/lodev09/truesheet/events/DidPresentEvent.kt +0 -26
  333. package/android/src/main/java/com/lodev09/truesheet/events/DragBeginEvent.kt +0 -26
  334. package/android/src/main/java/com/lodev09/truesheet/events/DragChangeEvent.kt +0 -26
  335. package/android/src/main/java/com/lodev09/truesheet/events/DragEndEvent.kt +0 -26
  336. package/android/src/main/java/com/lodev09/truesheet/events/MountEvent.kt +0 -20
  337. package/android/src/main/java/com/lodev09/truesheet/events/PositionChangeEvent.kt +0 -32
  338. package/android/src/main/java/com/lodev09/truesheet/events/SizeChangeEvent.kt +0 -27
  339. package/android/src/main/java/com/lodev09/truesheet/events/WillDismissEvent.kt +0 -20
  340. package/android/src/main/java/com/lodev09/truesheet/events/WillPresentEvent.kt +0 -26
  341. package/android/src/main/res/values/styles.xml +0 -8
  342. package/ios/events/OnDetentChangeEvent.h +0 -28
  343. package/ios/events/OnDetentChangeEvent.mm +0 -30
  344. package/ios/events/OnDidDismissEvent.mm +0 -25
  345. package/ios/events/OnDidPresentEvent.h +0 -28
  346. package/ios/events/OnDidPresentEvent.mm +0 -30
  347. package/ios/events/OnDragBeginEvent.h +0 -28
  348. package/ios/events/OnDragBeginEvent.mm +0 -30
  349. package/ios/events/OnDragChangeEvent.h +0 -28
  350. package/ios/events/OnDragChangeEvent.mm +0 -30
  351. package/ios/events/OnDragEndEvent.h +0 -28
  352. package/ios/events/OnDragEndEvent.mm +0 -30
  353. package/ios/events/OnMountEvent.mm +0 -25
  354. package/ios/events/OnPositionChangeEvent.mm +0 -32
  355. package/ios/events/OnSizeChangeEvent.h +0 -28
  356. package/ios/events/OnSizeChangeEvent.mm +0 -30
  357. package/ios/events/OnWillDismissEvent.mm +0 -25
  358. package/ios/events/OnWillPresentEvent.h +0 -28
  359. package/ios/events/OnWillPresentEvent.mm +0 -30
  360. package/ios/utils/LayoutUtil.h +0 -44
  361. package/ios/utils/LayoutUtil.mm +0 -50
  362. package/lib/module/TrueSheetGrabber.js +0 -51
  363. package/lib/module/TrueSheetGrabber.js.map +0 -1
  364. package/lib/typescript/src/TrueSheetGrabber.d.ts +0 -39
  365. package/lib/typescript/src/TrueSheetGrabber.d.ts.map +0 -1
  366. package/src/TrueSheetGrabber.tsx +0 -82
  367. package/src/__mocks__/index.js +0 -67
@@ -0,0 +1,303 @@
1
+ package com.lodev09.truesheet.core
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.content.Context
5
+ import android.content.res.Configuration
6
+ import android.view.MotionEvent
7
+ import android.view.View
8
+ import android.view.ViewConfiguration
9
+ import android.view.ViewGroup
10
+ import android.widget.EditText
11
+ import androidx.coordinatorlayout.widget.CoordinatorLayout
12
+ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
13
+ import com.facebook.react.uimanager.PointerEvents
14
+ import com.facebook.react.uimanager.ReactPointerEventsView
15
+ import com.facebook.react.uimanager.TouchTargetHelper
16
+ import com.lodev09.truesheet.utils.isDescendantOf
17
+
18
+ interface TrueSheetCoordinatorLayoutDelegate {
19
+ fun coordinatorLayoutDidLayout(changed: Boolean)
20
+ fun coordinatorLayoutDidChangeConfiguration()
21
+ fun findScrollView(): ViewGroup?
22
+ fun findSheetView(): TrueSheetBottomSheetView?
23
+ }
24
+
25
+ /**
26
+ * Custom CoordinatorLayout that hosts the bottom sheet and dim view.
27
+ * Implements ReactPointerEventsView to allow touch events to pass through
28
+ * to underlying React Native views when appropriate.
29
+ */
30
+ @SuppressLint("ViewConstructor")
31
+ class TrueSheetCoordinatorLayout(context: Context) :
32
+ CoordinatorLayout(context),
33
+ ReactPointerEventsView {
34
+
35
+ var delegate: TrueSheetCoordinatorLayoutDelegate? = null
36
+
37
+ private val touchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop
38
+ private var dragging = false
39
+ private var initialY = 0f
40
+ private var activePointerId = 0
41
+
42
+ // Horizontal-dominance tracking. iOS lets horizontal child gestures win over
43
+ // the sheet's vertical pan; we mirror that by locking out BottomSheetBehavior
44
+ // interception once the first significant movement of a stream is horizontal.
45
+ private var streamInitialX = 0f
46
+ private var streamInitialY = 0f
47
+ private var streamHorizontalLocked = false
48
+ private var streamDirectionDecided = false
49
+
50
+ // Native-gesture-claim tracking. RNGH handlers activate past BottomSheetBehavior's
51
+ // drag slop and never call requestDisallowInterceptTouchEvent, so the sheet would
52
+ // steal vertical gestures (e.g. a vertical pan) before they can activate. While the
53
+ // touch is inside a descendant GestureHandlerRootView we hold off interception within
54
+ // a grace distance; once a child claims the gesture (relayed from the RootView impls
55
+ // via childDidClaimNativeGesture) we yield the rest of the stream, mirroring iOS.
56
+ private var streamHasGestureRoot = false
57
+ private var streamGestureClaimed = false
58
+
59
+ // 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
61
+ // nothing vertically scrollable along its chain. Such targets consume the stream
62
+ // natively (no nested scroll), and BottomSheetBehavior won't intercept because the
63
+ // scrollable's bounds still contain the point (touchingScrollingChild) — intercept
64
+ // manually.
65
+ private var streamSheetDraggable = false
66
+ private var streamDraggableEditText = false
67
+
68
+ init {
69
+ layoutParams = LayoutParams(
70
+ LayoutParams.MATCH_PARENT,
71
+ LayoutParams.MATCH_PARENT
72
+ )
73
+
74
+ clipChildren = false
75
+ clipToPadding = false
76
+ }
77
+
78
+ override fun onLayout(
79
+ changed: Boolean,
80
+ l: Int,
81
+ t: Int,
82
+ r: Int,
83
+ b: Int
84
+ ) {
85
+ super.onLayout(changed, l, t, r, b)
86
+ delegate?.coordinatorLayoutDidLayout(changed)
87
+ }
88
+
89
+ override fun onConfigurationChanged(newConfig: Configuration?) {
90
+ super.onConfigurationChanged(newConfig)
91
+ delegate?.coordinatorLayoutDidChangeConfiguration()
92
+ }
93
+
94
+ override val pointerEvents: PointerEvents
95
+ get() = PointerEvents.BOX_NONE
96
+
97
+ /**
98
+ * Clears stale `nestedScrollingChildRef` from BottomSheetBehavior.
99
+ *
100
+ * The cached `nestedScrollingChildRef` can become stale when a child sheet
101
+ * with a ScrollView is dismissed and its ScrollView returns to this hierarchy.
102
+ */
103
+ private fun clearStaleNestedScrollingChildRef() {
104
+ val sheet = delegate?.findSheetView() ?: return
105
+ val behavior = sheet.behavior ?: return
106
+ try {
107
+ val field = behavior.javaClass.superclass.getDeclaredField("nestedScrollingChildRef")
108
+ field.isAccessible = true
109
+ @Suppress("UNCHECKED_CAST")
110
+ val ref = field.get(behavior) as? java.lang.ref.WeakReference<android.view.View> ?: return
111
+ val view = ref.get()
112
+ if (view == null || !view.isDescendantOf(sheet)) {
113
+ ref.clear()
114
+ }
115
+ } catch (_: Exception) {}
116
+ }
117
+
118
+ override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
119
+ // RN's ReactEditText fires this(true) on ACTION_DOWN, killing sheet drag over an input.
120
+ // Swallow only that case — nothing between the touched EditText and the sheet scrolls
121
+ // (an overflowing multiline EditText or an input inside a scrollable that can actually
122
+ // scroll still scrolls). Other children (maps, sliders, scrollables) keep the standard
123
+ // disallow contract.
124
+ if (disallowIntercept && streamDraggableEditText) return
125
+ super.requestDisallowInterceptTouchEvent(disallowIntercept)
126
+ }
127
+
128
+ /**
129
+ * 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
131
+ * scrollable, and nothing along the target's chain scrolls vertically.
132
+ */
133
+ private fun updateStreamTargetFlags(ev: MotionEvent) {
134
+ streamSheetDraggable = false
135
+ streamDraggableEditText = false
136
+
137
+ val sheet = delegate?.findSheetView() ?: return
138
+ val target = TouchTargetHelper.findTargetPathAndCoordinatesForTouch(ev.x, ev.y, this, FloatArray(2))
139
+ .firstNotNullOfOrNull { it.getView() } ?: return
140
+ if (target !== sheet && !target.isDescendantOf(sheet)) return
141
+
142
+ // A pinned scrollable that can scroll (or hosts pull-to-refresh) owns its
143
+ // touches — nested scrolling drags the sheet. When it can't scroll (e.g.
144
+ // the sheet is fully expanded and the content fits) it eats the gesture
145
+ // instead, so fall through and let the sheet drag from it — an EditText
146
+ // target needs the disallow-intercept swallow or its stream dies.
147
+ val scrollView = delegate?.findScrollView()
148
+ if (scrollView != null && (target === scrollView || target.isDescendantOf(scrollView))) {
149
+ val hasRefreshControl = scrollView.parent is SwipeRefreshLayout
150
+ if (hasRefreshControl || scrollView.scrollY > 0 || scrollView.canScrollVertically(1)) return
151
+ }
152
+
153
+ var view: View? = target
154
+ while (view != null && view !== this) {
155
+ if (view.canScrollVertically(1) || view.canScrollVertically(-1)) return
156
+ view = view.parent as? View
157
+ }
158
+
159
+ streamSheetDraggable = true
160
+ streamDraggableEditText = target is EditText
161
+ }
162
+
163
+ private fun findDescendantViewAt(view: View, rawX: Int, rawY: Int, predicate: (View) -> Boolean): View? {
164
+ val loc = IntArray(2)
165
+ view.getLocationOnScreen(loc)
166
+ if (rawX < loc[0] || rawX > loc[0] + view.width || rawY < loc[1] || rawY > loc[1] + view.height) return null
167
+ if (predicate(view)) return view
168
+ if (view is ViewGroup) {
169
+ for (i in 0 until view.childCount) {
170
+ findDescendantViewAt(view.getChildAt(i), rawX, rawY, predicate)?.let { return it }
171
+ }
172
+ }
173
+ return null
174
+ }
175
+
176
+ /**
177
+ * Called by descendant RootView implementations (controller, footer) when a child
178
+ * starts a native gesture — e.g. an RNGH handler activated or a scrollable began
179
+ * scrolling. Yields sheet interception for the rest of the touch stream.
180
+ */
181
+ fun childDidClaimNativeGesture() {
182
+ streamGestureClaimed = true
183
+ }
184
+
185
+ /**
186
+ * Intercepts touch events for ScrollViews that can't scroll (content < viewport),
187
+ * allowing the sheet to be dragged in these cases.
188
+ *
189
+ * TODO: Remove this workaround once NestedScrollView is merged into react-native core.
190
+ * See: https://github.com/facebook/react-native/pull/44099
191
+ */
192
+ override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
193
+ val action = ev.actionMasked
194
+
195
+ if (action == MotionEvent.ACTION_DOWN) {
196
+ clearStaleNestedScrollingChildRef()
197
+
198
+ streamInitialX = ev.rawX
199
+ streamInitialY = ev.rawY
200
+ streamHorizontalLocked = false
201
+ streamDirectionDecided = false
202
+ streamGestureClaimed = false
203
+ streamHasGestureRoot = findDescendantViewAt(this, ev.rawX.toInt(), ev.rawY.toInt()) {
204
+ it.javaClass.name == GESTURE_HANDLER_ROOT_VIEW_CLASS
205
+ } != null
206
+ updateStreamTargetFlags(ev)
207
+ }
208
+
209
+ // Decide gesture direction on first significant movement of the stream.
210
+ // If horizontal wins, lock out BottomSheetBehavior for the rest of the stream
211
+ // so child handlers (carousels, swipe buttons, etc.) can claim the touch.
212
+ if (!streamDirectionDecided && action == MotionEvent.ACTION_MOVE) {
213
+ val dx = kotlin.math.abs(ev.rawX - streamInitialX)
214
+ val dy = kotlin.math.abs(ev.rawY - streamInitialY)
215
+ if (dx > touchSlop || dy > touchSlop) {
216
+ streamDirectionDecided = true
217
+ streamHorizontalLocked = dx > dy
218
+ }
219
+ }
220
+
221
+ if (streamHorizontalLocked) {
222
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
223
+ streamHorizontalLocked = false
224
+ streamDirectionDecided = false
225
+ }
226
+ return false
227
+ }
228
+
229
+ if (streamGestureClaimed) {
230
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
231
+ streamGestureClaimed = false
232
+ }
233
+ return false
234
+ }
235
+
236
+ // Hold off BottomSheetBehavior within the grace distance so a gesture handler
237
+ // under the touch can activate and claim the stream first.
238
+ if (streamHasGestureRoot &&
239
+ action == MotionEvent.ACTION_MOVE &&
240
+ kotlin.math.abs(ev.rawY - streamInitialY) < touchSlop * GESTURE_CLAIM_SLOP_FACTOR
241
+ ) {
242
+ return false
243
+ }
244
+
245
+ val scrollView = delegate?.findScrollView()
246
+ val hasRefreshControl = scrollView?.parent is SwipeRefreshLayout
247
+ val cannotScroll = scrollView != null &&
248
+ !hasRefreshControl &&
249
+ scrollView.scrollY == 0 &&
250
+ !scrollView.canScrollVertically(1)
251
+
252
+ if (cannotScroll || streamSheetDraggable) {
253
+ when (ev.action and MotionEvent.ACTION_MASK) {
254
+ MotionEvent.ACTION_DOWN -> {
255
+ dragging = false
256
+ initialY = ev.y
257
+ activePointerId = ev.getPointerId(0)
258
+ }
259
+
260
+ MotionEvent.ACTION_MOVE -> {
261
+ val pointerIndex = ev.findPointerIndex(activePointerId)
262
+ if (pointerIndex != -1) {
263
+ val y = ev.getY(pointerIndex)
264
+ val deltaY = initialY - y
265
+ if (kotlin.math.abs(deltaY) > touchSlop) {
266
+ dragging = true
267
+ parent?.requestDisallowInterceptTouchEvent(true)
268
+ }
269
+ }
270
+ }
271
+
272
+ MotionEvent.ACTION_UP,
273
+ MotionEvent.ACTION_CANCEL -> {
274
+ dragging = false
275
+ }
276
+ }
277
+ } else {
278
+ dragging = false
279
+ }
280
+
281
+ return dragging || super.onInterceptTouchEvent(ev)
282
+ }
283
+
284
+ @SuppressLint("ClickableViewAccessibility")
285
+ override fun onTouchEvent(ev: MotionEvent): Boolean {
286
+ if (dragging) {
287
+ when (ev.action and MotionEvent.ACTION_MASK) {
288
+ MotionEvent.ACTION_UP,
289
+ MotionEvent.ACTION_CANCEL -> {
290
+ dragging = false
291
+ }
292
+ }
293
+ // Let parent CoordinatorLayout handle the touch for BottomSheetBehavior
294
+ return super.onTouchEvent(ev)
295
+ }
296
+ return super.onTouchEvent(ev)
297
+ }
298
+
299
+ companion object {
300
+ private const val GESTURE_HANDLER_ROOT_VIEW_CLASS = "com.swmansion.gesturehandler.react.RNGestureHandlerRootView"
301
+ private const val GESTURE_CLAIM_SLOP_FACTOR = 3
302
+ }
303
+ }
@@ -0,0 +1,277 @@
1
+ package com.lodev09.truesheet.core
2
+
3
+ import com.facebook.react.uimanager.PixelUtil.dpToPx
4
+ import com.facebook.react.uimanager.PixelUtil.pxToDp
5
+ import com.facebook.react.uimanager.ThemedReactContext
6
+ import com.facebook.react.util.RNLog
7
+ import com.google.android.material.bottomsheet.BottomSheetBehavior
8
+
9
+ /**
10
+ * Provides screen dimensions and content measurements for detent calculations.
11
+ */
12
+ interface TrueSheetDetentCalculatorDelegate {
13
+ val screenHeight: Int
14
+ val realScreenHeight: Int
15
+ val detents: MutableList<Double>
16
+ val contentHeight: Int
17
+ val headerHeight: Int
18
+ val absoluteHeader: Boolean
19
+ val footerHeight: Int
20
+ val absoluteFooter: Boolean
21
+ val peekContentHeight: Int
22
+ val contentBottomInset: Int
23
+ val maxContentHeight: Int?
24
+ val keyboardInset: Int
25
+ val topInset: Int
26
+ }
27
+
28
+ /**
29
+ * Handles all detent-related calculations for the bottom sheet.
30
+ */
31
+ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
32
+
33
+ var delegate: TrueSheetDetentCalculatorDelegate? = null
34
+
35
+ private val screenHeight: Int get() = delegate?.screenHeight ?: 0
36
+ private val realScreenHeight: Int get() = delegate?.realScreenHeight ?: 0
37
+ private val detents: List<Double> get() = delegate?.detents ?: emptyList()
38
+ private val contentHeight: Int get() = delegate?.contentHeight ?: 0
39
+
40
+ private val headerHeight: Int get() = delegate?.headerHeight ?: 0
41
+ private val footerHeight: Int get() = delegate?.footerHeight ?: 0
42
+ private val peekContentHeight: Int get() = delegate?.peekContentHeight ?: 0
43
+ private val contentBottomInset: Int get() = delegate?.contentBottomInset ?: 0
44
+ private val maxContentHeight: Int? get() = delegate?.maxContentHeight
45
+ private val keyboardInset: Int get() = delegate?.keyboardInset ?: 0
46
+ private val topInset: Int get() = delegate?.topInset ?: 0
47
+
48
+ /**
49
+ * Height for auto (-1.0) detents: content + header + footer height.
50
+ * An absolute (floating) header or footer overlaps the content, so it contributes no height.
51
+ */
52
+ private val autoDetentHeight: Int
53
+ get() = contentHeight +
54
+ (if (delegate?.absoluteHeader == true) 0 else headerHeight) +
55
+ (if (delegate?.absoluteFooter == true) 0 else footerHeight)
56
+
57
+ /**
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.
60
+ */
61
+ private val autoDetentBottomInset: Int
62
+ get() = if (footerHeight > 0 && delegate?.absoluteFooter == false) 0 else contentBottomInset
63
+
64
+ /**
65
+ * Bottom inset for peek detents. An absolute footer counts toward the peek
66
+ * height and absorbs the inset, so it isn't added again.
67
+ */
68
+ private val peekDetentBottomInset: Int
69
+ get() = if (footerHeight > 0 && delegate?.absoluteFooter == true) 0 else contentBottomInset
70
+
71
+ /**
72
+ * Height for peek (-2.0) detents: header + footer + peek content height.
73
+ * A relative footer is pushed off-screen at peek, so it contributes no height.
74
+ * Falls back to 150dp when none is present.
75
+ */
76
+ private val peekDetentHeight: Int
77
+ get() {
78
+ val height = headerHeight +
79
+ (if (delegate?.absoluteFooter == true) footerHeight else 0) +
80
+ peekContentHeight
81
+ return if (height > 0) height else DEFAULT_PEEK_HEIGHT.dpToPx().toInt()
82
+ }
83
+
84
+ /**
85
+ * Calculate the height in pixels for a given detent value.
86
+ * @param detent The detent value: -1.0 for content-fit, -2.0 for peek, or 0.0-1.0 for screen fraction
87
+ */
88
+ fun getDetentHeight(detent: Double, includeKeyboard: Boolean = true): Int {
89
+ val baseHeight = if (detent == -1.0) {
90
+ autoDetentHeight + autoDetentBottomInset
91
+ } else if (detent == -2.0) {
92
+ peekDetentHeight + peekDetentBottomInset
93
+ } else {
94
+ if (detent <= 0.0 || detent > 1.0) {
95
+ throw IllegalArgumentException("TrueSheet: detent fraction ($detent) must be between 0 and 1")
96
+ }
97
+ (detent * screenHeight).toInt() + contentBottomInset
98
+ }
99
+
100
+ val height = if (includeKeyboard) baseHeight + keyboardInset else baseHeight
101
+ val maxAllowedHeight = screenHeight + contentBottomInset
102
+ return maxContentHeight?.let { minOf(height, it, maxAllowedHeight) } ?: minOf(height, maxAllowedHeight)
103
+ }
104
+
105
+ /**
106
+ * Get the expected sheet top position for a detent index.
107
+ */
108
+ fun getSheetTopForDetentIndex(index: Int): Int {
109
+ if (index < 0 || index >= detents.size) {
110
+ RNLog.w(reactContext, "TrueSheet: Detent index ($index) is out of bounds (0..${detents.size - 1})")
111
+ return realScreenHeight
112
+ }
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.
117
+ val maxAvailableHeight = realScreenHeight - topInset
118
+ return realScreenHeight - minOf(getDetentHeight(detents[index]), maxAvailableHeight)
119
+ }
120
+
121
+ /**
122
+ * Calculate visible sheet height from sheet top position.
123
+ */
124
+ fun getVisibleSheetHeight(sheetTop: Int): Int = realScreenHeight - sheetTop
125
+
126
+ /**
127
+ * Convert visible sheet height to position in dp.
128
+ */
129
+ fun getPositionDp(visibleSheetHeight: Int): Float = (screenHeight - visibleSheetHeight).pxToDp()
130
+
131
+ /**
132
+ * Returns the raw screen fraction for a detent index (without bottomInset).
133
+ */
134
+ fun getDetentValueForIndex(index: Int): Float {
135
+ if (index < 0 || index >= detents.size) return 0f
136
+ val value = detents[index]
137
+ return if (value == -1.0) {
138
+ autoDetentHeight.toFloat() / screenHeight.toFloat()
139
+ } else if (value == -2.0) {
140
+ peekDetentHeight.toFloat() / screenHeight.toFloat()
141
+ } else {
142
+ value.toFloat()
143
+ }
144
+ }
145
+
146
+ // ====================================================================
147
+ // MARK: - State Mapping
148
+ // ====================================================================
149
+
150
+ /**
151
+ * Maps detent index to BottomSheetBehavior state based on detent count.
152
+ */
153
+ fun getStateForDetentIndex(index: Int): Int {
154
+ val stateMap = getDetentStateMap() ?: return BottomSheetBehavior.STATE_HIDDEN
155
+ return stateMap.entries.find { it.value == index }?.key ?: BottomSheetBehavior.STATE_HIDDEN
156
+ }
157
+
158
+ /**
159
+ * Maps BottomSheetBehavior state to detent index.
160
+ * @return The detent index, or null if state is not mapped
161
+ */
162
+ fun getDetentIndexForState(state: Int): Int? {
163
+ val stateMap = getDetentStateMap() ?: return null
164
+ return stateMap[state]
165
+ }
166
+
167
+ /**
168
+ * Returns state-to-index mapping based on detent count.
169
+ */
170
+ private fun getDetentStateMap(): Map<Int, Int>? =
171
+ when (detents.size) {
172
+ 1 -> mapOf(
173
+ BottomSheetBehavior.STATE_COLLAPSED to 0,
174
+ BottomSheetBehavior.STATE_EXPANDED to 0
175
+ )
176
+
177
+ 2 -> mapOf(
178
+ BottomSheetBehavior.STATE_COLLAPSED to 0,
179
+ BottomSheetBehavior.STATE_HALF_EXPANDED to 1,
180
+ BottomSheetBehavior.STATE_EXPANDED to 1
181
+ )
182
+
183
+ 3 -> mapOf(
184
+ BottomSheetBehavior.STATE_COLLAPSED to 0,
185
+ BottomSheetBehavior.STATE_HALF_EXPANDED to 1,
186
+ BottomSheetBehavior.STATE_EXPANDED to 2
187
+ )
188
+
189
+ else -> null
190
+ }
191
+
192
+ // ====================================================================
193
+ // MARK: - Interpolation
194
+ // ====================================================================
195
+
196
+ /**
197
+ * Find which segment the position falls into for interpolation.
198
+ * @return Triple(fromIndex, toIndex, progress) where progress is 0-1, or null if no detents
199
+ */
200
+ fun findSegmentForPosition(positionPx: Int): Triple<Int, Int, Float>? {
201
+ val count = detents.size
202
+ if (count == 0) return null
203
+
204
+ val firstPos = getSheetTopForDetentIndex(0)
205
+
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
210
+ return Triple(-1, 0, progress)
211
+ }
212
+
213
+ if (count == 1) return Triple(0, 0, 0f)
214
+
215
+ val lastPos = getSheetTopForDetentIndex(count - 1)
216
+ // Position is above last detent
217
+ if (positionPx < lastPos) {
218
+ return Triple(count - 1, count - 1, 0f)
219
+ }
220
+
221
+ // Find the segment containing this position
222
+ for (i in 0 until count - 1) {
223
+ val pos = getSheetTopForDetentIndex(i)
224
+ val nextPos = getSheetTopForDetentIndex(i + 1)
225
+
226
+ // Skip degenerate segments — keyboard growth can clamp adjacent detents
227
+ // to the same top; falling through reports the topmost detent
228
+ if (pos == nextPos) continue
229
+
230
+ if (positionPx in nextPos..pos) {
231
+ val range = pos - nextPos
232
+ val progress = if (range > 0) (pos - positionPx).toFloat() / range else 0f
233
+ return Triple(i, i + 1, maxOf(0f, minOf(1f, progress)))
234
+ }
235
+ }
236
+
237
+ return Triple(count - 1, count - 1, 0f)
238
+ }
239
+
240
+ /**
241
+ * Returns continuous index (e.g., 0.5 = halfway between detent 0 and 1).
242
+ */
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
260
+
261
+ val segment = findSegmentForPosition(positionPx) ?: return getDetentValueForIndex(0)
262
+ val (fromIndex, toIndex, progress) = segment
263
+
264
+ if (fromIndex == -1) {
265
+ val firstDetent = getDetentValueForIndex(0)
266
+ return maxOf(0f, firstDetent * (1 - progress))
267
+ }
268
+
269
+ val fromDetent = getDetentValueForIndex(fromIndex)
270
+ val toDetent = getDetentValueForIndex(toIndex)
271
+ return fromDetent + progress * (toDetent - fromDetent)
272
+ }
273
+
274
+ companion object {
275
+ private const val DEFAULT_PEEK_HEIGHT = 150f
276
+ }
277
+ }