@lodev09/react-native-true-sheet 4.0.0-beta.1 → 4.0.0-beta.10

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 (221) 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 +24 -7
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +211 -182
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +14 -19
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +106 -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 +82 -39
  12. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +275 -88
  13. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +26 -1
  14. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +75 -3
  15. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +56 -34
  16. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +137 -0
  17. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
  18. package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +1 -1
  19. package/android/src/main/java/com/lodev09/truesheet/utils/TouchEventDeduper.kt +31 -0
  20. package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
  21. package/android/src/main/jni/TrueSheetSpec.h +1 -0
  22. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +0 -8
  23. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +54 -29
  24. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +6 -1
  25. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +1 -1
  26. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +6 -4
  27. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +7 -0
  28. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +46 -0
  29. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
  30. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
  31. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +36 -0
  32. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +4 -35
  33. package/ios/TrueSheetContainerView.h +14 -7
  34. package/ios/TrueSheetContainerView.mm +10 -7
  35. package/ios/TrueSheetContentView.h +31 -15
  36. package/ios/TrueSheetContentView.mm +133 -179
  37. package/ios/TrueSheetFooterView.h +6 -0
  38. package/ios/TrueSheetFooterView.mm +48 -2
  39. package/ios/TrueSheetModule.h +2 -7
  40. package/ios/TrueSheetModule.mm +7 -16
  41. package/ios/TrueSheetOverlayView.h +32 -0
  42. package/ios/TrueSheetOverlayView.mm +161 -0
  43. package/ios/TrueSheetView.mm +171 -29
  44. package/ios/TrueSheetViewController.h +10 -0
  45. package/ios/TrueSheetViewController.mm +256 -74
  46. package/ios/core/TrueSheetGrabberView.mm +13 -3
  47. package/ios/core/TrueSheetOverlayContainerView.h +31 -0
  48. package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
  49. package/ios/events/TrueSheetLifecycleEvents.h +2 -0
  50. package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
  51. package/ios/tvos/TrueSheetStubs.mm +12 -0
  52. package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
  53. package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
  54. package/lib/module/TrueSheet.js +149 -34
  55. package/lib/module/TrueSheet.js.map +1 -1
  56. package/lib/module/TrueSheet.web.js +122 -53
  57. package/lib/module/TrueSheet.web.js.map +1 -1
  58. package/lib/module/TrueSheetOverlay.js +30 -0
  59. package/lib/module/TrueSheetOverlay.js.map +1 -0
  60. package/lib/module/TrueSheetOverlay.web.js +35 -0
  61. package/lib/module/TrueSheetOverlay.web.js.map +1 -0
  62. package/lib/module/TrueSheetPeek.web.js +3 -3
  63. package/lib/module/TrueSheetPeek.web.js.map +1 -1
  64. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  65. package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  66. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +5 -0
  67. package/lib/module/index.js +1 -0
  68. package/lib/module/index.js.map +1 -1
  69. package/lib/module/mocks/index.js +10 -0
  70. package/lib/module/mocks/index.js.map +1 -1
  71. package/lib/module/mocks/navigation.js +31 -21
  72. package/lib/module/mocks/navigation.js.map +1 -1
  73. package/lib/module/mocks/navigationExpoRouter.js +21 -0
  74. package/lib/module/mocks/navigationExpoRouter.js.map +1 -0
  75. package/lib/module/mocks/reanimated.js +1 -1
  76. package/lib/module/mocks/reanimated.js.map +1 -1
  77. package/lib/module/navigation/{TrueSheetView.js → TrueSheetNavigatorContent.js} +14 -12
  78. package/lib/module/navigation/TrueSheetNavigatorContent.js.map +1 -0
  79. package/lib/module/navigation/actions.js +50 -0
  80. package/lib/module/navigation/actions.js.map +1 -0
  81. package/lib/module/navigation/base-types.js +2 -0
  82. package/lib/module/navigation/base-types.js.map +1 -0
  83. package/lib/module/navigation/{TrueSheetRouter.js → createTrueSheetRouter.js} +11 -24
  84. package/lib/module/navigation/createTrueSheetRouter.js.map +1 -0
  85. package/lib/module/navigation/expo-router/base-types.js +2 -0
  86. package/lib/module/navigation/expo-router/base-types.js.map +1 -0
  87. package/lib/module/navigation/expo-router/index.js +50 -0
  88. package/lib/module/navigation/expo-router/index.js.map +1 -0
  89. package/lib/module/navigation/expo-router/types.js +4 -0
  90. package/lib/module/navigation/expo-router/types.js.map +1 -0
  91. package/lib/module/navigation/index.js +43 -2
  92. package/lib/module/navigation/index.js.map +1 -1
  93. package/lib/module/navigation/navigator.js +10 -0
  94. package/lib/module/navigation/navigator.js.map +1 -0
  95. package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js +2 -2
  96. package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js.map +1 -1
  97. package/lib/module/navigation/screen/TrueSheetScreen.js +2 -2
  98. package/lib/module/navigation/screen/TrueSheetScreen.js.map +1 -1
  99. package/lib/module/navigation/screen/useSheetScreenState.js +5 -4
  100. package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
  101. package/lib/module/navigation/useTrueSheetNavigation.js +1 -1
  102. package/lib/module/navigation/useTrueSheetNavigation.js.map +1 -1
  103. package/lib/module/web/dom.js +10 -0
  104. package/lib/module/web/dom.js.map +1 -0
  105. package/lib/module/web/vaul/constants.js +4 -0
  106. package/lib/module/web/vaul/constants.js.map +1 -1
  107. package/lib/module/web/vaul/context.js.map +1 -1
  108. package/lib/module/web/vaul/helpers.js +10 -4
  109. package/lib/module/web/vaul/helpers.js.map +1 -1
  110. package/lib/module/web/vaul/index.js +35 -2
  111. package/lib/module/web/vaul/index.js.map +1 -1
  112. package/lib/typescript/src/TrueSheet.d.ts +6 -0
  113. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  114. package/lib/typescript/src/TrueSheet.types.d.ts +72 -1
  115. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  116. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  117. package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
  118. package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
  119. package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
  120. package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
  121. package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
  122. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
  123. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
  124. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +4 -0
  125. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  126. package/lib/typescript/src/index.d.ts +1 -0
  127. package/lib/typescript/src/index.d.ts.map +1 -1
  128. package/lib/typescript/src/mocks/index.d.ts +4 -0
  129. package/lib/typescript/src/mocks/index.d.ts.map +1 -1
  130. package/lib/typescript/src/mocks/navigation.d.ts +5 -1
  131. package/lib/typescript/src/mocks/navigation.d.ts.map +1 -1
  132. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +22 -0
  133. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -0
  134. package/lib/typescript/src/mocks/reanimated.d.ts.map +1 -1
  135. package/lib/typescript/src/navigation/TrueSheetNavigatorContent.d.ts +5 -0
  136. package/lib/typescript/src/navigation/TrueSheetNavigatorContent.d.ts.map +1 -0
  137. package/lib/typescript/src/navigation/actions.d.ts +71 -0
  138. package/lib/typescript/src/navigation/actions.d.ts.map +1 -0
  139. package/lib/typescript/src/navigation/base-types.d.ts +9 -0
  140. package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
  141. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +3 -0
  142. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -0
  143. package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
  144. package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
  145. package/lib/typescript/src/navigation/expo-router/index.d.ts +37 -0
  146. package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -0
  147. package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
  148. package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
  149. package/lib/typescript/src/navigation/index.d.ts +42 -3
  150. package/lib/typescript/src/navigation/index.d.ts.map +1 -1
  151. package/lib/typescript/src/navigation/navigator.d.ts +7 -0
  152. package/lib/typescript/src/navigation/navigator.d.ts.map +1 -0
  153. package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts +1 -1
  154. package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts.map +1 -1
  155. package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts +1 -1
  156. package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts.map +1 -1
  157. package/lib/typescript/src/navigation/screen/types.d.ts +3 -4
  158. package/lib/typescript/src/navigation/screen/types.d.ts.map +1 -1
  159. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +5 -6
  160. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
  161. package/lib/typescript/src/navigation/types.d.ts +65 -5
  162. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  163. package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts +1 -1
  164. package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts.map +1 -1
  165. package/lib/typescript/src/web/dom.d.ts +7 -0
  166. package/lib/typescript/src/web/dom.d.ts.map +1 -0
  167. package/lib/typescript/src/web/vaul/constants.d.ts +1 -0
  168. package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
  169. package/lib/typescript/src/web/vaul/context.d.ts +1 -0
  170. package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
  171. package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
  172. package/lib/typescript/src/web/vaul/index.d.ts +7 -1
  173. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
  174. package/package.json +32 -9
  175. package/react-native.config.js +1 -0
  176. package/src/TrueSheet.tsx +184 -46
  177. package/src/TrueSheet.types.ts +77 -1
  178. package/src/TrueSheet.web.tsx +135 -64
  179. package/src/TrueSheetOverlay.tsx +24 -0
  180. package/src/TrueSheetOverlay.web.tsx +33 -0
  181. package/src/TrueSheetPeek.web.tsx +4 -3
  182. package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  183. package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  184. package/src/fabric/TrueSheetViewNativeComponent.ts +5 -0
  185. package/src/index.ts +1 -0
  186. package/src/mocks/index.ts +7 -0
  187. package/src/mocks/navigation.ts +32 -25
  188. package/src/mocks/navigationExpoRouter.ts +41 -0
  189. package/src/mocks/reanimated.ts +8 -9
  190. package/src/navigation/{TrueSheetView.tsx → TrueSheetNavigatorContent.tsx} +24 -17
  191. package/src/navigation/actions.ts +78 -0
  192. package/src/navigation/base-types.ts +20 -0
  193. package/src/navigation/createTrueSheetRouter.ts +194 -0
  194. package/src/navigation/expo-router/base-types.ts +18 -0
  195. package/src/navigation/expo-router/index.ts +90 -0
  196. package/src/navigation/expo-router/types.ts +116 -0
  197. package/src/navigation/index.ts +68 -2
  198. package/src/navigation/navigator.ts +18 -0
  199. package/src/navigation/screen/ReanimatedTrueSheetScreen.tsx +2 -2
  200. package/src/navigation/screen/TrueSheetScreen.tsx +2 -2
  201. package/src/navigation/screen/types.ts +4 -6
  202. package/src/navigation/screen/useSheetScreenState.ts +10 -15
  203. package/src/navigation/types.ts +99 -16
  204. package/src/navigation/useTrueSheetNavigation.ts +1 -1
  205. package/src/web/dom.ts +9 -0
  206. package/src/web/vaul/constants.ts +4 -0
  207. package/src/web/vaul/context.ts +1 -0
  208. package/src/web/vaul/helpers.ts +13 -6
  209. package/src/web/vaul/index.tsx +46 -1
  210. package/lib/module/navigation/TrueSheetRouter.js.map +0 -1
  211. package/lib/module/navigation/TrueSheetView.js.map +0 -1
  212. package/lib/module/navigation/createTrueSheetNavigator.js +0 -63
  213. package/lib/module/navigation/createTrueSheetNavigator.js.map +0 -1
  214. package/lib/typescript/src/navigation/TrueSheetRouter.d.ts +0 -57
  215. package/lib/typescript/src/navigation/TrueSheetRouter.d.ts.map +0 -1
  216. package/lib/typescript/src/navigation/TrueSheetView.d.ts +0 -10
  217. package/lib/typescript/src/navigation/TrueSheetView.d.ts.map +0 -1
  218. package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts +0 -35
  219. package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts.map +0 -1
  220. package/src/navigation/TrueSheetRouter.ts +0 -234
  221. package/src/navigation/createTrueSheetNavigator.tsx +0 -93
