Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-09 17:38:17 +05:00
parent 2396225311
commit 23ddf1a665
34 changed files with 1301 additions and 226 deletions

View file

@ -220,6 +220,8 @@ fun SecondaryButtonIconEnd(
modifier: Modifier = Modifier,
showProgress: Boolean = false,
enabled: Boolean = true,
size: TangemButtonSize = TangemButtonSize.Default,
shape: Shape = size.toShape(),
) {
TangemButton(
modifier = modifier,
@ -230,6 +232,8 @@ fun SecondaryButtonIconEnd(
enabled = enabled,
showProgress = showProgress,
textStyle = TangemTheme.typography.subtitle1,
size = size,
shape = shape,
)
}
@ -244,6 +248,8 @@ fun SecondaryButtonIconStart(
modifier: Modifier = Modifier,
showProgress: Boolean = false,
enabled: Boolean = true,
size: TangemButtonSize = TangemButtonSize.Default,
shape: Shape = size.toShape(),
) {
TangemButton(
modifier = modifier,
@ -254,6 +260,8 @@ fun SecondaryButtonIconStart(
enabled = enabled,
showProgress = showProgress,
textStyle = TangemTheme.typography.subtitle1,
size = size,
shape = shape,
)
}
// endregion SecondaryButton

View file

@ -17,11 +17,15 @@ 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.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.label.entity.LabelLeadingContentUM
import com.tangem.core.ui.components.label.entity.LabelSize
import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.TextReference
@ -37,6 +41,7 @@ import com.tangem.core.ui.res.TangemThemePreview
*
* @see <a href="https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=4480-1459&t=2QTpi1G7FeTexTFS-4">Figma</a>
*/
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable
fun Label(state: LabelUM, modifier: Modifier = Modifier) {
val backgroundColor by animateColorAsState(
@ -63,12 +68,28 @@ fun Label(state: LabelUM, modifier: Modifier = Modifier) {
},
)
AnimatedContent(targetState = state.text) { text ->
val horizontalArrangementSize = remember {
when (state.size) {
LabelSize.REGULAR -> 4.dp
LabelSize.BIG -> 8.dp
}
}
val paddings = remember {
when (state.size) {
LabelSize.REGULAR -> PaddingValues(horizontal = 8.dp, vertical = 4.dp)
LabelSize.BIG -> PaddingValues(horizontal = 16.dp, vertical = 8.dp)
}
}
AnimatedContent(
modifier = modifier,
targetState = state.text,
) { text ->
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp),
modifier = modifier
.padding(horizontal = 4.dp)
horizontalArrangement = Arrangement.spacedBy(horizontalArrangementSize),
modifier = Modifier
.clip(TangemTheme.shapes.roundedCorners8)
.background(color = backgroundColor)
.then(
@ -82,8 +103,22 @@ fun Label(state: LabelUM, modifier: Modifier = Modifier) {
Modifier
},
)
.padding(horizontal = 8.dp, vertical = 4.dp),
.padding(paddings),
) {
state.leadingContent.let { leadingContentUM ->
when (leadingContentUM) {
is LabelLeadingContentUM.Token -> {
Icon(
modifier = Modifier
.size(16.dp),
painter = painterResource(leadingContentUM.icon),
tint = Color.Unspecified,
contentDescription = null,
)
}
LabelLeadingContentUM.None -> Unit
}
}
Text(
modifier = Modifier.weight(1.0f, fill = false),
text = text.resolveReference(),
@ -109,6 +144,8 @@ fun Label(state: LabelUM, modifier: Modifier = Modifier) {
}
}
@Suppress("LongMethod")
@OptIn(ExperimentalLayoutApi::class)
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
@ -118,47 +155,93 @@ private fun LabelPreview() {
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.padding(16.dp),
) {
Label(
state = LabelUM(
text = TextReference.Str("Regular Label"),
style = LabelStyle.REGULAR,
),
)
Label(
state = LabelUM(
text = TextReference.Str("Accent Label"),
style = LabelStyle.ACCENT,
),
)
Label(
state = LabelUM(
text = TextReference.Str("Warning Label"),
style = LabelStyle.WARNING,
),
)
Label(
state = LabelUM(
text = TextReference.Str(
"Regular long long long long long long long long long long long long Label",
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Label(
state = LabelUM(
text = TextReference.Str("Regular Label"),
style = LabelStyle.REGULAR,
),
style = LabelStyle.REGULAR,
icon = R.drawable.ic_information_24,
),
)
Label(
state = LabelUM(
text = TextReference.Str("Accent Label"),
style = LabelStyle.ACCENT,
icon = R.drawable.ic_information_24,
),
)
Label(
state = LabelUM(
text = TextReference.Str("Warning Label"),
style = LabelStyle.WARNING,
icon = R.drawable.ic_information_24,
),
)
)
Label(
state = LabelUM(
leadingContent = LabelLeadingContentUM.Token(
icon = R.drawable.img_bsc_22,
),
text = TextReference.Str("Regular Label"),
style = LabelStyle.REGULAR,
),
)
Label(
state = LabelUM(
text = TextReference.Str("Accent Label"),
style = LabelStyle.ACCENT,
),
)
Label(
state = LabelUM(
text = TextReference.Str("Warning Label"),
style = LabelStyle.WARNING,
),
)
}
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Label(
state = LabelUM(
text = TextReference.Str(
"Regular long long long long long long long long long long long long Label",
),
style = LabelStyle.REGULAR,
icon = R.drawable.ic_information_24,
),
)
Label(
state = LabelUM(
text = TextReference.Str("Accent Label"),
style = LabelStyle.ACCENT,
icon = R.drawable.ic_information_24,
),
)
Label(
state = LabelUM(
text = TextReference.Str("Warning Label"),
style = LabelStyle.WARNING,
icon = R.drawable.ic_information_24,
),
)
}
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Label(
state = LabelUM(
text = TextReference.Str("Regular Label"),
style = LabelStyle.REGULAR,
size = LabelSize.BIG,
),
)
Label(
state = LabelUM(
text = TextReference.Str("Accent Label"),
style = LabelStyle.ACCENT,
size = LabelSize.BIG,
icon = R.drawable.ic_information_24,
),
)
Label(
state = LabelUM(
text = TextReference.Str("Warning Label"),
style = LabelStyle.WARNING,
size = LabelSize.BIG,
),
)
}
}
}
}

