@lodev09/react-native-true-sheet 3.5.0-beta.0 → 3.5.0-beta.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.
@@ -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
@@ -110,9 +111,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
110
111
  override val headerHeight: Int
111
112
  get() = containerView?.headerHeight ?: 0
112
113
 
113
- override val keyboardHeight: Int
114
- get() = keyboardObserver?.currentHeight ?: 0
115
-
116
114
  // ====================================================================
117
115
  // MARK: - State
118
116
  // ====================================================================
@@ -120,7 +118,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
120
118
  /** Interaction state for the sheet */
121
119
  private sealed class InteractionState {
122
120
  data object Idle : InteractionState()
123
- data class Dragging(val startTop: Int, val startKeyboardHeight: Int, val shouldDismissKeyboard: Boolean = false) : InteractionState()
121
+ data class Dragging(val startTop: Int) : InteractionState()
124
122
  data object Reconfiguring : InteractionState()
125
123
  }
126
124
 
@@ -141,6 +139,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
141
139
  private var lastStateWidth: Int = 0
142
140
  private var lastStateHeight: Int = 0
143
141
  private var lastEmittedPositionPx: Int = -1
142
+ private var detentIndexBeforeKeyboard: Int = -1
143
+ private var isKeyboardTransitioning: Boolean = false
144
144
 
145
145
  var presentPromise: (() -> Unit)? = null
146
146
  var dismissPromise: (() -> Unit)? = null
@@ -202,6 +202,12 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
202
202
  // MARK: - Computed Properties
203
203
  // ====================================================================
204
204
 
205
+ override val keyboardInset: Int
206
+ get() = keyboardObserver?.targetHeight ?: 0
207
+
208
+ private val currentKeyboardInset: Int
209
+ get() = keyboardObserver?.currentHeight ?: 0
210
+
205
211
  val bottomInset: Int
206
212
  get() = if (edgeToEdgeEnabled) ScreenUtils.getInsets(reactContext).bottom else 0
207
213
 
@@ -349,8 +355,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
349
355
  else -> { }
350
356
  }
351
357
 
352
- positionFooter(slideOffset)
353
- updateDimAmount(sheetView.top)
358
+ if (!isKeyboardTransitioning) {
359
+ positionFooter(slideOffset)
360
+ updateDimAmount(sheetView.top)
361
+ }
354
362
  }
355
363
 
