@lodev09/react-native-true-sheet 3.11.4 → 3.11.6

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 (49) hide show
  1. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +69 -1
  2. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPeekView.kt +72 -0
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPeekViewManager.kt +33 -0
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +5 -0
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +5 -0
  7. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +5 -3
  8. package/ios/TrueSheetContainerView.h +13 -0
  9. package/ios/TrueSheetContainerView.mm +72 -1
  10. package/ios/TrueSheetPeekView.h +32 -0
  11. package/ios/TrueSheetPeekView.mm +99 -0
  12. package/ios/TrueSheetView.mm +12 -0
  13. package/ios/TrueSheetViewController.h +1 -0
  14. package/ios/TrueSheetViewController.mm +28 -15
  15. package/ios/core/TrueSheetDetentCalculator.h +3 -2
  16. package/ios/core/TrueSheetDetentCalculator.mm +2 -1
  17. package/ios/tvos/TrueSheetStubs.mm +11 -0
  18. package/lib/module/TrueSheet.web.js +85 -70
  19. package/lib/module/TrueSheet.web.js.map +1 -1
  20. package/lib/module/TrueSheetPeek.js +15 -0
  21. package/lib/module/TrueSheetPeek.js.map +1 -0
  22. package/lib/module/TrueSheetPeek.web.js +46 -0
  23. package/lib/module/TrueSheetPeek.web.js.map +1 -0
  24. package/lib/module/fabric/TrueSheetPeekViewNativeComponent.ts +8 -0
  25. package/lib/module/index.js +1 -0
  26. package/lib/module/index.js.map +1 -1
  27. package/lib/module/mocks/index.js +10 -0
  28. package/lib/module/mocks/index.js.map +1 -1
  29. package/lib/typescript/src/TrueSheet.types.d.ts +3 -1
  30. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  31. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  32. package/lib/typescript/src/TrueSheetPeek.d.ts +9 -0
  33. package/lib/typescript/src/TrueSheetPeek.d.ts.map +1 -0
  34. package/lib/typescript/src/TrueSheetPeek.web.d.ts +19 -0
  35. package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -0
  36. package/lib/typescript/src/fabric/TrueSheetPeekViewNativeComponent.d.ts +6 -0
  37. package/lib/typescript/src/fabric/TrueSheetPeekViewNativeComponent.d.ts.map +1 -0
  38. package/lib/typescript/src/index.d.ts +1 -0
  39. package/lib/typescript/src/index.d.ts.map +1 -1
  40. package/lib/typescript/src/mocks/index.d.ts +6 -2
  41. package/lib/typescript/src/mocks/index.d.ts.map +1 -1
  42. package/package.json +4 -1
  43. package/src/TrueSheet.types.ts +3 -1
  44. package/src/TrueSheet.web.tsx +83 -69
  45. package/src/TrueSheetPeek.tsx +11 -0
  46. package/src/TrueSheetPeek.web.tsx +48 -0
  47. package/src/fabric/TrueSheetPeekViewNativeComponent.ts +8 -0
  48. package/src/index.ts +1 -0
  49. package/src/mocks/index.ts +8 -1
@@ -2,8 +2,10 @@ package com.lodev09.truesheet
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.view.View
5
+ import android.view.ViewGroup
5
6
  import com.facebook.react.uimanager.ThemedReactContext
6
7
  import com.facebook.react.uimanager.events.EventDispatcher
8
+ import com.facebook.react.util.RNLog
7
9
  import com.facebook.react.views.view.ReactViewGroup
8
10
 