@@ -0,0 +1,106 @@
1
+ package com.lodev09.truesheet
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.view.View
5
+ import android.view.ViewGroup
6
+ import android.view.accessibility.AccessibilityEvent
7
+ import com.facebook.react.uimanager.StateWrapper
8
+ import com.facebook.react.uimanager.ThemedReactContext
9
+ import com.facebook.react.uimanager.events.EventDispatcher
10
+ import com.facebook.react.views.view.ReactViewGroup
11
+ import com.lodev09.truesheet.core.TrueSheetOverlayRootView
12
+ import com.lodev09.truesheet.core.TrueSheetOverlayRootViewDelegate
13
+ import com.lodev09.truesheet.utils.findRootContainerView
14
+
15
+ /**
16
+ * Hidden host that renders its children in a root view attached to the
17
+ * activity's content view, above every presented sheet.
18
+ */
19
+ @SuppressLint("ViewConstructor")
20
+ class TrueSheetOverlayView(private val reactContext: ThemedReactContext) :
21
+ ReactViewGroup(reactContext),
22
+ TrueSheetOverlayRootViewDelegate {
23
+
24
+ private val rootView = TrueSheetOverlayRootView(reactContext).apply {
25
+ delegate = this@TrueSheetOverlayView
26
+ }
27
+
28
+ private var rootContainerView: ViewGroup? = null
29
+
30
+ override var eventDispatcher: EventDispatcher? = null
31
+
32
+ private var lastWidth = 0
33
+ private var lastHeight = 0
34
+
35
+ var stateWrapper: StateWrapper? = null
36
+ set(value) {
37
+ field = value
38
+ pushState()
39
+ }
40
+
41
+ init {
42
+ // The host stays in the React tree for layout only — children render in the root view
43
+ visibility = GONE
44
+ }
45
+
46
+ // ==================== View Hierarchy Management ====================
47
+
48
+ override fun onAttachedToWindow() {
49
+ super.onAttachedToWindow()
50
+ attachRootView()
51
+ }
52
+
53
+ override fun onDetachedFromWindow() {
54
+ detachRootView()
55
+ super.onDetachedFromWindow()
56
+ }
57
+
58
+ private fun attachRootView() {
59
+ if (rootView.parent != null) return
60
+
61
+ val container = findRootContainerView(reactContext) ?: return
62
+ container.addView(rootView)
63
+ rootContainerView = container
64
+ }
65
+
66
+ private fun detachRootView() {
67
+ rootContainerView?.removeView(rootView)
68
+ rootContainerView = null
69
+ }
70
+
71
+ fun onDropInstance() {
72
+ detachRootView()
73
+ rootView.delegate = null
74
+ }
75
+
76
+ override fun addView(child: View?, index: Int) {
77
+ rootView.addView(child, index)
78
+ }
79
+
80
+ override fun getChildCount(): Int = rootView.childCount
81
+
82
+ override fun getChildAt(index: Int): View? = rootView.getChildAt(index)
83
+
84
+ override fun removeViewAt(index: Int) {
85
+ rootView.removeViewAt(index)
86
+ }
87
+
88
+ // Accessibility: children live in the root view since this view is hidden
89
+ override fun addChildrenForAccessibility(outChildren: ArrayList<View>) {}
90
+ override fun dispatchPopulateAccessibilityEvent(event: AccessibilityEvent): Boolean = false
91
+
92
+ // ==================== State ====================
93
+
94
+ override fun overlayRootViewDidChangeSize(width: Int, height: Int) {
95
+ if (width == lastWidth && height == lastHeight) return
96
+
97
+ lastWidth = width
98
+ lastHeight = height
99
+ pushState()
100
+ }
101
+
102
+ private fun pushState() {
103
+ if (lastWidth == 0 && lastHeight == 0) return
104
+ stateWrapper?.let { TrueSheetStateUpdater.updateContainerSize(it, lastWidth, lastHeight) }
105
+ }
106
+ }
@@ -0,0 +1,45 @@
1
+ package com.lodev09.truesheet
2
+
3
+ import com.facebook.react.module.annotations.ReactModule
4
+ import com.facebook.react.uimanager.PointerEvents
5
+ import com.facebook.react.uimanager.ReactStylesDiffMap
6
+ import com.facebook.react.uimanager.StateWrapper
7
+ import com.facebook.react.uimanager.ThemedReactContext
8
+ import com.facebook.react.uimanager.UIManagerHelper
9
+ import com.facebook.react.uimanager.ViewGroupManager
10
+ import com.facebook.react.uimanager.annotations.ReactProp
11
+
12
+ /**
13
+ * ViewManager for TrueSheetOverlayView
14
+ * Renders content above every presented sheet
15
+ */
16
+ @ReactModule(name = TrueSheetOverlayViewManager.REACT_CLASS)
17
+ class TrueSheetOverlayViewManager : ViewGroupManager<TrueSheetOverlayView>() {
18
+
19
+ override fun getName(): String = REACT_CLASS
20
+
21
+ override fun createViewInstance(reactContext: ThemedReactContext): TrueSheetOverlayView = TrueSheetOverlayView(reactContext)
22
+
23
+ override fun onDropViewInstance(view: TrueSheetOverlayView) {
24
+ super.onDropViewInstance(view)
25
+ view.onDropInstance()
26
+ }
27
+
28
+ override fun addEventEmitters(reactContext: ThemedReactContext, view: TrueSheetOverlayView) {
29
+ view.eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id)
30
+ }
31
+
32
+ override fun updateState(view: TrueSheetOverlayView, props: ReactStylesDiffMap?, stateWrapper: StateWrapper?): Any? {
33
+ view.stateWrapper = stateWrapper
34
+ return null
35
+ }
36
+
37
+ @ReactProp(name = "pointerEvents")
38
+ fun setPointerEvents(view: TrueSheetOverlayView, pointerEventsStr: String?) {
39
+ view.pointerEvents = PointerEvents.parsePointerEvents(pointerEventsStr)
40
+ }
41
+
42
+ companion object {
43
+ const val REACT_CLASS = "TrueSheetOverlayView"
44
+ }
45
+ }
@@ -40,6 +40,7 @@ class TrueSheetPackage : BaseReactPackage() {
40
40
  TrueSheetContentViewManager(),
41
41
  TrueSheetHeaderViewManager(),
42
42
  TrueSheetFooterViewManager(),
43
- TrueSheetPeekViewManager()
43
+ TrueSheetPeekViewManager(),
44
+ TrueSheetOverlayViewManager()
44
45
  )
