@lodev09/react-native-true-sheet 4.0.0-beta.2 → 4.0.0-beta.21

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 (205) hide show
  1. package/README.md +25 -4
  2. package/android/build.gradle +22 -1
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +50 -16
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +303 -184
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +21 -110
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +116 -0
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +18 -0
  11. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +124 -50
  12. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +369 -163
  13. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +59 -12
  14. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetBehavior.kt +126 -0
  15. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +22 -9
  16. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +84 -5
  17. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +104 -39
  18. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +3 -0
  19. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +137 -0
  20. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
  21. package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +15 -2
  22. package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
  23. package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
  24. package/android/src/main/jni/TrueSheetSpec.h +1 -0
  25. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +1 -9
  26. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +110 -30
  27. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +30 -7
  28. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +2 -2
  29. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +9 -8
  30. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
  31. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +9 -3
  32. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +3 -6
  33. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
  34. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +4 -6
  35. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
  36. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
  37. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +43 -0
  38. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
  39. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
  40. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +33 -0
  41. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
  42. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +7 -40
  43. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
  44. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
  45. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
  46. package/ios/TrueSheetContainerView.h +27 -7
  47. package/ios/TrueSheetContainerView.mm +31 -11
  48. package/ios/TrueSheetContentView.h +43 -15
  49. package/ios/TrueSheetContentView.mm +211 -189
  50. package/ios/TrueSheetFooterView.h +6 -0
  51. package/ios/TrueSheetFooterView.mm +93 -17
  52. package/ios/TrueSheetModule.h +2 -7
  53. package/ios/TrueSheetModule.mm +7 -16
  54. package/ios/TrueSheetOverlayView.h +32 -0
  55. package/ios/TrueSheetOverlayView.mm +161 -0
  56. package/ios/TrueSheetView.mm +275 -56
  57. package/ios/TrueSheetViewController.h +31 -3
  58. package/ios/TrueSheetViewController.mm +393 -94
  59. package/ios/core/TrueSheetBlurView.h +0 -2
  60. package/ios/core/TrueSheetBlurView.mm +3 -36
  61. package/ios/core/TrueSheetDetentCalculator.h +7 -1
  62. package/ios/core/TrueSheetDetentCalculator.mm +27 -4
  63. package/ios/core/TrueSheetGrabberView.mm +13 -3
  64. package/ios/core/TrueSheetKeyboardObserver.mm +2 -0
  65. package/ios/core/TrueSheetOverlayContainerView.h +31 -0
  66. package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
  67. package/ios/events/TrueSheetLifecycleEvents.h +2 -0
  68. package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
  69. package/ios/tvos/TrueSheetStubs.mm +12 -0
  70. package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
  71. package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
  72. package/lib/module/TrueSheet.js +175 -39
  73. package/lib/module/TrueSheet.js.map +1 -1
  74. package/lib/module/TrueSheet.web.js +220 -243
  75. package/lib/module/TrueSheet.web.js.map +1 -1
  76. package/lib/module/TrueSheetBackground.js +15 -0
  77. package/lib/module/TrueSheetBackground.js.map +1 -0
  78. package/lib/module/TrueSheetOverlay.js +30 -0
  79. package/lib/module/TrueSheetOverlay.js.map +1 -0
  80. package/lib/module/TrueSheetOverlay.web.js +35 -0
  81. package/lib/module/TrueSheetOverlay.web.js.map +1 -0
  82. package/lib/module/TrueSheetPeek.web.js +20 -15
  83. package/lib/module/TrueSheetPeek.web.js.map +1 -1
  84. package/lib/module/detentBackgrounds.js +21 -0
  85. package/lib/module/detentBackgrounds.js.map +1 -0
  86. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  87. package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  88. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +15 -10
  89. package/lib/module/index.js +1 -0
  90. package/lib/module/index.js.map +1 -1
  91. package/lib/module/mocks/index.js +18 -2
  92. package/lib/module/mocks/index.js.map +1 -1
  93. package/lib/module/mocks/navigationExpoRouter.js.map +1 -1
  94. package/lib/module/navigation/base-types.js +2 -0
  95. package/lib/module/navigation/base-types.js.map +1 -0
  96. package/lib/module/navigation/createTrueSheetRouter.js +0 -9
  97. package/lib/module/navigation/createTrueSheetRouter.js.map +1 -1
  98. package/lib/module/navigation/expo-router/base-types.js +2 -0
  99. package/lib/module/navigation/expo-router/base-types.js.map +1 -0
  100. package/lib/module/navigation/expo-router/index.js +10 -7
  101. package/lib/module/navigation/expo-router/index.js.map +1 -1
  102. package/lib/module/navigation/expo-router/types.js +4 -0
  103. package/lib/module/navigation/expo-router/types.js.map +1 -0
  104. package/lib/module/navigation/index.js.map +1 -1
  105. package/lib/module/navigation/screen/useSheetScreenState.js +1 -0
  106. package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
  107. package/lib/module/web/constants.js +1 -1
  108. package/lib/module/web/constants.js.map +1 -1
  109. package/lib/module/web/layout.js +168 -0
  110. package/lib/module/web/layout.js.map +1 -0
  111. package/lib/module/web/vaul/constants.js +9 -0
  112. package/lib/module/web/vaul/constants.js.map +1 -1
  113. package/lib/module/web/vaul/context.js.map +1 -1
  114. package/lib/module/web/vaul/helpers.js +1 -0
  115. package/lib/module/web/vaul/helpers.js.map +1 -1
  116. package/lib/module/web/vaul/index.js +47 -7
  117. package/lib/module/web/vaul/index.js.map +1 -1
  118. package/lib/module/web/vaul/use-snap-points.js +21 -10
  119. package/lib/module/web/vaul/use-snap-points.js.map +1 -1
  120. package/lib/typescript/src/TrueSheet.d.ts +6 -0
  121. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  122. package/lib/typescript/src/TrueSheet.types.d.ts +157 -34
  123. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  124. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  125. package/lib/typescript/src/TrueSheetBackground.d.ts +3 -0
  126. package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
  127. package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
  128. package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
  129. package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
  130. package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
  131. package/lib/typescript/src/TrueSheetPeek.web.d.ts +8 -4
  132. package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
  133. package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
  134. package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
  135. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
  136. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
  137. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +12 -7
  138. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  139. package/lib/typescript/src/index.d.ts +1 -0
  140. package/lib/typescript/src/index.d.ts.map +1 -1
  141. package/lib/typescript/src/mocks/index.d.ts +4 -0
  142. package/lib/typescript/src/mocks/index.d.ts.map +1 -1
  143. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +3 -3
  144. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -1
  145. package/lib/typescript/src/navigation/base-types.d.ts +9 -0
  146. package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
  147. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +2 -13
  148. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -1
  149. package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
  150. package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
  151. package/lib/typescript/src/navigation/expo-router/index.d.ts +8 -6
  152. package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -1
  153. package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
  154. package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
  155. package/lib/typescript/src/navigation/index.d.ts +3 -4
  156. package/lib/typescript/src/navigation/index.d.ts.map +1 -1
  157. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +2 -1
  158. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
  159. package/lib/typescript/src/navigation/types.d.ts +41 -5
  160. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  161. package/lib/typescript/src/web/constants.d.ts +1 -1
  162. package/lib/typescript/src/web/constants.d.ts.map +1 -1
  163. package/lib/typescript/src/web/layout.d.ts +19 -0
  164. package/lib/typescript/src/web/layout.d.ts.map +1 -0
  165. package/lib/typescript/src/web/vaul/constants.d.ts +3 -0
  166. package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
  167. package/lib/typescript/src/web/vaul/context.d.ts +1 -0
  168. package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
  169. package/lib/typescript/src/web/vaul/helpers.d.ts +1 -0
  170. package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
  171. package/lib/typescript/src/web/vaul/index.d.ts +13 -1
  172. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
  173. package/lib/typescript/src/web/vaul/use-snap-points.d.ts +2 -1
  174. package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
  175. package/package.json +17 -8
  176. package/react-native.config.js +1 -0
  177. package/src/TrueSheet.tsx +208 -48
  178. package/src/TrueSheet.types.ts +166 -35
  179. package/src/TrueSheet.web.tsx +272 -290
  180. package/src/TrueSheetBackground.tsx +17 -0
  181. package/src/TrueSheetOverlay.tsx +24 -0
  182. package/src/TrueSheetOverlay.web.tsx +33 -0
  183. package/src/TrueSheetPeek.web.tsx +25 -23
  184. package/src/detentBackgrounds.ts +24 -0
  185. package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  186. package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  187. package/src/fabric/TrueSheetViewNativeComponent.ts +15 -10
  188. package/src/index.ts +1 -0
  189. package/src/mocks/index.ts +17 -2
  190. package/src/mocks/navigationExpoRouter.ts +6 -5
  191. package/src/navigation/base-types.ts +20 -0
  192. package/src/navigation/createTrueSheetRouter.ts +5 -33
  193. package/src/navigation/expo-router/base-types.ts +18 -0
  194. package/src/navigation/expo-router/index.ts +28 -30
  195. package/src/navigation/expo-router/types.ts +116 -0
  196. package/src/navigation/index.ts +6 -7
  197. package/src/navigation/screen/useSheetScreenState.ts +2 -0
  198. package/src/navigation/types.ts +75 -22
  199. package/src/web/constants.ts +1 -1
  200. package/src/web/layout.ts +196 -0
  201. package/src/web/vaul/constants.ts +9 -0
  202. package/src/web/vaul/context.ts +1 -0
  203. package/src/web/vaul/helpers.ts +5 -0
  204. package/src/web/vaul/index.tsx +75 -5
  205. package/src/web/vaul/use-snap-points.ts +32 -15