9
11
  interface TrueSheetContainerViewDelegate {
@@ -13,6 +15,7 @@ interface TrueSheetContainerViewDelegate {
13
15
  fun containerViewScrollViewDidChange()
14
16
  fun containerViewHeaderDidChangeSize(width: Int, height: Int)
15
17
  fun containerViewFooterDidChangeSize(width: Int, height: Int)
18
+ fun containerViewPeekDidChangeSize(width: Int, height: Int)
16
19
  }
17
20
 
18
21
  /**
@@ -24,18 +27,40 @@ class TrueSheetContainerView(reactContext: ThemedReactContext) :
24
27
  ReactViewGroup(reactContext),
25
28
  TrueSheetContentViewDelegate,
26
29
  TrueSheetHeaderViewDelegate,
27
- TrueSheetFooterViewDelegate {
30
+ TrueSheetFooterViewDelegate,
31
+ TrueSheetPeekViewDelegate {
28
32
 
29
33
  var delegate: TrueSheetContainerViewDelegate? = null
30
34
 
31
35
  var contentView: TrueSheetContentView? = null
32
36
  var headerView: TrueSheetHeaderView? = null
33
37
  var footerView: TrueSheetFooterView? = null
38
+ var peekView: TrueSheetPeekView? = null
34
39
 
35
40
  var contentHeight: Int = 0
36
41
  var headerHeight: Int = 0
37
42
  var footerHeight: Int = 0
38
43
 
44
+ /**
45
+ * Distance from the top of the content view to the bottom of the peek view.
46
+ * Includes the peek view's offset within the content (padding, views above it)
47
+ * so the peek detent reveals everything down to the peek content's bottom edge.
48
+ */
49
+ val peekContentHeight: Int
50
+ get() {
51
+ val peek = peekView ?: return 0
52
+ val content = contentView ?: return peek.height
53
+
54
+ var bottom = peek.height
55
+ var view: View = peek
56
+ while (view !== content) {
57
+ bottom += view.top
58
+ view = view.parent as? View ?: return peek.height
59
+ }
60
+
61
+ return bottom
62
+ }
63
+
39
64
  var insetAdjustment: TrueSheetInsetAdjustment = TrueSheetInsetAdjustment.AUTOMATIC
40
65
  var scrollViewBottomInset: Int = 0
41
66
  var scrollableEnabled: Boolean = false
@@ -75,6 +100,10 @@ class TrueSheetContainerView(reactContext: ThemedReactContext) :
75
100
  child.delegate = this
76
101
  child.scrollableOptions = scrollableOptions
77
102
  contentView = child
103
+
104
+ // Children mount bottom-up, so the content subtree is complete here.
105
+ // Late-mounted peek views attach themselves instead (see TrueSheetPeekView).
106
+ findPeekView(child)?.let { attachPeekView(it) }
78
107
  }
79
108
 
80
109
  is TrueSheetHeaderView -> {
@@ -113,6 +142,41 @@ class TrueSheetContainerView(reactContext: ThemedReactContext) :
113
142
  super.removeViewAt(index)
114
143
  }
115
144
 
145
+ // ==================== Peek View ====================
146
+
147
+ private fun findPeekView(view: View): TrueSheetPeekView? {
148
+ if (view is TrueSheetPeekView) return view
149
+
150
+ if (view is ViewGroup) {
151
+ for (i in 0 until view.childCount) {
152
+ findPeekView(view.getChildAt(i))?.let { return it }
153
+ }
154
+ }
155
+
156
+ return null
157
+ }
158
+
159
+ fun attachPeekView(view: TrueSheetPeekView) {
160
+ if (peekView === view) return
161
+
162
+ if (peekView != null) {
163
+ RNLog.w(context as ThemedReactContext, "TrueSheet: Sheet can only have one peek component.")
164
+ return
165
+ }
166
+
167
+ peekView = view
168
+ view.delegate = this
169
+ peekViewDidChangeSize(view.width, view.height)
170
+ }
171
+
172
+ fun detachPeekView(view: TrueSheetPeekView) {
173
+ if (peekView !== view) return
174
+
175
+ view.delegate = null
176
+ peekView = null
177
+ peekViewDidChangeSize(0, 0)
178
+ }
179
+
116
180
  // ==================== Delegate Implementations ====================
117
181
 
118
182
  override fun contentViewDidChangeSize(width: Int, height: Int) {
@@ -138,6 +202,10 @@ class TrueSheetContainerView(reactContext: ThemedReactContext) :
138
202
  delegate?.containerViewFooterDidChangeSize(width, height)
139
203
  }
140
204
 
