@lodev09/react-native-true-sheet 3.11.10 → 4.0.0-beta.1

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 (73) hide show
  1. package/README.md +6 -5
  2. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +23 -2
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +116 -26
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +7 -0
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +48 -0
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterViewManager.kt +7 -0
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +45 -0
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +28 -12
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +99 -34
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +9 -5
  11. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +12 -3
  12. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +33 -4
  13. package/android/src/main/jni/TrueSheetSpec.h +2 -0
  14. package/android/src/main/jni/TrueSheetStateUpdater.cpp +96 -0
  15. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +24 -0
  16. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +43 -0
  17. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +28 -0
  18. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +11 -0
  19. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +37 -0
  20. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +24 -0
  21. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +56 -0
  22. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +28 -0
  23. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +11 -0
  24. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +41 -0
  25. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +18 -0
  26. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +16 -0
  27. package/ios/TrueSheetContainerView.h +12 -10
  28. package/ios/TrueSheetContainerView.mm +11 -29
  29. package/ios/TrueSheetContentView.h +22 -7
  30. package/ios/TrueSheetContentView.mm +143 -41
  31. package/ios/TrueSheetFooterView.h +12 -2
  32. package/ios/TrueSheetFooterView.mm +74 -71
  33. package/ios/TrueSheetHeaderView.mm +0 -1
  34. package/ios/TrueSheetView.mm +60 -27
  35. package/ios/TrueSheetViewController.h +9 -0
  36. package/ios/TrueSheetViewController.mm +288 -122
  37. package/ios/core/TrueSheetDetentCalculator.h +9 -0
  38. package/ios/core/TrueSheetDetentCalculator.mm +13 -4
  39. package/ios/tvos/TrueSheetStubs.mm +2 -0
  40. package/lib/module/TrueSheet.js +32 -13
  41. package/lib/module/TrueSheet.js.map +1 -1
  42. package/lib/module/TrueSheet.web.js +262 -38
  43. package/lib/module/TrueSheet.web.js.map +1 -1
  44. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +5 -1
  45. package/lib/module/fabric/TrueSheetFooterViewNativeComponent.ts +10 -2
  46. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +8 -1
  47. package/lib/module/web/vaul/index.js +17 -6
  48. package/lib/module/web/vaul/index.js.map +1 -1
  49. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  50. package/lib/typescript/src/TrueSheet.types.d.ts +35 -11
  51. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  52. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  53. package/lib/typescript/src/fabric/TrueSheetContentViewNativeComponent.d.ts.map +1 -1
  54. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts +2 -0
  55. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts.map +1 -1
  56. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +5 -1
  57. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  58. package/lib/typescript/src/navigation/types.d.ts +1 -1
  59. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  60. package/lib/typescript/src/web/vaul/index.d.ts +8 -1
  61. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
  62. package/package.json +10 -10
  63. package/react-native.config.js +5 -1
  64. package/src/TrueSheet.tsx +45 -14
  65. package/src/TrueSheet.types.ts +38 -12
  66. package/src/TrueSheet.web.tsx +314 -44
  67. package/src/fabric/TrueSheetContentViewNativeComponent.ts +5 -1
  68. package/src/fabric/TrueSheetFooterViewNativeComponent.ts +10 -2
  69. package/src/fabric/TrueSheetViewNativeComponent.ts +8 -1
  70. package/src/navigation/types.ts +3 -1
  71. package/src/web/vaul/index.tsx +23 -5
  72. package/ios/utils/LayoutUtil.h +0 -77
  73. package/ios/utils/LayoutUtil.mm +0 -106
@@ -19,11 +19,12 @@
19
19
  #import "events/TrueSheetFocusEvents.h"
20
20
  #import "events/TrueSheetLifecycleEvents.h"
21
21
  #import "events/TrueSheetStateEvents.h"
22
- #import "utils/LayoutUtil.h"
22
+ #import "utils/WindowUtil.h"
23
23
 
24
24
  #import <react/renderer/components/TrueSheetSpec/EventEmitters.h>
25
25
  #import <react/renderer/components/TrueSheetSpec/Props.h>
26
26
  #import <react/renderer/components/TrueSheetSpec/RCTComponentViewHelpers.h>
