@lodev09/react-native-true-sheet 4.0.0-beta.10 → 4.0.0-beta.12

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 (24) hide show
  1. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +10 -13
  2. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +1 -1
  3. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +79 -24
  4. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +24 -6
  5. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +1 -1
  6. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +4 -5
  7. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
  8. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +2 -3
  9. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +3 -6
  10. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
  11. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +4 -6
  12. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
  13. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
  14. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +2 -5
  15. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +1 -1
  16. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +2 -2
  17. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +3 -6
  18. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
  19. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +4 -6
  20. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
  21. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
  22. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
  23. package/ios/TrueSheetContentView.mm +27 -21
  24. package/package.json +2 -2
@@ -14,6 +14,7 @@ import com.facebook.react.uimanager.ThemedReactContext
14
14
  import com.facebook.react.views.view.ReactViewGroup
15
15
  import com.lodev09.truesheet.core.TrueSheetKeyboardObserver
16
16
  import com.lodev09.truesheet.core.TrueSheetKeyboardObserverDelegate
17
+ import com.lodev09.truesheet.utils.ScreenUtils
17
18
  import com.lodev09.truesheet.utils.isDescendantOf
18
19
  import com.lodev09.truesheet.utils.smoothScrollTo
19
20
 
@@ -123,7 +124,6 @@ class TrueSheetContentView(private val reactContext: ThemedReactContext) : React
123
124
 
124
125
  private val scrollableLayoutListener = ViewTreeObserver.OnGlobalLayoutListener {
125
126
  updateContentInset()
126
- clampScrollPosition()
127
127
  }
128
128
 
129
129
  private val scrollContentLayoutListener = View.OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
@@ -198,12 +198,6 @@ class TrueSheetContentView(private val reactContext: ThemedReactContext) : React
198
198
  }
199
199
 
200
200
  updateContentInset()
201
-
202
- // If keyboard is currently showing, re-apply the keyboard inset to the new ScrollView
203
- val keyboardHeight = keyboardObserver?.currentHeight ?: 0
204
- if (keyboardHeight > 0) {
205
- updateScrollViewInsetForKeyboard(keyboardHeight)
206
- }
207
201
  }
208
202
 
