@lodev09/react-native-true-sheet 2.0.6 → 4.0.0-beta.0

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 (194) hide show
  1. package/README.md +36 -8
  2. package/RNTrueSheet.podspec +20 -0
  3. package/android/build.gradle +26 -14
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +108 -0
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerViewManager.kt +21 -0
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +46 -0
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +21 -0
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +47 -0
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterViewManager.kt +21 -0
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +165 -0
  11. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +36 -4
  12. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +257 -303
  13. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +855 -0
  14. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +104 -82
  15. package/android/src/main/java/com/lodev09/truesheet/events/DetentChangeEvent.kt +26 -0
  16. package/android/src/main/java/com/lodev09/truesheet/events/DidDismissEvent.kt +20 -0
  17. package/android/src/main/java/com/lodev09/truesheet/events/DidPresentEvent.kt +26 -0
  18. package/android/src/main/java/com/lodev09/truesheet/events/DragBeginEvent.kt +26 -0
  19. package/android/src/main/java/com/lodev09/truesheet/events/DragChangeEvent.kt +26 -0
  20. package/android/src/main/java/com/lodev09/truesheet/events/DragEndEvent.kt +26 -0
  21. package/android/src/main/java/com/lodev09/truesheet/events/MountEvent.kt +20 -0
  22. package/android/src/main/java/com/lodev09/truesheet/events/PositionChangeEvent.kt +32 -0
  23. package/android/src/main/java/com/lodev09/truesheet/events/SizeChangeEvent.kt +27 -0
  24. package/android/src/main/java/com/lodev09/truesheet/events/WillDismissEvent.kt +20 -0
  25. package/android/src/main/java/com/lodev09/truesheet/events/WillPresentEvent.kt +26 -0
  26. package/android/src/main/java/com/lodev09/truesheet/{core/Utils.kt → utils/ScreenUtils.kt} +47 -17
  27. package/android/src/main/res/values/styles.xml +8 -0
  28. package/ios/TrueSheetComponentDescriptor.h +24 -0
  29. package/ios/TrueSheetContainerView.h +47 -0
  30. package/ios/TrueSheetContainerView.mm +117 -0
  31. package/ios/TrueSheetContentView.h +37 -0
  32. package/ios/TrueSheetContentView.mm +114 -0
  33. package/ios/TrueSheetFooterView.h +27 -0
  34. package/ios/TrueSheetFooterView.mm +101 -0
  35. package/ios/TrueSheetModule.h +44 -0
  36. package/ios/TrueSheetModule.mm +133 -0
  37. package/ios/TrueSheetView.h +53 -0
  38. package/ios/TrueSheetView.mm +433 -0
  39. package/ios/TrueSheetViewController.h +53 -0
  40. package/ios/TrueSheetViewController.mm +649 -0
  41. package/ios/events/OnDetentChangeEvent.h +28 -0
  42. package/ios/events/OnDetentChangeEvent.mm +30 -0
  43. package/ios/events/OnDidDismissEvent.h +26 -0
  44. package/ios/events/OnDidDismissEvent.mm +25 -0
  45. package/ios/events/OnDidPresentEvent.h +28 -0
  46. package/ios/events/OnDidPresentEvent.mm +30 -0
  47. package/ios/events/OnDragBeginEvent.h +28 -0
  48. package/ios/events/OnDragBeginEvent.mm +30 -0
  49. package/ios/events/OnDragChangeEvent.h +28 -0
  50. package/ios/events/OnDragChangeEvent.mm +30 -0
  51. package/ios/events/OnDragEndEvent.h +28 -0
  52. package/ios/events/OnDragEndEvent.mm +30 -0
  53. package/ios/events/OnMountEvent.h +26 -0
  54. package/ios/events/OnMountEvent.mm +25 -0
  55. package/ios/events/OnPositionChangeEvent.h +29 -0
  56. package/ios/events/OnPositionChangeEvent.mm +32 -0
  57. package/ios/events/OnSizeChangeEvent.h +28 -0
  58. package/ios/events/OnSizeChangeEvent.mm +30 -0
  59. package/ios/events/OnWillDismissEvent.h +26 -0
  60. package/ios/events/OnWillDismissEvent.mm +25 -0
  61. package/ios/events/OnWillPresentEvent.h +28 -0
  62. package/ios/events/OnWillPresentEvent.mm +30 -0
  63. package/ios/utils/GestureUtil.h +25 -0
  64. package/ios/utils/GestureUtil.mm +26 -0
  65. package/ios/utils/LayoutUtil.h +44 -0
  66. package/ios/utils/LayoutUtil.mm +50 -0
  67. package/ios/utils/WindowUtil.h +27 -0
  68. package/ios/utils/WindowUtil.mm +42 -0
  69. package/lib/module/TrueSheet.js +231 -135
  70. package/lib/module/TrueSheet.js.map +1 -1
  71. package/lib/module/TrueSheetGrabber.js +16 -14
  72. package/lib/module/TrueSheetGrabber.js.map +1 -1
  73. package/lib/module/fabric/TrueSheetContainerViewNativeComponent.ts +8 -0
  74. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +8 -0
  75. package/lib/module/fabric/TrueSheetFooterViewNativeComponent.ts +8 -0
  76. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +63 -0
  77. package/lib/module/index.js +1 -0
  78. package/lib/module/index.js.map +1 -1
  79. package/lib/module/reanimated/ReanimatedTrueSheet.js +87 -0
  80. package/lib/module/reanimated/ReanimatedTrueSheet.js.map +1 -0
  81. package/lib/module/reanimated/ReanimatedTrueSheetProvider.js +72 -0
  82. package/lib/module/reanimated/ReanimatedTrueSheetProvider.js.map +1 -0
  83. package/lib/module/reanimated/index.js +6 -0
  84. package/lib/module/reanimated/index.js.map +1 -0
  85. package/lib/module/reanimated/useReanimatedPositionChangeHandler.js +19 -0
  86. package/lib/module/reanimated/useReanimatedPositionChangeHandler.js.map +1 -0
  87. package/lib/module/specs/NativeTrueSheetModule.js +12 -0
  88. package/lib/module/specs/NativeTrueSheetModule.js.map +1 -0
  89. package/lib/typescript/package.json +1 -0
  90. package/lib/typescript/src/TrueSheet.d.ts +79 -0
  91. package/lib/typescript/src/TrueSheet.d.ts.map +1 -0
  92. package/lib/typescript/src/TrueSheet.types.d.ts +260 -0
  93. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -0
  94. package/lib/typescript/{commonjs/src → src}/TrueSheetGrabber.d.ts +1 -1
  95. package/lib/typescript/src/TrueSheetGrabber.d.ts.map +1 -0
  96. package/lib/typescript/src/fabric/TrueSheetContainerViewNativeComponent.d.ts +6 -0
  97. package/lib/typescript/src/fabric/TrueSheetContainerViewNativeComponent.d.ts.map +1 -0
  98. package/lib/typescript/src/fabric/TrueSheetContentViewNativeComponent.d.ts +6 -0
  99. package/lib/typescript/src/fabric/TrueSheetContentViewNativeComponent.d.ts.map +1 -0
  100. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts +6 -0
  101. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts.map +1 -0
  102. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +44 -0
  103. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -0
  104. package/lib/typescript/{commonjs/src → src}/index.d.ts +1 -0
  105. package/lib/typescript/src/index.d.ts.map +1 -0
  106. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.d.ts +43 -0
  107. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.d.ts.map +1 -0
  108. package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts +57 -0
  109. package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts.map +1 -0
  110. package/lib/typescript/src/reanimated/index.d.ts +4 -0
  111. package/lib/typescript/src/reanimated/index.d.ts.map +1 -0
  112. package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.d.ts +6 -0
  113. package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.d.ts.map +1 -0
  114. package/lib/typescript/src/specs/NativeTrueSheetModule.d.ts +34 -0
  115. package/lib/typescript/src/specs/NativeTrueSheetModule.d.ts.map +1 -0
  116. package/package.json +104 -75
  117. package/react-native.config.js +17 -0
  118. package/src/TrueSheet.tsx +285 -188
  119. package/src/TrueSheet.types.ts +119 -106
  120. package/src/TrueSheetGrabber.tsx +29 -28
  121. package/src/__mocks__/index.js +60 -12
  122. package/src/fabric/TrueSheetContainerViewNativeComponent.ts +8 -0
  123. package/src/fabric/TrueSheetContentViewNativeComponent.ts +8 -0
  124. package/src/fabric/TrueSheetFooterViewNativeComponent.ts +8 -0
  125. package/src/fabric/TrueSheetViewNativeComponent.ts +63 -0
  126. package/src/index.ts +4 -3
  127. package/src/reanimated/ReanimatedTrueSheet.tsx +95 -0
  128. package/src/reanimated/ReanimatedTrueSheetProvider.tsx +92 -0
  129. package/src/reanimated/index.ts +3 -0
  130. package/src/reanimated/useReanimatedPositionChangeHandler.ts +26 -0
  131. package/src/specs/NativeTrueSheetModule.ts +38 -0
  132. package/TrueSheet.podspec +0 -49
  133. package/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt +0 -413
  134. package/android/src/main/java/com/lodev09/truesheet/TrueSheetEvent.kt +0 -22
  135. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewModule.kt +0 -63
  136. package/android/src/main/java/com/lodev09/truesheet/core/KeyboardManager.kt +0 -58
  137. package/android/src/main/java/com/lodev09/truesheet/core/RootSheetView.kt +0 -102
  138. package/ios/Extensions/UIBlurEffect+withTint.swift +0 -62
  139. package/ios/Extensions/UIView+pinTo.swift +0 -74
  140. package/ios/Extensions/UIViewController+detentForSize.swift +0 -134
  141. package/ios/TrueSheet-Bridging-Header.h +0 -14
  142. package/ios/TrueSheetEvent.swift +0 -48
  143. package/ios/TrueSheetView.swift +0 -467
  144. package/ios/TrueSheetViewController.swift +0 -275
  145. package/ios/TrueSheetViewManager.m +0 -53
  146. package/ios/TrueSheetViewManager.swift +0 -48
  147. package/ios/Utils/Logger.swift +0 -39
  148. package/ios/Utils/Promise.swift +0 -25
  149. package/lib/commonjs/TrueSheet.js +0 -258
  150. package/lib/commonjs/TrueSheet.js.map +0 -1
  151. package/lib/commonjs/TrueSheet.types.js +0 -6
  152. package/lib/commonjs/TrueSheet.types.js.map +0 -1
  153. package/lib/commonjs/TrueSheetFooter.js +0 -19
  154. package/lib/commonjs/TrueSheetFooter.js.map +0 -1
  155. package/lib/commonjs/TrueSheetGrabber.js +0 -54
  156. package/lib/commonjs/TrueSheetGrabber.js.map +0 -1
  157. package/lib/commonjs/TrueSheetModule.js +0 -19
  158. package/lib/commonjs/TrueSheetModule.js.map +0 -1
  159. package/lib/commonjs/__mocks__/index.js +0 -52
  160. package/lib/commonjs/__mocks__/index.js.map +0 -1
  161. package/lib/commonjs/index.js +0 -39
  162. package/lib/commonjs/index.js.map +0 -1
  163. package/lib/module/TrueSheetFooter.js +0 -14
  164. package/lib/module/TrueSheetFooter.js.map +0 -1
  165. package/lib/module/TrueSheetModule.js +0 -15
  166. package/lib/module/TrueSheetModule.js.map +0 -1
  167. package/lib/module/__mocks__/index.js +0 -21
  168. package/lib/module/__mocks__/index.js.map +0 -1
  169. package/lib/typescript/commonjs/package.json +0 -1
  170. package/lib/typescript/commonjs/src/TrueSheet.d.ts +0 -70
  171. package/lib/typescript/commonjs/src/TrueSheet.d.ts.map +0 -1
  172. package/lib/typescript/commonjs/src/TrueSheet.types.d.ts +0 -241
  173. package/lib/typescript/commonjs/src/TrueSheet.types.d.ts.map +0 -1
  174. package/lib/typescript/commonjs/src/TrueSheetFooter.d.ts +0 -7
  175. package/lib/typescript/commonjs/src/TrueSheetFooter.d.ts.map +0 -1
  176. package/lib/typescript/commonjs/src/TrueSheetGrabber.d.ts.map +0 -1
  177. package/lib/typescript/commonjs/src/TrueSheetModule.d.ts +0 -2
  178. package/lib/typescript/commonjs/src/TrueSheetModule.d.ts.map +0 -1
  179. package/lib/typescript/commonjs/src/index.d.ts.map +0 -1
  180. package/lib/typescript/module/src/TrueSheet.d.ts +0 -70
  181. package/lib/typescript/module/src/TrueSheet.d.ts.map +0 -1
  182. package/lib/typescript/module/src/TrueSheet.types.d.ts +0 -241
  183. package/lib/typescript/module/src/TrueSheet.types.d.ts.map +0 -1
  184. package/lib/typescript/module/src/TrueSheetFooter.d.ts +0 -7
  185. package/lib/typescript/module/src/TrueSheetFooter.d.ts.map +0 -1
  186. package/lib/typescript/module/src/TrueSheetGrabber.d.ts +0 -39
  187. package/lib/typescript/module/src/TrueSheetGrabber.d.ts.map +0 -1
  188. package/lib/typescript/module/src/TrueSheetModule.d.ts +0 -2
  189. package/lib/typescript/module/src/TrueSheetModule.d.ts.map +0 -1
  190. package/lib/typescript/module/src/index.d.ts +0 -4
  191. package/lib/typescript/module/src/index.d.ts.map +0 -1
  192. package/src/TrueSheetFooter.tsx +0 -17
  193. package/src/TrueSheetModule.ts +0 -19
  194. /package/lib/{typescript/module → module}/package.json +0 -0
