Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(POM-451): Adjustable bottom sheet height #259

Merged
merged 6 commits into from
Feb 4, 2025
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
2 changes: 1 addition & 1 deletion .idea/kotlinc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
buildscript {
ext {
androidGradlePluginVersion = '8.8.0'
kotlinVersion = '2.1.0'
kspVersion = '2.1.0-1.0.29'
kotlinVersion = '2.1.10'
kspVersion = '2.1.10-1.0.29'
dokkaVersion = '1.9.20'
androidxNavigationVersion = '2.8.6'
nexusPublishPluginVersion = '2.0.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import com.processout.sdk.ui.card.update.POCardUpdateConfiguration
import com.processout.sdk.ui.card.update.POCardUpdateConfiguration.CardInformation
import com.processout.sdk.ui.card.update.POCardUpdateLauncher
import com.processout.sdk.ui.googlepay.POGooglePayCardTokenizationLauncher
import com.processout.sdk.ui.shared.configuration.POCancellationConfiguration
import com.processout.sdk.ui.shared.configuration.POBottomSheetConfiguration
import com.processout.sdk.ui.shared.configuration.POBottomSheetConfiguration.Height.WrapContent
import com.processout.sdk.ui.shared.view.dialog.POAlertDialog
import kotlinx.coroutines.launch

Expand Down Expand Up @@ -97,10 +98,9 @@ class FeaturesFragment : BaseFragment<FragmentFeaturesBinding>(
scheme = card?.scheme,
preferredScheme = card?.coScheme
),
cancellation = POCancellationConfiguration(
backPressed = true,
dragDown = true,
touchOutside = false
bottomSheet = POBottomSheetConfiguration(
height = WrapContent,
expandable = false
)
)
)
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Wed Oct 05 19:43:19 EEST 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.*
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.viewModels
Expand All @@ -21,10 +20,13 @@ import com.processout.sdk.ui.base.BaseBottomSheetDialogFragment
import com.processout.sdk.ui.card.update.CardUpdateCompletion.Failure
import com.processout.sdk.ui.card.update.CardUpdateCompletion.Success
import com.processout.sdk.ui.card.update.CardUpdateEvent.Dismiss
import com.processout.sdk.ui.card.update.POCardUpdateConfiguration.Button
import com.processout.sdk.ui.core.theme.ProcessOutTheme
import com.processout.sdk.ui.shared.component.displayCutoutHeight
import com.processout.sdk.ui.shared.component.screenModeAsState
import com.processout.sdk.ui.shared.extension.dpToPx
import com.processout.sdk.ui.shared.configuration.POBottomSheetConfiguration
import com.processout.sdk.ui.shared.configuration.POBottomSheetConfiguration.Height.Fixed
import com.processout.sdk.ui.shared.configuration.POBottomSheetConfiguration.Height.WrapContent
import kotlin.math.roundToInt

internal class CardUpdateBottomSheet : BaseBottomSheetDialogFragment<POCard>() {

Expand All @@ -33,16 +35,20 @@ internal class CardUpdateBottomSheet : BaseBottomSheetDialogFragment<POCard>() {
}

override var expandable = false
override val defaultViewHeight by lazy { 440.dpToPx(requireContext()) }
override val defaultViewHeight by lazy { (screenHeight * 0.3).roundToInt() }

private var configuration: POCardUpdateConfiguration? = null
private val viewHeightConfiguration by lazy { configuration?.bottomSheet?.height ?: WrapContent }

private val viewModel: CardUpdateViewModel by viewModels {
CardUpdateViewModel.Factory(
app = requireActivity().application,
configuration = configuration ?: POCardUpdateConfiguration(
cardId = String(),
submitButton = Button()
bottomSheet = POBottomSheetConfiguration(
height = WrapContent,
expandable = false
)
)
)
}
Expand Down Expand Up @@ -74,12 +80,20 @@ internal class CardUpdateBottomSheet : BaseBottomSheetDialogFragment<POCard>() {
with(viewModel.completion.collectAsStateWithLifecycle()) {
LaunchedEffect(value) { handle(value) }
}
with(screenModeAsState(viewHeight = defaultViewHeight)) {
var viewHeight by remember { mutableIntStateOf(defaultViewHeight) }
with(screenModeAsState(viewHeight = viewHeight)) {
LaunchedEffect(value) { apply(value) }
}
val displayCutoutHeight = displayCutoutHeight()
CardUpdateScreen(
state = viewModel.state.collectAsStateWithLifecycle().value,
onEvent = remember { viewModel::onEvent },
onContentHeightChanged = { contentHeight ->
viewHeight = when (val height = viewHeightConfiguration) {
is Fixed -> (screenHeight * height.fraction + displayCutoutHeight).roundToInt()
WrapContent -> contentHeight
}
},
style = CardUpdateScreen.style(custom = configuration?.style)
)
}
Expand All @@ -88,7 +102,10 @@ internal class CardUpdateBottomSheet : BaseBottomSheetDialogFragment<POCard>() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
configuration?.let { apply(it.cancellation) }
configuration?.let {
expandable = it.bottomSheet.expandable
apply(it.bottomSheet.cancellation)
}
}

private fun handle(completion: CardUpdateCompletion) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.remember
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
Expand All @@ -16,8 +14,10 @@ import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import com.processout.sdk.ui.card.update.CardUpdateEvent.*
import com.processout.sdk.ui.core.component.*
Expand All @@ -26,25 +26,36 @@ import com.processout.sdk.ui.core.component.field.text.POTextField
import com.processout.sdk.ui.core.state.POActionState
import com.processout.sdk.ui.core.state.POImmutableList
import com.processout.sdk.ui.core.style.POAxis
import com.processout.sdk.ui.core.theme.ProcessOutTheme
import com.processout.sdk.ui.core.theme.ProcessOutTheme.colors
import com.processout.sdk.ui.core.theme.ProcessOutTheme.dimensions
import com.processout.sdk.ui.core.theme.ProcessOutTheme.shapes
import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing
import com.processout.sdk.ui.shared.component.DynamicFooter
import com.processout.sdk.ui.shared.component.rememberLifecycleEvent
import com.processout.sdk.ui.shared.extension.dpToPx
import com.processout.sdk.ui.shared.state.FieldState

@Composable
internal fun CardUpdateScreen(
state: CardUpdateState,
onEvent: (CardUpdateEvent) -> Unit,
onContentHeightChanged: (Int) -> Unit,
style: CardUpdateScreen.Style = CardUpdateScreen.style()
) {
var topBarHeight by remember { mutableIntStateOf(0) }
var bottomBarHeight by remember { mutableIntStateOf(0) }
Scaffold(
modifier = Modifier
.nestedScroll(rememberNestedScrollInteropConnection())
.clip(shape = ProcessOutTheme.shapes.topRoundedCornersLarge),
.clip(shape = shapes.topRoundedCornersLarge),
containerColor = style.backgroundColor,
topBar = {
POHeader(
modifier = Modifier.verticalScroll(rememberScrollState()),
modifier = Modifier
.verticalScroll(rememberScrollState())
.onGloballyPositioned {
topBarHeight = it.size.height
},
title = state.title,
style = style.title,
dividerColor = style.dividerColor,
Expand All @@ -58,7 +69,10 @@ internal fun CardUpdateScreen(
primary = state.primaryAction,
secondary = state.secondaryAction,
onEvent = onEvent,
style = style.actionsContainer
style = style.actionsContainer,
modifier = Modifier.onGloballyPositioned {
bottomBarHeight = it.size.height
}
)
}
}
Expand All @@ -68,21 +82,30 @@ internal fun CardUpdateScreen(
.fillMaxSize()
.padding(scaffoldPadding)
.verticalScroll(rememberScrollState())
.padding(ProcessOutTheme.spacing.extraLarge),
verticalArrangement = Arrangement.spacedBy(ProcessOutTheme.spacing.small)
.padding(spacing.extraLarge)
) {
Fields(
fields = state.fields,
onEvent = onEvent,
focusedFieldId = state.focusedFieldId,
isPrimaryActionEnabled = state.primaryAction.enabled && !state.primaryAction.loading,
style = style.field
)
POExpandableText(
text = state.errorMessage,
style = style.errorMessage,
modifier = Modifier.fillMaxWidth()
)
val verticalSpacingPx = (spacing.extraLarge * 4 + 10.dp).dpToPx()
Column(
modifier = Modifier.onGloballyPositioned {
val contentHeight = it.size.height + topBarHeight + bottomBarHeight + verticalSpacingPx
onContentHeightChanged(contentHeight)
}
) {
Fields(
fields = state.fields,
onEvent = onEvent,
focusedFieldId = state.focusedFieldId,
isPrimaryActionEnabled = state.primaryAction.enabled && !state.primaryAction.loading,
style = style.field
)
POExpandableText(
text = state.errorMessage,
style = style.errorMessage,
modifier = Modifier
.fillMaxWidth()
.padding(top = spacing.small)
)
}
}
}
}
Expand All @@ -95,49 +118,53 @@ private fun Fields(
isPrimaryActionEnabled: Boolean,
style: POField.Style
) {
val lifecycleEvent = rememberLifecycleEvent()
fields.elements.forEach { state ->
val focusRequester = remember { FocusRequester() }
POTextField(
value = state.value,
onValueChange = {
if (state.enabled) {
onEvent(
FieldValueChanged(
id = state.id,
value = state.inputFilter?.filter(it) ?: it
Column(
verticalArrangement = Arrangement.spacedBy(spacing.small)
) {
val lifecycleEvent = rememberLifecycleEvent()
fields.elements.forEach { state ->
val focusRequester = remember { FocusRequester() }
POTextField(
value = state.value,
onValueChange = {
if (state.enabled) {
onEvent(
FieldValueChanged(
id = state.id,
value = state.inputFilter?.filter(it) ?: it
)
)
)
}
},
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
.onFocusChanged {
onEvent(
FieldFocusChanged(
id = state.id,
isFocused = it.isFocused
)
)
}
},
style = style,
enabled = state.enabled,
readOnly = !state.enabled,
isError = state.isError,
forceTextDirectionLtr = state.forceTextDirectionLtr,
placeholderText = state.placeholder,
trailingIcon = { state.iconResId?.let { AnimatedFieldIcon(id = it) } },
keyboardOptions = state.keyboardOptions,
keyboardActions = POField.keyboardActions(
imeAction = state.keyboardOptions.imeAction,
actionId = state.keyboardActionId,
enabled = isPrimaryActionEnabled,
onClick = { onEvent(Action(id = it)) }
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
.onFocusChanged {
onEvent(
FieldFocusChanged(
id = state.id,
isFocused = it.isFocused
)
)
},
style = style,
enabled = state.enabled,
readOnly = !state.enabled,
isError = state.isError,
forceTextDirectionLtr = state.forceTextDirectionLtr,
placeholderText = state.placeholder,
trailingIcon = { state.iconResId?.let { AnimatedFieldIcon(id = it) } },
keyboardOptions = state.keyboardOptions,
keyboardActions = POField.keyboardActions(
imeAction = state.keyboardOptions.imeAction,
actionId = state.keyboardActionId,
enabled = isPrimaryActionEnabled,
onClick = { onEvent(Action(id = it)) }
)
)
)
if (state.id == focusedFieldId && lifecycleEvent == Lifecycle.Event.ON_RESUME) {
PORequestFocus(focusRequester, lifecycleEvent)
if (state.id == focusedFieldId && lifecycleEvent == Lifecycle.Event.ON_RESUME) {
PORequestFocus(focusRequester, lifecycleEvent)
}
}
}
}
Expand All @@ -147,7 +174,7 @@ private fun AnimatedFieldIcon(@DrawableRes id: Int) {
POAnimatedImage(
id = id,
modifier = Modifier
.requiredHeight(ProcessOutTheme.dimensions.formComponentMinHeight)
.requiredHeight(dimensions.formComponentMinHeight)
.padding(POField.contentPadding),
contentScale = ContentScale.FillHeight
)
Expand All @@ -158,11 +185,13 @@ private fun Actions(
primary: POActionState,
secondary: POActionState?,
onEvent: (CardUpdateEvent) -> Unit,
style: POActionsContainer.Style
style: POActionsContainer.Style,
modifier: Modifier = Modifier
) {
val actions = mutableListOf(primary)
secondary?.let { actions.add(it) }
POActionsContainer(
modifier = modifier,
actions = POImmutableList(
if (style.axis == POAxis.Horizontal) actions.reversed() else actions
),
Expand Down Expand Up @@ -200,12 +229,12 @@ internal object CardUpdateScreen {
} ?: POActionsContainer.default,
backgroundColor = custom?.backgroundColorResId?.let {
colorResource(id = it)
} ?: ProcessOutTheme.colors.surface.default,
} ?: colors.surface.default,
dividerColor = custom?.dividerColorResId?.let {
colorResource(id = it)
} ?: ProcessOutTheme.colors.border.subtle,
} ?: colors.border.subtle,
dragHandleColor = custom?.dragHandleColorResId?.let {
colorResource(id = it)
} ?: ProcessOutTheme.colors.border.subtle
} ?: colors.border.subtle
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ internal class CardUpdateViewModel private constructor(
}
)
},
draggable = cancellation.dragDown
draggable = bottomSheet.cancellation.dragDown || bottomSheet.expandable
)
}

Expand Down
Loading