@@ -6,7 +6,6 @@ import android.view.ViewGroup
6
6
  import android.view.accessibility.AccessibilityEvent
7
7
  import androidx.annotation.UiThread
8
8
  import com.facebook.react.bridge.LifecycleEventListener
9
- import com.facebook.react.bridge.WritableNativeMap
10
9
  import com.facebook.react.uimanager.PixelUtil.dpToPx
11
10
  import com.facebook.react.uimanager.PixelUtil.pxToDp
12
11
  import com.facebook.react.uimanager.StateWrapper
@@ -19,9 +18,11 @@ import com.lodev09.truesheet.core.AccessibilityOptions
19
18
  import com.lodev09.truesheet.core.GrabberOptions
20
19
  import com.lodev09.truesheet.core.RNScreensEventObserver
21
20
  import com.lodev09.truesheet.core.RNScreensEventObserverDelegate
21
+ import com.lodev09.truesheet.core.TrueSheetDismissThreshold
22
22
  import com.lodev09.truesheet.core.TrueSheetStackManager
23
23
  import com.lodev09.truesheet.events.*
24
24
  import com.lodev09.truesheet.utils.KeyboardUtils
25
+ import com.lodev09.truesheet.utils.findRootContainerView
25
26
 
26
27
  /**
27
28
  * Main TrueSheet host view that manages the sheet and dispatches events to JavaScript.
@@ -101,13 +102,30 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
101
102
 
102
103
  override fun onAttachedToWindow() {
103
104
  super.onAttachedToWindow()
105
+ presentInitialIfNeeded()
106
+ }
104
107
 
105
- if (initialDetentIndex >= 0 && !didInitiallyPresent) {
106
- didInitiallyPresent = true
107
- if (initialDetentAnimated) {
108
- present(initialDetentIndex, true) { }
109
- } else {
110
- post { present(initialDetentIndex, false) { } }
108
+ /**
109
+ * JS holds initialDetentIndex back one commit so effect-driven content (e.g. a
110
+ * navigation footer set via setOptions) commits together with it. The prop then
111
+ * arrives after attach, so the ViewManager triggers this on prop update too.
112
+ * Posted so sibling mounts from the current transaction land before measuring.
113
+ */
114
+ fun presentInitialIfNeeded() {
115
+ if (initialDetentIndex < 0 || didInitiallyPresent || !isAttachedToWindow) return
116
+
117
+ didInitiallyPresent = true
118
+ post {
119
+ // The view may have been dropped (flag reset) or detached before this
120
+ // ran — roll back so a later attach retries instead of presenting a
121
+ // sheet with no live host.
122
+ if (!didInitiallyPresent) return@post
123
+ if (!isAttachedToWindow) {
124
+ didInitiallyPresent = false
125
+ return@post
126
+ }
127
+ if (!viewController.isPresented) {
128
+ present(initialDetentIndex, initialDetentAnimated) { }
111
129
  }
112
130
  }
113
131
  }