209
203
  /**
@@ -221,7 +215,7 @@ class TrueSheetContentView(private val reactContext: ThemedReactContext) : React
221
215
  0
222
216
  }
223
217
 
224
- applyBottomInset()
218
+ updateScrollViewInsetForKeyboard(keyboardObserver?.currentHeight ?: 0)
225
219
  }
226
220
 
227
221
  // Fabric's measured height excludes the native inset, avoiding feedback.
@@ -339,12 +333,15 @@ class TrueSheetContentView(private val reactContext: ThemedReactContext) : React
339
333
  }
340
334
 
341
335
  private fun updateScrollViewInsetForKeyboard(keyboardHeight: Int) {
342
- // A relative footer stays behind the keyboard below the content, so its
343
- // height shields that much of the keyboard's overlap. An absolute footer
344
- // floats within the viewport — its clearance is the content padding's job
345
- // (the caret reveal accounts for it, see scrollToFocusedInput).
336
+ val scrollView = detectedScrollView ?: return
337
+
338
+ // Short or nested scroll views can end above the sheet's bottom edge.
339
+ // Their position also accounts for space occupied by a relative footer.
346
340
  keyboardBottomInset = if (keyboardHeight > 0) {
347
- val baseInset = maxOf(0, keyboardHeight + minOf(0, delegate?.footerKeyboardOcclusion ?: 0))
341
+ val location = IntArray(2)
342
+ scrollView.getLocationInWindow(location)
343
+ val keyboardTop = ScreenUtils.getRealScreenHeight(reactContext) - keyboardHeight
344
+ val baseInset = maxOf(0, location[1] + scrollView.height - keyboardTop)
348
345
  val adjustedInset = maxOf(0, baseInset + keyboardOffset.toInt())
349
346
  appliedKeyboardOffset = adjustedInset - baseInset
350
347
  adjustedInset
@@ -13,4 +13,4 @@ class TrueSheetContentViewComponentDescriptor final
13
13
  using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
14
14
  };
15
15
 
16
- } // namespace facebook::react
16
+ } // namespace facebook::react
@@ -1,35 +1,70 @@
1
1
  #include "TrueSheetContentViewShadowNode.h"
2
2
 
3
- #include <limits>
4
-
3
+ #include <folly/ScopeGuard.h>
5
4
  #include <react/renderer/components/view/conversions.h>
5
+ #include <react/renderer/core/ComponentDescriptor.h>
6
6
  #include <react/renderer/core/LayoutConstraints.h>
7
7
  #include <react/renderer/core/LayoutContext.h>
8
8
 
9
+ #include <limits>
10
+
9
11
  namespace facebook::react {
10
12
 
11
13
  using namespace yoga;
12
14
 
13
15
  extern const char TrueSheetContentViewComponentName[] = "TrueSheetContentView";
14
16
 
15
- // measure() lays out a clone of this node, which re-enters layout() on the
16
- // clone — guard so the measurement pass doesn't measure again recursively.
17
+ // The measurement pass re-enters layout() on a clone — guard so it doesn't
18
+ // measure again recursively.
17
19
  static thread_local bool gIsMeasuringNaturalHeight = false;
18
20
 
21
+ TrueSheetContentViewShadowNode::TrueSheetContentViewShadowNode(
22
+ const ShadowNode &sourceShadowNode, const ShadowNodeFragment &fragment)
23
+ : ConcreteViewShadowNode(sourceShadowNode, fragment),
24
+ measurement_(static_cast<const TrueSheetContentViewShadowNode &>(sourceShadowNode).measurement_),
25
+ measurementSourceChildren_(
26
+ static_cast<const TrueSheetContentViewShadowNode &>(sourceShadowNode).measurementSourceChildren_),
27
+ contentChanged_(static_cast<const TrueSheetContentViewShadowNode &>(sourceShadowNode).contentChanged_ ||
28
+ fragment.props || fragment.children) {
29
+ }
30
+
19
31
  void TrueSheetContentViewShadowNode::layout(LayoutContext layoutContext) {
32
+ // The sizing tree needs Yoga's result, not Fabric state updates or events.
33
+ if (gIsMeasuringNaturalHeight) {
34
+ return;
35
+ }
20
36
  ConcreteViewShadowNode::layout(layoutContext);
21
37
  updateNaturalHeightIfNeeded(layoutContext);
22
38
  }
23
39
 
24
- // The content's committed height follows the container instead of its own
25
- // content when it can grow/shrink along the main axis or uses a percent
26
- // height. Reporting that height as the natural height would echo the sheet's
27
- // own size back as a content size change — e.g. flex: 1 content tracking a
28
- // drag frame by frame.
29
- static bool isHeightContainerDerived(const yoga::Style &style) {
30
- return style.flexGrow().unwrapOrDefault(0) > 0 ||
31
- style.flexShrink().unwrapOrDefault(0) > 0 ||
32
- style.dimension(Dimension::Height).isPercent();
40
+ // Every descendant must be detached from React's runtime references before
41
+ // Yoga clones it during measurement. Disabling transfer only on the root
42
+ // still lets descendant clones replace React's live nodes. Reuse unchanged
43
+ // measurement subtrees; Yoga owns copy-on-write when their layout changes.
44
+ std::shared_ptr<ShadowNode> TrueSheetContentViewShadowNode::cloneForMeasurement(
45
+ const ShadowNode &node, const Children &previousSourceChildren, const Children &previousMeasurementChildren) {
46
+ auto children = std::make_shared<Children>();
47
+ children->reserve(node.getChildren().size());
48
+ for (size_t index = 0; index < node.getChildren().size(); index++) {
49
+ const auto &child = node.getChildren()[index];
50
+ if (index < previousSourceChildren.size() && ShadowNode::sameFamily(*child, *previousSourceChildren[index])) {
51
+ const auto &previousSource = previousSourceChildren[index];
52
+ const auto &previousMeasurement = previousMeasurementChildren[index];
53
+ children->push_back(child == previousSource ? previousMeasurement
54
+ : cloneForMeasurement(*child, previousSource->getChildren(),
55
+ previousMeasurement->getChildren()));
56
+ } else {
57
+ children->push_back(cloneForMeasurement(*child, {}, {}));
58
+ }
59
+ }
60
+
61
+ auto clone = node.getComponentDescriptor().cloneShadowNode(
62
+ node, {.children = children, .state = node.getState(), .runtimeShadowNodeReference = false});
63
+ if (auto content = dynamic_cast<TrueSheetContentViewShadowNode *>(clone.get())) {
64
+ content->measurement_.reset();
65
+ content->measurementSourceChildren_.reset();
66
+ }
67
+ return clone;
33
68
  }
34
69
 
35
70
  // The natural height is the height the content wants when unbounded — the
@@ -38,26 +73,46 @@ static bool isHeightContainerDerived(const yoga::Style &style) {
38
73
  // subtree with an unconstrained height instead. Yoga respects the user's
39
74
  // styles: an explicit-height ScrollView keeps its height while a flexible one
40
75
  // expands to its content — no ScrollView discovery involved.
41
- void TrueSheetContentViewShadowNode::updateNaturalHeightIfNeeded(
42
- const LayoutContext &layoutContext) {
43
- if (gIsMeasuringNaturalHeight) {
44
- return;
45
- }
46
-
76
+ void TrueSheetContentViewShadowNode::updateNaturalHeightIfNeeded(const LayoutContext &layoutContext) {
47
77
  auto stateData = getStateData();
48
78
  auto size = getLayoutMetrics().frame.size;
49
79
 
50
80
  Float naturalHeight = size.height;
51
- if (isHeightContainerDerived(getConcreteProps().yogaStyle)) {
81
+ // Auto-height content can also be capped by a shrinking ScrollView. Only
82
+ // a fixed, non-flexible height can be taken from the viewport layout.
83
+ if (yogaNode_.isNodeFlexible() || !getConcreteProps().yogaStyle.dimension(Dimension::Height).isPoints()) {
52
84
  auto constraints = LayoutConstraints{};
53
85
  constraints.minimumSize = {size.width, 0};
54
86
  constraints.maximumSize = {size.width, std::numeric_limits<Float>::infinity()};
55
87
  constraints.layoutDirection = getLayoutMetrics().layoutDirection;
56
88
 
57
- gIsMeasuringNaturalHeight = true;
58
- naturalHeight = measure(layoutContext, constraints).height;
59
- gIsMeasuringNaturalHeight = false;
89
+ auto measurementContext = layoutContext;
90
+ measurementContext.affectedNodes = nullptr;
91
+ auto errata = YGConfigGetErrata(&yogaConfig_);
92
+
93
+ // Yoga's viewport-only clones carry no new props or children. Reuse the
94
+ // intrinsic height across those layouts instead of measuring each drag frame.
95
+ if (contentChanged_ || !measurement_ || !(measurement_->constraints == constraints) ||
96
+ !(measurement_->context == measurementContext) || measurement_->errata != errata) {
97
+ gIsMeasuringNaturalHeight = true;
98
+ auto restoreMeasurement = folly::makeGuard([] { gIsMeasuringNaturalHeight = false; });
99
+ auto measurementNode =
100
+ measurement_ ? cloneForMeasurement(*this, *measurementSourceChildren_, measurement_->root->getChildren())
101
+ : cloneForMeasurement(*this, {}, {});
102
+ auto &measurement = static_cast<LayoutableShadowNode &>(*measurementNode);
103
+ measurement.layoutTree(measurementContext, constraints);
104
+ measurement_ = std::make_shared<const Measurement>(Measurement{.root = measurementNode,
105
+ .constraints = constraints,
106
+ .context = measurementContext,
107
+ .errata = errata,
108
+ .height = measurement.getLayoutMetrics().frame.size.height});
109
+ }
110
+ naturalHeight = measurement_->height;
111
+ } else {
112
+ measurement_.reset();
60
113
  }
114
+ measurementSourceChildren_ = children_;
115
+ contentChanged_ = false;
61
116
 
62
117
  if (stateData.naturalHeight != naturalHeight) {
63
118
  stateData.naturalHeight = naturalHeight;
@@ -65,4 +120,4 @@ void TrueSheetContentViewShadowNode::updateNaturalHeightIfNeeded(
65
120
  }
66
121
  }
67
122
 
68
- } // namespace facebook::react
123
+ } // namespace facebook::react
@@ -5,6 +5,8 @@
5
5
  #include <react/renderer/components/TrueSheetSpec/Props.h>
6
6
  #include <react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h>
7
7
  #include <react/renderer/components/view/ConcreteViewShadowNode.h>
8
+ #include <react/renderer/core/LayoutConstraints.h>
9
+ #include <react/renderer/core/LayoutContext.h>
8
10
 
9
11
  namespace facebook::react {
10
12
 
@@ -14,20 +16,36 @@ JSI_EXPORT extern const char TrueSheetContentViewComponentName[];
14
16
  * `ShadowNode` for <TrueSheetContentView> component.
15
17
  */
