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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/README.md +25 -4
  2. package/android/build.gradle +22 -1
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +50 -16
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +302 -184
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +21 -110
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +116 -0
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +18 -0
  11. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +119 -50
  12. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +360 -163
  13. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +54 -12
  14. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +22 -9
  15. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +84 -5
  16. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +104 -39
  17. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +3 -0
  18. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +137 -0
  19. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
  20. package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +15 -2
  21. package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
  22. package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
  23. package/android/src/main/jni/TrueSheetSpec.h +1 -0
  24. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +1 -9
  25. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +110 -30
  26. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +30 -7
  27. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +2 -2
  28. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +9 -8
  29. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
  30. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +9 -3
  31. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +3 -6
  32. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
  33. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +4 -6
  34. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
  35. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
  36. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +43 -0
  37. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
  38. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
  39. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +33 -0
  40. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
  41. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +7 -40
  42. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
  43. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
  44. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
  45. package/ios/TrueSheetContainerView.h +27 -7
  46. package/ios/TrueSheetContainerView.mm +31 -11
  47. package/ios/TrueSheetContentView.h +43 -15
  48. package/ios/TrueSheetContentView.mm +211 -189
  49. package/ios/TrueSheetFooterView.h +6 -0
  50. package/ios/TrueSheetFooterView.mm +93 -17
  51. package/ios/TrueSheetModule.h +2 -7
  52. package/ios/TrueSheetModule.mm +7 -16
  53. package/ios/TrueSheetOverlayView.h +32 -0
  54. package/ios/TrueSheetOverlayView.mm +161 -0
  55. package/ios/TrueSheetView.mm +212 -55
  56. package/ios/TrueSheetViewController.h +31 -3
  57. package/ios/TrueSheetViewController.mm +393 -94
  58. package/ios/core/TrueSheetBlurView.h +0 -2
  59. package/ios/core/TrueSheetBlurView.mm +3 -36
  60. package/ios/core/TrueSheetDetentCalculator.h +7 -1
  61. package/ios/core/TrueSheetDetentCalculator.mm +27 -4
  62. package/ios/core/TrueSheetGrabberView.mm +13 -3
  63. package/ios/core/TrueSheetKeyboardObserver.mm +2 -0
  64. package/ios/core/TrueSheetOverlayContainerView.h +31 -0
  65. package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
  66. package/ios/events/TrueSheetLifecycleEvents.h +2 -0
  67. package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
  68. package/ios/tvos/TrueSheetStubs.mm +12 -0
  69. package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
  70. package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
  71. package/lib/module/TrueSheet.js +173 -39
  72. package/lib/module/TrueSheet.js.map +1 -1
  73. package/lib/module/TrueSheet.web.js +218 -243
  74. package/lib/module/TrueSheet.web.js.map +1 -1
  75. package/lib/module/TrueSheetBackground.js +15 -0
  76. package/lib/module/TrueSheetBackground.js.map +1 -0
  77. package/lib/module/TrueSheetOverlay.js +30 -0
  78. package/lib/module/TrueSheetOverlay.js.map +1 -0
  79. package/lib/module/TrueSheetOverlay.web.js +35 -0
  80. package/lib/module/TrueSheetOverlay.web.js.map +1 -0
  81. package/lib/module/TrueSheetPeek.web.js +20 -15
  82. package/lib/module/TrueSheetPeek.web.js.map +1 -1
  83. package/lib/module/detentBackgrounds.js +21 -0
  84. package/lib/module/detentBackgrounds.js.map +1 -0
  85. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  86. package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  87. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +14 -10
  88. package/lib/module/index.js +1 -0
  89. package/lib/module/index.js.map +1 -1
  90. package/lib/module/mocks/index.js +18 -2
  91. package/lib/module/mocks/index.js.map +1 -1
  92. package/lib/module/mocks/navigationExpoRouter.js.map +1 -1
  93. package/lib/module/navigation/base-types.js +2 -0
  94. package/lib/module/navigation/base-types.js.map +1 -0
  95. package/lib/module/navigation/createTrueSheetRouter.js +0 -9
  96. package/lib/module/navigation/createTrueSheetRouter.js.map +1 -1
  97. package/lib/module/navigation/expo-router/base-types.js +2 -0
  98. package/lib/module/navigation/expo-router/base-types.js.map +1 -0
  99. package/lib/module/navigation/expo-router/index.js +10 -7
  100. package/lib/module/navigation/expo-router/index.js.map +1 -1
  101. package/lib/module/navigation/expo-router/types.js +4 -0
  102. package/lib/module/navigation/expo-router/types.js.map +1 -0
  103. package/lib/module/navigation/index.js.map +1 -1
  104. package/lib/module/navigation/screen/useSheetScreenState.js +1 -0
  105. package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
  106. package/lib/module/web/constants.js +1 -1
  107. package/lib/module/web/constants.js.map +1 -1
  108. package/lib/module/web/layout.js +168 -0
  109. package/lib/module/web/layout.js.map +1 -0
  110. package/lib/module/web/vaul/constants.js +4 -0
  111. package/lib/module/web/vaul/constants.js.map +1 -1
  112. package/lib/module/web/vaul/context.js.map +1 -1
  113. package/lib/module/web/vaul/helpers.js +1 -0
  114. package/lib/module/web/vaul/helpers.js.map +1 -1
  115. package/lib/module/web/vaul/index.js +44 -6
  116. package/lib/module/web/vaul/index.js.map +1 -1
  117. package/lib/module/web/vaul/use-snap-points.js +6 -8
  118. package/lib/module/web/vaul/use-snap-points.js.map +1 -1
  119. package/lib/typescript/src/TrueSheet.d.ts +6 -0
  120. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  121. package/lib/typescript/src/TrueSheet.types.d.ts +135 -34
  122. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  123. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  124. package/lib/typescript/src/TrueSheetBackground.d.ts +3 -0
  125. package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
  126. package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
  127. package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
  128. package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
  129. package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
  130. package/lib/typescript/src/TrueSheetPeek.web.d.ts +8 -4
  131. package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
  132. package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
  133. package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
  134. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
  135. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
  136. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +11 -7
  137. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  138. package/lib/typescript/src/index.d.ts +1 -0
  139. package/lib/typescript/src/index.d.ts.map +1 -1
  140. package/lib/typescript/src/mocks/index.d.ts +4 -0
  141. package/lib/typescript/src/mocks/index.d.ts.map +1 -1
  142. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +3 -3
  143. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -1
  144. package/lib/typescript/src/navigation/base-types.d.ts +9 -0
  145. package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
  146. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +2 -13
  147. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -1
  148. package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
  149. package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
  150. package/lib/typescript/src/navigation/expo-router/index.d.ts +8 -6
  151. package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -1
  152. package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
  153. package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
  154. package/lib/typescript/src/navigation/index.d.ts +3 -4
  155. package/lib/typescript/src/navigation/index.d.ts.map +1 -1
  156. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +2 -1
  157. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
  158. package/lib/typescript/src/navigation/types.d.ts +41 -5
  159. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  160. package/lib/typescript/src/web/constants.d.ts +1 -1
  161. package/lib/typescript/src/web/constants.d.ts.map +1 -1
  162. package/lib/typescript/src/web/layout.d.ts +19 -0
  163. package/lib/typescript/src/web/layout.d.ts.map +1 -0
  164. package/lib/typescript/src/web/vaul/constants.d.ts +1 -0
  165. package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
  166. package/lib/typescript/src/web/vaul/context.d.ts +1 -0
  167. package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
  168. package/lib/typescript/src/web/vaul/helpers.d.ts +1 -0
  169. package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
  170. package/lib/typescript/src/web/vaul/index.d.ts +7 -1
  171. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
  172. package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
  173. package/package.json +17 -8
  174. package/react-native.config.js +1 -0
  175. package/src/TrueSheet.tsx +206 -48
  176. package/src/TrueSheet.types.ts +142 -35
  177. package/src/TrueSheet.web.tsx +270 -290
  178. package/src/TrueSheetBackground.tsx +17 -0
  179. package/src/TrueSheetOverlay.tsx +24 -0
  180. package/src/TrueSheetOverlay.web.tsx +33 -0
  181. package/src/TrueSheetPeek.web.tsx +25 -23
  182. package/src/detentBackgrounds.ts +24 -0
  183. package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  184. package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  185. package/src/fabric/TrueSheetViewNativeComponent.ts +14 -10
  186. package/src/index.ts +1 -0
  187. package/src/mocks/index.ts +17 -2
  188. package/src/mocks/navigationExpoRouter.ts +6 -5
  189. package/src/navigation/base-types.ts +20 -0
  190. package/src/navigation/createTrueSheetRouter.ts +5 -33
  191. package/src/navigation/expo-router/base-types.ts +18 -0
  192. package/src/navigation/expo-router/index.ts +28 -30
  193. package/src/navigation/expo-router/types.ts +116 -0
  194. package/src/navigation/index.ts +6 -7
  195. package/src/navigation/screen/useSheetScreenState.ts +2 -0
  196. package/src/navigation/types.ts +75 -22
  197. package/src/web/constants.ts +1 -1
  198. package/src/web/layout.ts +196 -0
  199. package/src/web/vaul/constants.ts +4 -0
  200. package/src/web/vaul/context.ts +1 -0
  201. package/src/web/vaul/helpers.ts +5 -0
  202. package/src/web/vaul/index.tsx +67 -5
  203. package/src/web/vaul/use-snap-points.ts +6 -14
