@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
@@ -13,6 +13,7 @@
13
13
  #import "TrueSheetContentView.h"
14
14
  #import "TrueSheetFooterView.h"
15
15
  #import "TrueSheetModule.h"
16
+ #import "TrueSheetOverlayView.h"
16
17
  #import "TrueSheetViewController.h"
17
18
  #import "core/RNScreensEventObserver.h"
18
19
  #import "events/TrueSheetDragEvents.h"
@@ -53,7 +54,7 @@ using namespace facebook::react;
53
54
  NSInteger _initialDetentIndex;
54
55
  TrueSheetViewInsetAdjustment _insetAdjustment;
55
56
  ScrollableOptions *_scrollableOptions;
56
- BOOL _hasAutoDetent;
57
+ NSInteger _scrollableHandle;
57
58
  BOOL _initialDetentAnimated;
58
59
  BOOL _isSheetUpdatePending;
59
60
  BOOL _pendingLayoutUpdate;
@@ -63,6 +64,9 @@ using namespace facebook::react;
63
64
  BOOL _pendingMountEvent;
64
65
  BOOL _pendingSizeChange;
65
66
  BOOL _pendingPropsUpdate;
67
+ BOOL _isPresentPending;
68
+ NSUInteger _pendingPresentGeneration;
69
+ TrueSheetCompletionBlock _pendingPresentCompletion;
66
70
  NSArray *_pendingDetents;
67
71
  RNScreensEventObserver *_screensEventObserver;
68
72
  }
@@ -124,13 +128,38 @@ using namespace facebook::react;
124
128
 
125
129
  UIViewController *vc = [self findPresentingViewController];
126
130
 
127
- // Only present if the view controller is in the same window and not being dismissed
128
- if (!vc || vc.view.window != self.window || _controller.isBeingDismissed) {
131
+ // Only present if the view controller is in the same window
132
+ if (!vc || vc.view.window != self.window) {
129
133
  // Animate next time when sheet finally moves to the correct window
130
134
  _initialDetentAnimated = YES;
131
135
  return;
132
136
  }
133
137
 
138
+ // A sheet still animating out of the presenter (e.g. our own controller,
139
+ // recycled mid-dismissal) blocks presentation — retry once its transition ends.
140
+ UIViewController *dismissing = vc.presentedViewController;
141
+ if (dismissing && dismissing.isBeingDismissed) {
142
+ _initialDetentAnimated = YES;
143
+
144
+ __weak __typeof(self) weakSelf = self;
145
+ void (^retry)(void) = ^{
146
+ dispatch_async(dispatch_get_main_queue(), ^{
147
+ [weakSelf presentInitialIfNeeded];
148
+ });
149
+ };
150
+
151
+ id<UIViewControllerTransitionCoordinator> coordinator = dismissing.transitionCoordinator;
152
+ if (coordinator) {
153
+ [coordinator animateAlongsideTransition:nil
154
+ completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
155
+ retry();
156
+ }];
157
+ } else {
158
+ retry();
159
+ }
160
+ return;
161
+ }
162
+
134
163
  _didInitiallyPresent = YES;
135
164
 
136
165
  // Deferred a runloop turn so sibling mounts from the current transaction
@@ -157,6 +186,8 @@ using namespace facebook::react;
157
186
  [_screensEventObserver stopObserving];
158
187
  _screensEventObserver = nil;
159
188
 
189
+ [self cancelPendingPresent];
190
+
160
191
  if (_controller && _controller.presentingViewController) {
161
192
  // Find the root presenting controller to dismiss the entire stack
162
193
  UIViewController *root = _controller.presentingViewController;
@@ -175,8 +206,6 @@ using namespace facebook::react;
175
206
 
176
207
  [_snapshotView removeFromSuperview];
177
208
  _snapshotView = nil;
178
-
179
- [TrueSheetModule unregisterViewWithTag:@(self.tag)];
180
209
  }
181
210
 
182
211
  #pragma mark - RCTComponentViewProtocol
@@ -185,6 +214,15 @@ using namespace facebook::react;
185
214
  return concreteComponentDescriptorProvider<TrueSheetViewComponentDescriptor>();
186
215
  }
187
216
 