45
46
  }
@@ -1,5 +1,7 @@
1
1
  package com.lodev09.truesheet
2
2
 
3
+ import com.facebook.react.bridge.WritableNativeMap
4
+ import com.facebook.react.uimanager.PixelUtil.pxToDp
3
5
  import com.facebook.react.uimanager.StateWrapper
4
6
 
5
7
  /**
@@ -22,6 +24,22 @@ object TrueSheetStateUpdater {
22
24
  fun updateState(stateWrapper: StateWrapper, widthDp: Float, heightDp: Float): Boolean =
23
25
  isAvailable && nativeUpdateState(stateWrapper, widthDp, heightDp)
24
26
 
27
+ /**
28
+ * Pushes the container size (px) into the Fabric state — synchronously when
29
+ * the bridge is available, async otherwise.
30
+ */
31
+ fun updateContainerSize(stateWrapper: StateWrapper, widthPx: Int, heightPx: Int) {
32
+ val widthDp = widthPx.toFloat().pxToDp()
33
+ val heightDp = heightPx.toFloat().pxToDp()
34
+
35
+ if (updateState(stateWrapper, widthDp, heightDp)) return
36
+
37
+ val newStateData = WritableNativeMap()
38
+ newStateData.putDouble("containerWidth", widthDp.toDouble())
39
+ newStateData.putDouble("containerHeight", heightDp.toDouble())
40
+ stateWrapper.updateState(newStateData)
41
+ }
42
+
25
43
  /** Returns false when unavailable — caller should fall back to async updateState. */