View file

@ -1,16 +1,29 @@
package com.tangem.core.ui.components.label.entity
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
data class LabelUM(
val text: TextReference,
val style: LabelStyle,
val style: LabelStyle = LabelStyle.REGULAR,
val size: LabelSize = LabelSize.REGULAR,
val leadingContent: LabelLeadingContentUM = LabelLeadingContentUM.None,
@DrawableRes val icon: Int? = null,
val onIconClick: (() -> Unit)? = null,
val onClick: (() -> Unit)? = null,
)
@Immutable
sealed class LabelLeadingContentUM {
data object None : LabelLeadingContentUM()
data class Token(val icon: Int) : LabelLeadingContentUM()
}
enum class LabelStyle {
REGULAR, ACCENT, WARNING,
}
enum class LabelSize {
REGULAR, BIG,
}

View file

@ -0,0 +1,130 @@
package com.tangem.core.ui.components.pager
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
/**
* Horizontal pager indicator
*
* @param pagerState state of pager
* @param indicatorCount counter of visible indicator items
*/
@Composable
fun PagerIndicator(pagerState: PagerState, modifier: Modifier = Modifier, indicatorCount: Int = 5) {
val listState = rememberLazyListState()
val indicatorColor = TangemTheme.colors.control.key
val overlayColor = TangemTheme.colors.overlay.secondary
val indicatorSize = 8.dp
val spacing = 4.dp
val totalWidth: Dp = indicatorSize * indicatorCount + spacing * (indicatorCount - 1)
val widthInPx = LocalDensity.current.run { indicatorSize.toPx() }
val currentItem by remember {
derivedStateOf {
pagerState.currentPage
}
}
val itemCount = pagerState.pageCount
LaunchedEffect(key1 = currentItem) {
val viewportSize = listState.layoutInfo.viewportSize
listState.animateScrollToItem(
currentItem,
(widthInPx / 2 - viewportSize.width / 2).toInt(),
)
}
Box(
modifier = modifier
.height(32.dp)
.background(
color = overlayColor,
shape = CircleShape,
)
.padding(horizontal = 16.dp, vertical = 12.dp),
contentAlignment = Alignment.Center,
) {
LazyRow(
modifier = Modifier
.width(totalWidth),
state = listState,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp),
userScrollEnabled = false,
) {
indicatorItems(
itemCount = itemCount,
currentItem = currentItem,
indicatorShape = CircleShape,
activeColor = indicatorColor,
inActiveColor = indicatorColor.copy(alpha = 0.5f),
indicatorSize = indicatorSize,
)
}
}
}
@Suppress("LongParameterList")
private fun LazyListScope.indicatorItems(
itemCount: Int,
currentItem: Int,
indicatorShape: Shape,
activeColor: Color,
inActiveColor: Color,
indicatorSize: Dp,
) {
items(itemCount) { index ->
val isSelected = index == currentItem
Box(
modifier = Modifier
.clip(indicatorShape)
.size(indicatorSize)
.background(
if (isSelected) activeColor else inActiveColor,
indicatorShape,
),
)
}
}
@Preview(showBackground = true)
@Composable
private fun PagerIndicatorPreviewFirstPage() {
TangemThemePreview {
Box(
modifier = Modifier
.background(TangemTheme.colors.background.primary)
.padding(),
contentAlignment = Alignment.Center,
) {
val pagerState = rememberPagerState(
initialPage = 0,
pageCount = { 10 },
)
PagerIndicator(pagerState = pagerState)
}
}
}

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M2.667,8C2.667,5.055 5.055,2.667 8,2.667C10.946,2.667 13.333,5.055 13.333,8C13.333,10.946 10.946,13.333 8,13.333C5.055,13.333 2.667,10.946 2.667,8ZM1.333,8C1.333,11.682 4.318,14.667 8,14.667C11.682,14.667 14.667,11.682 14.667,8C14.667,4.318 11.682,1.334 8,1.334C4.318,1.334 1.333,4.318 1.333,8Z"
android:fillColor="#919191"/>
<path
android:pathData="M5.032,11.297C4.984,11.287 4.94,11.263 4.906,11.229C4.871,11.194 4.847,11.15 4.837,11.102C4.827,11.054 4.832,11.005 4.85,10.959L6.315,7.297C6.416,7.045 6.567,6.816 6.758,6.625C6.95,6.433 7.178,6.282 7.429,6.182L11.092,4.717C11.137,4.699 11.188,4.695 11.235,4.704C11.283,4.714 11.328,4.738 11.362,4.772C11.396,4.807 11.42,4.851 11.43,4.899C11.44,4.947 11.435,4.997 11.417,5.042L9.952,8.704C9.851,8.956 9.7,9.185 9.509,9.376C9.317,9.568 9.089,9.718 8.838,9.819L5.175,11.284C5.13,11.302 5.08,11.306 5.032,11.297ZM8.28,8.736C8.425,8.707 8.559,8.636 8.664,8.531C8.768,8.426 8.84,8.292 8.869,8.147C8.898,8.001 8.884,7.85 8.827,7.713C8.77,7.576 8.673,7.46 8.55,7.377C8.427,7.295 8.282,7.251 8.134,7.251C7.935,7.251 7.744,7.33 7.603,7.47C7.463,7.611 7.384,7.802 7.384,8.001C7.384,8.149 7.427,8.294 7.51,8.417C7.592,8.54 7.709,8.637 7.846,8.694C7.983,8.75 8.134,8.765 8.28,8.736Z"
android:fillColor="#919191"/>
</vector>

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<group>
<clip-path
android:pathData="M0,0h20v20h-20z"/>
<path
android:pathData="M13.75,1.75C16.73,1.75 19.083,4.093 19.084,7.083C19.084,8.9 18.257,10.495 16.961,12.081C15.673,13.657 13.843,15.315 11.713,17.247L10.505,18.347L10,18.806L9.495,18.347L8.288,17.247C6.157,15.315 4.327,13.657 3.039,12.081C1.742,10.495 0.917,8.9 0.917,7.083C0.917,4.093 3.27,1.75 6.25,1.75C7.646,1.75 8.986,2.29 10,3.174C11.013,2.29 12.353,1.75 13.75,1.75Z"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#1E1E1E"/>
</group>
</vector>

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M1.873,2.461C2.205,2.461 2.473,2.73 2.473,3.061V8.464C2.473,9.9 3.638,11.064 5.073,11.064H11.855V10.146C11.855,9.831 12.203,9.639 12.469,9.809L14.858,11.326C15.105,11.483 15.105,11.844 14.858,12.002L12.469,13.519C12.203,13.688 11.855,13.497 11.855,13.181V12.264H5.073C2.975,12.264 1.273,10.562 1.273,8.464V3.061C1.273,2.73 1.542,2.461 1.873,2.461Z"
android:fillColor="#0099FF"/>
<path
android:pathData="M9.473,6.413C9.805,6.413 10.073,6.682 10.073,7.013C10.073,7.345 9.805,7.613 9.473,7.613H5.673C5.342,7.613 5.074,7.345 5.073,7.013C5.073,6.682 5.342,6.413 5.673,6.413H9.473Z"
android:fillColor="#0099FF"/>
<path
android:pathData="M13.273,2.696C13.605,2.696 13.873,2.964 13.873,3.296C13.873,3.627 13.605,3.896 13.273,3.896H5.673C5.342,3.896 5.074,3.627 5.073,3.296C5.073,2.964 5.342,2.696 5.673,2.696H13.273Z"
android:fillColor="#0099FF"/>
</vector>