@lodev09/react-native-true-sheet 4.0.0-beta.2 → 4.0.0-beta.20
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.
- package/README.md +25 -4
- package/android/build.gradle +22 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +50 -16
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +302 -184
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +21 -110
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +116 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +18 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +119 -50
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +360 -163
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +54 -12
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +22 -9
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +84 -5
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +104 -39
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +3 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +137 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +15 -2
- package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
- package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
- package/android/src/main/jni/TrueSheetSpec.h +1 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +1 -9
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +110 -30
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +30 -7
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +2 -2
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +9 -8
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +9 -3
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +3 -6
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +4 -6
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +43 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +33 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +7 -40
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
- package/ios/TrueSheetContainerView.h +27 -7
- package/ios/TrueSheetContainerView.mm +31 -11
- package/ios/TrueSheetContentView.h +43 -15
- package/ios/TrueSheetContentView.mm +211 -189
- package/ios/TrueSheetFooterView.h +6 -0
- package/ios/TrueSheetFooterView.mm +93 -17
- package/ios/TrueSheetModule.h +2 -7
- package/ios/TrueSheetModule.mm +7 -16
- package/ios/TrueSheetOverlayView.h +32 -0
- package/ios/TrueSheetOverlayView.mm +161 -0
- package/ios/TrueSheetView.mm +212 -55
- package/ios/TrueSheetViewController.h +31 -3
- package/ios/TrueSheetViewController.mm +393 -94
- package/ios/core/TrueSheetBlurView.h +0 -2
- package/ios/core/TrueSheetBlurView.mm +3 -36
- package/ios/core/TrueSheetDetentCalculator.h +7 -1
- package/ios/core/TrueSheetDetentCalculator.mm +27 -4
- package/ios/core/TrueSheetGrabberView.mm +13 -3
- package/ios/core/TrueSheetKeyboardObserver.mm +2 -0
- package/ios/core/TrueSheetOverlayContainerView.h +31 -0
- package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
- package/ios/events/TrueSheetLifecycleEvents.h +2 -0
- package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
- package/ios/tvos/TrueSheetStubs.mm +12 -0
- package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
- package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
- package/lib/module/TrueSheet.js +173 -39
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +218 -243
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetBackground.js +15 -0
- package/lib/module/TrueSheetBackground.js.map +1 -0
- package/lib/module/TrueSheetOverlay.js +30 -0
- package/lib/module/TrueSheetOverlay.js.map +1 -0
- package/lib/module/TrueSheetOverlay.web.js +35 -0
- package/lib/module/TrueSheetOverlay.web.js.map +1 -0
- package/lib/module/TrueSheetPeek.web.js +20 -15
- package/lib/module/TrueSheetPeek.web.js.map +1 -1
- package/lib/module/detentBackgrounds.js +21 -0
- package/lib/module/detentBackgrounds.js.map +1 -0
- package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +14 -10
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/mocks/index.js +18 -2
- package/lib/module/mocks/index.js.map +1 -1
- package/lib/module/mocks/navigationExpoRouter.js.map +1 -1
- package/lib/module/navigation/base-types.js +2 -0
- package/lib/module/navigation/base-types.js.map +1 -0
- package/lib/module/navigation/createTrueSheetRouter.js +0 -9
- package/lib/module/navigation/createTrueSheetRouter.js.map +1 -1
- package/lib/module/navigation/expo-router/base-types.js +2 -0
- package/lib/module/navigation/expo-router/base-types.js.map +1 -0
- package/lib/module/navigation/expo-router/index.js +10 -7
- package/lib/module/navigation/expo-router/index.js.map +1 -1
- package/lib/module/navigation/expo-router/types.js +4 -0
- package/lib/module/navigation/expo-router/types.js.map +1 -0
- package/lib/module/navigation/index.js.map +1 -1
- package/lib/module/navigation/screen/useSheetScreenState.js +1 -0
- package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
- package/lib/module/web/constants.js +1 -1
- package/lib/module/web/constants.js.map +1 -1
- package/lib/module/web/layout.js +168 -0
- package/lib/module/web/layout.js.map +1 -0
- package/lib/module/web/vaul/constants.js +4 -0
- package/lib/module/web/vaul/constants.js.map +1 -1
- package/lib/module/web/vaul/context.js.map +1 -1
- package/lib/module/web/vaul/helpers.js +1 -0
- package/lib/module/web/vaul/helpers.js.map +1 -1
- package/lib/module/web/vaul/index.js +44 -6
- package/lib/module/web/vaul/index.js.map +1 -1
- package/lib/module/web/vaul/use-snap-points.js +6 -8
- package/lib/module/web/vaul/use-snap-points.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts +6 -0
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +135 -34
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetBackground.d.ts +3 -0
- package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
- package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
- package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetPeek.web.d.ts +8 -4
- package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
- package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
- package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
- package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +11 -7
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/index.d.ts +4 -0
- package/lib/typescript/src/mocks/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +3 -3
- package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -1
- package/lib/typescript/src/navigation/base-types.d.ts +9 -0
- package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +2 -13
- package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -1
- package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
- package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/expo-router/index.d.ts +8 -6
- package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
- package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/index.d.ts +3 -4
- package/lib/typescript/src/navigation/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +2 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
- package/lib/typescript/src/navigation/types.d.ts +41 -5
- package/lib/typescript/src/navigation/types.d.ts.map +1 -1
- package/lib/typescript/src/web/constants.d.ts +1 -1
- package/lib/typescript/src/web/constants.d.ts.map +1 -1
- package/lib/typescript/src/web/layout.d.ts +19 -0
- package/lib/typescript/src/web/layout.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/constants.d.ts +1 -0
- package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/context.d.ts +1 -0
- package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/helpers.d.ts +1 -0
- package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/index.d.ts +7 -1
- package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
- package/package.json +17 -8
- package/react-native.config.js +1 -0
- package/src/TrueSheet.tsx +206 -48
- package/src/TrueSheet.types.ts +142 -35
- package/src/TrueSheet.web.tsx +270 -290
- package/src/TrueSheetBackground.tsx +17 -0
- package/src/TrueSheetOverlay.tsx +24 -0
- package/src/TrueSheetOverlay.web.tsx +33 -0
- package/src/TrueSheetPeek.web.tsx +25 -23
- package/src/detentBackgrounds.ts +24 -0
- package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/src/fabric/TrueSheetViewNativeComponent.ts +14 -10
- package/src/index.ts +1 -0
- package/src/mocks/index.ts +17 -2
- package/src/mocks/navigationExpoRouter.ts +6 -5
- package/src/navigation/base-types.ts +20 -0
- package/src/navigation/createTrueSheetRouter.ts +5 -33
- package/src/navigation/expo-router/base-types.ts +18 -0
- package/src/navigation/expo-router/index.ts +28 -30
- package/src/navigation/expo-router/types.ts +116 -0
- package/src/navigation/index.ts +6 -7
- package/src/navigation/screen/useSheetScreenState.ts +2 -0
- package/src/navigation/types.ts +75 -22
- package/src/web/constants.ts +1 -1
- package/src/web/layout.ts +196 -0
- package/src/web/vaul/constants.ts +4 -0
- package/src/web/vaul/context.ts +1 -0
- package/src/web/vaul/helpers.ts +5 -0
- package/src/web/vaul/index.tsx +67 -5
- package/src/web/vaul/use-snap-points.ts +6 -14
|
@@ -25,6 +25,8 @@ using namespace facebook::react;
|
|
|
25
25
|
CGFloat _currentKeyboardOffset;
|
|
26
26
|
CGFloat _bottomInset;
|
|
27
27
|
BOOL _keyboardVisible;
|
|
28
|
+
BOOL _didPushBottomInset;
|
|
29
|
+
CGFloat _appliedBottomInset;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
@@ -43,6 +45,8 @@ using namespace facebook::react;
|
|
|
43
45
|
_currentKeyboardOffset = 0;
|
|
44
46
|
_bottomInset = 0;
|
|
45
47
|
_keyboardVisible = NO;
|
|
48
|
+
_didPushBottomInset = NO;
|
|
49
|
+
_appliedBottomInset = 0;
|
|
46
50
|
}
|
|
47
51
|
return self;
|
|
48
52
|
}
|
|
@@ -55,7 +59,11 @@ using namespace facebook::react;
|
|
|
55
59
|
// bottom safe-area inset — the footer owns the sheet's bottom edge, so it
|
|
56
60
|
// absorbs the inset and its background fills it.
|
|
57
61
|
- (void)setBottomInset:(CGFloat)bottomInset {
|
|
58
|
-
|
|
62
|
+
// The first push must go through even when the value matches the ivar
|
|
63
|
+
// default (0) — it marks the state initialized so the shadow node stops
|
|
64
|
+
// seeding from the app-wide precalculated inset, which may belong to
|
|
65
|
+
// another sheet (e.g. a floating sheet whose real inset is 0).
|
|
66
|
+
if (_didPushBottomInset && _bottomInset == bottomInset) {
|
|
59
67
|
return;
|
|
60
68
|
}
|
|
61
69
|
_bottomInset = bottomInset;
|
|
@@ -85,6 +93,14 @@ using namespace facebook::react;
|
|
|
85
93
|
// them an inset stale (same reason Android bridges through
|
|
86
94
|
// TrueSheetStateUpdater).
|
|
87
95
|
_state->updateState(std::move(newState), EventQueue::UpdateMode::unstable_Immediate);
|
|
96
|
+
_didPushBottomInset = YES;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// The inset baked into the current layout by the shadow node (pushed or
|
|
100
|
+
// seeded — see TrueSheetFooterViewShadowNode). Refreshed in
|
|
101
|
+
// updateLayoutMetrics so it always pairs with the measured height.
|
|
102
|
+
- (CGFloat)appliedBottomInset {
|
|
103
|
+
return _appliedBottomInset;
|
|
88
104
|
}
|
|
89
105
|
|
|
90
106
|
// Height the footer occupies above the keyboard — its layout height minus the
|
|
@@ -123,11 +139,24 @@ using namespace facebook::react;
|
|
|
123
139
|
oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics {
|
|
124
140
|
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
|
125
141
|
|
|
142
|
+
// State lands before layout metrics within the same mount transaction, so
|
|
143
|
+
// this reflects the inset the shadow node baked into this frame — seeded
|
|
144
|
+
// values are recorded back into the state at layout time (see
|
|
145
|
+
// TrueSheetFooterViewShadowNode).
|
|
146
|
+
CGFloat previousAppliedBottomInset = _appliedBottomInset;
|
|
147
|
+
if (_state) {
|
|
148
|
+
_appliedBottomInset = _state->getData().bottomInset;
|
|
149
|
+
}
|
|
150
|
+
|
|
126
151
|
CGFloat height = layoutMetrics.frame.size.height;
|
|
127
|
-
|
|
152
|
+
// An inset change alone (fixed-height footer: padding moves, frame doesn't)
|
|
153
|
+
// still changes what's baked in — report so the controller re-pairs.
|
|
154
|
+
if (height != _lastHeight || _appliedBottomInset != previousAppliedBottomInset) {
|
|
128
155
|
_lastHeight = height;
|
|
129
156
|
[self.delegate footerViewDidChangeSize:CGSizeMake(layoutMetrics.frame.size.width, height)];
|
|
130
157
|
}
|
|
158
|
+
|
|
159
|
+
[self applyKeyboardOffset];
|
|
131
160
|
}
|
|
132
161
|
|
|
133
162
|
- (void)prepareForRecycle {
|
|
@@ -143,32 +172,56 @@ using namespace facebook::react;
|
|
|
143
172
|
_currentKeyboardOffset = 0;
|
|
144
173
|
_bottomInset = 0;
|
|
145
174
|
_keyboardVisible = NO;
|
|
175
|
+
_didPushBottomInset = NO;
|
|
176
|
+
_appliedBottomInset = 0;
|
|
146
177
|
}
|
|
147
178
|
|
|
148
179
|
#pragma mark - TrueSheetKeyboardObserverDelegate
|
|
149
180
|
|
|
181
|
+
// Only an absolute footer floats above the keyboard — a relative footer stays
|
|
182
|
+
// in the layout flow behind it, and an absolute one opts out via
|
|
183
|
+
// footerOptions.avoidKeyboard.
|
|
184
|
+
- (BOOL)floatsAboveKeyboard {
|
|
185
|
+
TrueSheetViewController *controller = self.keyboardObserver.viewController;
|
|
186
|
+
return controller.absoluteFooter && controller.footerAvoidsKeyboard;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
- (CGFloat)keyboardSlideForHeight:(CGFloat)height {
|
|
190
|
+
UIWindow *window = self.window;
|
|
191
|
+
if (!window) {
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Measure in the transform's coordinate space, excluding the footer's current slide.
|
|
196
|
+
CGFloat footerBottom = self.center.y + self.bounds.size.height / 2;
|
|
197
|
+
CGPoint keyboardTopInWindow = CGPointMake(CGRectGetMidX(window.bounds), CGRectGetMaxY(window.bounds) - height);
|
|
198
|
+
CGFloat keyboardTop = [self.superview convertPoint:keyboardTopInWindow fromView:window].y;
|
|
199
|
+
CGFloat keyboardOffset = self.keyboardObserver.viewController.footerKeyboardOffset;
|
|
200
|
+
return MAX(0, footerBottom - keyboardTop + keyboardOffset);
|
|
201
|
+
}
|
|
202
|
+
|
|
150
203
|
// Yoga owns the footer's frame (pinned to the container's bottom edge), so the
|
|
151
|
-
// keyboard slide is carried by a transform instead of layout.
|
|
152
|
-
// footer floats above the keyboard — a relative footer stays in the layout
|
|
153
|
-
// flow behind it.
|
|
204
|
+
// keyboard slide is carried by a transform instead of layout.
|
|
154
205
|
- (void)keyboardWillShow:(CGFloat)height duration:(NSTimeInterval)duration curve:(UIViewAnimationOptions)curve {
|
|
155
|
-
if (!self
|
|
206
|
+
if (![self floatsAboveKeyboard]) {
|
|
156
207
|
return;
|
|
157
208
|
}
|
|
158
209
|
|
|
159
210
|
[self setKeyboardVisible:YES];
|
|
160
211
|
|
|
161
|
-
CGFloat
|
|
162
|
-
CGFloat slide = MAX(0, height + keyboardOffset);
|
|
212
|
+
CGFloat slide = [self keyboardSlideForHeight:height];
|
|
163
213
|
_currentKeyboardOffset = slide;
|
|
164
214
|
|
|
165
215
|
[UIView animateWithDuration:duration
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
216
|
+
delay:0
|
|
217
|
+
options:curve | UIViewAnimationOptionBeginFromCurrentState
|
|
218
|
+
animations:^{
|
|
219
|
+
self.transform = CGAffineTransformMakeTranslation(0, -slide);
|
|
220
|
+
[self syncEdgeEffect];
|
|
221
|
+
}
|
|
222
|
+
completion:^(BOOL finished) {
|
|
223
|
+
[self applyKeyboardOffset];
|
|
224
|
+
}];
|
|
172
225
|
}
|
|
173
226
|
|
|
174
227
|
- (void)keyboardWillHide:(NSTimeInterval)duration curve:(UIViewAnimationOptions)curve {
|
|
@@ -180,20 +233,43 @@ using namespace facebook::react;
|
|
|
180
233
|
options:curve | UIViewAnimationOptionBeginFromCurrentState
|
|
181
234
|
animations:^{
|
|
182
235
|
self.transform = CGAffineTransformIdentity;
|
|
236
|
+
[self syncEdgeEffect];
|
|
183
237
|
}
|
|
184
238
|
completion:nil];
|
|
185
239
|
}
|
|
186
240
|
|
|
241
|
+
// UIKit derives the edge effect region from model geometry at commit time,
|
|
242
|
+
// outside our animation context — the blur snaps to the footer's final
|
|
243
|
+
// position while the footer is still animating. Re-evaluating the scroll
|
|
244
|
+
// view's layout inside the animation block makes the region change inherit
|
|
245
|
+
// the keyboard curve.
|
|
246
|
+
- (void)syncEdgeEffect {
|
|
247
|
+
if (@available(iOS 26.0, *)) {
|
|
248
|
+
UIScrollView *scrollView = [self edgeInteractionScrollView];
|
|
249
|
+
if (scrollView) {
|
|
250
|
+
[scrollView setNeedsLayout];
|
|
251
|
+
[scrollView layoutIfNeeded];
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
187
256
|
- (void)applyKeyboardOffset {
|
|
188
257
|
CGFloat height = self.keyboardObserver.currentHeight;
|
|
189
|
-
if (height <= 0
|
|
258
|
+
if (height <= 0) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// avoidKeyboard flipped off mid-keyboard — drop the slide already applied
|
|
263
|
+
if (![self floatsAboveKeyboard]) {
|
|
264
|
+
[self setKeyboardVisible:NO];
|
|
265
|
+
_currentKeyboardOffset = 0;
|
|
266
|
+
self.transform = CGAffineTransformIdentity;
|
|
190
267
|
return;
|
|
191
268
|
}
|
|
192
269
|
|
|
193
270
|
[self setKeyboardVisible:YES];
|
|
194
271
|
|
|
195
|
-
CGFloat
|
|
196
|
-
CGFloat slide = MAX(0, height + keyboardOffset);
|
|
272
|
+
CGFloat slide = [self keyboardSlideForHeight:height];
|
|
197
273
|
_currentKeyboardOffset = slide;
|
|
198
274
|
self.transform = CGAffineTransformMakeTranslation(0, -slide);
|
|
199
275
|
}
|
package/ios/TrueSheetModule.h
CHANGED
|
@@ -27,16 +27,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Register a sheet component view with its React tag
|
|
30
|
-
* Called automatically by TrueSheetView
|
|
30
|
+
* Called automatically by TrueSheetView when it attaches to a window.
|
|
31
|
+
* The registry holds views weakly, so no unregistration is needed.
|
|
31
32
|
*/
|
|
32
33
|
+ (void)registerView:(TrueSheetView *)view withTag:(NSNumber *)tag;
|
|
33
34
|
|
|
34
|
-
/**
|
|
35
|
-
* Unregister a sheet component view
|
|
36
|
-
* Called automatically by TrueSheetView during dealloc
|
|
37
|
-
*/
|
|
38
|
-
+ (void)unregisterViewWithTag:(NSNumber *)tag;
|
|
39
|
-
|
|
40
35
|
@end
|
|
41
36
|
|
|
42
37
|
NS_ASSUME_NONNULL_END
|
package/ios/TrueSheetModule.mm
CHANGED
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
#import <TrueSheetSpec/TrueSheetSpec.h>
|
|
20
20
|
|
|
21
|
-
// Static registry
|
|
22
|
-
|
|
21
|
+
// Static registry of live hosts by tag — weak so an unmounted host deallocates
|
|
22
|
+
// (RN zeroes the tag before teardown, so hosts can't unregister themselves)
|
|
23
|
+
static NSMapTable<NSNumber *, TrueSheetView *> *viewRegistry;
|
|
23
24
|
|
|
24
25
|
@interface TrueSheetModule () <NativeTrueSheetModuleSpec>
|
|
25
26
|
@end
|
|
@@ -30,7 +31,7 @@ RCT_EXPORT_MODULE(TrueSheetModule)
|
|
|
30
31
|
|
|
31
32
|
+ (void)initialize {
|
|
32
33
|
if (self == [TrueSheetModule class]) {
|
|
33
|
-
viewRegistry = [
|
|
34
|
+
viewRegistry = [NSMapTable strongToWeakObjectsMapTable];
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -151,7 +152,7 @@ RCT_EXPORT_MODULE(TrueSheetModule)
|
|
|
151
152
|
// Find the root presented sheet (one without a parent TrueSheet)
|
|
152
153
|
TrueSheetView *rootSheet = nil;
|
|
153
154
|
|
|
154
|
-
for (TrueSheetView *view in viewRegistry.
|
|
155
|
+
for (TrueSheetView *view in viewRegistry.objectEnumerator) {
|
|
155
156
|
if (!view.viewController.isPresented) {
|
|
156
157
|
continue;
|
|
157
158
|
}
|
|
@@ -191,7 +192,7 @@ RCT_EXPORT_MODULE(TrueSheetModule)
|
|
|
191
192
|
}
|
|
192
193
|
|
|
193
194
|
@synchronized(viewRegistry) {
|
|
194
|
-
return viewRegistry
|
|
195
|
+
return [viewRegistry objectForKey:reactTag];
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
|
|
@@ -201,17 +202,7 @@ RCT_EXPORT_MODULE(TrueSheetModule)
|
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
@synchronized(viewRegistry) {
|
|
204
|
-
viewRegistry
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
+ (void)unregisterViewWithTag:(NSNumber *)tag {
|
|
209
|
-
if (!tag) {
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
@synchronized(viewRegistry) {
|
|
214
|
-
[viewRegistry removeObjectForKey:tag];
|
|
205
|
+
[viewRegistry setObject:view forKey:tag];
|
|
215
206
|
}
|
|
216
207
|
}
|
|
217
208
|
|
|
@@ -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
|
+
/**
|
|
17
|
+
* Hidden host that renders its children in a container attached directly to
|
|
18
|
+
* the window, above every presented sheet.
|
|
19
|
+
*/
|
|
20
|
+
@interface TrueSheetOverlayView : RCTViewComponentView
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Re-stacks every overlay container above the window's other subviews.
|
|
24
|
+
* Called when a sheet presents, since presenting adds its transition view on top.
|
|
25
|
+
*/
|
|
26
|
+
+ (void)bringOverlaysToFrontInWindow:(nullable UIWindow *)window;
|
|
27
|
+
|
|
28
|
+
@end
|
|
29
|
+
|
|
30
|
+
NS_ASSUME_NONNULL_END
|
|
31
|
+
|
|
32
|
+
#endif
|
|
@@ -0,0 +1,161 @@
|
|
|
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 "TrueSheetOverlayView.h"
|
|
12
|
+
#import "core/TrueSheetOverlayContainerView.h"
|
|
13
|
+
|
|
14
|
+
#import <react/renderer/components/TrueSheetSpec/EventEmitters.h>
|
|
15
|
+
#import <react/renderer/components/TrueSheetSpec/Props.h>
|
|
16
|
+
#import <react/renderer/components/TrueSheetSpec/RCTComponentViewHelpers.h>
|
|
17
|
+
#import <react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h>
|
|
18
|
+
#import <react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h>
|
|
19
|
+
|
|
20
|
+
#import <React/RCTSurfaceTouchHandler.h>
|
|
21
|
+
|
|
22
|
+
using namespace facebook::react;
|
|
23
|
+
|
|
24
|
+
static NSHashTable<TrueSheetOverlayView *> *gOverlayViews;
|
|
25
|
+
|
|
26
|
+
@interface TrueSheetOverlayView () <TrueSheetOverlayContainerViewDelegate>
|
|
27
|
+
@end
|
|
28
|
+
|
|
29
|
+
@implementation TrueSheetOverlayView {
|
|
30
|
+
TrueSheetOverlayContainerView *_containerView;
|
|
31
|
+
// Owns `_containerView` so it has a view controller in its responder chain. The
|
|
32
|
+
// container is a sibling of `rootViewController.view`, and a window's responder chain
|
|
33
|
+
// skips its root view controller — without an owner, `-[UIView reactViewController]`
|
|
34
|
+
// finds nothing and content that installs a view controller cannot attach.
|
|
35
|
+
UIViewController *_containerController;
|
|
36
|
+
RCTSurfaceTouchHandler *_touchHandler;
|
|
37
|
+
TrueSheetOverlayViewShadowNode::ConcreteState::Shared _state;
|
|
38
|
+
CGSize _lastStateSize;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
42
|
+
return concreteComponentDescriptorProvider<TrueSheetOverlayViewComponentDescriptor>();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
+ (void)bringOverlaysToFrontInWindow:(nullable UIWindow *)window {
|
|
46
|
+
if (!window)
|
|
47
|
+
return;
|
|
48
|
+
|
|
49
|
+
for (TrueSheetOverlayView *overlay in gOverlayViews) {
|
|
50
|
+
if (overlay->_containerView.superview == window) {
|
|
51
|
+
[window bringSubviewToFront:overlay->_containerView];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
57
|
+
if (self = [super initWithFrame:frame]) {
|
|
58
|
+
static const auto defaultProps = std::make_shared<const TrueSheetOverlayViewProps>();
|
|
59
|
+
_props = defaultProps;
|
|
60
|
+
|
|
61
|
+
_containerView = [[TrueSheetOverlayContainerView alloc] initWithFrame:CGRectZero];
|
|
62
|
+
_containerView.delegate = self;
|
|
63
|
+
|
|
64
|
+
// Assigning the container as its view is what puts the controller in the responder
|
|
65
|
+
// chain; the containment below is for lifecycle only.
|
|
66
|
+
_containerController = [UIViewController new];
|
|
67
|
+
_containerController.view = _containerView;
|
|
68
|
+
|
|
69
|
+
_touchHandler = [[RCTSurfaceTouchHandler alloc] init];
|
|
70
|
+
[_touchHandler attachToView:_containerView];
|
|
71
|
+
|
|
72
|
+
_lastStateSize = CGSizeZero;
|
|
73
|
+
|
|
74
|
+
// The host stays in the React tree for layout only — children render in the container
|
|
75
|
+
self.hidden = YES;
|
|
76
|
+
|
|
77
|
+
if (!gOverlayViews) {
|
|
78
|
+
gOverlayViews = [NSHashTable weakObjectsHashTable];
|
|
79
|
+
}
|
|
80
|
+
[gOverlayViews addObject:self];
|
|
81
|
+
}
|
|
82
|
+
return self;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
- (void)didMoveToWindow {
|
|
86
|
+
[super didMoveToWindow];
|
|
87
|
+
|
|
88
|
+
UIWindow *window = self.window;
|
|
89
|
+
if (window) {
|
|
90
|
+
_containerView.frame = window.bounds;
|
|
91
|
+
[window addSubview:_containerView];
|
|
92
|
+
|
|
93
|
+
UIViewController *root = window.rootViewController;
|
|
94
|
+
if (root != nil && _containerController.parentViewController != root) {
|
|
95
|
+
[root addChildViewController:_containerController];
|
|
96
|
+
[_containerController didMoveToParentViewController:root];
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
[_containerController willMoveToParentViewController:nil];
|
|
100
|
+
[_containerView removeFromSuperview];
|
|
101
|
+
[_containerController removeFromParentViewController];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#pragma mark - State
|
|
106
|
+
|
|
107
|
+
- (void)updateState:(const State::Shared &)state oldState:(const State::Shared &)oldState {
|
|
108
|
+
_state = std::static_pointer_cast<TrueSheetOverlayViewShadowNode::ConcreteState const>(state);
|
|
109
|
+
|
|
110
|
+
if (self.window) {
|
|
111
|
+
[self updateStateWithSize:self.window.bounds.size];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
- (void)overlayContainerViewDidLayout:(CGSize)size {
|
|
116
|
+
[self updateStateWithSize:size];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
- (void)updateStateWithSize:(CGSize)size {
|
|
120
|
+
if (!_state)
|
|
121
|
+
return;
|
|
122
|
+
|
|
123
|
+
if (fabs(size.width - _lastStateSize.width) < 0.5 && fabs(size.height - _lastStateSize.height) < 0.5)
|
|
124
|
+
return;
|
|
125
|
+
|
|
126
|
+
_lastStateSize = size;
|
|
127
|
+
|
|
128
|
+
auto stateData = _state->getData();
|
|
129
|
+
stateData.containerWidth = static_cast<float>(size.width);
|
|
130
|
+
stateData.containerHeight = static_cast<float>(size.height);
|
|
131
|
+
_state->updateState(std::move(stateData), EventQueue::UpdateMode::unstable_Immediate);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
#pragma mark - Child Component Mounting
|
|
135
|
+
|
|
136
|
+
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
|
|
137
|
+
[_containerView insertSubview:childComponentView atIndex:index];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
|
|
141
|
+
[childComponentView removeFromSuperview];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
- (void)prepareForRecycle {
|
|
145
|
+
[super prepareForRecycle];
|
|
146
|
+
|
|
147
|
+
[_containerController willMoveToParentViewController:nil];
|
|
148
|
+
[_containerView removeFromSuperview];
|
|
149
|
+
[_containerController removeFromParentViewController];
|
|
150
|
+
_lastStateSize = CGSizeZero;
|
|
151
|
+
_state.reset();
|
|
152
|
+
self.hidden = YES;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@end
|
|
156
|
+
|
|
157
|
+
Class<RCTComponentViewProtocol> TrueSheetOverlayViewCls(void) {
|
|
158
|
+
return TrueSheetOverlayView.class;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
#endif
|