@@ -187,6 +205,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
187
205
  setupScrollable()
188
206
 
189
207
  if (viewController.isPresented) {
208
+ viewController.setupBackground()
190
209
  viewController.sheetView?.setupBackground()
191
210
  viewController.sheetView?.setupGrabber()
192
211
  viewController.sheetView?.updateGravity()
@@ -207,15 +226,15 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
207
226
  viewController.maxContentWidth = width
208
227
  }
209
228
 
210
- fun setAnchor(anchor: String?) {
211
- val value = TrueSheetAnchor.fromString(anchor)
212
- if (viewController.anchor == value) return
213
- viewController.anchor = value
229
+ fun setPlacement(placement: String?) {
230
+ val value = TrueSheetPlacement.fromString(placement)
231
+ if (viewController.placement == value) return
232
+ viewController.placement = value
214
233
  }
215
234
 
216
- fun setAnchorOffset(offset: Int) {
217
- if (viewController.anchorOffset == offset) return
218
- viewController.anchorOffset = offset
235
+ fun setPlacementOffset(offset: Int) {
236
+ if (viewController.placementOffset == offset) return
237
+ viewController.placementOffset = offset
219
238
  }
220
239
 
221
240
  fun setDimmed(dimmed: Boolean) {
@@ -243,9 +262,12 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
243
262
  viewController.sheetCornerRadius = radius
244
263
  }
245
264
 
265
+ fun setDetentBackgrounds(colors: List<Int?>) {
266
+ viewController.detentBackgrounds = colors
267
+ }
268
+
246
269
  fun setSheetBackgroundColor(color: Int?) {
247
- if (viewController.sheetBackgroundColor == color) return
248
- viewController.sheetBackgroundColor = color
270
+ viewController.backgroundColor = color
249
271
  }
250
272
 
251
273
  fun setDismissible(dismissible: Boolean) {
@@ -256,6 +278,10 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
256
278
  viewController.draggable = draggable
257
279
  }
258
280
 
281
+ fun setDismissThreshold(dismissThreshold: String?) {
282
+ viewController.dismissThreshold = TrueSheetDismissThreshold.fromString(dismissThreshold)
283
+ }
284
+
259
285
  fun setGrabber(grabber: Boolean) {
260
286
  viewController.grabber = grabber
261
287
  }
@@ -287,6 +313,12 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
287
313
  setupScrollable()
288
314
  }
289
315
 
316
+ fun setScrollableHandle(handle: Int) {
317
+ if (viewController.scrollableHandle == handle) return
318
+ viewController.scrollableHandle = handle
319
+ setupScrollable()
320
+ }
321
+
290
322
  fun setAbsoluteHeader(absolute: Boolean) {
291
323
  viewController.absoluteHeader = absolute
292
324
  }
@@ -300,14 +332,20 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
300
332
  viewController.positionFooter()
301
333
  }