26
44
  fun updateFooterState(stateWrapper: StateWrapper, bottomInsetDp: Float): Boolean =
27
45
  isAvailable && nativeUpdateFooterState(stateWrapper, bottomInsetDp)
@@ -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
@@ -22,6 +21,7 @@ import com.lodev09.truesheet.core.RNScreensEventObserverDelegate
22
21
  import com.lodev09.truesheet.core.TrueSheetStackManager
23
22
  import com.lodev09.truesheet.events.*
24
23
  import com.lodev09.truesheet.utils.KeyboardUtils
24
+ import com.lodev09.truesheet.utils.findRootContainerView
25
25
 
26
26
  /**
27
27
  * Main TrueSheet host view that manages the sheet and dispatches events to JavaScript.
@@ -101,13 +101,30 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
101
101
 
102
102
  override fun onAttachedToWindow() {
103
103
  super.onAttachedToWindow()
104
+ presentInitialIfNeeded()
105
+ }
104
106
 
105
- if (initialDetentIndex >= 0 && !didInitiallyPresent) {
106
- didInitiallyPresent = true
107
- if (initialDetentAnimated) {
108
- present(initialDetentIndex, true) { }
109
- } else {
110
- post { present(initialDetentIndex, false) { } }
107
+ /**
108
+ * JS holds initialDetentIndex back one commit so effect-driven content (e.g. a
109
+ * navigation footer set via setOptions) commits together with it. The prop then
110
+ * arrives after attach, so the ViewManager triggers this on prop update too.
111
+ * Posted so sibling mounts from the current transaction land before measuring.
112
+ */
113
+ fun presentInitialIfNeeded() {
114
+ if (initialDetentIndex < 0 || didInitiallyPresent || !isAttachedToWindow) return
115
+
116
+ didInitiallyPresent = true
117
+ post {
118
+ // The view may have been dropped (flag reset) or detached before this
119
+ // ran — roll back so a later attach retries instead of presenting a
120
+ // sheet with no live host.
121
+ if (!didInitiallyPresent) return@post
122
+ if (!isAttachedToWindow) {
123
+ didInitiallyPresent = false
124
+ return@post
125
+ }
126
+ if (!viewController.isPresented) {
127
+ present(initialDetentIndex, initialDetentAnimated) { }
111
128
  }
112
129
  }
113
130
  }