16
18
  class JSI_EXPORT TrueSheetContentViewShadowNode final
17
- : public ConcreteViewShadowNode<
18
- TrueSheetContentViewComponentName,
19
- TrueSheetContentViewProps,
20
- TrueSheetContentViewEventEmitter,
21
- TrueSheetContentViewState> {
19
+ : public ConcreteViewShadowNode<TrueSheetContentViewComponentName, TrueSheetContentViewProps,
20
+ TrueSheetContentViewEventEmitter, TrueSheetContentViewState> {
22
21
  using ConcreteViewShadowNode::ConcreteViewShadowNode;
23
22
 
24
23
  public:
24
+ TrueSheetContentViewShadowNode(const ShadowNode &sourceShadowNode, const ShadowNodeFragment &fragment);
25
+
25
26
  #pragma mark - LayoutableShadowNode
26
27
 
27
28
  void layout(LayoutContext layoutContext) override;
28
29
 
29
30
  private:
31
+ using Children = std::vector<std::shared_ptr<const ShadowNode>>;
32
+
33
+ struct Measurement {
34
+ std::shared_ptr<const ShadowNode> root;
35
+ LayoutConstraints constraints;
36
+ LayoutContext context;
37
+ YGErrata errata;
38
+ Float height;
39
+ };
40
+
41
+ std::shared_ptr<const Measurement> measurement_;
42
+ std::shared_ptr<const Children> measurementSourceChildren_;
43
+ bool contentChanged_{true};
44
+
45
+ static std::shared_ptr<ShadowNode> cloneForMeasurement(
46
+ const ShadowNode &node, const Children &previousSourceChildren, const Children &previousMeasurementChildren);
47
+
30
48
  void updateNaturalHeightIfNeeded(const LayoutContext &layoutContext);
31
49
  };
32
50
 
33
- } // namespace facebook::react
51
+ } // namespace facebook::react
@@ -8,4 +8,4 @@ folly::dynamic TrueSheetContentViewState::getDynamic() const {
8
8
  }