@@ -14,8 +14,6 @@ NS_ASSUME_NONNULL_BEGIN
14
14
  @interface TrueSheetBlurView : UIVisualEffectView
15
15
 
16
16
  @property (nonatomic, assign) facebook::react::TrueSheetViewBackgroundBlur backgroundBlur;
17
- @property (nonatomic, strong, nullable) NSNumber *blurIntensity;
18
- @property (nonatomic, assign) BOOL blurInteraction;
19
17
 
20
18
  /// Adds the blur view to a parent view with proper constraints
21
19
  - (void)addToView:(UIView *)parentView;
@@ -13,15 +13,13 @@
13
13
 
14
14
  using namespace facebook::react;
15
15
 
16
- @implementation TrueSheetBlurView {
17
- UIViewPropertyAnimator *_blurAnimator;
18
- }
16
+ @implementation TrueSheetBlurView
19
17
 
20
18
  #pragma mark - Initialization
21
19
 
22
20
  - (instancetype)init {
23
21
  if (self = [super init]) {
24
- _blurInteraction = YES;
22
+ self.userInteractionEnabled = NO;
25
23
  }
26
24
  return self;
27
25
  }
@@ -38,45 +36,14 @@ using namespace facebook::react;
38
36
  [parentView insertSubview:self atIndex:0];
