@lodev09/react-native-true-sheet 3.0.0-beta.9 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -6
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +29 -33
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +3 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +53 -43
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +390 -89
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +42 -4
- package/android/src/main/java/com/lodev09/truesheet/core/RNScreensFragmentObserver.kt +0 -5
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDialogObserver.kt +67 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetGrabberView.kt +70 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetDragEvents.kt +71 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetFocusEvents.kt +65 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +94 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetStateEvents.kt +56 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +37 -33
- package/android/src/main/res/anim/true_sheet_slide_in.xml +13 -0
- package/android/src/main/res/anim/true_sheet_slide_out.xml +13 -0
- package/android/src/main/res/values/styles.xml +13 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +5 -3
- package/ios/TrueSheetContainerView.mm +4 -0
- package/ios/TrueSheetContentView.h +2 -1
- package/ios/TrueSheetContentView.mm +91 -11
- package/ios/TrueSheetView.mm +94 -41
- package/ios/TrueSheetViewController.h +22 -10
- package/ios/TrueSheetViewController.mm +360 -173
- package/ios/core/TrueSheetBlurView.h +26 -0
- package/ios/{utils/ConversionUtil.mm → core/TrueSheetBlurView.mm} +64 -3
- package/ios/core/TrueSheetGrabberView.h +42 -0
- package/ios/core/TrueSheetGrabberView.mm +107 -0
- package/ios/events/TrueSheetDragEvents.h +39 -0
- package/ios/events/TrueSheetDragEvents.mm +62 -0
- package/ios/events/{OnPositionChangeEvent.h → TrueSheetFocusEvents.h} +8 -5
- package/ios/events/TrueSheetFocusEvents.mm +49 -0
- package/ios/events/TrueSheetLifecycleEvents.h +40 -0
- package/ios/events/TrueSheetLifecycleEvents.mm +71 -0
- package/ios/events/TrueSheetStateEvents.h +35 -0
- package/ios/events/TrueSheetStateEvents.mm +49 -0
- package/ios/utils/GestureUtil.h +7 -0
- package/ios/utils/GestureUtil.mm +12 -0
- package/lib/module/TrueSheet.js +72 -12
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +28 -5
- package/lib/module/index.js +0 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/reanimated/ReanimatedTrueSheet.js +13 -7
- package/lib/module/reanimated/ReanimatedTrueSheet.js.map +1 -1
- package/lib/module/reanimated/ReanimatedTrueSheetProvider.js +4 -2
- package/lib/module/reanimated/ReanimatedTrueSheetProvider.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts +4 -0
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +105 -6
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +25 -5
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +0 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/reanimated/ReanimatedTrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts +8 -2
- package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts.map +1 -1
- package/package.json +8 -2
- package/src/TrueSheet.tsx +87 -10
- package/src/TrueSheet.types.ts +114 -6
- package/src/__mocks__/index.js +0 -5
- package/src/fabric/TrueSheetViewNativeComponent.ts +28 -5
- package/src/index.ts +0 -1
- package/src/reanimated/ReanimatedTrueSheet.tsx +12 -7
- package/src/reanimated/ReanimatedTrueSheetProvider.tsx +11 -3
- package/android/src/main/java/com/lodev09/truesheet/events/DetentChangeEvent.kt +0 -26
- package/android/src/main/java/com/lodev09/truesheet/events/DidDismissEvent.kt +0 -20
- package/android/src/main/java/com/lodev09/truesheet/events/DidPresentEvent.kt +0 -26
- package/android/src/main/java/com/lodev09/truesheet/events/DragBeginEvent.kt +0 -26
- package/android/src/main/java/com/lodev09/truesheet/events/DragChangeEvent.kt +0 -26
- package/android/src/main/java/com/lodev09/truesheet/events/DragEndEvent.kt +0 -26
- package/android/src/main/java/com/lodev09/truesheet/events/MountEvent.kt +0 -20
- package/android/src/main/java/com/lodev09/truesheet/events/PositionChangeEvent.kt +0 -32
- package/android/src/main/java/com/lodev09/truesheet/events/WillDismissEvent.kt +0 -20
- package/android/src/main/java/com/lodev09/truesheet/events/WillPresentEvent.kt +0 -26
- package/ios/events/OnDetentChangeEvent.h +0 -28
- package/ios/events/OnDetentChangeEvent.mm +0 -30
- package/ios/events/OnDidDismissEvent.h +0 -26
- package/ios/events/OnDidDismissEvent.mm +0 -25
- package/ios/events/OnDidPresentEvent.h +0 -28
- package/ios/events/OnDidPresentEvent.mm +0 -30
- package/ios/events/OnDragBeginEvent.h +0 -28
- package/ios/events/OnDragBeginEvent.mm +0 -30
- package/ios/events/OnDragChangeEvent.h +0 -28
- package/ios/events/OnDragChangeEvent.mm +0 -30
- package/ios/events/OnDragEndEvent.h +0 -28
- package/ios/events/OnDragEndEvent.mm +0 -30
- package/ios/events/OnMountEvent.h +0 -26
- package/ios/events/OnMountEvent.mm +0 -25
- package/ios/events/OnPositionChangeEvent.mm +0 -32
- package/ios/events/OnWillDismissEvent.h +0 -26
- package/ios/events/OnWillDismissEvent.mm +0 -25
- package/ios/events/OnWillPresentEvent.h +0 -28
- package/ios/events/OnWillPresentEvent.mm +0 -30
- package/ios/utils/ConversionUtil.h +0 -24
- package/lib/module/TrueSheetGrabber.js +0 -51
- package/lib/module/TrueSheetGrabber.js.map +0 -1
- package/lib/typescript/src/TrueSheetGrabber.d.ts +0 -39
- package/lib/typescript/src/TrueSheetGrabber.d.ts.map +0 -1
- package/src/TrueSheetGrabber.tsx +0 -82
|
@@ -2,6 +2,7 @@ package com.lodev09.truesheet
|
|
|
2
2
|
|
|
3
3
|
import android.view.WindowManager
|
|
4
4
|
import com.facebook.react.bridge.ReadableArray
|
|
5
|
+
import com.facebook.react.bridge.ReadableMap
|
|
5
6
|
import com.facebook.react.module.annotations.ReactModule
|
|
6
7
|
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
7
8
|
import com.facebook.react.uimanager.ReactStylesDiffMap
|
|
@@ -13,6 +14,7 @@ import com.facebook.react.uimanager.ViewManagerDelegate
|
|
|
13
14
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
14
15
|
import com.facebook.react.viewmanagers.TrueSheetViewManagerDelegate
|
|
15
16
|
import com.facebook.react.viewmanagers.TrueSheetViewManagerInterface
|
|
17
|
+
import com.lodev09.truesheet.core.GrabberOptions
|
|
16
18
|
import com.lodev09.truesheet.events.*
|
|
17
19
|
|
|
18
20
|
/**
|
|
@@ -67,7 +69,11 @@ class TrueSheetViewManager :
|
|
|
67
69
|
DragBeginEvent.EVENT_NAME to hashMapOf("registrationName" to DragBeginEvent.REGISTRATION_NAME),
|
|
68
70
|
DragChangeEvent.EVENT_NAME to hashMapOf("registrationName" to DragChangeEvent.REGISTRATION_NAME),
|
|
69
71
|
DragEndEvent.EVENT_NAME to hashMapOf("registrationName" to DragEndEvent.REGISTRATION_NAME),
|
|
70
|
-
PositionChangeEvent.EVENT_NAME to hashMapOf("registrationName" to PositionChangeEvent.REGISTRATION_NAME)
|
|
72
|
+
PositionChangeEvent.EVENT_NAME to hashMapOf("registrationName" to PositionChangeEvent.REGISTRATION_NAME),
|
|
73
|
+
WillFocusEvent.EVENT_NAME to hashMapOf("registrationName" to WillFocusEvent.REGISTRATION_NAME),
|
|
74
|
+
FocusEvent.EVENT_NAME to hashMapOf("registrationName" to FocusEvent.REGISTRATION_NAME),
|
|
75
|
+
WillBlurEvent.EVENT_NAME to hashMapOf("registrationName" to WillBlurEvent.REGISTRATION_NAME),
|
|
76
|
+
BlurEvent.EVENT_NAME to hashMapOf("registrationName" to BlurEvent.REGISTRATION_NAME)
|
|
71
77
|
)
|
|
72
78
|
|
|
73
79
|
// ==================== Props ====================
|
|
@@ -105,11 +111,39 @@ class TrueSheetViewManager :
|
|
|
105
111
|
view.setGrabber(grabber)
|
|
106
112
|
}
|
|
107
113
|
|
|
114
|
+
@ReactProp(name = "grabberOptions")
|
|
115
|
+
override fun setGrabberOptions(view: TrueSheetView, options: ReadableMap?) {
|
|
116
|
+
if (options == null) {
|
|
117
|
+
view.setGrabberOptions(null)
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
val grabberOptions = GrabberOptions(
|
|
122
|
+
width = if (options.hasKey("width")) options.getDouble("width").toFloat() else null,
|
|
123
|
+
height = if (options.hasKey("height")) options.getDouble("height").toFloat() else null,
|
|
124
|
+
topMargin = if (options.hasKey("topMargin")) options.getDouble("topMargin").toFloat() else null,
|
|
125
|
+
cornerRadius = if (options.hasKey("cornerRadius") &&
|
|
126
|
+
options.getDouble("cornerRadius") >= 0
|
|
127
|
+
) {
|
|
128
|
+
options.getDouble("cornerRadius").toFloat()
|
|
129
|
+
} else {
|
|
130
|
+
null
|
|
131
|
+
},
|
|
132
|
+
color = if (options.hasKey("color") && options.getInt("color") != 0) options.getInt("color") else null
|
|
133
|
+
)
|
|
134
|
+
view.setGrabberOptions(grabberOptions)
|
|
135
|
+
}
|
|
136
|
+
|
|
108
137
|
@ReactProp(name = "dismissible", defaultBoolean = true)
|
|
109
138
|
override fun setDismissible(view: TrueSheetView, dismissible: Boolean) {
|
|
110
139
|
view.setDismissible(dismissible)
|
|
111
140
|
}
|
|
112
141
|
|
|
142
|
+
@ReactProp(name = "draggable", defaultBoolean = true)
|
|
143
|
+
override fun setDraggable(view: TrueSheetView, draggable: Boolean) {
|
|
144
|
+
view.setDraggable(draggable)
|
|
145
|
+
}
|
|
146
|
+
|
|
113
147
|
@ReactProp(name = "dimmed", defaultBoolean = true)
|
|
114
148
|
override fun setDimmed(view: TrueSheetView, dimmed: Boolean) {
|
|
115
149
|
view.setDimmed(dimmed)
|
|
@@ -149,7 +183,11 @@ class TrueSheetViewManager :
|
|
|
149
183
|
@ReactProp(name = "blurTint")
|
|
150
184
|
override fun setBlurTint(view: TrueSheetView, tint: String?) {
|
|
151
185
|
// iOS-specific prop - no-op on Android
|
|
152
|
-
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@ReactProp(name = "blurOptions")
|
|
189
|
+
override fun setBlurOptions(view: TrueSheetView, options: ReadableMap?) {
|
|
190
|
+
// iOS-specific prop - no-op on Android
|
|
153
191
|
}
|
|
154
192
|
|
|
155
193
|
@ReactProp(name = "edgeToEdgeFullScreen", defaultBoolean = false)
|
|
@@ -157,8 +195,8 @@ class TrueSheetViewManager :
|
|
|
157
195
|
view.setEdgeToEdgeFullScreen(edgeToEdgeFullScreen)
|
|
158
196
|
}
|
|
159
197
|
|
|
160
|
-
@ReactProp(name = "
|
|
161
|
-
override fun
|
|
198
|
+
@ReactProp(name = "scrollable", defaultBoolean = false)
|
|
199
|
+
override fun setScrollable(view: TrueSheetView, value: Boolean) {
|
|
162
200
|
// iOS-specific prop - no-op on Android
|
|
163
201
|
}
|
|
164
202
|
|
|
@@ -19,11 +19,6 @@ class RNScreensFragmentObserver(
|
|
|
19
19
|
private var fragmentLifecycleCallback: FragmentManager.FragmentLifecycleCallbacks? = null
|
|
20
20
|
private val activeModalFragments: MutableSet<Fragment> = mutableSetOf()
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* Check if there are active modal fragments being tracked.
|
|
24
|
-
*/
|
|
25
|
-
fun hasActiveModals(): Boolean = activeModalFragments.isNotEmpty()
|
|
26
|
-
|
|
27
22
|
/**
|
|
28
23
|
* Start observing fragment lifecycle events.
|
|
29
24
|
*/
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
package com.lodev09.truesheet.core
|
|
2
|
+
|
|
3
|
+
import com.lodev09.truesheet.TrueSheetView
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Manages TrueSheet stacking behavior.
|
|
7
|
+
* Tracks presented sheets and handles visibility when sheets stack on top of each other.
|
|
8
|
+
*/
|
|
9
|
+
object TrueSheetDialogObserver {
|
|
10
|
+
|
|
11
|
+
private val presentedSheetStack = mutableListOf<TrueSheetView>()
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Called when a sheet is about to be presented.
|
|
15
|
+
* Returns the visible parent sheet to stack on, or null if none.
|
|
16
|
+
*/
|
|
17
|
+
@JvmStatic
|
|
18
|
+
fun onSheetWillPresent(sheetView: TrueSheetView, detentIndex: Int): TrueSheetView? {
|
|
19
|
+
synchronized(presentedSheetStack) {
|
|
20
|
+
val parentSheet = presentedSheetStack.lastOrNull()
|
|
21
|
+
?.takeIf { it.viewController.isPresented && it.viewController.isDialogVisible }
|
|
22
|
+
|
|
23
|
+
// Hide parent if the new sheet would cover it
|
|
24
|
+
parentSheet?.let {
|
|
25
|
+
val parentTop = it.viewController.currentSheetTop
|
|
26
|
+
val newSheetTop = sheetView.viewController.getExpectedSheetTop(detentIndex)
|
|
27
|
+
if (!it.viewController.isExpanded && parentTop <= newSheetTop) {
|
|
28
|
+
it.viewController.hideDialog()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!presentedSheetStack.contains(sheetView)) {
|
|
33
|
+
presentedSheetStack.add(sheetView)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return parentSheet
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Called when a sheet has been dismissed.
|
|
42
|
+
* Shows the parent sheet if this sheet was stacked on it.
|
|
43
|
+
*/
|
|
44
|
+
@JvmStatic
|
|
45
|
+
fun onSheetDidDismiss(sheetView: TrueSheetView, hadParent: Boolean) {
|
|
46
|
+
synchronized(presentedSheetStack) {
|
|
47
|
+
presentedSheetStack.remove(sheetView)
|
|
48
|
+
if (hadParent) {
|
|
49
|
+
presentedSheetStack.lastOrNull()?.viewController?.showDialog()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@JvmStatic
|
|
55
|
+
fun removeSheet(sheetView: TrueSheetView) {
|
|
56
|
+
synchronized(presentedSheetStack) {
|
|
57
|
+
presentedSheetStack.remove(sheetView)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@JvmStatic
|
|
62
|
+
fun clear() {
|
|
63
|
+
synchronized(presentedSheetStack) {
|
|
64
|
+
presentedSheetStack.clear()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
package com.lodev09.truesheet.core
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.graphics.Color
|
|
6
|
+
import android.graphics.drawable.GradientDrawable
|
|
7
|
+
import android.view.Gravity
|
|
8
|
+
import android.view.View
|
|
9
|
+
import android.widget.FrameLayout
|
|
10
|
+
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Options for customizing the grabber appearance.
|
|
14
|
+
*/
|
|
15
|
+
data class GrabberOptions(
|
|
16
|
+
val width: Float? = null,
|
|
17
|
+
val height: Float? = null,
|
|
18
|
+
val topMargin: Float? = null,
|
|
19
|
+
val cornerRadius: Float? = null,
|
|
20
|
+
val color: Int? = null
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Native grabber (drag handle) view for the bottom sheet.
|
|
25
|
+
* Displays a small pill-shaped indicator at the top of the sheet.
|
|
26
|
+
*/
|
|
27
|
+
@SuppressLint("ViewConstructor")
|
|
28
|
+
class TrueSheetGrabberView(context: Context, private val options: GrabberOptions? = null) : View(context) {
|
|
29
|
+
|
|
30
|
+
companion object {
|
|
31
|
+
private const val DEFAULT_WIDTH = 32f // dp
|
|
32
|
+
private const val DEFAULT_HEIGHT = 4f // dp
|
|
33
|
+
private const val DEFAULT_TOP_MARGIN = 16f // dp
|
|
34
|
+
private val DEFAULT_COLOR = Color.argb((0.4 * 255).toInt(), 73, 69, 79) // #49454F @ 40%
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private val grabberWidth: Float
|
|
38
|
+
get() = options?.width ?: DEFAULT_WIDTH
|
|
39
|
+
|
|
40
|
+
private val grabberHeight: Float
|
|
41
|
+
get() = options?.height ?: DEFAULT_HEIGHT
|
|
42
|
+
|
|
43
|
+
private val grabberTopMargin: Float
|
|
44
|
+
get() = options?.topMargin ?: DEFAULT_TOP_MARGIN
|
|
45
|
+
|
|
46
|
+
private val grabberCornerRadius: Float
|
|
47
|
+
get() = options?.cornerRadius ?: (grabberHeight / 2)
|
|
48
|
+
|
|
49
|
+
private val grabberColor: Int
|
|
50
|
+
get() = options?.color ?: DEFAULT_COLOR
|
|
51
|
+
|
|
52
|
+
init {
|
|
53
|
+
layoutParams = FrameLayout.LayoutParams(
|
|
54
|
+
grabberWidth.dpToPx().toInt(),
|
|
55
|
+
grabberHeight.dpToPx().toInt()
|
|
56
|
+
).apply {
|
|
57
|
+
gravity = Gravity.CENTER_HORIZONTAL or Gravity.TOP
|
|
58
|
+
topMargin = grabberTopMargin.dpToPx().toInt()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
background = GradientDrawable().apply {
|
|
62
|
+
shape = GradientDrawable.RECTANGLE
|
|
63
|
+
cornerRadius = grabberCornerRadius.dpToPx()
|
|
64
|
+
setColor(grabberColor)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// High elevation to ensure grabber appears above content views
|
|
68
|
+
elevation = 100f
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
package com.lodev09.truesheet.events
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import com.facebook.react.uimanager.events.Event
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Fired when dragging begins
|
|
9
|
+
* Payload: { index: number, position: number, detent: number }
|
|
10
|
+
*/
|
|
11
|
+
class DragBeginEvent(surfaceId: Int, viewId: Int, private val index: Int, private val position: Float, private val detent: Float) :
|
|
12
|
+
Event<DragBeginEvent>(surfaceId, viewId) {
|
|
13
|
+
|
|
14
|
+
override fun getEventName(): String = EVENT_NAME
|
|
15
|
+
|
|
16
|
+
override fun getEventData(): WritableMap =
|
|
17
|
+
Arguments.createMap().apply {
|
|
18
|
+
putInt("index", index)
|
|
19
|
+
putDouble("position", position.toDouble())
|
|
20
|
+
putDouble("detent", detent.toDouble())
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
companion object {
|
|
24
|
+
const val EVENT_NAME = "topDragBegin"
|
|
25
|
+
const val REGISTRATION_NAME = "onDragBegin"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Fired continuously during dragging
|
|
31
|
+
* Payload: { index: number, position: number, detent: number }
|
|
32
|
+
*/
|
|
33
|
+
class DragChangeEvent(surfaceId: Int, viewId: Int, private val index: Int, private val position: Float, private val detent: Float) :
|
|
34
|
+
Event<DragChangeEvent>(surfaceId, viewId) {
|
|
35
|
+
|
|
36
|
+
override fun getEventName(): String = EVENT_NAME
|
|
37
|
+
|
|
38
|
+
override fun getEventData(): WritableMap =
|
|
39
|
+
Arguments.createMap().apply {
|
|
40
|
+
putInt("index", index)
|
|
41
|
+
putDouble("position", position.toDouble())
|
|
42
|
+
putDouble("detent", detent.toDouble())
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
companion object {
|
|
46
|
+
const val EVENT_NAME = "topDragChange"
|
|
47
|
+
const val REGISTRATION_NAME = "onDragChange"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Fired when dragging ends
|
|
53
|
+
* Payload: { index: number, position: number, detent: number }
|
|
54
|
+
*/
|
|
55
|
+
class DragEndEvent(surfaceId: Int, viewId: Int, private val index: Int, private val position: Float, private val detent: Float) :
|
|
56
|
+
Event<DragEndEvent>(surfaceId, viewId) {
|
|
57
|
+
|
|
58
|
+
override fun getEventName(): String = EVENT_NAME
|
|
59
|
+
|
|
60
|
+
override fun getEventData(): WritableMap =
|
|
61
|
+
Arguments.createMap().apply {
|
|
62
|
+
putInt("index", index)
|
|
63
|
+
putDouble("position", position.toDouble())
|
|
64
|
+
putDouble("detent", detent.toDouble())
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
companion object {
|
|
68
|
+
const val EVENT_NAME = "topDragEnd"
|
|
69
|
+
const val REGISTRATION_NAME = "onDragEnd"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
package com.lodev09.truesheet.events
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import com.facebook.react.uimanager.events.Event
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Fired when the sheet is about to regain focus because a sheet on top of it is being dismissed
|
|
9
|
+
*/
|
|
10
|
+
class WillFocusEvent(surfaceId: Int, viewId: Int) : Event<WillFocusEvent>(surfaceId, viewId) {
|
|
11
|
+
|
|
12
|
+
override fun getEventName(): String = EVENT_NAME
|
|
13
|
+
|
|
14
|
+
override fun getEventData(): WritableMap = Arguments.createMap()
|
|
15
|
+
|
|
16
|
+
companion object {
|
|
17
|
+
const val EVENT_NAME = "topWillFocus"
|
|
18
|
+
const val REGISTRATION_NAME = "onWillFocus"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Fired when the sheet regains focus after a sheet on top of it is dismissed
|
|
24
|
+
*/
|
|
25
|
+
class FocusEvent(surfaceId: Int, viewId: Int) : Event<FocusEvent>(surfaceId, viewId) {
|
|
26
|
+
|
|
27
|
+
override fun getEventName(): String = EVENT_NAME
|
|
28
|
+
|
|
29
|
+
override fun getEventData(): WritableMap = Arguments.createMap()
|
|
30
|
+
|
|
31
|
+
companion object {
|
|
32
|
+
const val EVENT_NAME = "topDidFocus"
|
|
33
|
+
const val REGISTRATION_NAME = "onDidFocus"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Fired when the sheet is about to lose focus because another sheet is being presented on top of it
|
|
39
|
+
*/
|
|
40
|
+
class WillBlurEvent(surfaceId: Int, viewId: Int) : Event<WillBlurEvent>(surfaceId, viewId) {
|
|
41
|
+
|
|
42
|
+
override fun getEventName(): String = EVENT_NAME
|
|
43
|
+
|
|
44
|
+
override fun getEventData(): WritableMap = Arguments.createMap()
|
|
45
|
+
|
|
46
|
+
companion object {
|
|
47
|
+
const val EVENT_NAME = "topWillBlur"
|
|
48
|
+
const val REGISTRATION_NAME = "onWillBlur"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Fired when the sheet loses focus because another sheet is presented on top of it
|
|
54
|
+
*/
|
|
55
|
+
class BlurEvent(surfaceId: Int, viewId: Int) : Event<BlurEvent>(surfaceId, viewId) {
|
|
56
|
+
|
|
57
|
+
override fun getEventName(): String = EVENT_NAME
|
|
58
|
+
|
|
59
|
+
override fun getEventData(): WritableMap = Arguments.createMap()
|
|
60
|
+
|
|
61
|
+
companion object {
|
|
62
|
+
const val EVENT_NAME = "topDidBlur"
|
|
63
|
+
const val REGISTRATION_NAME = "onDidBlur"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
package com.lodev09.truesheet.events
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import com.facebook.react.uimanager.events.Event
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Fired when the sheet component is mounted and ready
|
|
9
|
+
*/
|
|
10
|
+
class MountEvent(surfaceId: Int, viewId: Int) : Event<MountEvent>(surfaceId, viewId) {
|
|
11
|
+
|
|
12
|
+
override fun getEventName(): String = EVENT_NAME
|
|
13
|
+
|
|
14
|
+
override fun getEventData(): WritableMap = Arguments.createMap()
|
|
15
|
+
|
|
16
|
+
companion object {
|
|
17
|
+
const val EVENT_NAME = "topMount"
|
|
18
|
+
const val REGISTRATION_NAME = "onMount"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Fired before the sheet is presented
|
|
24
|
+
* Payload: { index: number, position: number, detent: number }
|
|
25
|
+
*/
|
|
26
|
+
class WillPresentEvent(surfaceId: Int, viewId: Int, private val index: Int, private val position: Float, private val detent: Float) :
|
|
27
|
+
Event<WillPresentEvent>(surfaceId, viewId) {
|
|
28
|
+
|
|
29
|
+
override fun getEventName(): String = EVENT_NAME
|
|
30
|
+
|
|
31
|
+
override fun getEventData(): WritableMap =
|
|
32
|
+
Arguments.createMap().apply {
|
|
33
|
+
putInt("index", index)
|
|
34
|
+
putDouble("position", position.toDouble())
|
|
35
|
+
putDouble("detent", detent.toDouble())
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
companion object {
|
|
39
|
+
const val EVENT_NAME = "topWillPresent"
|
|
40
|
+
const val REGISTRATION_NAME = "onWillPresent"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Fired after the sheet is presented
|
|
46
|
+
* Payload: { index: number, position: number, detent: number }
|
|
47
|
+
*/
|
|
48
|
+
class DidPresentEvent(surfaceId: Int, viewId: Int, private val index: Int, private val position: Float, private val detent: Float) :
|
|
49
|
+
Event<DidPresentEvent>(surfaceId, viewId) {
|
|
50
|
+
|
|
51
|
+
override fun getEventName(): String = EVENT_NAME
|
|
52
|
+
|
|
53
|
+
override fun getEventData(): WritableMap =
|
|
54
|
+
Arguments.createMap().apply {
|
|
55
|
+
putInt("index", index)
|
|
56
|
+
putDouble("position", position.toDouble())
|
|
57
|
+
putDouble("detent", detent.toDouble())
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
companion object {
|
|
61
|
+
const val EVENT_NAME = "topDidPresent"
|
|
62
|
+
const val REGISTRATION_NAME = "onDidPresent"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Fired before the sheet is dismissed
|
|
68
|
+
*/
|
|
69
|
+
class WillDismissEvent(surfaceId: Int, viewId: Int) : Event<WillDismissEvent>(surfaceId, viewId) {
|
|
70
|
+
|
|
71
|
+
override fun getEventName(): String = EVENT_NAME
|
|
72
|
+
|
|
73
|
+
override fun getEventData(): WritableMap = Arguments.createMap()
|
|
74
|
+
|
|
75
|
+
companion object {
|
|
76
|
+
const val EVENT_NAME = "topWillDismiss"
|
|
77
|
+
const val REGISTRATION_NAME = "onWillDismiss"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Fired after the sheet is dismissed
|
|
83
|
+
*/
|
|
84
|
+
class DidDismissEvent(surfaceId: Int, viewId: Int) : Event<DidDismissEvent>(surfaceId, viewId) {
|
|
85
|
+
|
|
86
|
+
override fun getEventName(): String = EVENT_NAME
|
|
87
|
+
|
|
88
|
+
override fun getEventData(): WritableMap = Arguments.createMap()
|
|
89
|
+
|
|
90
|
+
companion object {
|
|
91
|
+
const val EVENT_NAME = "topDidDismiss"
|
|
92
|
+
const val REGISTRATION_NAME = "onDidDismiss"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
package com.lodev09.truesheet.events
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import com.facebook.react.uimanager.events.Event
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Fired when the detent changes
|
|
9
|
+
* Payload: { index: number, position: number, detent: number }
|
|
10
|
+
*/
|
|
11
|
+
class DetentChangeEvent(surfaceId: Int, viewId: Int, private val index: Int, private val position: Float, private val detent: Float) :
|
|
12
|
+
Event<DetentChangeEvent>(surfaceId, viewId) {
|
|
13
|
+
|
|
14
|
+
override fun getEventName(): String = EVENT_NAME
|
|
15
|
+
|
|
16
|
+
override fun getEventData(): WritableMap =
|
|
17
|
+
Arguments.createMap().apply {
|
|
18
|
+
putInt("index", index)
|
|
19
|
+
putDouble("position", position.toDouble())
|
|
20
|
+
putDouble("detent", detent.toDouble())
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
companion object {
|
|
24
|
+
const val EVENT_NAME = "topDetentChange"
|
|
25
|
+
const val REGISTRATION_NAME = "onDetentChange"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Fired continuously for position updates during drag and animation
|
|
31
|
+
* Payload: { index: number, position: number, detent: number, realtime: boolean }
|
|
32
|
+
*/
|
|
33
|
+
class PositionChangeEvent(
|
|
34
|
+
surfaceId: Int,
|
|
35
|
+
viewId: Int,
|
|
36
|
+
private val index: Float,
|
|
37
|
+
private val position: Float,
|
|
38
|
+
private val detent: Float,
|
|
39
|
+
private val realtime: Boolean = false
|
|
40
|
+
) : Event<PositionChangeEvent>(surfaceId, viewId) {
|
|
41
|
+
|
|
42
|
+
override fun getEventName(): String = EVENT_NAME
|
|
43
|
+
|
|
44
|
+
override fun getEventData(): WritableMap =
|
|
45
|
+
Arguments.createMap().apply {
|
|
46
|
+
putDouble("index", index.toDouble())
|
|
47
|
+
putDouble("position", position.toDouble())
|
|
48
|
+
putDouble("detent", detent.toDouble())
|
|
49
|
+
putBoolean("realtime", realtime)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
companion object {
|
|
53
|
+
const val EVENT_NAME = "topPositionChange"
|
|
54
|
+
const val REGISTRATION_NAME = "onPositionChange"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -19,6 +19,18 @@ object ScreenUtils {
|
|
|
19
19
|
context.resources.getIdentifier(name, "dimen", "android")
|
|
20
20
|
).takeIf { it > 0 } ?: 0
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Get the WindowInsets for API 30+, or null for older APIs
|
|
24
|
+
*/
|
|
25
|
+
private fun getWindowInsets(context: ReactContext): WindowInsets? {
|
|
26
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
27
|
+
return context.getSystemService(WindowManager::class.java)
|
|
28
|
+
?.currentWindowMetrics
|
|
29
|
+
?.windowInsets
|
|
30
|
+
}
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
|
|
22
34
|
/**
|
|
23
35
|
* Get the status bar height
|
|
24
36
|
*
|
|
@@ -26,20 +38,29 @@ object ScreenUtils {
|
|
|
26
38
|
* @return Status bar height in pixels
|
|
27
39
|
*/
|
|
28
40
|
fun getStatusBarHeight(context: ReactContext): Int {
|
|
29
|
-
// Modern approach using WindowInsets (API 30+)
|
|
30
41
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (insets != null) {
|
|
35
|
-
return insets.top
|
|
36
|
-
}
|
|
42
|
+
getWindowInsets(context)
|
|
43
|
+
?.getInsetsIgnoringVisibility(WindowInsets.Type.statusBars())
|
|
44
|
+
?.let { return it.top }
|
|
37
45
|
}
|
|
38
|
-
|
|
39
|
-
// Fallback to legacy approach for older APIs
|
|
40
46
|
return getIdentifierHeight(context, "status_bar_height")
|
|
41
47
|
}
|
|
42
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Get the navigation bar height (bottom inset)
|
|
51
|
+
*
|
|
52
|
+
* @param context React context
|
|
53
|
+
* @return Navigation bar height in pixels
|
|
54
|
+
*/
|
|
55
|
+
fun getNavigationBarHeight(context: ReactContext): Int {
|
|
56
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
57
|
+
getWindowInsets(context)
|
|
58
|
+
?.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars())
|
|
59
|
+
?.let { return it.bottom }
|
|
60
|
+
}
|
|
61
|
+
return getIdentifierHeight(context, "navigation_bar_height")
|
|
62
|
+
}
|
|
63
|
+
|
|
43
64
|
/**
|
|
44
65
|
* Calculate the screen height
|
|
45
66
|
*
|
|
@@ -55,36 +76,20 @@ object ScreenUtils {
|
|
|
55
76
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
56
77
|
context.display?.getRealMetrics(displayMetrics)
|
|
57
78
|
} else {
|
|
79
|
+
@Suppress("DEPRECATION")
|
|
58
80
|
windowManager.defaultDisplay.getMetrics(displayMetrics)
|
|
59
81
|
}
|
|
60
82
|
|
|
61
83
|
val screenHeight = displayMetrics.heightPixels
|
|
62
|
-
val statusBarHeight = getStatusBarHeight(context)
|
|
63
|
-
|
|
64
|
-
val hasNavigationBar = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
65
|
-
context.getSystemService(WindowManager::class.java)
|
|
66
|
-
?.currentWindowMetrics
|
|
67
|
-
?.windowInsets
|
|
68
|
-
?.isVisible(WindowInsets.Type.navigationBars()) ?: false
|
|
69
|
-
} else {
|
|
70
|
-
context.resources.getIdentifier("navigation_bar_height", "dimen", "android") > 0
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
val navigationBarHeight = if (hasNavigationBar) {
|
|
74
|
-
getIdentifierHeight(context, "navigation_bar_height")
|
|
75
|
-
} else {
|
|
76
|
-
0
|
|
77
|
-
}
|
|
78
84
|
|
|
79
85
|
return if (edgeToEdge) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
false -> screenHeight + navigationBarHeight
|
|
86
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
87
|
+
screenHeight
|
|
88
|
+
} else {
|
|
89
|
+
screenHeight + getNavigationBarHeight(context)
|
|
85
90
|
}
|
|
86
91
|
} else {
|
|
87
|
-
screenHeight -
|
|
92
|
+
screenHeight - getStatusBarHeight(context) - getNavigationBarHeight(context)
|
|
88
93
|
}
|
|
89
94
|
}
|
|
90
95
|
|
|
@@ -98,8 +103,7 @@ object ScreenUtils {
|
|
|
98
103
|
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
|
99
104
|
|
|
100
105
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
101
|
-
|
|
102
|
-
windowMetrics.bounds.width()
|
|
106
|
+
windowManager.currentWindowMetrics.bounds.width()
|
|
103
107
|
} else {
|
|
104
108
|
val displayMetrics = DisplayMetrics()
|
|
105
109
|
@Suppress("DEPRECATION")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
3
|
+
<translate
|
|
4
|
+
android:duration="200"
|
|
5
|
+
android:fromYDelta="20%p"
|
|
6
|
+
android:toYDelta="0"
|
|
7
|
+
android:interpolator="@android:interpolator/fast_out_slow_in" />
|
|
8
|
+
<alpha
|
|
9
|
+
android:duration="200"
|
|
10
|
+
android:fromAlpha="0.0"
|
|
11
|
+
android:toAlpha="1.0"
|
|
12
|
+
android:interpolator="@android:interpolator/fast_out_slow_in" />
|
|
13
|
+
</set>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
3
|
+
<translate
|
|
4
|
+
android:duration="150"
|
|
5
|
+
android:fromYDelta="0"
|
|
6
|
+
android:toYDelta="20%p"
|
|
7
|
+
android:interpolator="@android:interpolator/fast_out_slow_in" />
|
|
8
|
+
<alpha
|
|
9
|
+
android:duration="150"
|
|
10
|
+
android:fromAlpha="1.0"
|
|
11
|
+
android:toAlpha="0.0"
|
|
12
|
+
android:interpolator="@android:interpolator/fast_out_slow_in" />
|
|
13
|
+
</set>
|