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

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 (388) 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 +156 -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 +1224 -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/TouchEventDeduper.kt +31 -0
  37. package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +30 -0
  38. package/android/src/main/jni/CMakeLists.txt +63 -0
  39. package/android/src/main/jni/TrueSheetSpec.h +19 -0
  40. package/android/src/main/jni/TrueSheetStateUpdater.cpp +96 -0
  41. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +24 -0
  42. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +43 -0
  43. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +28 -0
  44. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +11 -0
  45. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +37 -0
  46. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +24 -0
  47. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +56 -0
  48. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +28 -0
  49. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +11 -0
  50. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +41 -0
  51. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +18 -0
  52. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +16 -0
  53. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +28 -0
  54. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +61 -0
  55. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +45 -0
  56. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +23 -0
  57. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +52 -0
  58. package/ios/TrueSheetContainerView.h +81 -6
  59. package/ios/TrueSheetContainerView.mm +275 -21
  60. package/ios/TrueSheetContentView.h +38 -1
  61. package/ios/TrueSheetContentView.mm +413 -43
  62. package/ios/TrueSheetFooterView.h +27 -2
  63. package/ios/TrueSheetFooterView.mm +141 -35
  64. package/ios/{events/OnDidDismissEvent.h → TrueSheetHeaderView.h} +10 -7
  65. package/ios/TrueSheetHeaderView.mm +64 -0
  66. package/ios/TrueSheetModule.mm +92 -5
  67. package/ios/TrueSheetPeekView.h +32 -0
  68. package/ios/TrueSheetPeekView.mm +99 -0
  69. package/ios/TrueSheetView.h +7 -15
  70. package/ios/TrueSheetView.mm +633 -215
  71. package/ios/TrueSheetViewController.h +84 -16
  72. package/ios/TrueSheetViewController.mm +1133 -333
  73. package/ios/core/RNScreensEventObserver.h +46 -0
  74. package/ios/core/RNScreensEventObserver.mm +280 -0
  75. package/ios/core/TrueSheetBlurView.h +27 -0
  76. package/ios/core/TrueSheetBlurView.mm +82 -0
  77. package/ios/core/TrueSheetDetentCalculator.h +107 -0
  78. package/ios/core/TrueSheetDetentCalculator.mm +197 -0
  79. package/ios/core/TrueSheetGrabberView.h +72 -0
  80. package/ios/core/TrueSheetGrabberView.mm +178 -0
  81. package/ios/core/TrueSheetKeyboardObserver.h +39 -0
  82. package/ios/core/TrueSheetKeyboardObserver.mm +112 -0
  83. package/ios/events/TrueSheetDragEvents.h +39 -0
  84. package/ios/events/TrueSheetDragEvents.mm +62 -0
  85. package/ios/events/{OnPositionChangeEvent.h → TrueSheetFocusEvents.h} +8 -5
  86. package/ios/events/TrueSheetFocusEvents.mm +49 -0
  87. package/ios/events/TrueSheetLifecycleEvents.h +40 -0
  88. package/ios/events/TrueSheetLifecycleEvents.mm +71 -0
  89. package/ios/events/TrueSheetStateEvents.h +35 -0
  90. package/ios/events/TrueSheetStateEvents.mm +49 -0
  91. package/ios/tvos/TrueSheetStubs.mm +154 -0
  92. package/ios/{events/OnMountEvent.h → utils/BlurUtil.h} +5 -7
  93. package/ios/utils/BlurUtil.mm +69 -0
  94. package/ios/utils/GestureUtil.h +7 -0
  95. package/ios/utils/GestureUtil.mm +12 -0
  96. package/ios/utils/PlatformUtil.h +15 -0
  97. package/ios/utils/UIView+FirstResponder.h +15 -0
  98. package/ios/utils/UIView+FirstResponder.mm +26 -0
  99. package/ios/{events/OnWillDismissEvent.h → utils/UIView+ScrollEdgeInteraction.h} +6 -7
  100. package/ios/utils/UIView+ScrollEdgeInteraction.mm +62 -0
  101. package/ios/utils/WindowUtil.mm +17 -1
  102. package/lib/module/TrueSheet.js +230 -64
  103. package/lib/module/TrueSheet.js.map +1 -1
  104. package/lib/module/TrueSheet.web.js +1203 -0
  105. package/lib/module/TrueSheet.web.js.map +1 -0
  106. package/lib/module/TrueSheetPeek.js +15 -0
  107. package/lib/module/TrueSheetPeek.js.map +1 -0
  108. package/lib/module/TrueSheetPeek.web.js +55 -0
  109. package/lib/module/TrueSheetPeek.web.js.map +1 -0
  110. package/lib/module/TrueSheetProvider.js +28 -0
  111. package/lib/module/TrueSheetProvider.js.map +1 -0
  112. package/lib/module/TrueSheetProvider.web.js +157 -0
  113. package/lib/module/TrueSheetProvider.web.js.map +1 -0
  114. package/lib/module/fabric/TrueSheetContainerViewNativeComponent.ts +1 -1
  115. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +5 -1
  116. package/lib/module/fabric/TrueSheetFooterViewNativeComponent.ts +10 -2
  117. package/lib/module/fabric/TrueSheetHeaderViewNativeComponent.ts +8 -0
  118. package/lib/module/fabric/TrueSheetPeekViewNativeComponent.ts +8 -0
  119. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +105 -18
  120. package/lib/module/index.js +3 -3
  121. package/lib/module/index.js.map +1 -1
  122. package/lib/module/mocks/index.js +93 -0
  123. package/lib/module/mocks/index.js.map +1 -0
  124. package/lib/module/mocks/navigation.js +95 -0
  125. package/lib/module/mocks/navigation.js.map +1 -0
  126. package/lib/module/mocks/navigationExpoRouter.js +21 -0
  127. package/lib/module/mocks/navigationExpoRouter.js.map +1 -0
  128. package/lib/module/mocks/reanimated.js +91 -0
  129. package/lib/module/mocks/reanimated.js.map +1 -0
  130. package/lib/module/navigation/TrueSheetNavigatorContent.js +73 -0
  131. package/lib/module/navigation/TrueSheetNavigatorContent.js.map +1 -0
  132. package/lib/module/navigation/actions.js +50 -0
  133. package/lib/module/navigation/actions.js.map +1 -0
  134. package/lib/module/navigation/createTrueSheetRouter.js +166 -0
  135. package/lib/module/navigation/createTrueSheetRouter.js.map +1 -0
  136. package/lib/module/navigation/expo-router/index.js +48 -0
  137. package/lib/module/navigation/expo-router/index.js.map +1 -0
  138. package/lib/module/navigation/index.js +47 -0
  139. package/lib/module/navigation/index.js.map +1 -0
  140. package/lib/module/navigation/navigator.js +10 -0
  141. package/lib/module/navigation/navigator.js.map +1 -0
  142. package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js +56 -0
  143. package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js.map +1 -0
  144. package/lib/module/navigation/screen/TrueSheetScreen.js +49 -0
  145. package/lib/module/navigation/screen/TrueSheetScreen.js.map +1 -0
  146. package/lib/module/navigation/screen/index.js +5 -0
  147. package/lib/module/navigation/screen/index.js.map +1 -0
  148. package/lib/module/navigation/screen/types.js +4 -0
  149. package/lib/module/navigation/screen/types.js.map +1 -0
  150. package/lib/module/navigation/screen/useSheetScreenState.js +68 -0
  151. package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -0
  152. package/lib/module/navigation/types.js +4 -0
  153. package/lib/module/navigation/types.js.map +1 -0
  154. package/lib/module/navigation/useTrueSheetNavigation.js +24 -0
  155. package/lib/module/navigation/useTrueSheetNavigation.js.map +1 -0
  156. package/lib/module/reanimated/ReanimatedTrueSheet.js +15 -9
  157. package/lib/module/reanimated/ReanimatedTrueSheet.js.map +1 -1
  158. package/lib/module/reanimated/ReanimatedTrueSheet.web.js +81 -0
  159. package/lib/module/reanimated/ReanimatedTrueSheet.web.js.map +1 -0
  160. package/lib/module/reanimated/ReanimatedTrueSheetProvider.js +4 -2
  161. package/lib/module/reanimated/ReanimatedTrueSheetProvider.js.map +1 -1
  162. package/lib/module/reanimated/index.js +2 -2
  163. package/lib/module/reanimated/index.js.map +1 -1
  164. package/lib/module/reanimated/useReanimatedPositionChangeHandler.web.js +20 -0
  165. package/lib/module/reanimated/useReanimatedPositionChangeHandler.web.js.map +1 -0
  166. package/lib/module/specs/NativeTrueSheetModule.js.map +1 -1
  167. package/lib/module/web/constants.js +16 -0
  168. package/lib/module/web/constants.js.map +1 -0
  169. package/lib/module/web/dom.js +10 -0
  170. package/lib/module/web/dom.js.map +1 -0
  171. package/lib/module/web/vaul/README.md +9 -0
  172. package/lib/module/web/vaul/browser.js +30 -0
  173. package/lib/module/web/vaul/browser.js.map +1 -0
  174. package/lib/module/web/vaul/constants.js +15 -0
  175. package/lib/module/web/vaul/constants.js.map +1 -0
  176. package/lib/module/web/vaul/context.js +59 -0
  177. package/lib/module/web/vaul/context.js.map +1 -0
  178. package/lib/module/web/vaul/helpers.js +99 -0
  179. package/lib/module/web/vaul/helpers.js.map +1 -0
  180. package/lib/module/web/vaul/index.js +1309 -0
  181. package/lib/module/web/vaul/index.js.map +1 -0
  182. package/lib/module/web/vaul/style.css +287 -0
  183. package/lib/module/web/vaul/style.css.d.js +2 -0
  184. package/lib/module/web/vaul/style.css.d.js.map +1 -0
  185. package/lib/module/web/vaul/types.js +2 -0
  186. package/lib/module/web/vaul/types.js.map +1 -0
  187. package/lib/module/web/vaul/use-composed-refs.js +34 -0
  188. package/lib/module/web/vaul/use-composed-refs.js.map +1 -0
  189. package/lib/module/web/vaul/use-controllable-state.js +54 -0
  190. package/lib/module/web/vaul/use-controllable-state.js.map +1 -0
  191. package/lib/module/web/vaul/use-position-fixed.js +123 -0
  192. package/lib/module/web/vaul/use-position-fixed.js.map +1 -0
  193. package/lib/module/web/vaul/use-prevent-scroll.js +258 -0
  194. package/lib/module/web/vaul/use-prevent-scroll.js.map +1 -0
  195. package/lib/module/web/vaul/use-scale-background.js +57 -0
  196. package/lib/module/web/vaul/use-scale-background.js.map +1 -0
  197. package/lib/module/web/vaul/use-snap-points.js +305 -0
  198. package/lib/module/web/vaul/use-snap-points.js.map +1 -0
  199. package/lib/typescript/src/TrueSheet.d.ts +51 -14
  200. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  201. package/lib/typescript/src/TrueSheet.types.d.ts +463 -54
  202. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  203. package/lib/typescript/src/TrueSheet.web.d.ts +5 -0
  204. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -0
  205. package/lib/typescript/src/TrueSheetPeek.d.ts +9 -0
  206. package/lib/typescript/src/TrueSheetPeek.d.ts.map +1 -0
  207. package/lib/typescript/src/TrueSheetPeek.web.d.ts +28 -0
  208. package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -0
  209. package/lib/typescript/src/TrueSheetProvider.d.ts +18 -0
  210. package/lib/typescript/src/TrueSheetProvider.d.ts.map +1 -0
  211. package/lib/typescript/src/TrueSheetProvider.web.d.ts +27 -0
  212. package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -0
  213. package/lib/typescript/src/fabric/TrueSheetContainerViewNativeComponent.d.ts.map +1 -1
  214. package/lib/typescript/src/fabric/TrueSheetContentViewNativeComponent.d.ts.map +1 -1
  215. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts +2 -0
  216. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts.map +1 -1
  217. package/lib/typescript/src/fabric/TrueSheetHeaderViewNativeComponent.d.ts +6 -0
  218. package/lib/typescript/src/fabric/TrueSheetHeaderViewNativeComponent.d.ts.map +1 -0
  219. package/lib/typescript/src/fabric/TrueSheetPeekViewNativeComponent.d.ts +6 -0
  220. package/lib/typescript/src/fabric/TrueSheetPeekViewNativeComponent.d.ts.map +1 -0
  221. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +67 -16
  222. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  223. package/lib/typescript/src/index.d.ts +2 -2
  224. package/lib/typescript/src/index.d.ts.map +1 -1
  225. package/lib/typescript/src/mocks/index.d.ts +43 -0
  226. package/lib/typescript/src/mocks/index.d.ts.map +1 -0
  227. package/lib/typescript/src/mocks/navigation.d.ts +70 -0
  228. package/lib/typescript/src/mocks/navigation.d.ts.map +1 -0
  229. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +22 -0
  230. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -0
  231. package/lib/typescript/src/mocks/reanimated.d.ts +48 -0
  232. package/lib/typescript/src/mocks/reanimated.d.ts.map +1 -0
  233. package/lib/typescript/src/navigation/TrueSheetNavigatorContent.d.ts +5 -0
  234. package/lib/typescript/src/navigation/TrueSheetNavigatorContent.d.ts.map +1 -0
  235. package/lib/typescript/src/navigation/actions.d.ts +71 -0
  236. package/lib/typescript/src/navigation/actions.d.ts.map +1 -0
  237. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +14 -0
  238. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -0
  239. package/lib/typescript/src/navigation/expo-router/index.d.ts +37 -0
  240. package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -0
  241. package/lib/typescript/src/navigation/index.d.ts +46 -0
  242. package/lib/typescript/src/navigation/index.d.ts.map +1 -0
  243. package/lib/typescript/src/navigation/navigator.d.ts +7 -0
  244. package/lib/typescript/src/navigation/navigator.d.ts.map +1 -0
  245. package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts +3 -0
  246. package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts.map +1 -0
  247. package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts +3 -0
  248. package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts.map +1 -0
  249. package/lib/typescript/src/navigation/screen/index.d.ts +4 -0
  250. package/lib/typescript/src/navigation/screen/index.d.ts.map +1 -0
  251. package/lib/typescript/src/navigation/screen/types.d.ts +14 -0
  252. package/lib/typescript/src/navigation/screen/types.d.ts.map +1 -0
  253. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +33 -0
  254. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -0
  255. package/lib/typescript/src/navigation/types.d.ts +177 -0
  256. package/lib/typescript/src/navigation/types.d.ts.map +1 -0
  257. package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts +23 -0
  258. package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts.map +1 -0
  259. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.d.ts.map +1 -1
  260. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.web.d.ts +41 -0
  261. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.web.d.ts.map +1 -0
  262. package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts +8 -2
  263. package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts.map +1 -1
  264. package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.web.d.ts +15 -0
  265. package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.web.d.ts.map +1 -0
  266. package/lib/typescript/src/specs/NativeTrueSheetModule.d.ts +25 -3
  267. package/lib/typescript/src/specs/NativeTrueSheetModule.d.ts.map +1 -1
  268. package/lib/typescript/src/web/constants.d.ts +14 -0
  269. package/lib/typescript/src/web/constants.d.ts.map +1 -0
  270. package/lib/typescript/src/web/dom.d.ts +7 -0
  271. package/lib/typescript/src/web/dom.d.ts.map +1 -0
  272. package/lib/typescript/src/web/vaul/browser.d.ts +8 -0
  273. package/lib/typescript/src/web/vaul/browser.d.ts.map +1 -0
  274. package/lib/typescript/src/web/vaul/constants.d.ts +13 -0
  275. package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -0
  276. package/lib/typescript/src/web/vaul/context.d.ts +53 -0
  277. package/lib/typescript/src/web/vaul/context.d.ts.map +1 -0
  278. package/lib/typescript/src/web/vaul/helpers.d.ts +17 -0
  279. package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -0
  280. package/lib/typescript/src/web/vaul/index.d.ts +228 -0
  281. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -0
  282. package/lib/typescript/src/web/vaul/types.d.ts +7 -0
  283. package/lib/typescript/src/web/vaul/types.d.ts.map +1 -0
  284. package/lib/typescript/src/web/vaul/use-composed-refs.d.ts +14 -0
  285. package/lib/typescript/src/web/vaul/use-composed-refs.d.ts.map +1 -0
  286. package/lib/typescript/src/web/vaul/use-controllable-state.d.ts +9 -0
  287. package/lib/typescript/src/web/vaul/use-controllable-state.d.ts.map +1 -0
  288. package/lib/typescript/src/web/vaul/use-position-fixed.d.ts +19 -0
  289. package/lib/typescript/src/web/vaul/use-position-fixed.d.ts.map +1 -0
  290. package/lib/typescript/src/web/vaul/use-prevent-scroll.d.ts +18 -0
  291. package/lib/typescript/src/web/vaul/use-prevent-scroll.d.ts.map +1 -0
  292. package/lib/typescript/src/web/vaul/use-scale-background.d.ts +2 -0
  293. package/lib/typescript/src/web/vaul/use-scale-background.d.ts.map +1 -0
  294. package/lib/typescript/src/web/vaul/use-snap-points.d.ts +38 -0
  295. package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -0
  296. package/package.json +95 -36
  297. package/react-native.config.js +9 -2
  298. package/src/TrueSheet.tsx +281 -62
  299. package/src/TrueSheet.types.ts +525 -71
  300. package/src/TrueSheet.web.tsx +1362 -0
  301. package/src/TrueSheetPeek.tsx +11 -0
  302. package/src/TrueSheetPeek.web.tsx +64 -0
  303. package/src/TrueSheetProvider.tsx +33 -0
  304. package/src/TrueSheetProvider.web.tsx +220 -0
  305. package/src/fabric/TrueSheetContainerViewNativeComponent.ts +1 -1
  306. package/src/fabric/TrueSheetContentViewNativeComponent.ts +5 -1
  307. package/src/fabric/TrueSheetFooterViewNativeComponent.ts +10 -2
  308. package/src/fabric/TrueSheetHeaderViewNativeComponent.ts +8 -0
  309. package/src/fabric/TrueSheetPeekViewNativeComponent.ts +8 -0
  310. package/src/fabric/TrueSheetViewNativeComponent.ts +105 -18
  311. package/src/index.ts +2 -2
  312. package/src/mocks/index.ts +89 -0
  313. package/src/mocks/navigation.ts +79 -0
  314. package/src/mocks/navigationExpoRouter.ts +40 -0
  315. package/src/mocks/reanimated.ts +100 -0
  316. package/src/navigation/TrueSheetNavigatorContent.tsx +106 -0
  317. package/src/navigation/actions.ts +78 -0
  318. package/src/navigation/createTrueSheetRouter.ts +222 -0
  319. package/src/navigation/expo-router/index.ts +89 -0
  320. package/src/navigation/index.ts +81 -0
  321. package/src/navigation/navigator.ts +18 -0
  322. package/src/navigation/screen/ReanimatedTrueSheetScreen.tsx +61 -0
  323. package/src/navigation/screen/TrueSheetScreen.tsx +54 -0
  324. package/src/navigation/screen/index.ts +3 -0
  325. package/src/navigation/screen/types.ts +19 -0
  326. package/src/navigation/screen/useSheetScreenState.ts +100 -0
  327. package/src/navigation/types.ts +251 -0
  328. package/src/navigation/useTrueSheetNavigation.ts +26 -0
  329. package/src/reanimated/ReanimatedTrueSheet.tsx +12 -7
  330. package/src/reanimated/ReanimatedTrueSheet.web.tsx +75 -0
  331. package/src/reanimated/ReanimatedTrueSheetProvider.tsx +12 -8
  332. package/src/reanimated/useReanimatedPositionChangeHandler.web.ts +31 -0
  333. package/src/specs/NativeTrueSheetModule.ts +28 -3
  334. package/src/web/constants.ts +13 -0
  335. package/src/web/dom.ts +9 -0
  336. package/src/web/vaul/README.md +9 -0
  337. package/src/web/vaul/browser.ts +38 -0
  338. package/src/web/vaul/constants.ts +20 -0
  339. package/src/web/vaul/context.ts +101 -0
  340. package/src/web/vaul/helpers.ts +131 -0
  341. package/src/web/vaul/index.tsx +1740 -0
  342. package/src/web/vaul/style.css +287 -0
  343. package/src/web/vaul/style.css.d.ts +1 -0
  344. package/src/web/vaul/types.ts +7 -0
  345. package/src/web/vaul/use-composed-refs.ts +35 -0
  346. package/src/web/vaul/use-controllable-state.ts +66 -0
  347. package/src/web/vaul/use-position-fixed.ts +147 -0
  348. package/src/web/vaul/use-prevent-scroll.ts +309 -0
  349. package/src/web/vaul/use-scale-background.ts +64 -0
  350. package/src/web/vaul/use-snap-points.ts +455 -0
  351. package/android/src/main/java/com/lodev09/truesheet/events/DetentChangeEvent.kt +0 -26
  352. package/android/src/main/java/com/lodev09/truesheet/events/DidDismissEvent.kt +0 -20
  353. package/android/src/main/java/com/lodev09/truesheet/events/DidPresentEvent.kt +0 -26
  354. package/android/src/main/java/com/lodev09/truesheet/events/DragBeginEvent.kt +0 -26
  355. package/android/src/main/java/com/lodev09/truesheet/events/DragChangeEvent.kt +0 -26
  356. package/android/src/main/java/com/lodev09/truesheet/events/DragEndEvent.kt +0 -26
  357. package/android/src/main/java/com/lodev09/truesheet/events/MountEvent.kt +0 -20
  358. package/android/src/main/java/com/lodev09/truesheet/events/PositionChangeEvent.kt +0 -32
  359. package/android/src/main/java/com/lodev09/truesheet/events/SizeChangeEvent.kt +0 -27
  360. package/android/src/main/java/com/lodev09/truesheet/events/WillDismissEvent.kt +0 -20
  361. package/android/src/main/java/com/lodev09/truesheet/events/WillPresentEvent.kt +0 -26
  362. package/android/src/main/res/values/styles.xml +0 -8
  363. package/ios/events/OnDetentChangeEvent.h +0 -28
  364. package/ios/events/OnDetentChangeEvent.mm +0 -30
  365. package/ios/events/OnDidDismissEvent.mm +0 -25
  366. package/ios/events/OnDidPresentEvent.h +0 -28
  367. package/ios/events/OnDidPresentEvent.mm +0 -30
  368. package/ios/events/OnDragBeginEvent.h +0 -28
  369. package/ios/events/OnDragBeginEvent.mm +0 -30
  370. package/ios/events/OnDragChangeEvent.h +0 -28
  371. package/ios/events/OnDragChangeEvent.mm +0 -30
  372. package/ios/events/OnDragEndEvent.h +0 -28
  373. package/ios/events/OnDragEndEvent.mm +0 -30
  374. package/ios/events/OnMountEvent.mm +0 -25
  375. package/ios/events/OnPositionChangeEvent.mm +0 -32
  376. package/ios/events/OnSizeChangeEvent.h +0 -28
  377. package/ios/events/OnSizeChangeEvent.mm +0 -30
  378. package/ios/events/OnWillDismissEvent.mm +0 -25
  379. package/ios/events/OnWillPresentEvent.h +0 -28
  380. package/ios/events/OnWillPresentEvent.mm +0 -30
  381. package/ios/utils/LayoutUtil.h +0 -44
  382. package/ios/utils/LayoutUtil.mm +0 -50
  383. package/lib/module/TrueSheetGrabber.js +0 -51
  384. package/lib/module/TrueSheetGrabber.js.map +0 -1
  385. package/lib/typescript/src/TrueSheetGrabber.d.ts +0 -39
  386. package/lib/typescript/src/TrueSheetGrabber.d.ts.map +0 -1
  387. package/src/TrueSheetGrabber.tsx +0 -82
  388. package/src/__mocks__/index.js +0 -67