27
+ #import <react/renderer/components/TrueSheetSpec/TrueSheetInsets.h>
27
28
  #import <react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h>
28
29
  #import <react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h>
29
30
  #import <react/renderer/components/TrueSheetSpec/TrueSheetViewState.h>
@@ -33,7 +34,6 @@
33
34
  #import <React/RCTLog.h>
34
35
  #import <React/RCTSurfaceTouchHandler.h>
35
36
  #import <React/RCTUtils.h>
36
- #import <cxxreact/ReactNativeVersion.h>
37
37
  #import <react/renderer/core/State.h>
38
38
 
39
39
  using namespace facebook::react;
@@ -52,8 +52,8 @@ using namespace facebook::react;
52
52
  CGSize _lastStateSize;
53
53
  NSInteger _initialDetentIndex;
54
54
  TrueSheetViewInsetAdjustment _insetAdjustment;
55
- BOOL _scrollable;
56
55
  ScrollableOptions *_scrollableOptions;
56
+ BOOL _hasAutoDetent;
57
57
  BOOL _initialDetentAnimated;
58
58
  BOOL _isSheetUpdatePending;
59
59
  BOOL _pendingLayoutUpdate;
@@ -86,7 +86,6 @@ using namespace facebook::react;
86
86
  _lastStateSize = CGSizeZero;
87
87
  _initialDetentIndex = -1;
88
88
  _initialDetentAnimated = YES;
89
- _scrollable = NO;
90
89
  _isSheetUpdatePending = NO;
91
90
 
92
91
  _screensEventObserver = [[RNScreensEventObserver alloc] init];
@@ -164,8 +163,12 @@ using namespace facebook::react;
164
163
 
165
164
  // Detents (-1 represents "auto")
166
165
  NSMutableArray *detents = [NSMutableArray new];
166
+ _hasAutoDetent = NO;
167
167
  for (const auto &detent : newProps.detents) {
168
168
  [detents addObject:@(detent)];
169
+ if (detent == -1) {
170
+ _hasAutoDetent = YES;
171
+ }
169
172
  }
170
173
 