217
+ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
218
+ oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics {
219
+ [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
220
+
221
+ // RN 0.88+ resets layout metrics on recycle, so the next mount force-applies
222
+ // `hidden` from the display type and would reveal this host over the screen.
223
+ self.hidden = YES;
224
+ }
225
+
188
226
  - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps {
189
227
  [super updateProps:props oldProps:oldProps];
190
228
 
@@ -192,12 +230,8 @@ using namespace facebook::react;
192
230
 
193
231
  // Detents (-1 represents "auto")
194
232
  NSMutableArray *detents = [NSMutableArray new];
195
- _hasAutoDetent = NO;
196
233
  for (const auto &detent : newProps.detents) {
197
234
  [detents addObject:@(detent)];
198
- if (detent == -1) {
199
- _hasAutoDetent = YES;
200
- }
201
235
  }
202
236
 
203
237
  if (oldProps) {
@@ -216,14 +250,29 @@ using namespace facebook::react;
216
250
  // Background color
217
251
  _controller.backgroundColor = RCTUIColorFromSharedColor(newProps.backgroundColor);
218
252
 
253
+ NSMutableArray<NSDictionary *> *detentBackgrounds = [NSMutableArray new];
254
+ ContextContainer contextContainer;
255
+ PropsParserContext parserContext{-1, contextContainer};
256
+ for (const auto &entry : newProps.detentBackgrounds) {
257
+ NSMutableDictionary *background = [NSMutableDictionary new];
258
+ UIColor *color = RCTUIColorFromSharedColor(entry.color);
259
+ if (color) {
260
+ background[@"color"] = color;
261
+ }
262
+ if (!entry.blur.empty()) {
263
+ auto blur = TrueSheetViewBackgroundBlur::None;
264
+ fromRawValue(parserContext, RawValue(entry.blur), blur);
265
+ background[@"blur"] = @(static_cast<int>(blur));
266
+ }
267
+ [detentBackgrounds addObject:background];
268
+ }
269
+ if (![_controller.detentBackgrounds isEqualToArray:detentBackgrounds]) {
270
+ _controller.detentBackgrounds = detentBackgrounds;
271
+ }
272
+
219
273
  // Blur tint
220
274
  _controller.backgroundBlur = newProps.backgroundBlur;
221
275
 
222
- // Blur options
223
- const auto &blurOpts = newProps.blurOptions;
224
- _controller.blurIntensity = blurOpts.intensity >= 0 ? @(blurOpts.intensity) : nil;
225
- _controller.blurInteraction = blurOpts.interaction;
226
-
227
276
  // Corner radius
228
277
  _controller.cornerRadius = newProps.cornerRadius < 0 ? nil : @(newProps.cornerRadius);
229
278
 
@@ -233,8 +282,9 @@ using namespace facebook::react;
233
282
  // Content width
234
283
  _controller.maxContentWidth = newProps.maxContentWidth != 0.0 ? @(newProps.maxContentWidth) : nil;
235
284
 
236
- // Anchor
237
- _controller.anchor = newProps.anchor;
285
+ _controller.placement = newProps.placement;
286
+
287
+ _controller.foldOptions = newProps.foldOptions;
238
288
 
239
289
  _controller.grabber = newProps.grabber;
240
290
 
@@ -269,6 +319,8 @@ using namespace facebook::react;
269
319
  _controller.dismissible = newProps.dismissible;
270
320
  _controller.draggable = newProps.draggable;
271
321
  _controller.dimmed = newProps.dimmed;
322
+ _controller.dimOpacity = newProps.dimOpacity < 0 ? nil : @(newProps.dimOpacity);
323
+ _controller.dimColor = RCTUIColorFromSharedColor(newProps.dimColor);
272
324
 
273
325
  if (newProps.dimmedDetentIndex >= 0) {
274
326
  _controller.dimmedDetentIndex = @(newProps.dimmedDetentIndex);
@@ -277,33 +329,38 @@ using namespace facebook::react;
277
329
  _initialDetentIndex = newProps.initialDetentIndex;
278
330
  _initialDetentAnimated = newProps.initialDetentAnimated;
279
331
 
280
- const auto &scrollableOpts = newProps.scrollableOptions;
281
- BOOL scrollingExpandsSheet = scrollableOpts.scrollingExpandsSheet;
282
- auto topEdgeEffect = scrollableOpts.topScrollEdgeEffect;
283
- auto bottomEdgeEffect = scrollableOpts.bottomScrollEdgeEffect;
284
- BOOL hasScrollableOptions = scrollableOpts.keyboardScrollOffset > 0 || !scrollingExpandsSheet ||
285
- topEdgeEffect != TrueSheetViewTopScrollEdgeEffect::Hidden ||
286
- bottomEdgeEffect != TrueSheetViewBottomScrollEdgeEffect::Hidden;
287
-
288
- if (hasScrollableOptions) {
289
- ScrollableOptions *options = [[ScrollableOptions alloc] init];
290
- options.keyboardScrollOffset = scrollableOpts.keyboardScrollOffset;
291
- options.scrollingExpandsSheet = scrollingExpandsSheet;
292
- options.topScrollEdgeEffect = topEdgeEffect;
293
- options.bottomScrollEdgeEffect = bottomEdgeEffect;
294
- _scrollableOptions = options;
295
- } else {
296
- _scrollableOptions = nil;
297
- }
332
+ _scrollableHandle = newProps.scrollableHandle;
298
333
 
299
- _controller.scrollingExpandsSheet = scrollingExpandsSheet;
334
+ const auto &scrollableOpts = newProps.scrollableOptions;
335
+ ScrollableOptions *options = [[ScrollableOptions alloc] init];
336
+ options.contentInsetAdjustment = scrollableOpts.contentInsetAdjustment;
337
+ options.keyboardScrollOffset = scrollableOpts.keyboardScrollOffset;
338
+ options.keyboardOffset = scrollableOpts.keyboardOffset;
339
+ options.scrollingExpandsSheet = scrollableOpts.scrollingExpandsSheet;
340
+ options.topScrollEdgeEffect = scrollableOpts.topScrollEdgeEffect;
341
+ options.bottomScrollEdgeEffect = scrollableOpts.bottomScrollEdgeEffect;
342
+
343
+ // nil while every option is at its default
344
+ BOOL hasScrollableOptions = options.keyboardScrollOffset > 0 || options.keyboardOffset != 0 ||
345
+ !options.scrollingExpandsSheet ||
346
+ options.contentInsetAdjustment != TrueSheetViewContentInsetAdjustment::Automatic ||
347
+ options.topScrollEdgeEffect != TrueSheetViewTopScrollEdgeEffect::Hidden ||
348
+ options.bottomScrollEdgeEffect != TrueSheetViewBottomScrollEdgeEffect::Hidden;
349
+ _scrollableOptions = hasScrollableOptions ? options : nil;
350
+
351
+ _controller.scrollingExpandsSheet = options.scrollingExpandsSheet;
300
352
 
301
353
  _controller.absoluteHeader = newProps.headerOptions.position == TrueSheetViewPosition::Absolute;
302
354
  _controller.absoluteFooter = newProps.footerOptions.footerPosition == TrueSheetViewFooterPosition::Absolute;
355
+ _controller.footerInsetAdjustment =
356
+ options.footerInsetAdjustment && _controller.absoluteFooter && _scrollableHandle > 0;
303
357
 
304
358
  CGFloat footerKeyboardOffset = newProps.footerOptions.keyboardOffset;
305
- if (_controller.footerKeyboardOffset != footerKeyboardOffset) {
359
+ BOOL footerAvoidsKeyboard = newProps.footerOptions.avoidKeyboard;
360
+ if (_controller.footerKeyboardOffset != footerKeyboardOffset ||
361
+ _controller.footerAvoidsKeyboard != footerAvoidsKeyboard) {
306
362
  _controller.footerKeyboardOffset = footerKeyboardOffset;
363
+ _controller.footerAvoidsKeyboard = footerAvoidsKeyboard;
307
364
  [_containerView updateFooterKeyboardOffset];
308
365
  }
309
366
 
@@ -366,7 +423,8 @@ using namespace facebook::react;
366
423
  [self setupScrollable];
367
424
 
368
425
  if (_controller.isPresented) {
369
- [self applySheetPropsUpdate];
426
+ _pendingPropsUpdate = YES;
427
+ [self setupSheetDetentsForSizeChange];
370
428
  } else if (_controller.isBeingPresented) {
371
429
  _pendingPropsUpdate = YES;
372
430
  } else if (_initialDetentIndex >= 0) {
@@ -380,7 +438,40 @@ using namespace facebook::react;
380
438
  - (void)prepareForRecycle {
381
439
  [super prepareForRecycle];
382
440
 
383
- [TrueSheetModule unregisterViewWithTag:@(self.tag)];
441
+ // Pooled mid-dismissal: the next mount can reuse this host — and its
442
+ // controller — while the previous sheet is still animating out. Detach until
443
+ // that transition ends so its tail (didBlur, didDismiss, position) can't reach
444
+ // the new mount, then hand the controller back and let auto-present retry.
445
+ if (_controller.isBeingDismissed) {
446
+ _controller.delegate = nil;
447
+
448
+ __weak __typeof(self) weakSelf = self;
449
+ void (^reattach)(void) = ^{
450
+ dispatch_async(dispatch_get_main_queue(), ^{
451
+ __typeof(self) strongSelf = weakSelf;
452
+ if (!strongSelf)
453
+ return;
454
+
455
+ strongSelf->_controller.delegate = strongSelf;
456
+ strongSelf->_controller.activeDetentIndex = -1;
457
+ if (strongSelf.window) {
458
+ [strongSelf presentInitialIfNeeded];
459
+ }
460
+ });
461
+ };
462
+
463
+ id<UIViewControllerTransitionCoordinator> coordinator = _controller.transitionCoordinator;
464
+ if (coordinator) {
465
+ [coordinator animateAlongsideTransition:nil
466
+ completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
467
+ reattach();
468
+ }];
469
+ } else {
470
+ reattach();
471
+ }
472
+ }
473
+
474
+ [self cancelPendingPresent];
384
475
 
385
476
  _lastStateSize = CGSizeZero;
386
477
  _didInitiallyPresent = NO;
@@ -421,7 +512,6 @@ using namespace facebook::react;
421
512
  [_touchHandler attachToView:_containerView];
422
513
  [_controller.view addSubview:_containerView];
423
514
  [_controller.view bringSubviewToFront:_containerView];
424
- _containerView.accessibilityViewIsModal = YES;
425
515
  _controller.accessibilityContentView = _containerView;
426
516
  [_controller setupAccessibilityContainer];
427
517
 
@@ -476,7 +566,7 @@ using namespace facebook::react;
476
566
  - (void)presentAtIndex:(NSInteger)index
477
567
  animated:(BOOL)animated
478
568
  completion:(nullable TrueSheetCompletionBlock)completion {
479
- if (_controller.isBeingPresented || _controller.isPresented) {
569
+ if (_controller.isBeingPresented || _controller.isPresented || _isPresentPending) {
480
570
  RCTLogWarn(@"TrueSheet: sheet is already presented. Use resize() to change detent.");
481
571
  if (completion) {
482
572
  completion(YES, nil);
@@ -498,11 +588,41 @@ using namespace facebook::react;
498
588
  return;
499
589
  }
500
590
 
591
+ // A controller still animating out of the presenter (e.g. a navigation modal
592
+ // closing) makes UIKit queue this presentation, so everything measured below
593
+ // goes stale before it starts. Present once that transition ends.
594
+ UIViewController *dismissing = presentingViewController.presentedViewController;
595
+ id<UIViewControllerTransitionCoordinator> dismissCoordinator =
596
+ dismissing.isBeingDismissed ? dismissing.transitionCoordinator : nil;
597
+ if (dismissCoordinator) {
598
+ _isPresentPending = YES;
599
+ _pendingPresentCompletion = completion;
600
+ NSUInteger generation = ++_pendingPresentGeneration;
601
+
602
+ __weak __typeof(self) weakSelf = self;
603
+ [dismissCoordinator
604
+ animateAlongsideTransition:nil
605
+ completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
606
+ dispatch_async(dispatch_get_main_queue(), ^{
607
+ __typeof(self) strongSelf = weakSelf;
608
+ if (!strongSelf || !strongSelf->_isPresentPending ||
609
+ strongSelf->_pendingPresentGeneration != generation)
610
+ return;
611
+
612
+ TrueSheetCompletionBlock pendingCompletion = strongSelf->_pendingPresentCompletion;
613
+ strongSelf->_isPresentPending = NO;
614
+ strongSelf->_pendingPresentCompletion = nil;
615
+ [strongSelf presentAtIndex:index animated:animated completion:pendingCompletion];
616
+ });
617
+ }];
618
+ return;
619
+ }
620
+
501
621
  [_controller setupAnchorViewInView:presentingViewController.view];
502
622
  [_controller setupSheetSizing];
503
623
  [_controller setupSheetProps];
504
624
 
505
- // Pin before measuring so a first-time pin doesn't shift sizes mid-animation.
625
+ // Detect before measuring so a first-time detection doesn't shift sizes mid-animation.
506
626
  [self setupScrollable];
507
627
 
508
628
  // Measure synchronously so the presentation starts at the right height —
@@ -515,6 +635,8 @@ using namespace facebook::react;
515
635
  _controller.peekContentHeight = @([_containerView peekContentHeight]);
516
636
  }
517
637
  _pendingSizeChange = NO;
638
+ // Props queued while the previous presentation dismissed — applied above.
639
+ _pendingPropsUpdate = NO;
518
640
 
519
641
  [self refreshFooterBottomInset];
520
642
  [_controller setupSheetDetents];
@@ -523,13 +645,29 @@ using namespace facebook::react;
523
645
  [_screensEventObserver capturePresenterScreenFromView:self];
524
646
  [_screensEventObserver startObservingWithState:_state.get()->getData()];
525
647
 
648
+ UIWindow *window = presentingViewController.view.window;
526
649
  [presentingViewController presentViewController:_controller
527
650
  animated:animated
528
651
  completion:^{
652
+ [TrueSheetOverlayView bringOverlaysToFrontInWindow:window];
529
653
  if (completion) {
530
654
  completion(YES, nil);
531
655
  }
532
656
  }];
657
+
658
+ // Presenting adds the sheet's transition view on top of the window once the
659
+ // transition starts — re-front overlays alongside it so the first frame is
660
+ // already ordered (and again on completion in case the transition was queued)
661
+ id<UIViewControllerTransitionCoordinator> coordinator = _controller.transitionCoordinator;
662
+ if (coordinator) {
663
+ [coordinator
664
+ animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
665
+ [TrueSheetOverlayView bringOverlaysToFrontInWindow:window];
666
+ }
667
+ completion:nil];
668
+ } else {
669
+ [TrueSheetOverlayView bringOverlaysToFrontInWindow:window];
670
+ }
533
671
  }
534
672
 
535
673
  - (void)resizeToIndex:(NSInteger)index completion:(nullable TrueSheetCompletionBlock)completion {
@@ -562,7 +700,46 @@ using namespace facebook::react;
562
700
  realtime:NO];
563
701
  }
564
702
 
703
+ /**
704
+ * Drops a deferred present and resolves its caller. Returns whether one was pending.
705
+ */
706
+ - (BOOL)cancelPendingPresent {
707
+ if (!_isPresentPending) {
708
+ return NO;
709
+ }
710
+
711
+ TrueSheetCompletionBlock pendingCompletion = _pendingPresentCompletion;
712
+ _isPresentPending = NO;
713
+ _pendingPresentCompletion = nil;
714
+ if (pendingCompletion) {
715
+ pendingCompletion(YES, nil);
716
+ }
717
+ return YES;
718
+ }
719
+
565
720
  - (void)dismissAnimated:(BOOL)animated completion:(nullable TrueSheetCompletionBlock)completion {
721
+ // Dismissing before a deferred present starts cancels it — nothing was presented.
722
+ if ([self cancelPendingPresent]) {
723
+ if (completion) {
724
+ completion(YES, nil);
725
+ }
726
+ return;
727
+ }
728
+
729
+ // Still animating in (isPresented flips at viewDidAppear): dismiss once the presentation lands
730
+ id<UIViewControllerTransitionCoordinator> presentCoordinator =
731
+ _controller.isBeingPresented ? _controller.transitionCoordinator : nil;
732
+ if (presentCoordinator) {
733
+ __weak __typeof(self) weakSelf = self;
734
+ [presentCoordinator animateAlongsideTransition:nil
735
+ completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
736
+ dispatch_async(dispatch_get_main_queue(), ^{
737
+ [weakSelf dismissAnimated:animated completion:completion];
738
+ });
739
+ }];
740
+ return;
741
+ }
742
+
566
743
  if (_controller.isBeingDismissed || !_controller.isPresented) {
567
744
  RCTLogWarn(@"TrueSheet: sheet is already dismissed. No need to dismiss it again.");
568
745
 
@@ -612,7 +789,8 @@ using namespace facebook::react;
612
789
  #pragma mark - TrueSheetContainerViewDelegate
613
790
 
614
791
  /**
615
- * Debounced sheet update to handle rapid content/header size changes.
792
+ * Coalesces prop and size changes outside Fabric's mount transaction so
793
+ * the footer's layout can mount inside UIKit's resize animation.
616
794
  */
617
795
  - (void)setupSheetDetentsForSizeChange {
618
796
  // Keep the footer's absorbed inset in sync with the latest measured heights
@@ -628,26 +806,52 @@ using namespace facebook::react;
628
806
  }
629
807
 
630
808
  // Not presented: presentAtIndex measures at present time.
631
- // Dismissing: the sheet is going away — touching detents perturbs the
632
- // presentation stack, visibly glitching the sheet behind.
633
- if (!_controller.isPresented || _controller.isBeingDismissed)
809
+ if (!_controller.isPresented || _isSheetUpdatePending)
634
810
  return;
635
811
 
636
- if (_isSheetUpdatePending)
812
+ // Dismissing: the sheet is going away — touching detents perturbs the
813
+ // presentation stack, visibly glitching the sheet behind. An interactive
814
+ // dismiss can still cancel, so retry once the transition resolves.
815
+ if (_controller.isBeingDismissed) {
816
+ id<UIViewControllerTransitionCoordinator> coordinator = _controller.transitionCoordinator;
817
+ if (coordinator) {
818
+ _isSheetUpdatePending = YES;
819
+
820
+ __weak __typeof(self) weakSelf = self;
821
+ [coordinator animateAlongsideTransition:nil
822
+ completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
823
+ dispatch_async(dispatch_get_main_queue(), ^{
824
+ __typeof(self) strongSelf = weakSelf;
825
+ if (!strongSelf)
826
+ return;
827
+
828
+ strongSelf->_isSheetUpdatePending = NO;
829
+ [strongSelf setupSheetDetentsForSizeChange];
830
+ });
831
+ }];
832
+ }
637
833
  return;
834
+ }
638
835
 
639
836
  _isSheetUpdatePending = YES;
640
837
 
641
838
  dispatch_async(dispatch_get_main_queue(), ^{
642
839
  self->_isSheetUpdatePending = NO;
643
- if (!self->_containerView || self->_controller.isBeingDismissed)
840
+ if (!self->_containerView || !self->_controller.isPresented || self->_controller.isBeingDismissed)
644
841
  return;
645
842
 
646
843
  // Refresh here (not just on peek size events) since the peek's offset
647
844
  // within the content can change without its own size changing.
648
845
  self->_controller.peekContentHeight = @([self->_containerView peekContentHeight]);
649
846
  [self refreshFooterBottomInset];
847
+
848
+ // Size first: the props rebuild refreshes the heights itself, hiding the
849
+ // change from the controller and skipping its post-resize settle.
650
850
  [self->_controller setupSheetDetentsForSizeChange];
851
+ if (self->_pendingPropsUpdate) {
852
+ self->_pendingPropsUpdate = NO;
853
+ [self applySheetPropsUpdate];
854
+ }
651
855
  });
652
856
  }
653
857
 
@@ -664,14 +868,18 @@ using namespace facebook::react;
664
868
  - (void)containerViewFooterDidChangeSize:(CGSize)newSize {
665
869
  [self syncFooterMetrics];
666
870
  [self setupSheetDetentsForSizeChange];
667
- [_controller setupAccessibilityContainer];
871
+ [_controller refreshAccessibilityContainer];
872
+ }
873
+
874
+ - (void)containerViewDidMountTransaction {
875
+ [_controller refreshAccessibilityContainerIfFooterChanged];
668
876
  }
669
877
 
670
878
  - (void)containerViewPeekDidChangeSize:(CGSize)newSize {
671
879
  [self setupSheetDetentsForSizeChange];
672
880
  }
673
881
 
674
- // When the ScrollView changes (e.g. conditional remount), re-pin the new ScrollView.
882
+ // When the ScrollView changes (e.g. conditional remount), re-detect the new ScrollView.
675
883
  - (void)containerViewScrollViewDidChange {
676
884
  [self setupScrollable];
677
885
  }
@@ -680,11 +888,12 @@ using namespace facebook::react;
680
888
 
681
889
  - (void)viewControllerWillPresentAtIndex:(NSInteger)index position:(CGFloat)position detent:(CGFloat)detent {
682
890
  _controller.activeDetentIndex = index;
891
+ // Before emitting, so an input focused in onWillPresent raises a tracked keyboard
892
+ [_containerView setupKeyboardObserverWithViewController:_controller];
683
893
  [TrueSheetLifecycleEvents emitWillPresent:_eventEmitter index:index position:position detent:detent];
684
894
  }
685
895
 
686
896
  - (void)viewControllerDidPresentAtIndex:(NSInteger)index position:(CGFloat)position detent:(CGFloat)detent {
687
- [_containerView setupKeyboardObserverWithViewController:_controller];
688
897
  [TrueSheetLifecycleEvents emitDidPresent:_eventEmitter index:index position:position detent:detent];
689
898
 
690
899
  if (_pendingPropsUpdate) {
@@ -724,6 +933,10 @@ using namespace facebook::react;
724
933
  }
725
934
  }
726
935
 
936
+ - (void)viewControllerDidAttemptDismiss {
937
+ [TrueSheetLifecycleEvents emitDismissAttempt:_eventEmitter];
938
+ }
939
+
727
940
  - (void)viewControllerDidDismiss {
728
941
  [_containerView cleanupKeyboardObserver];
729
942
  if (!_dismissedByNavigation) {
@@ -755,6 +968,7 @@ using namespace facebook::react;
755
968
 
756
969
  - (void)viewControllerSafeAreaInsetsDidChange {
757
970
  [self refreshFooterBottomInset];
971
+ [_containerView updateFooterKeyboardOffset];
758
972
  }
759
973
 
760
974
  - (void)viewControllerWillFocus {
@@ -822,9 +1036,11 @@ using namespace facebook::react;
822
1036
  if (!_containerView)
823
1037
  return;
824
1038
 
825
- _containerView.insetAdjustment = _insetAdjustment;
826
1039
  _containerView.scrollableOptions = _scrollableOptions;
827
- _containerView.hasAutoDetent = _hasAutoDetent;
1040
+ _containerView.scrollableHandle = _scrollableHandle;
1041
+ _containerView.safeAreaInsetAdjustment = (_scrollableOptions ? _scrollableOptions.safeAreaInsetAdjustment : YES) &&
1042
+ _insetAdjustment == TrueSheetViewInsetAdjustment::Automatic;
1043
+ _containerView.footerInsetAdjustment = _controller.footerInsetAdjustment;
828
1044
  [self refreshFooterBottomInset];
829
1045
  [_containerView setupScrollable];
830
1046
  }
@@ -26,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
26
26
  - (void)viewControllerDidPresentAtIndex:(NSInteger)index position:(CGFloat)position detent:(CGFloat)detent;
27
27
  - (void)viewControllerWillDismiss;
28
28
  - (void)viewControllerDidDismiss;
29
+ - (void)viewControllerDidAttemptDismiss;
29
30
  - (void)viewControllerDidChangeDetent:(NSInteger)index position:(CGFloat)position detent:(CGFloat)detent;
30
31
  - (void)viewControllerDidDrag:(UIGestureRecognizerState)state
31
32
  index:(NSInteger)index
@@ -54,6 +55,7 @@ NS_ASSUME_NONNULL_BEGIN
54
55
 
55
56
  @property (nonatomic, weak, nullable) id<TrueSheetViewControllerDelegate> delegate;
56
57
  @property (nonatomic, strong) NSArray<NSNumber *> *detents;
58
+ @property (nonatomic, copy) NSArray<NSDictionary *> *detentBackgrounds;
57
59
  @property (nonatomic, strong, nullable) NSNumber *maxContentHeight;
58
60
  @property (nonatomic, strong, nullable) NSNumber *maxContentWidth;
59
61
  @property (nonatomic, strong, nullable) NSNumber *contentHeight;
@@ -61,6 +63,13 @@ NS_ASSUME_NONNULL_BEGIN
61
63
  @property (nonatomic, assign) BOOL absoluteHeader;
62
64
  @property (nonatomic, strong, nullable) NSNumber *footerHeight;
63
65
  @property (nonatomic, assign) BOOL absoluteFooter;
66
+
67
+ /**
68
+ * YES while the plugged scrollable is padded by the absolute footer's height
69
+ * (see ScrollableOptions.contentInsetAdjustment) — the footer then counts
70
+ * toward the auto detent like a relative one, so the content ends above it.
71
+ */
72
+ @property (nonatomic, assign) BOOL footerInsetAdjustment;
64
73
  @property (nonatomic, strong, nullable) NSNumber *peekContentHeight;
65
74
  @property (nonatomic, strong, nullable) UIColor *backgroundColor;
66
75
  @property (nonatomic, strong, nullable) NSNumber *cornerRadius;
@@ -69,15 +78,22 @@ NS_ASSUME_NONNULL_BEGIN
69
78
  @property (nonatomic, assign) facebook::react::TrueSheetViewAccessibilityOptionsStruct accessibilityOptions;
70
79
  @property (nonatomic, assign) BOOL draggable;
71
80
  @property (nonatomic, assign) BOOL dimmed;
81
+ @property (nonatomic, strong, nullable) UIColor *dimColor;
82
+ @property (nonatomic, strong, nullable) NSNumber *dimOpacity;
72
83
  @property (nonatomic, strong, nullable) NSNumber *dimmedDetentIndex;
73
84
  @property (nonatomic, assign) facebook::react::TrueSheetViewBackgroundBlur backgroundBlur;
74
- @property (nonatomic, strong, nullable) NSNumber *blurIntensity;
75
- @property (nonatomic, assign) BOOL blurInteraction;
76
85
  @property (nonatomic, assign) facebook::react::TrueSheetViewPresentation presentation;
77
- @property (nonatomic, assign) facebook::react::TrueSheetViewAnchor anchor;
86
+ @property (nonatomic, assign) facebook::react::TrueSheetViewPlacement placement;
87
+ @property (nonatomic, assign) facebook::react::TrueSheetViewFoldOptionsStruct foldOptions;
78
88
  @property (nonatomic, assign) facebook::react::TrueSheetViewInsetAdjustment insetAdjustment;
79
89
  @property (nonatomic, assign) BOOL scrollingExpandsSheet;
80
90
  @property (nonatomic, assign) CGFloat footerKeyboardOffset;
91
+
92
+ /**
93
+ * NO keeps an absolute footer pinned to the sheet's bottom edge behind the
94
+ * keyboard instead of rising above it.
95
+ */
96
+ @property (nonatomic, assign) BOOL footerAvoidsKeyboard;
81
97
  @property (nonatomic, assign) BOOL dismissible;
82
98
  @property (nonatomic, assign) BOOL isPresented;
83
99
  @property (nonatomic, assign) NSInteger activeDetentIndex;
@@ -88,6 +104,19 @@ NS_ASSUME_NONNULL_BEGIN
88
104
  * ~keyboardHeight into the learned offset.
89
105
  */
90
106
  @property (nonatomic, assign) BOOL keyboardSheetGrown;
107
+
108
+ /**
109
+ * Target keyboard height, set as soon as the keyboard frame change is
110
+ * announced — ahead of the grow/shrink animation, unlike keyboardSheetGrown.
111
+ */
112
+ @property (nonatomic, assign) CGFloat keyboardHeight;
113
+
114
+ /**
115
+ * How far UIKit grows the sheet for the iPadOS minimized keyboard, which it
116
+ * treats as docked. Taken off every detent so the sheet stays put.
117
+ */
118
+ @property (nonatomic, assign) CGFloat minimizedKeyboardOverlap;
119
+
91
120
  @property (nonatomic, readonly) BOOL isTopmostPresentedController;
92
121
  @property (nonatomic, readonly) BOOL isStackedBehindChild;
93
122
  @property (nonatomic, weak, nullable) UIView *accessibilityContentView;
@@ -109,6 +138,9 @@ NS_ASSUME_NONNULL_BEGIN
109
138
  @property (nonatomic, assign) CGFloat appliedFooterBottomInset;
110
139
 
111
140
  - (void)setupAccessibilityContainer;
141
+ - (void)refreshAccessibilityContainer;
142
+ - (void)refreshAccessibilityContainerIfFooterChanged;
143
+
112
144
  - (void)applyActiveDetent;
113
145
  - (void)setupActiveDetentWithIndex:(NSInteger)index;
114
146
  - (void)resizeToDetentIndex:(NSInteger)index;
@@ -117,6 +149,7 @@ NS_ASSUME_NONNULL_BEGIN
117
149
  - (void)setupSheetDetents;
118
150
  - (void)setupSheetDetentsForSizeChange;
119
151
  - (void)setupSheetDetentsForDetentsChange;
152
+ - (void)invalidateDetents;
120
153
  - (void)setupDraggable;
121
154
  - (void)setupAnchorViewInView:(UIView *)parentView;
122
155