diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheet.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheet.kt index ec10ddfa0a..8b48fdcf60 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheet.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheet.kt @@ -1,11 +1,18 @@ package com.tangem.core.ui.components.bottomsheets import androidx.compose.foundation.layout.* -import androidx.compose.material3.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.SheetState +import androidx.compose.material3.SheetValue.Expanded +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity +import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.res.LocalBottomSheetAlwaysVisible import com.tangem.core.ui.res.LocalWindowSize import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.utils.WindowInsetsZero @@ -13,45 +20,79 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch /** - * Tangem bottom sheet with custom draggable header and config - * - * @param config data model containing logic and ui models - * @param - * @param content custom bottom sheet content + * Bottom sheet with [content], [titleText] and optional [titleAction]. + * */ +@Composable +inline fun TangemBottomSheet( + config: TangemBottomSheetConfig, + titleText: TextReference, + titleAction: TopAppBarButtonUM? = null, + containerColor: Color = TangemTheme.colors.background.primary, + addBottomInsets: Boolean = true, + crossinline content: @Composable ColumnScope.(T) -> Unit, +) { + TangemBottomSheet( + config = config, + containerColor = containerColor, + addBottomInsets = addBottomInsets, + title = { TangemBottomSheetTitle(title = titleText, endButton = titleAction) }, + content = content, + ) +} + +/** + * Bottom sheet with [content] and optional [title]. */ -@OptIn(ExperimentalMaterial3Api::class) @Composable inline fun TangemBottomSheet( config: TangemBottomSheetConfig, containerColor: Color = TangemTheme.colors.background.primary, addBottomInsets: Boolean = true, + crossinline title: @Composable BoxScope.(T) -> Unit = {}, crossinline content: @Composable ColumnScope.(T) -> Unit, +) { + val isAlwaysVisible = LocalBottomSheetAlwaysVisible.current + + if (isAlwaysVisible) { + PreviewBottomSheet( + config = config, + containerColor = containerColor, + addBottomInsets = addBottomInsets, + title = title, + content = content, + ) + } else { + DefaultBottomSheet( + config = config, + containerColor = containerColor, + addBottomInsets = addBottomInsets, + title = title, + content = content, + ) + } +} + +@Composable +@OptIn(ExperimentalMaterial3Api::class) +inline fun DefaultBottomSheet( + config: TangemBottomSheetConfig, + containerColor: Color, + addBottomInsets: Boolean, + crossinline title: @Composable (BoxScope.(T) -> Unit), + crossinline content: @Composable (ColumnScope.(T) -> Unit), ) { var isVisible by remember { mutableStateOf(value = config.isShow) } - val statusBarHeight = with(LocalDensity.current) { WindowInsets.statusBars.getTop(this).toDp() } val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + if (isVisible && config.content is T) { - ModalBottomSheet( - // FIXME temporary solution to fix height of the bottom sheet - modifier = Modifier.sizeIn(maxHeight = LocalWindowSize.current.height - statusBarHeight), - onDismissRequest = config.onDismissRequest, + BasicBottomSheet( + config = config, sheetState = sheetState, containerColor = containerColor, - shape = TangemTheme.shapes.bottomSheetLarge, - windowInsets = WindowInsetsZero, - dragHandle = { TangemBottomSheetDraggableHeader(color = containerColor) }, - ) { - if (addBottomInsets) { - Column( - // FIXME temporary solution to fix height of the bottom sheet - modifier = Modifier.navigationBarsPadding(), - ) { - content(config.content) - } - } else { - content(config.content) - } - } + addBottomInsets = addBottomInsets, + title = title, + content = content, + ) } LaunchedEffect(key1 = config.isShow) { @@ -63,6 +104,74 @@ inline fun TangemBottomSheet( } } +@Composable +@OptIn(ExperimentalMaterial3Api::class) +inline fun PreviewBottomSheet( + config: TangemBottomSheetConfig, + containerColor: Color, + addBottomInsets: Boolean, + crossinline title: @Composable (BoxScope.(T) -> Unit), + crossinline content: @Composable (ColumnScope.(T) -> Unit), +) { + BasicBottomSheet( + config = config, + sheetState = SheetState( + skipPartiallyExpanded = true, + initialValue = Expanded, + density = LocalDensity.current, + ), + containerColor = containerColor, + addBottomInsets = addBottomInsets, + title = title, + content = content, + ) +} + +@Suppress("LongParameterList") +@OptIn(ExperimentalMaterial3Api::class) +@Composable +inline fun BasicBottomSheet( + config: TangemBottomSheetConfig, + sheetState: SheetState, + containerColor: Color, + addBottomInsets: Boolean, + crossinline title: @Composable (BoxScope.(T) -> Unit), + crossinline content: @Composable (ColumnScope.(T) -> Unit), +) { + val model = config.content as? T ?: return + + val statusBarHeight = with(LocalDensity.current) { WindowInsets.statusBars.getTop(this).toDp() } + + ModalBottomSheet( + // FIXME temporary solution to fix height of the bottom sheet + modifier = Modifier.sizeIn(maxHeight = LocalWindowSize.current.height - statusBarHeight), + onDismissRequest = config.onDismissRequest, + sheetState = sheetState, + containerColor = containerColor, + shape = TangemTheme.shapes.bottomSheetLarge, + windowInsets = WindowInsetsZero, + dragHandle = { TangemBottomSheetDraggableHeader(color = containerColor) }, + ) { + Column( + modifier = Modifier.let { + if (addBottomInsets) { + it.navigationBarsPadding() + } else { + it + } + }, + ) { + Box( + modifier = Modifier.fillMaxWidth(), + ) { + title(model) + } + + content(model) + } + } +} + @OptIn(ExperimentalMaterial3Api::class) suspend fun SheetState.collapse(onCollapsed: () -> Unit) { coroutineScope { diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetDraggableHeader.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetDraggableHeader.kt index 0d2cbe9d4e..37bff1013a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetDraggableHeader.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetDraggableHeader.kt @@ -5,7 +5,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.material.Surface +import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -14,8 +14,7 @@ import com.tangem.core.ui.res.TangemTheme @Composable fun TangemBottomSheetDraggableHeader(color: Color = TangemTheme.colors.background.primary) { Surface( - modifier = Modifier - .height(TangemTheme.dimens.size20), + modifier = Modifier.height(TangemTheme.dimens.size20), color = color, ) { Box( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetTitle.kt new file mode 100644 index 0000000000..65e7c87924 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetTitle.kt @@ -0,0 +1,68 @@ +package com.tangem.core.ui.components.bottomsheets + +import android.content.res.Configuration +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.R +import com.tangem.core.ui.components.appbar.TangemTopAppBar +import com.tangem.core.ui.components.appbar.TangemTopAppBarHeight +import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview + +@Composable +fun TangemBottomSheetTitle( + title: TextReference?, + modifier: Modifier = Modifier, + endButton: TopAppBarButtonUM? = null, + containerColor: Color = Color.Transparent, +) { + TangemBottomSheetTitle( + modifier = modifier, + title = title?.resolveReference(), + endButton = endButton, + containerColor = containerColor, + ) +} + +@Composable +fun TangemBottomSheetTitle( + title: String?, + modifier: Modifier = Modifier, + endButton: TopAppBarButtonUM? = null, + containerColor: Color = Color.Transparent, +) { + TangemTopAppBar( + modifier = modifier, + title = title, + endButton = endButton, + textColor = TangemTheme.colors.text.primary1, + iconTint = TangemTheme.colors.icon.informative, + titleAlignment = Alignment.CenterHorizontally, + containerColor = containerColor, + height = TangemTopAppBarHeight.BOTTOM_SHEET, + ) +} + +// region Preview +@Composable +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun Preview_TangemBottomSheetTitle() { + TangemThemePreview { + TangemBottomSheetTitle( + title = "Title", + endButton = TopAppBarButtonUM( + iconRes = R.drawable.ic_information_24, + onIconClicked = {}, + ), + containerColor = TangemTheme.colors.background.secondary, + ) + } +} +// endregion Preview \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemePreview.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemePreview.kt index 2135bbdff8..c3ed096dce 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemePreview.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemePreview.kt @@ -3,6 +3,9 @@ package com.tangem.core.ui.res import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ProvidableCompositionLocal +import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.platform.LocalHapticFeedback import com.tangem.core.ui.haptic.MockHapticManager import com.tangem.core.ui.windowsize.rememberWindowSizePreview @@ -12,18 +15,30 @@ fun TangemThemePreview( isDark: Boolean? = null, typography: TangemTypography = TangemTheme.typography, dimens: TangemDimens = TangemTheme.dimens, + alwaysShowBottomSheets: Boolean = true, content: @Composable () -> Unit, ) { val isDarkTheme = isDark ?: isSystemInDarkTheme() - BoxWithConstraints { - TangemTheme( - isDark = isDarkTheme, - typography = typography, - dimens = dimens, - windowSize = rememberWindowSizePreview(maxWidth, maxHeight), - hapticManager = MockHapticManager(LocalHapticFeedback.current), - content = content, - ) + CompositionLocalProvider( + LocalBottomSheetAlwaysVisible provides alwaysShowBottomSheets, + ) { + BoxWithConstraints { + TangemTheme( + isDark = isDarkTheme, + typography = typography, + dimens = dimens, + windowSize = rememberWindowSizePreview(maxWidth, maxHeight), + hapticManager = MockHapticManager(LocalHapticFeedback.current), + content = content, + ) + } } +} + +/** + * This is used to make the bottom sheet always visible in the Preview and should be `true` only in the Preview. + * */ +val LocalBottomSheetAlwaysVisible: ProvidableCompositionLocal = compositionLocalOf { + false } \ No newline at end of file