205
+ override fun peekViewDidChangeSize(width: Int, height: Int) {
206
+ delegate?.containerViewPeekDidChangeSize(width, height)
207
+ }
208
+
141
209
  companion object {
142
210
  const val TAG_NAME = "TrueSheet"
143
211
  }
@@ -39,6 +39,7 @@ class TrueSheetPackage : BaseReactPackage() {
39
39
  TrueSheetContainerViewManager(),
40
40
  TrueSheetContentViewManager(),
41
41
  TrueSheetHeaderViewManager(),
42
- TrueSheetFooterViewManager()
42
+ TrueSheetFooterViewManager(),
43
+ TrueSheetPeekViewManager()
43
44
  )
44
45
  }
@@ -0,0 +1,72 @@
1
+ package com.lodev09.truesheet
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.view.ViewParent
5
+ import com.facebook.react.uimanager.ThemedReactContext
6
+ import com.facebook.react.views.view.ReactViewGroup
7
+
8
+ /**
9
+ * Delegate interface for peek view size changes
10
+ */
11
+ interface TrueSheetPeekViewDelegate {
12
+ fun peekViewDidChangeSize(width: Int, height: Int)
13
+ }
14
+
15
+ /**
16
+ * Peek view that marks part of the content as the sheet's peek content.
17
+ * The peek detent covers the content through this view's bottom edge.
18
+ */
19
+ @SuppressLint("ViewConstructor")
20
+ class TrueSheetPeekView(context: ThemedReactContext) : ReactViewGroup(context) {
21
+ var delegate: TrueSheetPeekViewDelegate? = null
22
+
23
+ override fun onLayout(
24
+ changed: Boolean,
25
+ left: Int,
26
+ top: Int,
27
+ right: Int,
28
+ bottom: Int
29
+ ) {
30
+ super.onLayout(changed, left, top, right, bottom)
31
+
32
+ attachToContainerView()
33
+
34
+ // Position changes matter too — the peek's offset within the content
35
+ // affects the peek detent.
36
+ if (changed) {
37
+ delegate?.peekViewDidChangeSize(width, height)
38
+ }
39
+ }
40
+
41
+ override fun onAttachedToWindow() {
42
+ super.onAttachedToWindow()
43
+ attachToContainerView()
44
+ }
45
+
46
+ /**
47
+ * Peek can be nested anywhere within the content, so it attaches itself to the
48
+ * nearest container instead of being mounted by it. The container also searches
49
+ * for it when the content is added to cover the initial (bottom-up) mount order.
50
+ */
51
+ private fun attachToContainerView() {
52
+ if (delegate != null) return
53
+
54
+ var parent: ViewParent? = parent
55
+ while (parent != null && parent !is TrueSheetContainerView) {
56
+ parent = parent.parent
57
+ }
58
+
59
+ (parent as? TrueSheetContainerView)?.attachPeekView(this)
60
+ }
61
+
62
+ /**
63
+ * Called by the ViewManager when the view is dropped (Fabric unmount)
64
+ */
65
+ fun detachFromContainerView() {
66
+ (delegate as? TrueSheetContainerView)?.detachPeekView(this)
67
+ }
68
+
69
+ companion object {
70
+ const val TAG_NAME = "TrueSheet"
71
+ }
72
+ }
@@ -0,0 +1,33 @@
1
+ package com.lodev09.truesheet
2
+
3
+ import com.facebook.react.module.annotations.ReactModule
4
+ import com.facebook.react.uimanager.PointerEvents
5
+ import com.facebook.react.uimanager.ThemedReactContext
6
+ import com.facebook.react.uimanager.ViewGroupManager
7
+ import com.facebook.react.uimanager.annotations.ReactProp
8
+
9
+ /**
10
+ * ViewManager for TrueSheetPeekView
11
+ * Marks part of the content as the sheet's peek content
12
+ */
13
+ @ReactModule(name = TrueSheetPeekViewManager.REACT_CLASS)
14
+ class TrueSheetPeekViewManager : ViewGroupManager<TrueSheetPeekView>() {
15
+
16
+ override fun getName(): String = REACT_CLASS
17
+
18
+ override fun createViewInstance(reactContext: ThemedReactContext): TrueSheetPeekView = TrueSheetPeekView(reactContext)
19
+
20
+ override fun onDropViewInstance(view: TrueSheetPeekView) {
21
+ super.onDropViewInstance(view)
22
+ view.detachFromContainerView()
23
+ }
24
+
25
+ @ReactProp(name = "pointerEvents")
26
+ fun setPointerEvents(view: TrueSheetPeekView, pointerEventsStr: String?) {
27
+ view.pointerEvents = PointerEvents.parsePointerEvents(pointerEventsStr)
28
+ }
29
+
30
+ companion object {
31
+ const val REACT_CLASS = "TrueSheetPeekView"
32
+ }
33
+ }
@@ -620,6 +620,11 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
620
620
  viewController.positionFooter()
