@lodev09/react-native-true-sheet 3.1.0 → 3.2.0
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 +5 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +103 -118
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +7 -2
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDialogObserver.kt +2 -2
- package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +64 -81
- package/android/src/main/res/values/styles.xml +1 -0
- package/ios/TrueSheetView.mm +20 -3
- package/ios/TrueSheetViewController.h +2 -0
- package/ios/TrueSheetViewController.mm +36 -18
- package/lib/module/TrueSheet.js +3 -1
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +5 -2
- package/lib/module/navigation/TrueSheetRouter.js +12 -18
- package/lib/module/navigation/TrueSheetRouter.js.map +1 -1
- package/lib/module/navigation/TrueSheetView.js +29 -128
- package/lib/module/navigation/TrueSheetView.js.map +1 -1
- package/lib/module/navigation/createTrueSheetNavigator.js +3 -3
- package/lib/module/navigation/createTrueSheetNavigator.js.map +1 -1
- package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js +46 -0
- package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js.map +1 -0
- package/lib/module/navigation/screen/TrueSheetScreen.js +39 -0
- package/lib/module/navigation/screen/TrueSheetScreen.js.map +1 -0
- package/lib/module/navigation/screen/index.js +5 -0
- package/lib/module/navigation/screen/index.js.map +1 -0
- package/lib/module/navigation/screen/types.js +4 -0
- package/lib/module/navigation/screen/types.js.map +1 -0
- package/lib/module/navigation/screen/useSheetScreenState.js +77 -0
- package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -0
- package/lib/module/navigation/useTrueSheetNavigation.js +1 -3
- package/lib/module/navigation/useTrueSheetNavigation.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +24 -0
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +3 -2
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/navigation/TrueSheetRouter.d.ts +4 -4
- package/lib/typescript/src/navigation/TrueSheetRouter.d.ts.map +1 -1
- package/lib/typescript/src/navigation/TrueSheetView.d.ts +1 -1
- package/lib/typescript/src/navigation/TrueSheetView.d.ts.map +1 -1
- package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts +1 -1
- package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts +3 -0
- package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts +3 -0
- package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen/index.d.ts +4 -0
- package/lib/typescript/src/navigation/screen/index.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen/types.d.ts +18 -0
- package/lib/typescript/src/navigation/screen/types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +35 -0
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -0
- package/lib/typescript/src/navigation/types.d.ts +29 -1
- package/lib/typescript/src/navigation/types.d.ts.map +1 -1
- package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts +1 -1
- package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/TrueSheet.tsx +3 -1
- package/src/TrueSheet.types.ts +26 -0
- package/src/fabric/TrueSheetViewNativeComponent.ts +5 -2
- package/src/navigation/TrueSheetRouter.ts +6 -12
- package/src/navigation/TrueSheetView.tsx +39 -212
- package/src/navigation/createTrueSheetNavigator.tsx +3 -3
- package/src/navigation/screen/ReanimatedTrueSheetScreen.tsx +47 -0
- package/src/navigation/screen/TrueSheetScreen.tsx +37 -0
- package/src/navigation/screen/index.ts +3 -0
- package/src/navigation/screen/types.ts +20 -0
- package/src/navigation/screen/useSheetScreenState.ts +106 -0
- package/src/navigation/types.ts +31 -0
- package/src/navigation/useTrueSheetNavigation.ts +2 -4
|
@@ -267,7 +267,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
267
267
|
viewController.sheetCornerRadius = radius
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
-
fun setSheetBackgroundColor(color: Int) {
|
|
270
|
+
fun setSheetBackgroundColor(color: Int?) {
|
|
271
271
|
if (viewController.sheetBackgroundColor == color) return
|
|
272
272
|
viewController.sheetBackgroundColor = color
|
|
273
273
|
}
|
|
@@ -299,6 +299,10 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
299
299
|
viewController.edgeToEdgeFullScreen = edgeToEdgeFullScreen
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
+
fun setInsetAdjustment(insetAdjustment: String) {
|
|
303
|
+
viewController.insetAdjustment = insetAdjustment
|
|
304
|
+
}
|
|
305
|
+
|
|
302
306
|
// ==================== State Management ====================
|
|
303
307
|
|
|
304
308
|
/**
|
|
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
|
|
4
4
|
import android.graphics.Color
|
|
5
5
|
import android.graphics.drawable.ShapeDrawable
|
|
6
6
|
import android.graphics.drawable.shapes.RoundRectShape
|
|
7
|
+
import android.util.Log
|
|
7
8
|
import android.util.TypedValue
|
|
8
9
|
import android.view.MotionEvent
|
|
9
10
|
import android.view.View
|
|
@@ -115,9 +116,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
115
116
|
var currentDetentIndex: Int = -1
|
|
116
117
|
private set
|
|
117
118
|
|
|
118
|
-
// Resolved detent positions (Y coordinate when sheet rests at each detent)
|
|
119
|
-
private val resolvedDetentPositions = mutableListOf<Int>()
|
|
120
|
-
|
|
121
119
|
private var isDragging = false
|
|
122
120
|
private var isDismissing = false
|
|
123
121
|
private var isReconfiguring = false
|
|
@@ -144,7 +142,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
144
142
|
var grabber: Boolean = true
|
|
145
143
|
var grabberOptions: GrabberOptions? = null
|
|
146
144
|
var sheetCornerRadius: Float = -1f
|
|
147
|
-
var sheetBackgroundColor: Int =
|
|
145
|
+
var sheetBackgroundColor: Int? = null
|
|
148
146
|
var edgeToEdgeFullScreen: Boolean = false
|
|
149
147
|
|
|
150
148
|
var dismissible: Boolean = true
|
|
@@ -167,12 +165,17 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
167
165
|
// MARK: - Computed Properties
|
|
168
166
|
// ====================================================================
|
|
169
167
|
|
|
170
|
-
val
|
|
171
|
-
get() = ScreenUtils.
|
|
168
|
+
val bottomInset: Int
|
|
169
|
+
get() = if (edgeToEdgeEnabled) ScreenUtils.getInsets(reactContext).bottom else 0
|
|
170
|
+
|
|
171
|
+
val topInset: Int
|
|
172
|
+
get() = if (edgeToEdgeEnabled) ScreenUtils.getInsets(reactContext).top else 0
|
|
173
|
+
|
|
174
|
+
var insetAdjustment: String = "automatic"
|
|
172
175
|
|
|
173
|
-
/**
|
|
174
|
-
|
|
175
|
-
get() =
|
|
176
|
+
/** Auto add bottom inset for consistency with iOS when insetAdjustment is 'automatic' */
|
|
177
|
+
val contentBottomInset: Int
|
|
178
|
+
get() = if (insetAdjustment == "automatic") bottomInset else 0
|
|
176
179
|
|
|
177
180
|
/** Edge-to-edge enabled by default on API 36+, or when explicitly configured. */
|
|
178
181
|
private val edgeToEdgeEnabled: Boolean
|
|
@@ -181,10 +184,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
181
184
|
return BuildConfig.EDGE_TO_EDGE_ENABLED || dialog?.edgeToEdgeEnabled == true || defaultEnabled
|
|
182
185
|
}
|
|
183
186
|
|
|
184
|
-
/** Top inset when edge-to-edge is enabled but not full-screen. */
|
|
185
|
-
private val sheetTopInset: Int
|
|
186
|
-
get() = if (edgeToEdgeEnabled && !edgeToEdgeFullScreen) statusBarHeight else 0
|
|
187
|
-
|
|
188
187
|
internal var eventDispatcher: EventDispatcher? = null
|
|
189
188
|
private val jSTouchDispatcher = JSTouchDispatcher(this)
|
|
190
189
|
private var jSPointerDispatcher: JSPointerDispatcher? = null
|
|
@@ -197,7 +196,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
197
196
|
// ====================================================================
|
|
198
197
|
|
|
199
198
|
init {
|
|
200
|
-
screenHeight = ScreenUtils.getScreenHeight(reactContext
|
|
199
|
+
screenHeight = ScreenUtils.getScreenHeight(reactContext)
|
|
201
200
|
screenWidth = ScreenUtils.getScreenWidth(reactContext)
|
|
202
201
|
jSPointerDispatcher = JSPointerDispatcher(this)
|
|
203
202
|
}
|
|
@@ -269,9 +268,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
269
268
|
setupGrabber()
|
|
270
269
|
|
|
271
270
|
sheetContainer?.post {
|
|
272
|
-
|
|
273
|
-
storeResolvedPosition(currentDetentIndex)
|
|
274
|
-
emitChangePositionDelegate(positionPx, realtime = false)
|
|
271
|
+
bottomSheetView?.let { emitChangePositionDelegate(it, realtime = false) }
|
|
275
272
|
positionFooter()
|
|
276
273
|
}
|
|
277
274
|
|
|
@@ -293,7 +290,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
293
290
|
|
|
294
291
|
isDismissing = true
|
|
295
292
|
emitWillDismissEvents()
|
|
296
|
-
|
|
293
|
+
emitDismissedPosition()
|
|
297
294
|
}
|
|
298
295
|
|
|
299
296
|
dialog.setOnDismissListener {
|
|
@@ -309,9 +306,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
309
306
|
object : BottomSheetBehavior.BottomSheetCallback() {
|
|
310
307
|
override fun onSlide(sheetView: View, slideOffset: Float) {
|
|
311
308
|
val behavior = behavior ?: return
|
|
312
|
-
val positionPx = getCurrentPositionPx(sheetView)
|
|
313
309
|
|
|
314
|
-
emitChangePositionDelegate(
|
|
310
|
+
emitChangePositionDelegate(sheetView, realtime = true)
|
|
315
311
|
|
|
316
312
|
when (behavior.state) {
|
|
317
313
|
BottomSheetBehavior.STATE_DRAGGING,
|
|
@@ -343,8 +339,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
343
339
|
if (isReconfiguring) return
|
|
344
340
|
|
|
345
341
|
getDetentInfoForState(newState)?.let { detentInfo ->
|
|
346
|
-
storeResolvedPosition(detentInfo.index)
|
|
347
|
-
|
|
348
342
|
if (isDragging) {
|
|
349
343
|
val detent = getDetentValueForIndex(detentInfo.index)
|
|
350
344
|
delegate?.viewControllerDidDragEnd(detentInfo.index, detentInfo.position, detent)
|
|
@@ -432,11 +426,11 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
432
426
|
val isExpanded: Boolean
|
|
433
427
|
get() {
|
|
434
428
|
val sheetTop = bottomSheetView?.top ?: return false
|
|
435
|
-
return sheetTop <=
|
|
429
|
+
return sheetTop <= topInset
|
|
436
430
|
}
|
|
437
431
|
|
|
438
432
|
val currentSheetTop: Int
|
|
439
|
-
get() = bottomSheetView?.
|
|
433
|
+
get() = bottomSheetView?.top ?: screenHeight
|
|
440
434
|
|
|
441
435
|
fun getExpectedSheetTop(detentIndex: Int): Int {
|
|
442
436
|
if (detentIndex < 0 || detentIndex >= detents.size) return screenHeight
|
|
@@ -445,15 +439,21 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
445
439
|
}
|
|
446
440
|
|
|
447
441
|
/** Hides without dismissing. Used for sheet stacking and RN Screens modals. */
|
|
448
|
-
fun hideDialog() {
|
|
442
|
+
fun hideDialog(emitPosition: Boolean = false) {
|
|
449
443
|
isDialogVisible = false
|
|
450
444
|
dialog?.window?.decorView?.visibility = INVISIBLE
|
|
445
|
+
if (emitPosition) {
|
|
446
|
+
emitDismissedPosition()
|
|
447
|
+
}
|
|
451
448
|
}
|
|
452
449
|
|
|
453
450
|
/** Shows a previously hidden dialog. */
|
|
454
|
-
fun showDialog() {
|
|
451
|
+
fun showDialog(emitPosition: Boolean = false) {
|
|
455
452
|
isDialogVisible = true
|
|
456
453
|
dialog?.window?.decorView?.visibility = VISIBLE
|
|
454
|
+
if (emitPosition) {
|
|
455
|
+
bottomSheetView?.let { emitChangePositionDelegate(it, realtime = false) }
|
|
456
|
+
}
|
|
457
457
|
}
|
|
458
458
|
|
|
459
459
|
// ====================================================================
|
|
@@ -496,10 +496,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
496
496
|
|
|
497
497
|
isDismissing = true
|
|
498
498
|
emitWillDismissEvents()
|
|
499
|
-
|
|
500
|
-
this.post {
|
|
501
|
-
emitChangePositionDelegate(screenHeight, realtime = false)
|
|
502
|
-
}
|
|
499
|
+
emitDismissedPosition()
|
|
503
500
|
|
|
504
501
|
if (!animated) {
|
|
505
502
|
dialog?.window?.setWindowAnimations(0)
|
|
@@ -515,19 +512,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
515
512
|
fun setupSheetDetents() {
|
|
516
513
|
val behavior = this.behavior ?: return
|
|
517
514
|
|
|
518
|
-
if (resolvedDetentPositions.size != detents.size) {
|
|
519
|
-
resolvedDetentPositions.clear()
|
|
520
|
-
repeat(detents.size) { resolvedDetentPositions.add(0) }
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
for (i in detents.indices) {
|
|
524
|
-
if (detents[i] == -1.0) {
|
|
525
|
-
val detentHeight = getDetentHeight(detents[i])
|
|
526
|
-
resolvedDetentPositions[i] = screenHeight - detentHeight
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
515
|
isReconfiguring = true
|
|
516
|
+
val realHeight = ScreenUtils.getRealScreenHeight(reactContext)
|
|
517
|
+
val edgeToEdgeTopInset: Int = if (!edgeToEdgeFullScreen) topInset else 0
|
|
531
518
|
|
|
532
519
|
behavior.apply {
|
|
533
520
|
isFitToContents = false
|
|
@@ -535,31 +522,26 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
535
522
|
|
|
536
523
|
val oldExpandOffset = expandedOffset
|
|
537
524
|
|
|
538
|
-
|
|
539
|
-
1 -> {
|
|
540
|
-
setPeekHeight(getDetentHeight(detents[0]), isPresented)
|
|
541
|
-
halfExpandedRatio = minOf(peekHeight.toFloat() / screenHeight.toFloat(), MAX_HALF_EXPANDED_RATIO)
|
|
542
|
-
expandedOffset = screenHeight - peekHeight
|
|
543
|
-
isFitToContents = expandedOffset == 0
|
|
544
|
-
}
|
|
525
|
+
val maxAvailableHeight = realHeight - edgeToEdgeTopInset
|
|
545
526
|
|
|
546
|
-
|
|
547
|
-
setPeekHeight(getDetentHeight(detents[0]), isPresented)
|
|
548
|
-
halfExpandedRatio = minOf(getDetentHeight(detents[1]).toFloat() / screenHeight.toFloat(), MAX_HALF_EXPANDED_RATIO)
|
|
549
|
-
expandedOffset = screenHeight - getDetentHeight(detents[1])
|
|
550
|
-
isFitToContents = expandedOffset == 0
|
|
551
|
-
}
|
|
527
|
+
setPeekHeight(getDetentHeight(detents[0]), isPresented)
|
|
552
528
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
expandedOffset = screenHeight - getDetentHeight(detents[2])
|
|
557
|
-
}
|
|
529
|
+
val halfExpandedDetentHeight = when (detents.size) {
|
|
530
|
+
1 -> peekHeight
|
|
531
|
+
else -> getDetentHeight(detents[1])
|
|
558
532
|
}
|
|
559
533
|
|
|
534
|
+
val maxDetentHeight = getDetentHeight(detents.last())
|
|
535
|
+
|
|
536
|
+
val adjustedHalfExpandedHeight = minOf(halfExpandedDetentHeight, maxAvailableHeight)
|
|
537
|
+
halfExpandedRatio = minOf(adjustedHalfExpandedHeight.toFloat() / realHeight.toFloat(), MAX_HALF_EXPANDED_RATIO)
|
|
538
|
+
|
|
539
|
+
expandedOffset = maxOf(edgeToEdgeTopInset, realHeight - maxDetentHeight)
|
|
540
|
+
isFitToContents = detents.size < 3 && expandedOffset == 0
|
|
541
|
+
|
|
560
542
|
if (oldExpandOffset != expandedOffset || expandedOffset == 0) {
|
|
561
|
-
val offset = if (expandedOffset == 0)
|
|
562
|
-
val newHeight =
|
|
543
|
+
val offset = if (expandedOffset == 0) topInset else 0
|
|
544
|
+
val newHeight = realHeight - expandedOffset - offset
|
|
563
545
|
delegate?.viewControllerDidChangeSize(width, newHeight)
|
|
564
546
|
}
|
|
565
547
|
|
|
@@ -592,7 +574,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
592
574
|
|
|
593
575
|
val cornerRadius = if (sheetCornerRadius < 0) DEFAULT_CORNER_RADIUS.dpToPx() else sheetCornerRadius
|
|
594
576
|
val outerRadii = floatArrayOf(cornerRadius, cornerRadius, cornerRadius, cornerRadius, 0f, 0f, 0f, 0f)
|
|
595
|
-
val backgroundColor =
|
|
577
|
+
val backgroundColor = sheetBackgroundColor ?: getDefaultBackgroundColor()
|
|
596
578
|
|
|
597
579
|
bottomSheet.background = ShapeDrawable(RoundRectShape(outerRadii, null, null)).apply {
|
|
598
580
|
paint.color = backgroundColor
|
|
@@ -633,15 +615,18 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
633
615
|
val bottomSheet = bottomSheetView ?: return
|
|
634
616
|
|
|
635
617
|
val footerHeight = footerView.height
|
|
636
|
-
val
|
|
618
|
+
val sheetHeight = bottomSheet.height
|
|
619
|
+
val sheetTop = bottomSheet.top
|
|
637
620
|
|
|
638
|
-
|
|
621
|
+
// Footer Y relative to sheet: place at bottom of sheet container minus footer height
|
|
622
|
+
var footerY = (sheetHeight - sheetTop - footerHeight).toFloat()
|
|
639
623
|
|
|
640
624
|
if (slideOffset != null && slideOffset < 0) {
|
|
641
625
|
footerY -= (footerHeight * slideOffset)
|
|
642
626
|
}
|
|
643
627
|
|
|
644
|
-
|
|
628
|
+
// Clamp to prevent footer from going above visible area
|
|
629
|
+
val maxAllowedY = (sheetHeight - topInset - footerHeight).toFloat()
|
|
645
630
|
footerView.y = minOf(footerY, maxAllowedY)
|
|
646
631
|
}
|
|
647
632
|
|
|
@@ -671,52 +656,54 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
671
656
|
// MARK: - Position & Drag Handling
|
|
672
657
|
// ====================================================================
|
|
673
658
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
val
|
|
680
|
-
|
|
681
|
-
delegate?.viewControllerDidChangePosition(interpolatedIndex, position, detent, realtime)
|
|
659
|
+
/**
|
|
660
|
+
* Calculate the visible sheet height from a sheet view.
|
|
661
|
+
* Uses real screen height for consistency across API levels.
|
|
662
|
+
*/
|
|
663
|
+
private fun getVisibleSheetHeight(sheetView: View): Int {
|
|
664
|
+
val realHeight = ScreenUtils.getRealScreenHeight(reactContext)
|
|
665
|
+
return realHeight - sheetView.top
|
|
682
666
|
}
|
|
683
667
|
|
|
684
|
-
private fun
|
|
685
|
-
if (index < 0 || index >= resolvedDetentPositions.size) return
|
|
686
|
-
val positionPx = bottomSheetView?.let { ScreenUtils.getScreenY(it) } ?: return
|
|
687
|
-
if (positionPx in 1..<screenHeight) {
|
|
688
|
-
resolvedDetentPositions[index] = positionPx
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
fun storeCurrentResolvedPosition() {
|
|
693
|
-
storeResolvedPosition(currentDetentIndex)
|
|
694
|
-
}
|
|
668
|
+
private fun getPositionDp(visibleSheetHeight: Int): Float = (screenHeight - visibleSheetHeight).pxToDp()
|
|
695
669
|
|
|
696
|
-
private fun
|
|
697
|
-
if (
|
|
670
|
+
private fun emitChangePositionDelegate(sheetView: View, realtime: Boolean) {
|
|
671
|
+
if (sheetView.top == lastEmittedPositionPx) return
|
|
698
672
|
|
|
699
|
-
|
|
700
|
-
|
|
673
|
+
lastEmittedPositionPx = sheetView.top
|
|
674
|
+
val position = getPositionDp(getVisibleSheetHeight(sheetView))
|
|
675
|
+
val interpolatedIndex = getInterpolatedIndexForPosition(sheetView.top)
|
|
676
|
+
val detent = getInterpolatedDetentForPosition(sheetView.top)
|
|
677
|
+
delegate?.viewControllerDidChangePosition(interpolatedIndex, position, detent, realtime)
|
|
678
|
+
}
|
|
701
679
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
680
|
+
private fun emitDismissedPosition() {
|
|
681
|
+
val position = screenHeight.pxToDp()
|
|
682
|
+
lastEmittedPositionPx = -1
|
|
683
|
+
delegate?.viewControllerDidChangePosition(-1f, position, 0f, false)
|
|
684
|
+
}
|
|
706
685
|
|
|
707
|
-
|
|
686
|
+
/**
|
|
687
|
+
* Get the expected sheetTop position for a detent index.
|
|
688
|
+
*/
|
|
689
|
+
private fun getSheetTopForDetentIndex(index: Int): Int {
|
|
690
|
+
val realHeight = ScreenUtils.getRealScreenHeight(reactContext)
|
|
691
|
+
if (index < 0 || index >= detents.size) return realHeight
|
|
692
|
+
val detentHeight = getDetentHeight(detents[index])
|
|
693
|
+
return realHeight - detentHeight
|
|
708
694
|
}
|
|
709
695
|
|
|
710
696
|
/** Returns (fromIndex, toIndex, progress) for interpolation, or null if < 2 detents. */
|
|
711
697
|
private fun findSegmentForPosition(positionPx: Int): Triple<Int, Int, Float>? {
|
|
712
|
-
val count =
|
|
698
|
+
val count = detents.size
|
|
713
699
|
if (count < 2) return null
|
|
714
700
|
|
|
715
|
-
val
|
|
716
|
-
val
|
|
701
|
+
val realHeight = ScreenUtils.getRealScreenHeight(reactContext)
|
|
702
|
+
val firstPos = getSheetTopForDetentIndex(0)
|
|
703
|
+
val lastPos = getSheetTopForDetentIndex(count - 1)
|
|
717
704
|
|
|
718
705
|
if (positionPx > firstPos) {
|
|
719
|
-
val range =
|
|
706
|
+
val range = realHeight - firstPos
|
|
720
707
|
val progress = if (range > 0) (positionPx - firstPos).toFloat() / range else 0f
|
|
721
708
|
return Triple(-1, 0, progress)
|
|
722
709
|
}
|
|
@@ -726,8 +713,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
726
713
|
}
|
|
727
714
|
|
|
728
715
|
for (i in 0 until count - 1) {
|
|
729
|
-
val pos =
|
|
730
|
-
val nextPos =
|
|
716
|
+
val pos = getSheetTopForDetentIndex(i)
|
|
717
|
+
val nextPos = getSheetTopForDetentIndex(i + 1)
|
|
731
718
|
|
|
732
719
|
if (positionPx in nextPos..pos) {
|
|
733
720
|
val range = pos - nextPos
|
|
@@ -741,7 +728,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
741
728
|
|
|
742
729
|
/** Returns continuous index (e.g., 0.5 = halfway between detent 0 and 1). */
|
|
743
730
|
private fun getInterpolatedIndexForPosition(positionPx: Int): Float {
|
|
744
|
-
val count =
|
|
731
|
+
val count = detents.size
|
|
745
732
|
if (count == 0) return -1f
|
|
746
733
|
if (count == 1) return 0f
|
|
747
734
|
|
|
@@ -754,7 +741,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
754
741
|
|
|
755
742
|
/** Returns interpolated screen fraction for position. */
|
|
756
743
|
private fun getInterpolatedDetentForPosition(positionPx: Int): Float {
|
|
757
|
-
val count =
|
|
744
|
+
val count = detents.size
|
|
758
745
|
if (count == 0) return 0f
|
|
759
746
|
|
|
760
747
|
val segment = findSegmentForPosition(positionPx) ?: return getDetentValueForIndex(0)
|
|
@@ -782,12 +769,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
782
769
|
}
|
|
783
770
|
|
|
784
771
|
private fun getCurrentDetentInfo(sheetView: View): DetentInfo {
|
|
785
|
-
val
|
|
786
|
-
return DetentInfo(currentDetentIndex,
|
|
772
|
+
val position = getPositionDp(getVisibleSheetHeight(sheetView))
|
|
773
|
+
return DetentInfo(currentDetentIndex, position)
|
|
787
774
|
}
|
|
788
775
|
|
|
789
|
-
private fun getCurrentPositionPx(sheetView: View): Int = ScreenUtils.getScreenY(sheetView)
|
|
790
|
-
|
|
791
776
|
private fun handleDragBegin(sheetView: View) {
|
|
792
777
|
val detentInfo = getCurrentDetentInfo(sheetView)
|
|
793
778
|
val detent = getDetentValueForIndex(detentInfo.index)
|
|
@@ -808,15 +793,17 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
808
793
|
|
|
809
794
|
private fun getDetentHeight(detent: Double): Int {
|
|
810
795
|
val height: Int = if (detent == -1.0) {
|
|
811
|
-
|
|
796
|
+
// Auto height: add bottomInset to content to match iOS behavior
|
|
797
|
+
contentHeight + headerHeight + contentBottomInset
|
|
812
798
|
} else {
|
|
813
799
|
if (detent <= 0.0 || detent > 1.0) {
|
|
814
800
|
throw IllegalArgumentException("TrueSheet: detent fraction ($detent) must be between 0 and 1")
|
|
815
801
|
}
|
|
816
|
-
|
|
802
|
+
// Fractional detent: add bottomInset to match iOS behavior
|
|
803
|
+
(detent * screenHeight).toInt() + contentBottomInset
|
|
817
804
|
}
|
|
818
805
|
|
|
819
|
-
val maxAllowedHeight = screenHeight
|
|
806
|
+
val maxAllowedHeight = screenHeight + contentBottomInset
|
|
820
807
|
return maxSheetHeight?.let { minOf(height, it, maxAllowedHeight) } ?: minOf(height, maxAllowedHeight)
|
|
821
808
|
}
|
|
822
809
|
|
|
@@ -866,15 +853,15 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
866
853
|
}
|
|
867
854
|
|
|
868
855
|
private fun getPositionForDetentIndex(index: Int): Float {
|
|
869
|
-
if (index < 0 || index >= detents.size) return
|
|
856
|
+
if (index < 0 || index >= detents.size) return screenHeight.pxToDp()
|
|
870
857
|
|
|
871
858
|
bottomSheetView?.let {
|
|
872
|
-
val
|
|
873
|
-
if (
|
|
859
|
+
val visibleSheetHeight = getVisibleSheetHeight(it)
|
|
860
|
+
if (visibleSheetHeight > 0) return getPositionDp(visibleSheetHeight)
|
|
874
861
|
}
|
|
875
862
|
|
|
876
863
|
val detentHeight = getDetentHeight(detents[index])
|
|
877
|
-
return (
|
|
864
|
+
return getPositionDp(detentHeight)
|
|
878
865
|
}
|
|
879
866
|
|
|
880
867
|
fun getDetentInfoForIndex(index: Int) = getDetentInfoForState(getStateForDetentIndex(index)) ?: DetentInfo(0, 0f)
|
|
@@ -893,20 +880,18 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
893
880
|
if (w == oldw && h == oldh) return
|
|
894
881
|
|
|
895
882
|
// Skip continuous size changes when fullScreen + edge-to-edge
|
|
896
|
-
if (h +
|
|
883
|
+
if (h + topInset > screenHeight && isExpanded && oldw == w) {
|
|
897
884
|
return
|
|
898
885
|
}
|
|
899
886
|
|
|
900
887
|
val oldScreenHeight = screenHeight
|
|
901
|
-
screenHeight = ScreenUtils.getScreenHeight(reactContext
|
|
888
|
+
screenHeight = ScreenUtils.getScreenHeight(reactContext)
|
|
902
889
|
|
|
903
890
|
if (isPresented && oldScreenHeight != screenHeight && oldScreenHeight > 0) {
|
|
904
891
|
setupSheetDetents()
|
|
905
892
|
this.post {
|
|
906
893
|
positionFooter()
|
|
907
|
-
|
|
908
|
-
val positionPx = bottomSheetView?.let { ScreenUtils.getScreenY(it) } ?: screenHeight
|
|
909
|
-
emitChangePositionDelegate(positionPx, realtime = false)
|
|
894
|
+
bottomSheetView?.let { emitChangePositionDelegate(it, realtime = false) }
|
|
910
895
|
}
|
|
911
896
|
}
|
|
912
897
|
}
|
|
@@ -97,8 +97,8 @@ class TrueSheetViewManager :
|
|
|
97
97
|
view.setDetents(detents)
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
@ReactProp(name = "
|
|
101
|
-
override fun
|
|
100
|
+
@ReactProp(name = "backgroundColor", customType = "Color")
|
|
101
|
+
override fun setBackgroundColor(view: TrueSheetView, color: Int?) {
|
|
102
102
|
view.setSheetBackgroundColor(color)
|
|
103
103
|
}
|
|
104
104
|
|
|
@@ -196,6 +196,11 @@ class TrueSheetViewManager :
|
|
|
196
196
|
view.setEdgeToEdgeFullScreen(edgeToEdgeFullScreen)
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
@ReactProp(name = "insetAdjustment")
|
|
200
|
+
override fun setInsetAdjustment(view: TrueSheetView, insetAdjustment: String?) {
|
|
201
|
+
view.setInsetAdjustment(insetAdjustment ?: "automatic")
|
|
202
|
+
}
|
|
203
|
+
|
|
199
204
|
@ReactProp(name = "scrollable", defaultBoolean = false)
|
|
200
205
|
override fun setScrollable(view: TrueSheetView, value: Boolean) {
|
|
201
206
|
// iOS-specific prop - no-op on Android
|
|
@@ -25,7 +25,7 @@ object TrueSheetDialogObserver {
|
|
|
25
25
|
val parentTop = it.viewController.currentSheetTop
|
|
26
26
|
val newSheetTop = sheetView.viewController.getExpectedSheetTop(detentIndex)
|
|
27
27
|
if (!it.viewController.isExpanded && parentTop <= newSheetTop) {
|
|
28
|
-
it.viewController.hideDialog()
|
|
28
|
+
it.viewController.hideDialog(emitPosition = true)
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -46,7 +46,7 @@ object TrueSheetDialogObserver {
|
|
|
46
46
|
synchronized(presentedSheetStack) {
|
|
47
47
|
presentedSheetStack.remove(sheetView)
|
|
48
48
|
if (hadParent) {
|
|
49
|
-
presentedSheetStack.lastOrNull()?.viewController?.showDialog()
|
|
49
|
+
presentedSheetStack.lastOrNull()?.viewController?.showDialog(emitPosition = true)
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
}
|