@lodev09/react-native-true-sheet 3.1.0-beta.6 → 3.1.0-beta.8
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/TrueSheetModule.kt +0 -4
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +29 -175
- package/ios/TrueSheetView.mm +5 -5
- package/ios/TrueSheetViewController.h +3 -2
- package/ios/TrueSheetViewController.mm +48 -87
- 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 +1 -1
- package/src/reanimated/ReanimatedTrueSheetProvider.tsx +6 -9
|
@@ -22,10 +22,6 @@ class TrueSheetModule(reactContext: ReactApplicationContext) :
|
|
|
22
22
|
|
|
23
23
|
override fun getName(): String = NAME
|
|
24
24
|
|
|
25
|
-
override fun initialize() {
|
|
26
|
-
super.initialize()
|
|
27
|
-
}
|
|
28
|
-
|
|
29
25
|
override fun invalidate() {
|
|
30
26
|
super.invalidate()
|
|
31
27
|
// Clear all registered views and observer on module invalidation
|
|
@@ -170,48 +170,26 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
170
170
|
val statusBarHeight: Int
|
|
171
171
|
get() = ScreenUtils.getStatusBarHeight(reactContext)
|
|
172
172
|
|
|
173
|
-
/**
|
|
174
|
-
* The bottom inset (navigation bar height) to add to sheet height.
|
|
175
|
-
* This matches iOS behavior where the system adds bottom safe area inset internally.
|
|
176
|
-
*/
|
|
173
|
+
/** Navigation bar height, added to sheet height to match iOS behavior. */
|
|
177
174
|
private val bottomInset: Int
|
|
178
175
|
get() = ScreenUtils.getNavigationBarHeight(reactContext)
|
|
179
176
|
|
|
180
|
-
/**
|
|
181
|
-
* Edge-to-edge is enabled by default on API 36+, or when explicitly configured.
|
|
182
|
-
*/
|
|
177
|
+
/** Edge-to-edge enabled by default on API 36+, or when explicitly configured. */
|
|
183
178
|
private val edgeToEdgeEnabled: Boolean
|
|
184
179
|
get() {
|
|
185
180
|
val defaultEnabled = android.os.Build.VERSION.SDK_INT >= 36
|
|
186
181
|
return BuildConfig.EDGE_TO_EDGE_ENABLED || dialog?.edgeToEdgeEnabled == true || defaultEnabled
|
|
187
182
|
}
|
|
188
183
|
|
|
189
|
-
/**
|
|
190
|
-
* The top inset to apply when edge-to-edge is enabled but not full-screen.
|
|
191
|
-
* This prevents the sheet from going under the status bar.
|
|
192
|
-
*/
|
|
184
|
+
/** Top inset when edge-to-edge is enabled but not full-screen. */
|
|
193
185
|
private val sheetTopInset: Int
|
|
194
186
|
get() = if (edgeToEdgeEnabled && !edgeToEdgeFullScreen) statusBarHeight else 0
|
|
195
187
|
|
|
196
|
-
// ====================================================================
|
|
197
|
-
// MARK: - Touch Dispatchers
|
|
198
|
-
// ====================================================================
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Touch dispatchers are required for RootView to properly forward touch events to React Native.
|
|
202
|
-
*/
|
|
203
188
|
internal var eventDispatcher: EventDispatcher? = null
|
|
204
189
|
private val jSTouchDispatcher = JSTouchDispatcher(this)
|
|
205
190
|
private var jSPointerDispatcher: JSPointerDispatcher? = null
|
|
206
191
|
|
|
207
|
-
|
|
208
|
-
// MARK: - Modal Observer
|
|
209
|
-
// ====================================================================
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Observes react-native-screens modal fragments to hide/show the sheet appropriately.
|
|
213
|
-
* This prevents the sheet from rendering on top of modals.
|
|
214
|
-
*/
|
|
192
|
+
/** Hides/shows the sheet when RN Screens modals are presented/dismissed. */
|
|
215
193
|
private var rnScreensObserver: RNScreensFragmentObserver? = null
|
|
216
194
|
|
|
217
195
|
// ====================================================================
|
|
@@ -253,7 +231,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
253
231
|
behavior.isHideable = dismissible
|
|
254
232
|
behavior.isDraggable = draggable
|
|
255
233
|
|
|
256
|
-
// Handle back press
|
|
257
234
|
onBackPressedDispatcher.addCallback(object : androidx.activity.OnBackPressedCallback(true) {
|
|
258
235
|
override fun handleOnBackPressed() {
|
|
259
236
|
this@TrueSheetViewController.delegate?.viewControllerDidBackPress()
|
|
@@ -293,25 +270,17 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
293
270
|
|
|
294
271
|
sheetContainer?.post {
|
|
295
272
|
val positionPx = bottomSheetView?.let { ScreenUtils.getScreenY(it) } ?: screenHeight
|
|
296
|
-
|
|
297
|
-
// Store resolved position for initial detent
|
|
298
273
|
storeResolvedPosition(currentDetentIndex)
|
|
299
274
|
emitChangePositionDelegate(positionPx, realtime = false)
|
|
300
|
-
|
|
301
275
|
positionFooter()
|
|
302
276
|
}
|
|
303
277
|
|
|
304
|
-
// Emit didPresent/didFocus after present animation completes
|
|
305
278
|
sheetContainer?.postDelayed({
|
|
306
279
|
val detentInfo = getDetentInfoForIndex(currentDetentIndex)
|
|
307
280
|
val detent = getDetentValueForIndex(detentInfo.index)
|
|
308
281
|
|
|
309
282
|
delegate?.viewControllerDidPresent(detentInfo.index, detentInfo.position, detent)
|
|
310
|
-
|
|
311
|
-
// Notify parent sheet that it has lost focus (after this sheet appeared)
|
|
312
283
|
parentSheetView?.viewControllerDidBlur()
|
|
313
|
-
|
|
314
|
-
// Emit didFocus with didPresent
|
|
315
284
|
delegate?.viewControllerDidFocus()
|
|
316
285
|
|
|
317
286
|
presentPromise?.invoke()
|
|
@@ -320,25 +289,19 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
320
289
|
}
|
|
321
290
|
|
|
322
291
|
dialog.setOnCancelListener {
|
|
323
|
-
// Skip if already handled by STATE_HIDDEN (programmatic dismiss)
|
|
324
292
|
if (isDismissing) return@setOnCancelListener
|
|
325
293
|
|
|
326
|
-
// User-initiated dismiss (back button, tap outside)
|
|
327
294
|
isDismissing = true
|
|
328
295
|
emitWillDismissEvents()
|
|
329
|
-
|
|
330
|
-
// Emit off-screen position since onSlide isn't triggered for user-initiated dismiss
|
|
331
296
|
emitChangePositionDelegate(screenHeight, realtime = false)
|
|
297
|
+
}
|
|
332
298
|
|
|
333
|
-
|
|
299
|
+
dialog.setOnDismissListener {
|
|
334
300
|
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
|
335
301
|
emitDidDismissEvents()
|
|
302
|
+
cleanupDialog()
|
|
336
303
|
}, DISMISS_ANIMATION_DURATION)
|
|
337
304
|
}
|
|
338
|
-
|
|
339
|
-
dialog.setOnDismissListener {
|
|
340
|
-
cleanupDialog()
|
|
341
|
-
}
|
|
342
305
|
}
|
|
343
306
|
|
|
344
307
|
private fun setupBottomSheetBehavior(dialog: BottomSheetDialog) {
|
|
@@ -362,9 +325,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
362
325
|
|
|
363
326
|
override fun onStateChanged(sheetView: View, newState: Int) {
|
|
364
327
|
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
|
365
|
-
|
|
328
|
+
if (isDismissing) return
|
|
366
329
|
isDismissing = true
|
|
367
|
-
|
|
330
|
+
emitWillDismissEvents()
|
|
368
331
|
dialog.dismiss()
|
|
369
332
|
return
|
|
370
333
|
}
|
|
@@ -377,15 +340,12 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
377
340
|
BottomSheetBehavior.STATE_EXPANDED,
|
|
378
341
|
BottomSheetBehavior.STATE_COLLAPSED,
|
|
379
342
|
BottomSheetBehavior.STATE_HALF_EXPANDED -> {
|
|
380
|
-
// Ignore state changes triggered by content size reconfiguration
|
|
381
343
|
if (isReconfiguring) return
|
|
382
344
|
|
|
383
345
|
getDetentInfoForState(newState)?.let { detentInfo ->
|
|
384
|
-
// Store resolved position when sheet settles
|
|
385
346
|
storeResolvedPosition(detentInfo.index)
|
|
386
347
|
|
|
387
348
|
if (isDragging) {
|
|
388
|
-
// Handle drag end
|
|
389
349
|
val detent = getDetentValueForIndex(detentInfo.index)
|
|
390
350
|
delegate?.viewControllerDidDragEnd(detentInfo.index, detentInfo.position, detent)
|
|
391
351
|
|
|
@@ -398,13 +358,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
398
358
|
}
|
|
399
359
|
|
|
400
360
|
isDragging = false
|
|
401
|
-
} else {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
currentDetentIndex = detentInfo.index
|
|
406
|
-
delegate?.viewControllerDidChangeDetent(detentInfo.index, detentInfo.position, detent)
|
|
407
|
-
}
|
|
361
|
+
} else if (detentInfo.index != currentDetentIndex) {
|
|
362
|
+
val detent = getDetentValueForIndex(detentInfo.index)
|
|
363
|
+
currentDetentIndex = detentInfo.index
|
|
364
|
+
delegate?.viewControllerDidChangeDetent(detentInfo.index, detentInfo.position, detent)
|
|
408
365
|
}
|
|
409
366
|
}
|
|
410
367
|
}
|
|
@@ -450,22 +407,12 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
450
407
|
rnScreensObserver = null
|
|
451
408
|
}
|
|
452
409
|
|
|
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
410
|
private fun emitWillDismissEvents() {
|
|
461
411
|
delegate?.viewControllerWillBlur()
|
|
462
412
|
delegate?.viewControllerWillDismiss()
|
|
463
413
|
parentSheetView?.viewControllerWillFocus()
|
|
464
414
|
}
|
|
465
415
|
|
|
466
|
-
/**
|
|
467
|
-
* Emits didBlur and didDismiss events, notifies parent sheet, and invokes dismiss promise.
|
|
468
|
-
*/
|
|
469
416
|
private fun emitDidDismissEvents() {
|
|
470
417
|
val hadParent = parentSheetView != null
|
|
471
418
|
parentSheetView?.viewControllerDidFocus()
|
|
@@ -482,53 +429,32 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
482
429
|
// MARK: - Dialog Visibility (for stacking)
|
|
483
430
|
// ====================================================================
|
|
484
431
|
|
|
485
|
-
/**
|
|
486
|
-
* Returns true if the sheet's top is at or above the status bar.
|
|
487
|
-
*/
|
|
488
432
|
val isExpanded: Boolean
|
|
489
433
|
get() {
|
|
490
434
|
val sheetTop = bottomSheetView?.top ?: return false
|
|
491
435
|
return sheetTop <= statusBarHeight
|
|
492
436
|
}
|
|
493
437
|
|
|
494
|
-
/**
|
|
495
|
-
* Returns the current top position of the sheet (Y coordinate from screen top).
|
|
496
|
-
* Used for comparing sheet positions during stacking.
|
|
497
|
-
*/
|
|
498
438
|
val currentSheetTop: Int
|
|
499
439
|
get() = bottomSheetView?.let { ScreenUtils.getScreenY(it) } ?: screenHeight
|
|
500
440
|
|
|
501
|
-
/**
|
|
502
|
-
* Returns the expected top position of the sheet when presented at the given detent index.
|
|
503
|
-
* Used for comparing sheet positions before presentation.
|
|
504
|
-
*/
|
|
505
441
|
fun getExpectedSheetTop(detentIndex: Int): Int {
|
|
506
442
|
if (detentIndex < 0 || detentIndex >= detents.size) return screenHeight
|
|
507
443
|
val detentHeight = getDetentHeight(detents[detentIndex])
|
|
508
444
|
return screenHeight - detentHeight
|
|
509
445
|
}
|
|
510
446
|
|
|
511
|
-
/**
|
|
512
|
-
* Hides the dialog without dismissing it.
|
|
513
|
-
* Used when another TrueSheet presents on top or when RN screen is presented.
|
|
514
|
-
*/
|
|
447
|
+
/** Hides without dismissing. Used for sheet stacking and RN Screens modals. */
|
|
515
448
|
fun hideDialog() {
|
|
516
449
|
isDialogVisible = false
|
|
517
450
|
dialog?.window?.decorView?.visibility = INVISIBLE
|
|
518
|
-
|
|
519
|
-
// Emit off-screen position (detent = 0 since sheet is fully hidden)
|
|
520
451
|
emitChangePositionDelegate(screenHeight, realtime = false)
|
|
521
452
|
}
|
|
522
453
|
|
|
523
|
-
/**
|
|
524
|
-
* Shows a previously hidden dialog.
|
|
525
|
-
* Used when the sheet on top dismisses.
|
|
526
|
-
*/
|
|
454
|
+
/** Shows a previously hidden dialog. */
|
|
527
455
|
fun showDialog() {
|
|
528
456
|
isDialogVisible = true
|
|
529
457
|
dialog?.window?.decorView?.visibility = VISIBLE
|
|
530
|
-
|
|
531
|
-
// Emit current position
|
|
532
458
|
val positionPx = bottomSheetView?.let { ScreenUtils.getScreenY(it) } ?: screenHeight
|
|
533
459
|
emitChangePositionDelegate(positionPx, realtime = false)
|
|
534
460
|
}
|
|
@@ -546,8 +472,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
546
472
|
setupDimmedBackground(detentIndex)
|
|
547
473
|
|
|
548
474
|
if (isPresented) {
|
|
549
|
-
// Detent change will be emitted when sheet settles in onStateChanged
|
|
550
|
-
// Don't update currentDetentIndex here - it will be updated when sheet settles
|
|
551
475
|
setStateForDetentIndex(detentIndex)
|
|
552
476
|
} else {
|
|
553
477
|
currentDetentIndex = detentIndex
|
|
@@ -558,12 +482,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
558
482
|
val detentInfo = getDetentInfoForIndex(detentIndex)
|
|
559
483
|
val detent = getDetentValueForIndex(detentInfo.index)
|
|
560
484
|
|
|
561
|
-
// Notify parent sheet that it is about to lose focus (before this sheet appears)
|
|
562
485
|
parentSheetView?.viewControllerWillBlur()
|
|
563
|
-
|
|
564
486
|
delegate?.viewControllerWillPresent(detentInfo.index, detentInfo.position, detent)
|
|
565
|
-
|
|
566
|
-
// Emit willFocus with willPresent
|
|
567
487
|
delegate?.viewControllerWillFocus()
|
|
568
488
|
|
|
569
489
|
if (!animated) {
|
|
@@ -575,10 +495,12 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
575
495
|
}
|
|
576
496
|
|
|
577
497
|
fun dismiss(animated: Boolean = true) {
|
|
498
|
+
if (isDismissing) return
|
|
499
|
+
|
|
500
|
+
isDismissing = true
|
|
578
501
|
emitWillDismissEvents()
|
|
579
502
|
|
|
580
503
|
this.post {
|
|
581
|
-
// Emit off-screen position (detent = 0 since sheet is fully hidden)
|
|
582
504
|
emitChangePositionDelegate(screenHeight, realtime = false)
|
|
583
505
|
}
|
|
584
506
|
|
|
@@ -586,9 +508,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
586
508
|
dialog?.window?.setWindowAnimations(0)
|
|
587
509
|
}
|
|
588
510
|
|
|
589
|
-
|
|
590
|
-
behavior?.isHideable = true
|
|
591
|
-
behavior?.state = BottomSheetBehavior.STATE_HIDDEN
|
|
511
|
+
dialog?.dismiss()
|
|
592
512
|
}
|
|
593
513
|
|
|
594
514
|
// ====================================================================
|
|
@@ -598,13 +518,11 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
598
518
|
fun setupSheetDetents() {
|
|
599
519
|
val behavior = this.behavior ?: return
|
|
600
520
|
|
|
601
|
-
// Reset resolved positions if detents count changed
|
|
602
521
|
if (resolvedDetentPositions.size != detents.size) {
|
|
603
522
|
resolvedDetentPositions.clear()
|
|
604
523
|
repeat(detents.size) { resolvedDetentPositions.add(0) }
|
|
605
524
|
}
|
|
606
525
|
|
|
607
|
-
// Always update auto detent positions based on current content height
|
|
608
526
|
for (i in detents.indices) {
|
|
609
527
|
if (detents[i] == -1.0) {
|
|
610
528
|
val detentHeight = getDetentHeight(detents[i])
|
|
@@ -612,7 +530,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
612
530
|
}
|
|
613
531
|
}
|
|
614
532
|
|
|
615
|
-
// Flag to prevent state change callbacks from updating detent index during reconfiguration
|
|
616
533
|
isReconfiguring = true
|
|
617
534
|
|
|
618
535
|
behavior.apply {
|
|
@@ -643,7 +560,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
643
560
|
}
|
|
644
561
|
}
|
|
645
562
|
|
|
646
|
-
// Keep container size in sync with sheet size
|
|
647
563
|
if (oldExpandOffset != expandedOffset || expandedOffset == 0) {
|
|
648
564
|
val offset = if (expandedOffset == 0) statusBarHeight else 0
|
|
649
565
|
val newHeight = screenHeight - expandedOffset - offset
|
|
@@ -651,7 +567,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
651
567
|
}
|
|
652
568
|
|
|
653
569
|
if (isPresented) {
|
|
654
|
-
// Re-apply current state to update position after config changes
|
|
655
570
|
setStateForDetentIndex(currentDetentIndex)
|
|
656
571
|
}
|
|
657
572
|
|
|
@@ -688,10 +603,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
688
603
|
bottomSheet.clipToOutline = true
|
|
689
604
|
}
|
|
690
605
|
|
|
691
|
-
/**
|
|
692
|
-
* Configures the dimmed background based on the current detent index.
|
|
693
|
-
* When not dimmed, touch events pass through to the activity behind the sheet.
|
|
694
|
-
*/
|
|
606
|
+
/** Configures dim and touch-through behavior based on detent index. */
|
|
695
607
|
fun setupDimmedBackground(detentIndex: Int) {
|
|
696
608
|
val dialog = this.dialog ?: return
|
|
697
609
|
dialog.window?.apply {
|
|
@@ -702,7 +614,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
702
614
|
setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND, WindowManager.LayoutParams.FLAG_DIM_BEHIND)
|
|
703
615
|
dialog.setCanceledOnTouchOutside(dismissible)
|
|
704
616
|
} else {
|
|
705
|
-
// Forward touch events to the activity when not dimmed
|
|
706
617
|
view.setOnTouchListener { v, event ->
|
|
707
618
|
event.setLocation(event.rawX - v.x, event.rawY - v.y)
|
|
708
619
|
reactContext.currentActivity?.dispatchTouchEvent(event)
|
|
@@ -718,11 +629,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
718
629
|
dialog?.window?.setWindowAnimations(windowAnimation)
|
|
719
630
|
}
|
|
720
631
|
|
|
721
|
-
/**
|
|
722
|
-
* Positions the footer view at the bottom of the sheet.
|
|
723
|
-
* The footer stays fixed at the bottom edge of the visible sheet area,
|
|
724
|
-
* adjusting during drag gestures via slideOffset.
|
|
725
|
-
*/
|
|
632
|
+
/** Positions footer at bottom of sheet, adjusting during drag via slideOffset. */
|
|
726
633
|
fun positionFooter(slideOffset: Float? = null) {
|
|
727
634
|
val footerView = containerView?.footerView ?: return
|
|
728
635
|
val bottomSheet = bottomSheetView ?: return
|
|
@@ -732,13 +639,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
732
639
|
|
|
733
640
|
var footerY = (screenHeight - bottomSheetY - footerHeight).toFloat()
|
|
734
641
|
|
|
735
|
-
// Animate footer down with sheet when below peek height
|
|
736
642
|
if (slideOffset != null && slideOffset < 0) {
|
|
737
643
|
footerY -= (footerHeight * slideOffset)
|
|
738
644
|
}
|
|
739
645
|
|
|
740
|
-
// Clamp footer position to prevent it from going off screen when positioning at the top
|
|
741
|
-
// This happens when fullScreen is enabled in edge-to-edge mode
|
|
742
646
|
val maxAllowedY = (screenHeight - statusBarHeight - footerHeight).toFloat()
|
|
743
647
|
footerView.y = minOf(footerY, maxAllowedY)
|
|
744
648
|
}
|
|
@@ -766,14 +670,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
766
670
|
}
|
|
767
671
|
|
|
768
672
|
// ====================================================================
|
|
769
|
-
// MARK: - Position
|
|
673
|
+
// MARK: - Position & Drag Handling
|
|
770
674
|
// ====================================================================
|
|
771
675
|
|
|
772
|
-
/**
|
|
773
|
-
* Emits position change to the delegate if the position has changed.
|
|
774
|
-
* @param positionPx The current position in pixels (screen Y coordinate)
|
|
775
|
-
* @param realtime Whether the position is a real-time value (during drag or animation tracking)
|
|
776
|
-
*/
|
|
777
676
|
private fun emitChangePositionDelegate(positionPx: Int, realtime: Boolean) {
|
|
778
677
|
if (positionPx == lastEmittedPositionPx) return
|
|
779
678
|
|
|
@@ -784,11 +683,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
784
683
|
delegate?.viewControllerDidChangePosition(interpolatedIndex, position, detent, realtime)
|
|
785
684
|
}
|
|
786
685
|
|
|
787
|
-
/**
|
|
788
|
-
* Stores the current Y position as the resolved position for the given detent index.
|
|
789
|
-
* This is called when the sheet settles at a detent to capture the actual position
|
|
790
|
-
* which may differ from the calculated position due to system adjustments.
|
|
791
|
-
*/
|
|
792
686
|
private fun storeResolvedPosition(index: Int) {
|
|
793
687
|
if (index < 0 || index >= resolvedDetentPositions.size) return
|
|
794
688
|
val positionPx = bottomSheetView?.let { ScreenUtils.getScreenY(it) } ?: return
|
|
@@ -797,24 +691,16 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
797
691
|
}
|
|
798
692
|
}
|
|
799
693
|
|
|
800
|
-
/**
|
|
801
|
-
* Stores the resolved position for the current detent.
|
|
802
|
-
* Called from TrueSheetView when content size changes.
|
|
803
|
-
*/
|
|
804
694
|
fun storeCurrentResolvedPosition() {
|
|
805
695
|
storeResolvedPosition(currentDetentIndex)
|
|
806
696
|
}
|
|
807
697
|
|
|
808
|
-
/**
|
|
809
|
-
* Returns the estimated Y position for a detent index, using stored positions when available.
|
|
810
|
-
*/
|
|
811
698
|
private fun getEstimatedPositionForIndex(index: Int): Int {
|
|
812
699
|
if (index < 0 || index >= resolvedDetentPositions.size) return screenHeight
|
|
813
700
|
|
|
814
701
|
val storedPos = resolvedDetentPositions[index]
|
|
815
702
|
if (storedPos > 0) return storedPos
|
|
816
703
|
|
|
817
|
-
// Estimate based on getDetentHeight which accounts for bottomInset and maxAllowedHeight
|
|
818
704
|
if (index < detents.size) {
|
|
819
705
|
val detentHeight = getDetentHeight(detents[index])
|
|
820
706
|
return screenHeight - detentHeight
|
|
@@ -823,11 +709,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
823
709
|
return screenHeight
|
|
824
710
|
}
|
|
825
711
|
|
|
826
|
-
/**
|
|
827
|
-
* Finds the segment index and interpolation progress for a given position.
|
|
828
|
-
* Returns a Triple of (fromIndex, toIndex, progress) where progress is 0-1 within that segment.
|
|
829
|
-
* Returns null if position count is less than 2.
|
|
830
|
-
*/
|
|
712
|
+
/** Returns (fromIndex, toIndex, progress) for interpolation, or null if < 2 detents. */
|
|
831
713
|
private fun findSegmentForPosition(positionPx: Int): Triple<Int, Int, Float>? {
|
|
832
714
|
val count = resolvedDetentPositions.size
|
|
833
715
|
if (count < 2) return null
|
|
@@ -835,19 +717,16 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
835
717
|
val firstPos = getEstimatedPositionForIndex(0)
|
|
836
718
|
val lastPos = getEstimatedPositionForIndex(count - 1)
|
|
837
719
|
|
|
838
|
-
// Below first detent
|
|
839
720
|
if (positionPx > firstPos) {
|
|
840
721
|
val range = screenHeight - firstPos
|
|
841
722
|
val progress = if (range > 0) (positionPx - firstPos).toFloat() / range else 0f
|
|
842
|
-
return Triple(-1, 0, progress)
|
|
723
|
+
return Triple(-1, 0, progress)
|
|
843
724
|
}
|
|
844
725
|
|
|
845
|
-
// Above last detent
|
|
846
726
|
if (positionPx < lastPos) {
|
|
847
727
|
return Triple(count - 1, count - 1, 0f)
|
|
848
728
|
}
|
|
849
729
|
|
|
850
|
-
// Find segment (positions decrease as index increases)
|
|
851
730
|
for (i in 0 until count - 1) {
|
|
852
731
|
val pos = getEstimatedPositionForIndex(i)
|
|
853
732
|
val nextPos = getEstimatedPositionForIndex(i + 1)
|
|
@@ -862,10 +741,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
862
741
|
return Triple(count - 1, count - 1, 0f)
|
|
863
742
|
}
|
|
864
743
|
|
|
865
|
-
/**
|
|
866
|
-
* Calculates the interpolated index based on position.
|
|
867
|
-
* Returns a continuous value (e.g., 0.5 means halfway between detent 0 and 1).
|
|
868
|
-
*/
|
|
744
|
+
/** Returns continuous index (e.g., 0.5 = halfway between detent 0 and 1). */
|
|
869
745
|
private fun getInterpolatedIndexForPosition(positionPx: Int): Float {
|
|
870
746
|
val count = resolvedDetentPositions.size
|
|
871
747
|
if (count == 0) return -1f
|
|
@@ -874,16 +750,11 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
874
750
|
val segment = findSegmentForPosition(positionPx) ?: return 0f
|
|
875
751
|
val (fromIndex, _, progress) = segment
|
|
876
752
|
|
|
877
|
-
// Below first detent
|
|
878
753
|
if (fromIndex == -1) return -progress
|
|
879
|
-
|
|
880
754
|
return fromIndex + progress
|
|
881
755
|
}
|
|
882
756
|
|
|
883
|
-
/**
|
|
884
|
-
* Calculates the interpolated detent value based on position.
|
|
885
|
-
* Returns the actual screen fraction, clamped to valid detent range.
|
|
886
|
-
*/
|
|
757
|
+
/** Returns interpolated screen fraction for position. */
|
|
887
758
|
private fun getInterpolatedDetentForPosition(positionPx: Int): Float {
|
|
888
759
|
val count = resolvedDetentPositions.size
|
|
889
760
|
if (count == 0) return 0f
|
|
@@ -891,7 +762,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
891
762
|
val segment = findSegmentForPosition(positionPx) ?: return getDetentValueForIndex(0)
|
|
892
763
|
val (fromIndex, toIndex, progress) = segment
|
|
893
764
|
|
|
894
|
-
// Below first detent
|
|
895
765
|
if (fromIndex == -1) {
|
|
896
766
|
val firstDetent = getDetentValueForIndex(0)
|
|
897
767
|
return maxOf(0f, firstDetent * (1 - progress))
|
|
@@ -902,11 +772,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
902
772
|
return fromDetent + progress * (toDetent - fromDetent)
|
|
903
773
|
}
|
|
904
774
|
|
|
905
|
-
/**
|
|
906
|
-
* Gets the detent value (fraction) for a given index.
|
|
907
|
-
* Returns the raw screen fraction without bottomInset for interpolation calculations.
|
|
908
|
-
* Note: bottomInset is only added in getDetentHeight() for actual sheet sizing.
|
|
909
|
-
*/
|
|
775
|
+
/** Returns raw screen fraction for index (without bottomInset). */
|
|
910
776
|
private fun getDetentValueForIndex(index: Int): Float {
|
|
911
777
|
if (index < 0 || index >= detents.size) return 0f
|
|
912
778
|
val value = detents[index]
|
|
@@ -917,10 +783,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
917
783
|
}
|
|
918
784
|
}
|
|
919
785
|
|
|
920
|
-
// ====================================================================
|
|
921
|
-
// MARK: - Drag Handling
|
|
922
|
-
// ====================================================================
|
|
923
|
-
|
|
924
786
|
private fun getCurrentDetentInfo(sheetView: View): DetentInfo {
|
|
925
787
|
val screenY = ScreenUtils.getScreenY(sheetView)
|
|
926
788
|
return DetentInfo(currentDetentIndex, screenY.pxToDp())
|
|
@@ -948,14 +810,11 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
948
810
|
|
|
949
811
|
private fun getDetentHeight(detent: Double): Int {
|
|
950
812
|
val height: Int = if (detent == -1.0) {
|
|
951
|
-
// For auto detent, add bottomInset to match iOS behavior where the system
|
|
952
|
-
// adds bottom safe area inset internally to the sheet height
|
|
953
813
|
contentHeight + headerHeight + bottomInset
|
|
954
814
|
} else {
|
|
955
815
|
if (detent <= 0.0 || detent > 1.0) {
|
|
956
816
|
throw IllegalArgumentException("TrueSheet: detent fraction ($detent) must be between 0 and 1")
|
|
957
817
|
}
|
|
958
|
-
// For fractional detents, add bottomInset to match iOS behavior
|
|
959
818
|
(detent * screenHeight).toInt() + bottomInset
|
|
960
819
|
}
|
|
961
820
|
|
|
@@ -1035,8 +894,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1035
894
|
super.onSizeChanged(w, h, oldw, oldh)
|
|
1036
895
|
if (w == oldw && h == oldh) return
|
|
1037
896
|
|
|
1038
|
-
// Skip
|
|
1039
|
-
// Size keeps changing on this case
|
|
897
|
+
// Skip continuous size changes when fullScreen + edge-to-edge
|
|
1040
898
|
if (h + statusBarHeight > screenHeight && isExpanded && oldw == w) {
|
|
1041
899
|
return
|
|
1042
900
|
}
|
|
@@ -1063,11 +921,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1063
921
|
// MARK: - Touch Event Handling
|
|
1064
922
|
// ====================================================================
|
|
1065
923
|
|
|
1066
|
-
/**
|
|
1067
|
-
* Custom touch dispatch to handle footer touch events.
|
|
1068
|
-
* The footer is positioned outside the normal view hierarchy, so we need to
|
|
1069
|
-
* manually check if touches fall within its bounds and forward them.
|
|
1070
|
-
*/
|
|
924
|
+
/** Forwards touch events to footer which is positioned outside normal hierarchy. */
|
|
1071
925
|
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
|
1072
926
|
val footer = containerView?.footerView
|
|
1073
927
|
if (footer != null && footer.isVisible) {
|
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,27 +22,21 @@
|
|
|
22
22
|
|
|
23
23
|
@implementation TrueSheetViewController {
|
|
24
24
|
CGFloat _lastPosition;
|
|
25
|
-
BOOL _isDragging;
|
|
26
25
|
NSInteger _pendingDetentIndex;
|
|
26
|
+
BOOL _pendingContentSizeChange;
|
|
27
27
|
|
|
28
|
-
// Transitioning tracker
|
|
29
28
|
CADisplayLink *_transitioningTimer;
|
|
30
29
|
UIView *_transitionFakeView;
|
|
30
|
+
BOOL _isDragging;
|
|
31
31
|
BOOL _isTransitioning;
|
|
32
|
+
BOOL _isTrackingPositionFromLayout;
|
|
32
33
|
|
|
33
|
-
// Reference to parent TrueSheetViewController (if presented from another sheet)
|
|
34
34
|
__weak TrueSheetViewController *_parentSheetController;
|
|
35
35
|
|
|
36
|
-
// Blur effect view
|
|
37
36
|
TrueSheetBlurView *_blurView;
|
|
38
|
-
|
|
39
|
-
// Custom grabber view
|
|
40
37
|
TrueSheetGrabberView *_grabberView;
|
|
41
38
|
|
|
42
|
-
// Resolved detent positions (Y coordinate when sheet rests at each detent)
|
|
43
39
|
NSMutableArray<NSNumber *> *_resolvedDetentPositions;
|
|
44
|
-
|
|
45
|
-
// Tracks whether this sheet has a presented controller (e.g., RN Screens modal)
|
|
46
40
|
BOOL _hasPresentedController;
|
|
47
41
|
}
|
|
48
42
|
|
|
@@ -61,10 +55,13 @@
|
|
|
61
55
|
_lastPosition = 0;
|
|
62
56
|
_isDragging = NO;
|
|
63
57
|
_isPresented = NO;
|
|
58
|
+
_pendingContentSizeChange = NO;
|
|
64
59
|
_activeDetentIndex = -1;
|
|
65
60
|
_pendingDetentIndex = -1;
|
|
61
|
+
|
|
66
62
|
_isTransitioning = NO;
|
|
67
63
|
_transitionFakeView = [UIView new];
|
|
64
|
+
_isTrackingPositionFromLayout = NO;
|
|
68
65
|
|
|
69
66
|
_blurInteraction = YES;
|
|
70
67
|
_resolvedDetentPositions = [NSMutableArray array];
|
|
@@ -133,7 +130,6 @@
|
|
|
133
130
|
[super viewDidLoad];
|
|
134
131
|
self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
|
|
135
132
|
|
|
136
|
-
// Create custom grabber view (hidden by default, shown when grabberOptions is set)
|
|
137
133
|
_grabberView = [[TrueSheetGrabberView alloc] init];
|
|
138
134
|
_grabberView.hidden = YES;
|
|
139
135
|
[_grabberView addToView:self.view];
|
|
@@ -142,18 +138,14 @@
|
|
|
142
138
|
- (void)viewWillAppear:(BOOL)animated {
|
|
143
139
|
[super viewWillAppear:animated];
|
|
144
140
|
|
|
145
|
-
// Only trigger on initial presentation, not repositioning
|
|
146
141
|
if (!_isPresented) {
|
|
147
|
-
// Initially store resolved position during presentation
|
|
148
142
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
149
143
|
[self storeResolvedPositionForIndex:self.currentDetentIndex];
|
|
150
144
|
});
|
|
151
145
|
|
|
152
|
-
// Capture parent sheet reference if presented from another TrueSheet
|
|
153
146
|
UIViewController *presenter = self.presentingViewController;
|
|
154
147
|
if ([presenter isKindOfClass:[TrueSheetViewController class]]) {
|
|
155
148
|
_parentSheetController = (TrueSheetViewController *)presenter;
|
|
156
|
-
// Notify parent that it is about to lose focus
|
|
157
149
|
if ([_parentSheetController.delegate respondsToSelector:@selector(viewControllerWillBlur)]) {
|
|
158
150
|
[_parentSheetController.delegate viewControllerWillBlur];
|
|
159
151
|
}
|
|
@@ -168,7 +160,6 @@
|
|
|
168
160
|
[self.delegate viewControllerWillPresentAtIndex:index position:position detent:detent];
|
|
169
161
|
}
|
|
170
162
|
|
|
171
|
-
// Emit willFocus with willPresent
|
|
172
163
|
if ([self.delegate respondsToSelector:@selector(viewControllerWillFocus)]) {
|
|
173
164
|
[self.delegate viewControllerWillFocus];
|
|
174
165
|
}
|
|
@@ -182,7 +173,6 @@
|
|
|
182
173
|
[super viewDidAppear:animated];
|
|
183
174
|
|
|
184
175
|
if (!_isPresented) {
|
|
185
|
-
// Notify parent that it has lost focus (after the child sheet appeared)
|
|
186
176
|
if (_parentSheetController) {
|
|
187
177
|
if ([_parentSheetController.delegate respondsToSelector:@selector(viewControllerDidBlur)]) {
|
|
188
178
|
[_parentSheetController.delegate viewControllerDidBlur];
|
|
@@ -196,10 +186,11 @@
|
|
|
196
186
|
[self.delegate viewControllerDidPresentAtIndex:index position:self.currentPosition detent:detent];
|
|
197
187
|
}
|
|
198
188
|
|
|
199
|
-
// Emit didFocus with didPresent
|
|
200
189
|
if ([self.delegate respondsToSelector:@selector(viewControllerDidFocus)]) {
|
|
201
190
|
[self.delegate viewControllerDidFocus];
|
|
202
191
|
}
|
|
192
|
+
|
|
193
|
+
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:@"did present"];
|
|
203
194
|
});
|
|
204
195
|
|
|
205
196
|
[self setupGestureRecognizer];
|
|
@@ -216,7 +207,6 @@
|
|
|
216
207
|
|
|
217
208
|
if (self.isDismissing) {
|
|
218
209
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
219
|
-
// Emit willBlur with willDismiss
|
|
220
210
|
if ([self.delegate respondsToSelector:@selector(viewControllerWillBlur)]) {
|
|
221
211
|
[self.delegate viewControllerWillBlur];
|
|
222
212
|
}
|
|
@@ -226,7 +216,6 @@
|
|
|
226
216
|
}
|
|
227
217
|
});
|
|
228
218
|
|
|
229
|
-
// Notify the parent sheet (if any) that it is about to regain focus
|
|
230
219
|
if (_parentSheetController) {
|
|
231
220
|
if ([_parentSheetController.delegate respondsToSelector:@selector(viewControllerWillFocus)]) {
|
|
232
221
|
[_parentSheetController.delegate viewControllerWillFocus];
|
|
@@ -240,12 +229,10 @@
|
|
|
240
229
|
- (void)viewDidDisappear:(BOOL)animated {
|
|
241
230
|
[super viewDidDisappear:animated];
|
|
242
231
|
|
|
243
|
-
// Only dispatch didDismiss when actually dismissing (not when another modal is presented on top)
|
|
244
232
|
if (self.isDismissing) {
|
|
245
233
|
_isPresented = NO;
|
|
246
234
|
_activeDetentIndex = -1;
|
|
247
235
|
|
|
248
|
-
// Notify the parent sheet (if any) that it regained focus
|
|
249
236
|
if (_parentSheetController) {
|
|
250
237
|
if ([_parentSheetController.delegate respondsToSelector:@selector(viewControllerDidFocus)]) {
|
|
251
238
|
[_parentSheetController.delegate viewControllerDidFocus];
|
|
@@ -253,7 +240,6 @@
|
|
|
253
240
|
_parentSheetController = nil;
|
|
254
241
|
}
|
|
255
242
|
|
|
256
|
-
// Emit didBlur with didDismiss
|
|
257
243
|
if ([self.delegate respondsToSelector:@selector(viewControllerDidBlur)]) {
|
|
258
244
|
[self.delegate viewControllerDidBlur];
|
|
259
245
|
}
|
|
@@ -264,6 +250,26 @@
|
|
|
264
250
|
}
|
|
265
251
|
}
|
|
266
252
|
|
|
253
|
+
- (void)viewWillLayoutSubviews {
|
|
254
|
+
[super viewWillLayoutSubviews];
|
|
255
|
+
|
|
256
|
+
if (!_isTransitioning) {
|
|
257
|
+
_isTrackingPositionFromLayout = YES;
|
|
258
|
+
|
|
259
|
+
UIViewController *presented = self.presentedViewController;
|
|
260
|
+
BOOL hasPresentedController = presented != nil && !presented.isBeingPresented && !presented.isBeingDismissed;
|
|
261
|
+
BOOL realtime = !hasPresentedController;
|
|
262
|
+
|
|
263
|
+
if (_pendingContentSizeChange) {
|
|
264
|
+
_pendingContentSizeChange = NO;
|
|
265
|
+
realtime = NO;
|
|
266
|
+
[self storeResolvedPositionForIndex:self.currentDetentIndex];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:realtime debug:@"layout"];
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
267
273
|
- (void)viewDidLayoutSubviews {
|
|
268
274
|
[super viewDidLayoutSubviews];
|
|
269
275
|
|
|
@@ -271,16 +277,6 @@
|
|
|
271
277
|
[self.delegate viewControllerDidChangeSize:self.view.frame.size];
|
|
272
278
|
}
|
|
273
279
|
|
|
274
|
-
UIViewController *presented = self.presentedViewController;
|
|
275
|
-
BOOL hasPresentedController = presented != nil && !presented.isBeingPresented && !presented.isBeingDismissed;
|
|
276
|
-
|
|
277
|
-
if (!_isDragging && !_isTransitioning) {
|
|
278
|
-
// Update stored position for current detent (handles content size changes)
|
|
279
|
-
[self storeResolvedPositionForIndex:self.currentDetentIndex];
|
|
280
|
-
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:hasPresentedController];
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Emit pending detent change after programmatic resize settles
|
|
284
280
|
if (_pendingDetentIndex >= 0) {
|
|
285
281
|
NSInteger pendingIndex = _pendingDetentIndex;
|
|
286
282
|
_pendingDetentIndex = -1;
|
|
@@ -289,23 +285,23 @@
|
|
|
289
285
|
if ([self.delegate respondsToSelector:@selector(viewControllerDidChangeDetent:position:detent:)]) {
|
|
290
286
|
CGFloat detent = [self detentValueForIndex:pendingIndex];
|
|
291
287
|
[self.delegate viewControllerDidChangeDetent:pendingIndex position:self.currentPosition detent:detent];
|
|
288
|
+
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:@"pending detent change"];
|
|
292
289
|
}
|
|
293
290
|
});
|
|
294
291
|
}
|
|
292
|
+
|
|
293
|
+
_isTrackingPositionFromLayout = NO;
|
|
295
294
|
}
|
|
296
295
|
|
|
297
|
-
#pragma mark - Presentation Tracking (
|
|
296
|
+
#pragma mark - Presentation Tracking (RN Screens)
|
|
298
297
|
|
|
299
298
|
- (void)presentViewController:(UIViewController *)viewControllerToPresent
|
|
300
299
|
animated:(BOOL)flag
|
|
301
300
|
completion:(void (^)(void))completion {
|
|
302
|
-
// Check if this is a non-TrueSheet controller (e.g., RN Screens modal)
|
|
303
301
|
BOOL isExternalController = ![viewControllerToPresent isKindOfClass:[TrueSheetViewController class]];
|
|
304
302
|
|
|
305
303
|
if (isExternalController && !_hasPresentedController) {
|
|
306
304
|
_hasPresentedController = YES;
|
|
307
|
-
|
|
308
|
-
// Emit blur events when an external controller is presented on top
|
|
309
305
|
if ([self.delegate respondsToSelector:@selector(viewControllerWillBlur)]) {
|
|
310
306
|
[self.delegate viewControllerWillBlur];
|
|
311
307
|
}
|
|
@@ -330,7 +326,6 @@
|
|
|
330
326
|
BOOL isExternalController = presented && ![presented isKindOfClass:[TrueSheetViewController class]];
|
|
331
327
|
|
|
332
328
|
if (isExternalController && _hasPresentedController) {
|
|
333
|
-
// Emit focus events when external controller is dismissed
|
|
334
329
|
if ([self.delegate respondsToSelector:@selector(viewControllerWillFocus)]) {
|
|
335
330
|
[self.delegate viewControllerWillFocus];
|
|
336
331
|
}
|
|
@@ -350,7 +345,7 @@
|
|
|
350
345
|
}];
|
|
351
346
|
}
|
|
352
347
|
|
|
353
|
-
#pragma mark - Gesture Handling
|
|
348
|
+
#pragma mark - Position & Gesture Handling
|
|
354
349
|
|
|
355
350
|
- (TrueSheetContentView *)findContentView:(UIView *)view {
|
|
356
351
|
if ([view isKindOfClass:[TrueSheetContentView class]]) {
|
|
@@ -372,11 +367,9 @@
|
|
|
372
367
|
if (!presentedView)
|
|
373
368
|
return;
|
|
374
369
|
|
|
375
|
-
// Disable pan gestures if draggable is NO
|
|
376
370
|
if (!self.draggable) {
|
|
377
371
|
[GestureUtil setPanGesturesEnabled:NO forView:presentedView];
|
|
378
372
|
|
|
379
|
-
// Also disable ScrollView's pan gesture if present
|
|
380
373
|
TrueSheetContentView *contentView = [self findContentView:presentedView];
|
|
381
374
|
if (contentView) {
|
|
382
375
|
RCTScrollViewComponentView *scrollViewComponent = [contentView findScrollView:nil];
|
|
@@ -387,10 +380,8 @@
|
|
|
387
380
|
return;
|
|
388
381
|
}
|
|
389
382
|
|
|
390
|
-
// Attach to presented view's pan gesture (sheet's drag gesture from UIKit)
|
|
391
383
|
[GestureUtil attachPanGestureHandler:presentedView target:self selector:@selector(handlePanGesture:)];
|
|
392
384
|
|
|
393
|
-
// Also attach to ScrollView's pan gesture if present
|
|
394
385
|
TrueSheetContentView *contentView = [self findContentView:presentedView];
|
|
395
386
|
if (contentView) {
|
|
396
387
|
RCTScrollViewComponentView *scrollViewComponent = [contentView findScrollView:nil];
|
|
@@ -402,14 +393,13 @@
|
|
|
402
393
|
}
|
|
403
394
|
}
|
|
404
395
|
|
|
405
|
-
- (void)
|
|
396
|
+
- (void)setupDraggable {
|
|
406
397
|
UIView *presentedView = self.presentedView;
|
|
407
398
|
if (!presentedView)
|
|
408
399
|
return;
|
|
409
400
|
|
|
410
401
|
[GestureUtil setPanGesturesEnabled:self.draggable forView:presentedView];
|
|
411
402
|
|
|
412
|
-
// Also update ScrollView's pan gesture if present
|
|
413
403
|
TrueSheetContentView *contentView = [self findContentView:presentedView];
|
|
414
404
|
if (contentView) {
|
|
415
405
|
RCTScrollViewComponentView *scrollViewComponent = [contentView findScrollView:nil];
|
|
@@ -432,15 +422,16 @@
|
|
|
432
422
|
_isDragging = YES;
|
|
433
423
|
break;
|
|
434
424
|
case UIGestureRecognizerStateChanged:
|
|
435
|
-
|
|
425
|
+
if (!_isTrackingPositionFromLayout) {
|
|
426
|
+
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:YES debug:@"drag change"];
|
|
427
|
+
}
|
|
436
428
|
break;
|
|
437
429
|
case UIGestureRecognizerStateEnded:
|
|
438
430
|
case UIGestureRecognizerStateCancelled: {
|
|
439
431
|
if (!_isTransitioning) {
|
|
440
432
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
441
|
-
// Store resolved position when drag ends
|
|
442
433
|
[self storeResolvedPositionForIndex:self.currentDetentIndex];
|
|
443
|
-
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO];
|
|
434
|
+
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:@"drag end"];
|
|
444
435
|
});
|
|
445
436
|
}
|
|
446
437
|
|
|
@@ -452,8 +443,6 @@
|
|
|
452
443
|
}
|
|
453
444
|
}
|
|
454
445
|
|
|
455
|
-
#pragma mark - Position Tracking
|
|
456
|
-
|
|
457
446
|
- (void)setupTransitionTracker {
|
|
458
447
|
if (!self.transitionCoordinator)
|
|
459
448
|
return;
|
|
@@ -461,16 +450,13 @@
|
|
|
461
450
|
_isTransitioning = YES;
|
|
462
451
|
|
|
463
452
|
CGRect dismissedFrame = CGRectMake(0, self.screenHeight, 0, 0);
|
|
464
|
-
CGRect presentedFrame = CGRectMake(0, self.
|
|
453
|
+
CGRect presentedFrame = CGRectMake(0, self.currentPosition, 0, 0);
|
|
465
454
|
|
|
466
|
-
// Set starting fake view position
|
|
467
455
|
_transitionFakeView.frame = self.isDismissing ? presentedFrame : dismissedFrame;
|
|
468
456
|
[self storeResolvedPositionForIndex:self.currentDetentIndex];
|
|
469
457
|
|
|
470
458
|
auto animation = ^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
|
471
459
|
[[context containerView] addSubview:self->_transitionFakeView];
|
|
472
|
-
|
|
473
|
-
// Set the fake view target position
|
|
474
460
|
self->_transitionFakeView.frame = self.isDismissing ? dismissedFrame : presentedFrame;
|
|
475
461
|
|
|
476
462
|
self->_transitioningTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleTransitionTracker)];
|
|
@@ -490,22 +476,19 @@
|
|
|
490
476
|
- (void)handleTransitionTracker {
|
|
491
477
|
if (!_isDragging && _transitionFakeView.layer) {
|
|
492
478
|
CALayer *layer = _transitionFakeView.layer;
|
|
493
|
-
|
|
494
479
|
CGFloat layerPosition = layer.presentationLayer.frame.origin.y;
|
|
495
480
|
|
|
496
481
|
if (self.currentPosition >= self.screenHeight) {
|
|
497
|
-
|
|
498
|
-
[self emitChangePositionDelegateWithPosition:
|
|
482
|
+
CGFloat position = fmax(_lastPosition, layerPosition);
|
|
483
|
+
[self emitChangePositionDelegateWithPosition:position realtime:YES debug:@"transition out"];
|
|
499
484
|
} else {
|
|
500
|
-
// Presenting position
|
|
501
485
|
CGFloat position = fmax(self.currentPosition, layerPosition);
|
|
502
|
-
[self emitChangePositionDelegateWithPosition:position realtime:YES];
|
|
486
|
+
[self emitChangePositionDelegateWithPosition:position realtime:YES debug:@"transition in"];
|
|
503
487
|
}
|
|
504
488
|
}
|
|
505
489
|
}
|
|
506
490
|
|
|
507
|
-
- (void)emitChangePositionDelegateWithPosition:(CGFloat)position realtime:(BOOL)realtime {
|
|
508
|
-
// Use epsilon comparison to avoid missing updates due to floating point precision
|
|
491
|
+
- (void)emitChangePositionDelegateWithPosition:(CGFloat)position realtime:(BOOL)realtime debug:(NSString *)debug {
|
|
509
492
|
if (fabs(_lastPosition - position) > 0.01) {
|
|
510
493
|
_lastPosition = position;
|
|
511
494
|
|
|
@@ -517,14 +500,12 @@
|
|
|
517
500
|
}
|
|
518
501
|
}
|
|
519
502
|
|
|
520
|
-
/// Stores the current position for the given detent index
|
|
521
503
|
- (void)storeResolvedPositionForIndex:(NSInteger)index {
|
|
522
504
|
if (index >= 0 && index < (NSInteger)_resolvedDetentPositions.count) {
|
|
523
505
|
_resolvedDetentPositions[index] = @(self.currentPosition);
|
|
524
506
|
}
|
|
525
507
|
}
|
|
526
508
|
|
|
527
|
-
/// Returns the estimated Y position for a detent index, using stored positions when available
|
|
528
509
|
- (CGFloat)estimatedPositionForIndex:(NSInteger)index {
|
|
529
510
|
if (index < 0 || index >= (NSInteger)_resolvedDetentPositions.count)
|
|
530
511
|
return 0;
|
|
@@ -534,11 +515,9 @@
|
|
|
534
515
|
return storedPos;
|
|
535
516
|
}
|
|
536
517
|
|
|
537
|
-
// Estimate based on detent value and known offset from first resolved position
|
|
538
518
|
CGFloat detentValue = [self detentValueForIndex:index];
|
|
539
519
|
CGFloat basePosition = self.screenHeight - (detentValue * self.screenHeight);
|
|
540
520
|
|
|
541
|
-
// Find a resolved position to calculate offset
|
|
542
521
|
for (NSInteger i = 0; i < (NSInteger)_resolvedDetentPositions.count; i++) {
|
|
543
522
|
CGFloat pos = [_resolvedDetentPositions[i] doubleValue];
|
|
544
523
|
if (pos > 0) {
|
|
@@ -552,8 +531,6 @@
|
|
|
552
531
|
return basePosition;
|
|
553
532
|
}
|
|
554
533
|
|
|
555
|
-
/// Finds the segment containing the given position and returns the lower index and progress within that segment.
|
|
556
|
-
/// Returns YES if a segment was found, NO otherwise. When NO, `outIndex` contains the boundary index.
|
|
557
534
|
- (BOOL)findSegmentForPosition:(CGFloat)position outIndex:(NSInteger *)outIndex outProgress:(CGFloat *)outProgress {
|
|
558
535
|
NSInteger count = _resolvedDetentPositions.count;
|
|
559
536
|
if (count == 0) {
|
|
@@ -571,7 +548,6 @@
|
|
|
571
548
|
CGFloat firstPos = [self estimatedPositionForIndex:0];
|
|
572
549
|
CGFloat lastPos = [self estimatedPositionForIndex:count - 1];
|
|
573
550
|
|
|
574
|
-
// Below first detent (position > firstPos means sheet is smaller)
|
|
575
551
|
if (position > firstPos) {
|
|
576
552
|
CGFloat range = self.screenHeight - firstPos;
|
|
577
553
|
*outIndex = -1;
|
|
@@ -579,14 +555,12 @@
|
|
|
579
555
|
return NO;
|
|
580
556
|
}
|
|
581
557
|
|
|
582
|
-
// Above last detent
|
|
583
558
|
if (position < lastPos) {
|
|
584
559
|
*outIndex = count - 1;
|
|
585
560
|
*outProgress = 0;
|
|
586
561
|
return NO;
|
|
587
562
|
}
|
|
588
563
|
|
|
589
|
-
// Find segment (positions decrease as index increases)
|
|
590
564
|
for (NSInteger i = 0; i < count - 1; i++) {
|
|
591
565
|
CGFloat pos = [self estimatedPositionForIndex:i];
|
|
592
566
|
CGFloat nextPos = [self estimatedPositionForIndex:i + 1];
|
|
@@ -611,14 +585,11 @@
|
|
|
611
585
|
|
|
612
586
|
if (!found) {
|
|
613
587
|
if (index == -1) {
|
|
614
|
-
// Below first detent - return negative progress
|
|
615
588
|
return -progress;
|
|
616
589
|
}
|
|
617
|
-
// At or beyond boundary
|
|
618
590
|
return index;
|
|
619
591
|
}
|
|
620
592
|
|
|
621
|
-
// Within a segment - interpolate
|
|
622
593
|
return index + fmax(0, fmin(1, progress));
|
|
623
594
|
}
|
|
624
595
|
|
|
@@ -629,15 +600,12 @@
|
|
|
629
600
|
|
|
630
601
|
if (!found) {
|
|
631
602
|
if (index == -1) {
|
|
632
|
-
// Below first detent
|
|
633
603
|
CGFloat firstDetent = [self detentValueForIndex:0];
|
|
634
604
|
return fmax(0, firstDetent * (1 - progress));
|
|
635
605
|
}
|
|
636
|
-
// At or beyond boundary
|
|
637
606
|
return [self detentValueForIndex:index];
|
|
638
607
|
}
|
|
639
608
|
|
|
640
|
-
// Within a segment - interpolate between detent values
|
|
641
609
|
CGFloat detent = [self detentValueForIndex:index];
|
|
642
610
|
CGFloat nextDetent = [self detentValueForIndex:index + 1];
|
|
643
611
|
return detent + progress * (nextDetent - detent);
|
|
@@ -646,7 +614,6 @@
|
|
|
646
614
|
- (CGFloat)detentValueForIndex:(NSInteger)index {
|
|
647
615
|
if (index >= 0 && index < (NSInteger)_detents.count) {
|
|
648
616
|
CGFloat value = [_detents[index] doubleValue];
|
|
649
|
-
// For auto (-1), calculate actual fraction from content + header height
|
|
650
617
|
if (value == -1) {
|
|
651
618
|
CGFloat autoHeight = [self.contentHeight floatValue] + [self.headerHeight floatValue];
|
|
652
619
|
return autoHeight / self.screenHeight;
|
|
@@ -658,6 +625,11 @@
|
|
|
658
625
|
|
|
659
626
|
#pragma mark - Sheet Configuration
|
|
660
627
|
|
|
628
|
+
- (void)setupSheetDetentsForSizeChange {
|
|
629
|
+
_pendingContentSizeChange = YES;
|
|
630
|
+
[self setupSheetDetents];
|
|
631
|
+
}
|
|
632
|
+
|
|
661
633
|
- (void)setupSheetDetents {
|
|
662
634
|
UISheetPresentationController *sheet = self.sheetPresentationController;
|
|
663
635
|
if (!sheet)
|
|
@@ -674,13 +646,11 @@
|
|
|
674
646
|
withAutoHeight:autoHeight
|
|
675
647
|
atIndex:index];
|
|
676
648
|
[detents addObject:sheetDetent];
|
|
677
|
-
// Initialize with placeholder - will be updated when sheet settles at each detent
|
|
678
649
|
[_resolvedDetentPositions addObject:@(0)];
|
|
679
650
|
}
|
|
680
651
|
|
|
681
652
|
sheet.detents = detents;
|
|
682
653
|
|
|
683
|
-
// Setup dimmed background
|
|
684
654
|
if (self.dimmed && [self.dimmedDetentIndex integerValue] == 0) {
|
|
685
655
|
sheet.largestUndimmedDetentIdentifier = nil;
|
|
686
656
|
} else {
|
|
@@ -710,7 +680,6 @@
|
|
|
710
680
|
|
|
711
681
|
CGFloat value = [detent doubleValue];
|
|
712
682
|
|
|
713
|
-
// -1 represents "auto" (fit content height)
|
|
714
683
|
if (value == -1) {
|
|
715
684
|
if (@available(iOS 16.0, *)) {
|
|
716
685
|
return [self customDetentWithIdentifier:@"custom-auto" height:autoHeight];
|
|
@@ -776,7 +745,6 @@
|
|
|
776
745
|
if (detentCount == 0)
|
|
777
746
|
return;
|
|
778
747
|
|
|
779
|
-
// Clamp index to valid range
|
|
780
748
|
NSInteger clampedIndex = _activeDetentIndex;
|
|
781
749
|
if (clampedIndex < 0) {
|
|
782
750
|
clampedIndex = 0;
|
|
@@ -833,7 +801,6 @@
|
|
|
833
801
|
|
|
834
802
|
self.view.backgroundColor = self.backgroundColor;
|
|
835
803
|
|
|
836
|
-
// Setup blur effect view - recreate only when blurTint changes
|
|
837
804
|
BOOL blurTintChanged = ![_blurView.blurTint isEqualToString:self.blurTint];
|
|
838
805
|
|
|
839
806
|
if (_blurView && blurTintChanged) {
|
|
@@ -852,11 +819,9 @@
|
|
|
852
819
|
[_blurView applyBlurEffect];
|
|
853
820
|
}
|
|
854
821
|
|
|
855
|
-
// Setup grabber
|
|
856
822
|
BOOL showGrabber = self.grabber && self.draggable;
|
|
857
823
|
|
|
858
824
|
if (self.grabberOptions) {
|
|
859
|
-
// Use custom grabber view when options are provided
|
|
860
825
|
sheet.prefersGrabberVisible = NO;
|
|
861
826
|
|
|
862
827
|
NSDictionary *options = self.grabberOptions;
|
|
@@ -868,7 +833,6 @@
|
|
|
868
833
|
[_grabberView applyConfiguration];
|
|
869
834
|
_grabberView.hidden = !showGrabber;
|
|
870
835
|
} else {
|
|
871
|
-
// Use system default grabber when no options provided
|
|
872
836
|
sheet.prefersGrabberVisible = showGrabber;
|
|
873
837
|
_grabberView.hidden = YES;
|
|
874
838
|
}
|
|
@@ -893,13 +857,10 @@
|
|
|
893
857
|
|
|
894
858
|
#if RNS_DISMISSIBLE_MODAL_PROTOCOL_AVAILABLE
|
|
895
859
|
- (BOOL)isDismissible {
|
|
896
|
-
// Prevent react-native-screens from dismissing this sheet when presenting a modal
|
|
897
860
|
return NO;
|
|
898
861
|
}
|
|
899
862
|
|
|
900
863
|
- (UIViewController *)newPresentingViewController {
|
|
901
|
-
// Find the topmost TrueSheetViewController in the chain
|
|
902
|
-
// This handles cases where this sheet is presenting another sheet (child sheet)
|
|
903
864
|
UIViewController *topmost = self;
|
|
904
865
|
while (topmost.presentedViewController != nil && !topmost.presentedViewController.isBeingDismissed &&
|
|
905
866
|
[topmost.presentedViewController isKindOfClass:[TrueSheetViewController class]]) {
|
|
@@ -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,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}>
|