621
621
  }
622
622
 
623
+ override fun containerViewPeekDidChangeSize(width: Int, height: Int) {
624
+ // Peek content height affects peek detents
625
+ updateSheetIfNeeded()
626
+ }
627
+
623
628
  // ==================== RNScreensEventObserverDelegate ====================
624
629
 
625
630
  override fun presenterScreenWillDisappear() {
@@ -276,6 +276,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
276
276
  private var cachedContentHeight: Int = 0
277
277
  private var cachedHeaderHeight: Int = 0
278
278
  private var cachedFooterHeight: Int = 0
279
+ private var cachedPeekContentHeight: Int = 0
279
280
 
280
281
  override val contentHeight: Int
281
282
  get() = containerView?.contentHeight ?: cachedContentHeight
@@ -286,6 +287,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
286
287
  override val footerHeight: Int
287
288
  get() = containerView?.footerHeight ?: cachedFooterHeight
288
289
 
290
+ override val peekContentHeight: Int
291
+ get() = containerView?.peekContentHeight ?: cachedPeekContentHeight
292
+
289
293
  // Insets
290
294
  // Target keyboard height used for detent calculations
291
295
  override val keyboardInset: Int
@@ -841,6 +845,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
841
845
  cachedContentHeight = it.contentHeight
842
846
  cachedHeaderHeight = it.headerHeight
843
847
  cachedFooterHeight = it.footerHeight
848
+ cachedPeekContentHeight = it.peekContentHeight
844
849
  }
845
850
 
846
851
  interactionState = InteractionState.Reconfiguring
