@lodev09/react-native-true-sheet 4.0.0-beta.16 → 4.0.0-beta.17
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/TrueSheetContainerView.kt +0 -5
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +5 -95
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +18 -46
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +1 -1
- package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
- package/package.json +1 -1
|
@@ -4,11 +4,9 @@ import android.annotation.SuppressLint
|
|
|
4
4
|
import android.view.View
|
|
5
5
|
import android.view.ViewGroup
|
|
6
6
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
7
|
-
import com.facebook.react.uimanager.events.EventDispatcher
|
|
8
7
|
import com.facebook.react.views.view.ReactViewGroup
|
|
9
8
|
|
|
10
9
|
interface TrueSheetContainerViewDelegate {
|
|
11
|
-
val eventDispatcher: EventDispatcher?
|
|
12
10
|
fun containerViewContentDidChangeSize(width: Int, height: Int)
|
|
13
11
|
fun containerViewContentDidScroll()
|
|
14
12
|
fun containerViewScrollViewDidChange()
|
|
@@ -107,9 +105,6 @@ class TrueSheetContainerView(reactContext: ThemedReactContext) :
|
|
|
107
105
|
contentView?.bottomInset = value
|
|
108
106
|
}
|
|
109
107
|
|
|
110
|
-
override val eventDispatcher: EventDispatcher?
|
|
111
|
-
get() = delegate?.eventDispatcher
|
|
112
|
-
|
|
113
108
|
init {
|
|
114
109
|
// Allow footer to position outside container bounds
|
|
115
110
|
clipChildren = false
|
|
@@ -1,29 +1,18 @@
|
|
|
1
1
|
package com.lodev09.truesheet
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
|
-
import android.view.MotionEvent
|
|
5
|
-
import android.view.View
|
|
6
|
-
import android.view.ViewParent
|
|
7
4
|
import com.facebook.react.bridge.WritableNativeMap
|
|
8
|
-
import com.facebook.react.uimanager.JSPointerDispatcher
|
|
9
|
-
import com.facebook.react.uimanager.JSTouchDispatcher
|
|
10
5
|
import com.facebook.react.uimanager.PixelUtil.pxToDp
|
|
11
|
-
import com.facebook.react.uimanager.PointerEvents
|
|
12
|
-
import com.facebook.react.uimanager.RootView
|
|
13
6
|
import com.facebook.react.uimanager.StateWrapper
|
|
14
7
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
15
|
-
import com.facebook.react.uimanager.events.EventDispatcher
|
|
16
8
|
import com.facebook.react.views.view.ReactViewGroup
|
|
17
|
-
import com.lodev09.truesheet.core.TrueSheetCoordinatorLayout
|
|
18
|
-
import com.lodev09.truesheet.utils.TouchEventDeduper
|
|
19
9
|
|
|
20
10
|
/**
|
|
21
|
-
* Delegate interface for footer view size changes
|
|
11
|
+
* Delegate interface for footer view size changes
|
|
22
12
|
*/
|
|
23
13
|
interface TrueSheetFooterViewDelegate {
|
|
24
14
|
fun footerViewDidChangeSize(width: Int, height: Int)
|
|
25
15
|
fun footerViewDidLayout()
|
|
26
|
-
val eventDispatcher: EventDispatcher?
|
|
27
16
|
}
|
|
28
17
|
|
|
29
18
|
/**
|
|
@@ -31,19 +20,16 @@ interface TrueSheetFooterViewDelegate {
|
|
|
31
20
|
* This is the second child of TrueSheetContainerView
|
|
32
21
|
* Positioned absolutely at the bottom of the sheet
|
|
33
22
|
*
|
|
34
|
-
*
|
|
23
|
+
* Touches reach it through the controller's RootView like any other sheet
|
|
24
|
+
* content — the container tracks the sheet's visible height, so the footer
|
|
25
|
+
* always lays out inside the container's bounds.
|
|
35
26
|
*/
|
|
36
27
|
@SuppressLint("ViewConstructor")
|
|
37
|
-
class TrueSheetFooterView(
|
|
38
|
-
ReactViewGroup(reactContext),
|
|
39
|
-
RootView {
|
|
28
|
+
class TrueSheetFooterView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext) {
|
|
40
29
|
|
|
41
30
|
var delegate: TrueSheetFooterViewDelegate? = null
|
|
42
31
|
var stateWrapper: StateWrapper? = null
|
|
43
32
|
|
|
44
|
-
private val eventDispatcher: EventDispatcher?
|
|
45
|
-
get() = delegate?.eventDispatcher
|
|
46
|
-
|
|
47
33
|
private var lastWidth = 0
|
|
48
34
|
private var lastHeight = 0
|
|
49
35
|
|
|
@@ -80,14 +66,6 @@ class TrueSheetFooterView(private val reactContext: ThemedReactContext) :
|
|
|
80
66
|
sw.updateState(newState)
|
|
81
67
|
}
|
|
82
68
|
|
|
83
|
-
private val jsTouchDispatcher = JSTouchDispatcher(this)
|
|
84
|
-
private var jsPointerDispatcher: JSPointerDispatcher? = null
|
|
85
|
-
private val touchDeduper = TouchEventDeduper()
|
|
86
|
-
|
|
87
|
-
init {
|
|
88
|
-
jsPointerDispatcher = JSPointerDispatcher(this)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
69
|
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
92
70
|
super.onSizeChanged(w, h, oldw, oldh)
|
|
93
71
|
|
|
@@ -109,74 +87,6 @@ class TrueSheetFooterView(private val reactContext: ThemedReactContext) :
|
|
|
109
87
|
delegate?.footerViewDidLayout()
|
|
110
88
|
}
|
|
111
89
|
|
|
112
|
-
// ==================== RootView Implementation ====================
|
|
113
|
-
|
|
114
|
-
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
|
|
115
|
-
eventDispatcher?.let { dispatcher ->
|
|
116
|
-
if (touchDeduper.shouldDispatch(event)) {
|
|
117
|
-
jsTouchDispatcher.handleTouchEvent(event, dispatcher, reactContext)
|
|
118
|
-
}
|
|
119
|
-
jsPointerDispatcher?.handleMotionEvent(event, dispatcher, true)
|
|
120
|
-
}
|
|
121
|
-
return super.onInterceptTouchEvent(event)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
|
|
125
|
-
// Mirrors ReactSurfaceView: keep receiving onInterceptTouchEvent so
|
|
126
|
-
// jsTouchDispatcher sees the gesture end, but forward the request up the tree.
|
|
127
|
-
parent?.requestDisallowInterceptTouchEvent(disallowIntercept)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
131
|
-
if (pointerEvents == PointerEvents.NONE || pointerEvents == PointerEvents.BOX_NONE) {
|
|
132
|
-
return false
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
eventDispatcher?.let { dispatcher ->
|
|
136
|
-
if (touchDeduper.shouldDispatch(event)) {
|
|
137
|
-
jsTouchDispatcher.handleTouchEvent(event, dispatcher, reactContext)
|
|
138
|
-
}
|
|
139
|
-
jsPointerDispatcher?.handleMotionEvent(event, dispatcher, false)
|
|
140
|
-
}
|
|
141
|
-
super.onTouchEvent(event)
|
|
142
|
-
return true
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
override fun onInterceptHoverEvent(event: MotionEvent): Boolean {
|
|
146
|
-
eventDispatcher?.let { jsPointerDispatcher?.handleMotionEvent(event, it, true) }
|
|
147
|
-
return super.onHoverEvent(event)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
override fun onHoverEvent(event: MotionEvent): Boolean {
|
|
151
|
-
eventDispatcher?.let { jsPointerDispatcher?.handleMotionEvent(event, it, false) }
|
|
152
|
-
return super.onHoverEvent(event)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
override fun onChildStartedNativeGesture(childView: View?, ev: MotionEvent) {
|
|
156
|
-
findCoordinatorLayout()?.childDidClaimNativeGesture()
|
|
157
|
-
eventDispatcher?.let { dispatcher ->
|
|
158
|
-
jsTouchDispatcher.onChildStartedNativeGesture(ev, dispatcher)
|
|
159
|
-
jsPointerDispatcher?.onChildStartedNativeGesture(childView, ev, dispatcher)
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
private fun findCoordinatorLayout(): TrueSheetCoordinatorLayout? {
|
|
164
|
-
var current: ViewParent? = parent
|
|
165
|
-
while (current != null && current !is TrueSheetCoordinatorLayout) {
|
|
166
|
-
current = current.parent
|
|
167
|
-
}
|
|
168
|
-
return current as? TrueSheetCoordinatorLayout
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
override fun onChildEndedNativeGesture(childView: View, ev: MotionEvent) {
|
|
172
|
-
eventDispatcher?.let { jsTouchDispatcher.onChildEndedNativeGesture(ev, it) }
|
|
173
|
-
jsPointerDispatcher?.onChildEndedNativeGesture()
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
override fun handleException(t: Throwable) {
|
|
177
|
-
reactContext.reactApplicationContext.handleException(RuntimeException(t))
|
|
178
|
-
}
|
|
179
|
-
|
|
180
90
|
companion object {
|
|
181
91
|
const val TAG_NAME = "TrueSheet"
|
|
182
92
|
}
|
|
@@ -209,12 +209,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
209
209
|
// Touch Dispatchers
|
|
210
210
|
private val jsTouchDispatcher = JSTouchDispatcher(this)
|
|
211
211
|
private val jsPointerDispatcher = JSPointerDispatcher(this)
|
|
212
|
-
|
|
213
|
-
// True when the active touch stream began inside the footer. Latched on
|
|
214
|
-
// ACTION_DOWN so subsequent MOVE events keep routing to the footer even if
|
|
215
|
-
// the finger drifts outside the footer's rect mid-gesture.
|
|
216
|
-
private var footerOwnsTouchStream = false
|
|
217
|
-
|
|
218
212
|
private val touchDeduper = TouchEventDeduper()
|
|
219
213
|
|
|
220
214
|
private val eventDispatcher
|
|
@@ -1042,13 +1036,25 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1042
1036
|
animate = isPresented
|
|
1043
1037
|
)
|
|
1044
1038
|
|
|
1045
|
-
updateStateDimensions()
|
|
1046
|
-
|
|
1047
1039
|
if (isPresented && applyState) {
|
|
1048
1040
|
// Prefer the pending target while a resize animation is in flight so a
|
|
1049
1041
|
// layout-driven reconfigure doesn't revert to the stale currentDetentIndex.
|
|
1050
1042
|
val targetIndex = if (pendingDetentIndex >= 0) pendingDetentIndex else currentDetentIndex
|
|
1043
|
+
|
|
1044
|
+
// Track the settle like resize() — the container (and the footer pinned
|
|
1045
|
+
// to its bottom) follows the sheet's visible height instead of jumping
|
|
1046
|
+
// to the target and riding the animation
|
|
1047
|
+
pendingDetentIndex = targetIndex
|
|
1048
|
+
sheetView?.let { updateStateDimensionsForPosition(it.top) }
|
|
1051
1049
|
setStateForDetentIndex(targetIndex)
|
|
1050
|
+
|
|
1051
|
+
// Already at the target — no slide frames will follow
|
|
1052
|
+
if (!isSettling) {
|
|
1053
|
+
pendingDetentIndex = -1
|
|
1054
|
+
updateStateDimensions()
|
|
1055
|
+
}
|
|
1056
|
+
} else {
|
|
1057
|
+
updateStateDimensions()
|
|
1052
1058
|
}
|
|
1053
1059
|
|
|
1054
1060
|
interactionState = InteractionState.Idle
|
|
@@ -1190,9 +1196,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1190
1196
|
val sheetHeight = sheet.height
|
|
1191
1197
|
|
|
1192
1198
|
// Pinned to the screen bottom (above the keyboard) while the sheet sits at
|
|
1193
|
-
// or above its lowest stop; past it the footer rides down with the
|
|
1194
|
-
// instead, like iOS
|
|
1195
|
-
|
|
1199
|
+
// or above its lowest stop; dragged past it the footer rides down with the
|
|
1200
|
+
// sheet instead, like iOS. A resize settle always pins — the sheet may be
|
|
1201
|
+
// animating up from below a stop that just grew
|
|
1202
|
+
val anchorTop = if (pendingDetentIndex >= 0) sheet.top else minOf(sheet.top, detentCalculator.getLowestSheetTop())
|
|
1196
1203
|
val footerY = (sheetHeight - anchorTop - footerHeight - keyboardShift).toFloat()
|
|
1197
1204
|
|
|
1198
1205
|
// Clamp to prevent footer going above safe area
|
|
@@ -1566,41 +1573,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1566
1573
|
// MARK: - RootView Touch Handling
|
|
1567
1574
|
// =============================================================================
|
|
1568
1575
|
|
|
1569
|
-
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
|
1570
|
-
val footer = containerView?.footerView
|
|
1571
|
-
val action = event.actionMasked
|
|
1572
|
-
|
|
1573
|
-
if (footer != null && footer.isShown) {
|
|
1574
|
-
if (action == MotionEvent.ACTION_DOWN) {
|
|
1575
|
-
val loc = ScreenUtils.getScreenLocation(footer)
|
|
1576
|
-
val x = event.rawX.toInt()
|
|
1577
|
-
val y = event.rawY.toInt()
|
|
1578
|
-
footerOwnsTouchStream = x >= loc[0] &&
|
|
1579
|
-
x <= loc[0] + footer.width &&
|
|
1580
|
-
y >= loc[1] &&
|
|
1581
|
-
y <= loc[1] + footer.height
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
if (footerOwnsTouchStream) {
|
|
1585
|
-
val loc = ScreenUtils.getScreenLocation(footer)
|
|
1586
|
-
val localEvent = MotionEvent.obtain(event)
|
|
1587
|
-
localEvent.setLocation(event.rawX - loc[0], event.rawY - loc[1])
|
|
1588
|
-
val handled = footer.dispatchTouchEvent(localEvent)
|
|
1589
|
-
localEvent.recycle()
|
|
1590
|
-
|
|
1591
|
-
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
|
1592
|
-
footerOwnsTouchStream = false
|
|
1593
|
-
}
|
|
1594
|
-
if (handled) return true
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
|
|
1598
|
-
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
|
1599
|
-
footerOwnsTouchStream = false
|
|
1600
|
-
}
|
|
1601
|
-
return super.dispatchTouchEvent(event)
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
1576
|
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
|
|
1605
1577
|
eventDispatcher?.let {
|
|
1606
1578
|
if (touchDeduper.shouldDispatch(event)) {
|
|
@@ -250,7 +250,7 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
/**
|
|
253
|
-
* Called by descendant RootView
|
|
253
|
+
* Called by the descendant RootView (controller) when a child
|
|
254
254
|
* starts a native gesture — e.g. an RNGH handler activated or a scrollable began
|
|
255
255
|
* scrolling. Yields sheet interception for the rest of the touch stream.
|
|
256
256
|
*/
|
|
@@ -104,18 +104,6 @@ object ScreenUtils {
|
|
|
104
104
|
*/
|
|
105
105
|
fun getScreenHeight(reactContext: ReactContext): Int = reactContext.resources.displayMetrics.heightPixels
|
|
106
106
|
|
|
107
|
-
/**
|
|
108
|
-
* Get the location of a view in screen coordinates
|
|
109
|
-
*
|
|
110
|
-
* @param view The view to get screen location for
|
|
111
|
-
* @return IntArray with [x, y] coordinates in screen space
|
|
112
|
-
*/
|
|
113
|
-
fun getScreenLocation(view: View): IntArray {
|
|
114
|
-
val location = IntArray(2)
|
|
115
|
-
view.getLocationOnScreen(location)
|
|
116
|
-
return location
|
|
117
|
-
}
|
|
118
|
-
|
|
119
107
|
/**
|
|
120
108
|
* Returns true if the device is a phone in portrait orientation.
|
|
121
109
|
*/
|
package/package.json
CHANGED