Updated on 2026-08-14

This commit is contained in:
Tangem 2024-06-20 10:25:11 +04:00
parent 3acfadfb08
commit 164030dc9c
5 changed files with 442 additions and 19 deletions

View file

@ -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

View file

@ -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 <T : Any> InformationBlockContentScope.ListItems(
items: ImmutableList<T>,
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 <T : Any> InformationBlockContentScope.GridItems(
items: ImmutableList<T>,
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)
}
}
}
}
}
}

View file

@ -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),
),
)
}
}

View file

@ -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,

View file

@ -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