302
334
 
335
+ fun setFooterAvoidsKeyboard(avoids: Boolean) {
336
+ viewController.footerAvoidsKeyboard = avoids
337
+ viewController.containerView?.footerAvoidsKeyboard = avoids
338
+ viewController.positionFooter()
339
+ }
340
+
303
341
  private fun setupScrollable() {
304
342
  viewController.containerView?.let {
305
- it.insetAdjustment = viewController.insetAdjustment
306
- it.scrollViewBottomInset = viewController.contentBottomInset
307
343
  it.scrollableOptions = viewController.scrollableOptions
308
- it.hasAutoDetent = viewController.detents.contains(-1.0)
344
+ it.scrollableHandle = viewController.scrollableHandle
345
+ it.scrollableBottomInset = viewController.contentBottomInset
309
346
  it.footerBottomInset = viewController.contentBottomInset
310
347
  it.absoluteFooter = viewController.absoluteFooter
348
+ it.footerAvoidsKeyboard = viewController.footerAvoidsKeyboard
311
349
  it.setupScrollable()
312
350
  }
313
351
 
@@ -354,18 +392,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
354
392
  lastContainerWidth = width
355
393
  lastContainerHeight = height
356
394
 
357
- val sw = stateWrapper ?: return
358
- val widthDp = width.toFloat().pxToDp()
359
- val heightDp = height.toFloat().pxToDp()
360
-
361
- // Synchronous update — commits and mounts within the same UI-thread frame
362
- if (TrueSheetStateUpdater.updateState(sw, widthDp, heightDp)) return
363
-
364
- // Fallback: async state update
365
- val newStateData = WritableNativeMap()
366
- newStateData.putDouble("containerWidth", widthDp.toDouble())
367
- newStateData.putDouble("containerHeight", heightDp.toDouble())
368
- sw.updateState(newStateData)
395
+ stateWrapper?.let { TrueSheetStateUpdater.updateContainerSize(it, width, height) }
369
396
  }