@@ -287,6 +304,12 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
287
304
  setupScrollable()
288
305
  }
289
306
 
307
+ fun setScrollableHandle(handle: Int) {
308
+ if (viewController.scrollableHandle == handle) return
309
+ viewController.scrollableHandle = handle
310
+ setupScrollable()
311
+ }
312
+
290
313
  fun setAbsoluteHeader(absolute: Boolean) {
291
314
  viewController.absoluteHeader = absolute
292
315
  }
@@ -302,10 +325,9 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
302
325
 
303
326
  private fun setupScrollable() {
304
327
  viewController.containerView?.let {
305
- it.insetAdjustment = viewController.insetAdjustment
306
- it.scrollViewBottomInset = viewController.contentBottomInset
307
328
  it.scrollableOptions = viewController.scrollableOptions
308
- it.hasAutoDetent = viewController.detents.contains(-1.0)
329
+ it.scrollableHandle = viewController.scrollableHandle
330
+ it.scrollableBottomInset = viewController.contentBottomInset
309
331
  it.footerBottomInset = viewController.contentBottomInset
310
332
  it.absoluteFooter = viewController.absoluteFooter
311
333
  it.setupScrollable()
@@ -354,18 +376,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
354
376
  lastContainerWidth = width
355
377
  lastContainerHeight = height
356
378
 
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)
379
+ stateWrapper?.let { TrueSheetStateUpdater.updateContainerSize(it, width, height) }
369
380
  }
