@lodev09/react-native-true-sheet 3.6.8 → 3.6.10
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 +11 -4
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +21 -9
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +1 -1
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +91 -7
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetKeyboardObserver.kt +18 -0
- package/ios/TrueSheetView.mm +6 -5
- package/package.json +1 -1
|
@@ -135,6 +135,11 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
135
135
|
val child = getChildAt(index)
|
|
136
136
|
if (child is TrueSheetContainerView) {
|
|
137
137
|
child.delegate = null
|
|
138
|
+
|
|
139
|
+
// Dismiss the sheet when container is removed
|
|
140
|
+
if (viewController.isPresented) {
|
|
141
|
+
viewController.dismiss(animated = false)
|
|
142
|
+
}
|
|
138
143
|
}
|
|
139
144
|
viewController.removeView(child)
|
|
140
145
|
}
|
|
@@ -159,14 +164,12 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
159
164
|
fun onDropInstance() {
|
|
160
165
|
reactContext.removeLifecycleEventListener(this)
|
|
161
166
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
167
|
+
viewController.dismiss()
|
|
168
|
+
viewController.delegate = null
|
|
165
169
|
|
|
166
170
|
TrueSheetModule.unregisterView(id)
|
|
167
171
|
TrueSheetStackManager.removeSheet(this)
|
|
168
172
|
|
|
169
|
-
viewController.delegate = null
|
|
170
173
|
didInitiallyPresent = false
|
|
171
174
|
}
|
|
172
175
|
|
|
@@ -245,6 +248,10 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
245
248
|
viewController.insetAdjustment = insetAdjustment
|
|
246
249
|
}
|
|
247
250
|
|
|
251
|
+
fun setScrollable(scrollable: Boolean) {
|
|
252
|
+
viewController.scrollable = scrollable
|
|
253
|
+
}
|
|
254
|
+
|
|
248
255
|
// ==================== State Management ====================
|
|
249
256
|
|
|
250
257
|
/**
|
|
@@ -139,7 +139,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
139
139
|
|
|
140
140
|
// Keyboard State
|
|
141
141
|
private var detentIndexBeforeKeyboard: Int = -1
|
|
142
|
-
private var isKeyboardTransitioning: Boolean = false
|
|
143
142
|
|
|
144
143
|
// Promises
|
|
145
144
|
var presentPromise: (() -> Unit)? = null
|
|
@@ -171,6 +170,11 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
171
170
|
override var grabberOptions: GrabberOptions? = null
|
|
172
171
|
override var sheetBackgroundColor: Int? = null
|
|
173
172
|
var insetAdjustment: String = "automatic"
|
|
173
|
+
var scrollable: Boolean = false
|
|
174
|
+
set(value) {
|
|
175
|
+
field = value
|
|
176
|
+
coordinatorLayout?.scrollable = value
|
|
177
|
+
}
|
|
174
178
|
|
|
175
179
|
override var sheetCornerRadius: Float = DEFAULT_CORNER_RADIUS.dpToPx()
|
|
176
180
|
set(value) {
|
|
@@ -238,6 +242,14 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
238
242
|
private val currentKeyboardInset: Int
|
|
239
243
|
get() = keyboardObserver?.currentHeight ?: 0
|
|
240
244
|
|
|
245
|
+
private val isKeyboardTransitioning: Boolean
|
|
246
|
+
get() = keyboardObserver?.isTransitioning ?: false
|
|
247
|
+
|
|
248
|
+
private fun isFocusedViewWithinSheet(): Boolean {
|
|
249
|
+
val sheet = sheetView ?: return false
|
|
250
|
+
return keyboardObserver?.isFocusedViewWithinSheet(sheet) ?: false
|
|
251
|
+
}
|
|
252
|
+
|
|
241
253
|
val bottomInset: Int
|
|
242
254
|
get() = if (edgeToEdgeEnabled) ScreenUtils.getInsets(reactContext).bottom else 0
|
|
243
255
|
|
|
@@ -290,6 +302,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
290
302
|
// Create coordinator layout
|
|
291
303
|
coordinatorLayout = TrueSheetCoordinatorLayout(reactContext).apply {
|
|
292
304
|
delegate = this@TrueSheetViewController
|
|
305
|
+
scrollable = this@TrueSheetViewController.scrollable
|
|
293
306
|
}
|
|
294
307
|
|
|
295
308
|
sheetView = TrueSheetBottomSheetView(reactContext).apply {
|
|
@@ -320,7 +333,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
320
333
|
isPresented = false
|
|
321
334
|
isSheetVisible = false
|
|
322
335
|
wasHiddenByModal = false
|
|
323
|
-
isKeyboardTransitioning = false
|
|
324
336
|
isPresentAnimating = false
|
|
325
337
|
lastEmittedPositionPx = -1
|
|
326
338
|
detentIndexBeforeKeyboard = -1
|
|
@@ -872,9 +884,11 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
872
884
|
// MARK: - Keyboard Handling
|
|
873
885
|
// =============================================================================
|
|
874
886
|
|
|
875
|
-
private fun shouldHandleKeyboard(): Boolean {
|
|
887
|
+
private fun shouldHandleKeyboard(checkFocus: Boolean = true): Boolean {
|
|
876
888
|
if (wasHiddenByModal) return false
|
|
877
|
-
return
|
|
889
|
+
if (!isTopmostSheet) return false
|
|
890
|
+
if (checkFocus && !isFocusedViewWithinSheet()) return false
|
|
891
|
+
return true
|
|
878
892
|
}
|
|
879
893
|
|
|
880
894
|
fun setupKeyboardObserver() {
|
|
@@ -886,7 +900,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
886
900
|
keyboardObserver = TrueSheetKeyboardObserver(coordinator, reactContext).apply {
|
|
887
901
|
delegate = object : TrueSheetKeyboardObserverDelegate {
|
|
888
902
|
override fun keyboardWillShow(height: Int) {
|
|
889
|
-
isKeyboardTransitioning = true
|
|
890
903
|
if (!shouldHandleKeyboard()) return
|
|
891
904
|
detentIndexBeforeKeyboard = currentDetentIndex
|
|
892
905
|
setupSheetDetents()
|
|
@@ -894,7 +907,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
894
907
|
}
|
|
895
908
|
|
|
896
909
|
override fun keyboardWillHide() {
|
|
897
|
-
if (!shouldHandleKeyboard()) return
|
|
910
|
+
if (!shouldHandleKeyboard(checkFocus = false)) return
|
|
911
|
+
|
|
898
912
|
setupSheetDetents()
|
|
899
913
|
if (!isDismissing && detentIndexBeforeKeyboard >= 0) {
|
|
900
914
|
setStateForDetentIndex(detentIndexBeforeKeyboard)
|
|
@@ -902,9 +916,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
902
916
|
}
|
|
903
917
|
}
|
|
904
918
|
|
|
905
|
-
override fun keyboardDidHide() {
|
|
906
|
-
isKeyboardTransitioning = false
|
|
907
|
-
}
|
|
919
|
+
override fun keyboardDidHide() {}
|
|
908
920
|
|
|
909
921
|
override fun keyboardDidChangeHeight(height: Int) {
|
|
910
922
|
if (!shouldHandleKeyboard()) return
|
|
@@ -190,7 +190,7 @@ class TrueSheetViewManager :
|
|
|
190
190
|
|
|
191
191
|
@ReactProp(name = "scrollable", defaultBoolean = false)
|
|
192
192
|
override fun setScrollable(view: TrueSheetView, value: Boolean) {
|
|
193
|
-
|
|
193
|
+
view.setScrollable(value)
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
@ReactProp(name = "pageSizing", defaultBoolean = true)
|
|
@@ -2,7 +2,10 @@ package com.lodev09.truesheet.core
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
-
import android.view.
|
|
5
|
+
import android.view.MotionEvent
|
|
6
|
+
import android.view.ViewConfiguration
|
|
7
|
+
import android.view.ViewGroup
|
|
8
|
+
import android.widget.ScrollView
|
|
6
9
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
7
10
|
import com.facebook.react.uimanager.PointerEvents
|
|
8
11
|
import com.facebook.react.uimanager.ReactPointerEventsView
|
|
@@ -22,15 +25,19 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
22
25
|
ReactPointerEventsView {
|
|
23
26
|
|
|
24
27
|
var delegate: TrueSheetCoordinatorLayoutDelegate? = null
|
|
28
|
+
var scrollable: Boolean = false
|
|
29
|
+
|
|
30
|
+
private val touchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop
|
|
31
|
+
private var dragging = false
|
|
32
|
+
private var initialY = 0f
|
|
33
|
+
private var activePointerId = 0
|
|
25
34
|
|
|
26
35
|
init {
|
|
27
|
-
// Fill the entire screen
|
|
28
36
|
layoutParams = LayoutParams(
|
|
29
37
|
LayoutParams.MATCH_PARENT,
|
|
30
38
|
LayoutParams.MATCH_PARENT
|
|
31
39
|
)
|
|
32
40
|
|
|
33
|
-
// Ensure we don't clip the sheet during animations
|
|
34
41
|
clipChildren = false
|
|
35
42
|
clipToPadding = false
|
|
36
43
|
}
|
|
@@ -46,10 +53,87 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
46
53
|
delegate?.coordinatorLayoutDidLayout(changed)
|
|
47
54
|
}
|
|
48
55
|
|
|
49
|
-
/**
|
|
50
|
-
* Allow pointer events to pass through to underlying views.
|
|
51
|
-
* The DimView and BottomSheetView handle their own touch interception.
|
|
52
|
-
*/
|
|
53
56
|
override val pointerEvents: PointerEvents
|
|
54
57
|
get() = PointerEvents.BOX_NONE
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Intercepts touch events for ScrollViews that can't scroll (content < viewport),
|
|
61
|
+
* allowing the sheet to be dragged in these cases.
|
|
62
|
+
*
|
|
63
|
+
* TODO: Remove this workaround once NestedScrollView is merged into react-native core.
|
|
64
|
+
* See: https://github.com/facebook/react-native/pull/44099
|
|
65
|
+
*/
|
|
66
|
+
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
|
|
67
|
+
if (!scrollable) {
|
|
68
|
+
return super.onInterceptTouchEvent(ev)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
val scrollView = findScrollView(this)
|
|
72
|
+
val cannotScroll = scrollView != null &&
|
|
73
|
+
scrollView.scrollY == 0 &&
|
|
74
|
+
!scrollView.canScrollVertically(1)
|
|
75
|
+
|
|
76
|
+
if (cannotScroll) {
|
|
77
|
+
when (ev.action and MotionEvent.ACTION_MASK) {
|
|
78
|
+
MotionEvent.ACTION_DOWN -> {
|
|
79
|
+
dragging = false
|
|
80
|
+
initialY = ev.y
|
|
81
|
+
activePointerId = ev.getPointerId(0)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
MotionEvent.ACTION_MOVE -> {
|
|
85
|
+
val pointerIndex = ev.findPointerIndex(activePointerId)
|
|
86
|
+
if (pointerIndex != -1) {
|
|
87
|
+
val y = ev.getY(pointerIndex)
|
|
88
|
+
val deltaY = initialY - y
|
|
89
|
+
if (kotlin.math.abs(deltaY) > touchSlop) {
|
|
90
|
+
dragging = true
|
|
91
|
+
parent?.requestDisallowInterceptTouchEvent(true)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
MotionEvent.ACTION_UP,
|
|
97
|
+
MotionEvent.ACTION_CANCEL -> {
|
|
98
|
+
dragging = false
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
dragging = false
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return dragging || super.onInterceptTouchEvent(ev)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@SuppressLint("ClickableViewAccessibility")
|
|
109
|
+
override fun onTouchEvent(ev: MotionEvent): Boolean {
|
|
110
|
+
if (dragging) {
|
|
111
|
+
when (ev.action and MotionEvent.ACTION_MASK) {
|
|
112
|
+
MotionEvent.ACTION_UP,
|
|
113
|
+
MotionEvent.ACTION_CANCEL -> {
|
|
114
|
+
dragging = false
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Let parent CoordinatorLayout handle the touch for BottomSheetBehavior
|
|
118
|
+
return super.onTouchEvent(ev)
|
|
119
|
+
}
|
|
120
|
+
return super.onTouchEvent(ev)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private fun findScrollView(view: android.view.View): ScrollView? {
|
|
124
|
+
if (view is ScrollView) {
|
|
125
|
+
return view
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (view is ViewGroup) {
|
|
129
|
+
for (i in 0 until view.childCount) {
|
|
130
|
+
val scrollView = findScrollView(view.getChildAt(i))
|
|
131
|
+
if (scrollView != null) {
|
|
132
|
+
return scrollView
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
55
139
|
}
|
|
@@ -31,6 +31,20 @@ class TrueSheetKeyboardObserver(private val targetView: View, private val reactC
|
|
|
31
31
|
var targetHeight: Int = 0
|
|
32
32
|
private set
|
|
33
33
|
|
|
34
|
+
var isTransitioning: Boolean = false
|
|
35
|
+
private set
|
|
36
|
+
|
|
37
|
+
fun isFocusedViewWithinSheet(sheetView: View): Boolean {
|
|
38
|
+
val focusedView = reactContext.currentActivity?.currentFocus ?: return false
|
|
39
|
+
var current: View? = focusedView
|
|
40
|
+
while (current != null && current !== targetView) {
|
|
41
|
+
if (current === sheetView) return true
|
|
42
|
+
val parent = current.parent
|
|
43
|
+
current = if (parent is View) parent else null
|
|
44
|
+
}
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
|
|
34
48
|
private var isHiding: Boolean = false
|
|
35
49
|
private var globalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
|
|
36
50
|
private var activityRootView: View? = null
|
|
@@ -80,6 +94,7 @@ class TrueSheetKeyboardObserver(private val targetView: View, private val reactC
|
|
|
80
94
|
endHeight = getKeyboardHeight()
|
|
81
95
|
targetHeight = endHeight
|
|
82
96
|
isHiding = endHeight < startHeight
|
|
97
|
+
isTransitioning = true
|
|
83
98
|
if (endHeight > startHeight) {
|
|
84
99
|
delegate?.keyboardWillShow(endHeight)
|
|
85
100
|
} else if (isHiding) {
|
|
@@ -102,6 +117,7 @@ class TrueSheetKeyboardObserver(private val targetView: View, private val reactC
|
|
|
102
117
|
override fun onEnd(animation: WindowInsetsAnimationCompat) {
|
|
103
118
|
val finalHeight = getKeyboardHeight()
|
|
104
119
|
updateHeight(startHeight, finalHeight, 1f)
|
|
120
|
+
isTransitioning = false
|
|
105
121
|
if (isHiding) {
|
|
106
122
|
delegate?.keyboardDidHide()
|
|
107
123
|
isHiding = false
|
|
@@ -134,6 +150,7 @@ class TrueSheetKeyboardObserver(private val targetView: View, private val reactC
|
|
|
134
150
|
targetHeight = newHeight
|
|
135
151
|
isHiding = newHeight < previousHeight
|
|
136
152
|
|
|
153
|
+
isTransitioning = true
|
|
137
154
|
if (newHeight > previousHeight) {
|
|
138
155
|
delegate?.keyboardWillShow(newHeight)
|
|
139
156
|
} else if (isHiding) {
|
|
@@ -142,6 +159,7 @@ class TrueSheetKeyboardObserver(private val targetView: View, private val reactC
|
|
|
142
159
|
|
|
143
160
|
// On legacy API, keyboard has already animated - just update immediately
|
|
144
161
|
updateHeight(previousHeight, newHeight, 1f)
|
|
162
|
+
isTransitioning = false
|
|
145
163
|
|
|
146
164
|
if (isHiding && newHeight == 0) {
|
|
147
165
|
delegate?.keyboardDidHide()
|
package/ios/TrueSheetView.mm
CHANGED
|
@@ -96,7 +96,12 @@ using namespace facebook::react;
|
|
|
96
96
|
|
|
97
97
|
- (void)dealloc {
|
|
98
98
|
if (_controller && _controller.presentingViewController) {
|
|
99
|
-
|
|
99
|
+
// Find the root presenting controller to dismiss the entire stack
|
|
100
|
+
UIViewController *root = _controller.presentingViewController;
|
|
101
|
+
while (root.presentingViewController != nil) {
|
|
102
|
+
root = root.presentingViewController;
|
|
103
|
+
}
|
|
104
|
+
[root dismissViewControllerAnimated:YES completion:nil];
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
_controller.delegate = nil;
|
|
@@ -258,10 +263,6 @@ using namespace facebook::react;
|
|
|
258
263
|
- (void)prepareForRecycle {
|
|
259
264
|
[super prepareForRecycle];
|
|
260
265
|
|
|
261
|
-
if (_controller && _controller.presentingViewController) {
|
|
262
|
-
[_controller dismissViewControllerAnimated:YES completion:nil];
|
|
263
|
-
}
|
|
264
|
-
|
|
265
266
|
[TrueSheetModule unregisterViewWithTag:@(self.tag)];
|
|
266
267
|
|
|
267
268
|
_lastStateSize = CGSizeZero;
|
package/package.json
CHANGED