356
364
  override fun onStateChanged(sheetView: View, newState: Int) {
@@ -387,17 +395,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
387
395
 
388
396
  when (interactionState) {
389
397
  is InteractionState.Dragging -> {
390
- val draggingState = interactionState as InteractionState.Dragging
391
398
  val detent = detentCalculator.getDetentValueForIndex(detentInfo.index)
392
399
  delegate?.viewControllerDidDragEnd(detentInfo.index, detentInfo.position, detent)
393
400
 
394
- // Dismiss keyboard if dragged past threshold
395
- if (draggingState.shouldDismissKeyboard) {
396
- val imm = reactContext.getSystemService(android.content.Context.INPUT_METHOD_SERVICE)
397
- as? android.view.inputmethod.InputMethodManager
398
- imm?.hideSoftInputFromWindow((dialog?.currentFocus ?: bottomSheetView)?.windowToken, 0)
399
- }
400
-
401
401
  if (detentInfo.index != currentDetentIndex) {
402
402
  presentPromise?.invoke()
403
403
  presentPromise = null
@@ -412,8 +412,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
412
412
  else -> {
413
413
  if (detentInfo.index != currentDetentIndex) {
414
414
  currentDetentIndex = detentInfo.index
415
- val detent = detentCalculator.getDetentValueForIndex(detentInfo.index)
416
- delegate?.viewControllerDidChangeDetent(detentInfo.index, detentInfo.position, detent)
415
+ if (!isKeyboardTransitioning) {
416
+ val detent = detentCalculator.getDetentValueForIndex(detentInfo.index)
417
+ delegate?.viewControllerDidChangeDetent(detentInfo.index, detentInfo.position, detent)
418
+ }
417
419
  }
418
420
  }
419
421
  }
@@ -427,23 +429,18 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
427
429
  isDialogVisible = false
428
430
  wasHiddenByModal = true
429
431
 
430
- bottomSheetView?.animate()?.alpha(0f)?.setDuration(200)?.start()
432
+ dialog?.window?.setWindowAnimations(com.lodev09.truesheet.R.style.TrueSheetFastFadeOut)
433
+ dialog?.window?.decorView?.visibility = GONE
431
434
  dimView?.visibility = INVISIBLE
432
435
  parentDimView?.visibility = INVISIBLE
433
- dialog?.window?.setFlags(
434
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
435
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
436
- )
437
436
  }
438
437
  },
439
438
  onModalWillDismiss = {
440
439
  if (isPresented && wasHiddenByModal) {
441
440
  isDialogVisible = true
442
441
 
443
- dialog?.window?.clearFlags(
444
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
445
- )
446
- bottomSheetView?.alpha = 1f
442
+ dialog?.window?.setWindowAnimations(0)
443
+ dialog?.window?.decorView?.visibility = VISIBLE
447
444
  dimView?.visibility = VISIBLE
448
445
  parentDimView?.visibility = VISIBLE
449
446
  }
@@ -689,15 +686,29 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
689
686
  val bottomSheet = bottomSheetView ?: return
690
687
  keyboardObserver = TrueSheetKeyboardObserver(bottomSheet, reactContext).apply {
691
688
  delegate = object : TrueSheetKeyboardObserverDelegate {
692
- override fun keyboardWillShow(height: Int) {}
693
-
694
- override fun keyboardWillHide() {}
689
+ override fun keyboardWillShow(height: Int) {
690
+ if (!shouldHandleKeyboard()) return
691
+ detentIndexBeforeKeyboard = currentDetentIndex
692
+ isKeyboardTransitioning = true
693
+ setupSheetDetents()
694
+ setStateForDetentIndex(detents.size - 1)
695
+ }
695
696
 
696
- override fun keyboardDidChangeHeight(height: Int) {
697
+ override fun keyboardWillHide() {
697
698
  if (!shouldHandleKeyboard()) return
698
699
  setupSheetDetents()
699
- positionFooter()
700
+ if (detentIndexBeforeKeyboard >= 0) {
701
+ setStateForDetentIndex(detentIndexBeforeKeyboard)
702
+ detentIndexBeforeKeyboard = -1
703
+ }
700
704
  }
705
+
706
+ override fun keyboardDidHide() {
707
+ if (!shouldHandleKeyboard()) return
708
+ isKeyboardTransitioning = false
709
+ }
710
+
711
+ override fun keyboardDidChangeHeight(height: Int) {}
701
712
  }
702
713
  start()
703
714
  }
@@ -784,7 +795,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
784
795
 
785
796
  fun updateDimAmount(sheetTop: Int? = null) {
786
797
  if (!dimmed) return
787
- val top = sheetTop ?: bottomSheetView?.top ?: return
798
+ val top = (sheetTop ?: bottomSheetView?.top ?: return) + currentKeyboardInset
788
799
  dimView?.interpolateAlpha(top, dimmedDetentIndex, detentCalculator::getSheetTopForDetentIndex)
789
800
  parentDimView?.interpolateAlpha(top, dimmedDetentIndex, detentCalculator::getSheetTopForDetentIndex)
790
801
  }
@@ -797,7 +808,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
797
808
  val sheetHeight = bottomSheet.height
798
809
  val sheetTop = bottomSheet.top
799
810
 
800
- var footerY = (sheetHeight - sheetTop - footerHeight - keyboardHeight).toFloat()
811
+ var footerY = (sheetHeight - sheetTop - footerHeight - currentKeyboardInset).toFloat()
801
812
  if (slideOffset != null && slideOffset < 0) {
802
813
  footerY -= (footerHeight * slideOffset)
803
814
  }
@@ -832,31 +843,14 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
832
843
  val position = detentCalculator.getPositionDp(detentCalculator.getVisibleSheetHeight(sheetView.top))
833
844
  val detent = detentCalculator.getDetentValueForIndex(currentDetentIndex)
834
845
  delegate?.viewControllerDidDragBegin(currentDetentIndex, position, detent)
835
- interactionState = InteractionState.Dragging(
836
- startTop = sheetView.top,
837
- startKeyboardHeight = keyboardHeight
838
- )
846
+ interactionState = InteractionState.Dragging(startTop = sheetView.top)
839
847
  }
840
848
 
841
849
  private fun handleDragChange(sheetView: View) {
842
- val draggingState = interactionState as? InteractionState.Dragging ?: return
850
+ if (interactionState !is InteractionState.Dragging) return
843
851
  val position = detentCalculator.getPositionDp(detentCalculator.getVisibleSheetHeight(sheetView.top))
844
852
  val detent = detentCalculator.getDetentValueForIndex(currentDetentIndex)
845
853
  delegate?.viewControllerDidDragChange(currentDetentIndex, position, detent)
846
-
847
- // Dismiss keyboard if dragged below original position (without keyboard)
848
- if (draggingState.startKeyboardHeight > 0) {
849
- val detentTopWithoutKeyboard = getExpectedSheetTop(currentDetentIndex) + draggingState.startKeyboardHeight
850
- val shouldDismiss = sheetView.top >= detentTopWithoutKeyboard
851
-
852
- if (shouldDismiss != draggingState.shouldDismissKeyboard) {
853
- android.util.Log.d(
854
- TAG_NAME,
855
- "shouldDismissKeyboard changed to: $shouldDismiss (currentTop: ${sheetView.top}, detentTop: $detentTopWithoutKeyboard)"
856
- )
857
- interactionState = draggingState.copy(shouldDismissKeyboard = shouldDismiss)
858
- }
859
- }
860
854
  }
861
855
 
862
856
  // ====================================================================
@@ -14,7 +14,7 @@ interface TrueSheetDetentMeasurements {
14
14
  val headerHeight: Int
15
15
  val contentBottomInset: Int
16
16
  val maxSheetHeight: Int?
17
- val keyboardHeight: Int
17
+ val keyboardInset: Int
18
18
  }
19
19
 
20
20
  /**
@@ -30,7 +30,7 @@ class TrueSheetDetentCalculator(private val measurements: TrueSheetDetentMeasure
30
30
  private val headerHeight: Int get() = measurements.headerHeight
31
31
  private val contentBottomInset: Int get() = measurements.contentBottomInset
32
32
  private val maxSheetHeight: Int? get() = measurements.maxSheetHeight
33
- private val keyboardHeight: Int get() = measurements.keyboardHeight
33
+ private val keyboardInset: Int get() = measurements.keyboardInset
34
34
 
35
35
  /**
36
36
  * Calculate the height in pixels for a given detent value.
@@ -46,7 +46,7 @@ class TrueSheetDetentCalculator(private val measurements: TrueSheetDetentMeasure
46
46
  (detent * screenHeight).toInt() + contentBottomInset
47
47
  }
48
48
 
49
- val height = baseHeight + keyboardHeight
49
+ val height = baseHeight + keyboardInset
50
50
  val maxAllowedHeight = screenHeight + contentBottomInset
51
51
  return maxSheetHeight?.let { minOf(height, it, maxAllowedHeight) } ?: minOf(height, maxAllowedHeight)
52
52
  }
@@ -12,6 +12,7 @@ import com.facebook.react.uimanager.ThemedReactContext
12
12
  interface TrueSheetKeyboardObserverDelegate {
13
13
  fun keyboardWillShow(height: Int)
14
14
  fun keyboardWillHide()
15
+ fun keyboardDidHide()
15
16
  fun keyboardDidChangeHeight(height: Int)
16
17
  }
17
18
 
@@ -26,6 +27,10 @@ class TrueSheetKeyboardObserver(private val targetView: View, private val reactC
26
27
  var currentHeight: Int = 0
27
28
  private set
28
29
 
30
+ var targetHeight: Int = 0
31
+ private set
32
+
33
+ private var isHiding: Boolean = false
29
34
  private var globalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
30
35
  private var activityRootView: View? = null
31
36
 
@@ -72,9 +77,11 @@ class TrueSheetKeyboardObserver(private val targetView: View, private val reactC
72
77
  bounds: WindowInsetsAnimationCompat.BoundsCompat
73
78
  ): WindowInsetsAnimationCompat.BoundsCompat {
74
79
  endHeight = getKeyboardHeight(ViewCompat.getRootWindowInsets(targetView))
80
+ targetHeight = endHeight
81
+ isHiding = endHeight < startHeight
75
82
  if (endHeight > startHeight) {
76
83
  delegate?.keyboardWillShow(endHeight)
77
- } else if (endHeight < startHeight) {
84
+ } else if (isHiding) {
78
85
  delegate?.keyboardWillHide()
79
86
  }
80
87
  return bounds
@@ -94,6 +101,10 @@ class TrueSheetKeyboardObserver(private val targetView: View, private val reactC
94
101
  override fun onEnd(animation: WindowInsetsAnimationCompat) {
95
102
  val finalHeight = getKeyboardHeight(ViewCompat.getRootWindowInsets(targetView))
96
103
  updateHeight(startHeight, finalHeight, 1f)
104
+ if (isHiding) {
105
+ delegate?.keyboardDidHide()
106
+ isHiding = false
107
+ }
97
108
  }
98
109
  }
99
110
  )
@@ -114,12 +125,16 @@ class TrueSheetKeyboardObserver(private val targetView: View, private val reactC
114
125
  val previousHeight = currentHeight
115
126
 
116
127
  if (previousHeight != newHeight) {
128
+ targetHeight = newHeight
117
129
  if (newHeight > previousHeight) {
118
130
  delegate?.keyboardWillShow(newHeight)
119
131
  } else if (newHeight < previousHeight) {
120
132
  delegate?.keyboardWillHide()
121
133
  }
122
134
  updateHeight(previousHeight, newHeight, 1f)
135
+ if (newHeight == 0 && previousHeight > 0) {
136
+ delegate?.keyboardDidHide()
137
+ }
123
138
  }
124
139
  }
125
140
 
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <alpha xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:fromAlpha="1.0"
4
+ android:toAlpha="0.0"
5
+ android:duration="100"
6
+ android:interpolator="@android:interpolator/accelerate_quad" />
@@ -6,6 +6,12 @@
6
6
  <item name="android:windowExitAnimation">@null</item>
7
7
  </style>
8
8
 
9
+ <!-- Fast fade out animation for hiding sheet when rn-screen is presented -->
10
+ <style name="TrueSheetFastFadeOut" parent="Animation.AppCompat.Dialog">
11
+ <item name="android:windowEnterAnimation">@null</item>
12
+ <item name="android:windowExitAnimation">@anim/fast_fade_out</item>
13
+ </style>
14
+
9
15
  <!-- Default BottomSheetDialog style with programmatic animations -->
10
16
  <style name="TrueSheetDialog" parent="Theme.Design.Light.BottomSheetDialog">
11
17
  <item name="android:windowAnimationStyle">@style/TrueSheetNoAnimation</item>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lodev09/react-native-true-sheet",
3
- "version": "3.5.0-beta.0",
3
+ "version": "3.5.0-beta.1",
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",