370
381
 
371
382
  // ==================== Sheet Actions ====================
@@ -469,7 +480,13 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
469
480
  if (viewController.containerView == null) return@post
470
481
 
471
482
  viewController.setupSheetDetentsForSizeChange()
472
- TrueSheetStackManager.updateParentTranslation(this)
483
+ // While settling, onSlide drives the parent translation in realtime —
484
+ // an animated update here would race it frame-by-frame, staggering the
485
+ // emitted position. Keyboard-driven resizes leave the parent alone (like
486
+ // iOS); keyboardDidHide resizes again once the detents are keyboard-free.
487
+ if (!viewController.isSettling && !viewController.isKeyboardActive) {
488
+ TrueSheetStackManager.updateParentTranslation(this)
489
+ }
473
490
  }
474
491
  }
475
492
 
@@ -497,14 +514,46 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
497
514
  }
498
515
  }
499
516
 
517
+ /**
518
+ * Realtime counterpart of updateTranslationForChild, driven by the child's
519
+ * onSlide frames so stacked sheets move in lockstep instead of on
520
+ * independently-timed animations.
521
+ */
522
+ fun syncTranslationForChild(childSheetTop: Int, childMinSheetTop: Int) {
523
+ if (!viewController.isSheetVisible || viewController.isExpanded) return
524
+
525
+ val mySheetTop = viewController.detentCalculator.getSheetTopForDetentIndex(viewController.currentDetentIndex)
526
+ val targetTranslation = maxOf(0, childMinSheetTop - mySheetTop)
527
+ val actualTranslation = maxOf(0, childSheetTop - mySheetTop)
528
+ val currentTranslation = viewController.currentTranslationY
529
+
530
+ // Ride the child's actual position, but only between the current
531
+ // translation and the settled target — tracks the child frame-by-frame
532
+ // when the alignment changes (auto detent resize) while staying put when
533
+ // the child moves between detents above its minimum. Returning early on
534
+ // no-ops avoids cancelling an in-flight realign animation.
535
+ val newTranslation = actualTranslation.coerceIn(
536
+ minOf(currentTranslation, targetTranslation),
537
+ maxOf(currentTranslation, targetTranslation)
538
+ )
539
+ if (newTranslation == currentTranslation) return
540
+
541
+ viewController.translateSheet(newTranslation, animated = false)
542
+
543
+ val additionalTranslation = newTranslation - currentTranslation
544
+ if (additionalTranslation > 0) {
545
+ TrueSheetStackManager.getParentSheet(this)?.addTranslation(additionalTranslation, animated = false)
546
+ }
547
+ }
548
+
500
549
  /**
501
550
  * Recursively adds translation to this sheet and all parent sheets.
502
551
  */