9
9
  #endif
10
10
 
11
- } // namespace facebook::react
11
+ } // namespace facebook::react
@@ -20,10 +20,9 @@ class TrueSheetContentViewState final {
20
20
  TrueSheetContentViewState() = default;
21
21
 
22
22
  #ifdef ANDROID
23
- TrueSheetContentViewState(
24
- TrueSheetContentViewState const &previousState,
25
- folly::dynamic data)
26
- : naturalHeight(previousState.naturalHeight) {}
23
+ TrueSheetContentViewState(TrueSheetContentViewState const &previousState, folly::dynamic data)
24
+ : naturalHeight(previousState.naturalHeight) {
25
+ }
27
26
  #endif
28
27
 
29
28
  Float naturalHeight{0};
@@ -36,4 +35,4 @@ class TrueSheetContentViewState final {
36
35
  #endif
37
36
  };
38
37
 
39
- } // namespace facebook::react
38
+ } // namespace facebook::react
@@ -8,17 +8,15 @@ namespace facebook::react {
8
8
  /*
9
9
  * Descriptor for <TrueSheetFooterView> component.
10
10
  */
11
- class TrueSheetFooterViewComponentDescriptor final
12
- : public ConcreteComponentDescriptor<TrueSheetFooterViewShadowNode> {
11
+ class TrueSheetFooterViewComponentDescriptor final : public ConcreteComponentDescriptor<TrueSheetFooterViewShadowNode> {
13
12
  using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
14
13
 
15
14
  void adopt(ShadowNode &shadowNode) const override {
16
- auto &concreteShadowNode =
17
- static_cast<TrueSheetFooterViewShadowNode &>(shadowNode);
15
+ auto &concreteShadowNode = static_cast<TrueSheetFooterViewShadowNode &>(shadowNode);
18
16
  concreteShadowNode.adjustLayoutWithState();
19
17
 
20
18
  ConcreteComponentDescriptor::adopt(shadowNode);
21
19
  }
22
20
  };
23
21
 
24
- } // namespace facebook::react
22
+ } // namespace facebook::react
@@ -13,8 +13,7 @@ extern const char TrueSheetFooterViewComponentName[] = "TrueSheetFooterView";
13
13
  void TrueSheetFooterViewShadowNode::adjustLayoutWithState() {
14
14
  ensureUnsealed();
15
15
 
16
- auto state = std::static_pointer_cast<
17
- const TrueSheetFooterViewShadowNode::ConcreteState>(getState());
16
+ auto state = std::static_pointer_cast<const TrueSheetFooterViewShadowNode::ConcreteState>(getState());
18
17
  auto stateData = state->getData();
19
18
 
20
19
  auto &props = getConcreteProps();
@@ -60,4 +59,4 @@ void TrueSheetFooterViewShadowNode::adjustLayoutWithState() {
60
59
  }
61
60
  }
