@lodev09/react-native-true-sheet 3.1.0-beta.9 → 3.1.1
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 +8 -2
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +18 -2
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +103 -121
- 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/TrueSheetModule.mm +17 -2
- package/ios/TrueSheetView.h +2 -0
- package/ios/TrueSheetView.mm +32 -3
- package/ios/TrueSheetViewController.h +2 -0
- package/ios/TrueSheetViewController.mm +45 -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 +119 -0
- package/lib/module/navigation/TrueSheetRouter.js.map +1 -0
- package/lib/module/navigation/TrueSheetView.js +169 -0
- package/lib/module/navigation/TrueSheetView.js.map +1 -0
- package/lib/module/navigation/createTrueSheetNavigator.js +59 -0
- package/lib/module/navigation/createTrueSheetNavigator.js.map +1 -0
- package/lib/module/navigation/index.js +6 -0
- package/lib/module/navigation/index.js.map +1 -0
- package/lib/module/navigation/types.js +4 -0
- package/lib/module/navigation/types.js.map +1 -0
- package/lib/module/navigation/useTrueSheetNavigation.js +26 -0
- package/lib/module/navigation/useTrueSheetNavigation.js.map +1 -0
- 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 +57 -0
- package/lib/typescript/src/navigation/TrueSheetRouter.d.ts.map +1 -0
- package/lib/typescript/src/navigation/TrueSheetView.d.ts +10 -0
- package/lib/typescript/src/navigation/TrueSheetView.d.ts.map +1 -0
- package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts +35 -0
- package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts.map +1 -0
- package/lib/typescript/src/navigation/index.d.ts +6 -0
- package/lib/typescript/src/navigation/index.d.ts.map +1 -0
- package/lib/typescript/src/navigation/types.d.ts +125 -0
- package/lib/typescript/src/navigation/types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts +23 -0
- package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts.map +1 -0
- package/package.json +12 -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 +172 -0
- package/src/navigation/TrueSheetView.tsx +271 -0
- package/src/navigation/createTrueSheetNavigator.tsx +89 -0
- package/src/navigation/index.ts +14 -0
- package/src/navigation/types.ts +176 -0
- package/src/navigation/useTrueSheetNavigation.ts +28 -0
|
@@ -85,8 +85,14 @@ class TrueSheetModule(reactContext: ReactApplicationContext) :
|
|
|
85
85
|
*/
|
|
86
86
|
@ReactMethod
|
|
87
87
|
fun resizeByRef(viewTag: Double, index: Double, promise: Promise) {
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
val tag = viewTag.toInt()
|
|
89
|
+
val detentIndex = index.toInt()
|
|
90
|
+
|
|
91
|
+
withTrueSheetView(tag, promise) { view ->
|
|
92
|
+
view.resize(detentIndex) {
|
|
93
|
+
promise.resolve(null)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package com.lodev09.truesheet
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
|
+
import android.util.Log
|
|
4
5
|
import android.view.View
|
|
5
6
|
import android.view.ViewStructure
|
|
6
7
|
import android.view.accessibility.AccessibilityEvent
|
|
@@ -12,6 +13,7 @@ import com.facebook.react.uimanager.StateWrapper
|
|
|
12
13
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
13
14
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
14
15
|
import com.facebook.react.uimanager.events.EventDispatcher
|
|
16
|
+
import com.facebook.react.util.RNLog
|
|
15
17
|
import com.facebook.react.views.view.ReactViewGroup
|
|
16
18
|
import com.lodev09.truesheet.core.GrabberOptions
|
|
17
19
|
import com.lodev09.truesheet.core.TrueSheetDialogObserver
|
|
@@ -92,7 +94,6 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
92
94
|
viewController.setupBackground()
|
|
93
95
|
viewController.setupGrabber()
|
|
94
96
|
updateSheetIfNeeded()
|
|
95
|
-
viewController.setStateForDetentIndex(viewController.currentDetentIndex)
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
|
|
@@ -266,7 +267,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
266
267
|
viewController.sheetCornerRadius = radius
|
|
267
268
|
}
|
|
268
269
|
|
|
269
|
-
fun setSheetBackgroundColor(color: Int) {
|
|
270
|
+
fun setSheetBackgroundColor(color: Int?) {
|
|
270
271
|
if (viewController.sheetBackgroundColor == color) return
|
|
271
272
|
viewController.sheetBackgroundColor = color
|
|
272
273
|
}
|
|
@@ -298,6 +299,10 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
298
299
|
viewController.edgeToEdgeFullScreen = edgeToEdgeFullScreen
|
|
299
300
|
}
|
|
300
301
|
|
|
302
|
+
fun setInsetAdjustment(insetAdjustment: String) {
|
|
303
|
+
viewController.insetAdjustment = insetAdjustment
|
|
304
|
+
}
|
|
305
|
+
|
|
301
306
|
// ==================== State Management ====================
|
|
302
307
|
|
|
303
308
|
/**
|
|
@@ -331,6 +336,17 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
331
336
|
viewController.dismiss(animated)
|
|
332
337
|
}
|
|
333
338
|
|
|
339
|
+
@UiThread
|
|
340
|
+
fun resize(detentIndex: Int, promiseCallback: () -> Unit) {
|
|
341
|
+
if (!viewController.isPresented) {
|
|
342
|
+
RNLog.w(reactContext, "TrueSheet: Cannot resize. Sheet is not presented.")
|
|
343
|
+
promiseCallback()
|
|
344
|
+
return
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
present(detentIndex, true, promiseCallback)
|
|
348
|
+
}
|
|
349
|
+
|
|
334
350
|
/**
|
|
335
351
|
* Debounced sheet update to handle rapid content/header size changes.
|
|
336
352
|
* Uses post to ensure all layout passes complete before reconfiguring.
|
|
@@ -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,18 +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
|
|
451
|
-
|
|
445
|
+
if (emitPosition) {
|
|
446
|
+
emitDismissedPosition()
|
|
447
|
+
}
|
|
452
448
|
}
|
|
453
449
|
|
|
454
450
|
/** Shows a previously hidden dialog. */
|
|
455
|
-
fun showDialog() {
|
|
451
|
+
fun showDialog(emitPosition: Boolean = false) {
|
|
456
452
|
isDialogVisible = true
|
|
457
453
|
dialog?.window?.decorView?.visibility = VISIBLE
|
|
458
|
-
|
|
459
|
-
|
|
454
|
+
if (emitPosition) {
|
|
455
|
+
bottomSheetView?.let { emitChangePositionDelegate(it, realtime = false) }
|
|
456
|
+
}
|
|
460
457
|
}
|
|
461
458
|
|
|
462
459
|
// ====================================================================
|
|
@@ -499,10 +496,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
499
496
|
|
|
500
497
|
isDismissing = true
|
|
501
498
|
emitWillDismissEvents()
|
|
502
|
-
|
|
503
|
-
this.post {
|
|
504
|
-
emitChangePositionDelegate(screenHeight, realtime = false)
|
|
505
|
-
}
|
|
499
|
+
emitDismissedPosition()
|
|
506
500
|
|
|
507
501
|
if (!animated) {
|
|
508
502
|
dialog?.window?.setWindowAnimations(0)
|
|
@@ -518,19 +512,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
518
512
|
fun setupSheetDetents() {
|
|
519
513
|
val behavior = this.behavior ?: return
|
|
520
514
|
|
|
521
|
-
if (resolvedDetentPositions.size != detents.size) {
|
|
522
|
-
resolvedDetentPositions.clear()
|
|
523
|
-
repeat(detents.size) { resolvedDetentPositions.add(0) }
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
for (i in detents.indices) {
|
|
527
|
-
if (detents[i] == -1.0) {
|
|
528
|
-
val detentHeight = getDetentHeight(detents[i])
|
|
529
|
-
resolvedDetentPositions[i] = screenHeight - detentHeight
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
515
|
isReconfiguring = true
|
|
516
|
+
val realHeight = ScreenUtils.getRealScreenHeight(reactContext)
|
|
517
|
+
val edgeToEdgeTopInset: Int = if (!edgeToEdgeFullScreen) topInset else 0
|
|
534
518
|
|
|
535
519
|
behavior.apply {
|
|
536
520
|
isFitToContents = false
|
|
@@ -538,31 +522,26 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
538
522
|
|
|
539
523
|
val oldExpandOffset = expandedOffset
|
|
540
524
|
|
|
541
|
-
|
|
542
|
-
1 -> {
|
|
543
|
-
setPeekHeight(getDetentHeight(detents[0]), isPresented)
|
|
544
|
-
halfExpandedRatio = minOf(peekHeight.toFloat() / screenHeight.toFloat(), MAX_HALF_EXPANDED_RATIO)
|
|
545
|
-
expandedOffset = screenHeight - peekHeight
|
|
546
|
-
isFitToContents = expandedOffset == 0
|
|
547
|
-
}
|
|
525
|
+
val maxAvailableHeight = realHeight - edgeToEdgeTopInset
|
|
548
526
|
|
|
549
|
-
|
|
550
|
-
setPeekHeight(getDetentHeight(detents[0]), isPresented)
|
|
551
|
-
halfExpandedRatio = minOf(getDetentHeight(detents[1]).toFloat() / screenHeight.toFloat(), MAX_HALF_EXPANDED_RATIO)
|
|
552
|
-
expandedOffset = screenHeight - getDetentHeight(detents[1])
|
|
553
|
-
isFitToContents = expandedOffset == 0
|
|
554
|
-
}
|
|
527
|
+
setPeekHeight(getDetentHeight(detents[0]), isPresented)
|
|
555
528
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
expandedOffset = screenHeight - getDetentHeight(detents[2])
|
|
560
|
-
}
|
|
529
|
+
val halfExpandedDetentHeight = when (detents.size) {
|
|
530
|
+
1 -> peekHeight
|
|
531
|
+
else -> getDetentHeight(detents[1])
|
|
561
532
|
}
|
|
562
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
|
+
|
|
563
542
|
if (oldExpandOffset != expandedOffset || expandedOffset == 0) {
|
|
564
|
-
val offset = if (expandedOffset == 0)
|
|
565
|
-
val newHeight =
|
|
543
|
+
val offset = if (expandedOffset == 0) topInset else 0
|
|
544
|
+
val newHeight = realHeight - expandedOffset - offset
|
|
566
545
|
delegate?.viewControllerDidChangeSize(width, newHeight)
|
|
567
546
|
}
|
|
568
547
|
|
|
@@ -595,7 +574,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
595
574
|
|
|
596
575
|
val cornerRadius = if (sheetCornerRadius < 0) DEFAULT_CORNER_RADIUS.dpToPx() else sheetCornerRadius
|
|
597
576
|
val outerRadii = floatArrayOf(cornerRadius, cornerRadius, cornerRadius, cornerRadius, 0f, 0f, 0f, 0f)
|
|
598
|
-
val backgroundColor =
|
|
577
|
+
val backgroundColor = sheetBackgroundColor ?: getDefaultBackgroundColor()
|
|
599
578
|
|
|
600
579
|
bottomSheet.background = ShapeDrawable(RoundRectShape(outerRadii, null, null)).apply {
|
|
601
580
|
paint.color = backgroundColor
|
|
@@ -636,15 +615,18 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
636
615
|
val bottomSheet = bottomSheetView ?: return
|
|
637
616
|
|
|
638
617
|
val footerHeight = footerView.height
|
|
639
|
-
val
|
|
618
|
+
val sheetHeight = bottomSheet.height
|
|
619
|
+
val sheetTop = bottomSheet.top
|
|
640
620
|
|
|
641
|
-
|
|
621
|
+
// Footer Y relative to sheet: place at bottom of sheet container minus footer height
|
|
622
|
+
var footerY = (sheetHeight - sheetTop - footerHeight).toFloat()
|
|
642
623
|
|
|
643
624
|
if (slideOffset != null && slideOffset < 0) {
|
|
644
625
|
footerY -= (footerHeight * slideOffset)
|
|
645
626
|
}
|
|
646
627
|
|
|
647
|
-
|
|
628
|
+
// Clamp to prevent footer from going above visible area
|
|
629
|
+
val maxAllowedY = (sheetHeight - topInset - footerHeight).toFloat()
|
|
648
630
|
footerView.y = minOf(footerY, maxAllowedY)
|
|
649
631
|
}
|
|
650
632
|
|
|
@@ -674,52 +656,54 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
674
656
|
// MARK: - Position & Drag Handling
|
|
675
657
|
// ====================================================================
|
|
676
658
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
val
|
|
683
|
-
|
|
684
|
-
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
|
|
685
666
|
}
|
|
686
667
|
|
|
687
|
-
private fun
|
|
688
|
-
if (index < 0 || index >= resolvedDetentPositions.size) return
|
|
689
|
-
val positionPx = bottomSheetView?.let { ScreenUtils.getScreenY(it) } ?: return
|
|
690
|
-
if (positionPx in 1..<screenHeight) {
|
|
691
|
-
resolvedDetentPositions[index] = positionPx
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
fun storeCurrentResolvedPosition() {
|
|
696
|
-
storeResolvedPosition(currentDetentIndex)
|
|
697
|
-
}
|
|
668
|
+
private fun getPositionDp(visibleSheetHeight: Int): Float = (screenHeight - visibleSheetHeight).pxToDp()
|
|
698
669
|
|
|
699
|
-
private fun
|
|
700
|
-
if (
|
|
670
|
+
private fun emitChangePositionDelegate(sheetView: View, realtime: Boolean) {
|
|
671
|
+
if (sheetView.top == lastEmittedPositionPx) return
|
|
701
672
|
|
|
702
|
-
|
|
703
|
-
|
|
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
|
+
}
|
|
704
679
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
680
|
+
private fun emitDismissedPosition() {
|
|
681
|
+
val position = screenHeight.pxToDp()
|
|
682
|
+
lastEmittedPositionPx = -1
|
|
683
|
+
delegate?.viewControllerDidChangePosition(-1f, position, 0f, false)
|
|
684
|
+
}
|
|
709
685
|
|
|
710
|
-
|
|
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
|
|
711
694
|
}
|
|
712
695
|
|
|
713
696
|
/** Returns (fromIndex, toIndex, progress) for interpolation, or null if < 2 detents. */
|
|
714
697
|
private fun findSegmentForPosition(positionPx: Int): Triple<Int, Int, Float>? {
|
|
715
|
-
val count =
|
|
698
|
+
val count = detents.size
|
|
716
699
|
if (count < 2) return null
|
|
717
700
|
|
|
718
|
-
val
|
|
719
|
-
val
|
|
701
|
+
val realHeight = ScreenUtils.getRealScreenHeight(reactContext)
|
|
702
|
+
val firstPos = getSheetTopForDetentIndex(0)
|
|
703
|
+
val lastPos = getSheetTopForDetentIndex(count - 1)
|
|
720
704
|
|
|
721
705
|
if (positionPx > firstPos) {
|
|
722
|
-
val range =
|
|
706
|
+
val range = realHeight - firstPos
|
|
723
707
|
val progress = if (range > 0) (positionPx - firstPos).toFloat() / range else 0f
|
|
724
708
|
return Triple(-1, 0, progress)
|
|
725
709
|
}
|
|
@@ -729,8 +713,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
729
713
|
}
|
|
730
714
|
|
|
731
715
|
for (i in 0 until count - 1) {
|
|
732
|
-
val pos =
|
|
733
|
-
val nextPos =
|
|
716
|
+
val pos = getSheetTopForDetentIndex(i)
|
|
717
|
+
val nextPos = getSheetTopForDetentIndex(i + 1)
|
|
734
718
|
|
|
735
719
|
if (positionPx in nextPos..pos) {
|
|
736
720
|
val range = pos - nextPos
|
|
@@ -744,7 +728,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
744
728
|
|
|
745
729
|
/** Returns continuous index (e.g., 0.5 = halfway between detent 0 and 1). */
|
|
746
730
|
private fun getInterpolatedIndexForPosition(positionPx: Int): Float {
|
|
747
|
-
val count =
|
|
731
|
+
val count = detents.size
|
|
748
732
|
if (count == 0) return -1f
|
|
749
733
|
if (count == 1) return 0f
|
|
750
734
|
|
|
@@ -757,7 +741,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
757
741
|
|
|
758
742
|
/** Returns interpolated screen fraction for position. */
|
|
759
743
|
private fun getInterpolatedDetentForPosition(positionPx: Int): Float {
|
|
760
|
-
val count =
|
|
744
|
+
val count = detents.size
|
|
761
745
|
if (count == 0) return 0f
|
|
762
746
|
|
|
763
747
|
val segment = findSegmentForPosition(positionPx) ?: return getDetentValueForIndex(0)
|
|
@@ -785,12 +769,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
785
769
|
}
|
|
786
770
|
|
|
787
771
|
private fun getCurrentDetentInfo(sheetView: View): DetentInfo {
|
|
788
|
-
val
|
|
789
|
-
return DetentInfo(currentDetentIndex,
|
|
772
|
+
val position = getPositionDp(getVisibleSheetHeight(sheetView))
|
|
773
|
+
return DetentInfo(currentDetentIndex, position)
|
|
790
774
|
}
|
|
791
775
|
|
|
792
|
-
private fun getCurrentPositionPx(sheetView: View): Int = ScreenUtils.getScreenY(sheetView)
|
|
793
|
-
|
|
794
776
|
private fun handleDragBegin(sheetView: View) {
|
|
795
777
|
val detentInfo = getCurrentDetentInfo(sheetView)
|
|
796
778
|
val detent = getDetentValueForIndex(detentInfo.index)
|
|
@@ -811,15 +793,17 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
811
793
|
|
|
812
794
|
private fun getDetentHeight(detent: Double): Int {
|
|
813
795
|
val height: Int = if (detent == -1.0) {
|
|
814
|
-
|
|
796
|
+
// Auto height: add bottomInset to content to match iOS behavior
|
|
797
|
+
contentHeight + headerHeight + contentBottomInset
|
|
815
798
|
} else {
|
|
816
799
|
if (detent <= 0.0 || detent > 1.0) {
|
|
817
800
|
throw IllegalArgumentException("TrueSheet: detent fraction ($detent) must be between 0 and 1")
|
|
818
801
|
}
|
|
819
|
-
|
|
802
|
+
// Fractional detent: add bottomInset to match iOS behavior
|
|
803
|
+
(detent * screenHeight).toInt() + contentBottomInset
|
|
820
804
|
}
|
|
821
805
|
|
|
822
|
-
val maxAllowedHeight = screenHeight
|
|
806
|
+
val maxAllowedHeight = screenHeight + contentBottomInset
|
|
823
807
|
return maxSheetHeight?.let { minOf(height, it, maxAllowedHeight) } ?: minOf(height, maxAllowedHeight)
|
|
824
808
|
}
|
|
825
809
|
|
|
@@ -869,15 +853,15 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
869
853
|
}
|
|
870
854
|
|
|
871
855
|
private fun getPositionForDetentIndex(index: Int): Float {
|
|
872
|
-
if (index < 0 || index >= detents.size) return
|
|
856
|
+
if (index < 0 || index >= detents.size) return screenHeight.pxToDp()
|
|
873
857
|
|
|
874
858
|
bottomSheetView?.let {
|
|
875
|
-
val
|
|
876
|
-
if (
|
|
859
|
+
val visibleSheetHeight = getVisibleSheetHeight(it)
|
|
860
|
+
if (visibleSheetHeight > 0) return getPositionDp(visibleSheetHeight)
|
|
877
861
|
}
|
|
878
862
|
|
|
879
863
|
val detentHeight = getDetentHeight(detents[index])
|
|
880
|
-
return (
|
|
864
|
+
return getPositionDp(detentHeight)
|
|
881
865
|
}
|
|
882
866
|
|
|
883
867
|
fun getDetentInfoForIndex(index: Int) = getDetentInfoForState(getStateForDetentIndex(index)) ?: DetentInfo(0, 0f)
|
|
@@ -896,20 +880,18 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
896
880
|
if (w == oldw && h == oldh) return
|
|
897
881
|
|
|
898
882
|
// Skip continuous size changes when fullScreen + edge-to-edge
|
|
899
|
-
if (h +
|
|
883
|
+
if (h + topInset > screenHeight && isExpanded && oldw == w) {
|
|
900
884
|
return
|
|
901
885
|
}
|
|
902
886
|
|
|
903
887
|
val oldScreenHeight = screenHeight
|
|
904
|
-
screenHeight = ScreenUtils.getScreenHeight(reactContext
|
|
888
|
+
screenHeight = ScreenUtils.getScreenHeight(reactContext)
|
|
905
889
|
|
|
906
890
|
if (isPresented && oldScreenHeight != screenHeight && oldScreenHeight > 0) {
|
|
907
891
|
setupSheetDetents()
|
|
908
892
|
this.post {
|
|
909
893
|
positionFooter()
|
|
910
|
-
|
|
911
|
-
val positionPx = bottomSheetView?.let { ScreenUtils.getScreenY(it) } ?: screenHeight
|
|
912
|
-
emitChangePositionDelegate(positionPx, realtime = false)
|
|
894
|
+
bottomSheetView?.let { emitChangePositionDelegate(it, realtime = false) }
|
|
913
895
|
}
|
|
914
896
|
}
|
|
915
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
|
}
|