Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1,13 +1,45 @@
package com.swmansion.rnscreens.gamma.modals.formsheet

import android.view.View
import android.widget.FrameLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior

internal class FormSheetBehaviorController(
private val sheetView: FrameLayout,
private val onDetentChanged: ((index: Int) -> Unit)? = null,
) {
private val behavior = BottomSheetBehavior.from(sheetView)

private var currentDetentsCount: Int = 1
private var lastEmittedDetentIndex: Int = -1

private val bottomSheetCallback =
object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(
bottomSheet: View,
newState: Int,
) {
val index = mapStateToDetentIndex(newState)
if (index != -1 && index != lastEmittedDetentIndex) {
lastEmittedDetentIndex = index
onDetentChanged?.invoke(index)
}
}
Comment thread
Copilot marked this conversation as resolved.

override fun onSlide(
bottomSheet: View,
slideOffset: Float,
) = Unit
}

internal fun setup() {
behavior.addBottomSheetCallback(bottomSheetCallback)
}

internal fun destroy() {
behavior.removeBottomSheetCallback(bottomSheetCallback)
}

/**
* @param detents - parsed detents configuration.
* @param sheetAvailableSpace - the full window height that detent fractions are measured against.
Expand All @@ -25,6 +57,8 @@ internal class FormSheetBehaviorController(
contentHeightForFitToContents: Int = 0,
nativeContainerPaddingBottom: Int = 0,
) {
currentDetentsCount = detents.count

if (sheetAvailableSpace <= 0) {
return
}
Expand Down Expand Up @@ -95,4 +129,23 @@ internal class FormSheetBehaviorController(
// TODO: @t0maboro - in v4 impl the state was passed as a param, consider the same approach
state = BottomSheetBehavior.STATE_COLLAPSED
}

private fun mapStateToDetentIndex(state: Int): Int =
when (currentDetentsCount) {
1 -> if (state == BottomSheetBehavior.STATE_EXPANDED) 0 else -1
2 ->
when (state) {
BottomSheetBehavior.STATE_COLLAPSED -> 0
BottomSheetBehavior.STATE_EXPANDED -> 1
else -> -1
}
3 ->
when (state) {
BottomSheetBehavior.STATE_COLLAPSED -> 0
BottomSheetBehavior.STATE_HALF_EXPANDED -> 1
BottomSheetBehavior.STATE_EXPANDED -> 2
else -> -1
}
else -> -1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.swmansion.rnscreens.gamma.modals.formsheet

import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event
import com.swmansion.rnscreens.gamma.common.event.NamingAwareEventType

class FormSheetDetentChangedEvent(
surfaceId: Int,
viewId: Int,
private val index: Int,
) : Event<FormSheetDetentChangedEvent>(surfaceId, viewId),
NamingAwareEventType {
override fun getEventName(): String = EVENT_NAME

override fun getEventRegistrationName() = EVENT_REGISTRATION_NAME

override fun getEventData(): WritableMap =
Arguments.createMap().apply {
putInt("index", index)
}

override fun canCoalesce(): Boolean = false

companion object : NamingAwareEventType {
const val EVENT_NAME = "topDetentChanged"
const val EVENT_REGISTRATION_NAME = "onDetentChanged"

override fun getEventName() = EVENT_NAME

override fun getEventRegistrationName() = EVENT_REGISTRATION_NAME
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.swmansion.rnscreens.gamma.modals.formsheet

import com.swmansion.rnscreens.gamma.common.event.ViewAppearanceEventEmitter

internal interface FormSheetDialogEventEmitter : ViewAppearanceEventEmitter {
fun emitOnNativeDismissEvent()

fun emitOnDetentChanged(index: Int)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import android.util.Log
import android.view.ContextThemeWrapper
import android.view.View
import android.widget.FrameLayout
import com.swmansion.rnscreens.gamma.common.event.ViewAppearanceEventEmitter
import com.swmansion.rnscreens.gamma.modals.dimmingview.DimmingViewManager
import kotlin.properties.Delegates

class FormSheetDialogManager(
context: Context,
private val contentView: View,
private val onDismissRequest: () -> Unit,
) {
private var formSheetConfig = FormSheetConfig()

Expand All @@ -34,7 +33,9 @@ class FormSheetDialogManager(
private val bottomSheetView = dialog.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)

private val behaviorController =
bottomSheetView?.let { FormSheetBehaviorController(it) }
bottomSheetView?.let {
FormSheetBehaviorController(it) { index -> eventEmitter?.emitOnDetentChanged(index) }
}

private val dimmingManager = DimmingViewManager(context, dialog)

Expand All @@ -51,17 +52,20 @@ class FormSheetDialogManager(
dialog = dialog,
bottomSheetView = bottomSheetView,
dimmingManager = dimmingManager,
onNativeDismiss = onDismissRequest,
onNativeDismiss = { eventEmitter?.emitOnNativeDismissEvent() },
)

internal var appearanceEventEmitter: ViewAppearanceEventEmitter? by presentationManager::appearanceEventEmitter
internal var eventEmitter: FormSheetDialogEventEmitter? by Delegates.observable(null) { _, _, newValue ->
presentationManager.appearanceEventEmitter = newValue
}

internal val contentSizeChangeDelegate: FormSheetContentSizeChangeDelegate
get() = dimensionsCoordinator

init {
presentationManager.setup()
dimensionsCoordinator.setup()
behaviorController?.setup()
}

internal fun applyConfig(newConfig: FormSheetConfig) {
Expand Down Expand Up @@ -104,6 +108,7 @@ class FormSheetDialogManager(
}

internal fun destroy() {
behaviorController?.destroy()
presentationManager.destroy()
dimensionsCoordinator.destroy()
dialog.dismiss()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class FormSheetHost(
FormSheetDialogManager(
context = context,
contentView = sheetContentView,
onDismissRequest = { eventEmitter.emitOnNativeDismissEvent() },
)

init {
Expand Down Expand Up @@ -78,7 +77,7 @@ class FormSheetHost(
internal fun onViewManagerAddEventEmitters() {
check(id != NO_ID) { "[RNScreens] FormSheetHost must have its tag set when registering event emitters" }
eventEmitter = FormSheetHostEventEmitter(reactContext, id)
dialogManager.appearanceEventEmitter = eventEmitter
dialogManager.eventEmitter = eventEmitter
}

internal fun onAfterUpdateTransaction() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ package com.swmansion.rnscreens.gamma.modals.formsheet

import com.facebook.react.bridge.ReactContext
import com.swmansion.rnscreens.gamma.common.event.BaseEventEmitter
import com.swmansion.rnscreens.gamma.common.event.ViewAppearanceEventEmitter

internal class FormSheetHostEventEmitter(
reactContext: ReactContext,
viewTag: Int,
) : BaseEventEmitter(reactContext, viewTag),
ViewAppearanceEventEmitter {
fun emitOnNativeDismissEvent() {
FormSheetDialogEventEmitter {
override fun emitOnNativeDismissEvent() {
reactEventDispatcher.dispatchEvent(
FormSheetNativeDismissEvent(surfaceId, viewTag),
)
Expand Down Expand Up @@ -44,4 +43,10 @@ internal class FormSheetHostEventEmitter(
FormSheetDidDisappearEvent(surfaceId, viewTag),
)
}

override fun emitOnDetentChanged(index: Int) {
reactEventDispatcher.dispatchEvent(
FormSheetDetentChangedEvent(surfaceId, viewTag, index),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class FormSheetHostViewManager :
makeEventRegistrationInfo(FormSheetDidAppearEvent),
makeEventRegistrationInfo(FormSheetWillDisappearEvent),
makeEventRegistrationInfo(FormSheetDidDisappearEvent),
makeEventRegistrationInfo(FormSheetDetentChangedEvent),
)

override fun updateState(
Expand Down
4 changes: 2 additions & 2 deletions apps/src/tests/single-feature-tests/form-sheet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import TestFormSheetInitialDetentIndex from './test-form-sheet-initial-detent-in
import TestFormSheetLargestUndimmedDetentIndex from './test-form-sheet-largest-undimmed-detent-index-ios';
import TestFormSheetLifecycleEvents from './test-form-sheet-lifecycle-events';
import TestFormSheetNativeContainerStyle from './test-form-sheet-native-container-style-ios';
import TestFormSheetOnDetentChanged from './test-form-sheet-on-detent-changed-ios';
import TestFormSheetOnDetentChanged from './test-form-sheet-on-detent-changed';
import TestFormSheetPreferredCornerRadius from './test-form-sheet-preferred-corner-radius-ios';
import TestFormSheetPresentationState from './test-form-sheet-presentation-state';
import TestFormSheetPreventNativeDismiss from './test-form-sheet-prevent-native-dismiss-ios';
Expand All @@ -28,7 +28,7 @@ export { default as TestFormSheetInitialDetentIndex } from './test-form-sheet-in
export { default as TestFormSheetLargestUndimmedDetentIndex } from './test-form-sheet-largest-undimmed-detent-index-ios';
export { default as TestFormSheetLifecycleEvents } from './test-form-sheet-lifecycle-events';
export { default as TestFormSheetNativeContainerStyle } from './test-form-sheet-native-container-style-ios';
export { default as TestFormSheetOnDetentChanged } from './test-form-sheet-on-detent-changed-ios';
export { default as TestFormSheetOnDetentChanged } from './test-form-sheet-on-detent-changed';
export { default as TestFormSheetPreferredCornerRadius } from './test-form-sheet-preferred-corner-radius-ios';
export { default as TestFormSheetPresentationState } from './test-form-sheet-presentation-state';
export { default as TestFormSheetPreventNativeDismiss } from './test-form-sheet-prevent-native-dismiss-ios';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import React, { useState } from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';
import { FormSheet } from 'react-native-screens/experimental';
import type { ScenarioDescription } from '@apps/tests/shared/helpers';
import { createScenario } from '@apps/tests/shared/helpers';
import { Colors } from '@apps/shared/styling';

const scenarioDescription: ScenarioDescription = {
name: 'OnDetentChanged',
key: 'test-form-sheet-on-detent-changed-ios',
details:
'Allows testing the onDetentChanged event, verifying that the correct detent index is reported when swiping.',
platforms: ['ios'],
};
import { scenarioDescription } from './scenario-description';

function TestFormSheetOnDetentChanged() {
const [isOpen, setIsOpen] = useState(false);
Expand Down Expand Up @@ -77,7 +69,6 @@ const styles = StyleSheet.create({
flex: 1,
backgroundColor: Colors.background,
padding: 24,
justifyContent: 'center',
alignItems: 'center',
},
sheetTitle: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ScenarioDescription } from '@apps/tests/shared/helpers';

export const scenarioDescription: ScenarioDescription = {
name: 'OnDetentChanged',
key: 'test-form-sheet-on-detent-changed',
details:
'Allows testing the onDetentChanged event, verifying that the correct detent index is reported when swiping.',
platforms: ['android', 'ios'],
e2eCoverage: 'tbd',
smokeTest: false,
};
2 changes: 1 addition & 1 deletion src/components/gamma/modals/form-sheet/FormSheet.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ export interface FormSheetProps {
*
* Provides the `index` of the newly selected detent from the `detents` array.
*
* @platform ios
* @platform android, ios
*/
onDetentChanged?:
| FormSheetEventHandler<FormSheetDetentChangedEvent>
Expand Down
Loading