From 164030dc9c501a9d70cf0b833ca7e7c45d8b45d7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 20 Jun 2024 10:25:11 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../block/information/InformationBlock.kt | 193 ++++++++++++++++++ .../information/InformationBlockContent.kt | 77 +++++++ .../core/ui/components/buttons/SmallButton.kt | 98 +++++++-- .../buttons/common/TangemButtonColors.kt | 2 +- .../core/ui/components/text/TooltipText.kt | 91 +++++++++ 5 files changed, 442 insertions(+), 19 deletions(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlock.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlockContent.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/text/TooltipText.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlock.kt b/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlock.kt new file mode 100644 index 0000000000..110b0048aa --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlock.kt @@ -0,0 +1,193 @@ +package com.tangem.core.ui.components.block.information + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.R +import com.tangem.core.ui.components.buttons.SecondarySmallButton +import com.tangem.core.ui.components.buttons.SmallButtonConfig +import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition +import com.tangem.core.ui.components.text.TooltipText +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import kotlinx.collections.immutable.persistentListOf + +@Immutable +class InformationBlockContentScope(val scope: BoxScope) : BoxScope by scope + +@Composable +fun InformationBlock( + title: @Composable BoxScope.() -> Unit, + modifier: Modifier = Modifier, + action: (@Composable BoxScope.() -> Unit)? = null, + content: (@Composable InformationBlockContentScope.() -> Unit)? = null, +) { + Column( + modifier = modifier + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(color = TangemTheme.colors.background.action), + horizontalAlignment = Alignment.Start, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size40) + .padding( + top = TangemTheme.dimens.spacing12, + bottom = TangemTheme.dimens.spacing6, + ) + .padding(horizontal = TangemTheme.dimens.spacing12), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = Modifier + .weight(weight = 1f) + .heightIn(min = TangemTheme.dimens.size20), + contentAlignment = Alignment.CenterStart, + content = title, + ) + if (action != null) { + Spacer(modifier = Modifier.size(TangemTheme.dimens.spacing8)) + Box( + modifier = Modifier + .weight(weight = 1f) + .heightIn(min = TangemTheme.dimens.size24), + contentAlignment = Alignment.CenterEnd, + content = action, + ) + } + } + + if (content != null) { + Box( + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing12) + .fillMaxWidth(), + ) { + val scope = InformationBlockContentScope(scope = this) + content(scope) + } + } + } +} + +// region Previews +@Composable +@Preview(showBackground = true, widthDp = 328) +@Preview(showBackground = true, widthDp = 328, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun Preview_Grid() { + TangemThemePreview { + InformationBlock( + title = { + TooltipText( + text = stringReference("Grid title"), + onInfoClick = { }, + ) + }, + content = { + GridItems( + items = persistentListOf( + stringReference("Fist item"), + stringReference("Second item"), + ), + itemContent = { + PreviewItem(text = it) + }, + ) + }, + ) + } +} + +@Composable +@Preview(showBackground = true, widthDp = 328) +@Preview(showBackground = true, widthDp = 328, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun Preview_List() { + TangemThemePreview { + InformationBlock( + title = { + Text( + text = "List", + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + }, + action = { + SecondarySmallButton( + config = SmallButtonConfig( + text = stringReference("Add token"), + icon = TangemButtonIconPosition.Start(R.drawable.ic_plus_24), + onClick = {}, + ), + ) + }, + content = { + ListItems( + items = persistentListOf( + stringReference("Fist item"), + stringReference("Second item"), + ), + itemContent = { + PreviewItem(it) + }, + ) + }, + ) + } +} + +@Composable +@Preview(showBackground = true, widthDp = 328) +@Preview(showBackground = true, widthDp = 328, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun Preview_Plain() { + TangemThemePreview { + InformationBlock( + title = { + Column( + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4), + ) { + TooltipText( + text = stringReference("Title"), + onInfoClick = { /* [REDACTED_TODO_COMMENT]*/ }, + ) + + Text( + text = "Subtitle", + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.tertiary, + ) + } + }, + action = { + PreviewItem(text = stringReference("Action")) + }, + ) + } +} + +@Composable +private fun PreviewItem(text: TextReference) { + Box( + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors.background.secondary) + .padding(all = TangemTheme.dimens.spacing12), + ) { + Text( + text = text.resolveReference(), + color = TangemTheme.colors.text.primary1, + ) + } +} +// endregion \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlockContent.kt b/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlockContent.kt new file mode 100644 index 0000000000..02b783e839 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlockContent.kt @@ -0,0 +1,77 @@ +package com.tangem.core.ui.components.block.information + +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.util.fastForEach +import com.tangem.core.ui.res.TangemTheme +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList + +@Composable +fun InformationBlockContentScope.ListItems( + items: ImmutableList, + itemContent: @Composable BoxScope.(T) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .padding(vertical = TangemTheme.dimens.spacing8) + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8, Alignment.Top), + ) { + items.fastForEach { item -> + Box( + modifier = Modifier.fillMaxWidth(), + ) { + itemContent(item) + } + } + } +} + +@Composable +fun InformationBlockContentScope.GridItems( + items: ImmutableList, + itemContent: @Composable BoxScope.(T) -> Unit, + modifier: Modifier = Modifier, +) { + val rowItems by remember(items) { + derivedStateOf { + items.asSequence() + .windowed(size = 2, step = 2, partialWindows = true) + .map { it.toImmutableList() } + .toImmutableList() + } + } + + Column( + modifier = modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Top, + ) { + rowItems.fastForEach { row -> + Row( + modifier = Modifier + .padding(vertical = TangemTheme.dimens.spacing8) + .fillMaxWidth(), + verticalAlignment = Alignment.Top, + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12, Alignment.Start), + ) { + row.fastForEach { item -> + Box( + modifier = Modifier.weight(1f), + contentAlignment = Alignment.Center, + ) { + itemContent(item) + } + } + } + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/SmallButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/SmallButton.kt index 15f640bdbf..e02ae97109 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/SmallButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/SmallButton.kt @@ -6,17 +6,21 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text +import androidx.compose.material3.Icon +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.R +import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference -import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview /** * Small button config @@ -28,6 +32,7 @@ import com.tangem.core.ui.res.TangemTheme data class SmallButtonConfig( val text: TextReference, val onClick: () -> Unit, + val icon: TangemButtonIconPosition = TangemButtonIconPosition.None, ) /** @@ -61,13 +66,12 @@ private fun SmallButton(config: SmallButtonConfig, isPrimary: Boolean, modifier: label = "Update background color", ) - val textColor by animateColorAsState( - targetValue = if (isPrimary) TangemTheme.colors.text.primary2 else TangemTheme.colors.text.primary1, - label = "Update text color", - ) - Box( + Row( modifier = modifier - .defaultMinSize(minWidth = TangemTheme.dimens.size46, minHeight = TangemTheme.dimens.size24) + .defaultMinSize( + minWidth = TangemTheme.dimens.size46, + minHeight = TangemTheme.dimens.size24, + ) .clip(shape) .background( color = backgroundColor, @@ -75,22 +79,68 @@ private fun SmallButton(config: SmallButtonConfig, isPrimary: Boolean, modifier: ) .clickable(enabled = true, onClick = config.onClick) .padding( - vertical = TangemTheme.dimens.spacing2, + paddingValues = when (config.icon) { + is TangemButtonIconPosition.None -> PaddingValues( + horizontal = TangemTheme.dimens.spacing12, + ) + is TangemButtonIconPosition.End -> PaddingValues( + start = TangemTheme.dimens.spacing12, + end = TangemTheme.dimens.spacing8, + ) + is TangemButtonIconPosition.Start -> PaddingValues( + start = TangemTheme.dimens.spacing8, + end = TangemTheme.dimens.spacing12, + ) + }, ), - contentAlignment = Alignment.Center, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, ) { - Text( - modifier = Modifier.padding( - horizontal = TangemTheme.dimens.spacing10, - ), - text = config.text.resolveReference(), - color = textColor, - maxLines = 1, - style = TangemTheme.typography.button, + ContentContainer( + iconPosition = config.icon, + text = { + val textColor by animateColorAsState( + targetValue = if (isPrimary) TangemTheme.colors.text.primary2 else TangemTheme.colors.text.primary1, + label = "Update text color", + ) + + Text( + modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing4), + text = config.text.resolveReference(), + color = textColor, + maxLines = 1, + style = TangemTheme.typography.button, + ) + }, + icon = { iconResId -> + Icon( + modifier = Modifier.size(TangemTheme.dimens.size16), + painter = painterResource(id = iconResId), + tint = TangemTheme.colors.icon.secondary, + contentDescription = null, + ) + }, ) } } +@Composable +private fun RowScope.ContentContainer( + iconPosition: TangemButtonIconPosition, + text: @Composable RowScope.() -> Unit, + icon: @Composable RowScope.(Int) -> Unit, +) { + if (iconPosition is TangemButtonIconPosition.Start) { + icon(iconPosition.iconResId) + Spacer(modifier = Modifier.requiredWidth(TangemTheme.dimens.spacing4)) + } + text() + if (iconPosition is TangemButtonIconPosition.End) { + Spacer(modifier = Modifier.requiredWidth(TangemTheme.dimens.spacing4)) + icon(iconPosition.iconResId) + } +} + @Preview(showBackground = true) @Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable @@ -112,5 +162,17 @@ private fun ButtonsSample() { ) PrimarySmallButton(config = config) SecondarySmallButton(config = config.copy(text = TextReference.Str(value = "Add"))) + SecondarySmallButton( + config = config.copy( + text = TextReference.Str(value = "Rating"), + icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_chevron_24), + ), + ) + SecondarySmallButton( + config = config.copy( + text = TextReference.Str(value = "Add token"), + icon = TangemButtonIconPosition.Start(iconResId = R.drawable.ic_plus_24), + ), + ) } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonColors.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonColors.kt index 344811dac2..f4235c7619 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonColors.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonColors.kt @@ -6,7 +6,7 @@ import androidx.compose.runtime.State import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.graphics.Color -class TangemButtonColors( +data class TangemButtonColors( private val backgroundColor: Color, private val contentColor: Color, private val disabledBackgroundColor: Color, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/text/TooltipText.kt b/core/ui/src/main/java/com/tangem/core/ui/components/text/TooltipText.kt new file mode 100644 index 0000000000..110080210d --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/text/TooltipText.kt @@ -0,0 +1,91 @@ +package com.tangem.core.ui.components.text + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.R +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview + +@Composable +fun TooltipText( + text: TextReference, + onInfoClick: () -> Unit, + modifier: Modifier = Modifier, + useSmallerText: Boolean = false, +) { + val interactionSource = remember { MutableInteractionSource() } + + Row( + modifier = modifier + .clickable( + interactionSource = interactionSource, + indication = null, + onClick = onInfoClick, + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceAround, + ) { + Text( + modifier = Modifier.weight(1f, fill = false), + text = text.resolveReference(), + style = if (useSmallerText) { + TangemTheme.typography.caption2 + } else { + TangemTheme.typography.subtitle2 + }, + color = TangemTheme.colors.text.tertiary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + + IconButton( + modifier = Modifier.requiredSize(TangemTheme.dimens.size24), + interactionSource = interactionSource, + onClick = onInfoClick, + ) { + Icon( + modifier = Modifier.size(TangemTheme.dimens.size16), + painter = painterResource(id = R.drawable.ic_information_24), + tint = TangemTheme.colors.icon.informative, + contentDescription = null, + ) + } + } +} + +// region Preview +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun Preview_TooltipText() { + TangemThemePreview { + Box( + modifier = Modifier + .width(width = 90.dp) + .background(color = TangemTheme.colors.background.primary), + ) { + TooltipText( + text = stringReference("Text"), + onInfoClick = { /* [REDACTED_TODO_COMMENT]*/ }, + ) + } + } +} +// endregion Preview \ No newline at end of file