@@ -1,176 +1,80 @@
1
1
  package com.lodev09.truesheet
2
2
 
3
- import android.content.Context
3
+ import android.annotation.SuppressLint
4
4
  import android.view.View
5
- import android.view.ViewGroup
6
5
  import android.view.ViewStructure
7
6
  import android.view.accessibility.AccessibilityEvent
8
- import com.facebook.react.bridge.Arguments
7
+ import androidx.annotation.UiThread
9
8
  import com.facebook.react.bridge.LifecycleEventListener
10
- import com.facebook.react.bridge.UiThreadUtil
11
- import com.facebook.react.bridge.WritableMap
12
9
  import com.facebook.react.uimanager.ThemedReactContext
13
10
  import com.facebook.react.uimanager.UIManagerHelper
14
11
  import com.facebook.react.uimanager.events.EventDispatcher
15
- import com.google.android.material.bottomsheet.BottomSheetBehavior
16
- import com.lodev09.truesheet.core.RootSheetView
17
- import com.lodev09.truesheet.core.Utils
18
-
19
- class TrueSheetView(context: Context) :
20
- ViewGroup(context),
21
- LifecycleEventListener {
22
-
23
- private val reactContext: ThemedReactContext
24
- get() = context as ThemedReactContext
25
-
26
- private val surfaceId: Int
27
- get() = UIManagerHelper.getSurfaceId(this)
28
-
29
- var eventDispatcher: EventDispatcher?
30
- get() = rootSheetView.eventDispatcher
31
- set(eventDispatcher) {
32
- rootSheetView.eventDispatcher = eventDispatcher
33
- }
34
-
35
- var initialIndex: Int = -1
36
- var initialIndexAnimated: Boolean = true
37
-
38
- /**
39
- * Determines if the sheet is being dragged by the user.
40
- */
41
- private var isDragging = false
12
+ import com.facebook.react.views.view.ReactViewGroup
13
+ import com.lodev09.truesheet.events.DetentChangeEvent
14
+ import com.lodev09.truesheet.events.DidDismissEvent
15
+ import com.lodev09.truesheet.events.DidPresentEvent
16
+ import com.lodev09.truesheet.events.DragBeginEvent
17
+ import com.lodev09.truesheet.events.DragChangeEvent
18
+ import com.lodev09.truesheet.events.DragEndEvent
19
+ import com.lodev09.truesheet.events.MountEvent
20
+ import com.lodev09.truesheet.events.PositionChangeEvent
21
+ import com.lodev09.truesheet.events.SizeChangeEvent
22
+ import com.lodev09.truesheet.events.WillDismissEvent
23
+ import com.lodev09.truesheet.events.WillPresentEvent
24
+
25
+ /**
26
+ * Main TrueSheet host view.
27
+ * Manages the sheet dialog and container, and dispatches events to JavaScript.
28
+ */
29
+ @SuppressLint("ViewConstructor")
30
+ class TrueSheetView(private val reactContext: ThemedReactContext) :
31
+ ReactViewGroup(reactContext),
32
+ LifecycleEventListener,
33
+ TrueSheetViewControllerDelegate,
34
+ TrueSheetContainerViewDelegate {
42
35
 
43
36
  /**
44
- * Current activeIndex.
37
+ * The TrueSheetViewController instance that acts as both root view and controller
45
38
  */
46
- private var currentSizeIndex: Int = -1
39
+ private val viewController: TrueSheetViewController = TrueSheetViewController(reactContext)
47
40
 
48
41
  /**
49
- * Promise callback to be invoked after `present` is called.
42
+ * Gets the container view (first child of view controller)
50
43
  */
51
- private var presentPromise: (() -> Unit)? = null
44
+ private val containerView: TrueSheetContainerView?
45
+ get() = viewController.getChildAt(0) as? TrueSheetContainerView
52
46
 
53
- /**
54
- * Promise callback to be invoked after `dismiss` is called.
55
- */
56
- private var dismissPromise: (() -> Unit)? = null
47
+ var eventDispatcher: EventDispatcher? = null
57
48
 
58
- /**
59
- * The main BottomSheetDialog instance.
60
- */
61
- private val sheetDialog: TrueSheetDialog
49
+ var initialDetentIndex: Int = -1
50
+ var initialDetentAnimated: Boolean = true
62
51
 
63
52
  /**
64
- * React root view placeholder.
53
+ * Tracks if initial presentation has been handled
65
54
  */
66
- private val rootSheetView: RootSheetView
55
+ private var hasHandledInitialPresentation = false
67
56
 
68
57
  init {
69
58
  reactContext.addLifecycleEventListener(this)
70
59
 
71
- rootSheetView = RootSheetView(context)
72
- sheetDialog = TrueSheetDialog(reactContext, rootSheetView)
73
-
74
- // Configure Sheet Dialog
75
- sheetDialog.apply {
76
- setOnSizeChangeListener { w, h ->
77
- val data = Arguments.createMap()
78
- data.putDouble("width", Utils.toDIP(w.toFloat()).toDouble())
79
- data.putDouble("height", Utils.toDIP(h.toFloat()).toDouble())
60
+ // Set delegates
61
+ viewController.delegate = this
80
62
 
81
- dispatchEvent(TrueSheetEvent.CONTAINER_SIZE_CHANGE, data)
82
- }
83
-
84
- // Setup listener when the dialog has been presented.
85
- setOnShowListener {
86
- registerKeyboardManager()
87
-
88
- // Initialize footer y
89
- UiThreadUtil.runOnUiThread {
90
- positionFooter()
91
- }
92
-
93
- // Re-enable animation
94
- resetAnimation()
95
-
96
- // Resolve the present promise
97
- presentPromise?.let { promise ->
98
- promise()
99
- presentPromise = null
100
- }
101
-
102
- // Dispatch onPresent event
103
- dispatchEvent(TrueSheetEvent.PRESENT, sizeInfoData(getSizeInfoForIndex(currentSizeIndex)))
104
- }
105
-
106
- // Setup listener when the dialog has been dismissed.
107
- setOnDismissListener {
108
- unregisterKeyboardManager()
109
-
110
- // Resolve the dismiss promise
111
- dismissPromise?.let { promise ->
112
- promise()
113
- dismissPromise = null
114
- }
115
-
116
- // Dispatch onDismiss event
117
- dispatchEvent(TrueSheetEvent.DISMISS)
118
- }
119
-
120
- // Configure sheet behavior events
121
- behavior.addBottomSheetCallback(
122
- object : BottomSheetBehavior.BottomSheetCallback() {
123
- override fun onSlide(sheetView: View, slideOffset: Float) {
124
- when (behavior.state) {
125
- // For consistency with IOS, we consider SETTLING as dragging change.
126
- BottomSheetBehavior.STATE_DRAGGING,
127
- BottomSheetBehavior.STATE_SETTLING -> handleDragChange(sheetView)
128
-
129
- else -> { }
130
- }
131
-
132
- footerView?.let {
133
- val y = (maxScreenHeight - sheetView.top - footerHeight).toFloat()
134
- if (slideOffset >= 0) {
135
- // Sheet is expanding
136
- it.y = y
137
- } else {
138
- // Sheet is collapsing
139
- it.y = y - footerHeight * slideOffset
140
- }
141
- }
142
- }
143
-
144
- override fun onStateChanged(sheetView: View, newState: Int) {
145
- if (!isShowing) return
146
-
147
- when (newState) {
148
- // When changed to dragging, we know that the drag has started
149
- BottomSheetBehavior.STATE_DRAGGING -> handleDragBegin(sheetView)
150
-
151
- // Either of the following state determines drag end
152
- BottomSheetBehavior.STATE_EXPANDED,
153
- BottomSheetBehavior.STATE_COLLAPSED,
154
- BottomSheetBehavior.STATE_HALF_EXPANDED -> handleDragEnd(newState)
155
-
156
- else -> { }
157
- }
158
- }
159
- }
160
- )
161
- }
63
+ // Hide the host view from layout and touch handling
64
+ // The actual content is shown in a dialog window
65
+ visibility = GONE
162
66
  }
163
67
 
164
68
  override fun dispatchProvideStructure(structure: ViewStructure) {
165
- rootSheetView.dispatchProvideStructure(structure)
69
+ super.dispatchProvideStructure(structure)
166
70
  }
167
71
 
168
72
  override fun onLayout(
169
73
  changed: Boolean,
170
- l: Int,
171
- t: Int,
172
- r: Int,
173
- b: Int
74
+ left: Int,
75
+ top: Int,
76
+ right: Int,
77
+ bottom: Int
174
78
  ) {
175
79
  // Do nothing as we are laid out by UIManager
176
80
  }
@@ -178,261 +82,311 @@ class TrueSheetView(context: Context) :
178
82
  override fun setId(id: Int) {
179
83
  super.setId(id)
180
84
 
181
- // Forward the ID to our content view, so event dispatching behaves correctly
182
- rootSheetView.id = id
85
+ viewController.id = id
86
+ TrueSheetModule.registerView(this, id)
183
87
  }
184
88
 
185
89
  override fun onAttachedToWindow() {
186
90
  super.onAttachedToWindow()
187
-
188
- // Initialize content
189
- UiThreadUtil.runOnUiThread {
190
- sheetDialog.contentView?.height?.let { setContentHeight(it) }
191
- sheetDialog.footerView?.height?.let { setFooterHeight(it) }
192
-
193
- if (initialIndex >= 0) {
194
- currentSizeIndex = initialIndex
195
- sheetDialog.present(initialIndex, initialIndexAnimated)
196
- }
197
-
198
- // Dispatch onMount event
199
- dispatchEvent(TrueSheetEvent.MOUNT)
200
- }
201
91
  }
202
92
 
203
93
  override fun onDetachedFromWindow() {
204
94
  super.onDetachedFromWindow()
205
95
  onDropInstance()
96
+
97
+ TrueSheetModule.unregisterView(id)
206
98
  }
207
99
 
208
- override fun addView(child: View, index: Int) {
209
- UiThreadUtil.assertOnUiThread()
210
- rootSheetView.addView(child, index)
100
+ /**
101
+ * showOrUpdate will display the Dialog. It is called by the manager once all properties are set
102
+ * because we need to know all of them before creating the Dialog. It is also smart during updates
103
+ * if the changed properties can be applied directly to the Dialog or require the recreation of a
104
+ * new Dialog.
105
+ */
106
+ fun showOrUpdate() {
107
+ // Only handle initial presentation once on mount
108
+ if (!hasHandledInitialPresentation && initialDetentIndex >= 0) {
109
+ hasHandledInitialPresentation = true
110
+
111
+ // Create dialog if not created yet
112
+ if (!viewController.isPresented) {
113
+ viewController.createDialog()
114
+ }
211
115
 
212
- // Hide this host view
213
- visibility = GONE
116
+ post {
117
+ present(initialDetentIndex, initialDetentAnimated) { }
118
+ }
119
+ } else if (viewController.isPresented) {
120
+ viewController.setupSheetDetents()
121
+ viewController.setStateForDetentIndex(viewController.currentDetentIndex)
122
+ viewController.positionFooter()
123
+ }
214
124
  }
215
125
 
216
- override fun getChildCount(): Int {
217
- // This method may be called by the parent constructor
218
- // before rootView is initialized.
219
- return rootSheetView.childCount
126
+ // ==================== View Management ====================
127
+
128
+ override fun addView(child: View?, index: Int) {
129
+ // Add the child to our ViewController
130
+ // This is the TrueSheetContainerView
131
+ viewController.addView(child, index)
132
+
133
+ // Create dialog and dispatch mount event when TrueSheetContainerView is added
134
+ if (child is TrueSheetContainerView) {
135
+ // Set up container delegate to listen for content size changes
136
+ child.delegate = this
137
+
138
+ // Get initial content height from container
139
+ val contentHeight = child.contentHeight
140
+ if (contentHeight > 0) {
141
+ viewController.contentHeight = contentHeight
142
+ }
143
+
144
+ // Create the dialog now that the container is mounted
145
+ viewController.createDialog()
146
+
147
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
148
+ eventDispatcher?.dispatchEvent(
149
+ MountEvent(surfaceId, id)
150
+ )
151
+ }
220
152
  }
221
153
 
222
- override fun getChildAt(index: Int): View = rootSheetView.getChildAt(index)
154
+ override fun getChildCount(): Int = viewController.childCount
155
+ override fun getChildAt(index: Int): View? = viewController.getChildAt(index)
156
+
157
+ override fun removeView(child: View?) {
158
+ if (child != null) {
159
+ // Clean up container delegate
160
+ if (child is TrueSheetContainerView) {
161
+ child.delegate = null
162
+ }
223
163
 
224
- override fun removeView(child: View) {
225
- UiThreadUtil.assertOnUiThread()
226
- rootSheetView.removeView(child)
164
+ viewController.removeView(child)
165
+ }
227
166
  }
228
167
 
229
168
  override fun removeViewAt(index: Int) {
230
- UiThreadUtil.assertOnUiThread()
231
169
  val child = getChildAt(index)
232
- rootSheetView.removeView(child)
170
+ viewController.removeView(child)
233
171
  }
234
172
 
235
173
  override fun addChildrenForAccessibility(outChildren: ArrayList<View>) {
236
174
  // Explicitly override this to prevent accessibility events being passed down to children
237
- // Those will be handled by the rootView which lives in the dialog
175
+ // Those will be handled by the mHostView which lives in the dialog
238
176
  }
239
177
 
240
178
  // Explicitly override this to prevent accessibility events being passed down to children
241
179
  // Those will be handled by the mHostView which lives in the dialog
242
- public override fun dispatchPopulateAccessibilityEvent(event: AccessibilityEvent): Boolean = false
180
+ override fun dispatchPopulateAccessibilityEvent(event: AccessibilityEvent): Boolean = false
181
+
182
+ fun onDropInstance() {
183
+ reactContext.removeLifecycleEventListener(this)
184
+ TrueSheetModule.unregisterView(id)
185
+
186
+ if (viewController.isPresented) {
187
+ viewController.dismiss()
188
+ }
189
+ viewController.delegate = null
190
+ }
243
191
 
244
192
  override fun onHostResume() {
245
- configureIfShowing()
193
+ showOrUpdate()
246
194
  }
247
195
 
248
196
  override fun onHostPause() {
249
- // do nothing
250
197
  }
251
198
 
252
199
  override fun onHostDestroy() {
253
- // Drop the instance if the host is destroyed which will dismiss the dialog
254
200
  onDropInstance()
255
201
  }
256
202
 
257
- fun onDropInstance() {
258
- reactContext.removeLifecycleEventListener(this)
259
- sheetDialog.dismiss()
260
- }
261
-
262
- private fun sizeInfoData(sizeInfo: SizeInfo): WritableMap {
263
- val data = Arguments.createMap()
264
- data.putInt("index", sizeInfo.index)
265
- data.putDouble("value", sizeInfo.value.toDouble())
203
+ // ==================== TrueSheetViewControllerDelegate Implementation ====================
266
204
 
267
- return data
205
+ override fun viewControllerWillPresent(index: Int, position: Float) {
206
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
207
+ eventDispatcher?.dispatchEvent(
208
+ WillPresentEvent(surfaceId, id, index, position)
209
+ )
268
210
  }
269
211
 
270
- private fun getCurrentSizeInfo(sheetView: View): SizeInfo {
271
- val height = sheetDialog.maxScreenHeight - sheetView.top
272
- val currentSizeInfo = SizeInfo(currentSizeIndex, Utils.toDIP(height.toFloat()))
273
-
274
- return currentSizeInfo
275
- }
212
+ override fun viewControllerDidPresent(index: Int, position: Float) {
213
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
214
+ eventDispatcher?.dispatchEvent(
215
+ DidPresentEvent(surfaceId, id, index, position)
216
+ )
276
217
 
277
- private fun handleDragBegin(sheetView: View) {
278
- // Dispatch drag started event
279
- dispatchEvent(TrueSheetEvent.DRAG_BEGIN, sizeInfoData(getCurrentSizeInfo(sheetView)))
280
- // Flag sheet is being dragged
281
- isDragging = true
218
+ // Set our touch event dispatcher on the view controller
219
+ viewController.eventDispatcher = eventDispatcher
282
220
  }
283
221
 
284
- private fun handleDragChange(sheetView: View) {
285
- if (!isDragging) return
222
+ override fun viewControllerWillDismiss() {
223
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
224
+ eventDispatcher?.dispatchEvent(
225
+ WillDismissEvent(surfaceId, id)
226
+ )
286
227
 
287
- // Dispatch drag change event
288
- dispatchEvent(TrueSheetEvent.DRAG_CHANGE, sizeInfoData(getCurrentSizeInfo(sheetView)))
228
+ // Clear our touch event dispatcher on the view controller
229
+ viewController.eventDispatcher = null
289
230
  }
290
231
 
291
- private fun handleDragEnd(state: Int) {
292
- if (!isDragging) return
293
-
294
- // For consistency with IOS,
295
- // we only handle state changes after dragging.
296
- //
297
- // Changing size programmatically is handled via the present method.
298
- val sizeInfo = sheetDialog.getSizeInfoForState(state)
299
- sizeInfo?.let {
300
- // Dispatch drag ended after dragging
301
- dispatchEvent(TrueSheetEvent.DRAG_END, sizeInfoData(it))
302
- if (it.index != currentSizeIndex) {
303
- // Invoke promise when sheet resized programmatically
304
- presentPromise?.let { promise ->
305
- promise()
306
- presentPromise = null
307
- }
308
-
309
- currentSizeIndex = it.index
310
- sheetDialog.setupDimmedBackground(it.index)
311
-
312
- // Dispatch onSizeChange event
313
- dispatchEvent(TrueSheetEvent.SIZE_CHANGE, sizeInfoData(it))
314
- }
315
- }
316
-
317
- isDragging = false
232
+ override fun viewControllerDidDismiss() {
233
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
234
+ eventDispatcher?.dispatchEvent(
235
+ DidDismissEvent(surfaceId, id)
236
+ )
318
237
  }
319
238
 
320
- private fun dispatchEvent(name: String, data: WritableMap? = null) {
321
- eventDispatcher?.dispatchEvent(TrueSheetEvent(surfaceId, id, name, data))
239
+ override fun viewControllerDidChangeDetent(index: Int, position: Float) {
240
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
241
+ eventDispatcher?.dispatchEvent(
242
+ DetentChangeEvent(surfaceId, id, index, position)
243
+ )
322
244
  }
323
245
 
324
- fun configureIfShowing() {
325
- if (sheetDialog.isShowing) {
326
- sheetDialog.configure()
327
- sheetDialog.setStateForSizeIndex(currentSizeIndex)
328
-
329
- UiThreadUtil.runOnUiThread {
330
- sheetDialog.positionFooter()
331
- }
332
- }
246
+ override fun viewControllerDidDragBegin(index: Int, position: Float) {
247
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
248
+ eventDispatcher?.dispatchEvent(
249
+ DragBeginEvent(surfaceId, id, index, position)
250
+ )
333
251
  }
334
252
 
335
- fun setEdgeToEdge(edgeToEdge: Boolean) {
336
- sheetDialog.edgeToEdge = edgeToEdge
253
+ override fun viewControllerDidDragChange(index: Int, position: Float) {
254
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
255
+ eventDispatcher?.dispatchEvent(
256
+ DragChangeEvent(surfaceId, id, index, position)
257
+ )
337
258
  }
338
259
 
339
- fun setMaxHeight(height: Int) {
340
- if (sheetDialog.maxSheetHeight == height) return
341
-
342
- sheetDialog.maxSheetHeight = height
343
- configureIfShowing()
260
+ override fun viewControllerDidDragEnd(index: Int, position: Float) {
261
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
262
+ eventDispatcher?.dispatchEvent(
263
+ DragEndEvent(surfaceId, id, index, position)
264
+ )
344
265
  }
345
266
 
346
- fun setContentHeight(height: Int) {
347
- if (sheetDialog.contentHeight == height) return
348
-
349
- sheetDialog.contentHeight = height
350
- configureIfShowing()
267
+ override fun viewControllerDidChangePosition(index: Int, position: Float, transitioning: Boolean) {
268
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
269
+ eventDispatcher?.dispatchEvent(
270
+ PositionChangeEvent(surfaceId, id, index, position, transitioning)
271
+ )
351
272
  }
352
273
 
353
- fun setFooterHeight(height: Int) {
354
- if (sheetDialog.footerHeight == height) return
274
+ // ==================== Property Setters (forward to controller) ====================
355
275
 
356
- sheetDialog.footerHeight = height
357
- configureIfShowing()
276
+ fun setMaxHeight(height: Int) {
277
+ if (viewController.maxSheetHeight == height) return
278
+ viewController.maxSheetHeight = height
358
279
  }
359
280
 
360
281
  fun setDimmed(dimmed: Boolean) {
361
- if (sheetDialog.dimmed == dimmed) return
362
-
363
- sheetDialog.dimmed = dimmed
364
- if (sheetDialog.isShowing) {
365
- sheetDialog.setupDimmedBackground(currentSizeIndex)
282
+ if (viewController.dimmed == dimmed) return
283
+ viewController.dimmed = dimmed
284
+ if (viewController.isPresented) {
285
+ viewController.setupDimmedBackground(viewController.currentDetentIndex)
366
286
  }
367
287
  }
368
288
 
369
289
  fun setDimmedIndex(index: Int) {
370
- if (sheetDialog.dimmedIndex == index) return
371
-
372
- sheetDialog.dimmedIndex = index
373
- if (sheetDialog.isShowing) {
374
- sheetDialog.setupDimmedBackground(currentSizeIndex)
290
+ if (viewController.dimmedIndex == index) return
291
+ viewController.dimmedIndex = index
292
+ if (viewController.isPresented) {
293
+ viewController.setupDimmedBackground(viewController.currentDetentIndex)
375
294
  }
376
295
  }
377
296
 
378
297
  fun setCornerRadius(radius: Float) {
379
- if (sheetDialog.cornerRadius == radius) return
380
-
381
- sheetDialog.cornerRadius = radius
382
- sheetDialog.setupBackground()
298
+ if (viewController.cornerRadius == radius) return
299
+ viewController.cornerRadius = radius
300
+ viewController.setupBackground()
383
301
  }
384
302
 
385
- fun setBackground(color: Int) {
386
- if (sheetDialog.backgroundColor == color) return
387
-
388
- sheetDialog.backgroundColor = color
389
- sheetDialog.setupBackground()
303
+ fun setSheetBackgroundColor(color: Int) {
304
+ if (viewController.sheetBackgroundColor == color) return
305
+ viewController.sheetBackgroundColor = color
306
+ viewController.setupBackground()
390
307
  }
391
308
 
392
309
  fun setSoftInputMode(mode: Int) {
393
- sheetDialog.window?.apply {
394
- this.setSoftInputMode(mode)
395
- }
310
+ viewController.setSoftInputMode(mode)
396
311
  }
397
312
 
398
313
  fun setDismissible(dismissible: Boolean) {
399
- sheetDialog.dismissible = dismissible
314
+ viewController.dismissible = dismissible
400
315
  }
401
316
 
402
- fun setSizes(newSizes: Array<Any>) {
403
- sheetDialog.sizes = newSizes
404
- configureIfShowing()
405
- }
406
-
407
- /**
408
- * Present the sheet at given size index.
409
- */
410
- fun present(sizeIndex: Int, promiseCallback: () -> Unit) {
411
- UiThreadUtil.assertOnUiThread()
317
+ fun setGrabber(grabber: Boolean) {}
412
318
 
413
- currentSizeIndex = sizeIndex
319
+ fun setDetents(newDetents: MutableList<Double>) {
320
+ viewController.detents = newDetents
321
+ }
414
322
 
415
- if (sheetDialog.isShowing) {
416
- // For consistency with IOS, we are not waiting
417
- // for the state to change before dispatching onSizeChange event.
418
- val sizeInfo = sheetDialog.getSizeInfoForIndex(sizeIndex)
419
- dispatchEvent(TrueSheetEvent.SIZE_CHANGE, sizeInfoData(sizeInfo))
323
+ fun setBlurTint(tint: String?) {}
420
324
 
421
- promiseCallback()
422
- } else {
423
- presentPromise = promiseCallback
424
- }
325
+ fun setEdgeToEdgeFullScreen(edgeToEdgeFullScreen: Boolean) {
326
+ viewController.edgeToEdgeFullScreen = edgeToEdgeFullScreen
327
+ }
425
328
 
426
- sheetDialog.present(sizeIndex)
329
+ /**
330
+ * Presents the sheet at the given detent index.
331
+ *
332
+ * @param detentIndex The detent index to present at
333
+ * @param animated Whether to animate the presentation
334
+ * @param promiseCallback Callback invoked when presentation completes
335
+ */
336
+ @UiThread
337
+ fun present(detentIndex: Int, animated: Boolean = true, promiseCallback: () -> Unit) {
338
+ viewController.presentPromise = promiseCallback
339
+ viewController.present(detentIndex, animated)
427
340
  }
428
341
 
429
342
  /**
430
343
  * Dismisses the sheet.
344
+ *
345
+ * @param promiseCallback Callback invoked when dismissal completes
431
346
  */
347
+ @UiThread
432
348
  fun dismiss(promiseCallback: () -> Unit) {
433
- UiThreadUtil.assertOnUiThread()
349
+ viewController.dismissPromise = promiseCallback
350
+ viewController.dismiss()
351
+ }
352
+
353
+ override fun viewControllerDidChangeSize(width: Int, height: Int) {
354
+ // Dispatch size change event to JS
355
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
356
+ eventDispatcher?.dispatchEvent(
357
+ SizeChangeEvent(surfaceId, id, width, height)
358
+ )
359
+ }
360
+
361
+ // ==================== TrueSheetContainerViewDelegate Implementation ====================
362
+
363
+ override fun containerViewContentDidChangeSize(width: Int, height: Int) {
364
+ // Clamp content height to container height to prevent unbounded growth with scrollable content
365
+ val containerHeight = viewController.maxScreenHeight
366
+ val contentHeight = if (containerHeight > 0) minOf(height, containerHeight) else height
367
+
368
+ viewController.contentHeight = contentHeight
369
+
370
+ // Update detents if sheet is already presented
371
+ if (viewController.isPresented) {
372
+ // Reconfigure sheet detents with new content height
373
+ viewController.setupSheetDetents()
374
+
375
+ // Use post to ensure layout is complete before positioning footer
376
+ viewController.post {
377
+ viewController.positionFooter()
378
+ }
379
+ }
380
+ }
381
+
382
+ override fun containerViewFooterDidChangeSize(width: Int, height: Int) {
383
+ // Reposition footer when its size changes
384
+ if (viewController.isPresented) {
385
+ viewController.positionFooter()
386
+ }
387
+ }
434
388
 
435
- dismissPromise = promiseCallback
436
- sheetDialog.dismiss()
389
+ companion object {
390
+ const val TAG_NAME = "TrueSheet"
437
391
  }
438
392
  }