370
397
 
371
398
  // ==================== Sheet Actions ====================
@@ -385,11 +412,22 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
385
412
  val parentSheet = TrueSheetStackManager.getTopmostSheet()
386
413
  val isFocusedViewWithinSheet = parentSheet?.viewController?.isFocusedViewWithinSheet() == true
387
414
  val shouldDismissKeyboard = isFocusedViewWithinSheet || viewController.isDimmedAtDetentIndex(detentIndex)
388
- if (KeyboardUtils.isKeyboardVisible(reactContext) && shouldDismissKeyboard) {
415
+ if (shouldDismissKeyboard && KeyboardUtils.isKeyboardVisible(reactContext)) {
389
416
  viewController.saveFocusedView()
390
417
  KeyboardUtils.dismiss(this) {
391
- post { present(detentIndex, animated, promiseCallback) }
418
+ post { presentSheet(detentIndex, animated, promiseCallback) }
392
419
  }
420
+ // Blur like iOS so JS focus state stays in sync until restoreFocusedView() refocuses it
421
+ rootView.findFocus()?.clearFocus()
422
+ return
423
+ }
424
+
425
+ presentSheet(detentIndex, animated, promiseCallback)
426
+ }
427
+
428
+ private fun presentSheet(detentIndex: Int, animated: Boolean, promiseCallback: () -> Unit) {
429
+ if (viewController.isPresented) {
430
+ promiseCallback()
393
431
  return
394
432
  }
395
433
 
@@ -469,7 +507,13 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
469
507
  if (viewController.containerView == null) return@post
470
508
 
471
509
  viewController.setupSheetDetentsForSizeChange()
472
- TrueSheetStackManager.updateParentTranslation(this)
510
+ // While settling, onSlide drives the parent translation in realtime —
511
+ // an animated update here would race it frame-by-frame, staggering the
512
+ // emitted position. Keyboard-driven resizes leave the parent alone (like
513
+ // iOS); keyboardDidHide resizes again once the detents are keyboard-free.
514
+ if (!viewController.isSettling && !viewController.isKeyboardActive) {
515
+ TrueSheetStackManager.updateParentTranslation(this)
516
+ }
473
517
  }
474
518
  }
475
519
 
@@ -497,14 +541,46 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
497
541
  }
498
542
  }
499
543
 