62
61
 
63
- } // namespace facebook::react
62
+ } // namespace facebook::react
@@ -14,15 +14,12 @@ JSI_EXPORT extern const char TrueSheetFooterViewComponentName[];
14
14
  * `ShadowNode` for <TrueSheetFooterView> component.
15
15
  */
16
16
  class JSI_EXPORT TrueSheetFooterViewShadowNode final
17
- : public ConcreteViewShadowNode<
18
- TrueSheetFooterViewComponentName,
19
- TrueSheetFooterViewProps,
20
- TrueSheetFooterViewEventEmitter,
21
- TrueSheetFooterViewState> {
17
+ : public ConcreteViewShadowNode<TrueSheetFooterViewComponentName, TrueSheetFooterViewProps,
18
+ TrueSheetFooterViewEventEmitter, TrueSheetFooterViewState> {
22
19
  using ConcreteViewShadowNode::ConcreteViewShadowNode;
23
20
 
24
21
  public:
25
22
  void adjustLayoutWithState();
26
23
  };
27
24
 
28
- } // namespace facebook::react
25
+ } // namespace facebook::react
@@ -8,4 +8,4 @@ folly::dynamic TrueSheetFooterViewState::getDynamic() const {
8
8
  }
9
9
  #endif
10
10
 
11
- } // namespace facebook::react
11
+ } // namespace facebook::react
@@ -18,11 +18,9 @@ class TrueSheetFooterViewState final {
18
18
  TrueSheetFooterViewState() = default;
19
19
 
20
20
  #ifdef ANDROID
21
- TrueSheetFooterViewState(
22
- TrueSheetFooterViewState const &previousState,
23
- folly::dynamic data)
24
- : bottomInset(static_cast<float>(data["bottomInset"].getDouble())),
25
- initialized(true) {}
21
+ TrueSheetFooterViewState(TrueSheetFooterViewState const &previousState, folly::dynamic data)
22
+ : bottomInset(static_cast<float>(data["bottomInset"].getDouble())), initialized(true) {
23
+ }
26
24
  #endif
27
25
 
28
26
  float bottomInset{0};
@@ -38,4 +36,4 @@ class TrueSheetFooterViewState final {
38
36
  #endif
39
37
  };
40
38
 
41
- } // namespace facebook::react
39
+ } // namespace facebook::react
@@ -15,4 +15,4 @@ float TrueSheetInsets::bottomSafeArea() {
15
15
  return gBottomSafeArea.load(std::memory_order_relaxed);
16
16
  }
17
17
 
18
- } // namespace facebook::react
18
+ } // namespace facebook::react
@@ -13,4 +13,4 @@ class TrueSheetInsets {
13
13
  static float bottomSafeArea();
14
14
  };
15
15
 
16
- } // namespace facebook::react
16
+ } // namespace facebook::react
@@ -11,10 +11,7 @@ namespace facebook::react {
11
11
  * native. A zero dimension leaves that axis to Yoga.
12
12
  */
13
13
  inline void applyContainerSizeToYogaNode(
14
- yoga::Node &node,
15
- const yoga::Style &baseStyle,
16
- float containerWidth,
17
- float containerHeight) {
14
+ yoga::Node &node, const yoga::Style &baseStyle, float containerWidth, float containerHeight) {
18
15
  if (containerWidth <= 0 && containerHeight <= 0) {
19
16
  return;
20
17
  }
@@ -43,4 +40,4 @@ inline void applyContainerSizeToYogaNode(
43
40
  }
44
41
  }
45
42
 
46
- } // namespace facebook::react
43
+ } // namespace facebook::react
@@ -18,4 +18,4 @@ class TrueSheetOverlayViewComponentDescriptor final
18
18
  }
19
19
  };
20
20
 
