@lodev09/react-native-true-sheet 3.3.0-beta.2 → 3.3.0-beta.3

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.
@@ -29,7 +29,8 @@ import com.google.android.material.bottomsheet.BottomSheetDialog
29
29
  import com.lodev09.truesheet.core.GrabberOptions
30
30
  import com.lodev09.truesheet.core.RNScreensFragmentObserver
31
31
  import com.lodev09.truesheet.core.TrueSheetGrabberView
32
- import com.lodev09.truesheet.core.TrueSheetKeyboardHandler
32
+ import com.lodev09.truesheet.core.TrueSheetKeyboardObserver
33
+ import com.lodev09.truesheet.core.TrueSheetKeyboardObserverDelegate
33
34
  import com.lodev09.truesheet.utils.ScreenUtils
34
35
 
35
36
  data class DetentInfo(val index: Int, val position: Float)
@@ -224,7 +225,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
224
225
 
225
226
  window?.apply {
226
227
  windowAnimation = attributes.windowAnimations
227
- // Disable default keyboard avoidance - sheet handles it via setupKeyboardHandler
228
+ // Disable default keyboard avoidance - sheet handles it via setupKeyboardObserver
228
229
  setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
229
230
  }
230
231
 
@@ -255,7 +256,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
255
256
  setOnDismissListener(null)
256
257
  }
257
258
 
258
- cleanupKeyboardHandler()
259
+ cleanupKeyboardObserver()
259
260
  cleanupModalObserver()
260
261
  sheetContainer?.removeView(this)
261
262
 
@@ -274,7 +275,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
274
275
  resetAnimation()
275
276
  setupBackground()
276
277
  setupGrabber()
277
- setupKeyboardHandler()
278
+ setupKeyboardObserver()
278
279
 
279
280
  sheetContainer?.post {
280
281
  bottomSheetView?.let { emitChangePositionDelegate(it, realtime = false) }
@@ -568,18 +569,23 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
568
569
  bottomSheet.addView(grabberView)
569
570
  }
570
571
 
571
- private var keyboardHandler: TrueSheetKeyboardHandler? = null
572
+ private var keyboardObserver: TrueSheetKeyboardObserver? = null
572
573
 