@@ -16,6 +16,7 @@ interface TrueSheetDetentCalculatorDelegate {
16
16
  val contentHeight: Int
17
17
  val headerHeight: Int
18
18
  val footerHeight: Int
19
+ val peekContentHeight: Int
19
20
  val contentBottomInset: Int
20
21
  val maxContentHeight: Int?
21
22
  val keyboardInset: Int
@@ -34,17 +35,18 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
34
35
  private val contentHeight: Int get() = delegate?.contentHeight ?: 0
35
36
  private val headerHeight: Int get() = delegate?.headerHeight ?: 0
36
37
  private val footerHeight: Int get() = delegate?.footerHeight ?: 0
38
+ private val peekContentHeight: Int get() = delegate?.peekContentHeight ?: 0
37
39
  private val contentBottomInset: Int get() = delegate?.contentBottomInset ?: 0
38
40
  private val maxContentHeight: Int? get() = delegate?.maxContentHeight
39
41
  private val keyboardInset: Int get() = delegate?.keyboardInset ?: 0
40
42
 
41
43
  /**
42
- * Height for peek (-2.0) detents: header + footer height.
43
- * Falls back to 150dp when neither is present.
44
+ * Height for peek (-2.0) detents: header + footer + peek content height.
45
+ * Falls back to 150dp when none is present.
44
46
  */
45
47
  private val peekDetentHeight: Int
46
48
  get() {
47
- val height = headerHeight + footerHeight
49
+ val height = headerHeight + footerHeight + peekContentHeight
48
50
  return if (height > 0) height else DEFAULT_PEEK_HEIGHT.dpToPx().toInt()
49
51
  }
50
52
 
@@ -23,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN
23
23
 
24
24
  @end
25
25
 
26
+ @class TrueSheetPeekView;
27
+
26
28
  @protocol TrueSheetContainerViewDelegate <NSObject>
27
29
 
28
30
  - (void)containerViewContentDidChangeSize:(CGSize)newSize;
@@ -32,6 +34,7 @@ NS_ASSUME_NONNULL_BEGIN
32
34
 
33
35
  - (void)containerViewHeaderDidChangeSize:(CGSize)newSize;
34
36
  - (void)containerViewFooterDidChangeSize:(CGSize)newSize;
37
+ - (void)containerViewPeekDidChangeSize:(CGSize)newSize;
35
38
 
36
39
  @end
37
40
 
@@ -72,6 +75,16 @@ NS_ASSUME_NONNULL_BEGIN
72
75
  */
73
76
  - (CGFloat)footerHeight;
74
77
 
78
+ /**
79
+ * Returns the current height of the peek view within the content
80
+ */
81
+ - (CGFloat)peekContentHeight;
82
+
83
+ /**
84
+ * Registers a peek view found within the content subtree
85
+ */
86
+ - (void)attachPeekView:(TrueSheetPeekView *)peekView;
87
+
75
88
  /**
76
89
  * Updates footer layout constraints if needed
77
90
  */
@@ -13,6 +13,7 @@
13
13
  #import "TrueSheetContentView.h"
14
14
  #import "TrueSheetFooterView.h"
15
15
  #import "TrueSheetHeaderView.h"
16
+ #import "TrueSheetPeekView.h"
16
17
  #import "TrueSheetViewController.h"
17
18
  #import "core/TrueSheetKeyboardObserver.h"
18
19
  #import "utils/UIView+ScrollEdgeInteraction.h"
@@ -45,13 +46,15 @@ using namespace facebook::react;
45
46
 
46
47
  @interface TrueSheetContainerView () <TrueSheetContentViewDelegate,
47
48
  TrueSheetHeaderViewDelegate,
48
- TrueSheetFooterViewDelegate>
49
+ TrueSheetFooterViewDelegate,
50
+ TrueSheetPeekViewDelegate>
49
51
  @end
50
52
 
51
53
  @implementation TrueSheetContainerView {
52
54
  TrueSheetContentView *_contentView;
53
55
  TrueSheetHeaderView *_headerView;
54
56
  TrueSheetFooterView *_footerView;
57
+ TrueSheetPeekView *__weak _peekView;
55
58
  TrueSheetKeyboardObserver *_keyboardObserver;
56
59
  BOOL _scrollableSet;
57
60
  }
@@ -123,6 +126,21 @@ using namespace facebook::react;
123
126
  return _footerView ? _footerView.frame.size.height : 0;
124
127
  }
125
128
 
129
+ // Distance from the top of the content view to the bottom of the peek view.
130
+ // Includes the peek view's offset within the content (padding, views above it)
131
+ // so the peek detent reveals everything down to the peek content's bottom edge.
132
+ - (CGFloat)peekContentHeight {
133
+ if (!_peekView) {
134
+ return 0;
135
+ }
136
+
137
+ if (!_contentView || ![_peekView isDescendantOfView:_contentView]) {
138
+ return _peekView.frame.size.height;
139
+ }
140
+
141
+ return CGRectGetMaxY([_peekView convertRect:_peekView.bounds toView:_contentView]);
142
+ }
143
+
126
144
  - (void)layoutFooter {
127
145
  if (_footerView) {
128
146
  CGFloat height = _footerView.frame.size.height;
@@ -196,6 +214,13 @@ using namespace facebook::react;
196
214
  }
197
215
  _contentView = (TrueSheetContentView *)childComponentView;
198
216
  _contentView.delegate = self;
217
+
218
+ // Children mount bottom-up, so the content subtree is complete here.
219
+ // Late-mounted peek views attach themselves instead (see TrueSheetPeekView).
220
+ TrueSheetPeekView *peekView = [self findPeekViewIn:_contentView];
221
+ if (peekView) {
222
+ [self attachPeekView:peekView];
223
+ }
199
224
  }
200
225
 
201
226
  if ([childComponentView isKindOfClass:[TrueSheetHeaderView class]]) {
@@ -278,6 +303,52 @@ using namespace facebook::react;
278
303
  }
279
304
  }