39
37
  }
40
38
 
41
- - (void)clearAnimator {
42
- if (_blurAnimator) {
43
- [_blurAnimator stopAnimation:YES];
44
- _blurAnimator = nil;
45
- }
46
- }
47
-
48
39
  - (void)applyBlurEffect {
49
- self.userInteractionEnabled = self.blurInteraction;
50
-
51
40
  if (self.backgroundBlur == TrueSheetViewBackgroundBlur::None) {
52
- [self clearAnimator];
53
41
  self.effect = nil;
54
42
  return;
55
43
  }
56
44
 
57
45
  UIBlurEffectStyle style = [BlurUtil blurEffectStyleFromEnum:self.backgroundBlur];
58
- UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:style];
59
-
60
- CGFloat intensity =
61
- (self.blurIntensity && [self.blurIntensity floatValue] >= 0) ? [self.blurIntensity floatValue] / 100.0 : 1.0;
62
-
63
- if (intensity >= 1.0) {
64
- [self clearAnimator];
65
- self.effect = blurEffect;
66
- return;
67
- }
68
-
69
- if (!_blurAnimator) {
70
- __weak __typeof(self) weakSelf = self;
71
- _blurAnimator = [[UIViewPropertyAnimator alloc] initWithDuration:1.0
72
- curve:UIViewAnimationCurveLinear
73
- animations:^{
74
- weakSelf.effect = blurEffect;
75
- }];
76
- }
77
-
78
- _blurAnimator.pausesOnCompletion = YES;
79
- _blurAnimator.fractionComplete = intensity;
46
+ self.effect = [UIBlurEffect effectWithStyle:style];
80
47
  }
81
48
 
82
49
  @end
@@ -24,6 +24,9 @@ NS_ASSUME_NONNULL_BEGIN
24
24
  @property (nonatomic, readonly) BOOL absoluteHeader;
25
25
  @property (nonatomic, strong, readonly, nullable) NSNumber *footerHeight;
26
26
  @property (nonatomic, readonly) BOOL absoluteFooter;
27
+ @property (nonatomic, readonly) BOOL footerInsetAdjustment;
28
+ @property (nonatomic, readonly) BOOL footerAvoidsKeyboard;
29
+ @property (nonatomic, readonly) CGFloat appliedFooterBottomInset;
27
30
  @property (nonatomic, strong, readonly, nullable) NSNumber *peekContentHeight;
28
31
 
29
32
  @end
@@ -43,12 +46,15 @@ NS_ASSUME_NONNULL_BEGIN
43
46
  For peek (-2) detents, calculates based on header + footer height.
44
47
  */
45
48
  - (CGFloat)detentValueForIndex:(NSInteger)index;