21
- } // namespace facebook::react
21
+ } // namespace facebook::react
@@ -11,7 +11,7 @@ void TrueSheetOverlayViewShadowNode::adjustLayoutWithState() {
11
11
 
12
12
  const auto &stateData = getStateData();
13
13
  applyContainerSizeToYogaNode(
14
- yogaNode_, getConcreteProps().yogaStyle, stateData.containerWidth, stateData.containerHeight);
14
+ yogaNode_, getConcreteProps().yogaStyle, stateData.containerWidth, stateData.containerHeight);
15
15
  }
16
16
 
17
- } // namespace facebook::react
17
+ } // namespace facebook::react
@@ -16,11 +16,8 @@ JSI_EXPORT extern const char TrueSheetOverlayViewComponentName[];
16
16
  * lay out against the full window like the sheet's container.
17
17
  */
18
18
  class JSI_EXPORT TrueSheetOverlayViewShadowNode final
19
- : public ConcreteViewShadowNode<
20
- TrueSheetOverlayViewComponentName,
21
- TrueSheetOverlayViewProps,
22
- TrueSheetOverlayViewEventEmitter,
23
- TrueSheetViewState> {
19
+ : public ConcreteViewShadowNode<TrueSheetOverlayViewComponentName, TrueSheetOverlayViewProps,
20
+ TrueSheetOverlayViewEventEmitter, TrueSheetViewState> {
24
21
  using ConcreteViewShadowNode::ConcreteViewShadowNode;
25
22
 
26
23
  public:
@@ -33,4 +30,4 @@ class JSI_EXPORT TrueSheetOverlayViewShadowNode final
33
30
  void adjustLayoutWithState();
34
31
  };
35
32
 
36
- } // namespace facebook::react
33
+ } // namespace facebook::react
@@ -8,13 +8,11 @@ namespace facebook::react {
8
8
  /*
9
9
  * Descriptor for <TrueSheetView> component.
10
10
  */
11
- class TrueSheetViewComponentDescriptor final
12
- : public ConcreteComponentDescriptor<TrueSheetViewShadowNode> {
11
+ class TrueSheetViewComponentDescriptor final : public ConcreteComponentDescriptor<TrueSheetViewShadowNode> {
13
12
  using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
14
13
 
15
14
  void adopt(ShadowNode &shadowNode) const override {
16
- auto &concreteShadowNode =
17
- static_cast<TrueSheetViewShadowNode &>(shadowNode);
15
+ auto &concreteShadowNode = static_cast<TrueSheetViewShadowNode &>(shadowNode);
18
16
  concreteShadowNode.adjustLayoutWithState();
19
17
 
20
18
  ConcreteComponentDescriptor::adopt(shadowNode);
@@ -25,4 +23,4 @@ class TrueSheetViewComponentDescriptor final
25
23
  }
26
24
  };
27
25
 
28
- } // namespace facebook::react
26
+ } // namespace facebook::react
@@ -11,20 +11,18 @@ void TrueSheetViewShadowNode::adjustLayoutWithState() {
11
11
 
12
12
  const auto &stateData = getStateData();
13
13
  applyContainerSizeToYogaNode(
14
- yogaNode_, getConcreteProps().yogaStyle, stateData.containerWidth, stateData.containerHeight);
14
+ yogaNode_, getConcreteProps().yogaStyle, stateData.containerWidth, stateData.containerHeight);
15
15
  }
16
16
 
17
17
  #if !defined(ANDROID)
18
- void TrueSheetViewShadowNode::setEventDispatcher(
19
- std::weak_ptr<const EventDispatcher> dispatcher) {
18
+ void TrueSheetViewShadowNode::setEventDispatcher(std::weak_ptr<const EventDispatcher> dispatcher) {
20
19
  getStateDataMutable().setEventDispatcher(dispatcher);
21
20
  }
22
21
 
23
- TrueSheetViewShadowNode::StateData &
24
- TrueSheetViewShadowNode::getStateDataMutable() {
22
+ TrueSheetViewShadowNode::StateData &TrueSheetViewShadowNode::getStateDataMutable() {
25
23
  ensureUnsealed();
26
24
  return const_cast<TrueSheetViewShadowNode::StateData &>(getStateData());
27
25
  }
28
26
  #endif
29
27
 
30
- } // namespace facebook::react
28
+ } // namespace facebook::react
@@ -15,12 +15,8 @@ JSI_EXPORT extern const char TrueSheetViewComponentName[];
15
15
  /*
16
16
  * `ShadowNode` for <TrueSheetView> component.
17
17
  */