171
174
  if (oldProps) {
@@ -244,7 +247,6 @@ using namespace facebook::react;
244
247
 
245
248
  _initialDetentIndex = newProps.initialDetentIndex;
246
249
  _initialDetentAnimated = newProps.initialDetentAnimated;
247
- _scrollable = newProps.scrollable;
248
250
 
249
251
  const auto &scrollableOpts = newProps.scrollableOptions;
250
252
  BOOL scrollingExpandsSheet = scrollableOpts.scrollingExpandsSheet;
@@ -267,6 +269,9 @@ using namespace facebook::react;
267
269
 
268
270
  _controller.scrollingExpandsSheet = scrollingExpandsSheet;
269
271
 
272
+ _controller.absoluteHeader = newProps.headerOptions.position == TrueSheetViewPosition::Absolute;
273
+ _controller.absoluteFooter = newProps.footerOptions.footerPosition == TrueSheetViewFooterPosition::Absolute;
274
+
270
275
  CGFloat footerKeyboardOffset = newProps.footerOptions.keyboardOffset;
271
276
  if (_controller.footerKeyboardOffset != footerKeyboardOffset) {
272
277
  _controller.footerKeyboardOffset = footerKeyboardOffset;
@@ -282,9 +287,16 @@ using namespace facebook::react;
282
287
  - (void)updateState:(const State::Shared &)state oldState:(const State::Shared &)oldState {
283
288
  _state = std::static_pointer_cast<TrueSheetViewShadowNode::ConcreteState const>(state);
284
289
 
285
- if (_controller) {
286
- // Initialize with _controller size to set initial width
287
- [self viewControllerDidChangeSize:_controller.view.frame.size];
290
+ if (_controller && !_controller.isStackedBehindChild) {
291
+ CGSize size = _controller.view.frame.size;
292
+ if (size.width < 1 || size.height < 1) {
293
+ // Pre-present the controller has no layout yet. Seed with screen
294
+ // dimensions so content (e.g. a FlatList viewport) can lay out and
295
+ // measure before the sheet presents (parity with Android).
296
+ UIWindow *window = [WindowUtil keyWindow];
297
+ size = window ? window.bounds.size : UIScreen.mainScreen.bounds.size;
298
+ }
299
+ [self viewControllerDidChangeSize:size];
288
300
  }
289
301
  }
290
302
 
@@ -304,13 +316,10 @@ using namespace facebook::react;
304
316
  stateData.containerWidth = static_cast<float>(size.width);
305
317
  stateData.containerHeight = static_cast<float>(size.height);
306
318
 
307
- #if REACT_NATIVE_VERSION_MINOR >= 82
308
- // TODO: RN 0.82+ processes state updates in the same layout pass (synchronous).
309
- // Once stable, we can drop native layout constraints in favor of synchronous Yoga layout.
319
+ // RN 0.82+ processes immediate state updates in the same layout pass, so Yoga
320
+ // resizes the container synchronously with the sheet (e.g. inside UIKit's
321
+ // animation block during detent transitions).
310
322
  _state->updateState(std::move(stateData), facebook::react::EventQueue::UpdateMode::unstable_Immediate);
311
- #else
312
- _state->updateState(std::move(stateData));
313
- #endif
314
323
  }
315
324
 
316
325
  - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask {
@@ -355,7 +364,6 @@ using namespace facebook::react;
355
364
 
356
365
  _containerView.delegate = nil;
357
366
  [_touchHandler detachFromView:_containerView];
358
- [LayoutUtil unpinView:_containerView fromParentView:nil];
359
367
  [_containerView removeFromSuperview];
360
368
 
361
369
  _containerView = nil;
@@ -380,7 +388,6 @@ using namespace facebook::react;
380
388
 
381
389
  [_touchHandler attachToView:_containerView];
382
390
  [_controller.view addSubview:_containerView];
383
- [LayoutUtil pinView:_containerView toParentView:_controller.view edges:UIRectEdgeAll];
384
391
  [_controller.view bringSubviewToFront:_containerView];
385
392
  _containerView.accessibilityViewIsModal = YES;
386
393
  _controller.accessibilityContentView = _containerView;
@@ -463,11 +470,24 @@ using namespace facebook::react;
463
470
  [_controller setupAnchorViewInView:presentingViewController.view];
464
471
  [_controller setupSheetSizing];
465
472
  [_controller setupSheetProps];
466
- [_controller setupSheetDetents];
467
- [_controller setupActiveDetentWithIndex:index];
468
473
 
474
+ // Pin before measuring so a first-time pin doesn't shift sizes mid-animation.
469
475
  [self setupScrollable];
470
476
 
477
+ // Measure synchronously so the presentation starts at the right height —
478
+ // async size reports would otherwise land mid-animation and force a
479
+ // detent retarget that snaps the presentation stack.
480
+ if (_containerView) {
481
+ _controller.contentHeight = @([_containerView contentHeight]);
482
+ _controller.headerHeight = @([_containerView headerHeight]);
483
+ _controller.footerHeight = @([_containerView footerHeight]);
484
+ _controller.peekContentHeight = @([_containerView peekContentHeight]);
485
+ }
486
+ _pendingSizeChange = NO;
487
+
488
+ [_controller setupSheetDetents];
489
+ [_controller setupActiveDetentWithIndex:index];
490
+
471
491
  [_screensEventObserver capturePresenterScreenFromView:self];
472
492
  [_screensEventObserver startObservingWithState:_state.get()->getData()];
473
493
 
@@ -563,19 +583,28 @@ using namespace facebook::react;
563
583
  * Debounced sheet update to handle rapid content/header size changes.
564
584
  */
565
585
  - (void)setupSheetDetentsForSizeChange {
566
- if (_isSheetUpdatePending)
567
- return;
568
-
586
+ // Retargeting the in-flight presentation makes UIKit snap the whole stack
587
+ // (including the sheet behind) instead of animating. Defer to didPresent —
588
+ // presentAtIndex measured synchronously, so this is usually a no-op.
569
589
  if (_controller.isBeingPresented) {
570
590
  _pendingSizeChange = YES;
571
591
  return;
572
592
  }
573
593
 
594
+ // Not presented: presentAtIndex measures at present time.
595
+ // Dismissing: the sheet is going away — touching detents perturbs the
596
+ // presentation stack, visibly glitching the sheet behind.
597
+ if (!_controller.isPresented || _controller.isBeingDismissed)
598
+ return;
599
+
600
+ if (_isSheetUpdatePending)
601
+ return;
602
+
574
603
  _isSheetUpdatePending = YES;
575
604
 
576
605
  dispatch_async(dispatch_get_main_queue(), ^{
577
606
  self->_isSheetUpdatePending = NO;
578
- if (!self->_containerView)
607
+ if (!self->_containerView || self->_controller.isBeingDismissed)
579
608
  return;
580
609
 
581
610
  // Refresh here (not just on peek size events) since the peek's offset
@@ -684,10 +713,7 @@ using namespace facebook::react;
684
713
  }
685
714
 
686
715
  - (void)viewControllerDidChangeSize:(CGSize)size {
687
- // TODO: Explicit screen height for now until synchronous layout is supported.
688
- CGSize effectiveSize = CGSizeMake(size.width, _controller.screenHeight);
689
-
690
- [self updateStateWithSize:effectiveSize];
716
+ [self updateStateWithSize:size];
691
717
  }
692
718
 
693
719
  - (void)viewControllerWillFocus {
@@ -755,10 +781,17 @@ using namespace facebook::react;
755
781
  if (!_containerView)
756
782
  return;
757
783
 
758
- _containerView.scrollableEnabled = _scrollable;
759
784
  _containerView.insetAdjustment = _insetAdjustment;
760
785
  _containerView.scrollableOptions = _scrollableOptions;
786
+ _containerView.hasAutoDetent = _hasAutoDetent;
787
+ _containerView.footerBottomInset = _controller.footerBottomInset;
761
788
  [_containerView setupScrollable];
789
+
790
+ // Publish the inset so a footer set later (e.g. navigation setOptions) is
791
+ // padded on its very first layout instead of resizing the sheet twice.
792
+ if (_insetAdjustment == TrueSheetViewInsetAdjustment::Automatic) {
793
+ TrueSheetInsets::setBottomSafeArea(_controller.footerBottomInset);
794
+ }
762
795
  }
763
796
 
764
797
  - (void)applySheetPropsUpdate {
@@ -57,7 +57,9 @@ NS_ASSUME_NONNULL_BEGIN
57
57
  @property (nonatomic, strong, nullable) NSNumber *maxContentWidth;
58
58
  @property (nonatomic, strong, nullable) NSNumber *contentHeight;
59
59
  @property (nonatomic, strong, nullable) NSNumber *headerHeight;
60
+ @property (nonatomic, assign) BOOL absoluteHeader;
60
61
  @property (nonatomic, strong, nullable) NSNumber *footerHeight;
62
+ @property (nonatomic, assign) BOOL absoluteFooter;
61
63
  @property (nonatomic, strong, nullable) NSNumber *peekContentHeight;
62
64
  @property (nonatomic, strong, nullable) UIColor *backgroundColor;
63
65
  @property (nonatomic, strong, nullable) NSNumber *cornerRadius;
@@ -86,10 +88,17 @@ NS_ASSUME_NONNULL_BEGIN
86
88
  */
87
89
  @property (nonatomic, assign) BOOL keyboardSheetGrown;
88
90
  @property (nonatomic, readonly) BOOL isTopmostPresentedController;
91
+ @property (nonatomic, readonly) BOOL isStackedBehindChild;
89
92
  @property (nonatomic, weak, nullable) UIView *accessibilityContentView;
90
93
 
91
94
  @property (nonatomic, readonly) CGFloat screenHeight;
92
95
 
96
+ /**
97
+ * Bottom safe-area inset the footer absorbs as padding — the footer
98
+ * owns the sheet's bottom edge, so its background fills the inset.
99
+ */
100
+ @property (nonatomic, readonly) CGFloat footerBottomInset;
101
+
93
102
  - (void)setupAccessibilityContainer;
94
103
  - (void)applyActiveDetent;
95
104
  - (void)setupActiveDetentWithIndex:(NSInteger)index;