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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/README.md +28 -5
  2. package/android/build.gradle +23 -1
  3. package/android/proguard-rules.pro +3 -0
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +50 -16
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +303 -184
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +38 -109
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +13 -0
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +127 -0
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
  11. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
  12. package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +29 -3
  13. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +234 -66
  14. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +491 -186
  15. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +64 -10
  16. package/android/src/main/java/com/lodev09/truesheet/core/RNScreensEventObserver.kt +31 -14
  17. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetBehavior.kt +135 -0
  18. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +23 -23
  19. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +130 -6
  20. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +104 -39
  21. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +14 -17
  22. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetGrabberView.kt +2 -4
  23. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +148 -0
  24. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +33 -40
  25. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
  26. package/android/src/main/java/com/lodev09/truesheet/utils/ColorUtils.kt +19 -0
  27. package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +15 -2
  28. package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
  29. package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
  30. package/android/src/main/jni/TrueSheetSpec.h +1 -0
  31. package/android/src/main/jni/TrueSheetStateUpdater.cpp +37 -6
  32. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +1 -9
  33. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +114 -30
  34. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +30 -7
  35. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +2 -2
  36. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +9 -8
  37. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
  38. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +14 -3
  39. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +5 -6
  40. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
  41. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +10 -6
  42. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
  43. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
  44. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +43 -0
  45. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
  46. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
  47. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +33 -0
  48. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
  49. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +7 -40
  50. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
  51. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
  52. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
  53. package/ios/TrueSheetContainerView.h +23 -7
  54. package/ios/TrueSheetContainerView.mm +45 -25
  55. package/ios/TrueSheetContentView.h +44 -15
  56. package/ios/TrueSheetContentView.mm +221 -190
  57. package/ios/TrueSheetFooterView.mm +133 -28
  58. package/ios/TrueSheetModule.h +2 -7
  59. package/ios/TrueSheetModule.mm +18 -18
  60. package/ios/TrueSheetOverlayView.h +42 -0
  61. package/ios/TrueSheetOverlayView.mm +175 -0
  62. package/ios/TrueSheetView.h +2 -0
  63. package/ios/TrueSheetView.mm +268 -52
  64. package/ios/TrueSheetViewController.h +36 -3
  65. package/ios/TrueSheetViewController.mm +797 -102
  66. package/ios/core/RNScreensEventObserver.mm +31 -28
  67. package/ios/core/TrueSheetBlurView.h +0 -2
  68. package/ios/core/TrueSheetBlurView.mm +3 -36
  69. package/ios/core/TrueSheetDetentCalculator.h +18 -1
  70. package/ios/core/TrueSheetDetentCalculator.mm +44 -9
  71. package/ios/core/TrueSheetDimView.h +27 -0
  72. package/ios/core/TrueSheetDimView.mm +41 -0
  73. package/ios/core/TrueSheetGrabberView.mm +13 -3
  74. package/ios/core/TrueSheetKeyboardObserver.h +7 -0
  75. package/ios/core/TrueSheetKeyboardObserver.mm +107 -15
  76. package/ios/core/TrueSheetOverlayContainerView.h +31 -0
  77. package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
  78. package/ios/events/TrueSheetLifecycleEvents.h +2 -0
  79. package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
  80. package/ios/tvos/TrueSheetStubs.mm +12 -0
  81. package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
  82. package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
  83. package/lib/module/TrueSheet.js +222 -54
  84. package/lib/module/TrueSheet.js.map +1 -1
  85. package/lib/module/TrueSheet.web.js +364 -275
  86. package/lib/module/TrueSheet.web.js.map +1 -1
  87. package/lib/module/TrueSheetBackground.js +15 -0
  88. package/lib/module/TrueSheetBackground.js.map +1 -0
  89. package/lib/module/TrueSheetOverlay.js +30 -0
  90. package/lib/module/TrueSheetOverlay.js.map +1 -0
  91. package/lib/module/TrueSheetOverlay.web.js +35 -0
  92. package/lib/module/TrueSheetOverlay.web.js.map +1 -0
  93. package/lib/module/TrueSheetPeek.web.js +20 -15
  94. package/lib/module/TrueSheetPeek.web.js.map +1 -1
  95. package/lib/module/TrueSheetProvider.web.js +13 -9
  96. package/lib/module/TrueSheetProvider.web.js.map +1 -1
  97. package/lib/module/detentBackgrounds.js +21 -0
  98. package/lib/module/detentBackgrounds.js.map +1 -0
  99. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  100. package/lib/module/fabric/TrueSheetFooterViewNativeComponent.ts +2 -3
  101. package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  102. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +89 -73
  103. package/lib/module/index.js +1 -0
  104. package/lib/module/index.js.map +1 -1
  105. package/lib/module/mocks/index.js +18 -2
  106. package/lib/module/mocks/index.js.map +1 -1
  107. package/lib/module/mocks/navigationExpoRouter.js.map +1 -1
  108. package/lib/module/navigation/base-types.js +2 -0
  109. package/lib/module/navigation/base-types.js.map +1 -0
  110. package/lib/module/navigation/createTrueSheetRouter.js +158 -100
  111. package/lib/module/navigation/createTrueSheetRouter.js.map +1 -1
  112. package/lib/module/navigation/expo-router/base-types.js +2 -0
  113. package/lib/module/navigation/expo-router/base-types.js.map +1 -0
  114. package/lib/module/navigation/expo-router/extendTrueSheetRouter.js +71 -0
  115. package/lib/module/navigation/expo-router/extendTrueSheetRouter.js.map +1 -0
  116. package/lib/module/navigation/expo-router/index.js +18 -7
  117. package/lib/module/navigation/expo-router/index.js.map +1 -1
  118. package/lib/module/navigation/expo-router/types.js +4 -0
  119. package/lib/module/navigation/expo-router/types.js.map +1 -0
  120. package/lib/module/navigation/index.js.map +1 -1
  121. package/lib/module/navigation/screen/useSheetScreenState.js +1 -0
  122. package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
  123. package/lib/module/scrollable.js +28 -0
  124. package/lib/module/scrollable.js.map +1 -0
  125. package/lib/module/web/constants.js +1 -1
  126. package/lib/module/web/constants.js.map +1 -1
  127. package/lib/module/web/keyboard.js +69 -0
  128. package/lib/module/web/keyboard.js.map +1 -0
  129. package/lib/module/web/layout.js +168 -0
  130. package/lib/module/web/layout.js.map +1 -0
  131. package/lib/module/web/vaul/constants.js +9 -0
  132. package/lib/module/web/vaul/constants.js.map +1 -1
  133. package/lib/module/web/vaul/context.js +4 -0
  134. package/lib/module/web/vaul/context.js.map +1 -1
  135. package/lib/module/web/vaul/helpers.js +1 -0
  136. package/lib/module/web/vaul/helpers.js.map +1 -1
  137. package/lib/module/web/vaul/index.js +83 -21
  138. package/lib/module/web/vaul/index.js.map +1 -1
  139. package/lib/module/web/vaul/use-snap-points.js +57 -28
  140. package/lib/module/web/vaul/use-snap-points.js.map +1 -1
  141. package/lib/typescript/src/TrueSheet.d.ts +18 -1
  142. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  143. package/lib/typescript/src/TrueSheet.types.d.ts +209 -35
  144. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  145. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  146. package/lib/typescript/src/TrueSheetBackground.d.ts +3 -0
  147. package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
  148. package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
  149. package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
  150. package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
  151. package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
  152. package/lib/typescript/src/TrueSheetPeek.web.d.ts +8 -4
  153. package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
  154. package/lib/typescript/src/TrueSheetProvider.web.d.ts +5 -1
  155. package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -1
  156. package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
  157. package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
  158. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts +2 -3
  159. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts.map +1 -1
  160. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
  161. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
  162. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +76 -65
  163. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  164. package/lib/typescript/src/index.d.ts +1 -0
  165. package/lib/typescript/src/index.d.ts.map +1 -1
  166. package/lib/typescript/src/mocks/index.d.ts +4 -0
  167. package/lib/typescript/src/mocks/index.d.ts.map +1 -1
  168. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +3 -3
  169. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -1
  170. package/lib/typescript/src/navigation/base-types.d.ts +9 -0
  171. package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
  172. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +36 -10
  173. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -1
  174. package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
  175. package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
  176. package/lib/typescript/src/navigation/expo-router/extendTrueSheetRouter.d.ts +33 -0
  177. package/lib/typescript/src/navigation/expo-router/extendTrueSheetRouter.d.ts.map +1 -0
  178. package/lib/typescript/src/navigation/expo-router/index.d.ts +9 -6
  179. package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -1
  180. package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
  181. package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
  182. package/lib/typescript/src/navigation/index.d.ts +3 -4
  183. package/lib/typescript/src/navigation/index.d.ts.map +1 -1
  184. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +2 -1
  185. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
  186. package/lib/typescript/src/navigation/types.d.ts +43 -6
  187. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  188. package/lib/typescript/src/scrollable.d.ts +2 -0
  189. package/lib/typescript/src/scrollable.d.ts.map +1 -0
  190. package/lib/typescript/src/web/constants.d.ts +1 -1
  191. package/lib/typescript/src/web/constants.d.ts.map +1 -1
  192. package/lib/typescript/src/web/keyboard.d.ts +15 -0
  193. package/lib/typescript/src/web/keyboard.d.ts.map +1 -0
  194. package/lib/typescript/src/web/layout.d.ts +20 -0
  195. package/lib/typescript/src/web/layout.d.ts.map +1 -0
  196. package/lib/typescript/src/web/vaul/constants.d.ts +3 -0
  197. package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
  198. package/lib/typescript/src/web/vaul/context.d.ts +5 -1
  199. package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
  200. package/lib/typescript/src/web/vaul/helpers.d.ts +1 -0
  201. package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
  202. package/lib/typescript/src/web/vaul/index.d.ts +20 -1
  203. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
  204. package/lib/typescript/src/web/vaul/types.d.ts +9 -0
  205. package/lib/typescript/src/web/vaul/types.d.ts.map +1 -1
  206. package/lib/typescript/src/web/vaul/use-snap-points.d.ts +6 -4
  207. package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
  208. package/package.json +18 -8
  209. package/react-native.config.js +1 -0
  210. package/src/TrueSheet.tsx +257 -62
  211. package/src/TrueSheet.types.ts +225 -35
  212. package/src/TrueSheet.web.tsx +445 -333
  213. package/src/TrueSheetBackground.tsx +17 -0
  214. package/src/TrueSheetOverlay.tsx +24 -0
  215. package/src/TrueSheetOverlay.web.tsx +33 -0
  216. package/src/TrueSheetPeek.web.tsx +25 -23
  217. package/src/TrueSheetProvider.web.tsx +22 -13
  218. package/src/detentBackgrounds.ts +24 -0
  219. package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  220. package/src/fabric/TrueSheetFooterViewNativeComponent.ts +2 -3
  221. package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  222. package/src/fabric/TrueSheetViewNativeComponent.ts +89 -73
  223. package/src/index.ts +1 -0
  224. package/src/mocks/index.ts +17 -2
  225. package/src/mocks/navigationExpoRouter.ts +6 -5
  226. package/src/navigation/base-types.ts +20 -0
  227. package/src/navigation/createTrueSheetRouter.ts +220 -144
  228. package/src/navigation/expo-router/base-types.ts +18 -0
  229. package/src/navigation/expo-router/extendTrueSheetRouter.ts +99 -0
  230. package/src/navigation/expo-router/index.ts +41 -30
  231. package/src/navigation/expo-router/types.ts +116 -0
  232. package/src/navigation/index.ts +6 -7
  233. package/src/navigation/screen/useSheetScreenState.ts +2 -0
  234. package/src/navigation/types.ts +81 -23
  235. package/src/scrollable.ts +25 -0
  236. package/src/web/constants.ts +1 -1
  237. package/src/web/keyboard.ts +79 -0
  238. package/src/web/layout.ts +196 -0
  239. package/src/web/vaul/constants.ts +9 -0
  240. package/src/web/vaul/context.ts +7 -1
  241. package/src/web/vaul/helpers.ts +5 -0
  242. package/src/web/vaul/index.tsx +113 -16
  243. package/src/web/vaul/types.ts +10 -0
  244. package/src/web/vaul/use-snap-points.ts +86 -33