@@ -2,72 +2,84 @@ package com.lodev09.truesheet
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.view.View
5
- import android.view.ViewStructure
5
+ import android.view.ViewGroup
6
6
  import android.view.accessibility.AccessibilityEvent
7
7
  import androidx.annotation.UiThread
8
8
  import com.facebook.react.bridge.LifecycleEventListener
9
+ import com.facebook.react.bridge.WritableNativeMap
10
+ import com.facebook.react.uimanager.PixelUtil.dpToPx
11
+ import com.facebook.react.uimanager.PixelUtil.pxToDp
12
+ import com.facebook.react.uimanager.StateWrapper
9
13
  import com.facebook.react.uimanager.ThemedReactContext
10
14
  import com.facebook.react.uimanager.UIManagerHelper
11
15
  import com.facebook.react.uimanager.events.EventDispatcher
16
+ import com.facebook.react.util.RNLog
12
17
  import com.facebook.react.views.view.ReactViewGroup
13
- import com.lodev09.truesheet.events.DetentChangeEvent
14
- import com.lodev09.truesheet.events.DidDismissEvent
15
- import com.lodev09.truesheet.events.DidPresentEvent
16
- import com.lodev09.truesheet.events.DragBeginEvent
17
- import com.lodev09.truesheet.events.DragChangeEvent
18
- import com.lodev09.truesheet.events.DragEndEvent
19
- import com.lodev09.truesheet.events.MountEvent
20
- import com.lodev09.truesheet.events.PositionChangeEvent
21
- import com.lodev09.truesheet.events.SizeChangeEvent
22
- import com.lodev09.truesheet.events.WillDismissEvent
23
- import com.lodev09.truesheet.events.WillPresentEvent
18
+ import com.lodev09.truesheet.core.AccessibilityOptions
19
+ import com.lodev09.truesheet.core.GrabberOptions
20
+ import com.lodev09.truesheet.core.RNScreensEventObserver
21
+ import com.lodev09.truesheet.core.RNScreensEventObserverDelegate
22
+ import com.lodev09.truesheet.core.TrueSheetStackManager
23
+ import com.lodev09.truesheet.events.*
24
+ import com.lodev09.truesheet.utils.KeyboardUtils
24
25
 
