diff --git a/NOTES.md b/NOTES.md index d748ab0..73a0a1d 100644 --- a/NOTES.md +++ b/NOTES.md @@ -5,3 +5,4 @@ - [UISheetPresentationController](https://sarunw.com/posts/bottom-sheet-in-ios-15-with-uisheetpresentationcontroller/#detent) - [Bottom Sheet (Android)](https://shopify.engineering/creating-native-components-accept-react-native-subviews) - [BottomSheetBehaviour](https://medium.com/@wind.orca.pe/implementing-a-bottom-sheet-with-multiple-nested-scroll-children-in-android-22361c8223dd) +- [Native View + Module](https://susuthapa19961227.medium.com/bridging-the-gap-how-to-call-native-component-functions-from-reactnative-a8d212588b72) diff --git a/android/build.gradle b/android/build.gradle index 67942ca..96ab6be 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -90,7 +90,6 @@ dependencies { //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0" - implementation 'com.google.android.material:material:1.11.0' + implementation "com.google.android.material:material:1.11.0" } diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetBottomSheetBehavior.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetBottomSheetBehavior.kt deleted file mode 100644 index d612542..0000000 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetBottomSheetBehavior.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.lodev09.truesheet - -import android.content.Context -import android.util.AttributeSet -import android.view.MotionEvent -import android.view.View -import android.view.ViewGroup -import android.widget.ScrollView -import androidx.coordinatorlayout.widget.CoordinatorLayout -import com.google.android.material.bottomsheet.BottomSheetBehavior - -class TrueSheetBottomSheetBehavior: BottomSheetBehavior { - - constructor() : super() - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) - - override fun onInterceptTouchEvent(parent: CoordinatorLayout, child: T, event: MotionEvent): Boolean { - val isDownEvent = (event.actionMasked == MotionEvent.ACTION_DOWN) - val expanded = (state == BottomSheetBehavior.STATE_EXPANDED) - - if(isDownEvent && expanded){ - val content = child.getChildAt(0) as ViewGroup - for(i in 0 until content.childCount){ - val contentChild = content.getChildAt(i) - val scrolled = (contentChild is ScrollView && contentChild.scrollY > 0) - if(!scrolled) continue - - val inside = isMotionEventInsideView(contentChild, event) - if(inside) return false - } - } - - return super.onInterceptTouchEvent(parent, child, event) - } - - private fun isMotionEventInsideView(view: View, event: MotionEvent): Boolean { - val coords = intArrayOf(0, 0) - view.getLocationInWindow(coords) - return ( - event.rawX >= coords[0] && event.rawX <= (coords[0] + view.width) && - event.rawY >= coords[1] && event.rawY <= (coords[1] + view.height) - ) - } -} diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt index fb0c98e..447e89f 100644 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +++ b/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt @@ -7,9 +7,7 @@ import com.facebook.react.uimanager.ViewManager class TrueSheetPackage : ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext): List { - return listOf( - TrueSheetViewModule(reactContext) - ) + return listOf(TrueSheetViewModule(reactContext)) } override fun createViewManagers(reactContext: ReactApplicationContext): List> { diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt index 6908e34..83f2711 100644 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +++ b/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt @@ -1,32 +1,36 @@ package com.lodev09.truesheet +import android.annotation.SuppressLint import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater import android.view.View +import android.widget.FrameLayout +import android.widget.LinearLayout import android.widget.RelativeLayout -import com.google.android.material.bottomsheet.BottomSheetBehavior import androidx.coordinatorlayout.widget.CoordinatorLayout +import com.facebook.react.bridge.ReactContext import com.google.android.material.bottomsheet.BottomSheetDialog -class TrueSheetView(context: Context): CoordinatorLayout(context) { +class TrueSheetView(context: ReactContext): FrameLayout(context) { - private lateinit var contents: RelativeLayout - private set - private lateinit var behavior: BottomSheetBehavior<*> - private set + private lateinit var contentView: View + private lateinit var bottomSheetDialog: BottomSheetDialog override fun onViewAdded(child: View?) { super.onViewAdded(child) + if (child != null) { + removeView(child) + bottomSheetDialog = BottomSheetDialog(context) + bottomSheetDialog.setContentView(child) + } + } - contents = child as RelativeLayout + fun present() { + bottomSheetDialog.show() + } - behavior = BottomSheetBehavior.from(contents).apply { - // virtually disables 'third' breakpoint - halfExpandedRatio = 0.9999999f - isFitToContents = true - isHideable = true - // default to no collapsed state - skipCollapsed = true - setPeekHeight(Integer.MAX_VALUE) - } + fun dismiss() { + bottomSheetDialog.dismiss() } } diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt index 5061f29..d487587 100644 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +++ b/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt @@ -10,7 +10,7 @@ class TrueSheetViewManager : ViewGroupManager() { @SuppressLint("InflateParams") override fun createViewInstance(reactContext: ThemedReactContext): TrueSheetView { - return LayoutInflater.from(reactContext).inflate(R.layout.truesheet_layout, null) as TrueSheetView + return TrueSheetView(reactContext) } companion object { diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetViewModule.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetViewModule.kt index bb88249..2d3c2be 100644 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetViewModule.kt +++ b/android/src/main/java/com/lodev09/truesheet/TrueSheetViewModule.kt @@ -1,13 +1,57 @@ package com.lodev09.truesheet +import android.util.Log +import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.UiThreadUtil import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.uimanager.UIManagerHelper +import kotlinx.coroutines.CoroutineScope +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine @ReactModule(name = TrueSheetViewModule.NAME) class TrueSheetViewModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { override fun getName(): String = NAME + private fun withTrueSheetView(tag: Int, closure: (trueSheetView: TrueSheetView) -> Unit) { + UiThreadUtil.runOnUiThread { + try { + val manager = UIManagerHelper.getUIManagerForReactTag(reactApplicationContext, tag) + val view = manager?.resolveView(tag) + if (view == null) { + Log.d(NAME, "TrueSheetView with tag $tag not found") + return@runOnUiThread + } + + if (view is TrueSheetView) { + closure(view) + } else { + Log.d(NAME, "View is not of type TrueSheetView") + } + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + @ReactMethod + fun present(tag: Int, index: Int, promise: Promise) { + withTrueSheetView(tag) { trueSheetView -> + trueSheetView.present() + } + } + + @ReactMethod + fun dismiss(tag: Int, promise: Promise) { + withTrueSheetView(tag) { trueSheetView -> + trueSheetView.dismiss() + } + } + companion object { const val NAME = "TrueSheetView" } diff --git a/android/src/main/java/com/lodev09/truesheet/utils/withPromise.kt b/android/src/main/java/com/lodev09/truesheet/utils/withPromise.kt new file mode 100644 index 0000000..e3ee45d --- /dev/null +++ b/android/src/main/java/com/lodev09/truesheet/utils/withPromise.kt @@ -0,0 +1,13 @@ +package com.lodev09.truesheet.utils + +import com.facebook.react.bridge.Promise + +inline fun withPromise(promise: Promise, closure: () -> Any?) { + try { + val result = closure() + promise.resolve(result) + } catch (e: Throwable) { + e.printStackTrace() + promise.reject("Error", e.message, e.cause) + } +} diff --git a/android/src/main/res/layout/truesheet_layout.xml b/android/src/main/res/layout/truesheet_layout.xml index 8209418..4eb9d04 100644 --- a/android/src/main/res/layout/truesheet_layout.xml +++ b/android/src/main/res/layout/truesheet_layout.xml @@ -9,7 +9,6 @@ android:id="@+id/contents" android:layout_gravity="center_horizontal" android:layout_width="match_parent" - android:layout_height="match_parent" - app:layout_behavior="com.lodev09.truesheet.TrueSheetBottomSheetBehavior"/> + android:layout_height="match_parent" /> diff --git a/example/src/App.tsx b/example/src/App.tsx index dd84f9b..f69c718 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -41,12 +41,12 @@ export default function App() { const _scrollViewRef = useRef(null) const _flatListRef = useRef(null) - const presentSheet1 = (_index = 0) => { - // sheet1.current?.present(index) + const presentSheet1 = (index = 0) => { + sheet1.current?.present(index) } const dismissSheet1 = () => { - // sheet1.current?.dismiss() + sheet1.current?.dismiss() } return ( @@ -67,9 +67,9 @@ export default function App() { > -