@@ -33,6 +33,8 @@ NS_ASSUME_NONNULL_BEGIN
33
33
 
34
34
  + (void)emitDidDismiss:(std::shared_ptr<const facebook::react::EventEmitter>)eventEmitter;
35
35
 
36
+ + (void)emitDismissAttempt:(std::shared_ptr<const facebook::react::EventEmitter>)eventEmitter;
37
+
36
38
  @end
37
39
 
38
40
  NS_ASSUME_NONNULL_END
@@ -66,6 +66,14 @@
66
66
  emitter->onDidDismiss({});
67
67
  }
68
68
 
69
+ + (void)emitDismissAttempt:(std::shared_ptr<const facebook::react::EventEmitter>)eventEmitter {
70
+ if (!eventEmitter)
71
+ return;
72
+
73
+ auto emitter = std::static_pointer_cast<TrueSheetViewEventEmitter const>(eventEmitter);
74
+ emitter->onDismissAttempt({});
75
+ }
76
+
69
77
  @end
70
78
 
71
79
  #endif // RCT_NEW_ARCH_ENABLED
@@ -20,6 +20,7 @@
20
20
  #import <react/renderer/components/TrueSheetSpec/ComponentDescriptors.h>
21
21
  #import <react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h>
22
22
  #import <react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h>