503
- private fun addTranslation(amount: Int) {
552
+ private fun addTranslation(amount: Int, animated: Boolean = true) {
504
553
  if (viewController.isExpanded) return
505
554
 
506
- viewController.translateSheet(viewController.currentTranslationY + amount)
507
- TrueSheetStackManager.getParentSheet(this)?.addTranslation(amount)
555
+ viewController.translateSheet(viewController.currentTranslationY + amount, animated)
556
+ TrueSheetStackManager.getParentSheet(this)?.addTranslation(amount, animated)
508
557
  }
509
558
 
510
559
  /**
@@ -542,6 +591,11 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
542
591
  eventDispatcher?.dispatchEvent(WillDismissEvent(surfaceId, id))
543
592
  }
544
593
 
594
+ override fun viewControllerDidAttemptDismiss() {
595
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
596
+ eventDispatcher?.dispatchEvent(DismissAttemptEvent(surfaceId, id))
597
+ }
598
+
545
599
  override fun viewControllerDidDismiss(parent: TrueSheetView?) {
546
600
  // Detach coordinator from the root container view
547
601
  viewController.coordinatorLayout?.let { rootContainerView?.removeView(it) }
@@ -626,7 +680,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
626
680
  viewController.commitKeyboardDetent()
627
681
  }
628
682
 
629
- // When the ScrollView changes (e.g. conditional remount), re-pin the new ScrollView
683
+ // When the ScrollView changes (e.g. conditional remount), re-detect the new ScrollView
630
684
  // and request layout so BottomSheetBehavior re-discovers the nested scrolling child.
631
685
  override fun containerViewScrollViewDidChange() {
632
686
  setupScrollable()
@@ -672,16 +726,5 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
672
726
  * of whichever window this view is in - whether that's the activity's window or a
673
727
  * Modal's dialog window.
674
728
  */
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
- }
729
+ override fun findRootContainerView(): ViewGroup? = findRootContainerView(reactContext)
687
730
  }