@lodev09/react-native-true-sheet 3.1.0-beta.5 → 3.1.0-beta.7
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/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +4 -4
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +77 -24
- package/ios/TrueSheetView.mm +5 -5
- package/ios/TrueSheetViewController.h +3 -2
- package/ios/TrueSheetViewController.mm +95 -30
- package/lib/module/reanimated/ReanimatedTrueSheetProvider.js +3 -3
- package/lib/module/reanimated/ReanimatedTrueSheetProvider.js.map +1 -1
- package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__mocks__/index.js +6 -5
- package/src/reanimated/ReanimatedTrueSheetProvider.tsx +6 -9
|
@@ -157,15 +157,15 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
157
157
|
override fun viewControllerWillPresent(index: Int, position: Float, detent: Float) {
|
|
158
158
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
159
159
|
eventDispatcher?.dispatchEvent(WillPresentEvent(surfaceId, id, index, position, detent))
|
|
160
|
+
|
|
161
|
+
// Enable touch event dispatching to React Native
|
|
162
|
+
viewController.eventDispatcher = eventDispatcher
|
|
163
|
+
containerView?.footerView?.eventDispatcher = eventDispatcher
|
|
160
164
|
}
|
|
161
165
|
|
|
162
166
|
override fun viewControllerDidPresent(index: Int, position: Float, detent: Float) {
|
|
163
167
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
164
168
|
eventDispatcher?.dispatchEvent(DidPresentEvent(surfaceId, id, index, position, detent))
|
|
165
|
-
|
|
166
|
-
// Enable touch event dispatching to React Native
|
|
167
|
-
viewController.eventDispatcher = eventDispatcher
|
|
168
|
-
containerView?.footerView?.eventDispatcher = eventDispatcher
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
override fun viewControllerWillDismiss() {
|
|
@@ -66,6 +66,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
66
66
|
private const val GRABBER_TAG = "TrueSheetGrabber"
|
|
67
67
|
private const val DEFAULT_MAX_WIDTH = 640 // dp
|
|
68
68
|
private const val DEFAULT_CORNER_RADIUS = 16 // dp
|
|
69
|
+
|
|
70
|
+
// Animation durations from res/anim/true_sheet_slide_in.xml and true_sheet_slide_out.xml
|
|
71
|
+
private const val PRESENT_ANIMATION_DURATION = 200L
|
|
72
|
+
private const val DISMISS_ANIMATION_DURATION = 150L
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
// ====================================================================
|
|
@@ -115,6 +119,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
115
119
|
private val resolvedDetentPositions = mutableListOf<Int>()
|
|
116
120
|
|
|
117
121
|
private var isDragging = false
|
|
122
|
+
private var isDismissing = false
|
|
118
123
|
private var isReconfiguring = false
|
|
119
124
|
private var windowAnimation: Int = 0
|
|
120
125
|
private var lastEmittedPositionPx: Int = -1
|
|
@@ -272,6 +277,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
272
277
|
|
|
273
278
|
dialog = null
|
|
274
279
|
isDragging = false
|
|
280
|
+
isDismissing = false
|
|
275
281
|
isPresented = false
|
|
276
282
|
isDialogVisible = false
|
|
277
283
|
lastEmittedPositionPx = -1
|
|
@@ -286,43 +292,51 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
286
292
|
setupGrabber()
|
|
287
293
|
|
|
288
294
|
sheetContainer?.post {
|
|
289
|
-
val detentInfo = getDetentInfoForIndex(currentDetentIndex)
|
|
290
|
-
val detent = getDetentValueForIndex(detentInfo.index)
|
|
291
295
|
val positionPx = bottomSheetView?.let { ScreenUtils.getScreenY(it) } ?: screenHeight
|
|
292
296
|
|
|
293
|
-
delegate?.viewControllerDidPresent(detentInfo.index, detentInfo.position, detent)
|
|
294
|
-
|
|
295
297
|
// Store resolved position for initial detent
|
|
296
|
-
storeResolvedPosition(
|
|
298
|
+
storeResolvedPosition(currentDetentIndex)
|
|
297
299
|
emitChangePositionDelegate(positionPx, realtime = false)
|
|
298
300
|
|
|
301
|
+
positionFooter()
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Emit didPresent/didFocus after present animation completes
|
|
305
|
+
sheetContainer?.postDelayed({
|
|
306
|
+
val detentInfo = getDetentInfoForIndex(currentDetentIndex)
|
|
307
|
+
val detent = getDetentValueForIndex(detentInfo.index)
|
|
308
|
+
|
|
309
|
+
delegate?.viewControllerDidPresent(detentInfo.index, detentInfo.position, detent)
|
|
310
|
+
|
|
299
311
|
// Notify parent sheet that it has lost focus (after this sheet appeared)
|
|
300
312
|
parentSheetView?.viewControllerDidBlur()
|
|
301
313
|
|
|
314
|
+
// Emit didFocus with didPresent
|
|
315
|
+
delegate?.viewControllerDidFocus()
|
|
316
|
+
|
|
302
317
|
presentPromise?.invoke()
|
|
303
318
|
presentPromise = null
|
|
304
|
-
|
|
305
|
-
positionFooter()
|
|
306
|
-
}
|
|
319
|
+
}, PRESENT_ANIMATION_DURATION)
|
|
307
320
|
}
|
|
308
321
|
|
|
309
322
|
dialog.setOnCancelListener {
|
|
310
|
-
|
|
323
|
+
// Skip if already handled by STATE_HIDDEN (programmatic dismiss)
|
|
324
|
+
if (isDismissing) return@setOnCancelListener
|
|
311
325
|
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
326
|
+
// User-initiated dismiss (back button, tap outside)
|
|
327
|
+
isDismissing = true
|
|
328
|
+
emitWillDismissEvents()
|
|
315
329
|
|
|
316
|
-
|
|
317
|
-
|
|
330
|
+
// Emit off-screen position since onSlide isn't triggered for user-initiated dismiss
|
|
331
|
+
emitChangePositionDelegate(screenHeight, realtime = false)
|
|
318
332
|
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
|
|
333
|
+
// Emit didBlur/didDismiss after dismiss animation completes
|
|
334
|
+
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
|
335
|
+
emitDidDismissEvents()
|
|
336
|
+
}, DISMISS_ANIMATION_DURATION)
|
|
337
|
+
}
|
|
322
338
|
|
|
323
|
-
|
|
324
|
-
dismissPromise = null
|
|
325
|
-
delegate?.viewControllerDidDismiss(hadParent)
|
|
339
|
+
dialog.setOnDismissListener {
|
|
326
340
|
cleanupDialog()
|
|
327
341
|
}
|
|
328
342
|
}
|
|
@@ -348,7 +362,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
348
362
|
|
|
349
363
|
override fun onStateChanged(sheetView: View, newState: Int) {
|
|
350
364
|
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
|
351
|
-
|
|
365
|
+
// Mark as dismissing so setOnCancelListener skips emitting events
|
|
366
|
+
isDismissing = true
|
|
367
|
+
emitDidDismissEvents()
|
|
368
|
+
dialog.dismiss()
|
|
352
369
|
return
|
|
353
370
|
}
|
|
354
371
|
|
|
@@ -433,6 +450,34 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
433
450
|
rnScreensObserver = null
|
|
434
451
|
}
|
|
435
452
|
|
|
453
|
+
// ====================================================================
|
|
454
|
+
// MARK: - Dismiss Event Helpers
|
|
455
|
+
// ====================================================================
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Emits willBlur and willDismiss events, and notifies parent sheet of upcoming focus change.
|
|
459
|
+
*/
|
|
460
|
+
private fun emitWillDismissEvents() {
|
|
461
|
+
delegate?.viewControllerWillBlur()
|
|
462
|
+
delegate?.viewControllerWillDismiss()
|
|
463
|
+
parentSheetView?.viewControllerWillFocus()
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Emits didBlur and didDismiss events, notifies parent sheet, and invokes dismiss promise.
|
|
468
|
+
*/
|
|
469
|
+
private fun emitDidDismissEvents() {
|
|
470
|
+
val hadParent = parentSheetView != null
|
|
471
|
+
parentSheetView?.viewControllerDidFocus()
|
|
472
|
+
parentSheetView = null
|
|
473
|
+
|
|
474
|
+
delegate?.viewControllerDidBlur()
|
|
475
|
+
delegate?.viewControllerDidDismiss(hadParent)
|
|
476
|
+
|
|
477
|
+
dismissPromise?.invoke()
|
|
478
|
+
dismissPromise = null
|
|
479
|
+
}
|
|
480
|
+
|
|
436
481
|
// ====================================================================
|
|
437
482
|
// MARK: - Dialog Visibility (for stacking)
|
|
438
483
|
// ====================================================================
|
|
@@ -518,6 +563,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
518
563
|
|
|
519
564
|
delegate?.viewControllerWillPresent(detentInfo.index, detentInfo.position, detent)
|
|
520
565
|
|
|
566
|
+
// Emit willFocus with willPresent
|
|
567
|
+
delegate?.viewControllerWillFocus()
|
|
568
|
+
|
|
521
569
|
if (!animated) {
|
|
522
570
|
dialog.window?.setWindowAnimations(0)
|
|
523
571
|
}
|
|
@@ -527,15 +575,20 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
527
575
|
}
|
|
528
576
|
|
|
529
577
|
fun dismiss(animated: Boolean = true) {
|
|
578
|
+
emitWillDismissEvents()
|
|
579
|
+
|
|
530
580
|
this.post {
|
|
531
581
|
// Emit off-screen position (detent = 0 since sheet is fully hidden)
|
|
532
582
|
emitChangePositionDelegate(screenHeight, realtime = false)
|
|
533
583
|
}
|
|
534
584
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
post { dismiss() }
|
|
585
|
+
if (!animated) {
|
|
586
|
+
dialog?.window?.setWindowAnimations(0)
|
|
538
587
|
}
|
|
588
|
+
|
|
589
|
+
// Temporarily enable hideable to allow STATE_HIDDEN transition
|
|
590
|
+
behavior?.isHideable = true
|
|
591
|
+
behavior?.state = BottomSheetBehavior.STATE_HIDDEN
|
|
539
592
|
}
|
|
540
593
|
|
|
541
594
|
// ====================================================================
|
package/ios/TrueSheetView.mm
CHANGED
|
@@ -223,7 +223,7 @@ using namespace facebook::react;
|
|
|
223
223
|
[self->_controller setupSheetDetents];
|
|
224
224
|
[self->_controller applyActiveDetent];
|
|
225
225
|
}];
|
|
226
|
-
[_controller
|
|
226
|
+
[_controller setupDraggable];
|
|
227
227
|
} else if (_initialDetentIndex >= 0) {
|
|
228
228
|
[self presentAtIndex:_initialDetentIndex animated:_initialDetentAnimated completion:nil];
|
|
229
229
|
}
|
|
@@ -361,7 +361,7 @@ using namespace facebook::react;
|
|
|
361
361
|
/**
|
|
362
362
|
* Debounced sheet update to handle rapid content/header size changes.
|
|
363
363
|
*/
|
|
364
|
-
- (void)
|
|
364
|
+
- (void)setupSheetDetentsForSizeChange {
|
|
365
365
|
if (!_controller.isPresented || _isSheetUpdatePending)
|
|
366
366
|
return;
|
|
367
367
|
|
|
@@ -371,19 +371,19 @@ using namespace facebook::react;
|
|
|
371
371
|
self->_isSheetUpdatePending = NO;
|
|
372
372
|
|
|
373
373
|
[self->_controller.sheetPresentationController animateChanges:^{
|
|
374
|
-
[self->_controller
|
|
374
|
+
[self->_controller setupSheetDetentsForSizeChange];
|
|
375
375
|
}];
|
|
376
376
|
});
|
|
377
377
|
}
|
|
378
378
|
|
|
379
379
|
- (void)containerViewContentDidChangeSize:(CGSize)newSize {
|
|
380
380
|
_controller.contentHeight = @(newSize.height);
|
|
381
|
-
[self
|
|
381
|
+
[self setupSheetDetentsForSizeChange];
|
|
382
382
|
}
|
|
383
383
|
|
|
384
384
|
- (void)containerViewHeaderDidChangeSize:(CGSize)newSize {
|
|
385
385
|
_controller.headerHeight = @(newSize.height);
|
|
386
|
-
[self
|
|
386
|
+
[self setupSheetDetentsForSizeChange];
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
#pragma mark - TrueSheetViewControllerDelegate
|
|
@@ -69,9 +69,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
69
69
|
- (void)applyActiveDetent;
|
|
70
70
|
- (void)setupActiveDetentWithIndex:(NSInteger)index;
|
|
71
71
|
- (void)resizeToDetentIndex:(NSInteger)index;
|
|
72
|
-
- (void)setupSheetDetents;
|
|
73
72
|
- (void)setupSheetProps;
|
|
74
|
-
- (void)
|
|
73
|
+
- (void)setupSheetDetents;
|
|
74
|
+
- (void)setupSheetDetentsForSizeChange;
|
|
75
|
+
- (void)setupDraggable;
|
|
75
76
|
|
|
76
77
|
@end
|
|
77
78
|
|
|
@@ -22,13 +22,15 @@
|
|
|
22
22
|
|
|
23
23
|
@implementation TrueSheetViewController {
|
|
24
24
|
CGFloat _lastPosition;
|
|
25
|
-
BOOL _isDragging;
|
|
26
25
|
NSInteger _pendingDetentIndex;
|
|
26
|
+
BOOL _pendingContentSizeChange;
|
|
27
27
|
|
|
28
|
-
//
|
|
28
|
+
// Position tracking
|
|
29
29
|
CADisplayLink *_transitioningTimer;
|
|
30
30
|
UIView *_transitionFakeView;
|
|
31
|
+
BOOL _isDragging;
|
|
31
32
|
BOOL _isTransitioning;
|
|
33
|
+
BOOL _isTrackingPositionFromLayout;
|
|
32
34
|
|
|
33
35
|
// Reference to parent TrueSheetViewController (if presented from another sheet)
|
|
34
36
|
__weak TrueSheetViewController *_parentSheetController;
|
|
@@ -61,10 +63,13 @@
|
|
|
61
63
|
_lastPosition = 0;
|
|
62
64
|
_isDragging = NO;
|
|
63
65
|
_isPresented = NO;
|
|
66
|
+
_pendingContentSizeChange = NO;
|
|
64
67
|
_activeDetentIndex = -1;
|
|
65
68
|
_pendingDetentIndex = -1;
|
|
69
|
+
|
|
66
70
|
_isTransitioning = NO;
|
|
67
71
|
_transitionFakeView = [UIView new];
|
|
72
|
+
_isTrackingPositionFromLayout = NO;
|
|
68
73
|
|
|
69
74
|
_blurInteraction = YES;
|
|
70
75
|
_resolvedDetentPositions = [NSMutableArray array];
|
|
@@ -159,15 +164,20 @@
|
|
|
159
164
|
}
|
|
160
165
|
}
|
|
161
166
|
|
|
162
|
-
|
|
163
|
-
|
|
167
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
168
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerWillPresentAtIndex:position:detent:)]) {
|
|
164
169
|
NSInteger index = self.currentDetentIndex;
|
|
165
170
|
CGFloat position = self.currentPosition;
|
|
166
171
|
CGFloat detent = [self detentValueForIndex:index];
|
|
167
172
|
|
|
168
173
|
[self.delegate viewControllerWillPresentAtIndex:index position:position detent:detent];
|
|
169
|
-
}
|
|
170
|
-
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Emit willFocus with willPresent
|
|
177
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerWillFocus)]) {
|
|
178
|
+
[self.delegate viewControllerWillFocus];
|
|
179
|
+
}
|
|
180
|
+
});
|
|
171
181
|
}
|
|
172
182
|
|
|
173
183
|
[self setupTransitionTracker];
|
|
@@ -184,13 +194,22 @@
|
|
|
184
194
|
}
|
|
185
195
|
}
|
|
186
196
|
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
198
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerDidPresentAtIndex:position:detent:)]) {
|
|
189
199
|
NSInteger index = [self currentDetentIndex];
|
|
190
200
|
CGFloat detent = [self detentValueForIndex:index];
|
|
191
201
|
[self.delegate viewControllerDidPresentAtIndex:index position:self.currentPosition detent:detent];
|
|
192
|
-
}
|
|
193
|
-
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Emit didFocus with didPresent
|
|
205
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerDidFocus)]) {
|
|
206
|
+
[self.delegate viewControllerDidFocus];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Emit correct position after presentation
|
|
210
|
+
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:@"did present"];
|
|
211
|
+
});
|
|
212
|
+
|
|
194
213
|
[self setupGestureRecognizer];
|
|
195
214
|
_isPresented = YES;
|
|
196
215
|
}
|
|
@@ -204,9 +223,16 @@
|
|
|
204
223
|
[super viewWillDisappear:animated];
|
|
205
224
|
|
|
206
225
|
if (self.isDismissing) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
226
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
227
|
+
// Emit willBlur with willDismiss
|
|
228
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerWillBlur)]) {
|
|
229
|
+
[self.delegate viewControllerWillBlur];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerWillDismiss)]) {
|
|
233
|
+
[self.delegate viewControllerWillDismiss];
|
|
234
|
+
}
|
|
235
|
+
});
|
|
210
236
|
|
|
211
237
|
// Notify the parent sheet (if any) that it is about to regain focus
|
|
212
238
|
if (_parentSheetController) {
|
|
@@ -235,12 +261,42 @@
|
|
|
235
261
|
_parentSheetController = nil;
|
|
236
262
|
}
|
|
237
263
|
|
|
264
|
+
// Emit didBlur with didDismiss
|
|
265
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerDidBlur)]) {
|
|
266
|
+
[self.delegate viewControllerDidBlur];
|
|
267
|
+
}
|
|
268
|
+
|
|
238
269
|
if ([self.delegate respondsToSelector:@selector(viewControllerDidDismiss)]) {
|
|
239
270
|
[self.delegate viewControllerDidDismiss];
|
|
240
271
|
}
|
|
241
272
|
}
|
|
242
273
|
}
|
|
243
274
|
|
|
275
|
+
- (void)viewWillLayoutSubviews {
|
|
276
|
+
[super viewWillLayoutSubviews];
|
|
277
|
+
|
|
278
|
+
if (!_isTransitioning) {
|
|
279
|
+
_isTrackingPositionFromLayout = YES;
|
|
280
|
+
|
|
281
|
+
UIViewController *presented = self.presentedViewController;
|
|
282
|
+
|
|
283
|
+
// Not realtime when another controller is presented that triggers our layout
|
|
284
|
+
BOOL hasPresentedController = presented != nil && !presented.isBeingPresented && !presented.isBeingDismissed;
|
|
285
|
+
|
|
286
|
+
BOOL realtime = !hasPresentedController;
|
|
287
|
+
|
|
288
|
+
if (_pendingContentSizeChange) {
|
|
289
|
+
_pendingContentSizeChange = NO;
|
|
290
|
+
realtime = NO;
|
|
291
|
+
|
|
292
|
+
// Store resolved position after content size changes
|
|
293
|
+
[self storeResolvedPositionForIndex:self.currentDetentIndex];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:realtime debug:@"layout"];
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
244
300
|
- (void)viewDidLayoutSubviews {
|
|
245
301
|
[super viewDidLayoutSubviews];
|
|
246
302
|
|
|
@@ -248,15 +304,6 @@
|
|
|
248
304
|
[self.delegate viewControllerDidChangeSize:self.view.frame.size];
|
|
249
305
|
}
|
|
250
306
|
|
|
251
|
-
UIViewController *presented = self.presentedViewController;
|
|
252
|
-
BOOL hasPresentedController = presented != nil && !presented.isBeingPresented && !presented.isBeingDismissed;
|
|
253
|
-
|
|
254
|
-
if (!_isDragging && !_isTransitioning) {
|
|
255
|
-
// Update stored position for current detent (handles content size changes)
|
|
256
|
-
[self storeResolvedPositionForIndex:self.currentDetentIndex];
|
|
257
|
-
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:hasPresentedController];
|
|
258
|
-
}
|
|
259
|
-
|
|
260
307
|
// Emit pending detent change after programmatic resize settles
|
|
261
308
|
if (_pendingDetentIndex >= 0) {
|
|
262
309
|
NSInteger pendingIndex = _pendingDetentIndex;
|
|
@@ -266,9 +313,14 @@
|
|
|
266
313
|
if ([self.delegate respondsToSelector:@selector(viewControllerDidChangeDetent:position:detent:)]) {
|
|
267
314
|
CGFloat detent = [self detentValueForIndex:pendingIndex];
|
|
268
315
|
[self.delegate viewControllerDidChangeDetent:pendingIndex position:self.currentPosition detent:detent];
|
|
316
|
+
|
|
317
|
+
// Emit position for the final position
|
|
318
|
+
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:@"pending detent change"];
|
|
269
319
|
}
|
|
270
320
|
});
|
|
271
321
|
}
|
|
322
|
+
|
|
323
|
+
_isTrackingPositionFromLayout = NO;
|
|
272
324
|
}
|
|
273
325
|
|
|
274
326
|
#pragma mark - Presentation Tracking (for RN Screens integration)
|
|
@@ -379,7 +431,7 @@
|
|
|
379
431
|
}
|
|
380
432
|
}
|
|
381
433
|
|
|
382
|
-
- (void)
|
|
434
|
+
- (void)setupDraggable {
|
|
383
435
|
UIView *presentedView = self.presentedView;
|
|
384
436
|
if (!presentedView)
|
|
385
437
|
return;
|
|
@@ -409,7 +461,9 @@
|
|
|
409
461
|
_isDragging = YES;
|
|
410
462
|
break;
|
|
411
463
|
case UIGestureRecognizerStateChanged:
|
|
412
|
-
|
|
464
|
+
if (!_isTrackingPositionFromLayout) {
|
|
465
|
+
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:YES debug:@"drag change"];
|
|
466
|
+
}
|
|
413
467
|
break;
|
|
414
468
|
case UIGestureRecognizerStateEnded:
|
|
415
469
|
case UIGestureRecognizerStateCancelled: {
|
|
@@ -417,7 +471,9 @@
|
|
|
417
471
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
418
472
|
// Store resolved position when drag ends
|
|
419
473
|
[self storeResolvedPositionForIndex:self.currentDetentIndex];
|
|
420
|
-
|
|
474
|
+
|
|
475
|
+
// Emit the correct position after dragging
|
|
476
|
+
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:@"drag end"];
|
|
421
477
|
});
|
|
422
478
|
}
|
|
423
479
|
|
|
@@ -438,7 +494,7 @@
|
|
|
438
494
|
_isTransitioning = YES;
|
|
439
495
|
|
|
440
496
|
CGRect dismissedFrame = CGRectMake(0, self.screenHeight, 0, 0);
|
|
441
|
-
CGRect presentedFrame = CGRectMake(0, self.
|
|
497
|
+
CGRect presentedFrame = CGRectMake(0, self.currentPosition, 0, 0);
|
|
442
498
|
|
|
443
499
|
// Set starting fake view position
|
|
444
500
|
_transitionFakeView.frame = self.isDismissing ? presentedFrame : dismissedFrame;
|
|
@@ -447,7 +503,7 @@
|
|
|
447
503
|
auto animation = ^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
|
448
504
|
[[context containerView] addSubview:self->_transitionFakeView];
|
|
449
505
|
|
|
450
|
-
// Set
|
|
506
|
+
// Set ending fake view position
|
|
451
507
|
self->_transitionFakeView.frame = self.isDismissing ? dismissedFrame : presentedFrame;
|
|
452
508
|
|
|
453
509
|
self->_transitioningTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleTransitionTracker)];
|
|
@@ -472,16 +528,17 @@
|
|
|
472
528
|
|
|
473
529
|
if (self.currentPosition >= self.screenHeight) {
|
|
474
530
|
// Dismissing position
|
|
475
|
-
|
|
531
|
+
CGFloat position = fmax(_lastPosition, layerPosition);
|
|
532
|
+
[self emitChangePositionDelegateWithPosition:position realtime:YES debug:@"transition out"];
|
|
476
533
|
} else {
|
|
477
534
|
// Presenting position
|
|
478
535
|
CGFloat position = fmax(self.currentPosition, layerPosition);
|
|
479
|
-
[self emitChangePositionDelegateWithPosition:position realtime:YES];
|
|
536
|
+
[self emitChangePositionDelegateWithPosition:position realtime:YES debug:@"transition in"];
|
|
480
537
|
}
|
|
481
538
|
}
|
|
482
539
|
}
|
|
483
540
|
|
|
484
|
-
- (void)emitChangePositionDelegateWithPosition:(CGFloat)position realtime:(BOOL)realtime {
|
|
541
|
+
- (void)emitChangePositionDelegateWithPosition:(CGFloat)position realtime:(BOOL)realtime debug:(NSString *)debug {
|
|
485
542
|
// Use epsilon comparison to avoid missing updates due to floating point precision
|
|
486
543
|
if (fabs(_lastPosition - position) > 0.01) {
|
|
487
544
|
_lastPosition = position;
|
|
@@ -491,6 +548,9 @@
|
|
|
491
548
|
if ([self.delegate respondsToSelector:@selector(viewControllerDidChangePosition:position:detent:realtime:)]) {
|
|
492
549
|
[self.delegate viewControllerDidChangePosition:index position:position detent:detent realtime:realtime];
|
|
493
550
|
}
|
|
551
|
+
|
|
552
|
+
// Debug position tracking
|
|
553
|
+
// NSLog(@"position from %@: %f, realtime: %i", debug, position, realtime);
|
|
494
554
|
}
|
|
495
555
|
}
|
|
496
556
|
|
|
@@ -635,6 +695,11 @@
|
|
|
635
695
|
|
|
636
696
|
#pragma mark - Sheet Configuration
|
|
637
697
|
|
|
698
|
+
- (void)setupSheetDetentsForSizeChange {
|
|
699
|
+
_pendingContentSizeChange = YES;
|
|
700
|
+
[self setupSheetDetents];
|
|
701
|
+
}
|
|
702
|
+
|
|
638
703
|
- (void)setupSheetDetents {
|
|
639
704
|
UISheetPresentationController *sheet = self.sheetPresentationController;
|
|
640
705
|
if (!sheet)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { createContext, useContext
|
|
3
|
+
import { createContext, useContext } from 'react';
|
|
4
4
|
import { useWindowDimensions } from 'react-native';
|
|
5
5
|
import { useSharedValue } from 'react-native-reanimated';
|
|
6
6
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
@@ -31,11 +31,11 @@ export const ReanimatedTrueSheetProvider = ({
|
|
|
31
31
|
const animatedPosition = useSharedValue(height);
|
|
32
32
|
const animatedIndex = useSharedValue(-1);
|
|
33
33
|
const animatedDetent = useSharedValue(0);
|
|
34
|
-
const value =
|
|
34
|
+
const value = {
|
|
35
35
|
animatedPosition,
|
|
36
36
|
animatedIndex,
|
|
37
37
|
animatedDetent
|
|
38
|
-
}
|
|
38
|
+
};
|
|
39
39
|
return /*#__PURE__*/_jsx(ReanimatedTrueSheetContext.Provider, {
|
|
40
40
|
value: value,
|
|
41
41
|
children: children
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["createContext","useContext","
|
|
1
|
+
{"version":3,"names":["createContext","useContext","useWindowDimensions","useSharedValue","jsx","_jsx","ReanimatedTrueSheetContext","ReanimatedTrueSheetProvider","children","height","animatedPosition","animatedIndex","animatedDetent","value","Provider","useReanimatedTrueSheet","context","Error"],"sourceRoot":"../../../src","sources":["reanimated/ReanimatedTrueSheetProvider.tsx"],"mappings":";;AAAA,SAASA,aAAa,EAAEC,UAAU,QAAwB,OAAO;AACjE,SAASC,mBAAmB,QAAQ,cAAc;AAClD,SAASC,cAAc,QAA0B,yBAAyB;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAmB3E,MAAMC,0BAA0B,gBAAGN,aAAa,CAAyC,IAAI,CAAC;AAM9F;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMO,2BAA2B,GAAGA,CAAC;EAAEC;AAA2C,CAAC,KAAK;EAC7F,MAAM;IAAEC;EAAO,CAAC,GAAGP,mBAAmB,CAAC,CAAC;EACxC,MAAMQ,gBAAgB,GAAGP,cAAc,CAACM,MAAM,CAAC;EAC/C,MAAME,aAAa,GAAGR,cAAc,CAAC,CAAC,CAAC,CAAC;EACxC,MAAMS,cAAc,GAAGT,cAAc,CAAC,CAAC,CAAC;EAExC,MAAMU,KAAsC,GAAG;IAC7CH,gBAAgB;IAChBC,aAAa;IACbC;EACF,CAAC;EAED,oBACEP,IAAA,CAACC,0BAA0B,CAACQ,QAAQ;IAACD,KAAK,EAAEA,KAAM;IAAAL,QAAA,EAC/CA;EAAQ,CAC0B,CAAC;AAE1C,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMO,sBAAsB,GAAGA,CAAA,KAAuC;EAC3E,MAAMC,OAAO,GAAGf,UAAU,CAACK,0BAA0B,CAAC;EAEtD,IAAI,CAACU,OAAO,EAAE;IACZ,MAAM,IAAIC,KAAK,CACb,4EAA4E,GAC1E,2EACJ,CAAC;EACH;EAEA,OAAOD,OAAO;AAChB,CAAC","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReanimatedTrueSheetProvider.d.ts","sourceRoot":"","sources":["../../../../src/reanimated/ReanimatedTrueSheetProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"ReanimatedTrueSheetProvider.d.ts","sourceRoot":"","sources":["../../../../src/reanimated/ReanimatedTrueSheetProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAElE,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3E,MAAM,WAAW,+BAA+B;IAC9C;;OAEG;IACH,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC;;;OAGG;IACH,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC;;;OAGG;IACH,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACrC;AAID,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,2BAA2B,GAAI,cAAc,gCAAgC,4CAiBzF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,sBAAsB,QAAO,+BAWzC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lodev09/react-native-true-sheet",
|
|
3
|
-
"version": "3.1.0-beta.
|
|
3
|
+
"version": "3.1.0-beta.7",
|
|
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",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"@eslint/js": "^9.35.0",
|
|
81
81
|
"@evilmartians/lefthook": "^2.0.4",
|
|
82
82
|
"@react-native/babel-preset": "^0.82.1",
|
|
83
|
-
"@react-native/eslint-config": "^0.
|
|
83
|
+
"@react-native/eslint-config": "^0.82.1",
|
|
84
84
|
"@release-it/conventional-changelog": "^10.0.1",
|
|
85
85
|
"@testing-library/react-native": "^13.3.3",
|
|
86
86
|
"@types/babel__core": "^7",
|
package/src/__mocks__/index.js
CHANGED
|
@@ -6,13 +6,13 @@ export class TrueSheet extends React.Component {
|
|
|
6
6
|
static instances = {};
|
|
7
7
|
|
|
8
8
|
// Static methods
|
|
9
|
-
static dismiss = jest.fn((name) => Promise.resolve());
|
|
10
|
-
static present = jest.fn((name, index = 0) => Promise.resolve());
|
|
9
|
+
static dismiss = jest.fn((name, animated = true) => Promise.resolve());
|
|
10
|
+
static present = jest.fn((name, index = 0, animated = true) => Promise.resolve());
|
|
11
11
|
static resize = jest.fn((name, index) => Promise.resolve());
|
|
12
12
|
|
|
13
13
|
// Instance methods
|
|
14
|
-
dismiss = jest.fn(() => Promise.resolve());
|
|
15
|
-
present = jest.fn((index = 0) => Promise.resolve());
|
|
14
|
+
dismiss = jest.fn((animated = true) => Promise.resolve());
|
|
15
|
+
present = jest.fn((index = 0, animated = true) => Promise.resolve());
|
|
16
16
|
resize = jest.fn((index) => Promise.resolve());
|
|
17
17
|
|
|
18
18
|
componentDidMount() {
|
|
@@ -30,9 +30,10 @@ export class TrueSheet extends React.Component {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
render() {
|
|
33
|
-
const { children, footer, style, ...rest } = this.props;
|
|
33
|
+
const { children, header, footer, style, ...rest } = this.props;
|
|
34
34
|
return (
|
|
35
35
|
<View style={style} {...rest}>
|
|
36
|
+
{header}
|
|
36
37
|
{children}
|
|
37
38
|
{footer}
|
|
38
39
|
</View>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useContext,
|
|
1
|
+
import { createContext, useContext, type ReactNode } from 'react';
|
|
2
2
|
import { useWindowDimensions } from 'react-native';
|
|
3
3
|
import { useSharedValue, type SharedValue } from 'react-native-reanimated';
|
|
4
4
|
|
|
@@ -48,14 +48,11 @@ export const ReanimatedTrueSheetProvider = ({ children }: ReanimatedTrueSheetPro
|
|
|
48
48
|
const animatedIndex = useSharedValue(-1);
|
|
49
49
|
const animatedDetent = useSharedValue(0);
|
|
50
50
|
|
|
51
|
-
const value =
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}),
|
|
57
|
-
[animatedPosition, animatedIndex, animatedDetent]
|
|
58
|
-
);
|
|
51
|
+
const value: ReanimatedTrueSheetContextValue = {
|
|
52
|
+
animatedPosition,
|
|
53
|
+
animatedIndex,
|
|
54
|
+
animatedDetent,
|
|
55
|
+
};
|
|
59
56
|
|
|
60
57
|
return (
|
|
61
58
|
<ReanimatedTrueSheetContext.Provider value={value}>
|