@lodev09/react-native-true-sheet 3.0.0-beta.6 → 3.0.0-beta.8

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 (50) hide show
  1. package/README.md +1 -0
  2. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +51 -99
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerViewManager.kt +0 -7
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +10 -18
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +76 -20
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetHeaderView.kt +38 -0
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetHeaderViewManager.kt +21 -0
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +1 -0
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +106 -136
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +305 -419
  11. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +9 -4
  12. package/android/src/main/java/com/lodev09/truesheet/core/RNScreensFragmentObserver.kt +116 -0
  13. package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +33 -5
  14. package/android/src/main/jni/TrueSheetSpec.h +1 -1
  15. package/common/cpp/react/renderer/components/TrueSheetSpec/{TrueSheetContainerViewComponentDescriptor.h → TrueSheetViewComponentDescriptor.h} +5 -5
  16. package/common/cpp/react/renderer/components/TrueSheetSpec/{TrueSheetContainerViewShadowNode.cpp → TrueSheetViewShadowNode.cpp} +4 -4
  17. package/common/cpp/react/renderer/components/TrueSheetSpec/{TrueSheetContainerViewShadowNode.h → TrueSheetViewShadowNode.h} +8 -8
  18. package/common/cpp/react/renderer/components/TrueSheetSpec/{TrueSheetContainerViewState.cpp → TrueSheetViewState.cpp} +2 -2
  19. package/common/cpp/react/renderer/components/TrueSheetSpec/{TrueSheetContainerViewState.h → TrueSheetViewState.h} +6 -6
  20. package/ios/TrueSheetContainerView.h +20 -2
  21. package/ios/TrueSheetContainerView.mm +62 -62
  22. package/ios/TrueSheetContentView.h +4 -2
  23. package/ios/TrueSheetContentView.mm +29 -72
  24. package/ios/TrueSheetFooterView.mm +2 -2
  25. package/ios/TrueSheetHeaderView.h +29 -0
  26. package/ios/TrueSheetHeaderView.mm +60 -0
  27. package/ios/TrueSheetView.mm +195 -214
  28. package/ios/TrueSheetViewController.h +2 -2
  29. package/ios/TrueSheetViewController.mm +126 -231
  30. package/ios/utils/LayoutUtil.h +2 -1
  31. package/ios/utils/LayoutUtil.mm +14 -1
  32. package/lib/module/TrueSheet.js +9 -2
  33. package/lib/module/TrueSheet.js.map +1 -1
  34. package/lib/module/fabric/TrueSheetContainerViewNativeComponent.ts +1 -3
  35. package/lib/module/fabric/TrueSheetHeaderViewNativeComponent.ts +8 -0
  36. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +3 -1
  37. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  38. package/lib/typescript/src/TrueSheet.types.d.ts +9 -9
  39. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  40. package/lib/typescript/src/fabric/TrueSheetContainerViewNativeComponent.d.ts.map +1 -1
  41. package/lib/typescript/src/fabric/TrueSheetHeaderViewNativeComponent.d.ts +6 -0
  42. package/lib/typescript/src/fabric/TrueSheetHeaderViewNativeComponent.d.ts.map +1 -0
  43. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  44. package/package.json +6 -3
  45. package/react-native.config.js +1 -1
  46. package/src/TrueSheet.tsx +9 -0
  47. package/src/TrueSheet.types.ts +10 -11
  48. package/src/fabric/TrueSheetContainerViewNativeComponent.ts +1 -3
  49. package/src/fabric/TrueSheetHeaderViewNativeComponent.ts +8 -0
  50. package/src/fabric/TrueSheetViewNativeComponent.ts +3 -1
package/README.md CHANGED
@@ -13,6 +13,7 @@ The true native bottom sheet experience for your React Native Apps. 💩
13
13
  * 🎯 **Type-safe** - Full TypeScript support with Codegen-generated native interfaces
14
14
  * 🚀 **Blazing fast** - Direct C++ communication, no bridge overhead
15
15
  * 🎨 **Native** - Implemented in the native realm
