@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.
Files changed (101) hide show
  1. package/README.md +16 -6
  2. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +29 -33
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +3 -1
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +53 -43
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +390 -89
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +42 -4
  7. package/android/src/main/java/com/lodev09/truesheet/core/RNScreensFragmentObserver.kt +0 -5
  8. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDialogObserver.kt +67 -0
  9. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetGrabberView.kt +70 -0
  10. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetDragEvents.kt +71 -0
  11. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetFocusEvents.kt +65 -0
  12. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +94 -0
  13. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetStateEvents.kt +56 -0
  14. package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +37 -33
  15. package/android/src/main/res/anim/true_sheet_slide_in.xml +13 -0
  16. package/android/src/main/res/anim/true_sheet_slide_out.xml +13 -0
  17. package/android/src/main/res/values/styles.xml +13 -1
  18. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +5 -3
  19. package/ios/TrueSheetContainerView.mm +4 -0
  20. package/ios/TrueSheetContentView.h +2 -1
  21. package/ios/TrueSheetContentView.mm +91 -11
  22. package/ios/TrueSheetView.mm +94 -41
  23. package/ios/TrueSheetViewController.h +22 -10
  24. package/ios/TrueSheetViewController.mm +360 -173
  25. package/ios/core/TrueSheetBlurView.h +26 -0
  26. package/ios/{utils/ConversionUtil.mm → core/TrueSheetBlurView.mm} +64 -3
  27. package/ios/core/TrueSheetGrabberView.h +42 -0
  28. package/ios/core/TrueSheetGrabberView.mm +107 -0
  29. package/ios/events/TrueSheetDragEvents.h +39 -0
  30. package/ios/events/TrueSheetDragEvents.mm +62 -0
  31. package/ios/events/{OnPositionChangeEvent.h → TrueSheetFocusEvents.h} +8 -5
  32. package/ios/events/TrueSheetFocusEvents.mm +49 -0
  33. package/ios/events/TrueSheetLifecycleEvents.h +40 -0
  34. package/ios/events/TrueSheetLifecycleEvents.mm +71 -0
  35. package/ios/events/TrueSheetStateEvents.h +35 -0
  36. package/ios/events/TrueSheetStateEvents.mm +49 -0
  37. package/ios/utils/GestureUtil.h +7 -0
  38. package/ios/utils/GestureUtil.mm +12 -0
  39. package/lib/module/TrueSheet.js +72 -12
  40. package/lib/module/TrueSheet.js.map +1 -1
  41. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +28 -5
  42. package/lib/module/index.js +0 -1
  43. package/lib/module/index.js.map +1 -1
  44. package/lib/module/reanimated/ReanimatedTrueSheet.js +13 -7
  45. package/lib/module/reanimated/ReanimatedTrueSheet.js.map +1 -1
  46. package/lib/module/reanimated/ReanimatedTrueSheetProvider.js +4 -2
  47. package/lib/module/reanimated/ReanimatedTrueSheetProvider.js.map +1 -1
  48. package/lib/typescript/src/TrueSheet.d.ts +4 -0
  49. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  50. package/lib/typescript/src/TrueSheet.types.d.ts +105 -6
  51. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  52. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +25 -5
  53. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  54. package/lib/typescript/src/index.d.ts +0 -1
  55. package/lib/typescript/src/index.d.ts.map +1 -1
  56. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.d.ts.map +1 -1
  57. package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts +8 -2
  58. package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts.map +1 -1
  59. package/package.json +8 -2
  60. package/src/TrueSheet.tsx +87 -10
  61. package/src/TrueSheet.types.ts +114 -6
  62. package/src/__mocks__/index.js +0 -5
  63. package/src/fabric/TrueSheetViewNativeComponent.ts +28 -5
  64. package/src/index.ts +0 -1
  65. package/src/reanimated/ReanimatedTrueSheet.tsx +12 -7
  66. package/src/reanimated/ReanimatedTrueSheetProvider.tsx +11 -3
  67. package/android/src/main/java/com/lodev09/truesheet/events/DetentChangeEvent.kt +0 -26
  68. package/android/src/main/java/com/lodev09/truesheet/events/DidDismissEvent.kt +0 -20
  69. package/android/src/main/java/com/lodev09/truesheet/events/DidPresentEvent.kt +0 -26
  70. package/android/src/main/java/com/lodev09/truesheet/events/DragBeginEvent.kt +0 -26
  71. package/android/src/main/java/com/lodev09/truesheet/events/DragChangeEvent.kt +0 -26
  72. package/android/src/main/java/com/lodev09/truesheet/events/DragEndEvent.kt +0 -26
  73. package/android/src/main/java/com/lodev09/truesheet/events/MountEvent.kt +0 -20
  74. package/android/src/main/java/com/lodev09/truesheet/events/PositionChangeEvent.kt +0 -32
  75. package/android/src/main/java/com/lodev09/truesheet/events/WillDismissEvent.kt +0 -20
  76. package/android/src/main/java/com/lodev09/truesheet/events/WillPresentEvent.kt +0 -26
  77. package/ios/events/OnDetentChangeEvent.h +0 -28
  78. package/ios/events/OnDetentChangeEvent.mm +0 -30
  79. package/ios/events/OnDidDismissEvent.h +0 -26
  80. package/ios/events/OnDidDismissEvent.mm +0 -25
  81. package/ios/events/OnDidPresentEvent.h +0 -28
  82. package/ios/events/OnDidPresentEvent.mm +0 -30
  83. package/ios/events/OnDragBeginEvent.h +0 -28
  84. package/ios/events/OnDragBeginEvent.mm +0 -30
  85. package/ios/events/OnDragChangeEvent.h +0 -28
  86. package/ios/events/OnDragChangeEvent.mm +0 -30
  87. package/ios/events/OnDragEndEvent.h +0 -28
  88. package/ios/events/OnDragEndEvent.mm +0 -30
  89. package/ios/events/OnMountEvent.h +0 -26
  90. package/ios/events/OnMountEvent.mm +0 -25
  91. package/ios/events/OnPositionChangeEvent.mm +0 -32
  92. package/ios/events/OnWillDismissEvent.h +0 -26
  93. package/ios/events/OnWillDismissEvent.mm +0 -25
  94. package/ios/events/OnWillPresentEvent.h +0 -28
  95. package/ios/events/OnWillPresentEvent.mm +0 -30
  96. package/ios/utils/ConversionUtil.h +0 -24
  97. package/lib/module/TrueSheetGrabber.js +0 -51
  98. package/lib/module/TrueSheetGrabber.js.map +0 -1
  99. package/lib/typescript/src/TrueSheetGrabber.d.ts +0 -39
  100. package/lib/typescript/src/TrueSheetGrabber.d.ts.map +0 -1
  101. 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
- view.setBlurTint(tint)
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 = "fitScrollView", defaultBoolean = false)
161
- override fun setFitScrollView(view: TrueSheetView, value: Boolean) {
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
- val windowManager = context.getSystemService(WindowManager::class.java)
32
- val windowMetrics = windowManager?.currentWindowMetrics
33
- val insets = windowMetrics?.windowInsets?.getInsetsIgnoringVisibility(WindowInsets.Type.statusBars())
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
- // getRealMetrics includes navigation bar height
81
- // windowManager.defaultDisplay.getMetrics doesn't
82
- when (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
83
- true -> screenHeight
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 - statusBarHeight - navigationBarHeight
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
- val windowMetrics = windowManager.currentWindowMetrics
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>