573
- /** Sets up keyboard handler for IME transitions. */
574
- fun setupKeyboardHandler() {
574
+ fun setupKeyboardObserver() {
575
575
  val bottomSheet = bottomSheetView ?: return
576
- keyboardHandler = TrueSheetKeyboardHandler(bottomSheet, reactContext) { topInset }
577
- keyboardHandler?.setup()
576
+ keyboardObserver = TrueSheetKeyboardObserver(bottomSheet, reactContext).apply {
577
+ delegate = object : TrueSheetKeyboardObserverDelegate {
578
+ override fun keyboardHeightDidChange(height: Int) {
579
+ setupSheetDetents()
580
+ }
581
+ }
582
+ start()
583
+ }
578
584
  }
579
585
 
580
- fun cleanupKeyboardHandler() {
581
- keyboardHandler?.cleanup()
582
- keyboardHandler = null
586
+ fun cleanupKeyboardObserver() {
587
+ keyboardObserver?.stop()
588
+ keyboardObserver = null
583
589
  }
584
590
 
585
591
  fun setupBackground() {
@@ -632,7 +638,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
632
638
  val sheetTop = bottomSheet.top
633
639
 
634
640
  // Footer Y relative to sheet: place at bottom of sheet container minus footer height
635
- var footerY = (sheetHeight - sheetTop - footerHeight).toFloat()
641
+ var footerY = (sheetHeight - sheetTop - footerHeight - keyboardHeight).toFloat()
636
642
 
637
643
  if (slideOffset != null && slideOffset < 0) {
638
644
  footerY -= (footerHeight * slideOffset)
@@ -806,16 +812,17 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
806
812
  // MARK: - Detent Calculations
807
813
  // ====================================================================
808
814
 
815
+ private val keyboardHeight: Int
816
+ get() = keyboardObserver?.currentHeight ?: 0
817
+
809
818
  private fun getDetentHeight(detent: Double): Int {
810
- val height: Int = if (detent == -1.0) {
811
- // Auto height: add bottomInset to content to match iOS behavior
812
- contentHeight + headerHeight + contentBottomInset
819
+ val height = if (detent == -1.0) {
820
+ contentHeight + headerHeight + contentBottomInset + keyboardHeight
813
821
  } else {
814
822
  if (detent <= 0.0 || detent > 1.0) {
815
823
  throw IllegalArgumentException("TrueSheet: detent fraction ($detent) must be between 0 and 1")
816
824
  }
817
- // Fractional detent: add bottomInset to match iOS behavior
818
- (detent * screenHeight).toInt() + contentBottomInset
825
+ (detent * screenHeight).toInt() + contentBottomInset + keyboardHeight
819
826
  }
820
827
 
821
828
  val maxAllowedHeight = screenHeight + contentBottomInset
@@ -9,25 +9,25 @@ import androidx.core.view.WindowInsetsAnimationCompat
9
9
  import androidx.core.view.WindowInsetsCompat
10
10
  import com.facebook.react.uimanager.ThemedReactContext
11
11
 
12
+ interface TrueSheetKeyboardObserverDelegate {
13
+ fun keyboardHeightDidChange(height: Int)
14
+ }
15
+
12
16
  /**
13
- * Handles keyboard (IME) for sheet translation.
14
- * Uses WindowInsetsAnimationCompat for smooth animation on API 30+,
15
- * falls back to ViewTreeObserver on Activity's decor view for API 29 and below.
16
- *
17
- * @param targetView The view to translate (typically the bottom sheet)
18
- * @param reactContext The React context to get the current activity
19
- * @param topInset The top safe area inset to respect
17
+ * Tracks keyboard height and notifies delegate on changes.
18
+ * Uses WindowInsetsAnimationCompat on API 30+, ViewTreeObserver fallback on older versions.
20
19
  */
21
- class TrueSheetKeyboardHandler(
22
- private val targetView: View,
23
- private val reactContext: ThemedReactContext,
24
- private val topInset: () -> Int
25
- ) {
20
+ class TrueSheetKeyboardObserver(private val targetView: View, private val reactContext: ThemedReactContext) {
21
+
22
+ var delegate: TrueSheetKeyboardObserverDelegate? = null
23
+
24
+ var currentHeight: Int = 0
25
+ private set
26
26
 
27
27
  private var globalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
28
28
  private var activityRootView: View? = null
29
29
 
30
- fun setup() {
30
+ fun start() {
31
31
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
32
32
  setupAnimationCallback()
33
33
  } else {
@@ -35,7 +35,7 @@ class TrueSheetKeyboardHandler(
35
35
  }
36
36
  }
37
37
 
38
- fun cleanup() {
38
+ fun stop() {
39
39
  globalLayoutListener?.let { listener ->
40
40
  activityRootView?.viewTreeObserver?.removeOnGlobalLayoutListener(listener)
41
41
  globalLayoutListener = null
@@ -44,35 +44,31 @@ class TrueSheetKeyboardHandler(
44
44
  ViewCompat.setWindowInsetsAnimationCallback(targetView, null)
45
45
  }
46
46
 
47
- private fun applyTranslation(imeHeight: Int) {
48
- // Cap translation so sheet doesn't move beyond screen bounds
49
- val maxTranslation = maxOf(0, targetView.top - topInset())
50
- val translation = minOf(imeHeight, maxTranslation)
51
- targetView.translationY = -translation.toFloat()
47
+ private fun updateHeight(height: Int) {
48
+ if (currentHeight != height) {
49
+ currentHeight = height
50
+ delegate?.keyboardHeightDidChange(height)
51
+ }
52
52
  }
53
53
 
54
- /** API 30+ smooth keyboard animation */
54
+ private fun getKeyboardHeight(insets: WindowInsetsCompat?): Int = insets?.getInsets(WindowInsetsCompat.Type.ime())?.bottom ?: 0
55
+
55
56
  private fun setupAnimationCallback() {
56
57
  ViewCompat.setWindowInsetsAnimationCallback(
57
58
  targetView,
58
59
  object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
59
- private var startImeHeight = 0
60
- private var endImeHeight = 0
61
-
62
- private fun getKeyboardHeight(rootInsets: WindowInsetsCompat?): Int {
63
- if (rootInsets == null) return 0
64
- return rootInsets.getInsets(WindowInsetsCompat.Type.ime()).bottom
65
- }
60
+ private var startHeight = 0
61
+ private var endHeight = 0
66
62
 
67
63
  override fun onPrepare(animation: WindowInsetsAnimationCompat) {
68
- startImeHeight = getKeyboardHeight(ViewCompat.getRootWindowInsets(targetView))
64
+ startHeight = getKeyboardHeight(ViewCompat.getRootWindowInsets(targetView))
69
65
  }
70
66
 
71
67
  override fun onStart(
72
68
  animation: WindowInsetsAnimationCompat,
73
69
  bounds: WindowInsetsAnimationCompat.BoundsCompat
74
70
  ): WindowInsetsAnimationCompat.BoundsCompat {
75
- endImeHeight = getKeyboardHeight(ViewCompat.getRootWindowInsets(targetView))
71
+ endHeight = getKeyboardHeight(ViewCompat.getRootWindowInsets(targetView))
76
72
  return bounds
77
73
  }
78
74
 
@@ -82,23 +78,21 @@ class TrueSheetKeyboardHandler(
82
78
  } ?: return insets
83
79
 
84
80
  val fraction = imeAnimation.interpolatedFraction
85
- val currentImeHeight = (startImeHeight + (endImeHeight - startImeHeight) * fraction).toInt()
86
- applyTranslation(currentImeHeight)
81
+ val currentHeight = (startHeight + (endHeight - startHeight) * fraction).toInt()
82
+ updateHeight(currentHeight)
87
83
 
88
84
  return insets
89
85
  }
90
86
 
91
87
  override fun onEnd(animation: WindowInsetsAnimationCompat) {
92
- applyTranslation(getKeyboardHeight(ViewCompat.getRootWindowInsets(targetView)))
88
+ updateHeight(getKeyboardHeight(ViewCompat.getRootWindowInsets(targetView)))
93
89
  }
94
90
  }
95
91
  )
96
92
  }
97
93
 
98
- /** API 29 and below fallback using ViewTreeObserver on Activity's root view */
99
94
  private fun setupLegacyListener() {
100
95
  val rootView = reactContext.currentActivity?.window?.decorView?.rootView ?: return
101
-
102
96
  activityRootView = rootView
103
97
 
104
98
  globalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener {
@@ -108,11 +102,7 @@ class TrueSheetKeyboardHandler(
108
102
  val screenHeight = rootView.height
109
103
  val keyboardHeight = screenHeight - rect.bottom
110
104
 
111
- if (keyboardHeight > screenHeight * 0.15) {
112
- applyTranslation(keyboardHeight)
113
- } else {
114
- applyTranslation(0)
115
- }
105
+ updateHeight(if (keyboardHeight > screenHeight * 0.15) keyboardHeight else 0)
116
106
  }
117
107
 
118
108
  rootView.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lodev09/react-native-true-sheet",
3
- "version": "3.3.0-beta.2",
3
+ "version": "3.3.0-beta.3",
4
4
  "description": "The true native bottom sheet experience for your React Native Apps.",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./lib/module/index.js",