23
+ #import <react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h>
23
24
  #import <react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h>
24
25
 
25
26
  #import <TrueSheetSpec/TrueSheetSpec.h>
@@ -94,6 +95,17 @@ using namespace facebook::react;
94
95
 
95
96
  @end
96
97
 
98
+ @interface TrueSheetOverlayView : RCTViewComponentView
99
+ @end
100
+
101
+ @implementation TrueSheetOverlayView
102
+
103
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
104
+ return concreteComponentDescriptorProvider<TrueSheetOverlayViewComponentDescriptor>();
105
+ }
106
+
107
+ @end
108
+
97
109
  #pragma mark - Module
98
110
 
99
111
  @interface TrueSheetModule : NSObject <RCTBridgeModule, NativeTrueSheetModuleSpec>
@@ -17,6 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
17
17
  - (void)setupEdgeInteractionWithScrollView:(nullable UIScrollView *)scrollView
18
18
  edge:(UIRectEdge)edge API_AVAILABLE(ios(26.0));
19
19
  - (void)cleanupEdgeInteraction API_AVAILABLE(ios(26.0));
20
+ - (nullable UIScrollView *)edgeInteractionScrollView API_AVAILABLE(ios(26.0));
20
21
 
21
22
  @end
22
23
 
@@ -31,15 +31,37 @@ static const void *kEdgeEffectHintKey = &kEdgeEffectHintKey;
31
31
  objc_setAssociatedObject(self, kEdgeEffectHintKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
32
32
  }
33
33
 