16
+ * ♿ **Accessible** - Native accessibility and screen reader support out of the box
16
17
  * 🔄 **Imperative API** - Asynchronus `ref` [methods](https://sheet.lodev09.com/reference/methods#ref-methods)
17
18
 
18
19
  ## Installation
@@ -3,82 +3,28 @@ package com.lodev09.truesheet
3
3
  import android.annotation.SuppressLint
4
4
  import android.view.View
5
5
  import androidx.core.view.isNotEmpty
6
- import com.facebook.react.bridge.WritableNativeMap
7
- import com.facebook.react.uimanager.PixelUtil.pxToDp
8
- import com.facebook.react.uimanager.StateWrapper
9
6
  import com.facebook.react.uimanager.ThemedReactContext
10
7
  import com.facebook.react.views.view.ReactViewGroup
11
8
 
12
- /**
13
- * Delegate interface for container view changes
14
- */
15
9
  interface TrueSheetContainerViewDelegate {
16
10
  fun containerViewContentDidChangeSize(width: Int, height: Int)
11
+ fun containerViewHeaderDidChangeSize(width: Int, height: Int)
17
12
  fun containerViewFooterDidChangeSize(width: Int, height: Int)
18
13
  }
19
14
 
20
15
  /**
21
- * Container view that manages the bottom sheet content and holds content and footer views.
22
- * Simplified to be a lightweight content manager - events are now handled via dialog delegate.
16
+ * Container view that manages the sheet's content, header, and footer views.
17
+ * Size changes are forwarded to the delegate for sheet reconfiguration.
23
18
  */
24
19
  @SuppressLint("ViewConstructor")
25
- class TrueSheetContainerView(private val reactContext: ThemedReactContext) :
20
+ class TrueSheetContainerView(reactContext: ThemedReactContext) :
26
21
  ReactViewGroup(reactContext),
27
22
  TrueSheetContentViewDelegate,
23
+ TrueSheetHeaderViewDelegate,
28
24
  TrueSheetFooterViewDelegate {
29
25
 
30
26
  var delegate: TrueSheetContainerViewDelegate? = null
31
27
 
32
- private var stateWrapper: StateWrapper? = null
33
-
34
- // Pending dimensions to update when stateWrapper becomes available
35
- private var pendingWidth: Int = 0
36
- private var pendingHeight: Int = 0
37
-
38
- fun setStateWrapper(wrapper: StateWrapper?) {
39
- stateWrapper = wrapper
40
-
41
- if (wrapper == null) return
42
-
43
- // Get dimensions from parent controller and update state if we haven't yet
44
- val controller = parent as? TrueSheetViewController
45
- if (controller != null && pendingWidth == 0) {
46
- val w = controller.width
47
- val h = controller.height
48
- if (w > 0 && h > 0) {
49
- updateState(w, h)
50
- }
51
- }
52
- }
53
-
54
- /**
55
- * Update state with container dimensions.
56
- * Called by the controller when the dialog size changes.
57
- */
58
- fun updateState(width: Int, height: Int) {
59
- // Skip if dimensions haven't changed
60
- if (width == pendingWidth && height == pendingHeight && stateWrapper != null) {
61
- return
62
- }
63
-
64
- // Store dimensions
65
- pendingWidth = width
66
- pendingHeight = height
67
-
68
- val sw = stateWrapper ?: return
69
-
70
- val realWidth = width.toFloat().pxToDp()
71
- val realHeight = height.toFloat().pxToDp()
72
-
73
- val newStateData = WritableNativeMap()
74
- newStateData.putDouble("containerWidth", realWidth.toDouble())
75
- newStateData.putDouble("containerHeight", realHeight.toDouble())
76
- sw.updateState(newStateData)
77
- }
78
-
79
- /**
80
- * Reference to content view (first child)
81
- */
82
28
  val contentView: TrueSheetContentView?
83
29
  get() = if (isNotEmpty() && getChildAt(0) is TrueSheetContentView) {
84
30
  getChildAt(0) as TrueSheetContentView
@@ -86,30 +32,30 @@ class TrueSheetContainerView(private val reactContext: ThemedReactContext) :
86
32
  null
87
33
  }
88
34
 
89
- /**
90
- * Reference to footer view (second child)
91
- */
92
- val footerView: TrueSheetFooterView?
93
- get() = if (childCount > 1 && getChildAt(1) is TrueSheetFooterView) {
94
- getChildAt(1) as TrueSheetFooterView
95
- } else {
96
- null
35
+ val headerView: TrueSheetHeaderView?
36
+ get() {
37
+ for (i in 0 until childCount) {
38
+ val child = getChildAt(i)
39
+ if (child is TrueSheetHeaderView) return child
40
+ }
41
+ return null
97
42
  }
98
43
 
99
- /**
100
- * The content view height
101
- */
102
- val contentHeight: Int
103
- get() = contentView?.height ?: 0
44
+ val footerView: TrueSheetFooterView?
45
+ get() {
46
+ for (i in 0 until childCount) {
47
+ val child = getChildAt(i)
48
+ if (child is TrueSheetFooterView) return child
49
+ }
50
+ return null
51
+ }
104
52
 
105
- /**
106
- * The footer view height
107
- */
108
- val footerHeight: Int
109
- get() = footerView?.height ?: 0
53
+ var contentHeight: Int = 0
54
+ var headerHeight: Int = 0
55
+ var footerHeight: Int = 0
110
56
 
111
57
  init {
112
- // Container should not clip children to allow footer to position absolutely
58
+ // Allow footer to position outside container bounds
113
59
  clipChildren = false
114
60
  clipToPadding = false
115
61
  }
@@ -117,42 +63,48 @@ class TrueSheetContainerView(private val reactContext: ThemedReactContext) :
117
63
  override fun addView(child: View?, index: Int) {
118
64
  super.addView(child, index)
119
65
 
120
- // Set up delegate when content view is added
121
- if (child is TrueSheetContentView) {
122
- child.delegate = this
123
- }
124
-
125
- // Set up delegate when footer view is added
126
- if (child is TrueSheetFooterView) {
127
- child.delegate = this
66
+ when (child) {
67
+ is TrueSheetContentView -> child.delegate = this
68
+ is TrueSheetHeaderView -> child.delegate = this
69
+ is TrueSheetFooterView -> child.delegate = this
128
70
  }
129
71
  }
130
72
 
131
- override fun removeView(view: View?) {
132
- // Clean up delegate when content view is removed
133
- if (view is TrueSheetContentView) {
134
- view.delegate = null
135
- }
73
+ override fun removeViewAt(index: Int) {
74
+ val view = getChildAt(index)
75
+
76
+ when (view) {
77
+ is TrueSheetContentView -> view.delegate = null
78
+
79
+ is TrueSheetHeaderView -> {
80
+ view.delegate = null
81
+ headerViewDidChangeSize(0, 0)
82
+ }
136
83
 
137
- // Clean up delegate when footer view is removed
138
- if (view is TrueSheetFooterView) {
139
- view.delegate = null
84
+ is TrueSheetFooterView -> view.delegate = null
140
85
  }
141
86
 
142
- super.removeView(view)
87
+ super.removeViewAt(index)
143
88
  }
144
89
 
145
- // ==================== TrueSheetContentViewDelegate Implementation ====================
90
+ // ==================== Delegate Implementations ====================
146
91
 
147
92
  override fun contentViewDidChangeSize(width: Int, height: Int) {
148
- // Forward content size changes to controller for sheet resizing
93
+ contentHeight = height
149
94
  delegate?.containerViewContentDidChangeSize(width, height)
150
95
  }
151
96
 
152
- // ==================== TrueSheetFooterViewDelegate Implementation ====================
97
+ override fun headerViewDidChangeSize(width: Int, height: Int) {
98
+ headerHeight = height
99
+ delegate?.containerViewHeaderDidChangeSize(width, height)
100
+ }
153
101
 
154
102
  override fun footerViewDidChangeSize(width: Int, height: Int) {
155
- // Forward footer size changes to host view for repositioning
103
+ footerHeight = height
156
104
  delegate?.containerViewFooterDidChangeSize(width, height)
157
105
  }
106
+
107
+ companion object {
108
+ const val TAG_NAME = "TrueSheet"
109
+ }
158
110
  }
@@ -1,8 +1,6 @@
1
1
  package com.lodev09.truesheet
2
2
 
3
3
  import com.facebook.react.module.annotations.ReactModule
4
- import com.facebook.react.uimanager.ReactStylesDiffMap
5
- import com.facebook.react.uimanager.StateWrapper
6
4
  import com.facebook.react.uimanager.ThemedReactContext
7
5
  import com.facebook.react.uimanager.ViewGroupManager
8
6
 
@@ -17,11 +15,6 @@ class TrueSheetContainerViewManager : ViewGroupManager<TrueSheetContainerView>()
17
15
 
18
16
  override fun createViewInstance(reactContext: ThemedReactContext): TrueSheetContainerView = TrueSheetContainerView(reactContext)
19
17
 
20
- override fun updateState(view: TrueSheetContainerView, props: ReactStylesDiffMap?, stateWrapper: StateWrapper?): Any? {
21
- view.setStateWrapper(stateWrapper)
22
- return null
23
- }
24
-
25
18
  companion object {
26
19
  const val REACT_CLASS = "TrueSheetContainerView"
27
20
  }
@@ -22,25 +22,17 @@ class TrueSheetContentView(context: ThemedReactContext) : ReactViewGroup(context
22
22
  private var lastWidth = 0
23
23
  private var lastHeight = 0
24
24
 
25
- override fun onLayout(
26
- changed: Boolean,
27
- left: Int,
28
- top: Int,
29
- right: Int,
30
- bottom: Int
31
- ) {
32
- super.onLayout(changed, left, top, right, bottom)
25
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
26
+ super.onSizeChanged(w, h, oldw, oldh)
33
27
 
34
- // Notify delegate when content size changes
35
- val newWidth = right - left
36
- val newHeight = bottom - top
37
-
38
- if (newWidth != lastWidth || newHeight != lastHeight) {
39
- lastWidth = newWidth
40
- lastHeight = newHeight
41
-
42
- // Notify delegate of size change
43
- delegate?.contentViewDidChangeSize(newWidth, newHeight)
28
+ if (w != lastWidth || h != lastHeight) {
29
+ lastWidth = w
30
+ lastHeight = h
31
+ delegate?.contentViewDidChangeSize(w, h)
44
32
  }
45
33
  }
34
+
35
+ companion object {
36
+ const val TAG_NAME = "TrueSheet"
37
+ }
46
38
  }
@@ -1,7 +1,13 @@
1
1
  package com.lodev09.truesheet
2
2
 
3
3
  import android.annotation.SuppressLint
4
+ import android.view.MotionEvent
5
+ import android.view.View
6
+ import com.facebook.react.uimanager.JSPointerDispatcher
7
+ import com.facebook.react.uimanager.JSTouchDispatcher
8
+ import com.facebook.react.uimanager.RootView
4
9
  import com.facebook.react.uimanager.ThemedReactContext
10
+ import com.facebook.react.uimanager.events.EventDispatcher
5
11
  import com.facebook.react.views.view.ReactViewGroup
6
12
 
7
13
  /**
@@ -15,33 +21,83 @@ interface TrueSheetFooterViewDelegate {
15
21
  * Footer view that holds the footer content
16
22
  * This is the second child of TrueSheetContainerView
17
23
  * Positioned absolutely at the bottom of the sheet
24
+ *
25
+ * Implements RootView to handle touch events when positioned outside parent bounds.
18
26
  */
19
27
  @SuppressLint("ViewConstructor")
20
- class TrueSheetFooterView(context: ThemedReactContext) : ReactViewGroup(context) {
28
+ class TrueSheetFooterView(private val reactContext: ThemedReactContext) :
29
+ ReactViewGroup(reactContext),
30
+ RootView {
31
+
21
32
  var delegate: TrueSheetFooterViewDelegate? = null
33
+ var eventDispatcher: EventDispatcher? = null
22
34
 
23
35
  private var lastWidth = 0
24
36
  private var lastHeight = 0
25
37
 
26
- override fun onLayout(
27
- changed: Boolean,
28
- left: Int,
29
- top: Int,
30
- right: Int,
31
- bottom: Int
32
- ) {
33
- super.onLayout(changed, left, top, right, bottom)
34
-
35
- // Notify delegate when footer size changes
36
- val newWidth = right - left
37
- val newHeight = bottom - top
38
-
39
- if (newWidth != lastWidth || newHeight != lastHeight) {
40
- lastWidth = newWidth
41
- lastHeight = newHeight
42
-
43
- // Notify delegate of size change
44
- delegate?.footerViewDidChangeSize(newWidth, newHeight)
38
+ private val jsTouchDispatcher = JSTouchDispatcher(this)
39
+ private var jsPointerDispatcher: JSPointerDispatcher? = null
40
+
41
+ init {
42
+ jsPointerDispatcher = JSPointerDispatcher(this)
43
+ }
44
+
45
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
46
+ super.onSizeChanged(w, h, oldw, oldh)
47
+
48
+ if (w != lastWidth || h != lastHeight) {
49
+ lastWidth = w
50
+ lastHeight = h
51
+ delegate?.footerViewDidChangeSize(w, h)
45
52
  }
46
53
  }
54
+
55
+ // ==================== RootView Implementation ====================
56
+
57
+ override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
58
+ eventDispatcher?.let { dispatcher ->
59
+ jsTouchDispatcher.handleTouchEvent(event, dispatcher, reactContext)
60
+ jsPointerDispatcher?.handleMotionEvent(event, dispatcher, true)
61
+ }
62
+ return super.onInterceptTouchEvent(event)
63
+ }
64
+
65
+ override fun onTouchEvent(event: MotionEvent): Boolean {
66
+ eventDispatcher?.let { dispatcher ->
67
+ jsTouchDispatcher.handleTouchEvent(event, dispatcher, reactContext)
68
+ jsPointerDispatcher?.handleMotionEvent(event, dispatcher, false)
69
+ }
70
+ super.onTouchEvent(event)
71
+ return true
72
+ }
73
+
74
+ override fun onInterceptHoverEvent(event: MotionEvent): Boolean {
75
+ eventDispatcher?.let { jsPointerDispatcher?.handleMotionEvent(event, it, true) }
76
+ return super.onHoverEvent(event)
77
+ }
78
+
79
+ override fun onHoverEvent(event: MotionEvent): Boolean {
80
+ eventDispatcher?.let { jsPointerDispatcher?.handleMotionEvent(event, it, false) }
81
+ return super.onHoverEvent(event)
82
+ }
83
+
84
+ override fun onChildStartedNativeGesture(childView: View?, ev: MotionEvent) {
85
+ eventDispatcher?.let { dispatcher ->
86
+ jsTouchDispatcher.onChildStartedNativeGesture(ev, dispatcher)
87
+ jsPointerDispatcher?.onChildStartedNativeGesture(childView, ev, dispatcher)
88
+ }
89
+ }
90
+
91
+ override fun onChildEndedNativeGesture(childView: View, ev: MotionEvent) {
92
+ eventDispatcher?.let { jsTouchDispatcher.onChildEndedNativeGesture(ev, it) }
93
+ jsPointerDispatcher?.onChildEndedNativeGesture()
94
+ }
95
+
96
+ override fun handleException(t: Throwable) {
97
+ reactContext.reactApplicationContext.handleException(RuntimeException(t))
98
+ }
99
+
100
+ companion object {
101
+ const val TAG_NAME = "TrueSheet"
102
+ }
47
103
  }
@@ -0,0 +1,38 @@
1
+ package com.lodev09.truesheet
2
+
3
+ import android.annotation.SuppressLint
4
+ import com.facebook.react.uimanager.ThemedReactContext
5
+ import com.facebook.react.views.view.ReactViewGroup
6
+
7
+ /**
8
+ * Delegate interface for header view size changes
9
+ */
10
+ interface TrueSheetHeaderViewDelegate {
11
+ fun headerViewDidChangeSize(width: Int, height: Int)
12
+ }
13
+
14
+ /**
15
+ * Header view that holds the header content
16
+ * Positioned at the top of the sheet content area
17
+ */
18
+ @SuppressLint("ViewConstructor")
19
+ class TrueSheetHeaderView(context: ThemedReactContext) : ReactViewGroup(context) {
20
+ var delegate: TrueSheetHeaderViewDelegate? = null
21
+
22
+ private var lastWidth = 0
23
+ private var lastHeight = 0
24
+
25
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
26
+ super.onSizeChanged(w, h, oldw, oldh)
27
+
28
+ if (w != lastWidth || h != lastHeight) {
29
+ lastWidth = w
30
+ lastHeight = h
31
+ delegate?.headerViewDidChangeSize(w, h)
32
+ }
33
+ }
34
+
35
+ companion object {
36
+ const val TAG_NAME = "TrueSheet"
37
+ }
38
+ }
@@ -0,0 +1,21 @@
1
+ package com.lodev09.truesheet
2
+
3
+ import com.facebook.react.module.annotations.ReactModule
4
+ import com.facebook.react.uimanager.ThemedReactContext
5
+ import com.facebook.react.uimanager.ViewGroupManager
6
+
7
+ /**
8
+ * ViewManager for TrueSheetHeaderView
9
+ * Manages the header area of the sheet
10
+ */
11
+ @ReactModule(name = TrueSheetHeaderViewManager.REACT_CLASS)
12
+ class TrueSheetHeaderViewManager : ViewGroupManager<TrueSheetHeaderView>() {
13
+
14
+ override fun getName(): String = REACT_CLASS
15
+
16
+ override fun createViewInstance(reactContext: ThemedReactContext): TrueSheetHeaderView = TrueSheetHeaderView(reactContext)
17
+
18
+ companion object {
19
+ const val REACT_CLASS = "TrueSheetHeaderView"
20
+ }
21
+ }
@@ -39,6 +39,7 @@ class TrueSheetPackage : TurboReactPackage() {
39
39
  TrueSheetViewManager(),
40
40
  TrueSheetContainerViewManager(),
41
41
  TrueSheetContentViewManager(),
42
+ TrueSheetHeaderViewManager(),
42
43
  TrueSheetFooterViewManager()
43
44
  )
44
45
  }