@lodev09/react-native-true-sheet 4.0.0-beta.25 → 4.0.0-beta.26

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 (32) hide show
  1. package/README.md +1 -0
  2. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +1 -1
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +31 -10
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +3 -3
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +5 -0
  6. package/android/src/main/java/com/lodev09/truesheet/core/RNScreensEventObserver.kt +8 -16
  7. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetBehavior.kt +9 -0
  8. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +0 -8
  9. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +29 -38
  10. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +6 -2
  11. package/ios/TrueSheetContainerView.h +1 -0
  12. package/ios/TrueSheetContainerView.mm +6 -0
  13. package/ios/TrueSheetContentView.h +1 -0
  14. package/ios/TrueSheetContentView.mm +2 -0
  15. package/ios/TrueSheetFooterView.mm +5 -0
  16. package/ios/TrueSheetView.mm +9 -2
  17. package/ios/TrueSheetViewController.h +12 -0
  18. package/ios/TrueSheetViewController.mm +332 -29
  19. package/ios/core/TrueSheetDetentCalculator.h +11 -0
  20. package/ios/core/TrueSheetDetentCalculator.mm +17 -5
  21. package/ios/core/TrueSheetKeyboardObserver.mm +89 -9
  22. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +6 -0
  23. package/lib/typescript/src/TrueSheet.types.d.ts +31 -0
  24. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  25. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +5 -0
  26. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  27. package/lib/typescript/src/navigation/types.d.ts +1 -1
  28. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/src/TrueSheet.types.ts +34 -0
  31. package/src/fabric/TrueSheetViewNativeComponent.ts +6 -0
  32. package/src/navigation/types.ts +1 -0