18
- class JSI_EXPORT TrueSheetViewShadowNode final
19
- : public ConcreteViewShadowNode<
20
- TrueSheetViewComponentName,
21
- TrueSheetViewProps,
22
- TrueSheetViewEventEmitter,
23
- TrueSheetViewState> {
18
+ class JSI_EXPORT TrueSheetViewShadowNode final : public ConcreteViewShadowNode<TrueSheetViewComponentName,
19
+ TrueSheetViewProps, TrueSheetViewEventEmitter, TrueSheetViewState> {
24
20
  using ConcreteViewShadowNode::ConcreteViewShadowNode;
25
21
 
26
22
  public:
@@ -42,4 +38,4 @@ class JSI_EXPORT TrueSheetViewShadowNode final
42
38
  #endif
43
39
  };
44
40
 
45
- } // namespace facebook::react
41
+ } // namespace facebook::react
@@ -9,15 +9,13 @@ folly::dynamic TrueSheetViewState::getDynamic() const {
9
9
  #endif
10
10
 
11
11
  #if !defined(ANDROID)
12
- void TrueSheetViewState::setEventDispatcher(
13
- std::weak_ptr<const EventDispatcher> dispatcher) {
12
+ void TrueSheetViewState::setEventDispatcher(std::weak_ptr<const EventDispatcher> dispatcher) {
14
13
  eventDispatcher_ = dispatcher;
15
14
  }
16
15
 
17
- std::weak_ptr<const EventDispatcher> TrueSheetViewState::getEventDispatcher()
18
- const noexcept {
16
+ std::weak_ptr<const EventDispatcher> TrueSheetViewState::getEventDispatcher() const noexcept {
19
17
  return eventDispatcher_;
20
18
  }
21
19
  #endif
22
20
 
23
- } // namespace facebook::react
21
+ } // namespace facebook::react
@@ -23,11 +23,10 @@ class TrueSheetViewState final {
23
23
  TrueSheetViewState() = default;
24
24
 
25
25
  #ifdef ANDROID
26
- TrueSheetViewState(
27
- TrueSheetViewState const &previousState,
28
- folly::dynamic data)
26
+ TrueSheetViewState(TrueSheetViewState const &previousState, folly::dynamic data)
29
27
  : containerWidth(static_cast<float>(data["containerWidth"].getDouble())),
30
- containerHeight(static_cast<float>(data["containerHeight"].getDouble())) {}
28
+ containerHeight(static_cast<float>(data["containerHeight"].getDouble())) {
29
+ }
31
30
  #endif
32
31
 
33
32
  float containerWidth{0};
@@ -49,4 +48,4 @@ class TrueSheetViewState final {
49
48
  #endif
50
49
  };
51
50
 
52
- } // namespace facebook::react
51
+ } // namespace facebook::react
@@ -168,6 +168,8 @@ using namespace facebook::react;
168
168
  if (_scrollableHandle > 0 && !_detectedScrollView) {
169
169
  [self.delegate contentViewScrollViewDidChange];
170
170
  }
171
+
172
+ [self updateKeyboardInset];
171
173
  }
172
174
 
173
175
  #pragma mark - Scrollable
@@ -183,6 +185,13 @@ using namespace facebook::react;
183
185
  _detectedScrollView.scrollView.contentInset = contentInset;
184
186
  }
185
187
 
188
+ - (void)updateKeyboardInset {
189
+ CGFloat keyboardHeight = _keyboardObserver.currentHeight;
190
+ if (_detectedScrollView && keyboardHeight > 0) {
191
+ [self setKeyboardInset:[self keyboardInsetWithHeight:keyboardHeight]];
192
+ }
193
+ }
194
+
186
195
  - (void)clearScrollable {
187
196
  [self setKeyboardInset:0];
188
197
  _appliedKeyboardOffset = 0;
@@ -213,26 +222,22 @@ using namespace facebook::react;
213
222
  [self applyContentInsetAdjustment];
214
223
 
215
224
  // If keyboard is currently showing, re-apply the keyboard inset to the new ScrollView
216
- CGFloat keyboardHeight = _keyboardObserver ? _keyboardObserver.currentHeight : 0;
217
- if (keyboardHeight > 0) {
218
- [self setKeyboardInset:[self keyboardInsetWithHeight:keyboardHeight]];
219
- }
225
+ [self updateKeyboardInset];
220
226
  }
221
227
 