34
+ - (nullable UIScrollView *)edgeInteractionScrollView API_AVAILABLE(ios(26.0)) {
35
+ #if RNTS_IPHONE_OS_VERSION_AVAILABLE(26_0)
36
+ for (id<UIInteraction> interaction in self.interactions) {
37
+ if ([interaction isKindOfClass:[UIScrollEdgeElementContainerInteraction class]]) {
38
+ return ((UIScrollEdgeElementContainerInteraction *)interaction).scrollView;
39
+ }
40
+ }
41
+ #endif
42
+ return nil;
43
+ }
44
+
34
45
  - (void)setupEdgeInteractionWithScrollView:(nullable UIScrollView *)scrollView
35
46
  edge:(UIRectEdge)edge API_AVAILABLE(ios(26.0)) {
36
- [self cleanupEdgeInteraction];
37
-
38
47
  if (!scrollView) {
48
+ [self cleanupEdgeInteraction];
39
49
  return;
40
50
  }
41
51
 
42
52
  #if RNTS_IPHONE_OS_VERSION_AVAILABLE(26_0)
53
+ // Update in place — this re-runs on every footer/header size change, and
54
+ // tearing the interaction down mid keyboard animation flashes the edge
55
+ // effect at the footer's final position.
56
+ for (id<UIInteraction> interaction in self.interactions) {
57
+ if ([interaction isKindOfClass:[UIScrollEdgeElementContainerInteraction class]]) {
58
+ UIScrollEdgeElementContainerInteraction *existing = (UIScrollEdgeElementContainerInteraction *)interaction;
59
+ existing.scrollView = scrollView;
60
+ existing.edge = edge;
61
+ return;
62
+ }
63
+ }
64
+
43
65
  // UIScrollEdgeElementContainerInteraction requires standard UIKit element
44
66
  // descendants (UILabel, UIControl, etc.) to trigger the edge effect.
45
67
  // RCTViewComponentView subviews are not recognized, so we add a
@@ -7,8 +7,16 @@ import TrueSheetContentViewNativeComponent from './fabric/TrueSheetContentViewNa
7
7
  import TrueSheetHeaderViewNativeComponent from './fabric/TrueSheetHeaderViewNativeComponent';
8
8
  import TrueSheetFooterViewNativeComponent from './fabric/TrueSheetFooterViewNativeComponent';
9
9
  import TrueSheetModule from "./specs/NativeTrueSheetModule.js";
10
- import { Platform, StyleSheet, BackHandler, findNodeHandle, processColor } from 'react-native';
11
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
+ import { TrueSheetBackground } from "./TrueSheetBackground.js";
11
+ import { normalizeDetentBackground } from "./detentBackgrounds.js";
12
+ import { resolveScrollable } from "./scrollable.js";
13
+ import { Platform, StyleSheet, BackHandler, RootTagContext, findNodeHandle, processColor } from 'react-native';
14
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
15
+ // RN has no public API for hosting the element inspector inside a presented view;
16
+ // this is the internal RN's Modal uses. Dev-only so prod bundles never resolve it.
17
+ // The template literal keeps the RN babel preset's deep-import warning off while
18
+ // Metro still resolves the module statically.
19
+ const AppContainer = __DEV__ ? require(`react-native/Libraries/ReactNative/AppContainer`).default : null;
12
20
  const LINKING_ERROR = `The package '@lodev09/react-native-true-sheet' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
13
21
  ios: "- You have run 'pod install'\n",
14
22
  default: ''
@@ -16,12 +24,21 @@ const LINKING_ERROR = `The package '@lodev09/react-native-true-sheet' doesn't se
16
24
  if (!TrueSheetModule) {
17
25
  throw new Error(LINKING_ERROR);
18
26
  }
19
- // Claim touches that no descendant claimed so the responder negotiation
20
- // doesn't bubble up to touchables outside the sheet
21
- const absorbUnclaimedTouches = () => true;
22
-
23
27
  // Stop raw touch events from bubbling past the sheet to outside ancestors
24
28
  const stopTouchPropagation = event => event.stopPropagation();
29
+
30
+ // Stop the responder negotiation at the sheet boundary without claiming the
31
+ // responder. Views inside the sheet are asked before this one, so in-sheet
32
+ // touchables work normally; views outside are asked after, so stopping here
33
+ // keeps unclaimed touches from leaking to touchables behind the sheet.
34
+ // Claiming instead (returning true) would steal the responder from a pressed
35
+ // touchable whenever the negotiation re-runs mid-gesture — e.g. a second
36
+ // finger, or the duplicate touch stream from a nested react-native-screens
37
+ // screen that attaches its own touch handler inside the sheet.
38
+ const stopResponderNegotiation = event => {
39
+ event.stopPropagation();
40
+ return false;
41
+ };
25
42
  // Gates auto-present (initialDetentIndex) by one commit. Rendered last in the
26
43
  // sheet subtree so its mount effect runs after the content's mount effects —
27
44
  // updates they schedule (e.g. a navigation footer via setOptions) batch with
@@ -37,6 +54,7 @@ const InitialPresentGate = ({
37
54
  };
38
55
  export class TrueSheet extends PureComponent {
39
56
  displayName = 'TrueSheet';
57
+ static contextType = RootTagContext;
40
58
  backHandlerSubscription = null;
41
59
  isPresented = false;
42
60
  isSheetVisible = true;
@@ -47,10 +65,20 @@ export class TrueSheet extends PureComponent {
47
65
  static instances = {};
48
66
 
49
67
  /**
50
- * Resolver to be called when mount event is received
68
+ * All mounted sheets, named or not.
69
+ */
70
+ static mountedInstances = new Set();
71
+
72
+ /**
73
+ * Resolver to be called when mount event is received, or with `false` when the present is cancelled
51
74
  */
52
75
  presentationResolver = null;
53
76
 
77
+ /**
78
+ * Settles once a scheduled lazy unmount has committed
79
+ */
80
+ nativeViewUnmount = null;
81
+
54
82
  /**
55
83
  * Tracks if a present operation is in progress
56
84
  */
@@ -60,15 +88,17 @@ export class TrueSheet extends PureComponent {
60
88
  this.nativeRef = /*#__PURE__*/createRef();
61
89
  this.validateDetents();
62
90
 
63
- // Lazy load by default, except when initialDetentIndex is set (for auto-presentation)
64
- const shouldRenderImmediately = props.initialDetentIndex !== undefined && props.initialDetentIndex >= 0;
91
+ // Lazy load by default, except when auto-presenting or when lazy is disabled
92
+ const shouldRenderImmediately = props.initialDetentIndex !== undefined && props.initialDetentIndex >= 0 || props.lazy === false;
65
93
  this.state = {
66
94
  shouldRenderNativeView: shouldRenderImmediately,
67
- initialPresentReady: false
95
+ initialPresentReady: false,
96
+ scrollableHandle: null
68
97
  };
69
98
  this.onMount = this.onMount.bind(this);
70
99
  this.onWillDismiss = this.onWillDismiss.bind(this);
71
100
  this.onDidDismiss = this.onDidDismiss.bind(this);
101
+ this.onDismissAttempt = this.onDismissAttempt.bind(this);
72
102
  this.onWillPresent = this.onWillPresent.bind(this);
73
103
  this.onDidPresent = this.onDidPresent.bind(this);
74
104
  this.onDetentChange = this.onDetentChange.bind(this);
@@ -89,6 +119,32 @@ export class TrueSheet extends PureComponent {
89
119
  initialPresentReady: true
90
120
  });
91
121
  }
122
+
123
+ // Resolves after commit (didMount/didUpdate) — the scrollable mounts with the
124
+ // sheet content, so its ref is only populated once the native tree renders.
125
+ updateScrollableHandle() {
126
+ const {
127
+ scrollableRef
128
+ } = this.props;
129
+ let scrollableHandle = null;
130
+ if (scrollableRef?.current) {
131
+ try {
132
+ scrollableHandle = findNodeHandle(resolveScrollable(scrollableRef.current));
133
+ } catch (error) {
134
+ // findNodeHandle throws on a stale ref (e.g. a wrapper that never clears
135
+ // it on unmount) or a handle with no usable scroll accessor. Treat it as
136
+ // no scrollable instead of crashing.
137
+ if (__DEV__) {
138
+ console.warn('TrueSheet: scrollableRef could not be resolved to a native view. It may be stale or expose no scroll node.', error);
139
+ }
140
+ }
141
+ }
142
+ if (scrollableHandle !== this.state.scrollableHandle) {
143
+ this.setState({
144
+ scrollableHandle
145
+ });
146
+ }
147
+ }
92
148
  validateDetents() {
93
149
  const {
94
150
  detents,
@@ -203,6 +259,7 @@ export class TrueSheet extends PureComponent {
203
259
  * @returns Promise that resolves when all sheets are dismissed
204
260
  */
205
261
  static async dismissAll(animated = true) {
262
+ TrueSheet.mountedInstances.forEach(sheet => sheet.cancelPendingPresent());
206
263
  return TrueSheetModule?.dismissAll(animated);
207
264
  }
208
265
  registerInstance() {
@@ -232,25 +289,26 @@ export class TrueSheet extends PureComponent {
232
289
  onWillDismiss(event) {
233
290
  this.props.onWillDismiss?.(event);
234
291
  }
292
+ onDismissAttempt(event) {
293
+ this.props.onDismissAttempt?.(event);
294
+ }
235
295
  onDidDismiss(event) {
236
296
  this.isPresented = false;
237
297
  this.isSheetVisible = true;
238
298
  this.backHandlerSubscription?.remove();
239
299
  this.backHandlerSubscription = null;
240
300
 
241
- // Clean up native view after dismiss for lazy loading.
242
- // Skip unmount if a present is in progress to avoid race condition.
243
- if (!this.isPresenting) {
244
- this.setState({
245
- shouldRenderNativeView: false
246
- });
301
+ // Non-lazy content stays mounted; otherwise clean it up unless another presentation is active.
302
+ if (!this.isPresenting && this.props.lazy !== false) {
303
+ this.unmountNativeView();
247
304
  }
248
305
  this.props.onDidDismiss?.(event);
249
306
  }
250
307
  onMount(event) {
251
- // Resolve the mount promise if waiting
252
- if (this.presentationResolver) {
253
- this.presentationResolver();
308
+ // Resolve the mount promise if waiting. While an unmount is pending, the event is from
309
+ // the outgoing view — present() waits for the remount instead.
310
+ if (this.presentationResolver && !this.nativeViewUnmount) {
311
+ this.presentationResolver(true);
254
312
  this.presentationResolver = null;
255
313
  }
256
314
  this.props.onMount?.(event);
@@ -291,11 +349,22 @@ export class TrueSheet extends PureComponent {
291
349
  const nodeHandle = findNodeHandle(this.nativeRef.current);
292
350
  if (nodeHandle == null || nodeHandle === -1) return false;
293
351
 
294
- // When not dismissible, let back propagate (e.g. navigation goes back to the previous screen)
352
+ // When not dismissible, report the attempt. An attempt handler consumes the back
353
+ // press; otherwise let it propagate (e.g. navigation goes back to the previous screen)
295
354
  if (this.props.dismissible === false) {
296
- return this.props.onBackPress?.() ?? false;
355
+ const {
356
+ onDismissAttempt,
357
+ onBackPress
358
+ } = this.props;
359
+ onDismissAttempt?.({
360
+ nativeEvent: null
361
+ });
362
+ return onBackPress?.() ?? onDismissAttempt != null;
297
363
  }
298
- TrueSheetModule?.handleBackPress(nodeHandle);
364
+
365
+ // Rejects when the native view is gone but JS hasn't unmounted yet (e.g. the surface
366
+ // stopped while this back press was queued). Nothing is left to dismiss.
367
+ TrueSheetModule?.handleBackPress(nodeHandle).catch(() => {});
299
368
  return this.props.onBackPress?.() ?? true;
300
369
  }
301
370
 
@@ -309,21 +378,67 @@ export class TrueSheet extends PureComponent {
309
378
  if (index < 0 || index >= detentsLength) {
310
379
  throw new Error(`TrueSheet: present index (${index}) is out of bounds. detents array has ${detentsLength} item(s)`);
311
380
  }
381
+
382
+ // Still waiting on the lazy mount — like native, a second present is a no-op
383
+ if (this.presentationResolver) {
384
+ console.warn('TrueSheet: sheet is already presented. Use resize() to change detent.');
385
+ return;
386
+ }
312
387
  this.isPresenting = true;
313
388
 
314
389
  // Lazy load: render native view if not already rendered
315
- if (!this.state.shouldRenderNativeView) {
316
- await new Promise(resolve => {
390
+ const unmount = this.nativeViewUnmount;
391
+ if (unmount || !this.state.shouldRenderNativeView) {
392
+ const mounted = await new Promise(resolve => {
317
393
  this.presentationResolver = resolve;
318
- this.setState({
319
- shouldRenderNativeView: true
320
- });
394
+ const mount = () => {
395
+ if (this.presentationResolver === resolve) {
396
+ this.setState({
397
+ shouldRenderNativeView: true
398
+ });
399
+ }
400
+ };
401
+
402
+ // An unmount from the last dismiss may not have committed yet — state still reads
403
+ // mounted, and presenting now would lose the sheet when it lands. Remount after it.
404
+ if (unmount) {
405
+ unmount.then(mount);
406
+ } else {
407
+ mount();
408
+ }
321
409
  });
410
+
411
+ // Dismissed before the native view mounted
412
+ if (!mounted) {
413
+ this.isPresenting = false;
414
+ return;
415
+ }
322
416
  }
323
417
  await TrueSheetModule?.presentByRef(this.handle, index, animated);
324
418
  this.isPresenting = false;
325
419
  }
326
420
 
421
+ /**
422
+ * Cancels a present still waiting on the lazy mount. Returns whether one was pending.
423
+ */
424
+ cancelPendingPresent() {
425
+ if (!this.presentationResolver) return false;
426
+ this.presentationResolver(false);
427
+ this.presentationResolver = null;
428
+ this.unmountNativeView();
429
+ return true;
430
+ }
431
+ unmountNativeView() {
432
+ this.nativeViewUnmount = new Promise(resolve => {
433
+ this.setState({
434
+ shouldRenderNativeView: false
435
+ }, () => {
436
+ this.nativeViewUnmount = null;
437
+ resolve();
438
+ });
439
+ });
440
+ }
441
+
327
442
  /**
328
443
  * Resize the sheet to a given detent index.
329
444
  * @param index - The detent index to resize to
@@ -337,6 +452,8 @@ export class TrueSheet extends PureComponent {
337
452
  * @param animated - Whether to animate the dismissal (default: true)
338
453
  */
339
454
  async dismiss(animated = true) {
455
+ // Dismissing before the lazy mount finishes cancels the present — nothing was presented
456
+ if (this.cancelPendingPresent()) return;
340
457
  return TrueSheetModule?.dismissByRef(this.handle, animated);
341
458
  }
342
459
 
@@ -349,10 +466,18 @@ export class TrueSheet extends PureComponent {
349
466
  return TrueSheetModule?.dismissStackByRef(this.handle, animated);
350
467
  }
351
468
  componentDidMount() {
469
+ TrueSheet.mountedInstances.add(this);
352
470
  this.registerInstance();
471
+ this.updateScrollableHandle();
353
472
  }
354
473
  componentDidUpdate(prevProps) {
355
474
  this.registerInstance();
475
+ this.updateScrollableHandle();
476
+ if (prevProps.lazy !== false && this.props.lazy === false && !this.state.shouldRenderNativeView) {
477
+ this.setState({
478
+ shouldRenderNativeView: true
479
+ });
480
+ }
356
481
 
357
482
  // Validate when detents prop changes
358
483
  if (prevProps.detents !== this.props.detents) {
@@ -371,31 +496,39 @@ export class TrueSheet extends PureComponent {
371
496
  }
372
497
  }
373
498
  componentWillUnmount() {
499
+ TrueSheet.mountedInstances.delete(this);
374
500
  this.unregisterInstance();
375
501
  this.backHandlerSubscription?.remove();
376
502
  this.backHandlerSubscription = null;
503
+ // Don't leave a present waiting on the lazy mount hanging
504
+ this.presentationResolver?.(false);
377
505
  this.presentationResolver = null;
378
506
  }
379
507
  render() {
380
508
  const {
381
509
  detents = [0.5, 1],
382
510
  backgroundColor,
511
+ detentBackgrounds,
512
+ background,
513
+ backgroundStyle,
383
514
  dismissible = true,
384
515
  draggable = true,
516
+ dismissThreshold = 'half',
385
517
  grabber = true,
386
518
  grabberOptions,
387
519
  accessibilityOptions,
388
520
  dimmed = true,
521
+ dimColor,
522
+ dimOpacity,
389
523
  initialDetentIndex = -1,
390
524
  initialDetentAnimated = true,
391
525
  dimmedDetentIndex,
392
526
  backgroundBlur,
393
- blurOptions,
394
527
  cornerRadius,
395
528
  maxContentHeight,
396
529
  maxContentWidth,
397
- anchor = 'center',
398
- anchorOffset,
530
+ placement = 'automatic',
531
+ placementOffset,
399
532
  scrollableOptions,
400
533
  footerOptions,
401
534
  presentation = 'page',
@@ -409,6 +542,7 @@ export class TrueSheet extends PureComponent {
409
542
  insetAdjustment = 'automatic',
410
543
  ...rest
411
544
  } = this.props;
545
+ delete rest.lazy;
412
546
 
413
547
  // Trim to max 3 detents and clamp fractions
414
548
  const resolvedDetents = detents.slice(0, 3).map(detent => {
@@ -435,33 +569,62 @@ export class TrueSheet extends PureComponent {
435
569
  color: processColor(grabberOptions?.color)
436
570
  };
437
571
  }
572
+ const sheetContent = /*#__PURE__*/_jsxs(_Fragment, {
573
+ children: [/*#__PURE__*/_jsx(TrueSheetBackground, {
574
+ background: background,
575
+ backgroundStyle: backgroundStyle
576
+ }), header && /*#__PURE__*/_jsx(TrueSheetHeaderViewNativeComponent, {
577
+ style: [styles.header, headerOptions?.position === 'absolute' && styles.absoluteHeader, headerStyle],
578
+ children: /*#__PURE__*/isValidElement(header) ? header : /*#__PURE__*/createElement(header)
579
+ }), /*#__PURE__*/_jsx(TrueSheetContentViewNativeComponent, {
580
+ style: style,
581
+ children: children
582
+ }), footer && /*#__PURE__*/_jsx(TrueSheetFooterViewNativeComponent, {
583
+ autoBottomInset: insetAdjustment === 'automatic',
584
+ style: [styles.footer, footerOptions?.position === 'absolute' ? styles.absoluteFooter : styles.relativeFooter, footerStyle],
585
+ children: /*#__PURE__*/isValidElement(footer) ? footer : /*#__PURE__*/createElement(footer)
586
+ }), autoPresent && !this.state.initialPresentReady && /*#__PURE__*/_jsx(InitialPresentGate, {
587
+ onReady: this.onInitialPresentReady
588
+ })]
589
+ });
438
590
  return /*#__PURE__*/_jsx(TrueSheetViewNativeComponent, {
439
591
  ...rest,
440
592
  ref: this.nativeRef,
441
593
  style: styles.sheetView,
442
594
  detents: resolvedDetents,
443
595
  backgroundBlur: backgroundBlur,
444
- blurOptions: blurOptions,
445
596
  backgroundColor: backgroundColor,
597
+ detentBackgrounds: detentBackgrounds && Array.from(detentBackgrounds.slice(0, 3), entry => {
598
+ const normalized = normalizeDetentBackground(entry);
599
+ return normalized ? {
600
+ ...normalized,
601
+ color: processColor(normalized.color)
602
+ } : {};
603
+ }),
446
604
  cornerRadius: cornerRadius,
447
605
  grabber: grabber,
448
606
  grabberOptions: this.resolvedGrabberOptions,
449
607
  accessibilityOptions: accessibilityOptions,
450
608
  dimmed: dimmed,
609
+ dimColor: dimColor,
610
+ dimOpacity: dimOpacity,
451
611
  dimmedDetentIndex: dimmedDetentIndex,
452
612
  initialDetentIndex: gatedInitialDetentIndex,
453
613
  initialDetentAnimated: initialDetentAnimated,
454
614
  dismissible: dismissible,
455
615
  draggable: draggable,
616
+ dismissThreshold: dismissThreshold,
456
617
  maxContentHeight: maxContentHeight,
457
618
  maxContentWidth: maxContentWidth,
458
- anchor: anchor,
459
- anchorOffset: anchorOffset,
619
+ placement: placement,
620
+ placementOffset: placementOffset,
621
+ scrollableHandle: this.state.scrollableHandle ?? -1,
460
622
  scrollableOptions: scrollableOptions,
461
623
  headerOptions: headerOptions,
462
624
  footerOptions: {
463
625
  footerPosition: footerOptions?.position,
464
- keyboardOffset: footerOptions?.keyboardOffset
626
+ keyboardOffset: footerOptions?.keyboardOffset,
627
+ avoidKeyboard: footerOptions?.avoidKeyboard
465
628
  },
466
629
  presentation: presentation,
467
630
  insetAdjustment: insetAdjustment,
@@ -470,6 +633,7 @@ export class TrueSheet extends PureComponent {
470
633
  onDidPresent: this.onDidPresent,
471
634
  onWillDismiss: this.onWillDismiss,
472
635
  onDidDismiss: this.onDidDismiss,
636
+ onDismissAttempt: this.onDismissAttempt,
473
637
  onDetentChange: this.onDetentChange,
474
638
  onDragBegin: this.onDragBegin,
475
639
  onDragChange: this.onDragChange,
@@ -482,30 +646,32 @@ export class TrueSheet extends PureComponent {
482
646
  onVisibilityChange: this.onVisibilityChange
483
647
  // These must stay on this host view, not on the container. The container is
484
648
  // reparented into the native sheet, so this view is never a native ancestor of
485
- // the sheet content. Claiming the responder here stops the negotiation at the
486
- // sheet boundary without making an ancestor of the content the JS responder,
487
- // which would block scrolling inside the sheet.
649
+ // the sheet content — this is the React-tree boundary between the sheet and
650
+ // the presenting screen, where both the responder negotiation and raw touch
651
+ // bubbling must stop.
488
652
  ,
489
- onStartShouldSetResponder: absorbUnclaimedTouches,
653
+ onStartShouldSetResponder: stopResponderNegotiation,
654
+ onMoveShouldSetResponder: stopResponderNegotiation,
490
655
  onTouchStart: stopTouchPropagation,
491
656
  onTouchMove: stopTouchPropagation,
492
657
  onTouchEnd: stopTouchPropagation,
493
658
  onTouchCancel: stopTouchPropagation,
494
- children: this.state.shouldRenderNativeView && /*#__PURE__*/_jsxs(TrueSheetContainerViewNativeComponent, {
659
+ children: this.state.shouldRenderNativeView && /*#__PURE__*/_jsx(TrueSheetContainerViewNativeComponent, {
495
660
  style: styles.container,
496
- children: [header && /*#__PURE__*/_jsx(TrueSheetHeaderViewNativeComponent, {
497
- style: [styles.header, headerOptions?.position === 'absolute' && styles.absoluteHeader, headerStyle],
498
- children: /*#__PURE__*/isValidElement(header) ? header : /*#__PURE__*/createElement(header)
499
- }), /*#__PURE__*/_jsx(TrueSheetContentViewNativeComponent, {
500
- style: style,
501
- children: children
502
- }), footer && /*#__PURE__*/_jsx(TrueSheetFooterViewNativeComponent, {
503
- autoBottomInset: insetAdjustment === 'automatic',
504
- style: [styles.footer, footerOptions?.position === 'absolute' ? styles.absoluteFooter : styles.relativeFooter, footerStyle],
505
- children: /*#__PURE__*/isValidElement(footer) ? footer : /*#__PURE__*/createElement(footer)
506
- }), autoPresent && !this.state.initialPresentReady && /*#__PURE__*/_jsx(InitialPresentGate, {
507
- onReady: this.onInitialPresentReady
508
- })]
661
+ children: AppContainer ?
662
+ /*#__PURE__*/
663
+ // Hosts the element inspector inside the sheet, like RN's Modal does —
664
+ // the app-level one sits behind the presented sheet.
665
+ // `display: contents` keeps AppContainer's wrapper views out of both
666
+ // Yoga layout and the native tree, so header/content/footer remain the
667
+ // container's direct native children, while the inspector overlay
668
+ // anchors to the container and lines up with measured frames.
669
+ _jsx(AppContainer, {
670
+ rootTag: this.context,
671
+ rootViewStyle: styles.inspectorHost,
672
+ internal_excludeLogBox: true,
673
+ children: sheetContent
674
+ }) : sheetContent
509
675
  })
510
676
  });
511
677
  }
@@ -516,13 +682,15 @@ TrueSheet;
516
682
  const styles = StyleSheet.create({
517
683
  sheetView: {
518
684
  ...StyleSheet.absoluteFill,
519
- zIndex: -9999,
520
- pointerEvents: 'box-none'
685
+ pointerEvents: 'none'
521
686
  },
522
687
  // Fill the sheet so flex layouts track the sheet's size per detent
523
688
  container: {
524
689
  ...StyleSheet.absoluteFill
525
690
  },
691
+ inspectorHost: {
692
+ display: 'contents'
693
+ },
526
694
  header: {
527
695
  pointerEvents: 'box-none'
528
696
  },