package/README.md CHANGED
@@ -35,6 +35,7 @@ The true native bottom sheet experience for your React Native Apps. 💩
35
35
  * 🍞 **`TrueSheetOverlay`** - Render toasts, dialogs, and other [overlays above your sheets](https://sheet.lodev09.com/guides/overlays) — no more `FullWindowOverlay`/`Modal` workarounds
36
36
  * 🔍 **Element inspector** - React Native's dev menu inspector now works inside a presented sheet
37
37
  * 🎨 **Dim color and opacity** - [Customize the dim](https://sheet.lodev09.com/guides/dimming#dim-color-and-opacity) behind the sheet with `dimColor` and `dimOpacity`
38
+ * 📱 **Foldables** - [Rest the sheet on the hinge](https://sheet.lodev09.com/guides/foldables) of a half-folded iPhone with `foldOptions` (iOS 27.1+)
38
39
 
39
40
  ```sh
40
41
  npx expo install @lodev09/react-native-true-sheet@beta
@@ -67,7 +67,7 @@ class TrueSheetOverlayView(private val reactContext: ThemedReactContext) :
67
67
 
68
68
  override fun onDetachedFromWindow() {
69
69
  viewTreeObserver.removeOnWindowAttachListener(windowAttachListener)
70
- detachRootView()
70
+ rootContainerView?.post { if (!isAttachedToWindow) detachRootView() }
71
71
  super.onDetachedFromWindow()
72
72
  }
73
73
 
@@ -72,6 +72,9 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
72
72
  // Debounce flag to coalesce rapid layout changes into a single sheet update
73
73
  private var isSheetUpdatePending: Boolean = false
74
74
 
75
+ // didFocus waiting for the slide-back after a child dismissed
76
+ private var isDidFocusPending: Boolean = false
77
+
75
78
  // Root container for the coordinator layout (activity or Modal dialog content view)
76
79
  internal var rootContainerView: ViewGroup? = null
77
80
 
@@ -610,7 +613,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
610
613
 
611
614
  // Propagate any additional translation up the stack
612
615
  if (additionalTranslation > 0) {
613
- TrueSheetStackManager.getParentSheet(this)?.addTranslation(additionalTranslation)
616
+ viewController.parentSheetView?.addTranslation(additionalTranslation)
614
617
  }
615
618
  }
616
619
 
@@ -642,7 +645,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
642
645
 
643
646
  val additionalTranslation = newTranslation - currentTranslation
644
647
  if (additionalTranslation > 0) {
645
- TrueSheetStackManager.getParentSheet(this)?.addTranslation(additionalTranslation, animated = false)
648
+ viewController.parentSheetView?.addTranslation(additionalTranslation, animated = false)
646
649
  }
647
650
  }
648
651
 
@@ -653,7 +656,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
653
656
  if (viewController.isExpanded) return
654
657
 
655
658
  viewController.translateSheet(viewController.currentTranslationY + amount, animated)
656
- TrueSheetStackManager.getParentSheet(this)?.addTranslation(amount, animated)
659
+ viewController.parentSheetView?.addTranslation(amount, animated)
657
660
  }
658
661
 
659
662
  /**
@@ -666,7 +669,25 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
666
669
 
667
670
  // Parent should recalculate its translation based on this sheet's position
668
671
  val mySheetTop = viewController.detentCalculator.getSheetTopForDetentIndex(viewController.currentDetentIndex)
669
- TrueSheetStackManager.getParentSheet(this)?.updateTranslationForChild(mySheetTop)
672
+ viewController.parentSheetView?.updateTranslationForChild(mySheetTop)
673
+ }
674
+
675
+ /**
676
+ * Resets translation after a child dismissed and emits didFocus once the sheet settles.
677
+ * A sheet presented on top before then cancels the slide-back, so the next blur flushes it.
678
+ */
679
+ private fun focusAfterResetTranslation() {
680
+ isDidFocusPending = true
681
+ resetTranslation { emitPendingDidFocus() }
682
+ }
683
+
684
+ private fun emitPendingDidFocus() {
685
+ if (!isDidFocusPending) return
686
+ isDidFocusPending = false
687
+
688
+ if (viewController.isPresented && !viewController.isBeingDismissed) {
689
+ viewControllerDidFocus()
690
+ }
670
691
  }
671
692
 
672
693
  // ==================== TrueSheetViewControllerDelegate ====================
@@ -701,6 +722,9 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
701
722
  }
702
723
 
703
724
  override fun viewControllerDidDismiss(parent: TrueSheetView?) {
725
+ // A slide-back cut short by this sheet's own dismissal never emits; don't carry it over
726
+ isDidFocusPending = false
727
+
704
728
  // Detach coordinator from the root container view
705
729
  viewController.coordinatorLayout?.let { rootContainerView?.removeView(it) }
706
730
  rootContainerView = null
@@ -715,12 +739,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
715
739
  // The sheets below stop blending this sheet's dim
716
740
  parent?.viewController?.takeIf { it.isPresented }?.updateDimAmount()
717
741
 
718
- parent?.resetTranslation {
719
- val parentController = parent.viewController
720
- if (parentController.isPresented && !parentController.isBeingDismissed) {
721
- parent.viewControllerDidFocus()
722
- }
723
- }
742
+ parent?.focusAfterResetTranslation()
724
743
  }
725
744
 
726
745
  override fun viewControllerDidChangeDetent(index: Int, position: Float, detent: Float) {
@@ -763,6 +782,8 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
763
782
  }
764
783
 
765
784
  override fun viewControllerWillBlur() {
785
+ emitPendingDidFocus()
786
+
766
787
  val surfaceId = UIManagerHelper.getSurfaceId(this)
767
788
  eventDispatcher?.dispatchEvent(WillBlurEvent(surfaceId, id))
768
789
  }
@@ -886,6 +886,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
886
886
  setupSheetDetents()
887
887
  setupDimmedBackground()
888
888
  setupKeyboardObserver()
889
+ // Before the slide, so an input focused in onWillPresent insets the scrollable
890
+ containerView?.setupKeyboardHandler()
889
891
 
890
892
  displayedBackgroundIndex = detentIndex
891
893
  setupBackground(animated = false)
@@ -1008,11 +1010,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
1008
1010
  // Restore isHideable to actual value after present animation
1009
1011
  behavior?.isHideable = dismissible
1010
1012
 
1011
- containerView?.setupKeyboardHandler()
1012
-
1013
1013
  val (index, position, detent) = getDetentInfoWithValue(currentDetentIndex)
1014
- delegate?.viewControllerDidPresent(index, position, detent)
1015
1014
  parentSheetView?.viewControllerDidBlur()
1015
+ delegate?.viewControllerDidPresent(index, position, detent)
1016
1016
  delegate?.viewControllerDidFocus()
1017
1017
  sheetView?.updateGrabberAccessibilityValue(index, detents.size)
1018
1018
 
@@ -253,6 +253,11 @@ class TrueSheetViewManager :
253
253
  // iOS-specific prop - no-op on Android
254
254
  }
255
255
 
256
+ @ReactProp(name = "foldOptions")
257
+ override fun setFoldOptions(view: TrueSheetView, options: ReadableMap?) {
258
+ // iOS-specific prop - no-op on Android
259
+ }
260
+
256
261
  @ReactProp(name = "insetAdjustment")
257
262
  override fun setInsetAdjustment(view: TrueSheetView, insetAdjustment: String?) {
258
263
  view.setInsetAdjustment(insetAdjustment ?: "automatic")
@@ -44,22 +44,9 @@ class RNScreensEventObserver : EventDispatcherListener {
44
44
  }
45
45
 
46
46
  fun capturePresenterScreenFromView(view: View?) {
47
- presenterScreenTag = 0
48
- val ancestors = mutableSetOf<Int>()
49
-
50
- var current: View? = view
51
- while (current != null) {
52
- if (isScreenView(current)) {
53
- if (presenterScreenTag == 0) {
54
- presenterScreenTag = current.id
55
- } else {
56
- ancestors.add(current.id)
57
- }
58
- }
59
- current = (current.parent as? View)
60
- }
61
-
62
- ancestorScreenTags = ancestors
47
+ val tags = screenTags(view).toList()
48
+ presenterScreenTag = tags.firstOrNull() ?: 0
49
+ ancestorScreenTags = tags.drop(1).toSet()
63
50
  }
64
51
 
65
52
  override fun onEventDispatch(event: Event<*>) {
@@ -84,5 +71,10 @@ class RNScreensEventObserver : EventDispatcherListener {
84
71
 
85
72
  companion object {
86
73
  private fun isScreenView(view: View): Boolean = view.javaClass.name == RN_SCREENS_VIEW_CLASS
74
+
75
+ // Tags of the screens enclosing the view, innermost first
76
+ private fun screenTags(view: View?): Sequence<Int> = generateSequence(view) { it.parent as? View }.filter(::isScreenView).map { it.id }
77
+
78
+ fun findPresenterScreenTag(view: View?): Int = screenTags(view).firstOrNull() ?: 0
87
79
  }
88
80
  }
@@ -40,6 +40,15 @@ class TrueSheetBottomSheetBehavior<V : View> : BottomSheetBehavior<V>() {
40
40
  hideFriction = HIDE_FRICTION
41
41
  }
42
42
 
43
+ // Material's first layout registers an IME callback that rides the sheet's translationY
44
+ // with the keyboard. TrueSheet positions sheets for the keyboard itself, and the callback
45
+ // overwrites a parent's stack translation when the keyboard rises mid-translation.
46
+ override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
47
+ val handled = super.onLayoutChild(parent, child, layoutDirection)
48
+ ViewCompat.setWindowInsetsAnimationCallback(child, null)
49
+ return handled
50
+ }
51
+
43
52
  // With the short threshold, settle at or below the lowest detent like
44
53
  // Compose Material3's sheet instead of Material's half-height rule.
45
54
  private fun releaseState(child: V, dragged: Boolean, velocityY: Float): Int? {
@@ -81,14 +81,6 @@ class TrueSheetBottomSheetView(private val reactContext: ThemedReactContext) : F
81
81
  clipToPadding = false
82
82
  }
83
83
 
84
- override fun setTranslationY(translationY: Float) {
85
- // This prevents keyboard inset animations from resetting parent sheet translation
86
- if (translationY == 0f && this.translationY != 0f) {
87
- return
88
- }
89
- super.setTranslationY(translationY)
90
- }
91
-
92
84
  // =============================================================================
93
85
  // MARK: - Layout
94
86
  // =============================================================================
@@ -12,16 +12,6 @@ object TrueSheetStackManager {
12
12
 
13
13
  private val presentedSheetStack = mutableListOf<TrueSheetView>()
14
14
 
15
- /**
16
- * Gets the parent sheet at the given index, if any.
17
- * Only returns a parent if it's in the same container.
18
- * Must be called within synchronized block.
19
- */
20
- private fun getParentSheetAt(index: Int, rootContainer: ViewGroup?): TrueSheetView? {
21
- if (index <= 0) return null
22
- return presentedSheetStack[index - 1].takeIf { it.rootContainerView == rootContainer }
23
- }
24
-
25
15
  /**
26
16
  * Returns the topmost presented and visible sheet, skipping sheets that are dismissing.
27
17
  * A dismissing sheet can't parent a new one: its dim would suppress the child's own dim
@@ -33,6 +23,22 @@ object TrueSheetStackManager {
33
23
  it.viewController.run { isPresented && isSheetVisible && !isBeingDismissed }
34
24
  }
35
25
 
26
+ /**
27
+ * Returns the topmost sheet hidden by screen navigation that was presented from the same
28
+ * screen as the given sheet. Sheets presented on the covering screen must not parent to it,
29
+ * or the hidden sheet would receive focus and blur events while it can't be seen.
30
+ * Must be called within synchronized block.
31
+ */
32
+ private fun findScreenHiddenSheet(sheetView: TrueSheetView): TrueSheetView? {
33
+ val screenTag = RNScreensEventObserver.findPresenterScreenTag(sheetView)
34
+ if (screenTag == 0) return null
35
+
36
+ return presentedSheetStack.lastOrNull {
37
+ it.viewController.run { isPresented && wasHiddenByScreen && !isBeingDismissed } &&
38
+ it.screensEventObserver?.presenterScreenTag == screenTag
39
+ }
40
+ }
41
+
36
42
  /**
37
43
  * Registers a sheet in the stack and returns its parent sheet if any.
38
44
  * Only returns a parent if it's in the same container (e.g., same Screen).
@@ -40,7 +46,8 @@ object TrueSheetStackManager {
40
46
  @JvmStatic
41
47
  fun registerSheet(sheetView: TrueSheetView): TrueSheetView? {
42
48
  synchronized(presentedSheetStack) {
43
- val parentSheet = findTopmostSheet()?.takeIf { it.rootContainerView == sheetView.rootContainerView }
49
+ val parentSheet = (findTopmostSheet() ?: findScreenHiddenSheet(sheetView))
50
+ ?.takeIf { it.rootContainerView == sheetView.rootContainerView }
44
51
 
45
52
  if (!presentedSheetStack.contains(sheetView)) {
46
53
  presentedSheetStack.add(sheetView)
@@ -62,24 +69,20 @@ object TrueSheetStackManager {
62
69
 
63
70
  /**
64
71
  * Updates parent sheet translation based on the child sheet's position.
65
- * Only affects parent sheets in the same container.
66
72
  */
67
73
  @JvmStatic
68
74
  fun updateParentTranslation(sheetView: TrueSheetView) {
69
- synchronized(presentedSheetStack) {
70
- val index = presentedSheetStack.indexOf(sheetView)
71
- val parentSheet = getParentSheetAt(index, sheetView.rootContainerView) ?: return
72
-
73
- // Post to ensure layout is complete before reading position
74
- sheetView.viewController.post {
75
- val childMinSheetTop = sheetView.viewController.detentCalculator.getSheetTopForDetentIndex(0)
76
- val childCurrentSheetTop = sheetView.viewController.detentCalculator.getSheetTopForDetentIndex(
77
- sheetView.viewController.currentDetentIndex
78
- )
79
- // Cap to minimum detent position
80
- val childSheetTop = maxOf(childMinSheetTop, childCurrentSheetTop)
81
- parentSheet.updateTranslationForChild(childSheetTop)
82
- }
75
+ val parentSheet = sheetView.viewController.parentSheetView ?: return
76
+
77
+ // Post to ensure layout is complete before reading position
78
+ sheetView.viewController.post {
79
+ val childMinSheetTop = sheetView.viewController.detentCalculator.getSheetTopForDetentIndex(0)
80
+ val childCurrentSheetTop = sheetView.viewController.detentCalculator.getSheetTopForDetentIndex(
81
+ sheetView.viewController.currentDetentIndex
82
+ )
83
+ // Cap to minimum detent position
84
+ val childSheetTop = maxOf(childMinSheetTop, childCurrentSheetTop)
85
+ parentSheet.updateTranslationForChild(childSheetTop)
83
86
  }
84
87
  }
85
88
 
@@ -113,18 +116,6 @@ object TrueSheetStackManager {
113
116
  }
114
117
  }
115
118
 
116
- /**
117
- * Gets the parent sheet of the given sheet, if any.
118
- * Only returns a parent if it's in the same container.
119
- */
120
- @JvmStatic
121
- fun getParentSheet(sheetView: TrueSheetView): TrueSheetView? {
122
- synchronized(presentedSheetStack) {
123
- val index = presentedSheetStack.indexOf(sheetView)
124
- return getParentSheetAt(index, sheetView.rootContainerView)
125
- }
126
- }
127
-
128
119
  /**
129
120
  * Returns true if the given sheet is the topmost presented sheet in its container.
130
121
  */
@@ -1,6 +1,5 @@
1
1
  #include "TrueSheetContentViewShadowNode.h"
2
2
 
3
- #include <folly/ScopeGuard.h>
4
3
  #include <react/renderer/components/view/conversions.h>
5
4
  #include <react/renderer/core/ComponentDescriptor.h>
6
5
  #include <react/renderer/core/LayoutConstraints.h>
@@ -95,7 +94,12 @@ void TrueSheetContentViewShadowNode::updateNaturalHeightIfNeeded(const LayoutCon
95
94
  if (contentChanged_ || !measurement_ || !(measurement_->constraints == constraints) ||
96
95
  !(measurement_->context == measurementContext) || measurement_->errata != errata) {
97
96
  gIsMeasuringNaturalHeight = true;
98
- auto restoreMeasurement = folly::makeGuard([] { gIsMeasuringNaturalHeight = false; });
97
+ // Not folly::makeGuard: its terminate() isn't linked into RN < 0.86 on Android.
98
+ struct RestoreMeasurement {
99
+ ~RestoreMeasurement() {
100
+ gIsMeasuringNaturalHeight = false;
101
+ }
102
+ } restoreMeasurement;
99
103
  auto measurementNode =
100
104
  measurement_ ? cloneForMeasurement(*this, *measurementSourceChildren_, measurement_->root->getChildren())
101
105
  : cloneForMeasurement(*this, {}, {});
@@ -37,6 +37,7 @@ NS_ASSUME_NONNULL_BEGIN
37
37
 
38
38
  - (void)containerViewContentDidChangeSize:(CGSize)newSize;
39
39
  - (void)containerViewScrollViewDidChange;
40
+ - (void)containerViewDidMountTransaction;
40
41
 
41
42
  @optional
42
43
 
@@ -287,6 +287,12 @@ using namespace facebook::react;
287
287
  [self.delegate containerViewScrollViewDidChange];
288
288
  }
289
289
 
290
+ // Footer controls can mount, unmount, or hide deep in its subtree without
291
+ // passing through its own mount hooks or changing its size.
292
+ - (void)contentViewDidMountTransaction {
293
+ [self.delegate containerViewDidMountTransaction];
294
+ }
295
+
290
296
  #pragma mark - TrueSheetHeaderViewDelegate
291
297
 
292
298
  - (void)headerViewDidChangeSize:(CGSize)newSize {
@@ -26,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
26
26
 
27
27
  - (void)contentViewDidChangeSize:(CGSize)newSize;
28
28
  - (void)contentViewScrollViewDidChange;
29
+ - (void)contentViewDidMountTransaction;
29
30
 
30
31
  @end
31
32
 
@@ -181,6 +181,8 @@ using namespace facebook::react;
181
181
  // Layout has settled for the whole tree — the footer overlap (sheet height,
182
182
  // scroll view frame) and the keyboard inset are current here
183
183
  [self updateBottomInset];
184
+
185
+ [self.delegate contentViewDidMountTransaction];
184
186
  }
185
187
 
186
188
  #pragma mark - Scrollable
@@ -123,6 +123,11 @@ using namespace facebook::react;
123
123
 
124
124
  - (void)collectAccessibilityElementsFromView:(UIView *)view into:(NSMutableArray *)elements {
125
125
  for (UIView *subview in view.subviews) {
126
+ // Fabric hides `display: none` views instead of unmounting them
127
+ if (subview.hidden) {
128
+ continue;
129
+ }
130
+
126
131
  if (subview.isAccessibilityElement || subview.accessibilityLabel || subview.accessibilityIdentifier) {
127
132
  [elements addObject:subview];
128
133
  } else if (subview.accessibilityElements.count > 0) {
@@ -284,6 +284,8 @@ using namespace facebook::react;
284
284
 
285
285
  _controller.placement = newProps.placement;
286
286
 
287
+ _controller.foldOptions = newProps.foldOptions;
288
+
287
289
  _controller.grabber = newProps.grabber;
288
290
 
289
291
  // Grabber options - check if any non-default values are set
@@ -866,7 +868,11 @@ using namespace facebook::react;
866
868
  - (void)containerViewFooterDidChangeSize:(CGSize)newSize {
867
869
  [self syncFooterMetrics];
868
870
  [self setupSheetDetentsForSizeChange];
869
- [_controller setupAccessibilityContainer];
871
+ [_controller refreshAccessibilityContainer];
872
+ }
873
+
874
+ - (void)containerViewDidMountTransaction {
875
+ [_controller refreshAccessibilityContainerIfFooterChanged];
870
876
  }
871
877
 
872
878
  - (void)containerViewPeekDidChangeSize:(CGSize)newSize {
@@ -882,11 +888,12 @@ using namespace facebook::react;
882
888
 
883
889
  - (void)viewControllerWillPresentAtIndex:(NSInteger)index position:(CGFloat)position detent:(CGFloat)detent {
884
890
  _controller.activeDetentIndex = index;
891
+ // Before emitting, so an input focused in onWillPresent raises a tracked keyboard
892
+ [_containerView setupKeyboardObserverWithViewController:_controller];
885
893
  [TrueSheetLifecycleEvents emitWillPresent:_eventEmitter index:index position:position detent:detent];
886
894
  }
887
895
 
888
896
  - (void)viewControllerDidPresentAtIndex:(NSInteger)index position:(CGFloat)position detent:(CGFloat)detent {
889
- [_containerView setupKeyboardObserverWithViewController:_controller];
890
897
  [TrueSheetLifecycleEvents emitDidPresent:_eventEmitter index:index position:position detent:detent];
891
898
 
892
899
  if (_pendingPropsUpdate) {
@@ -84,6 +84,7 @@ NS_ASSUME_NONNULL_BEGIN
84
84
  @property (nonatomic, assign) facebook::react::TrueSheetViewBackgroundBlur backgroundBlur;
85
85
  @property (nonatomic, assign) facebook::react::TrueSheetViewPresentation presentation;
86
86
  @property (nonatomic, assign) facebook::react::TrueSheetViewPlacement placement;
87
+ @property (nonatomic, assign) facebook::react::TrueSheetViewFoldOptionsStruct foldOptions;
87
88
  @property (nonatomic, assign) facebook::react::TrueSheetViewInsetAdjustment insetAdjustment;
88
89
  @property (nonatomic, assign) BOOL scrollingExpandsSheet;
89
90
  @property (nonatomic, assign) CGFloat footerKeyboardOffset;
@@ -109,6 +110,13 @@ NS_ASSUME_NONNULL_BEGIN
109
110
  * announced — ahead of the grow/shrink animation, unlike keyboardSheetGrown.
110
111
  */
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
+
112
120
  @property (nonatomic, readonly) BOOL isTopmostPresentedController;
113
121
  @property (nonatomic, readonly) BOOL isStackedBehindChild;
114
122
  @property (nonatomic, weak, nullable) UIView *accessibilityContentView;
@@ -130,6 +138,9 @@ NS_ASSUME_NONNULL_BEGIN
130
138
  @property (nonatomic, assign) CGFloat appliedFooterBottomInset;
131
139
 
132
140
  - (void)setupAccessibilityContainer;
141
+ - (void)refreshAccessibilityContainer;
142
+ - (void)refreshAccessibilityContainerIfFooterChanged;
143
+
133
144
  - (void)applyActiveDetent;
134
145
  - (void)setupActiveDetentWithIndex:(NSInteger)index;
135
146
  - (void)resizeToDetentIndex:(NSInteger)index;
@@ -138,6 +149,7 @@ NS_ASSUME_NONNULL_BEGIN
138
149
  - (void)setupSheetDetents;
139
150
  - (void)setupSheetDetentsForSizeChange;
140
151
  - (void)setupSheetDetentsForDetentsChange;
152
+ - (void)invalidateDetents;
141
153
  - (void)setupDraggable;
142
154
  - (void)setupAnchorViewInView:(UIView *)parentView;
143
155