222
- // A relative footer stays behind the keyboard below the content, so its
223
- // height shields that much of the keyboard's overlap. An absolute footer
224
- // floats within the viewport — its clearance is the content padding's job
225
- // (the caret reveal accounts for it, see footerOcclusion).
228
+ // Short or nested scroll views can end above the sheet's bottom edge.
229
+ // Their frame also accounts for space occupied by a relative footer.
226
230
  - (CGFloat)keyboardInsetWithHeight:(CGFloat)height {
227
- CGFloat inset = height;
228
- if (self.footerView && !_keyboardObserver.viewController.absoluteFooter) {
229
- inset = MAX(0, height - self.footerView.frame.size.height);
230
- }
231
+ UIScrollView *scrollView = _detectedScrollView.scrollView;
232
+ UIWindow *window = scrollView.window;
233
+ CGRect scrollFrame = [scrollView convertRect:scrollView.bounds toView:window];
234
+ CGFloat keyboardTop = CGRectGetMaxY(window.bounds) - height;
235
+ CGFloat inset = MAX(0, CGRectGetMaxY(scrollFrame) - keyboardTop);
231
236
 
232
237
  // UIKit stacks the safe area on top of contentInset while the adjustment
233
238
  // behavior is automatic — take it out so the total lands on the keyboard edge.
234
239
  if (_appliedContentInsetAdjustment) {
235
- inset = MAX(0, inset - _detectedScrollView.scrollView.safeAreaInsets.bottom);
240
+ inset = MAX(0, inset - scrollView.safeAreaInsets.bottom);
236
241
  }
237
242
 
238
243
  // Track how much of keyboardOffset actually lands in the inset so the caret
@@ -242,7 +247,6 @@ using namespace facebook::react;
242
247
 
243
248
  // Content that already fits above the keyboard has nothing to reveal — the
244
249
  // inset would only open blank scroll range below it (huge gap at the bottom).
245
- UIScrollView *scrollView = _detectedScrollView.scrollView;
246
250
  if (scrollView.contentSize.height <= scrollView.bounds.size.height - adjustedInset) {
247
251
  _appliedKeyboardOffset = 0;
248
252
  return 0;
@@ -334,12 +338,14 @@ using namespace facebook::react;
334
338
 
335
339
  CGFloat inset = [self keyboardInsetWithHeight:height];
336
340
  [UIView animateWithDuration:duration
337
- delay:0
338
- options:curve | UIViewAnimationOptionBeginFromCurrentState
339
- animations:^{
340
- [self setKeyboardInset:inset];
341
- }
342
- completion:nil];
341
+ delay:0
342
+ options:curve | UIViewAnimationOptionBeginFromCurrentState
343
+ animations:^{
344
+ [self setKeyboardInset:inset];
345
+ }
346
+ completion:^(BOOL finished) {
347
+ [self updateKeyboardInset];
348
+ }];
343
349
 
344
350
  // Defer scroll until the next run loop so content insets are applied first
345
351
  if (firstResponder) {
@@ -363,7 +369,7 @@ using namespace facebook::react;
363
369
  dispatch_async(dispatch_get_main_queue(), ^{
364
370
  // Typing can grow the content (e.g. a multiline input) past the fits-above-
365
371
  // the-keyboard threshold — keep the inset in sync before revealing the caret.
366
- [self setKeyboardInset:[self keyboardInsetWithHeight:self->_keyboardObserver.currentHeight]];
372
+ [self updateKeyboardInset];
367
373
  [self scrollToFocusedCaretAnimated:YES];
368
374
  });
369
375
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lodev09/react-native-true-sheet",
3
- "version": "4.0.0-beta.10",
3
+ "version": "4.0.0-beta.12",
4
4
  "description": "The true native bottom sheet experience for your React Native Apps.",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./lib/module/index.js",
@@ -58,7 +58,7 @@
58
58
  "typecheck": "tsc",
59
59
  "lint": "eslint --fix \"**/*.{ts,tsx}\"",
60
60
  "format": "prettier --write \"**/*.{ts,tsx}\"",
61
- "tidy": "yarn typecheck && yarn lint && yarn format && scripts/objclint.sh && scripts/ktlint.sh",
61
+ "tidy": "yarn typecheck && yarn lint && yarn format && scripts/objclint.sh && scripts/cpplint.sh && scripts/ktlint.sh",
62
62
  "clean": "scripts/clean.sh",
63
63
  "prepare": "bob build",
64
64
  "release": "release-it",