280
305
 
306
+ #pragma mark - TrueSheetPeekViewDelegate
307
+
308
+ - (nullable TrueSheetPeekView *)findPeekViewIn:(UIView *)view {
309
+ if ([view isKindOfClass:[TrueSheetPeekView class]]) {
310
+ return (TrueSheetPeekView *)view;
311
+ }
312
+
313
+ for (UIView *subview in view.subviews) {
314
+ TrueSheetPeekView *found = [self findPeekViewIn:subview];
315
+ if (found) {
316
+ return found;
317
+ }
318
+ }
319
+
320
+ return nil;
321
+ }
322
+
323
+ - (void)attachPeekView:(TrueSheetPeekView *)peekView {
324
+ if (_peekView == peekView) {
325
+ return;
326
+ }
327
+
328
+ if (_peekView != nil) {
329
+ RCTLogWarn(@"TrueSheet: Sheet can only have one peek component.");
330
+ return;
331
+ }
332
+
333
+ _peekView = peekView;
334
+ peekView.delegate = self;
335
+ [self peekViewDidChangeSize:peekView.frame.size];
336
+ }
337
+
338
+ - (void)peekViewDidChangeSize:(CGSize)newSize {
339
+ [self.delegate containerViewPeekDidChangeSize:newSize];
340
+ }
341
+
342
+ - (void)peekViewWillDetach:(TrueSheetPeekView *)peekView {
343
+ if (_peekView != peekView) {
344
+ return;
345
+ }
346
+
347
+ _peekView.delegate = nil;
348
+ _peekView = nil;
349
+ [self peekViewDidChangeSize:CGSizeZero];
350
+ }
351
+
281
352
  #pragma mark - Keyboard Observer
282
353
 