25
26
  /**
26
- * Main TrueSheet host view.
27
- * Manages the sheet dialog and container, and dispatches events to JavaScript.
27
+ * Main TrueSheet host view that manages the sheet and dispatches events to JavaScript.
28
+ * This view is hidden (GONE) and delegates all rendering to TrueSheetViewController
29
+ * using a CoordinatorLayout approach (no separate dialog window).
28
30
  */
29
31
  @SuppressLint("ViewConstructor")
30
32
  class TrueSheetView(private val reactContext: ThemedReactContext) :
31
33
  ReactViewGroup(reactContext),
32
34
  LifecycleEventListener,
33
35
  TrueSheetViewControllerDelegate,
34
- TrueSheetContainerViewDelegate {
36
+ TrueSheetContainerViewDelegate,
37
+ RNScreensEventObserverDelegate {
35
38
 
36
- /**
37
- * The TrueSheetViewController instance that acts as both root view and controller
38
- */
39
- private val viewController: TrueSheetViewController = TrueSheetViewController(reactContext)
40
-
41
- /**
42
- * Gets the container view (first child of view controller)
43
- */
44
- private val containerView: TrueSheetContainerView?
45
- get() = viewController.getChildAt(0) as? TrueSheetContainerView
39
+ // ==================== Properties ====================
46
40
 
47
- var eventDispatcher: EventDispatcher? = null
41
+ internal val viewController: TrueSheetViewController = TrueSheetViewController(reactContext)
42
+ override var eventDispatcher: EventDispatcher? = null
48
43
 
44
+ // Initial present configuration (set by ViewManager before mount)
49
45
  var initialDetentIndex: Int = -1
50
46
  var initialDetentAnimated: Boolean = true
47
+ private var didInitiallyPresent: Boolean = false
51
48
 
52
- /**
53
- * Tracks if initial presentation has been handled
54
- */
55
- private var hasHandledInitialPresentation = false
49
+ private var lastContainerWidth: Int = 0
50
+ private var lastContainerHeight: Int = 0
51
+
52
+ var stateWrapper: StateWrapper? = null
53
+ set(value) {
54
+ field = value
55
+
56
+ // On first state wrapper assignment, immediately update state with screen dimensions.
57
+ // This ensures Yoga has initial width/height for content layout before presenting.
58
+ if (value != null && lastContainerWidth == 0 && lastContainerHeight == 0) {
59
+ updateState(viewController.screenWidth, viewController.screenHeight)
60
+ }
61
+ }
62
+
63
+ // Debounce flag to coalesce rapid layout changes into a single sheet update
64
+ private var isSheetUpdatePending: Boolean = false
65
+
66
+ // Root container for the coordinator layout (activity or Modal dialog content view)
67
+ internal var rootContainerView: ViewGroup? = null
68
+
69
+ // Screen event observer for react-native-screens integration
70
+ internal var screensEventObserver: RNScreensEventObserver? = null
71
+
72
+ // ==================== Initialization ====================
56
73
 
57
74
  init {
58
75
  reactContext.addLifecycleEventListener(this)
59
-
60
- // Set delegates
61
76
  viewController.delegate = this
62
77
 
63
- // Hide the host view from layout and touch handling
64
- // The actual content is shown in a dialog window
78
+ // Hide the host view - actual content is rendered in the dialog window
65
79
  visibility = GONE
66
80
  }
67
81
 
68
- override fun dispatchProvideStructure(structure: ViewStructure) {
69
- super.dispatchProvideStructure(structure)
70
- }
82
+ // ==================== ReactViewGroup Overrides ====================
71
83
 
72
84
  override fun onLayout(
73
85
  changed: Boolean,
@@ -76,317 +88,600 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
76
88
  right: Int,
77
89
  bottom: Int
78
90
  ) {
79
- // Do nothing as we are laid out by UIManager
91
+ // No-op: layout is managed by React Native's UIManager
80
92
  }
81
93
 
82
94
  override fun setId(id: Int) {
83
95
  super.setId(id)
84
-
85
96
  viewController.id = id
86
97
  TrueSheetModule.registerView(this, id)
87
98
  }
88
99
 
100
+ // ==================== View Hierarchy Management ====================
101
+
89
102
  override fun onAttachedToWindow() {
90
103
  super.onAttachedToWindow()
91
- }
92
104
 
93
- override fun onDetachedFromWindow() {
94
- super.onDetachedFromWindow()
95
- onDropInstance()
96
-
97
- TrueSheetModule.unregisterView(id)
98
- }
99
-
100
- /**
101
- * showOrUpdate will display the Dialog. It is called by the manager once all properties are set
102
- * because we need to know all of them before creating the Dialog. It is also smart during updates
103
- * if the changed properties can be applied directly to the Dialog or require the recreation of a
104
- * new Dialog.
105
- */
106
- fun showOrUpdate() {
107
- // Only handle initial presentation once on mount
108
- if (!hasHandledInitialPresentation && initialDetentIndex >= 0) {
109
- hasHandledInitialPresentation = true
110
-
111
- // Create dialog if not created yet
112
- if (!viewController.isPresented) {
113
- viewController.createDialog()
105
+ if (initialDetentIndex >= 0 && !didInitiallyPresent) {
106
+ didInitiallyPresent = true
107
+ if (initialDetentAnimated) {
108
+ present(initialDetentIndex, true) { }
109
+ } else {
110
+ post { present(initialDetentIndex, false) { } }
114
111
  }
115
-
116
- post {
117
- present(initialDetentIndex, initialDetentAnimated) { }
118
- }
119
- } else if (viewController.isPresented) {
120
- viewController.setupSheetDetents()
121
- viewController.setStateForDetentIndex(viewController.currentDetentIndex)
122
- viewController.positionFooter()
123
112
  }
124
113
  }
125
114
 
126
- // ==================== View Management ====================
127
-
128
115
  override fun addView(child: View?, index: Int) {
129
- // Add the child to our ViewController
130
- // This is the TrueSheetContainerView
116
+ if (child is TrueSheetContainerView) {
117
+ viewController.removeSheetSnapshot()
118
+ }
119
+
131
120
  viewController.addView(child, index)
132
121
 
133
- // Create dialog and dispatch mount event when TrueSheetContainerView is added
134
122
  if (child is TrueSheetContainerView) {
135
- // Set up container delegate to listen for content size changes
136
123
  child.delegate = this
137
-
138
- // Get initial content height from container
139
- val contentHeight = child.contentHeight
140
- if (contentHeight > 0) {
141
- viewController.contentHeight = contentHeight
142
- }
143
-
144
- // Create the dialog now that the container is mounted
145
- viewController.createDialog()
146
-
147
124
  val surfaceId = UIManagerHelper.getSurfaceId(this)
148
- eventDispatcher?.dispatchEvent(
149
- MountEvent(surfaceId, id)
150
- )
125
+ eventDispatcher?.dispatchEvent(MountEvent(surfaceId, id))
151
126
  }
152
127
  }
153
128
 
154
129
  override fun getChildCount(): Int = viewController.childCount
155
- override fun getChildAt(index: Int): View? = viewController.getChildAt(index)
156
-
157
- override fun removeView(child: View?) {
158
- if (child != null) {
159
- // Clean up container delegate
160
- if (child is TrueSheetContainerView) {
161
- child.delegate = null
162
- }
163
130
 
164
- viewController.removeView(child)
165
- }
166
- }
131
+ override fun getChildAt(index: Int): View? = viewController.getChildAt(index)
167
132
 
168
133
  override fun removeViewAt(index: Int) {
169
134
  val child = getChildAt(index)
135
+ if (child is TrueSheetContainerView) {
136
+ child.delegate = null
137
+
138
+ // Skip if already dismissing - snapshot preserves visuals during dismiss
139
+ if (!viewController.isBeingDismissed) {
140
+ viewController.createSheetSnapshot()
141
+ if (viewController.isPresented) {
142
+ dismiss(true) {}
143
+ }
144
+ }
145
+ }
170
146
  viewController.removeView(child)
171
147
  }
172
148
 
173
- override fun addChildrenForAccessibility(outChildren: ArrayList<View>) {
174
- // Explicitly override this to prevent accessibility events being passed down to children
175
- // Those will be handled by the mHostView which lives in the dialog
176
- }
177
-
178
- // Explicitly override this to prevent accessibility events being passed down to children
179
- // Those will be handled by the mHostView which lives in the dialog
149
+ // Accessibility: delegate to dialog's host view since this view is hidden
150
+ override fun addChildrenForAccessibility(outChildren: ArrayList<View>) {}
180
151
  override fun dispatchPopulateAccessibilityEvent(event: AccessibilityEvent): Boolean = false
181
152
 
182
- fun onDropInstance() {
183
- reactContext.removeLifecycleEventListener(this)
184
- TrueSheetModule.unregisterView(id)
185
-
186
- if (viewController.isPresented) {
187
- viewController.dismiss()
188
- }
189
- viewController.delegate = null
190
- }
153
+ // ==================== Lifecycle ====================
191
154
 
192
155
  override fun onHostResume() {
193
- showOrUpdate()
156
+ viewController.reapplyHiddenState()
157
+ finalizeUpdates()
194
158
  }
195
159
 
196
- override fun onHostPause() {
197
- }
160
+ override fun onHostPause() {}
198
161
 
199
162
  override fun onHostDestroy() {
200
163
  onDropInstance()
201
164
  }
202
165
 
203
- // ==================== TrueSheetViewControllerDelegate Implementation ====================
204
-
205
- override fun viewControllerWillPresent(index: Int, position: Float) {
206
- val surfaceId = UIManagerHelper.getSurfaceId(this)
207
- eventDispatcher?.dispatchEvent(
208
- WillPresentEvent(surfaceId, id, index, position)
209
- )
210
- }
166
+ fun onDropInstance() {
167
+ reactContext.removeLifecycleEventListener(this)
211
168
 
212
- override fun viewControllerDidPresent(index: Int, position: Float) {
213
- val surfaceId = UIManagerHelper.getSurfaceId(this)
214
- eventDispatcher?.dispatchEvent(
215
- DidPresentEvent(surfaceId, id, index, position)
216
- )
169
+ TrueSheetModule.unregisterView(id)
170
+ TrueSheetStackManager.removeSheet(this)
217
171
 
218
- // Set our touch event dispatcher on the view controller
219
- viewController.eventDispatcher = eventDispatcher
220
- }
172
+ cleanupScreenEventObserver()
173
+ didInitiallyPresent = false
221
174
 
222
- override fun viewControllerWillDismiss() {
223
- val surfaceId = UIManagerHelper.getSurfaceId(this)
224
- eventDispatcher?.dispatchEvent(
225
- WillDismissEvent(surfaceId, id)
226
- )
175
+ viewController.dismissPromise = { viewController.delegate = null }
227
176
 
228
- // Clear our touch event dispatcher on the view controller
229
- viewController.eventDispatcher = null
177
+ if (viewController.isPresented && !viewController.isBeingDismissed) {
178
+ viewController.dismiss(animated = false)
179
+ }
230
180
  }
231
181
 
232
- override fun viewControllerDidDismiss() {
233
- val surfaceId = UIManagerHelper.getSurfaceId(this)
234
- eventDispatcher?.dispatchEvent(
235
- DidDismissEvent(surfaceId, id)
236
- )
237
- }
182
+ /**
183
+ * Called by the ViewManager after all properties are set.
184
+ * Reconfigures the sheet if it's currently presented.
185
+ */
186
+ fun finalizeUpdates() {
187
+ setupScrollable()
238
188
 
239
- override fun viewControllerDidChangeDetent(index: Int, position: Float) {
240
- val surfaceId = UIManagerHelper.getSurfaceId(this)
241
- eventDispatcher?.dispatchEvent(
242
- DetentChangeEvent(surfaceId, id, index, position)
243
- )
189
+ if (viewController.isPresented) {
190
+ viewController.sheetView?.setupBackground()
191
+ viewController.sheetView?.setupGrabber()
192
+ viewController.sheetView?.updateGravity()
193
+ viewController.updateBehaviorMaxWidth()
194
+ updateSheetIfNeeded()
195
+ }
244
196
  }
245
197
 
246
- override fun viewControllerDidDragBegin(index: Int, position: Float) {
247
- val surfaceId = UIManagerHelper.getSurfaceId(this)
248
- eventDispatcher?.dispatchEvent(
249
- DragBeginEvent(surfaceId, id, index, position)
250
- )
251
- }
198
+ // ==================== Property Setters ====================
252
199
 
253
- override fun viewControllerDidDragChange(index: Int, position: Float) {
254
- val surfaceId = UIManagerHelper.getSurfaceId(this)
255
- eventDispatcher?.dispatchEvent(
256
- DragChangeEvent(surfaceId, id, index, position)
257
- )
200
+ fun setMaxContentHeight(height: Int?) {
201
+ if (viewController.maxContentHeight == height) return
202
+ viewController.maxContentHeight = height
258
203
  }
259
204
 
260
- override fun viewControllerDidDragEnd(index: Int, position: Float) {
261
- val surfaceId = UIManagerHelper.getSurfaceId(this)
262
- eventDispatcher?.dispatchEvent(
263
- DragEndEvent(surfaceId, id, index, position)
264
- )
205
+ fun setMaxContentWidth(width: Int?) {
206
+ if (viewController.maxContentWidth == width) return
207
+ viewController.maxContentWidth = width
265
208
  }
266
209
 
267
- override fun viewControllerDidChangePosition(index: Int, position: Float, transitioning: Boolean) {
268
- val surfaceId = UIManagerHelper.getSurfaceId(this)
269
- eventDispatcher?.dispatchEvent(
270
- PositionChangeEvent(surfaceId, id, index, position, transitioning)
271
- )
210
+ fun setAnchor(anchor: String?) {
211
+ val value = TrueSheetAnchor.fromString(anchor)
212
+ if (viewController.anchor == value) return
213
+ viewController.anchor = value
272
214
  }
273
215
 
274
- // ==================== Property Setters (forward to controller) ====================
275
-
276
- fun setMaxHeight(height: Int) {
277
- if (viewController.maxSheetHeight == height) return
278
- viewController.maxSheetHeight = height
216
+ fun setAnchorOffset(offset: Int) {
217
+ if (viewController.anchorOffset == offset) return
218
+ viewController.anchorOffset = offset
279
219
  }
280
220
 
281
221
  fun setDimmed(dimmed: Boolean) {
282
222
  if (viewController.dimmed == dimmed) return
283
223
  viewController.dimmed = dimmed
284
224
  if (viewController.isPresented) {
285
- viewController.setupDimmedBackground(viewController.currentDetentIndex)
225
+ viewController.setupDimmedBackground()
226
+ viewController.updateDimAmount()
227
+ TrueSheetStackManager.updateBackgroundAccessibility()
286
228
  }
287
229
  }
288
230
 
289
- fun setDimmedIndex(index: Int) {
290
- if (viewController.dimmedIndex == index) return
291
- viewController.dimmedIndex = index
231
+ fun setDimmedDetentIndex(index: Int) {
232
+ if (viewController.dimmedDetentIndex == index) return
233
+ viewController.dimmedDetentIndex = index
292
234
  if (viewController.isPresented) {
293
- viewController.setupDimmedBackground(viewController.currentDetentIndex)
235
+ viewController.setupDimmedBackground()
236
+ viewController.updateDimAmount()
237
+ TrueSheetStackManager.updateBackgroundAccessibility()
294
238
  }
295
239
  }
296
240
 
297
241
  fun setCornerRadius(radius: Float) {
298
- if (viewController.cornerRadius == radius) return
299
- viewController.cornerRadius = radius
300
- viewController.setupBackground()
242
+ if (viewController.sheetCornerRadius == radius) return
243
+ viewController.sheetCornerRadius = radius
301
244
  }
302
245
 
303
- fun setSheetBackgroundColor(color: Int) {
246
+ fun setSheetBackgroundColor(color: Int?) {
304
247
  if (viewController.sheetBackgroundColor == color) return
305
248
  viewController.sheetBackgroundColor = color
306
- viewController.setupBackground()
307
- }
308
-
309
- fun setSoftInputMode(mode: Int) {
310
- viewController.setSoftInputMode(mode)
311
249
  }
312
250
 
313
251
  fun setDismissible(dismissible: Boolean) {
314
252
  viewController.dismissible = dismissible
315
253
  }
316
254
 
317
- fun setGrabber(grabber: Boolean) {}
255
+ fun setDraggable(draggable: Boolean) {
256
+ viewController.draggable = draggable
257
+ }
258
+
259
+ fun setGrabber(grabber: Boolean) {
260
+ viewController.grabber = grabber
261
+ }
262
+
263
+ fun setGrabberOptions(options: GrabberOptions?) {
264
+ viewController.grabberOptions = options
265
+ }
266
+
267
+ fun setAccessibilityOptions(options: AccessibilityOptions?) {
268
+ viewController.accessibilityOptions = options
269
+ }
270
+
271
+ fun setSheetElevation(elevation: Float) {
272
+ viewController.sheetElevation = elevation
273
+ }
318
274
 
319
275
  fun setDetents(newDetents: MutableList<Double>) {
320
276
  viewController.detents = newDetents
277
+ setupScrollable()
278
+ }
279
+
280
+ fun setInsetAdjustment(insetAdjustment: String) {
281
+ viewController.insetAdjustment = TrueSheetInsetAdjustment.fromString(insetAdjustment)
282
+ setupScrollable()
321
283
  }
322
284
 
323
- fun setBlurTint(tint: String?) {}
285
+ fun setScrollableOptions(options: ScrollableOptions?) {
286
+ viewController.scrollableOptions = options
287
+ setupScrollable()
288
+ }
289
+
290
+ fun setAbsoluteHeader(absolute: Boolean) {
291
+ viewController.absoluteHeader = absolute
292
+ }
293
+
294
+ fun setAbsoluteFooter(absolute: Boolean) {
295
+ viewController.absoluteFooter = absolute
296
+ }
297
+
298
+ fun setFooterKeyboardOffset(offset: Float) {
299
+ viewController.footerKeyboardOffset = offset.dpToPx().toInt()
300
+ viewController.positionFooter()
301
+ }
302
+
303
+ private fun setupScrollable() {
304
+ viewController.containerView?.let {
305
+ it.insetAdjustment = viewController.insetAdjustment
306
+ it.scrollViewBottomInset = viewController.contentBottomInset
307
+ it.scrollableOptions = viewController.scrollableOptions
308
+ it.hasAutoDetent = viewController.detents.contains(-1.0)
309
+ it.footerBottomInset = viewController.contentBottomInset
310
+ it.absoluteFooter = viewController.absoluteFooter
311
+ it.setupScrollable()
312
+ }
313
+
314
+ // Publish the inset so a footer set later (e.g. navigation setOptions) is
315
+ // padded on its very first layout instead of resizing the sheet twice.
316
+ if (viewController.insetAdjustment == TrueSheetInsetAdjustment.AUTOMATIC) {
317
+ TrueSheetStateUpdater.setBottomSafeArea(viewController.contentBottomInset.toFloat().pxToDp())
318
+ }
319
+ }
324
320
 
325
- fun setEdgeToEdgeFullScreen(edgeToEdgeFullScreen: Boolean) {
326
- viewController.edgeToEdgeFullScreen = edgeToEdgeFullScreen
321
+ // ==================== Screen Event Observer ====================
322
+
323
+ private fun setupScreenEventObserver() {
324
+ screensEventObserver = RNScreensEventObserver().apply {
325
+ delegate = this@TrueSheetView
326
+
327
+ // For stacked sheets on the same screen, inherit parent's presenter screen tag.
328
+ // If parent was hidden by screen navigation, this sheet is on a different screen.
329
+ val parentScreenTag = viewController.parentSheetView?.screensEventObserver?.presenterScreenTag ?: 0
330
+ val parentHiddenByScreen = viewController.parentSheetView?.viewController?.wasHiddenByScreen == true
331
+ if (parentScreenTag != 0 && !parentHiddenByScreen) {
332
+ presenterScreenTag = parentScreenTag
333
+ } else {
334
+ capturePresenterScreenFromView(this@TrueSheetView)
335
+ }
336
+ startObserving(eventDispatcher)
337
+ }
327
338
  }
328
339
 
340
+ private fun cleanupScreenEventObserver() {
341
+ screensEventObserver?.stopObserving()
342
+ screensEventObserver = null
343
+ }
344
+
345
+ // ==================== State Management ====================
346
+
329
347
  /**
330
- * Presents the sheet at the given detent index.
331
- *
332
- * @param detentIndex The detent index to present at
333
- * @param animated Whether to animate the presentation
334
- * @param promiseCallback Callback invoked when presentation completes
348
+ * Updates the Fabric state with container dimensions for Yoga layout.
349
+ * Converts pixel values to density-independent pixels (dp).
335
350
  */
351
+ fun updateState(width: Int, height: Int) {
352
+ if (width == lastContainerWidth && height == lastContainerHeight) return
353
+
354
+ lastContainerWidth = width
355
+ lastContainerHeight = height
356
+
357
+ val sw = stateWrapper ?: return
358
+ val widthDp = width.toFloat().pxToDp()
359
+ val heightDp = height.toFloat().pxToDp()
360
+
361
+ // Synchronous update — commits and mounts within the same UI-thread frame
362
+ if (TrueSheetStateUpdater.updateState(sw, widthDp, heightDp)) return
363
+
364
+ // Fallback: async state update
365
+ val newStateData = WritableNativeMap()
366
+ newStateData.putDouble("containerWidth", widthDp.toDouble())
367
+ newStateData.putDouble("containerHeight", heightDp.toDouble())
368
+ sw.updateState(newStateData)
369
+ }
370
+
371
+ // ==================== Sheet Actions ====================
372
+
336
373
  @UiThread
337
374
  fun present(detentIndex: Int, animated: Boolean = true, promiseCallback: () -> Unit) {
375
+ if (viewController.isPresented) {
376
+ RNLog.w(reactContext, "TrueSheet: sheet is already presented. Use resize() to change detent.")
377
+ promiseCallback()
378
+ return
379
+ }
380
+
381
+ viewController.createSheet()
382
+ setupScrollable()
383
+
384
+ // Dismiss keyboard if focused view is within a sheet or if target detent will be dimmed
385
+ val parentSheet = TrueSheetStackManager.getTopmostSheet()
386
+ val isFocusedViewWithinSheet = parentSheet?.viewController?.isFocusedViewWithinSheet() == true
387
+ val shouldDismissKeyboard = isFocusedViewWithinSheet || viewController.isDimmedAtDetentIndex(detentIndex)
388
+ if (KeyboardUtils.isKeyboardVisible(reactContext) && shouldDismissKeyboard) {
389
+ viewController.saveFocusedView()
390
+ KeyboardUtils.dismiss(this) {
391
+ post { present(detentIndex, animated, promiseCallback) }
392
+ }
393
+ return
394
+ }
395
+
396
+ // Attach coordinator to the root container
397
+ rootContainerView = findRootContainerView()
398
+ viewController.coordinatorLayout?.let { rootContainerView?.addView(it) }
399
+
400
+ // Register with observer to track sheet stack hierarchy
401
+ viewController.parentSheetView = TrueSheetStackManager.registerSheet(this)
402
+
338
403
  viewController.presentPromise = promiseCallback
339
404
  viewController.present(detentIndex, animated)
340
405
  }
341
406
 
342
- /**
343
- * Dismisses the sheet.
344
- *
345
- * @param promiseCallback Callback invoked when dismissal completes
346
- */
347
407
  @UiThread
348
- fun dismiss(promiseCallback: () -> Unit) {
408
+ fun handleBackPress() {
409
+ viewController.handleBackPress()
410
+ }
411
+
412
+ @UiThread
413
+ fun dismiss(animated: Boolean = true, promiseCallback: () -> Unit) {
414
+ if (viewController.isBeingDismissed || !viewController.isPresented) {
415
+ RNLog.w(reactContext, "TrueSheet: sheet is already dismissed. No need to dismiss it again.")
416
+ promiseCallback()
417
+ return
418
+ }
419
+
420
+ // Dismiss all sheets above first
421
+ dismissStack(animated) {}
422
+
423
+ // Then dismiss itself
349
424
  viewController.dismissPromise = promiseCallback
350
- viewController.dismiss()
425
+ viewController.dismiss(animated)
351
426
  }
352
427
 
353
- override fun viewControllerDidChangeSize(width: Int, height: Int) {
354
- // Dispatch size change event to JS
428
+ @UiThread
429
+ fun dismissStack(animated: Boolean = true, promiseCallback: () -> Unit) {
430
+ if (viewController.isBeingDismissed || !viewController.isPresented) {
431
+ RNLog.w(reactContext, "TrueSheet: sheet is already dismissed. No need to dismiss it again.")
432
+ promiseCallback()
433
+ return
434
+ }
435
+
436
+ val sheetsAbove = TrueSheetStackManager.getSheetsAbove(this)
437
+ // Create snapshot only for topmost sheet (first in reversed list)
438
+ sheetsAbove.firstOrNull()?.viewController?.createSheetSnapshot()
439
+
440
+ for (sheet in sheetsAbove) {
441
+ sheet.viewController.dismiss(animated)
442
+ }
443
+
444
+ promiseCallback()
445
+ }
446
+
447
+ @UiThread
448
+ fun resize(detentIndex: Int, promiseCallback: () -> Unit) {
449
+ if (!viewController.isPresented) {
450
+ RNLog.w(reactContext, "TrueSheet: Cannot resize. Sheet is not presented.")
451
+ promiseCallback()
452
+ return
453
+ }
454
+
455
+ viewController.resizePromise = promiseCallback
456
+ viewController.resize(detentIndex)
457
+ }
458
+
459
+ /**
460
+ * Debounced sheet update to handle rapid content/header size changes.
461
+ * Uses post() to ensure all layout passes complete before reconfiguring.
462
+ */
463
+ fun updateSheetIfNeeded() {
464
+ if (!viewController.isPresented || isSheetUpdatePending) return
465
+
466
+ isSheetUpdatePending = true
467
+ viewController.post {
468
+ isSheetUpdatePending = false
469
+ if (viewController.containerView == null) return@post
470
+
471
+ viewController.setupSheetDetentsForSizeChange()
472
+ TrueSheetStackManager.updateParentTranslation(this)
473
+ }
474
+ }
475
+
476
+ // ==================== Sheet Stack Translation ====================
477
+
478
+ /**
479
+ * Updates this sheet's translation and disables dragging when a child sheet is presented.
480
+ * Parent sheets slide down to create a stacked appearance.
481
+ * Propagates additional translation to parent so the entire stack stays visually consistent.
482
+ */
483
+ fun updateTranslationForChild(childSheetTop: Int) {
484
+ if (!viewController.isSheetVisible || viewController.isExpanded) return
485
+
486
+ viewController.sheetView?.behavior?.isDraggable = false
487
+
488
+ val mySheetTop = viewController.detentCalculator.getSheetTopForDetentIndex(viewController.currentDetentIndex)
489
+ val newTranslation = maxOf(0, childSheetTop - mySheetTop)
490
+ val additionalTranslation = newTranslation - viewController.currentTranslationY
491
+
492
+ viewController.translateSheet(newTranslation)
493
+
494
+ // Propagate any additional translation up the stack
495
+ if (additionalTranslation > 0) {
496
+ TrueSheetStackManager.getParentSheet(this)?.addTranslation(additionalTranslation)
497
+ }
498
+ }
499
+
500
+ /**
501
+ * Recursively adds translation to this sheet and all parent sheets.
502
+ */
503
+ private fun addTranslation(amount: Int) {
504
+ if (viewController.isExpanded) return
505
+
506
+ viewController.translateSheet(viewController.currentTranslationY + amount)
507
+ TrueSheetStackManager.getParentSheet(this)?.addTranslation(amount)
508
+ }
509
+
510
+ /**
511
+ * Resets this sheet's translation and restores dragging when it becomes topmost.
512
+ * Parent recalculates its translation based on this sheet's position.
513
+ */
514
+ fun resetTranslation(onTranslateEnd: (() -> Unit)? = null) {
515
+ viewController.sheetView?.behavior?.isDraggable = viewController.draggable
516
+ viewController.translateSheet(0, onEnd = onTranslateEnd)
517
+
518
+ // Parent should recalculate its translation based on this sheet's position
519
+ val mySheetTop = viewController.detentCalculator.getSheetTopForDetentIndex(viewController.currentDetentIndex)
520
+ TrueSheetStackManager.getParentSheet(this)?.updateTranslationForChild(mySheetTop)
521
+ }
522
+
523
+ // ==================== TrueSheetViewControllerDelegate ====================
524
+
525
+ override fun viewControllerWillPresent(index: Int, position: Float, detent: Float) {
526
+ // Update parent sheet translation now that content is measured
527
+ TrueSheetStackManager.updateParentTranslation(this)
528
+
355
529
  val surfaceId = UIManagerHelper.getSurfaceId(this)
356
- eventDispatcher?.dispatchEvent(
357
- SizeChangeEvent(surfaceId, id, width, height)
358
- )
530
+ eventDispatcher?.dispatchEvent(WillPresentEvent(surfaceId, id, index, position, detent))
359
531
  }
360
532
 
361
- // ==================== TrueSheetContainerViewDelegate Implementation ====================
533
+ override fun viewControllerDidPresent(index: Int, position: Float, detent: Float) {
534
+ setupScreenEventObserver()
362
535
 
363
- override fun containerViewContentDidChangeSize(width: Int, height: Int) {
364
- // Clamp content height to container height to prevent unbounded growth with scrollable content
365
- val containerHeight = viewController.maxScreenHeight
366
- val contentHeight = if (containerHeight > 0) minOf(height, containerHeight) else height
536
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
537
+ eventDispatcher?.dispatchEvent(DidPresentEvent(surfaceId, id, index, position, detent))
538
+ }
367
539
 
368
- viewController.contentHeight = contentHeight
540
+ override fun viewControllerWillDismiss() {
541
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
542
+ eventDispatcher?.dispatchEvent(WillDismissEvent(surfaceId, id))
543
+ }
369
544
 
370
- // Update detents if sheet is already presented
371
- if (viewController.isPresented) {
372
- // Reconfigure sheet detents with new content height
373
- viewController.setupSheetDetents()
545
+ override fun viewControllerDidDismiss(parent: TrueSheetView?) {
546
+ // Detach coordinator from the root container view
547
+ viewController.coordinatorLayout?.let { rootContainerView?.removeView(it) }
548
+ rootContainerView = null
549
+
550
+ cleanupScreenEventObserver()
374
551
 
375
- // Use post to ensure layout is complete before positioning footer
376
- viewController.post {
377
- viewController.positionFooter()
552
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
553
+ eventDispatcher?.dispatchEvent(DidDismissEvent(surfaceId, id))
554
+
555
+ TrueSheetStackManager.unregisterSheet(this)
556
+
557
+ parent?.resetTranslation {
558
+ val parentController = parent.viewController
559
+ if (parentController.isPresented && !parentController.isBeingDismissed) {
560
+ parent.viewControllerDidFocus()
378
561
  }
379
562
  }
380
563
  }
381
564
 
565
+ override fun viewControllerDidChangeDetent(index: Int, position: Float, detent: Float) {
566
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
567
+ eventDispatcher?.dispatchEvent(DetentChangeEvent(surfaceId, id, index, position, detent))
568
+ }
569
+
570
+ override fun viewControllerDidDragBegin(index: Int, position: Float, detent: Float) {
571
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
572
+ eventDispatcher?.dispatchEvent(DragBeginEvent(surfaceId, id, index, position, detent))
573
+ }
574
+
575
+ override fun viewControllerDidDragChange(index: Int, position: Float, detent: Float) {
576
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
577
+ eventDispatcher?.dispatchEvent(DragChangeEvent(surfaceId, id, index, position, detent))
578
+ }
579
+
580
+ override fun viewControllerDidDragEnd(index: Int, position: Float, detent: Float) {
581
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
582
+ eventDispatcher?.dispatchEvent(DragEndEvent(surfaceId, id, index, position, detent))
583
+ }
584
+
585
+ override fun viewControllerDidChangePosition(index: Float, position: Float, detent: Float, realtime: Boolean) {
586
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
587
+ eventDispatcher?.dispatchEvent(PositionChangeEvent(surfaceId, id, index, position, detent, realtime))
588
+ }
589
+
590
+ override fun viewControllerDidChangeSize(width: Int, height: Int) {
591
+ updateState(width, height)
592
+ }
593
+
594
+ override fun viewControllerWillFocus() {
595
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
596
+ eventDispatcher?.dispatchEvent(WillFocusEvent(surfaceId, id))
597
+ }
598
+
599
+ override fun viewControllerDidFocus() {
600
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
601
+ eventDispatcher?.dispatchEvent(FocusEvent(surfaceId, id))
602
+ }
603
+
604
+ override fun viewControllerWillBlur() {
605
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
606
+ eventDispatcher?.dispatchEvent(WillBlurEvent(surfaceId, id))
607
+ }
608
+
609
+ override fun viewControllerDidBlur() {
610
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
611
+ eventDispatcher?.dispatchEvent(BlurEvent(surfaceId, id))
612
+ }
613
+
614
+ override fun viewControllerDidChangeVisibility(visible: Boolean) {
615
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
616
+ eventDispatcher?.dispatchEvent(VisibilityChangeEvent(surfaceId, id, visible))
617
+ }
618
+
619
+ // ==================== TrueSheetContainerViewDelegate ====================
620
+
621
+ override fun containerViewContentDidChangeSize(width: Int, height: Int) {
622
+ updateSheetIfNeeded()
623
+ }
624
+
625
+ override fun containerViewContentDidScroll() {
626
+ viewController.commitKeyboardDetent()
627
+ }
628
+
629
+ // When the ScrollView changes (e.g. conditional remount), re-pin the new ScrollView
630
+ // and request layout so BottomSheetBehavior re-discovers the nested scrolling child.
631
+ override fun containerViewScrollViewDidChange() {
632
+ setupScrollable()
633
+ viewController.sheetView?.requestLayout()
634
+ }
635
+
636
+ override fun containerViewHeaderDidChangeSize(width: Int, height: Int) {
637
+ updateSheetIfNeeded()
638
+ }
639
+
382
640
  override fun containerViewFooterDidChangeSize(width: Int, height: Int) {
383
- // Reposition footer when its size changes
384
- if (viewController.isPresented) {
385
- viewController.positionFooter()
641
+ // Footer height affects peek detents
642
+ updateSheetIfNeeded()
643
+ viewController.positionFooter()
644
+ }
645
+
646
+ override fun containerViewPeekDidChangeSize(width: Int, height: Int) {
647
+ // Peek content height affects peek detents
648
+ updateSheetIfNeeded()
649
+ }
650
+
651
+ // ==================== RNScreensEventObserverDelegate ====================
652
+
653
+ override fun presenterScreenWillDisappear() {
654
+ if (viewController.isPresented && viewController.isSheetVisible) {
655
+ KeyboardUtils.dismiss(this) {}
656
+ viewController.post { viewController.hideForScreen() }
657
+ }
658
+ }
659
+
660
+ override fun presenterScreenWillAppear() {
661
+ if (viewController.isPresented && viewController.wasHiddenByScreen) {
662
+ viewController.wasHiddenByScreen = false
663
+ viewController.showAfterScreen()
386
664
  }
387
665
  }
388
666
 
389
- companion object {
390
- const val TAG_NAME = "TrueSheet"
667
+ // ==================== Private Helpers ====================
668
+
669
+ /**
670
+ * Find the root container view for presenting the sheet.
671
+ * This traverses up the view hierarchy to find the content view (android.R.id.content)
672
+ * of whichever window this view is in - whether that's the activity's window or a
673
+ * Modal's dialog window.
674
+ */
675
+ override fun findRootContainerView(): ViewGroup? {
676
+ var current: android.view.ViewParent? = parent
677
+
678
+ while (current != null) {
679
+ if (current is ViewGroup && current.id == android.R.id.content) {
680
+ return current
681
+ }
682
+ current = current.parent
683
+ }
684
+
685
+ return reactContext.currentActivity?.findViewById(android.R.id.content)
391
686
  }
392
687
  }