Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@ import com.lodev09.truesheet.core.Utils
class TrueSheetView(context: Context) :
ViewGroup(context),
LifecycleEventListener {
private var eventDispatcher: EventDispatcher? = null

private val reactContext: ThemedReactContext
get() = context as ThemedReactContext

private val surfaceId: Int
get() = UIManagerHelper.getSurfaceId(this)

var eventDispatcher: EventDispatcher?
get() = rootSheetView.eventDispatcher
set(eventDispatcher) {
rootSheetView.eventDispatcher = eventDispatcher
}

var initialIndex: Int = -1
var initialIndexAnimated: Boolean = true

Expand Down Expand Up @@ -62,11 +67,8 @@ class TrueSheetView(context: Context) :

init {
reactContext.addLifecycleEventListener(this)
eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)

rootSheetView = RootSheetView(context)
rootSheetView.eventDispatcher = eventDispatcher

sheetDialog = TrueSheetDialog(reactContext, rootSheetView)

// Configure Sheet Dialog
Expand Down Expand Up @@ -173,6 +175,13 @@ class TrueSheetView(context: Context) :
// Do nothing as we are laid out by UIManager
}

override fun setId(id: Int) {
super.setId(id)

// Forward the ID to our content view, so event dispatching behaves correctly
rootSheetView.id = id
}

override fun onAttachedToWindow() {
super.onAttachedToWindow()

Expand All @@ -193,7 +202,7 @@ class TrueSheetView(context: Context) :

override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
sheetDialog.dismiss()
onDropInstance()
}

override fun addView(child: View, index: Int) {
Expand All @@ -219,7 +228,6 @@ class TrueSheetView(context: Context) :

override fun removeViewAt(index: Int) {
UiThreadUtil.assertOnUiThread()

val child = getChildAt(index)
rootSheetView.removeView(child)
}
Expand All @@ -229,14 +237,12 @@ class TrueSheetView(context: Context) :
// Those will be handled by the rootView which lives in the dialog
}

override fun dispatchPopulateAccessibilityEvent(event: AccessibilityEvent): Boolean {
// Explicitly override this to prevent accessibility events being passed down to children
// Those will be handled by the rootView which lives in the dialog
return false
}
// Explicitly override this to prevent accessibility events being passed down to children
// Those will be handled by the mHostView which lives in the dialog
public override fun dispatchPopulateAccessibilityEvent(event: AccessibilityEvent): Boolean = false

override fun onHostResume() {
// do nothing
configureIfShowing()
}

override fun onHostPause() {
Expand All @@ -245,6 +251,10 @@ class TrueSheetView(context: Context) :

override fun onHostDestroy() {
// Drop the instance if the host is destroyed which will dismiss the dialog
onDropInstance()
}

fun onDropInstance() {
reactContext.removeLifecycleEventListener(this)
sheetDialog.dismiss()
}
Expand Down Expand Up @@ -311,7 +321,7 @@ class TrueSheetView(context: Context) :
eventDispatcher?.dispatchEvent(TrueSheetEvent(surfaceId, id, name, data))
}

private fun configureIfShowing() {
fun configureIfShowing() {
if (sheetDialog.isShowing) {
sheetDialog.configure()
sheetDialog.positionFooter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableType
import com.facebook.react.common.MapBuilder
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.ViewGroupManager
import com.facebook.react.uimanager.annotations.ReactProp
import com.lodev09.truesheet.core.Utils
Expand All @@ -19,7 +20,19 @@ class TrueSheetViewManager : ViewGroupManager<TrueSheetView>() {

override fun onDropViewInstance(view: TrueSheetView) {
super.onDropViewInstance(view)
view.onHostDestroy()
view.onDropInstance()
}

override fun addEventEmitters(reactContext: ThemedReactContext, view: TrueSheetView) {
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id)
dispatcher?.let {
view.eventDispatcher = it
}
}

override fun onAfterUpdateTransaction(view: TrueSheetView) {
super.onAfterUpdateTransaction(view)
view.configureIfShowing()
}

override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> =
Expand Down
39 changes: 15 additions & 24 deletions android/src/main/java/com/lodev09/truesheet/core/RootSheetView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ class RootSheetView(private val context: Context?) :

private val jSTouchDispatcher = JSTouchDispatcher(this)
private var jSPointerDispatcher: JSPointerDispatcher? = null
var sizeChangeListener: ((w: Int, h: Int) -> Unit)? = null

var sizeChangeListener: ((w: Int, h: Int) -> Unit)? = null
var eventDispatcher: EventDispatcher? = null

private val reactContext: ThemedReactContext
Expand All @@ -59,53 +59,44 @@ class RootSheetView(private val context: Context?) :
}

override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
eventDispatcher?.let { jSTouchDispatcher.handleTouchEvent(event, it) }
jSPointerDispatcher?.handleMotionEvent(event, eventDispatcher, true)
eventDispatcher?.let { eventDispatcher ->
jSTouchDispatcher.handleTouchEvent(event, eventDispatcher, reactContext)
jSPointerDispatcher?.handleMotionEvent(event, eventDispatcher, true)
}
return super.onInterceptTouchEvent(event)
}

@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
eventDispatcher?.let { jSTouchDispatcher.handleTouchEvent(event, it) }
jSPointerDispatcher?.handleMotionEvent(event, eventDispatcher, false)
eventDispatcher?.let { eventDispatcher ->
jSTouchDispatcher.handleTouchEvent(event, eventDispatcher, reactContext)
jSPointerDispatcher?.handleMotionEvent(event, eventDispatcher, false)
}
super.onTouchEvent(event)

// In case when there is no children interested in handling touch event, we return true from
// the root view in order to receive subsequent events related to that gesture
return true
}