283
354
  - (void)setupKeyboardObserverWithViewController:(UIViewController *)viewController {
@@ -0,0 +1,32 @@
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 <React/RCTViewComponentView.h>
12
+ #import <UIKit/UIKit.h>
13
+
14
+ NS_ASSUME_NONNULL_BEGIN
15
+
16
+ @class TrueSheetPeekView;
17
+
18
+ @protocol TrueSheetPeekViewDelegate <NSObject>
19
+ @optional
20
+ - (void)peekViewDidChangeSize:(CGSize)size;
21
+ - (void)peekViewWillDetach:(TrueSheetPeekView *)peekView;
22
+ @end
23
+
24
+ @interface TrueSheetPeekView : RCTViewComponentView
25
+
26
+ @property (nonatomic, weak, nullable) id<TrueSheetPeekViewDelegate> delegate;
27
+
28
+ @end
29
+
30
+ NS_ASSUME_NONNULL_END
31
+
32
+ #endif
@@ -0,0 +1,99 @@
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 "TrueSheetPeekView.h"
12
+ #import <react/renderer/components/TrueSheetSpec/ComponentDescriptors.h>
13
+ #import <react/renderer/components/TrueSheetSpec/EventEmitters.h>
14
+ #import <react/renderer/components/TrueSheetSpec/Props.h>
15
+ #import <react/renderer/components/TrueSheetSpec/RCTComponentViewHelpers.h>
16
+ #import "TrueSheetContainerView.h"
17
+
18
+ using namespace facebook::react;
19
+
20
+ @implementation TrueSheetPeekView {
21
+ CGRect _lastFrame;
22
+ }
23
+
24
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
25
+ return concreteComponentDescriptorProvider<TrueSheetPeekViewComponentDescriptor>();
26
+ }
27
+
28
+ - (instancetype)initWithFrame:(CGRect)frame {
29
+ if (self = [super initWithFrame:frame]) {
30
+ static const auto defaultProps = std::make_shared<const TrueSheetPeekViewProps>();
31
+ _props = defaultProps;
32
+
33
+ _lastFrame = CGRectZero;
34
+ }
35
+ return self;
36
+ }
37
+
38
+ /**
39
+ * Peek can be nested anywhere within the content, so it attaches itself to the
40
+ * nearest container instead of being mounted by it. The container also searches
41
+ * for it when the content mounts to cover the initial (bottom-up) mount order.
42
+ */
43
+ - (void)attachToContainerView {
44
+ if (self.delegate) {
45
+ return;
46
+ }
47
+
48
+ UIView *view = self.superview;
49
+ while (view && ![view isKindOfClass:[TrueSheetContainerView class]]) {
50
+ view = view.superview;
51
+ }
52
+
53
+ if (view) {
54
+ [(TrueSheetContainerView *)view attachPeekView:self];
55
+ }
56
+ }
57
+
58
+ - (void)didMoveToSuperview {
59
+ [super didMoveToSuperview];
60
+ if (self.superview) {
61
+ [self attachToContainerView];
62
+ }
63
+ }
64
+
65
+ - (void)didMoveToWindow {
66
+ [super didMoveToWindow];
67
+ if (self.window) {
68
+ [self attachToContainerView];
69
+ }
70
+ }
71
+
72
+ - (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics
73
+ oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics {
74
+ [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
75
+
76
+ // Compare the whole frame — the peek's offset within the content affects
77
+ // the peek detent, so position changes matter too.
78
+ CGRect newFrame = CGRectMake(layoutMetrics.frame.origin.x, layoutMetrics.frame.origin.y,
79
+ layoutMetrics.frame.size.width, layoutMetrics.frame.size.height);
80
+
81
+ if (!CGRectEqualToRect(newFrame, _lastFrame)) {
82
+ _lastFrame = newFrame;
83
+ [self.delegate peekViewDidChangeSize:newFrame.size];
84
+ }
85
+ }
86
+
87
+ - (void)prepareForRecycle {
88
+ [super prepareForRecycle];
89
+ _lastFrame = CGRectZero;
90
+ [self.delegate peekViewWillDetach:self];
91
+ }
92
+
93
+ @end
94
+
95
+ Class<RCTComponentViewProtocol> TrueSheetPeekViewCls(void) {
96
+ return TrueSheetPeekView.class;
97
+ }
98
+
99
+ #endif
@@ -398,6 +398,11 @@ using namespace facebook::react;
398
398
  _controller.footerHeight = @(footerHeight);
399
399
  }
400
400
 
401
+ CGFloat peekContentHeight = [_containerView peekContentHeight];
402
+ if (peekContentHeight > 0) {
403
+ _controller.peekContentHeight = @(peekContentHeight);
404
+ }
405
+
401
406
  if (_eventEmitter) {
402
407
  [TrueSheetLifecycleEvents emitMount:_eventEmitter];
403
408
  } else {
@@ -570,6 +575,9 @@ using namespace facebook::react;
570
575
  if (!self->_containerView)
571
576
  return;
572
577
 
578
+ // Refresh here (not just on peek size events) since the peek's offset
579
+ // within the content can change without its own size changing.
580
+ self->_controller.peekContentHeight = @([self->_containerView peekContentHeight]);
573
581
  [self->_controller setupSheetDetentsForSizeChange];
574
582
  });
575
583
  }
@@ -590,6 +598,10 @@ using namespace facebook::react;
590
598
  [_controller setupAccessibilityContainer];
591
599
  }
592
600
 
601
+ - (void)containerViewPeekDidChangeSize:(CGSize)newSize {
602
+ [self setupSheetDetentsForSizeChange];
603
+ }
604
+
593
605
  // When the ScrollView changes (e.g. conditional remount), re-pin the new ScrollView.
594
606
  - (void)containerViewScrollViewDidChange {
595
607
  [self setupScrollable];
@@ -58,6 +58,7 @@ NS_ASSUME_NONNULL_BEGIN
58
58
  @property (nonatomic, strong, nullable) NSNumber *contentHeight;
59
59
  @property (nonatomic, strong, nullable) NSNumber *headerHeight;
60
60
  @property (nonatomic, strong, nullable) NSNumber *footerHeight;
61
+ @property (nonatomic, strong, nullable) NSNumber *peekContentHeight;
61
62
  @property (nonatomic, strong, nullable) UIColor *backgroundColor;
62
63
  @property (nonatomic, strong, nullable) NSNumber *cornerRadius;
63
64
  @property (nonatomic, assign) BOOL grabber;