@lodev09/react-native-true-sheet 4.0.0-beta.2 → 4.0.0-beta.20
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 +50 -16
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +302 -184
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +21 -110
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +116 -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 +119 -50
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +360 -163
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +54 -12
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +22 -9
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +84 -5
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +104 -39
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +3 -0
- 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 +15 -2
- package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
- 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 +1 -9
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +110 -30
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +30 -7
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +2 -2
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +9 -8
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +9 -3
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +3 -6
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +4 -6
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +43 -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 +33 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +7 -40
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
- package/ios/TrueSheetContainerView.h +27 -7
- package/ios/TrueSheetContainerView.mm +31 -11
- package/ios/TrueSheetContentView.h +43 -15
- package/ios/TrueSheetContentView.mm +211 -189
- package/ios/TrueSheetFooterView.h +6 -0
- package/ios/TrueSheetFooterView.mm +93 -17
- 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 +212 -55
- package/ios/TrueSheetViewController.h +31 -3
- package/ios/TrueSheetViewController.mm +393 -94
- package/ios/core/TrueSheetBlurView.h +0 -2
- package/ios/core/TrueSheetBlurView.mm +3 -36
- package/ios/core/TrueSheetDetentCalculator.h +7 -1
- package/ios/core/TrueSheetDetentCalculator.mm +27 -4
- package/ios/core/TrueSheetGrabberView.mm +13 -3
- package/ios/core/TrueSheetKeyboardObserver.mm +2 -0
- 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 +173 -39
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +218 -243
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetBackground.js +15 -0
- package/lib/module/TrueSheetBackground.js.map +1 -0
- 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 +20 -15
- package/lib/module/TrueSheetPeek.web.js.map +1 -1
- package/lib/module/detentBackgrounds.js +21 -0
- package/lib/module/detentBackgrounds.js.map +1 -0
- package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +14 -10
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/mocks/index.js +18 -2
- package/lib/module/mocks/index.js.map +1 -1
- package/lib/module/mocks/navigationExpoRouter.js.map +1 -1
- package/lib/module/navigation/base-types.js +2 -0
- package/lib/module/navigation/base-types.js.map +1 -0
- package/lib/module/navigation/createTrueSheetRouter.js +0 -9
- package/lib/module/navigation/createTrueSheetRouter.js.map +1 -1
- 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 +10 -7
- package/lib/module/navigation/expo-router/index.js.map +1 -1
- 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.map +1 -1
- package/lib/module/navigation/screen/useSheetScreenState.js +1 -0
- package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
- package/lib/module/web/constants.js +1 -1
- package/lib/module/web/constants.js.map +1 -1
- package/lib/module/web/layout.js +168 -0
- package/lib/module/web/layout.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 +1 -0
- package/lib/module/web/vaul/helpers.js.map +1 -1
- package/lib/module/web/vaul/index.js +44 -6
- package/lib/module/web/vaul/index.js.map +1 -1
- package/lib/module/web/vaul/use-snap-points.js +6 -8
- package/lib/module/web/vaul/use-snap-points.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 +135 -34
- 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/TrueSheetBackground.d.ts +3 -0
- package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
- 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 +8 -4
- package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
- package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
- package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
- 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 +11 -7
- 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/navigationExpoRouter.d.ts +3 -3
- package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -1
- 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 +2 -13
- package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -1
- 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 +8 -6
- package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -1
- 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 +3 -4
- package/lib/typescript/src/navigation/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +2 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
- package/lib/typescript/src/navigation/types.d.ts +41 -5
- package/lib/typescript/src/navigation/types.d.ts.map +1 -1
- package/lib/typescript/src/web/constants.d.ts +1 -1
- package/lib/typescript/src/web/constants.d.ts.map +1 -1
- package/lib/typescript/src/web/layout.d.ts +19 -0
- package/lib/typescript/src/web/layout.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 +1 -0
- 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/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
- package/package.json +17 -8
- package/react-native.config.js +1 -0
- package/src/TrueSheet.tsx +206 -48
- package/src/TrueSheet.types.ts +142 -35
- package/src/TrueSheet.web.tsx +270 -290
- package/src/TrueSheetBackground.tsx +17 -0
- package/src/TrueSheetOverlay.tsx +24 -0
- package/src/TrueSheetOverlay.web.tsx +33 -0
- package/src/TrueSheetPeek.web.tsx +25 -23
- package/src/detentBackgrounds.ts +24 -0
- package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/src/fabric/TrueSheetViewNativeComponent.ts +14 -10
- package/src/index.ts +1 -0
- package/src/mocks/index.ts +17 -2
- package/src/mocks/navigationExpoRouter.ts +6 -5
- package/src/navigation/base-types.ts +20 -0
- package/src/navigation/createTrueSheetRouter.ts +5 -33
- package/src/navigation/expo-router/base-types.ts +18 -0
- package/src/navigation/expo-router/index.ts +28 -30
- package/src/navigation/expo-router/types.ts +116 -0
- package/src/navigation/index.ts +6 -7
- package/src/navigation/screen/useSheetScreenState.ts +2 -0
- package/src/navigation/types.ts +75 -22
- package/src/web/constants.ts +1 -1
- package/src/web/layout.ts +196 -0
- package/src/web/vaul/constants.ts +4 -0
- package/src/web/vaul/context.ts +1 -0
- package/src/web/vaul/helpers.ts +5 -0
- package/src/web/vaul/index.tsx +67 -5
- package/src/web/vaul/use-snap-points.ts +6 -14
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
package com.lodev09.truesheet
|
|
2
2
|
|
|
3
3
|
import android.view.WindowManager
|
|
4
|
+
import com.facebook.react.bridge.ColorPropConverter
|
|
4
5
|
import com.facebook.react.bridge.ReadableArray
|
|
5
6
|
import com.facebook.react.bridge.ReadableMap
|
|
7
|
+
import com.facebook.react.bridge.ReadableType
|
|
6
8
|
import com.facebook.react.module.annotations.ReactModule
|
|
7
9
|
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
10
|
+
import com.facebook.react.uimanager.PointerEvents
|
|
8
11
|
import com.facebook.react.uimanager.ReactStylesDiffMap
|
|
9
12
|
import com.facebook.react.uimanager.StateWrapper
|
|
10
13
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
@@ -55,6 +58,20 @@ class TrueSheetViewManager :
|
|
|
55
58
|
|
|
56
59
|
override fun getDelegate(): ViewManagerDelegate<TrueSheetView> = delegate
|
|
57
60
|
|
|
61
|
+
// The codegen delegate drops props outside the spec (pointerEvents comes from the
|
|
62
|
+
// host's style), so apply it here after the delegate pass.
|
|
63
|
+
override fun updateProperties(viewToUpdate: TrueSheetView, props: ReactStylesDiffMap) {
|
|
64
|
+
super.updateProperties(viewToUpdate, props)
|
|
65
|
+
if (props.hasKey("pointerEvents")) {
|
|
66
|
+
setPointerEvents(viewToUpdate, props.getString("pointerEvents"))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@ReactProp(name = "pointerEvents")
|
|
71
|
+
fun setPointerEvents(view: TrueSheetView, pointerEventsStr: String?) {
|
|
72
|
+
view.pointerEvents = PointerEvents.parsePointerEvents(pointerEventsStr)
|
|
73
|
+
}
|
|
74
|
+
|
|
58
75
|
/**
|
|
59
76
|
* Export custom direct event types for Fabric
|
|
60
77
|
* Uses Kotlin native collections with decoupled event classes
|
|
@@ -66,6 +83,7 @@ class TrueSheetViewManager :
|
|
|
66
83
|
DidPresentEvent.EVENT_NAME to hashMapOf("registrationName" to DidPresentEvent.REGISTRATION_NAME),
|
|
67
84
|
WillDismissEvent.EVENT_NAME to hashMapOf("registrationName" to WillDismissEvent.REGISTRATION_NAME),
|
|
68
85
|
DidDismissEvent.EVENT_NAME to hashMapOf("registrationName" to DidDismissEvent.REGISTRATION_NAME),
|
|
86
|
+
DismissAttemptEvent.EVENT_NAME to hashMapOf("registrationName" to DismissAttemptEvent.REGISTRATION_NAME),
|
|
69
87
|
DetentChangeEvent.EVENT_NAME to hashMapOf("registrationName" to DetentChangeEvent.REGISTRATION_NAME),
|
|
70
88
|
DragBeginEvent.EVENT_NAME to hashMapOf("registrationName" to DragBeginEvent.REGISTRATION_NAME),
|
|
71
89
|
DragChangeEvent.EVENT_NAME to hashMapOf("registrationName" to DragChangeEvent.REGISTRATION_NAME),
|
|
@@ -98,6 +116,21 @@ class TrueSheetViewManager :
|
|
|
98
116
|
view.setDetents(detents)
|
|
99
117
|
}
|
|
100
118
|
|
|
119
|
+
@ReactProp(name = "detentBackgrounds")
|
|
120
|
+
override fun setDetentBackgrounds(view: TrueSheetView, value: ReadableArray?) {
|
|
121
|
+
view.setDetentBackgrounds(
|
|
122
|
+
(0 until (value?.size() ?: 0)).map { index ->
|
|
123
|
+
val entry = value?.getMap(index)
|
|
124
|
+
if (entry != null && entry.hasKey("color") && !entry.isNull("color")) {
|
|
125
|
+
val color = if (entry.getType("color") == ReadableType.Map) entry.getMap("color") else entry.getDouble("color")
|
|
126
|
+
ColorPropConverter.getColor(color, view.context)
|
|
127
|
+
} else {
|
|
128
|
+
null
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
101
134
|
@ReactProp(name = "backgroundColor", customType = "Color")
|
|
102
135
|
override fun setBackgroundColor(view: TrueSheetView, color: Int?) {
|
|
103
136
|
view.setSheetBackgroundColor(color)
|
|
@@ -179,6 +212,7 @@ class TrueSheetViewManager :
|
|
|
179
212
|
@ReactProp(name = "initialDetentIndex", defaultInt = -1)
|
|
180
213
|
override fun setInitialDetentIndex(view: TrueSheetView, index: Int) {
|
|
181
214
|
view.initialDetentIndex = index
|
|
215
|
+
view.presentInitialIfNeeded()
|
|
182
216
|
}
|
|
183
217
|
|
|
184
218
|
@ReactProp(name = "initialDetentAnimated", defaultBoolean = true)
|
|
@@ -196,14 +230,14 @@ class TrueSheetViewManager :
|
|
|
196
230
|
view.setMaxContentWidth(if (width > 0) width.dpToPx().toInt() else null)
|
|
197
231
|
}
|
|
198
232
|
|
|
199
|
-
@ReactProp(name = "
|
|
200
|
-
override fun
|
|
201
|
-
view.
|
|
233
|
+
@ReactProp(name = "placement")
|
|
234
|
+
override fun setPlacement(view: TrueSheetView, placement: String?) {
|
|
235
|
+
view.setPlacement(placement)
|
|
202
236
|
}
|
|
203
237
|
|
|
204
|
-
@ReactProp(name = "
|
|
205
|
-
override fun
|
|
206
|
-
view.
|
|
238
|
+
@ReactProp(name = "placementOffset", defaultDouble = 0.0)
|
|
239
|
+
override fun setPlacementOffset(view: TrueSheetView, offset: Double) {
|
|
240
|
+
view.setPlacementOffset(if (offset > 0) offset.dpToPx().toInt() else 0)
|
|
207
241
|
}
|
|
208
242
|
|
|
209
243
|
@ReactProp(name = "backgroundBlur")
|
|
@@ -211,11 +245,6 @@ class TrueSheetViewManager :
|
|
|
211
245
|
// iOS-specific prop - no-op on Android
|
|
212
246
|
}
|
|
213
247
|
|
|
214
|
-
@ReactProp(name = "blurOptions")
|
|
215
|
-
override fun setBlurOptions(view: TrueSheetView, options: ReadableMap?) {
|
|
216
|
-
// iOS-specific prop - no-op on Android
|
|
217
|
-
}
|
|
218
|
-
|
|
219
248
|
@ReactProp(name = "insetAdjustment")
|
|
220
249
|
override fun setInsetAdjustment(view: TrueSheetView, insetAdjustment: String?) {
|
|
221
250
|
view.setInsetAdjustment(insetAdjustment ?: "automatic")
|
|
@@ -231,6 +260,11 @@ class TrueSheetViewManager :
|
|
|
231
260
|
view.setSheetElevation(elevation.toFloat())
|
|
232
261
|
}
|
|
233
262
|
|
|
263
|
+
@ReactProp(name = "scrollableHandle", defaultInt = -1)
|
|
264
|
+
override fun setScrollableHandle(view: TrueSheetView, handle: Int) {
|
|
265
|
+
view.setScrollableHandle(handle)
|
|
266
|
+
}
|
|
267
|
+
|
|
234
268
|
@ReactProp(name = "scrollableOptions")
|
|
235
269
|
override fun setScrollableOptions(view: TrueSheetView, options: ReadableMap?) {
|
|
236
270
|
if (options == null) {
|
|
@@ -240,7 +274,11 @@ class TrueSheetViewManager :
|
|
|
240
274
|
|
|
241
275
|
val scrollableOptions = ScrollableOptions(
|
|
242
276
|
keyboardScrollOffset = if (options.hasKey("keyboardScrollOffset")) options.getDouble("keyboardScrollOffset").toFloat() else 0f,
|
|
243
|
-
|
|
277
|
+
keyboardOffset = if (options.hasKey("keyboardOffset")) options.getDouble("keyboardOffset").toFloat() else 0f,
|
|
278
|
+
scrollingExpandsSheet = if (options.hasKey("scrollingExpandsSheet")) options.getBoolean("scrollingExpandsSheet") else true,
|
|
279
|
+
contentInsetAdjustment = TrueSheetContentInsetAdjustment.fromString(
|
|
280
|
+
if (options.hasKey("contentInsetAdjustment")) options.getString("contentInsetAdjustment") else null
|
|
281
|
+
)
|
|
244
282
|
)
|
|
245
283
|
view.setScrollableOptions(scrollableOptions)
|
|
246
284
|
}
|
|
@@ -259,6 +297,10 @@ class TrueSheetViewManager :
|
|
|
259
297
|
val keyboardOffset =
|
|
260
298
|
if (options != null && options.hasKey("keyboardOffset")) options.getDouble("keyboardOffset").toFloat() else 0f
|
|
261
299
|
view.setFooterKeyboardOffset(keyboardOffset)
|
|
300
|
+
|
|
301
|
+
val avoidKeyboard =
|
|
302
|
+
if (options != null && options.hasKey("avoidKeyboard")) options.getBoolean("avoidKeyboard") else true
|
|
303
|
+
view.setFooterAvoidsKeyboard(avoidKeyboard)
|
|
262
304
|
}
|
|
263
305
|
|
|
264
306
|
companion object {
|
|
@@ -18,7 +18,7 @@ import androidx.core.view.ViewCompat
|
|
|
18
18
|
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
19
19
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
20
20
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
21
|
-
import com.lodev09.truesheet.
|
|
21
|
+
import com.lodev09.truesheet.TrueSheetPlacement
|
|
22
22
|
import com.lodev09.truesheet.utils.ScreenUtils
|
|
23
23
|
|
|
24
24
|
interface TrueSheetBottomSheetViewDelegate {
|
|
@@ -27,8 +27,8 @@ interface TrueSheetBottomSheetViewDelegate {
|
|
|
27
27
|
val sheetElevation: Float
|
|
28
28
|
val sheetBackgroundColor: Int?
|
|
29
29
|
val maxContentWidth: Int?
|
|
30
|
-
val
|
|
31
|
-
val
|
|
30
|
+
val placement: TrueSheetPlacement
|
|
31
|
+
val placementOffset: Int
|
|
32
32
|
val grabber: Boolean
|
|
33
33
|
val grabberOptions: GrabberOptions?
|
|
34
34
|
val accessibilityOptions: AccessibilityOptions?
|
|
@@ -94,13 +94,19 @@ class TrueSheetBottomSheetView(private val reactContext: ThemedReactContext) : F
|
|
|
94
94
|
// =============================================================================
|
|
95
95
|
|
|
96
96
|
private fun resolveAnchor(): Pair<Int, Int> {
|
|
97
|
-
val
|
|
98
|
-
val gravity = when (
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
val placement = if (ScreenUtils.isPortraitPhone(reactContext)) null else delegate?.placement
|
|
98
|
+
val gravity = when (placement) {
|
|
99
|
+
TrueSheetPlacement.LEADING -> Gravity.START
|
|
100
|
+
TrueSheetPlacement.TRAILING -> Gravity.END
|
|
101
101
|
else -> Gravity.CENTER_HORIZONTAL
|
|
102
102
|
} or Gravity.BOTTOM
|
|
103
|
-
val margin = if (
|
|
103
|
+
val margin = if (placement == TrueSheetPlacement.LEADING ||
|
|
104
|
+
placement == TrueSheetPlacement.TRAILING
|
|
105
|
+
) {
|
|
106
|
+
delegate?.placementOffset ?: 0
|
|
107
|
+
} else {
|
|
108
|
+
0
|
|
109
|
+
}
|
|
104
110
|
return Pair(gravity, margin)
|
|
105
111
|
}
|
|
106
112
|
|
|
@@ -173,7 +179,14 @@ class TrueSheetBottomSheetView(private val reactContext: ThemedReactContext) : F
|
|
|
173
179
|
clipToOutline = true
|
|
174
180
|
}
|
|
175
181
|
|
|
176
|
-
|
|
182
|
+
fun updateBackgroundColor(color: Int) {
|
|
183
|
+
(background as? ShapeDrawable)?.let {
|
|
184
|
+
it.paint.color = color
|
|
185
|
+
it.invalidateSelf()
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
fun getDefaultBackgroundColor(): Int {
|
|
177
190
|
val isNight = (
|
|
178
191
|
reactContext.resources.configuration.uiMode and
|
|
179
192
|
Configuration.UI_MODE_NIGHT_MASK
|
|
@@ -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,13 +65,26 @@ 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
|
|
64
72
|
// manually.
|
|
65
73
|
private var streamSheetDraggable = false
|
|
66
74
|
private var streamDraggableEditText = false
|
|
75
|
+
private var streamScrollsIndependently = false
|
|
76
|
+
|
|
77
|
+
// Pull-down tracking. BottomSheetBehavior gives no callback when a downward drag
|
|
78
|
+
// is clamped by `isHideable = false` — the sheet stays put (or the scrollable
|
|
79
|
+
// overscrolls) and no state change fires. Accumulate the downward finger travel
|
|
80
|
+
// neither the sheet nor its scrollable followed and report it on release so the
|
|
81
|
+
// controller can surface a blocked dismiss attempt.
|
|
82
|
+
private var pullTracking = false
|
|
83
|
+
private var pullStartX = 0f
|
|
84
|
+
private var pullStartY = 0f
|
|
85
|
+
private var pullLastY = 0f
|
|
86
|
+
private var pullOverdrag = 0f
|
|
87
|
+
private var pullKeyboardVisible = false
|
|
67
88
|
|
|
68
89
|
init {
|
|
69
90
|
layoutParams = LayoutParams(
|
|
@@ -94,6 +115,49 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
94
115
|
override val pointerEvents: PointerEvents
|
|
95
116
|
get() = PointerEvents.BOX_NONE
|
|
96
117
|
|
|
118
|
+
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
|
|
119
|
+
when (ev.actionMasked) {
|
|
120
|
+
MotionEvent.ACTION_DOWN -> {
|
|
121
|
+
val sheet = delegate?.findSheetView()
|
|
122
|
+
pullTracking = sheet != null && isTouchInside(sheet, ev)
|
|
123
|
+
pullStartX = ev.rawX
|
|
124
|
+
pullStartY = ev.rawY
|
|
125
|
+
pullLastY = ev.rawY
|
|
126
|
+
pullOverdrag = 0f
|
|
127
|
+
pullKeyboardVisible = pullTracking && KeyboardUtils.isKeyboardVisible(this)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
MotionEvent.ACTION_MOVE -> if (pullTracking) {
|
|
131
|
+
val sheet = delegate?.findSheetView()
|
|
132
|
+
val topBefore = sheet?.top
|
|
133
|
+
val handled = super.dispatchTouchEvent(ev)
|
|
134
|
+
val step = ev.rawY - pullLastY
|
|
135
|
+
pullLastY = ev.rawY
|
|
136
|
+
// Finger moved down but the sheet stayed put and the scrollable is at
|
|
137
|
+
// its top — the pull was clamped
|
|
138
|
+
if (step > 0 && sheet != null && sheet.top == topBefore && delegate?.findScrollView()?.canScrollVertically(-1) != true) {
|
|
139
|
+
pullOverdrag += step
|
|
140
|
+
}
|
|
141
|
+
return handled
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
MotionEvent.ACTION_UP -> if (pullTracking) {
|
|
145
|
+
pullTracking = false
|
|
146
|
+
val dx = kotlin.math.abs(ev.rawX - pullStartX)
|
|
147
|
+
val dy = ev.rawY - pullStartY
|
|
148
|
+
// Let the behavior settle first so the controller sees the resting position.
|
|
149
|
+
val handled = super.dispatchTouchEvent(ev)
|
|
150
|
+
if (dy > dx && pullOverdrag > PULL_DOWN_THRESHOLD_DP.dpToPx()) {
|
|
151
|
+
delegate?.coordinatorLayoutDidPullDown(pullOverdrag.toInt(), pullKeyboardVisible)
|
|
152
|
+
}
|
|
153
|
+
return handled
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
MotionEvent.ACTION_CANCEL -> pullTracking = false
|
|
157
|
+
}
|
|
158
|
+
return super.dispatchTouchEvent(ev)
|
|
159
|
+
}
|
|
160
|
+
|
|
97
161
|
/**
|
|
98
162
|
* Clears stale `nestedScrollingChildRef` from BottomSheetBehavior.
|
|
99
163
|
*
|
|
@@ -127,19 +191,20 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
127
191
|
|
|
128
192
|
/**
|
|
129
193
|
* 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
|
|
194
|
+
* decides whether the sheet may drag from it — inside the sheet, outside the detected
|
|
131
195
|
* scrollable, and nothing along the target's chain scrolls vertically.
|
|
132
196
|
*/
|
|
133
197
|
private fun updateStreamTargetFlags(ev: MotionEvent) {
|
|
134
198
|
streamSheetDraggable = false
|
|
135
199
|
streamDraggableEditText = false
|
|
200
|
+
streamScrollsIndependently = false
|
|
136
201
|
|
|
137
202
|
val sheet = delegate?.findSheetView() ?: return
|
|
138
203
|
val target = TouchTargetHelper.findTargetPathAndCoordinatesForTouch(ev.x, ev.y, this, FloatArray(2))
|
|
139
204
|
.firstNotNullOfOrNull { it.getView() } ?: return
|
|
140
205
|
if (target !== sheet && !target.isDescendantOf(sheet)) return
|
|
141
206
|
|
|
142
|
-
// A
|
|
207
|
+
// A detected scrollable that can scroll (or hosts pull-to-refresh) owns its
|
|
143
208
|
// touches — nested scrolling drags the sheet. When it can't scroll (e.g.
|
|
144
209
|
// the sheet is fully expanded and the content fits) it eats the gesture
|
|
145
210
|
// instead, so fall through and let the sheet drag from it — an EditText
|
|
@@ -152,7 +217,10 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
152
217
|
|
|
153
218
|
var view: View? = target
|
|
154
219
|
while (view != null && view !== this) {
|
|
155
|
-
if (view.canScrollVertically(1) || view.canScrollVertically(-1))
|
|
220
|
+
if (view.canScrollVertically(1) || view.canScrollVertically(-1)) {
|
|
221
|
+
streamScrollsIndependently = !view.isNestedScrollingEnabled
|
|
222
|
+
return
|
|
223
|
+
}
|
|
156
224
|
view = view.parent as? View
|
|
157
225
|
}
|
|
158
226
|
|
|
@@ -160,6 +228,14 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
160
228
|
streamDraggableEditText = target is EditText
|
|
161
229
|
}
|
|
162
230
|
|
|
231
|
+
private fun isTouchInside(view: View, ev: MotionEvent): Boolean {
|
|
232
|
+
val loc = IntArray(2)
|
|
233
|
+
view.getLocationOnScreen(loc)
|
|
234
|
+
val x = ev.rawX.toInt()
|
|
235
|
+
val y = ev.rawY.toInt()
|
|
236
|
+
return x >= loc[0] && x <= loc[0] + view.width && y >= loc[1] && y <= loc[1] + view.height
|
|
237
|
+
}
|
|
238
|
+
|
|
163
239
|
private fun findDescendantViewAt(view: View, rawX: Int, rawY: Int, predicate: (View) -> Boolean): View? {
|
|
164
240
|
val loc = IntArray(2)
|
|
165
241
|
view.getLocationOnScreen(loc)
|
|
@@ -174,7 +250,7 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
174
250
|
}
|
|
175
251
|
|
|
176
252
|
/**
|
|
177
|
-
* Called by descendant RootView
|
|
253
|
+
* Called by the descendant RootView (controller) when a child
|
|
178
254
|
* starts a native gesture — e.g. an RNGH handler activated or a scrollable began
|
|
179
255
|
* scrolling. Yields sheet interception for the rest of the touch stream.
|
|
180
256
|
*/
|
|
@@ -226,6 +302,8 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
226
302
|
return false
|
|
227
303
|
}
|
|
228
304
|
|
|
305
|
+
if (streamScrollsIndependently) return false
|
|
306
|
+
|
|
229
307
|
if (streamGestureClaimed) {
|
|
230
308
|
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
|
231
309
|
streamGestureClaimed = false
|
|
@@ -299,5 +377,6 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
299
377
|
companion object {
|
|
300
378
|
private const val GESTURE_HANDLER_ROOT_VIEW_CLASS = "com.swmansion.gesturehandler.react.RNGestureHandlerRootView"
|
|
301
379
|
private const val GESTURE_CLAIM_SLOP_FACTOR = 3
|
|
380
|
+
private const val PULL_DOWN_THRESHOLD_DP = 24f
|
|
302
381
|
}
|
|
303
382
|
}
|
|
@@ -18,11 +18,27 @@ interface TrueSheetDetentCalculatorDelegate {
|
|
|
18
18
|
val absoluteHeader: Boolean
|
|
19
19
|
val footerHeight: Int
|
|
20
20
|
val absoluteFooter: Boolean
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* True while the plugged scrollable is padded by the absolute footer's
|
|
24
|
+
* height (see ScrollableOptions.footerInsetAdjustment) — the footer then
|
|
25
|
+
* counts toward the auto detent like a relative one, so the content ends
|
|
26
|
+
* above it.
|
|
27
|
+
*/
|
|
28
|
+
val footerInsetAdjustment: Boolean
|
|
29
|
+
val footerAvoidsKeyboard: Boolean
|
|
21
30
|
val peekContentHeight: Int
|
|
22
31
|
val contentBottomInset: Int
|
|
23
32
|
val maxContentHeight: Int?
|
|
24
33
|
val keyboardInset: Int
|
|
25
34
|
val topInset: Int
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* True while the keyboard holds the sheet up: the collapsed stop then sits at
|
|
38
|
+
* the keyboard-free first detent (the "floor") so a drag-down dismisses the
|
|
39
|
+
* keyboard before it can dismiss the sheet.
|
|
40
|
+
*/
|
|
41
|
+
val hasKeyboardFloor: Boolean
|
|
26
42
|
}
|
|
27
43
|
|
|
28
44
|
/**
|
|
@@ -44,6 +60,15 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
44
60
|
private val maxContentHeight: Int? get() = delegate?.maxContentHeight
|
|
45
61
|
private val keyboardInset: Int get() = delegate?.keyboardInset ?: 0
|
|
46
62
|
private val topInset: Int get() = delegate?.topInset ?: 0
|
|
63
|
+
private val hasKeyboardFloor: Boolean get() = delegate?.hasKeyboardFloor == true
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* An absolute footer floats over the content and contributes no height to
|
|
67
|
+
* the auto detent — unless it pads the scrollable, then the content ends
|
|
68
|
+
* above it like a relative footer.
|
|
69
|
+
*/
|
|
70
|
+
private val footerInAuto: Boolean
|
|
71
|
+
get() = delegate?.absoluteFooter == false || delegate?.footerInsetAdjustment == true
|
|
47
72
|
|
|
48
73
|
/**
|
|
49
74
|
* Height for auto (-1.0) detents: content + header + footer height.
|
|
@@ -52,14 +77,14 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
52
77
|
private val autoDetentHeight: Int
|
|
53
78
|
get() = contentHeight +
|
|
54
79
|
(if (delegate?.absoluteHeader == true) 0 else headerHeight) +
|
|
55
|
-
(if (
|
|
80
|
+
(if (footerInAuto) footerHeight else 0)
|
|
56
81
|
|
|
57
82
|
/**
|
|
58
|
-
* Bottom inset for auto detents. A
|
|
59
|
-
* edge, so it absorbs the inset instead of adding it
|
|
83
|
+
* Bottom inset for auto detents. A footer counted in the auto height owns
|
|
84
|
+
* the sheet's bottom edge, so it absorbs the inset instead of adding it.
|
|
60
85
|
*/
|
|
61
86
|
private val autoDetentBottomInset: Int
|
|
62
|
-
get() = if (footerHeight > 0 &&
|
|
87
|
+
get() = if (footerHeight > 0 && footerInAuto) 0 else contentBottomInset
|
|
63
88
|
|
|
64
89
|
/**
|
|
65
90
|
* Bottom inset for peek detents. An absolute footer counts toward the peek
|
|
@@ -97,11 +122,37 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
97
122
|
(detent * screenHeight).toInt() + contentBottomInset
|
|
98
123
|
}
|
|
99
124
|
|
|
100
|
-
val
|
|
125
|
+
val keyboardHeight = if (includeKeyboard) keyboardInset else 0
|
|
126
|
+
val footerKeyboardOverlap = if (detent == -1.0 &&
|
|
127
|
+
delegate?.absoluteFooter == true &&
|
|
128
|
+
footerInAuto &&
|
|
129
|
+
delegate?.footerAvoidsKeyboard == false
|
|
130
|
+
) {
|
|
131
|
+
minOf(keyboardHeight, maxOf(0, footerHeight - contentBottomInset))
|
|
132
|
+
} else {
|
|
133
|
+
0
|
|
134
|
+
}
|
|
135
|
+
val height = baseHeight + keyboardHeight - footerKeyboardOverlap
|
|
101
136
|
val maxAllowedHeight = screenHeight + contentBottomInset
|
|
102
137
|
return maxContentHeight?.let { minOf(height, it, maxAllowedHeight) } ?: minOf(height, maxAllowedHeight)
|
|
103
138
|
}
|
|
104
139
|
|
|
140
|
+
fun getBackgroundIndex(sheetTop: Int, displayedIndex: Int): Int {
|
|
141
|
+
if (detents.isEmpty()) return -1
|
|
142
|
+
|
|
143
|
+
val height = getVisibleSheetHeight(sheetTop)
|
|
144
|
+
var nextIndex = displayedIndex.coerceIn(0, detents.size - 1)
|
|
145
|
+
var distance = kotlin.math.abs(height - getVisibleSheetHeight(getSheetTopForDetentIndex(nextIndex)))
|
|
146
|
+
for (index in detents.indices) {
|
|
147
|
+
val nextDistance = kotlin.math.abs(height - getVisibleSheetHeight(getSheetTopForDetentIndex(index)))
|
|
148
|
+
if (nextDistance < distance) {
|
|
149
|
+
nextIndex = index
|
|
150
|
+
distance = nextDistance
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return nextIndex
|
|
154
|
+
}
|
|
155
|
+
|
|
105
156
|
/**
|
|
106
157
|
* Get the expected sheet top position for a detent index.
|
|
107
158
|
*/
|
|
@@ -110,12 +161,31 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
110
161
|
RNLog.w(reactContext, "TrueSheet: Detent index ($index) is out of bounds (0..${detents.size - 1})")
|
|
111
162
|
return realScreenHeight
|
|
112
163
|
}
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
164
|
+
// With a keyboard floor only the last detent stays keyboard-shifted — it's
|
|
165
|
+
// the keyboard-expanded resting position; the stops below it are keyboard-free
|
|
166
|
+
val isKeyboardFreeStop = hasKeyboardFloor && index != detents.size - 1
|
|
167
|
+
return getSheetTop(detents[index], includeKeyboard = !isKeyboardFreeStop)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Top of the lowest stop the sheet can rest at: the keyboard floor while it's
|
|
172
|
+
* active, else the first detent.
|
|
173
|
+
*/
|
|
174
|
+
fun getLowestSheetTop(): Int = if (hasKeyboardFloor) getSheetTop(detents[0], includeKeyboard = false) else getSheetTopForDetentIndex(0)
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Top of the half stop while the keyboard floor is active: the keyboard-free
|
|
178
|
+
* second detent, or the floor itself for a single detent.
|
|
179
|
+
*/
|
|
180
|
+
fun getKeyboardHalfStopTop(): Int = getSheetTop(detents[minOf(1, detents.size - 1)], includeKeyboard = false)
|
|
181
|
+
|
|
182
|
+
// Clamp to the space the sheet can actually occupy — matching
|
|
183
|
+
// setupSheetDetents. A keyboard-inflated detent height can exceed it,
|
|
184
|
+
// placing the expected top above the screen while the real sheet stops
|
|
185
|
+
// at the top inset.
|
|
186
|
+
private fun getSheetTop(detent: Double, includeKeyboard: Boolean = true): Int {
|
|
117
187
|
val maxAvailableHeight = realScreenHeight - topInset
|
|
118
|
-
return realScreenHeight - minOf(getDetentHeight(
|
|
188
|
+
return realScreenHeight - minOf(getDetentHeight(detent, includeKeyboard), maxAvailableHeight)
|
|
119
189
|
}
|
|
120
190
|
|
|
121
191
|
/**
|
|
@@ -152,6 +222,9 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
152
222
|
*/
|
|
153
223
|
fun getStateForDetentIndex(index: Int): Int {
|
|
154
224
|
val stateMap = getDetentStateMap() ?: return BottomSheetBehavior.STATE_HIDDEN
|
|
225
|
+
// With a keyboard floor the keyboard-expanded last detent lives at the
|
|
226
|
+
// expanded stop — the half stop (which may share its index) is keyboard-free
|
|
227
|
+
if (hasKeyboardFloor && index == detents.size - 1) return BottomSheetBehavior.STATE_EXPANDED
|
|
155
228
|
return stateMap.entries.find { it.value == index }?.key ?: BottomSheetBehavior.STATE_HIDDEN
|
|
156
229
|
}
|
|
157
230
|
|
|
@@ -169,8 +242,12 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
169
242
|
*/
|
|
170
243
|
private fun getDetentStateMap(): Map<Int, Int>? =
|
|
171
244
|
when (detents.size) {
|
|
245
|
+
// With one detent halfExpandedRatio matches the peek height, so the
|
|
246
|
+
// behavior can settle HALF_EXPANDED at the same position (e.g. an upward
|
|
247
|
+
// drag release) — map it too, or the settle is unmappable
|
|
172
248
|
1 -> mapOf(
|
|
173
249
|
BottomSheetBehavior.STATE_COLLAPSED to 0,
|
|
250
|
+
BottomSheetBehavior.STATE_HALF_EXPANDED to 0,
|
|
174
251
|
BottomSheetBehavior.STATE_EXPANDED to 0
|
|
175
252
|
)
|
|
176
253
|
|
|
@@ -197,16 +274,18 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
197
274
|
* Find which segment the position falls into for interpolation.
|
|
198
275
|
* @return Triple(fromIndex, toIndex, progress) where progress is 0-1, or null if no detents
|
|
199
276
|
*/
|
|
200
|
-
fun findSegmentForPosition(positionPx: Int): Triple<Int, Int, Float>? {
|
|
277
|
+
private fun findSegmentForPosition(positionPx: Int): Triple<Int, Int, Float>? {
|
|
201
278
|
val count = detents.size
|
|
202
279
|
if (count == 0) return null
|
|
203
280
|
|
|
204
|
-
|
|
281
|
+
// A single keyboard-shifted detent can rest down at its keyboard floor —
|
|
282
|
+
// that range is still index 0, not a dismissal
|
|
283
|
+
val floorPos = getLowestSheetTop()
|
|
205
284
|
|
|
206
|
-
// Position is below
|
|
207
|
-
if (positionPx >
|
|
208
|
-
val range = realScreenHeight -
|
|
209
|
-
val progress = if (range > 0) (positionPx -
|
|
285
|
+
// Position is below the lowest stop (sheet is being dragged down to dismiss)
|
|
286
|
+
if (positionPx > floorPos) {
|
|
287
|
+
val range = realScreenHeight - floorPos
|
|
288
|
+
val progress = if (range > 0) (positionPx - floorPos).toFloat() / range else 0f
|
|
210
289
|
return Triple(-1, 0, progress)
|
|
211
290
|
}
|
|
212
291
|
|
|
@@ -238,37 +317,23 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
238
317
|
}
|
|
239
318
|
|
|
240
319
|
/**
|
|
241
|
-
* Returns continuous index (e.g., 0.5 = halfway between detent
|
|
320
|
+
* Returns interpolated continuous index (e.g., 0.5 = halfway between detent
|
|
321
|
+
* 0 and 1) and screen fraction for a position, sharing a single segment
|
|
322
|
+
* lookup — this runs on every position frame.
|
|
242
323
|
*/
|
|
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
|
|
324
|
+
fun getInterpolatedPositionInfo(positionPx: Int): Pair<Float, Float> {
|
|
325
|
+
if (detents.isEmpty()) return Pair(-1f, 0f)
|
|
260
326
|
|
|
261
|
-
val
|
|
262
|
-
|
|
327
|
+
val (fromIndex, toIndex, progress) = findSegmentForPosition(positionPx)
|
|
328
|
+
?: return Pair(0f, getDetentValueForIndex(0))
|
|
263
329
|
|
|
264
330
|
if (fromIndex == -1) {
|
|
265
|
-
|
|
266
|
-
return maxOf(0f, firstDetent * (1 - progress))
|
|
331
|
+
return Pair(-progress, maxOf(0f, getDetentValueForIndex(0) * (1 - progress)))
|
|
267
332
|
}
|
|
268
333
|
|
|
269
334
|
val fromDetent = getDetentValueForIndex(fromIndex)
|
|
270
335
|
val toDetent = getDetentValueForIndex(toIndex)
|
|
271
|
-
return fromDetent + progress * (toDetent - fromDetent)
|
|
336
|
+
return Pair(fromIndex + progress, fromDetent + progress * (toDetent - fromDetent))
|
|
272
337
|
}
|
|
273
338
|
|
|
274
339
|
companion object {
|
|
@@ -139,6 +139,9 @@ class TrueSheetDimView(private val reactContext: ThemedReactContext) :
|
|
|
139
139
|
|
|
140
140
|
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
141
141
|
if (blockGestures) {
|
|
142
|
+
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
|
143
|
+
parent?.requestDisallowInterceptTouchEvent(true)
|
|
144
|
+
}
|
|
142
145
|
// When dimmed, consume touch and trigger click on ACTION_UP
|
|
143
146
|
if (event.action == MotionEvent.ACTION_UP) {
|
|
144
147
|
performClick()
|