49
+ - (NSInteger)backgroundIndexForPosition:(CGFloat)position displayedIndex:(NSInteger)displayedIndex;
46
50
 
47
51
  /**
48
52
  Returns the height for auto (-1) detents: content + header + footer height.
49
53
  An absolute (floating) header or footer contributes no height.
54
+ A pinned footer contributes only its overlap above the keyboard. UIKit adds
55
+ the keyboard expansion separately. Pass 0 for the keyboard-free height.
50
56
  */
51
- - (CGFloat)autoHeight;
57
+ - (CGFloat)autoHeightForKeyboardHeight:(CGFloat)keyboardHeight;
52
58
 
53
59
  /**
54
60
  Returns the height for peek (-2) detents: header + footer + peek content height.
@@ -19,7 +19,7 @@
19
19
  if (index >= 0 && index < (NSInteger)detents.count) {
20
20
  CGFloat value = [detents[index] doubleValue];
21
21
  if (value == -1) {
22
- return [self autoHeight] / self.delegate.screenHeight;
22
+ return [self autoHeightForKeyboardHeight:0] / self.delegate.screenHeight;
23
23
  }
24
24
  if (value == -2) {
25
25
  return [self peekHeight] / self.delegate.screenHeight;
@@ -29,10 +29,15 @@
29
29
  return 0;
30
30
  }
31
31
 
32
- - (CGFloat)autoHeight {
33
- // An absolute (floating) header or footer overlaps the content, so it contributes no height
32
+ - (CGFloat)autoHeightForKeyboardHeight:(CGFloat)keyboardHeight {
33
+ // An absolute (floating) header or footer overlaps the content, so it contributes no height —
34
+ // unless the footer pads the scrollable, then the content ends above it like a relative footer
34
35
  CGFloat headerHeight = self.delegate.absoluteHeader ? 0 : [self.delegate.headerHeight floatValue];
35
- CGFloat footerHeight = self.delegate.absoluteFooter ? 0 : [self.delegate.footerHeight floatValue];
36
+ BOOL floatingFooter = self.delegate.absoluteFooter && !self.delegate.footerInsetAdjustment;
37
+ CGFloat footerHeight = floatingFooter ? 0 : [self.delegate.footerHeight floatValue];
38
+ if (self.delegate.absoluteFooter && !floatingFooter && !self.delegate.footerAvoidsKeyboard) {
39
+ footerHeight -= MIN(keyboardHeight, MAX(0, footerHeight - self.delegate.appliedFooterBottomInset));
40
+ }
36
41
  return [self.delegate.contentHeight floatValue] + headerHeight + footerHeight;
37
42
  }
38
43
 
@@ -93,6 +98,24 @@
93
98
  return 0;
94
99
  }
95
100
 
101
+ - (NSInteger)backgroundIndexForPosition:(CGFloat)position displayedIndex:(NSInteger)displayedIndex {
102
+ NSInteger count = self.delegate.detents.count;
103
+ if (count == 0)
104
+ return -1;
105
+
106
+ CGFloat height = self.delegate.screenHeight - position;
107
+ NSInteger nextIndex = MAX(0, MIN(displayedIndex, count - 1));
108
+ CGFloat distance = fabs(height - [self resolvedHeightForIndex:nextIndex]);
109
+ for (NSInteger index = 0; index < count; index++) {
110
+ CGFloat nextDistance = fabs(height - [self resolvedHeightForIndex:index]);
111
+ if (nextDistance < distance) {
112
+ nextIndex = index;
113
+ distance = nextDistance;
114
+ }
115
+ }
116
+ return nextIndex;
117
+ }
118
+
96
119
  - (BOOL)findSegmentForPosition:(CGFloat)position outIndex:(NSInteger *)outIndex outProgress:(CGFloat *)outProgress {
97
120
  NSArray<NSNumber *> *detents = self.delegate.detents;
98
121
  NSInteger count = detents.count;
@@ -91,6 +91,13 @@ static const CGFloat kHitPaddingVertical = 10.0;
91
91
  }
92
92
  }
93
93
 
94
+ // Pad the pill evenly like the system grabber. The view frame itself stays inside
95
+ // the sheet, since the reveal check reads the frame rather than this rect.
96
+ - (CGRect)accessibilityFrame {
97
+ CGRect rect = CGRectInset(_vibrancyView.frame, -kHitPaddingHorizontal, -kHitPaddingVertical);
98
+ return UIAccessibilityConvertFrameToScreenCoordinates(rect, self);
99
+ }
100
+
94
101
  - (void)accessibilityIncrement {
95
102
  if (_onIncrement) {
96
103
  _onIncrement();
@@ -144,14 +151,17 @@ static const CGFloat kHitPaddingVertical = 10.0;
144
151
  CGFloat topMargin = [self effectiveTopMargin];
145
152
  CGFloat parentWidth = self.superview ? self.superview.bounds.size.width : UIScreen.mainScreen.bounds.size.width;
146
153
 
154
+ // Keep the frame inside the sheet. A frame above the top edge reads as a clipped element
155
+ // to VoiceOver, and the sheet reveals it on focus by moving to the next detent.
147
156
  CGFloat frameWidth = pillWidth + kHitPaddingHorizontal * 2;
148
- CGFloat frameHeight = pillHeight + kHitPaddingVertical * 2;
149
- CGFloat frameY = topMargin - kHitPaddingVertical;
157
+ CGFloat frameY = MAX(0, topMargin - kHitPaddingVertical);
158
+ CGFloat pillY = topMargin - frameY;
159
+ CGFloat frameHeight = pillY + pillHeight + kHitPaddingVertical;
150
160
 
151
161
  self.frame = CGRectMake((parentWidth - frameWidth) / 2.0, frameY, frameWidth, frameHeight);
152
162
  self.backgroundColor = UIColor.clearColor;
153
163
 
154
- CGRect pillRect = CGRectMake(kHitPaddingHorizontal, kHitPaddingVertical, pillWidth, pillHeight);
164
+ CGRect pillRect = CGRectMake(kHitPaddingHorizontal, pillY, pillWidth, pillHeight);
155
165
  _vibrancyView.frame = pillRect;
156
166
  _vibrancyView.layer.cornerRadius = [self effectiveCornerRadius];
157
167
  _vibrancyView.clipsToBounds = YES;
@@ -98,6 +98,8 @@
98
98
  });
99
99
  }
100
100
 
101
+ _viewController.keyboardHeight = keyboardHeight;
102
+
101
103
  for (id<TrueSheetKeyboardObserverDelegate> delegate in _delegates) {
102
104
  if (keyboardHeight > 0) {
103
105
  [delegate keyboardWillShow:keyboardHeight duration:duration curve:curve];
@@ -0,0 +1,31 @@
1
+ //
2
+ // Created by Jovanni Lo (@lodev09)
3
+ // Copyright (c) 2024-present. All rights reserved.
4
+ //
5
+ // This source code is licensed under the MIT license found in the
6
+ // LICENSE file in the root directory of this source tree.
7
+ //
8
+
9
+ #ifdef RCT_NEW_ARCH_ENABLED
10
+
11
+ #import <UIKit/UIKit.h>
12
+
13
+ NS_ASSUME_NONNULL_BEGIN
14
+
15
+ @protocol TrueSheetOverlayContainerViewDelegate <NSObject>
16
+ - (void)overlayContainerViewDidLayout:(CGSize)size;
17
+ @end
18
+
19
+ /**
20
+ * Window-level view hosting an overlay's children.
21
+ * Touches that miss every child fall through to the views beneath.
22
+ */
23
+ @interface TrueSheetOverlayContainerView : UIView
24
+
25
+ @property (nonatomic, weak, nullable) id<TrueSheetOverlayContainerViewDelegate> delegate;
26
+
27
+ @end
28
+
29
+ NS_ASSUME_NONNULL_END
30
+
31
+ #endif
@@ -0,0 +1,35 @@
1
+ //
2
+ // Created by Jovanni Lo (@lodev09)
3
+ // Copyright (c) 2024-present. All rights reserved.
4
+ //
5
+ // This source code is licensed under the MIT license found in the
6
+ // LICENSE file in the root directory of this source tree.
7
+ //
8
+
9
+ #ifdef RCT_NEW_ARCH_ENABLED
10
+
11
+ #import "TrueSheetOverlayContainerView.h"
12
+
13
+ @implementation TrueSheetOverlayContainerView
14
+
15
+ - (instancetype)initWithFrame:(CGRect)frame {
16
+ if (self = [super initWithFrame:frame]) {
17
+ self.backgroundColor = UIColor.clearColor;
18
+ self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
19
+ }
20
+ return self;
21
+ }
22
+
23
+ - (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event {
24
+ UIView *hit = [super hitTest:point withEvent:event];
25
+ return hit == self ? nil : hit;
26
+ }
27
+
28
+ - (void)layoutSubviews {
29
+ [super layoutSubviews];
30
+ [self.delegate overlayContainerViewDidLayout:self.bounds.size];
31
+ }
32
+
33
+ @end
34
+
35
+ #endif
@@ -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