Updated on 2026-08-14
This commit is contained in:
parent
625cf611f2
commit
0c033feefe
1 changed files with 321 additions and 0 deletions
321
core/ui/src/main/java/com/tangem/core/ui/ds/badge/TangemBadge.kt
Normal file
321
core/ui/src/main/java/com/tangem/core/ui/ds/badge/TangemBadge.kt
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
package com.tangem.core.ui.ds.badge
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.ds.badge.TangemBadgeSize.*
|
||||
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.TangemThemePreviewRedesign
|
||||
|
||||
/**
|
||||
* Tangem badge component to display a small piece of information with optional icon.
|
||||
* [Figma](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8441-83535&m=dev)
|
||||
*
|
||||
* @param text TextReference for the badge label.
|
||||
* @param modifier Modifier to be applied to the badge.
|
||||
* @param iconRes Drawable resource ID for the icon to be displayed in the badge.
|
||||
* @param size [TangemBadgeSize] defining the size of the badge.
|
||||
* @param shape [TangemBadgeShape] defining the shape of the badge.
|
||||
* @param color [TangemBadgeColor] defining the color scheme of the badge.
|
||||
* @param type [TangemBadgeType] defining the style of the badge.
|
||||
* @param iconPosition [TangemBadgeIconPosition] defining icon position of the badge.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
fun TangemBadge(
|
||||
text: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
@DrawableRes iconRes: Int? = null,
|
||||
size: TangemBadgeSize = X9,
|
||||
shape: TangemBadgeShape = TangemBadgeShape.Default,
|
||||
color: TangemBadgeColor = TangemBadgeColor.Gray,
|
||||
type: TangemBadgeType = TangemBadgeType.Solid,
|
||||
iconPosition: TangemBadgeIconPosition = TangemBadgeIconPosition.Start,
|
||||
) {
|
||||
val iconColor = getIconColor(type = type, color = color)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(size.toContentPadding()),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier
|
||||
.heightIn(min = size.toHeightDp())
|
||||
.clip(shape.toShape(size))
|
||||
.getBackgroundColor(type = type, color = color, shape = shape.toShape(size))
|
||||
.padding(size.toPaddingDp(position = iconPosition)),
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = iconRes != null && iconPosition == TangemBadgeIconPosition.Start,
|
||||
modifier = Modifier.size(size = size.toContentSize()),
|
||||
label = "Start Icon Visibility",
|
||||
) {
|
||||
val wrappedIconRes = remember(this) { requireNotNull(iconRes) }
|
||||
Icon(
|
||||
painter = painterResource(id = wrappedIconRes),
|
||||
contentDescription = null,
|
||||
tint = iconColor,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
style = size.toTextStyle(),
|
||||
maxLines = 1,
|
||||
color = getTextColor(type = type, color = color),
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = iconRes != null && iconPosition == TangemBadgeIconPosition.End,
|
||||
modifier = Modifier.size(size = size.toContentSize()),
|
||||
label = "End Icon Visibility",
|
||||
) {
|
||||
val wrappedIconRes = remember(this) { requireNotNull(iconRes) }
|
||||
Icon(
|
||||
painter = painterResource(id = wrappedIconRes),
|
||||
contentDescription = null,
|
||||
tint = iconColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tangem badge shape options.
|
||||
*/
|
||||
enum class TangemBadgeShape {
|
||||
Default,
|
||||
Rounded,
|
||||
;
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
internal fun toShape(size: TangemBadgeSize) = RoundedCornerShape(
|
||||
when (this) {
|
||||
Rounded -> when (size) {
|
||||
X4,
|
||||
X6,
|
||||
-> TangemTheme.dimens2.x4
|
||||
X9 -> TangemTheme.dimens2.x25
|
||||
}
|
||||
Default -> when (size) {
|
||||
X4 -> TangemTheme.dimens2.x1
|
||||
X6,
|
||||
X9,
|
||||
-> 6.dp
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tangem badge size options.
|
||||
*/
|
||||
enum class TangemBadgeSize {
|
||||
X4,
|
||||
X6,
|
||||
X9,
|
||||
;
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
internal fun toHeightDp() = when (this) {
|
||||
X4 -> TangemTheme.dimens2.x4
|
||||
X6 -> TangemTheme.dimens2.x6
|
||||
X9 -> TangemTheme.dimens2.x9
|
||||
}
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
internal fun toPaddingDp(position: TangemBadgeIconPosition) = when (this) {
|
||||
X4 -> when (position) {
|
||||
TangemBadgeIconPosition.Start -> PaddingValues(start = 4.dp, end = 6.dp)
|
||||
TangemBadgeIconPosition.End -> PaddingValues(start = 6.dp, end = 4.dp)
|
||||
}
|
||||
X6 -> when (position) {
|
||||
TangemBadgeIconPosition.Start -> PaddingValues(start = 8.dp, end = 12.dp)
|
||||
TangemBadgeIconPosition.End -> PaddingValues(start = 12.dp, end = 8.dp)
|
||||
}
|
||||
X9 -> when (position) {
|
||||
TangemBadgeIconPosition.Start -> PaddingValues(start = 12.dp, end = 16.dp)
|
||||
TangemBadgeIconPosition.End -> PaddingValues(start = 16.dp, end = 12.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
internal fun toContentSize() = when (this) {
|
||||
X4 -> TangemTheme.dimens2.x3
|
||||
X6,
|
||||
X9,
|
||||
-> TangemTheme.dimens2.x4
|
||||
}
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
internal fun toContentPadding() = when (this) {
|
||||
X4 -> TangemTheme.dimens2.x0_5
|
||||
X6,
|
||||
X9,
|
||||
-> TangemTheme.dimens2.x1
|
||||
}
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
internal fun toTextStyle() = when (this) {
|
||||
X4 -> TangemTheme.typography2.captionSemibold11
|
||||
X6 -> TangemTheme.typography2.captionSemibold12
|
||||
X9 -> TangemTheme.typography2.bodySemibold16
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Position of the icon in the Tangem badge.
|
||||
*/
|
||||
enum class TangemBadgeIconPosition {
|
||||
Start,
|
||||
End,
|
||||
}
|
||||
|
||||
/**
|
||||
* Tangem badge type options.
|
||||
*/
|
||||
enum class TangemBadgeType {
|
||||
Solid,
|
||||
Tinted,
|
||||
Outline,
|
||||
}
|
||||
|
||||
/**
|
||||
* Tangem badge color options.
|
||||
*/
|
||||
enum class TangemBadgeColor {
|
||||
Blue,
|
||||
Red,
|
||||
Gray,
|
||||
}
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
private fun getIconColor(type: TangemBadgeType, color: TangemBadgeColor) = when (color) {
|
||||
TangemBadgeColor.Gray -> TangemTheme.colors2.markers.iconGray
|
||||
TangemBadgeColor.Blue -> when (type) {
|
||||
TangemBadgeType.Outline,
|
||||
TangemBadgeType.Tinted,
|
||||
-> TangemTheme.colors2.markers.iconBlue
|
||||
TangemBadgeType.Solid -> TangemTheme.colors2.graphic.neutral.primaryInvertedConstant
|
||||
}
|
||||
TangemBadgeColor.Red -> when (type) {
|
||||
TangemBadgeType.Outline,
|
||||
TangemBadgeType.Tinted,
|
||||
-> TangemTheme.colors2.markers.iconRed
|
||||
TangemBadgeType.Solid -> TangemTheme.colors2.graphic.neutral.primaryInvertedConstant
|
||||
}
|
||||
}
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
private fun getTextColor(type: TangemBadgeType, color: TangemBadgeColor) = when (color) {
|
||||
TangemBadgeColor.Gray -> TangemTheme.colors2.markers.textGray
|
||||
TangemBadgeColor.Blue -> when (type) {
|
||||
TangemBadgeType.Outline,
|
||||
TangemBadgeType.Tinted,
|
||||
-> TangemTheme.colors2.markers.textBlue
|
||||
TangemBadgeType.Solid -> TangemTheme.colors2.text.neutral.primaryInvertedConstant
|
||||
}
|
||||
TangemBadgeColor.Red -> when (type) {
|
||||
TangemBadgeType.Outline,
|
||||
TangemBadgeType.Tinted,
|
||||
-> TangemTheme.colors2.markers.textRed
|
||||
TangemBadgeType.Solid -> TangemTheme.colors2.text.neutral.primaryInvertedConstant
|
||||
}
|
||||
}
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
private fun Modifier.getBackgroundColor(type: TangemBadgeType, color: TangemBadgeColor, shape: Shape) = when (type) {
|
||||
TangemBadgeType.Solid -> background(
|
||||
when (color) {
|
||||
TangemBadgeColor.Gray -> TangemTheme.colors2.markers.backgroundSolidGray
|
||||
TangemBadgeColor.Blue -> TangemTheme.colors2.markers.backgroundSolidBlue
|
||||
TangemBadgeColor.Red -> TangemTheme.colors2.markers.backgroundSolidRed
|
||||
},
|
||||
)
|
||||
TangemBadgeType.Tinted -> background(
|
||||
when (color) {
|
||||
TangemBadgeColor.Gray -> TangemTheme.colors2.markers.backgroundTintedGray
|
||||
TangemBadgeColor.Blue -> TangemTheme.colors2.markers.backgroundTintedBlue
|
||||
TangemBadgeColor.Red -> TangemTheme.colors2.markers.backgroundTintedRed
|
||||
},
|
||||
)
|
||||
TangemBadgeType.Outline -> {
|
||||
border(
|
||||
color = when (color) {
|
||||
TangemBadgeColor.Gray -> TangemTheme.colors2.markers.borderGray
|
||||
TangemBadgeColor.Blue -> TangemTheme.colors2.markers.borderTintedBlue
|
||||
TangemBadgeColor.Red -> TangemTheme.colors2.markers.borderTintedRed
|
||||
},
|
||||
shape = shape,
|
||||
width = 1.dp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun TangemBadge_Preview(@PreviewParameter(TangemBadgePreviewProvider::class) params: TangemBadgeColor) {
|
||||
TangemThemePreviewRedesign {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors2.surface.level1)
|
||||
.padding(8.dp),
|
||||
) {
|
||||
repeat(2) { yIndex ->
|
||||
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
repeat(TangemBadgeType.entries.size) { index ->
|
||||
TangemBadge(
|
||||
text = stringReference("Title"),
|
||||
iconRes = R.drawable.ic_information_24,
|
||||
type = TangemBadgeType.entries[index],
|
||||
color = params,
|
||||
shape = TangemBadgeShape.entries[yIndex % 2],
|
||||
iconPosition = TangemBadgeIconPosition.entries[yIndex % 2],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TangemBadgePreviewProvider : PreviewParameterProvider<TangemBadgeColor> {
|
||||
override val values: Sequence<TangemBadgeColor>
|
||||
get() = sequenceOf(
|
||||
TangemBadgeColor.Gray,
|
||||
TangemBadgeColor.Blue,
|
||||
TangemBadgeColor.Red,
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
Loading…
Add table
Add a link
Reference in a new issue