544
+ /**
545
+ * Realtime counterpart of updateTranslationForChild, driven by the child's
546
+ * onSlide frames so stacked sheets move in lockstep instead of on
547
+ * independently-timed animations.
548
+ */
549
+ fun syncTranslationForChild(childSheetTop: Int, childMinSheetTop: Int) {
550
+ if (!viewController.isSheetVisible || viewController.isExpanded) return
551
+
552
+ val mySheetTop = viewController.detentCalculator.getSheetTopForDetentIndex(viewController.currentDetentIndex)
553
+ val targetTranslation = maxOf(0, childMinSheetTop - mySheetTop)
554
+ val actualTranslation = maxOf(0, childSheetTop - mySheetTop)
555
+ val currentTranslation = viewController.currentTranslationY
556
+
557
+ // Ride the child's actual position, but only between the current
558
+ // translation and the settled target — tracks the child frame-by-frame
559
+ // when the alignment changes (auto detent resize) while staying put when
560
+ // the child moves between detents above its minimum. Returning early on
561
+ // no-ops avoids cancelling an in-flight realign animation.
562
+ val newTranslation = actualTranslation.coerceIn(
563
+ minOf(currentTranslation, targetTranslation),
564
+ maxOf(currentTranslation, targetTranslation)
565
+ )
566
+ if (newTranslation == currentTranslation) return
567
+
568
+ viewController.translateSheet(newTranslation, animated = false)
569
+
570
+ val additionalTranslation = newTranslation - currentTranslation
571
+ if (additionalTranslation > 0) {
572
+ TrueSheetStackManager.getParentSheet(this)?.addTranslation(additionalTranslation, animated = false)
573
+ }
574
+ }
575
+
500
576
  /**
501
577
  * Recursively adds translation to this sheet and all parent sheets.
502
578
  */
503
- private fun addTranslation(amount: Int) {
579
+ private fun addTranslation(amount: Int, animated: Boolean = true) {
504
580
  if (viewController.isExpanded) return
505
581
 
506
- viewController.translateSheet(viewController.currentTranslationY + amount)
507
- TrueSheetStackManager.getParentSheet(this)?.addTranslation(amount)
582
+ viewController.translateSheet(viewController.currentTranslationY + amount, animated)
583
+ TrueSheetStackManager.getParentSheet(this)?.addTranslation(amount, animated)
508
584
  }
509
585
 
510
586
  /**
@@ -542,6 +618,11 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
542
618
  eventDispatcher?.dispatchEvent(WillDismissEvent(surfaceId, id))
543
619
  }
544
620
 
621
+ override fun viewControllerDidAttemptDismiss() {
622
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
623
+ eventDispatcher?.dispatchEvent(DismissAttemptEvent(surfaceId, id))
624
+ }
625
+
545
626
  override fun viewControllerDidDismiss(parent: TrueSheetView?) {
546
627
  // Detach coordinator from the root container view
547
628
  viewController.coordinatorLayout?.let { rootContainerView?.removeView(it) }
@@ -626,7 +707,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
626
707
  viewController.commitKeyboardDetent()
627
708
  }
628
709
 
629
- // When the ScrollView changes (e.g. conditional remount), re-pin the new ScrollView
710
+ // When the ScrollView changes (e.g. conditional remount), re-detect the new ScrollView
630
711
  // and request layout so BottomSheetBehavior re-discovers the nested scrolling child.
631
712
  override fun containerViewScrollViewDidChange() {
632
713
  setupScrollable()
@@ -643,6 +724,10 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
643
724
  viewController.positionFooter()
644
725
  }
645
726
 
727
+ override fun containerViewFooterDidLayout() {
728
+ viewController.positionFooter()
729
+ }
730
+
646
731
  override fun containerViewPeekDidChangeSize(width: Int, height: Int) {
647
732
  // Peek content height affects peek detents
648
733
  updateSheetIfNeeded()
@@ -672,16 +757,5 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
672
757
  * of whichever window this view is in - whether that's the activity's window or a
673
758
  * Modal's dialog window.
674
759
  */
675
- override fun findRootContainerView(): ViewGroup? {
676
- var current: android.view.ViewParent? = parent
677
-
678
- while (current != null) {
679
- if (current is ViewGroup && current.id == android.R.id.content) {
680
- return current
681
- }
682
- current = current.parent
683
- }
684
-
685
- return reactContext.currentActivity?.findViewById(android.R.id.content)
686
- }
760
+ override fun findRootContainerView(): ViewGroup? = findRootContainerView(reactContext)
687
761
  }