override fun onInterceptHoverEvent(event: MotionEvent): Boolean {
jSPointerDispatcher?.handleMotionEvent(event, eventDispatcher, true)
eventDispatcher?.let { jSPointerDispatcher?.handleMotionEvent(event, it, true) }
return super.onHoverEvent(event)
}

override fun onHoverEvent(event: MotionEvent): Boolean {
jSPointerDispatcher?.handleMotionEvent(event, eventDispatcher, false)
eventDispatcher?.let { jSPointerDispatcher?.handleMotionEvent(event, it, false) }
return super.onHoverEvent(event)
}

@Deprecated("Deprecated in Java")
override fun onChildStartedNativeGesture(ev: MotionEvent) {
eventDispatcher?.let {
if (ev != null) {
jSTouchDispatcher.onChildStartedNativeGesture(ev, it)
}
override fun onChildStartedNativeGesture(childView: View, ev: MotionEvent) {
eventDispatcher?.let { eventDispatcher ->
jSTouchDispatcher.onChildStartedNativeGesture(ev, eventDispatcher)
jSPointerDispatcher?.onChildStartedNativeGesture(childView, ev, eventDispatcher)
}
}

override fun onChildStartedNativeGesture(childView: View?, ev: MotionEvent) {
eventDispatcher?.let { jSTouchDispatcher.onChildStartedNativeGesture(ev, it) }
jSPointerDispatcher?.onChildStartedNativeGesture(childView, ev, eventDispatcher)
}

override fun onChildEndedNativeGesture(childView: View, ev: MotionEvent) {
eventDispatcher?.let { jSTouchDispatcher.onChildEndedNativeGesture(ev, it) }
jSPointerDispatcher?.onChildEndedNativeGesture()
}

override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
// No-op - override in order to still receive events to onInterceptTouchEvent
// even when some other view disallow that
}
}
10 changes: 7 additions & 3 deletions docs/docs/troubleshooting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@ On Android, [RNGH does not work](https://docs.swmansion.com/react-native-gesture
import { GestureHandlerRootView } from 'react-native-gesture-handler'
```

```tsx
```tsx {3-5}
return (
<TrueSheet ref={sheet}>
<GestureHandlerRootView>
<GestureHandlerRootView style={{ flexGrow: 1 }}>
<View />
</GestureHandlerRootView>
</TrueSheet>
)
```

:::info
Note that we are using `flexGrow` instead of `flex` here. For some weird reason, `flex` does not work correctly. See [below](#weird-layout-render).
:::

## React Navigation

[`react-navigation`](https://reactnavigation.org)
Expand All @@ -35,7 +39,7 @@ return (
On IOS, navigating to a screen from within the Sheet can cause issues. To resolve this, dismiss the sheet before navigating!

Example:
```tsx
```tsx {4-5}
const sheet = useRef<TrueSheet>(null)

const navigate = async () => {
Expand Down
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1214,7 +1214,7 @@ PODS:
- React-Core
- react-native-safe-area-context (5.2.0):
- React-Core
- react-native-true-sheet (2.0.2):
- react-native-true-sheet (2.0.3):
- DoubleConversion
- glog
- hermes-engine
Expand Down Expand Up @@ -1923,7 +1923,7 @@ SPEC CHECKSUMS:
React-microtasksnativemodule: 72564d5469003687d39bfc4efad281df8efc0684
react-native-maps: ee1e65647460c3d41e778071be5eda10e3da6225
react-native-safe-area-context: 849d7df29ecb2a7155c769c0b76849ba952c2aa3
react-native-true-sheet: 7fb131224336979607e4d81c523e07f618ae3a71
react-native-true-sheet: b5cc3652405dd472035b5af191857179c47d56d8
React-nativeconfig: cb207ebba7cafce30657c7ad9f1587a8f32e4564
React-NativeModulesApple: 82a8bee52df9f5b378195a500f22be3a6ef0f890
React-perflogger: 8152bab3f0eb4b8751f282f9af7caed2c823a9ea
Expand Down
41 changes: 22 additions & 19 deletions example/src/components/DemoContent.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { View, type ColorValue, type ViewProps, Text, StyleSheet } from 'react-native'
import {
View,
type ColorValue,
type ViewProps,
Text,
type ViewStyle,
type TextStyle,
} from 'react-native'
import { BORDER_RADIUS, SPACING } from '../utils'

interface DemoContentProps extends ViewProps {
Expand All @@ -10,25 +17,21 @@ interface DemoContentProps extends ViewProps {
export const DemoContent = (props: DemoContentProps) => {
const { text, radius = BORDER_RADIUS, style: $style, color = 'rgba(0,0,0,0.2)', ...rest } = props
return (
<View
style={[styles.content, { backgroundColor: color, borderRadius: radius }, $style]}
{...rest}
>
{text && <Text style={styles.text}>{text}</Text>}
<View style={[$content, { backgroundColor: color, borderRadius: radius }, $style]} {...rest}>
{text && <Text style={$text}>{text}</Text>}
</View>
)
}

const styles = StyleSheet.create({
content: {
height: 100,
marginBottom: 16,
padding: SPACING / 2,
alignItems: 'center',
},
text: {
fontSize: 16,
lineHeight: 20,
color: 'white',
},
})
const $content: ViewStyle = {
height: 100,
marginBottom: 16,
padding: SPACING / 2,
alignItems: 'center',
}

const $text: TextStyle = {
fontSize: 16,
lineHeight: 20,
color: 'white',
}
16 changes: 7 additions & 9 deletions example/src/components/sheets/BasicSheet.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { forwardRef, useRef, type Ref, useImperativeHandle } from 'react'
import { StyleSheet } from 'react-native'
import { type ViewStyle } from 'react-native'
import { TrueSheet, type TrueSheetProps } from '@lodev09/react-native-true-sheet'

import { DARK, DARK_BLUE, GRABBER_COLOR, SPACING } from '../../utils'
Expand Down Expand Up @@ -44,7 +44,7 @@ export const BasicSheet = forwardRef((props: BasicSheetProps, ref: Ref<TrueSheet
<TrueSheet
sizes={['auto', '80%', 'large']}
ref={sheetRef}
contentContainerStyle={styles.content}
contentContainerStyle={$content}
blurTint="dark"
backgroundColor={DARK}
cornerRadius={12}
Expand Down Expand Up @@ -92,7 +92,7 @@ export const BasicSheet = forwardRef((props: BasicSheetProps, ref: Ref<TrueSheet
ref={childSheet}
sizes={['auto']}
backgroundColor={DARK}
contentContainerStyle={styles.content}
contentContainerStyle={$content}
FooterComponent={<Footer />}
>
<DemoContent color={DARK_BLUE} />
Expand All @@ -104,10 +104,8 @@ export const BasicSheet = forwardRef((props: BasicSheetProps, ref: Ref<TrueSheet
)
})

BasicSheet.displayName = 'BasicSheet'
const $content: ViewStyle = {
padding: SPACING,
}

const styles = StyleSheet.create({
content: {
padding: SPACING,
},
})
BasicSheet.displayName = 'BasicSheet'
14 changes: 6 additions & 8 deletions example/src/components/sheets/BlankSheet.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { forwardRef, type Ref } from 'react'
import { StyleSheet, Text } from 'react-native'
import { Text, type ViewStyle } from 'react-native'
import { TrueSheet, type TrueSheetProps } from '@lodev09/react-native-true-sheet'

import { $WHITE_TEXT, DARK, SPACING } from '../../utils'
Expand All @@ -16,18 +16,16 @@ export const BlankSheet = forwardRef((props: BlankSheetProps, ref: Ref<TrueSheet
edgeToEdge
backgroundColor={DARK}
keyboardMode="pan"
contentContainerStyle={styles.content}
contentContainerStyle={$content}
{...props}
>
<Text style={$WHITE_TEXT}>Blank Sheet</Text>
</TrueSheet>
)
})

BlankSheet.displayName = 'BlankSheet'
const $content: ViewStyle = {
padding: SPACING,
}

const styles = StyleSheet.create({
content: {
padding: SPACING,
},
})
BlankSheet.displayName = 'BlankSheet'
Loading
Loading