@lodev09/react-native-true-sheet 4.0.0-beta.1 → 4.0.0-beta.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/README.md +25 -4
- package/android/build.gradle +22 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +24 -7
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +211 -182
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +14 -19
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +106 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +18 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +82 -39
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +275 -88
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +26 -1
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +75 -3
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +56 -34
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +137 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +1 -1
- package/android/src/main/java/com/lodev09/truesheet/utils/TouchEventDeduper.kt +31 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
- package/android/src/main/jni/TrueSheetSpec.h +1 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +0 -8
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +54 -29
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +6 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +6 -4
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +7 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +46 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +36 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +4 -35
- package/ios/TrueSheetContainerView.h +14 -7
- package/ios/TrueSheetContainerView.mm +10 -7
- package/ios/TrueSheetContentView.h +31 -15
- package/ios/TrueSheetContentView.mm +133 -179
- package/ios/TrueSheetFooterView.h +6 -0
- package/ios/TrueSheetFooterView.mm +48 -2
- package/ios/TrueSheetModule.h +2 -7
- package/ios/TrueSheetModule.mm +7 -16
- package/ios/TrueSheetOverlayView.h +32 -0
- package/ios/TrueSheetOverlayView.mm +161 -0
- package/ios/TrueSheetView.mm +171 -29
- package/ios/TrueSheetViewController.h +10 -0
- package/ios/TrueSheetViewController.mm +256 -74
- package/ios/core/TrueSheetGrabberView.mm +13 -3
- package/ios/core/TrueSheetOverlayContainerView.h +31 -0
- package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
- package/ios/events/TrueSheetLifecycleEvents.h +2 -0
- package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
- package/ios/tvos/TrueSheetStubs.mm +12 -0
- package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
- package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
- package/lib/module/TrueSheet.js +149 -34
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +122 -53
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetOverlay.js +30 -0
- package/lib/module/TrueSheetOverlay.js.map +1 -0
- package/lib/module/TrueSheetOverlay.web.js +35 -0
- package/lib/module/TrueSheetOverlay.web.js.map +1 -0
- package/lib/module/TrueSheetPeek.web.js +3 -3
- package/lib/module/TrueSheetPeek.web.js.map +1 -1
- package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +5 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/mocks/index.js +10 -0
- package/lib/module/mocks/index.js.map +1 -1
- package/lib/module/mocks/navigation.js +31 -21
- package/lib/module/mocks/navigation.js.map +1 -1
- package/lib/module/mocks/navigationExpoRouter.js +21 -0
- package/lib/module/mocks/navigationExpoRouter.js.map +1 -0
- package/lib/module/mocks/reanimated.js +1 -1
- package/lib/module/mocks/reanimated.js.map +1 -1
- package/lib/module/navigation/{TrueSheetView.js → TrueSheetNavigatorContent.js} +14 -12
- package/lib/module/navigation/TrueSheetNavigatorContent.js.map +1 -0
- package/lib/module/navigation/actions.js +50 -0
- package/lib/module/navigation/actions.js.map +1 -0
- package/lib/module/navigation/base-types.js +2 -0
- package/lib/module/navigation/base-types.js.map +1 -0
- package/lib/module/navigation/{TrueSheetRouter.js → createTrueSheetRouter.js} +11 -24
- package/lib/module/navigation/createTrueSheetRouter.js.map +1 -0
- package/lib/module/navigation/expo-router/base-types.js +2 -0
- package/lib/module/navigation/expo-router/base-types.js.map +1 -0
- package/lib/module/navigation/expo-router/index.js +50 -0
- package/lib/module/navigation/expo-router/index.js.map +1 -0
- package/lib/module/navigation/expo-router/types.js +4 -0
- package/lib/module/navigation/expo-router/types.js.map +1 -0
- package/lib/module/navigation/index.js +43 -2
- package/lib/module/navigation/index.js.map +1 -1
- package/lib/module/navigation/navigator.js +10 -0
- package/lib/module/navigation/navigator.js.map +1 -0
- package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js +2 -2
- package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js.map +1 -1
- package/lib/module/navigation/screen/TrueSheetScreen.js +2 -2
- package/lib/module/navigation/screen/TrueSheetScreen.js.map +1 -1
- package/lib/module/navigation/screen/useSheetScreenState.js +5 -4
- package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
- package/lib/module/navigation/useTrueSheetNavigation.js +1 -1
- package/lib/module/navigation/useTrueSheetNavigation.js.map +1 -1
- package/lib/module/web/dom.js +10 -0
- package/lib/module/web/dom.js.map +1 -0
- package/lib/module/web/vaul/constants.js +4 -0
- package/lib/module/web/vaul/constants.js.map +1 -1
- package/lib/module/web/vaul/context.js.map +1 -1
- package/lib/module/web/vaul/helpers.js +10 -4
- package/lib/module/web/vaul/helpers.js.map +1 -1
- package/lib/module/web/vaul/index.js +35 -2
- package/lib/module/web/vaul/index.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts +6 -0
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +72 -1
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
- package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
- package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
- package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
- package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +4 -0
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/index.d.ts +4 -0
- package/lib/typescript/src/mocks/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/navigation.d.ts +5 -1
- package/lib/typescript/src/mocks/navigation.d.ts.map +1 -1
- package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +22 -0
- package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -0
- package/lib/typescript/src/mocks/reanimated.d.ts.map +1 -1
- package/lib/typescript/src/navigation/TrueSheetNavigatorContent.d.ts +5 -0
- package/lib/typescript/src/navigation/TrueSheetNavigatorContent.d.ts.map +1 -0
- package/lib/typescript/src/navigation/actions.d.ts +71 -0
- package/lib/typescript/src/navigation/actions.d.ts.map +1 -0
- package/lib/typescript/src/navigation/base-types.d.ts +9 -0
- package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +3 -0
- package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -0
- package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
- package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/expo-router/index.d.ts +37 -0
- package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -0
- package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
- package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/index.d.ts +42 -3
- package/lib/typescript/src/navigation/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation/navigator.d.ts +7 -0
- package/lib/typescript/src/navigation/navigator.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts +1 -1
- package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts +1 -1
- package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/types.d.ts +3 -4
- package/lib/typescript/src/navigation/screen/types.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +5 -6
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
- package/lib/typescript/src/navigation/types.d.ts +65 -5
- package/lib/typescript/src/navigation/types.d.ts.map +1 -1
- package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts +1 -1
- package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts.map +1 -1
- package/lib/typescript/src/web/dom.d.ts +7 -0
- package/lib/typescript/src/web/dom.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/constants.d.ts +1 -0
- package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/context.d.ts +1 -0
- package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/index.d.ts +7 -1
- package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
- package/package.json +32 -9
- package/react-native.config.js +1 -0
- package/src/TrueSheet.tsx +184 -46
- package/src/TrueSheet.types.ts +77 -1
- package/src/TrueSheet.web.tsx +135 -64
- package/src/TrueSheetOverlay.tsx +24 -0
- package/src/TrueSheetOverlay.web.tsx +33 -0
- package/src/TrueSheetPeek.web.tsx +4 -3
- package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/src/fabric/TrueSheetViewNativeComponent.ts +5 -0
- package/src/index.ts +1 -0
- package/src/mocks/index.ts +7 -0
- package/src/mocks/navigation.ts +32 -25
- package/src/mocks/navigationExpoRouter.ts +41 -0
- package/src/mocks/reanimated.ts +8 -9
- package/src/navigation/{TrueSheetView.tsx → TrueSheetNavigatorContent.tsx} +24 -17
- package/src/navigation/actions.ts +78 -0
- package/src/navigation/base-types.ts +20 -0
- package/src/navigation/createTrueSheetRouter.ts +194 -0
- package/src/navigation/expo-router/base-types.ts +18 -0
- package/src/navigation/expo-router/index.ts +90 -0
- package/src/navigation/expo-router/types.ts +116 -0
- package/src/navigation/index.ts +68 -2
- package/src/navigation/navigator.ts +18 -0
- package/src/navigation/screen/ReanimatedTrueSheetScreen.tsx +2 -2
- package/src/navigation/screen/TrueSheetScreen.tsx +2 -2
- package/src/navigation/screen/types.ts +4 -6
- package/src/navigation/screen/useSheetScreenState.ts +10 -15
- package/src/navigation/types.ts +99 -16
- package/src/navigation/useTrueSheetNavigation.ts +1 -1
- package/src/web/dom.ts +9 -0
- package/src/web/vaul/constants.ts +4 -0
- package/src/web/vaul/context.ts +1 -0
- package/src/web/vaul/helpers.ts +13 -6
- package/src/web/vaul/index.tsx +46 -1
- package/lib/module/navigation/TrueSheetRouter.js.map +0 -1
- package/lib/module/navigation/TrueSheetView.js.map +0 -1
- package/lib/module/navigation/createTrueSheetNavigator.js +0 -63
- package/lib/module/navigation/createTrueSheetNavigator.js.map +0 -1
- package/lib/typescript/src/navigation/TrueSheetRouter.d.ts +0 -57
- package/lib/typescript/src/navigation/TrueSheetRouter.d.ts.map +0 -1
- package/lib/typescript/src/navigation/TrueSheetView.d.ts +0 -10
- package/lib/typescript/src/navigation/TrueSheetView.d.ts.map +0 -1
- package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts +0 -35
- package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts.map +0 -1
- package/src/navigation/TrueSheetRouter.ts +0 -234
- package/src/navigation/createTrueSheetNavigator.tsx +0 -93
|
@@ -5,6 +5,7 @@ import com.facebook.react.bridge.ReadableArray
|
|
|
5
5
|
import com.facebook.react.bridge.ReadableMap
|
|
6
6
|
import com.facebook.react.module.annotations.ReactModule
|
|
7
7
|
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
8
|
+
import com.facebook.react.uimanager.PointerEvents
|
|
8
9
|
import com.facebook.react.uimanager.ReactStylesDiffMap
|
|
9
10
|
import com.facebook.react.uimanager.StateWrapper
|
|
10
11
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
@@ -55,6 +56,20 @@ class TrueSheetViewManager :
|
|
|
55
56
|
|
|
56
57
|
override fun getDelegate(): ViewManagerDelegate<TrueSheetView> = delegate
|
|
57
58
|
|
|
59
|
+
// The codegen delegate drops props outside the spec (pointerEvents comes from the
|
|
60
|
+
// host's style), so apply it here after the delegate pass.
|
|
61
|
+
override fun updateProperties(viewToUpdate: TrueSheetView, props: ReactStylesDiffMap) {
|
|
62
|
+
super.updateProperties(viewToUpdate, props)
|
|
63
|
+
if (props.hasKey("pointerEvents")) {
|
|
64
|
+
setPointerEvents(viewToUpdate, props.getString("pointerEvents"))
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@ReactProp(name = "pointerEvents")
|
|
69
|
+
fun setPointerEvents(view: TrueSheetView, pointerEventsStr: String?) {
|
|
70
|
+
view.pointerEvents = PointerEvents.parsePointerEvents(pointerEventsStr)
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
/**
|
|
59
74
|
* Export custom direct event types for Fabric
|
|
60
75
|
* Uses Kotlin native collections with decoupled event classes
|
|
@@ -66,6 +81,7 @@ class TrueSheetViewManager :
|
|
|
66
81
|
DidPresentEvent.EVENT_NAME to hashMapOf("registrationName" to DidPresentEvent.REGISTRATION_NAME),
|
|
67
82
|
WillDismissEvent.EVENT_NAME to hashMapOf("registrationName" to WillDismissEvent.REGISTRATION_NAME),
|
|
68
83
|
DidDismissEvent.EVENT_NAME to hashMapOf("registrationName" to DidDismissEvent.REGISTRATION_NAME),
|
|
84
|
+
DismissAttemptEvent.EVENT_NAME to hashMapOf("registrationName" to DismissAttemptEvent.REGISTRATION_NAME),
|
|
69
85
|
DetentChangeEvent.EVENT_NAME to hashMapOf("registrationName" to DetentChangeEvent.REGISTRATION_NAME),
|
|
70
86
|
DragBeginEvent.EVENT_NAME to hashMapOf("registrationName" to DragBeginEvent.REGISTRATION_NAME),
|
|
71
87
|
DragChangeEvent.EVENT_NAME to hashMapOf("registrationName" to DragChangeEvent.REGISTRATION_NAME),
|
|
@@ -179,6 +195,7 @@ class TrueSheetViewManager :
|
|
|
179
195
|
@ReactProp(name = "initialDetentIndex", defaultInt = -1)
|
|
180
196
|
override fun setInitialDetentIndex(view: TrueSheetView, index: Int) {
|
|
181
197
|
view.initialDetentIndex = index
|
|
198
|
+
view.presentInitialIfNeeded()
|
|
182
199
|
}
|
|
183
200
|
|
|
184
201
|
@ReactProp(name = "initialDetentAnimated", defaultBoolean = true)
|
|
@@ -231,6 +248,11 @@ class TrueSheetViewManager :
|
|
|
231
248
|
view.setSheetElevation(elevation.toFloat())
|
|
232
249
|
}
|
|
233
250
|
|
|
251
|
+
@ReactProp(name = "scrollableHandle", defaultInt = -1)
|
|
252
|
+
override fun setScrollableHandle(view: TrueSheetView, handle: Int) {
|
|
253
|
+
view.setScrollableHandle(handle)
|
|
254
|
+
}
|
|
255
|
+
|
|
234
256
|
@ReactProp(name = "scrollableOptions")
|
|
235
257
|
override fun setScrollableOptions(view: TrueSheetView, options: ReadableMap?) {
|
|
236
258
|
if (options == null) {
|
|
@@ -240,7 +262,10 @@ class TrueSheetViewManager :
|
|
|
240
262
|
|
|
241
263
|
val scrollableOptions = ScrollableOptions(
|
|
242
264
|
keyboardScrollOffset = if (options.hasKey("keyboardScrollOffset")) options.getDouble("keyboardScrollOffset").toFloat() else 0f,
|
|
243
|
-
|
|
265
|
+
keyboardOffset = if (options.hasKey("keyboardOffset")) options.getDouble("keyboardOffset").toFloat() else 0f,
|
|
266
|
+
scrollingExpandsSheet = if (options.hasKey("scrollingExpandsSheet")) options.getBoolean("scrollingExpandsSheet") else true,
|
|
267
|
+
contentInsetAdjustment = !options.hasKey("contentInsetAdjustmentBehavior") ||
|
|
268
|
+
options.getBoolean("contentInsetAdjustmentBehavior")
|
|
244
269
|
)
|
|
245
270
|
view.setScrollableOptions(scrollableOptions)
|
|
246
271
|
}
|
|
@@ -10,9 +10,11 @@ import android.view.ViewGroup
|
|
|
10
10
|
import android.widget.EditText
|
|
11
11
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
12
12
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
13
|
+
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
13
14
|
import com.facebook.react.uimanager.PointerEvents
|
|
14
15
|
import com.facebook.react.uimanager.ReactPointerEventsView
|
|
15
16
|
import com.facebook.react.uimanager.TouchTargetHelper
|
|
17
|
+
import com.lodev09.truesheet.utils.KeyboardUtils
|
|
16
18
|
import com.lodev09.truesheet.utils.isDescendantOf
|
|
17
19
|
|
|
18
20
|
interface TrueSheetCoordinatorLayoutDelegate {
|
|
@@ -20,6 +22,12 @@ interface TrueSheetCoordinatorLayoutDelegate {
|
|
|
20
22
|
fun coordinatorLayoutDidChangeConfiguration()
|
|
21
23
|
fun findScrollView(): ViewGroup?
|
|
22
24
|
fun findSheetView(): TrueSheetBottomSheetView?
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A downward pull the sheet couldn't follow ended; `overdragPx` is the travel it
|
|
28
|
+
* didn't follow, `keyboardWasVisible` whether the keyboard was up when it began.
|
|
29
|
+
*/
|
|
30
|
+
fun coordinatorLayoutDidPullDown(overdragPx: Int, keyboardWasVisible: Boolean)
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
/**
|
|
@@ -57,7 +65,7 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
57
65
|
private var streamGestureClaimed = false
|
|
58
66
|
|
|
59
67
|
// Whether the sheet owns vertical drags for this stream: the touch target is inside
|
|
60
|
-
// the sheet but outside the
|
|
68
|
+
// the sheet but outside the detected scrollable (e.g. a header overlaying it), with
|
|
61
69
|
// nothing vertically scrollable along its chain. Such targets consume the stream
|
|
62
70
|
// natively (no nested scroll), and BottomSheetBehavior won't intercept because the
|
|
63
71
|
// scrollable's bounds still contain the point (touchingScrollingChild) — intercept
|
|
@@ -65,6 +73,18 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
65
73
|
private var streamSheetDraggable = false
|
|
66
74
|
private var streamDraggableEditText = false
|
|
67
75
|
|
|
76
|
+
// Pull-down tracking. BottomSheetBehavior gives no callback when a downward drag
|
|
77
|
+
// is clamped by `isHideable = false` — the sheet stays put (or the scrollable
|
|
78
|
+
// overscrolls) and no state change fires. Accumulate the downward finger travel
|
|
79
|
+
// neither the sheet nor its scrollable followed and report it on release so the
|
|
80
|
+
// controller can surface a blocked dismiss attempt.
|
|
81
|
+
private var pullTracking = false
|
|
82
|
+
private var pullStartX = 0f
|
|
83
|
+
private var pullStartY = 0f
|
|
84
|
+
private var pullLastY = 0f
|
|
85
|
+
private var pullOverdrag = 0f
|
|
86
|
+
private var pullKeyboardVisible = false
|
|
87
|
+
|
|
68
88
|
init {
|
|
69
89
|
layoutParams = LayoutParams(
|
|
70
90
|
LayoutParams.MATCH_PARENT,
|
|
@@ -94,6 +114,49 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
94
114
|
override val pointerEvents: PointerEvents
|
|
95
115
|
get() = PointerEvents.BOX_NONE
|
|
96
116
|
|
|
117
|
+
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
|
|
118
|
+
when (ev.actionMasked) {
|
|
119
|
+
MotionEvent.ACTION_DOWN -> {
|
|
120
|
+
val sheet = delegate?.findSheetView()
|
|
121
|
+
pullTracking = sheet != null && isTouchInside(sheet, ev)
|
|
122
|
+
pullStartX = ev.rawX
|
|
123
|
+
pullStartY = ev.rawY
|
|
124
|
+
pullLastY = ev.rawY
|
|
125
|
+
pullOverdrag = 0f
|
|
126
|
+
pullKeyboardVisible = pullTracking && KeyboardUtils.isKeyboardVisible(this)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
MotionEvent.ACTION_MOVE -> if (pullTracking) {
|
|
130
|
+
val sheet = delegate?.findSheetView()
|
|
131
|
+
val topBefore = sheet?.top
|
|
132
|
+
val handled = super.dispatchTouchEvent(ev)
|
|
133
|
+
val step = ev.rawY - pullLastY
|
|
134
|
+
pullLastY = ev.rawY
|
|
135
|
+
// Finger moved down but the sheet stayed put and the scrollable is at
|
|
136
|
+
// its top — the pull was clamped
|
|
137
|
+
if (step > 0 && sheet != null && sheet.top == topBefore && delegate?.findScrollView()?.canScrollVertically(-1) != true) {
|
|
138
|
+
pullOverdrag += step
|
|
139
|
+
}
|
|
140
|
+
return handled
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
MotionEvent.ACTION_UP -> if (pullTracking) {
|
|
144
|
+
pullTracking = false
|
|
145
|
+
val dx = kotlin.math.abs(ev.rawX - pullStartX)
|
|
146
|
+
val dy = ev.rawY - pullStartY
|
|
147
|
+
// Let the behavior settle first so the controller sees the resting position.
|
|
148
|
+
val handled = super.dispatchTouchEvent(ev)
|
|
149
|
+
if (dy > dx && pullOverdrag > PULL_DOWN_THRESHOLD_DP.dpToPx()) {
|
|
150
|
+
delegate?.coordinatorLayoutDidPullDown(pullOverdrag.toInt(), pullKeyboardVisible)
|
|
151
|
+
}
|
|
152
|
+
return handled
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
MotionEvent.ACTION_CANCEL -> pullTracking = false
|
|
156
|
+
}
|
|
157
|
+
return super.dispatchTouchEvent(ev)
|
|
158
|
+
}
|
|
159
|
+
|
|
97
160
|
/**
|
|
98
161
|
* Clears stale `nestedScrollingChildRef` from BottomSheetBehavior.
|
|
99
162
|
*
|
|
@@ -127,7 +190,7 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
127
190
|
|
|
128
191
|
/**
|
|
129
192
|
* Resolves the stream's touch target (respecting zIndex and pointerEvents) and
|
|
130
|
-
* decides whether the sheet may drag from it — inside the sheet, outside the
|
|
193
|
+
* decides whether the sheet may drag from it — inside the sheet, outside the detected
|
|
131
194
|
* scrollable, and nothing along the target's chain scrolls vertically.
|
|
132
195
|
*/
|
|
133
196
|
private fun updateStreamTargetFlags(ev: MotionEvent) {
|
|
@@ -139,7 +202,7 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
139
202
|
.firstNotNullOfOrNull { it.getView() } ?: return
|
|
140
203
|
if (target !== sheet && !target.isDescendantOf(sheet)) return
|
|
141
204
|
|
|
142
|
-
// A
|
|
205
|
+
// A detected scrollable that can scroll (or hosts pull-to-refresh) owns its
|
|
143
206
|
// touches — nested scrolling drags the sheet. When it can't scroll (e.g.
|
|
144
207
|
// the sheet is fully expanded and the content fits) it eats the gesture
|
|
145
208
|
// instead, so fall through and let the sheet drag from it — an EditText
|
|
@@ -160,6 +223,14 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
160
223
|
streamDraggableEditText = target is EditText
|
|
161
224
|
}
|
|
162
225
|
|
|
226
|
+
private fun isTouchInside(view: View, ev: MotionEvent): Boolean {
|
|
227
|
+
val loc = IntArray(2)
|
|
228
|
+
view.getLocationOnScreen(loc)
|
|
229
|
+
val x = ev.rawX.toInt()
|
|
230
|
+
val y = ev.rawY.toInt()
|
|
231
|
+
return x >= loc[0] && x <= loc[0] + view.width && y >= loc[1] && y <= loc[1] + view.height
|
|
232
|
+
}
|
|
233
|
+
|
|
163
234
|
private fun findDescendantViewAt(view: View, rawX: Int, rawY: Int, predicate: (View) -> Boolean): View? {
|
|
164
235
|
val loc = IntArray(2)
|
|
165
236
|
view.getLocationOnScreen(loc)
|
|
@@ -299,5 +370,6 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
299
370
|
companion object {
|
|
300
371
|
private const val GESTURE_HANDLER_ROOT_VIEW_CLASS = "com.swmansion.gesturehandler.react.RNGestureHandlerRootView"
|
|
301
372
|
private const val GESTURE_CLAIM_SLOP_FACTOR = 3
|
|
373
|
+
private const val PULL_DOWN_THRESHOLD_DP = 24f
|
|
302
374
|
}
|
|
303
375
|
}
|
|
@@ -23,6 +23,13 @@ interface TrueSheetDetentCalculatorDelegate {
|
|
|
23
23
|
val maxContentHeight: Int?
|
|
24
24
|
val keyboardInset: Int
|
|
25
25
|
val topInset: Int
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* True while the keyboard holds the sheet up: the collapsed stop then sits at
|
|
29
|
+
* the keyboard-free first detent (the "floor") so a drag-down dismisses the
|
|
30
|
+
* keyboard before it can dismiss the sheet.
|
|
31
|
+
*/
|
|
32
|
+
val hasKeyboardFloor: Boolean
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
/**
|
|
@@ -44,6 +51,7 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
44
51
|
private val maxContentHeight: Int? get() = delegate?.maxContentHeight
|
|
45
52
|
private val keyboardInset: Int get() = delegate?.keyboardInset ?: 0
|
|
46
53
|
private val topInset: Int get() = delegate?.topInset ?: 0
|
|
54
|
+
private val hasKeyboardFloor: Boolean get() = delegate?.hasKeyboardFloor == true
|
|
47
55
|
|
|
48
56
|
/**
|
|
49
57
|
* Height for auto (-1.0) detents: content + header + footer height.
|
|
@@ -110,12 +118,31 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
110
118
|
RNLog.w(reactContext, "TrueSheet: Detent index ($index) is out of bounds (0..${detents.size - 1})")
|
|
111
119
|
return realScreenHeight
|
|
112
120
|
}
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
// With a keyboard floor only the last detent stays keyboard-shifted — it's
|
|
122
|
+
// the keyboard-expanded resting position; the stops below it are keyboard-free
|
|
123
|
+
val isKeyboardFreeStop = hasKeyboardFloor && index != detents.size - 1
|
|
124
|
+
return getSheetTop(detents[index], includeKeyboard = !isKeyboardFreeStop)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Top of the lowest stop the sheet can rest at: the keyboard floor while it's
|
|
129
|
+
* active, else the first detent.
|
|
130
|
+
*/
|
|
131
|
+
fun getLowestSheetTop(): Int = if (hasKeyboardFloor) getSheetTop(detents[0], includeKeyboard = false) else getSheetTopForDetentIndex(0)
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Top of the half stop while the keyboard floor is active: the keyboard-free
|
|
135
|
+
* second detent, or the floor itself for a single detent.
|
|
136
|
+
*/
|
|
137
|
+
fun getKeyboardHalfStopTop(): Int = getSheetTop(detents[minOf(1, detents.size - 1)], includeKeyboard = false)
|
|
138
|
+
|
|
139
|
+
// Clamp to the space the sheet can actually occupy — matching
|
|
140
|
+
// setupSheetDetents. A keyboard-inflated detent height can exceed it,
|
|
141
|
+
// placing the expected top above the screen while the real sheet stops
|
|
142
|
+
// at the top inset.
|
|
143
|
+
private fun getSheetTop(detent: Double, includeKeyboard: Boolean = true): Int {
|
|
117
144
|
val maxAvailableHeight = realScreenHeight - topInset
|
|
118
|
-
return realScreenHeight - minOf(getDetentHeight(
|
|
145
|
+
return realScreenHeight - minOf(getDetentHeight(detent, includeKeyboard), maxAvailableHeight)
|
|
119
146
|
}
|
|
120
147
|
|
|
121
148
|
/**
|
|
@@ -152,6 +179,9 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
152
179
|
*/
|
|
153
180
|
fun getStateForDetentIndex(index: Int): Int {
|
|
154
181
|
val stateMap = getDetentStateMap() ?: return BottomSheetBehavior.STATE_HIDDEN
|
|
182
|
+
// With a keyboard floor the keyboard-expanded last detent lives at the
|
|
183
|
+
// expanded stop — the half stop (which may share its index) is keyboard-free
|
|
184
|
+
if (hasKeyboardFloor && index == detents.size - 1) return BottomSheetBehavior.STATE_EXPANDED
|
|
155
185
|
return stateMap.entries.find { it.value == index }?.key ?: BottomSheetBehavior.STATE_HIDDEN
|
|
156
186
|
}
|
|
157
187
|
|
|
@@ -169,8 +199,12 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
169
199
|
*/
|
|
170
200
|
private fun getDetentStateMap(): Map<Int, Int>? =
|
|
171
201
|
when (detents.size) {
|
|
202
|
+
// With one detent halfExpandedRatio matches the peek height, so the
|
|
203
|
+
// behavior can settle HALF_EXPANDED at the same position (e.g. an upward
|
|
204
|
+
// drag release) — map it too, or the settle is unmappable
|
|
172
205
|
1 -> mapOf(
|
|
173
206
|
BottomSheetBehavior.STATE_COLLAPSED to 0,
|
|
207
|
+
BottomSheetBehavior.STATE_HALF_EXPANDED to 0,
|
|
174
208
|
BottomSheetBehavior.STATE_EXPANDED to 0
|
|
175
209
|
)
|
|
176
210
|
|
|
@@ -197,16 +231,18 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
197
231
|
* Find which segment the position falls into for interpolation.
|
|
198
232
|
* @return Triple(fromIndex, toIndex, progress) where progress is 0-1, or null if no detents
|
|
199
233
|
*/
|
|
200
|
-
fun findSegmentForPosition(positionPx: Int): Triple<Int, Int, Float>? {
|
|
234
|
+
private fun findSegmentForPosition(positionPx: Int): Triple<Int, Int, Float>? {
|
|
201
235
|
val count = detents.size
|
|
202
236
|
if (count == 0) return null
|
|
203
237
|
|
|
204
|
-
|
|
238
|
+
// A single keyboard-shifted detent can rest down at its keyboard floor —
|
|
239
|
+
// that range is still index 0, not a dismissal
|
|
240
|
+
val floorPos = getLowestSheetTop()
|
|
205
241
|
|
|
206
|
-
// Position is below
|
|
207
|
-
if (positionPx >
|
|
208
|
-
val range = realScreenHeight -
|
|
209
|
-
val progress = if (range > 0) (positionPx -
|
|
242
|
+
// Position is below the lowest stop (sheet is being dragged down to dismiss)
|
|
243
|
+
if (positionPx > floorPos) {
|
|
244
|
+
val range = realScreenHeight - floorPos
|
|
245
|
+
val progress = if (range > 0) (positionPx - floorPos).toFloat() / range else 0f
|
|
210
246
|
return Triple(-1, 0, progress)
|
|
211
247
|
}
|
|
212
248
|
|
|
@@ -238,37 +274,23 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
238
274
|
}
|
|
239
275
|
|
|
240
276
|
/**
|
|
241
|
-
* Returns continuous index (e.g., 0.5 = halfway between detent
|
|
277
|
+
* Returns interpolated continuous index (e.g., 0.5 = halfway between detent
|
|
278
|
+
* 0 and 1) and screen fraction for a position, sharing a single segment
|
|
279
|
+
* lookup — this runs on every position frame.
|
|
242
280
|
*/
|
|
243
|
-
fun
|
|
244
|
-
|
|
245
|
-
if (count == 0) return -1f
|
|
246
|
-
|
|
247
|
-
val segment = findSegmentForPosition(positionPx) ?: return 0f
|
|
248
|
-
val (fromIndex, _, progress) = segment
|
|
249
|
-
|
|
250
|
-
if (fromIndex == -1) return -progress
|
|
251
|
-
return fromIndex + progress
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Returns interpolated screen fraction for position.
|
|
256
|
-
*/
|
|
257
|
-
fun getInterpolatedDetentForPosition(positionPx: Int): Float {
|
|
258
|
-
val count = detents.size
|
|
259
|
-
if (count == 0) return 0f
|
|
281
|
+
fun getInterpolatedPositionInfo(positionPx: Int): Pair<Float, Float> {
|
|
282
|
+
if (detents.isEmpty()) return Pair(-1f, 0f)
|
|
260
283
|
|
|
261
|
-
val
|
|
262
|
-
|
|
284
|
+
val (fromIndex, toIndex, progress) = findSegmentForPosition(positionPx)
|
|
285
|
+
?: return Pair(0f, getDetentValueForIndex(0))
|
|
263
286
|
|
|
264
287
|
if (fromIndex == -1) {
|
|
265
|
-
|
|
266
|
-
return maxOf(0f, firstDetent * (1 - progress))
|
|
288
|
+
return Pair(-progress, maxOf(0f, getDetentValueForIndex(0) * (1 - progress)))
|
|
267
289
|
}
|
|
268
290
|
|
|
269
291
|
val fromDetent = getDetentValueForIndex(fromIndex)
|
|
270
292
|
val toDetent = getDetentValueForIndex(toIndex)
|
|
271
|
-
return fromDetent + progress * (toDetent - fromDetent)
|
|
293
|
+
return Pair(fromIndex + progress, fromDetent + progress * (toDetent - fromDetent))
|
|
272
294
|
}
|
|
273
295
|
|
|
274
296
|
companion object {
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
package com.lodev09.truesheet.core
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.view.MotionEvent
|
|
5
|
+
import android.view.View
|
|
6
|
+
import android.widget.FrameLayout
|
|
7
|
+
import com.facebook.react.uimanager.JSPointerDispatcher
|
|
8
|
+
import com.facebook.react.uimanager.JSTouchDispatcher
|
|
9
|
+
import com.facebook.react.uimanager.RootView
|
|
10
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
11
|
+
import com.facebook.react.uimanager.TouchTargetHelper
|
|
12
|
+
import com.facebook.react.uimanager.events.EventDispatcher
|
|
13
|
+
import com.lodev09.truesheet.utils.TouchEventDeduper
|
|
14
|
+
|
|
15
|
+
interface TrueSheetOverlayRootViewDelegate {
|
|
16
|
+
val eventDispatcher: EventDispatcher?
|
|
17
|
+
fun overlayRootViewDidChangeSize(width: Int, height: Int)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Window-level view hosting an overlay's children, attached to the activity's
|
|
22
|
+
* content view above the sheet coordinators.
|
|
23
|
+
*
|
|
24
|
+
* Implements RootView to dispatch touches to JS. Touches that miss every child
|
|
25
|
+
* fall through to the views beneath.
|
|
26
|
+
*/
|
|
27
|
+
@SuppressLint("ViewConstructor")
|
|
28
|
+
class TrueSheetOverlayRootView(private val reactContext: ThemedReactContext) :
|
|
29
|
+
FrameLayout(reactContext),
|
|
30
|
+
RootView {
|
|
31
|
+
|
|
32
|
+
var delegate: TrueSheetOverlayRootViewDelegate? = null
|
|
33
|
+
|
|
34
|
+
private val eventDispatcher: EventDispatcher?
|
|
35
|
+
get() = delegate?.eventDispatcher
|
|
36
|
+
|
|
37
|
+
private val jsTouchDispatcher = JSTouchDispatcher(this)
|
|
38
|
+
private val jsPointerDispatcher = JSPointerDispatcher(this)
|
|
39
|
+
private val touchDeduper = TouchEventDeduper()
|
|
40
|
+
|
|
41
|
+
// Whether the current touch stream landed on a child
|
|
42
|
+
private var isHandlingStream = false
|
|
43
|
+
|
|
44
|
+
init {
|
|
45
|
+
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
46
|
+
|
|
47
|
+
// Draws and receives touches above the sheet coordinators regardless of add order
|
|
48
|
+
translationZ = OVERLAY_Z
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Children are positioned by Fabric — a FrameLayout pass would stretch them to match_parent
|
|
52
|
+
override fun onLayout(
|
|
53
|
+
changed: Boolean,
|
|
54
|
+
left: Int,
|
|
55
|
+
top: Int,
|
|
56
|
+
right: Int,
|
|
57
|
+
bottom: Int
|
|
58
|
+
) {
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
62
|
+
super.onSizeChanged(w, h, oldw, oldh)
|
|
63
|
+
delegate?.overlayRootViewDidChangeSize(w, h)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
|
|
67
|
+
if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
|
|
68
|
+
// The path ends with this root, so a touch that misses every child resolves to it
|
|
69
|
+
val target = TouchTargetHelper
|
|
70
|
+
.findTargetPathAndCoordinatesForTouch(ev.x, ev.y, this, FloatArray(2))
|
|
71
|
+
.firstNotNullOfOrNull { it.getView() }
|
|
72
|
+
isHandlingStream = target != null && target !== this
|
|
73
|
+
}
|
|
74
|
+
if (!isHandlingStream) return false
|
|
75
|
+
return super.dispatchTouchEvent(ev)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ==================== RootView Implementation ====================
|
|
79
|
+
|
|
80
|
+
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
|
|
81
|
+
eventDispatcher?.let { dispatcher ->
|
|
82
|
+
if (touchDeduper.shouldDispatch(event)) {
|
|
83
|
+
jsTouchDispatcher.handleTouchEvent(event, dispatcher, reactContext)
|
|
84
|
+
}
|
|
85
|
+
jsPointerDispatcher.handleMotionEvent(event, dispatcher, true)
|
|
86
|
+
}
|
|
87
|
+
return super.onInterceptTouchEvent(event)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
|
|
91
|
+
// Mirrors ReactSurfaceView: keep receiving onInterceptTouchEvent so
|
|
92
|
+
// jsTouchDispatcher sees the gesture end, but forward the request up the tree.
|
|
93
|
+
parent?.requestDisallowInterceptTouchEvent(disallowIntercept)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@SuppressLint("ClickableViewAccessibility")
|
|
97
|
+
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
98
|
+
eventDispatcher?.let { dispatcher ->
|
|
99
|
+
if (touchDeduper.shouldDispatch(event)) {
|
|
100
|
+
jsTouchDispatcher.handleTouchEvent(event, dispatcher, reactContext)
|
|
101
|
+
}
|
|
102
|
+
jsPointerDispatcher.handleMotionEvent(event, dispatcher, false)
|
|
103
|
+
}
|
|
104
|
+
super.onTouchEvent(event)
|
|
105
|
+
return true
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
override fun onInterceptHoverEvent(event: MotionEvent): Boolean {
|
|
109
|
+
eventDispatcher?.let { jsPointerDispatcher.handleMotionEvent(event, it, true) }
|
|
110
|
+
return super.onHoverEvent(event)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
override fun onHoverEvent(event: MotionEvent): Boolean {
|
|
114
|
+
eventDispatcher?.let { jsPointerDispatcher.handleMotionEvent(event, it, false) }
|
|
115
|
+
return super.onHoverEvent(event)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
override fun onChildStartedNativeGesture(childView: View?, ev: MotionEvent) {
|
|
119
|
+
eventDispatcher?.let { dispatcher ->
|
|
120
|
+
jsTouchDispatcher.onChildStartedNativeGesture(ev, dispatcher)
|
|
121
|
+
jsPointerDispatcher.onChildStartedNativeGesture(childView, ev, dispatcher)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
override fun onChildEndedNativeGesture(childView: View, ev: MotionEvent) {
|
|
126
|
+
eventDispatcher?.let { jsTouchDispatcher.onChildEndedNativeGesture(ev, it) }
|
|
127
|
+
jsPointerDispatcher.onChildEndedNativeGesture()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
override fun handleException(t: Throwable) {
|
|
131
|
+
reactContext.reactApplicationContext.handleException(RuntimeException(t))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
companion object {
|
|
135
|
+
private const val OVERLAY_Z = 1000f
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -93,6 +93,21 @@ class DidDismissEvent(surfaceId: Int, viewId: Int) : Event<DidDismissEvent>(surf
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Fired when the user tries to dismiss a non-dismissible sheet
|
|
98
|
+
*/
|
|
99
|
+
class DismissAttemptEvent(surfaceId: Int, viewId: Int) : Event<DismissAttemptEvent>(surfaceId, viewId) {
|
|
100
|
+
|
|
101
|
+
override fun getEventName(): String = EVENT_NAME
|
|
102
|
+
|
|
103
|
+
override fun getEventData(): WritableMap = Arguments.createMap()
|
|
104
|
+
|
|
105
|
+
companion object {
|
|
106
|
+
const val EVENT_NAME = "topDismissAttempt"
|
|
107
|
+
const val REGISTRATION_NAME = "onDismissAttempt"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
96
111
|
/**
|
|
97
112
|
* Fired when the sheet visibility changes due to screen transitions
|
|
98
113
|
*/
|
|
@@ -19,7 +19,7 @@ object KeyboardUtils {
|
|
|
19
19
|
return isKeyboardVisible(rootView)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
fun isKeyboardVisible(view: View): Boolean {
|
|
23
23
|
val insets = ViewCompat.getRootWindowInsets(view) ?: return false
|
|
24
24
|
return insets.isVisible(WindowInsetsCompat.Type.ime())
|
|
25
25
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package com.lodev09.truesheet.utils
|
|
2
|
+
|
|
3
|
+
import android.view.MotionEvent
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Guards a nested RootView against dispatching the same [MotionEvent] to JS twice.
|
|
7
|
+
*
|
|
8
|
+
* An event that no child consumes reaches both `onInterceptTouchEvent` and `onTouchEvent`,
|
|
9
|
+
* so dispatching from both starts a second JS touch stream for the same gesture.
|
|
10
|
+
*
|
|
11
|
+
* A plain root can live with that, but the sheet's roots are nested: touches that hit no
|
|
12
|
+
* React child resolve to the root's own tag, and the second stream then finds that view
|
|
13
|
+
* already holding the responder, so React only asks its ancestors - handing the touch to
|
|
14
|
+
* touchables outside the sheet.
|
|
15
|
+
*/
|
|
16
|
+
internal class TouchEventDeduper {
|
|
17
|
+
private var downTime = Long.MIN_VALUE
|
|
18
|
+
private var eventTime = Long.MIN_VALUE
|
|
19
|
+
private var action = -1
|
|
20
|
+
|
|
21
|
+
fun shouldDispatch(event: MotionEvent): Boolean {
|
|
22
|
+
if (event.downTime == downTime && event.eventTime == eventTime && event.action == action) {
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
downTime = event.downTime
|
|
27
|
+
eventTime = event.eventTime
|
|
28
|
+
action = event.action
|
|
29
|
+
return true
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -2,8 +2,10 @@ package com.lodev09.truesheet.utils
|
|
|
2
2
|
|
|
3
3
|
import android.view.View
|
|
4
4
|
import android.view.ViewGroup
|
|
5
|
+
import android.view.ViewParent
|
|
5
6
|
import android.widget.ScrollView
|
|
6
7
|
import androidx.core.widget.NestedScrollView
|
|
8
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
7
9
|
|
|
8
10
|
fun View.isDescendantOf(ancestor: View): Boolean {
|
|
9
11
|
if (!isAttachedToWindow) return false
|
|
@@ -15,16 +17,26 @@ fun View.isDescendantOf(ancestor: View): Boolean {
|
|
|
15
17
|
return false
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
fun ViewGroup.smoothScrollBy(dx: Int, dy: Int) {
|
|
19
|
-
when (this) {
|
|
20
|
-
is ScrollView -> smoothScrollBy(dx, dy)
|
|
21
|
-
is NestedScrollView -> smoothScrollBy(dx, dy)
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
20
|
fun ViewGroup.smoothScrollTo(x: Int, y: Int) {
|
|
26
21
|
when (this) {
|
|
27
22
|
is ScrollView -> smoothScrollTo(x, y)
|
|
28
23
|
is NestedScrollView -> smoothScrollTo(x, y)
|
|
29
24
|
}
|
|
30
25
|
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The content view hosting this view — the activity's, or a Modal dialog's.
|
|
29
|
+
* Window-level views (sheet coordinators, overlays) attach here.
|
|
30
|
+
*/
|
|
31
|
+
fun View.findRootContainerView(reactContext: ThemedReactContext): ViewGroup? {
|
|
32
|
+
var current: ViewParent? = parent
|
|
33
|
+
|
|
34
|
+
while (current != null) {
|
|
35
|
+
if (current is ViewGroup && current.id == android.R.id.content) {
|
|
36
|
+
return current
|
|
37
|
+
}
|
|
38
|
+
current = current.parent
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return reactContext.currentActivity?.findViewById(android.R.id.content)
|
|
42
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#include <jsi/jsi.h>
|
|
6
6
|
#include <react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h>
|
|
7
7
|
#include <react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h>
|
|
8
|
+
#include <react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h>
|
|
8
9
|
#include <react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h>
|
|
9
10
|
|
|
10
11
|
namespace facebook {
|
package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h
CHANGED
|
@@ -11,14 +11,6 @@ namespace facebook::react {
|
|
|
11
11
|
class TrueSheetContentViewComponentDescriptor final
|
|
12
12
|
: public ConcreteComponentDescriptor<TrueSheetContentViewShadowNode> {
|
|
13
13
|
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
|
|
14
|
-
|
|
15
|
-
void adopt(ShadowNode &shadowNode) const override {
|
|
16
|
-
auto &concreteShadowNode =
|
|
17
|
-
static_cast<TrueSheetContentViewShadowNode &>(shadowNode);
|
|
18
|
-
concreteShadowNode.adjustLayoutWithState();
|
|
19
|
-
|
|
20
|
-
ConcreteComponentDescriptor::adopt(shadowNode);
|
|
21
|
-
}
|
|
22
14
|
};
|
|
23
15
|
|
|
24
16
|
} // namespace facebook::react
|