Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-04 16:05:34 +05:00
parent 1858d5453a
commit 1ffec50319
17 changed files with 305 additions and 98 deletions

View file

@ -1,22 +1,18 @@
package com.tangem.core.ui.ds.row.header
import android.content.res.Configuration
import androidx.annotation.DrawableRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
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.graphics.vector.ImageVector
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
@ -25,10 +21,13 @@ import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.ds.image.TangemIcon
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.internal.TangemRowTail
import com.tangem.core.ui.ds.row.internal.TangemRowTailUM
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.core.ui.test.TokenElementsTestTags
import org.burnoutcrew.reorderable.ReorderableLazyListState
/**
* UI model for header row component
@ -37,13 +36,19 @@ import com.tangem.core.ui.test.TokenElementsTestTags
* @param modifier Modifier for the composable
*/
@Composable
fun TangemHeaderRow(headerRowUM: TangemHeaderRowUM, modifier: Modifier = Modifier, isBalanceHidden: Boolean = false) {
fun TangemHeaderRow(
headerRowUM: TangemHeaderRowUM,
modifier: Modifier = Modifier,
reorderableState: ReorderableLazyListState? = null,
isBalanceHidden: Boolean = false,
) {
TangemHeaderRow(
headTangemIconUM = headerRowUM.startIconUM,
footerTangemIconRes = headerRowUM.endIconRes,
tailUM = headerRowUM.tailUM,
title = headerRowUM.title,
subtitle = headerRowUM.subtitle,
isBalanceHidden = isBalanceHidden,
reorderableState = reorderableState,
modifier = modifier,
)
}
@ -54,7 +59,7 @@ fun TangemHeaderRow(headerRowUM: TangemHeaderRowUM, modifier: Modifier = Modifie
* @param modifier Modifier for the composable
* @param subtitle Optional subtitle as a TextReference
* @param onItemClick Optional click callback for the row
* @param footerTangemIconRes Optional drawable resource ID for the footer icon
* @param tailUM Optional TailUM for the tail content
* @param titleContent Composable lambda for the title content
* @param headContent Composable lambda for the head content
*/
@ -64,7 +69,7 @@ fun TangemHeaderRow(
isBalanceHidden: Boolean = false,
subtitle: TextReference? = null,
onItemClick: (() -> Unit)? = null,
@DrawableRes footerTangemIconRes: Int? = null,
tailUM: TangemRowTailUM = TangemRowTailUM.Empty,
titleContent: @Composable (Modifier) -> Unit,
headContent: @Composable (Modifier) -> Unit,
) {
@ -101,17 +106,7 @@ fun TangemHeaderRow(
)
}
SpacerWMax()
AnimatedVisibility(
visible = footerTangemIconRes != null,
) {
val wrappedIconUM = remember(this) { requireNotNull(footerTangemIconRes) }
Icon(
imageVector = ImageVector.vectorResource(id = wrappedIconUM),
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.secondary,
modifier = Modifier.size(TangemTheme.dimens2.x4),
)
}
TangemRowTail(tangemRowTailUM = tailUM)
}
}
@ -133,7 +128,8 @@ fun TangemHeaderRow(
isBalanceHidden: Boolean = false,
subtitle: TextReference? = null,
headTangemIconUM: TangemIconUM? = null,
@DrawableRes footerTangemIconRes: Int? = null,
tailUM: TangemRowTailUM = TangemRowTailUM.Empty,
reorderableState: ReorderableLazyListState? = null,
isEnabled: Boolean = false,
onItemClick: (() -> Unit)? = null,
) {
@ -182,17 +178,10 @@ fun TangemHeaderRow(
)
}
SpacerWMax()
AnimatedVisibility(
visible = footerTangemIconRes != null,
) {
val wrappedIconUM = remember(this) { requireNotNull(footerTangemIconRes) }
Icon(
imageVector = ImageVector.vectorResource(id = wrappedIconUM),
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.secondary,
modifier = Modifier.size(TangemTheme.dimens2.x4),
)
}
TangemRowTail(
tangemRowTailUM = tailUM,
reorderableState = reorderableState,
)
}
}
@ -217,7 +206,25 @@ private class PreviewProvider : PreviewParameterProvider<TangemHeaderRowUM> {
startIconUM = TangemIconUM.Currency(
currencyIconState = CurrencyIconState.Locked,
),
endIconRes = R.drawable.ic_minimize_24,
tailUM = TangemRowTailUM.Empty,
title = stringReference("Account"),
subtitle = stringReference("\$ 42,900.17"),
),
TangemHeaderRowUM(
id = "1",
startIconUM = TangemIconUM.Currency(
currencyIconState = CurrencyIconState.Locked,
),
tailUM = TangemRowTailUM.Icon(R.drawable.ic_arrow_collapse_24),
title = stringReference("Account"),
subtitle = stringReference("\$ 42,900.17"),
),
TangemHeaderRowUM(
id = "1",
startIconUM = TangemIconUM.Currency(
currencyIconState = CurrencyIconState.Locked,
),
tailUM = TangemRowTailUM.Icon(R.drawable.ic_group_drop_24),
title = stringReference("Account"),
subtitle = stringReference("\$ 42,900.17"),
),

View file

@ -1,9 +1,9 @@
package com.tangem.core.ui.ds.row.header
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.TangemRowUM
import com.tangem.core.ui.ds.row.internal.TangemRowTailUM
import com.tangem.core.ui.extensions.TextReference
/**
@ -13,7 +13,7 @@ import com.tangem.core.ui.extensions.TextReference
* @param title Title text reference
* @param subtitle Subtitle text reference (optional)
* @param startIconUM Icon UI model (optional)
* @param endIconRes Icon UI model (optional)
* @param tailUM Tail UI model (optional)
* @param isEnabled Flag indicating if click is enabled
* @param onItemClick Callback for item click (optional)
*/
@ -23,7 +23,7 @@ data class TangemHeaderRowUM(
val title: TextReference,
val subtitle: TextReference? = null,
val startIconUM: TangemIconUM? = null,
@DrawableRes val endIconRes: Int? = null,
val tailUM: TangemRowTailUM = TangemRowTailUM.Empty,
val isEnabled: Boolean = false,
val onItemClick: (() -> Unit)? = null,
) : TangemRowUM

View file

@ -1,7 +1,9 @@
package com.tangem.core.ui.ds.row.token.internal
package com.tangem.core.ui.ds.row.internal
import androidx.annotation.DrawableRes
import androidx.compose.animation.AnimatedContent
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.material3.Icon
@ -9,11 +11,10 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
import com.tangem.core.ui.R
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveAnnotatedReference
import com.tangem.core.ui.res.TangemTheme
@ -22,37 +23,48 @@ import org.burnoutcrew.reorderable.ReorderableLazyListState
import org.burnoutcrew.reorderable.detectReorder
@Composable
internal fun TokenRowTail(
tailUM: TangemTokenRowUM.TailUM,
reorderableTokenListState: ReorderableLazyListState?,
internal fun TangemRowTail(
tangemRowTailUM: TangemRowTailUM,
modifier: Modifier = Modifier,
reorderableState: ReorderableLazyListState? = null,
) {
AnimatedContent(
targetState = tailUM,
targetState = tangemRowTailUM,
label = "Update non content fiat block",
modifier = modifier,
modifier = modifier.height(TangemTheme.dimens2.x4),
contentKey = { it::class.java },
) { animatedState ->
val innerModifier = Modifier.padding(start = TangemTheme.dimens2.x2)
when (animatedState) {
TangemTokenRowUM.TailUM.Empty -> Unit
is TangemTokenRowUM.TailUM.Draggable -> DraggableImage(
reorderableTokenListState = reorderableTokenListState,
TangemRowTailUM.Empty -> Unit
is TangemRowTailUM.Draggable -> DraggableImage(
iconRes = animatedState.iconRes,
reorderableState = reorderableState,
modifier = innerModifier,
)
is TangemTokenRowUM.TailUM.Text -> ContentText(text = animatedState.text, modifier = innerModifier)
is TangemRowTailUM.Text -> ContentText(text = animatedState.text, modifier = innerModifier)
is TangemRowTailUM.Icon -> Icon(
imageVector = ImageVector.vectorResource(animatedState.iconRes),
contentDescription = null,
modifier = modifier,
tint = TangemTheme.colors2.graphic.neutral.tertiaryConstant,
)
}
}
}
@Composable
private fun DraggableImage(reorderableTokenListState: ReorderableLazyListState?, modifier: Modifier = Modifier) {
private fun DraggableImage(
@DrawableRes iconRes: Int,
reorderableState: ReorderableLazyListState?,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier
.size(size = TangemTheme.dimens2.x6)
.then(
other = if (reorderableTokenListState != null) {
Modifier.detectReorder(reorderableTokenListState)
other = if (reorderableState != null) {
Modifier.detectReorder(reorderableState)
} else {
Modifier
},
@ -61,7 +73,7 @@ private fun DraggableImage(reorderableTokenListState: ReorderableLazyListState?,
contentAlignment = Alignment.Center,
) {
Icon(
painter = painterResource(id = R.drawable.ic_drag_24),
imageVector = ImageVector.vectorResource(iconRes),
tint = TangemTheme.colors2.graphic.neutral.tertiaryConstant,
contentDescription = null,
)

View file

@ -0,0 +1,25 @@
package com.tangem.core.ui.ds.row.internal
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
/**
* Tail UI model for row components
*/
@Immutable
sealed class TangemRowTailUM {
data class Text(
val text: TextReference,
) : TangemRowTailUM()
data class Icon(
@DrawableRes val iconRes: Int,
) : TangemRowTailUM()
data class Draggable(
@DrawableRes val iconRes: Int,
) : TangemRowTailUM()
data object Empty : TangemRowTailUM()
}

View file

@ -19,6 +19,7 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter
import com.tangem.core.ui.ds.image.TangemIcon
import com.tangem.core.ui.ds.row.TangemRowContainer
import com.tangem.core.ui.ds.row.TangemRowLayoutId
import com.tangem.core.ui.ds.row.internal.TangemRowTail
import com.tangem.core.ui.ds.row.token.internal.*
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
@ -30,16 +31,16 @@ import org.burnoutcrew.reorderable.ReorderableLazyListState
*
* [Token Row](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8207-17583&t=k8dyaykorsNocGVq-4)
*
* @param tokenRowUM The user model containing the data for the token row.
* @param isBalanceHidden A boolean indicating whether the balance should be hidden.
* @param reorderableTokenListState The state of the reorderable lazy list, if applicable.
* @param modifier The modifier to be applied to the row.
* @param tokenRowUM The user model containing the data for the token row.
* @param isBalanceHidden A boolean indicating whether the balance should be hidden.
* @param reorderableState The state of the reorderable lazy list, if applicable.
* @param modifier The modifier to be applied to the row.
*/
@Composable
fun TangemTokenRow(
tokenRowUM: TangemTokenRowUM,
isBalanceHidden: Boolean,
reorderableTokenListState: ReorderableLazyListState?,
reorderableState: ReorderableLazyListState?,
modifier: Modifier = Modifier,
) {
TangemRowContainer(
@ -88,9 +89,9 @@ fun TangemTokenRow(
.testTag(tag = TokenElementsTestTags.TOKEN_CRYPTO_AMOUNT),
)
TokenRowTail(
tailUM = tokenRowUM.tailUM,
reorderableTokenListState = reorderableTokenListState,
TangemRowTail(
tangemRowTailUM = tokenRowUM.tailUM,
reorderableState = reorderableState,
modifier = Modifier
.layoutId(layoutId = TangemRowLayoutId.TAIL)
.testTag(tag = TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK),
@ -114,18 +115,18 @@ fun TangemTokenRow(
*
* [Token Row](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8207-17583&t=k8dyaykorsNocGVq-4)
*
* @param tokenRowUM The user model containing the data for the token row.
* @param headComponent The composable function representing the head component.
* @param titleComponent The composable function representing the title component.
* @param isBalanceHidden A boolean indicating whether the balance should be hidden.
* @param reorderableTokenListState The state of the reorderable lazy list, if applicable.
* @param modifier The modifier to be applied to the row.
* @param tokenRowUM The user model containing the data for the token row.
* @param headComponent The composable function representing the head component.
* @param titleComponent The composable function representing the title component.
* @param isBalanceHidden A boolean indicating whether the balance should be hidden.
* @param reorderableState The state of the reorderable lazy list, if applicable.
* @param modifier The modifier to be applied to the row.
*/
@Composable
fun TangemTokenRow(
tokenRowUM: TangemTokenRowUM,
isBalanceHidden: Boolean,
reorderableTokenListState: ReorderableLazyListState?,
reorderableState: ReorderableLazyListState?,
modifier: Modifier = Modifier,
headComponent: @Composable (Modifier) -> Unit,
titleComponent: @Composable (Modifier) -> Unit,
@ -183,9 +184,9 @@ fun TangemTokenRow(
.testTag(tag = TokenElementsTestTags.TOKEN_CRYPTO_AMOUNT),
)
TokenRowTail(
tailUM = tokenRowUM.tailUM,
reorderableTokenListState = reorderableTokenListState,
TangemRowTail(
tangemRowTailUM = tokenRowUM.tailUM,
reorderableState = reorderableState,
modifier = Modifier
.layoutId(layoutId = TangemRowLayoutId.TAIL)
.testTag(tag = TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK),
@ -232,7 +233,7 @@ private fun TangemTokenRow_Preview(
TangemTokenRow(
tokenRowUM = tokenRowUM,
isBalanceHidden = false,
reorderableTokenListState = null,
reorderableState = null,
modifier = Modifier.background(TangemTheme.colors2.surface.level1),
)
}

View file

@ -6,6 +6,7 @@ import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.ds.badge.TangemBadgeUM
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.TangemRowUM
import com.tangem.core.ui.ds.row.internal.TangemRowTailUM
import com.tangem.core.ui.extensions.TextReference
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@ -32,7 +33,7 @@ sealed class TangemTokenRowUM : TangemRowUM {
abstract val bottomEndContentUM: EndContentUM
/** Token row tail UM */
abstract val tailUM: TailUM
abstract val tailUM: TangemRowTailUM
/** Promo banner UM */
abstract val promoBannerUM: PromoBannerUM
@ -54,7 +55,7 @@ sealed class TangemTokenRowUM : TangemRowUM {
override val topEndContentUM: EndContentUM,
override val bottomEndContentUM: EndContentUM,
override val promoBannerUM: PromoBannerUM = PromoBannerUM.Empty,
override val tailUM: TailUM = TailUM.Empty,
override val tailUM: TangemRowTailUM = TangemRowTailUM.Empty,
override val onItemClick: (() -> Unit)?,
override val onItemLongClick: (() -> Unit)?,
) : TangemTokenRowUM()
@ -71,7 +72,7 @@ sealed class TangemTokenRowUM : TangemRowUM {
override val topEndContentUM: EndContentUM = EndContentUM.Loading
override val bottomEndContentUM: EndContentUM = EndContentUM.Loading
override val promoBannerUM: PromoBannerUM = PromoBannerUM.Empty
override val tailUM: TailUM = TailUM.Empty
override val tailUM: TangemRowTailUM = TangemRowTailUM.Empty
override val onItemClick: (() -> Unit)? = null
override val onItemLongClick: (() -> Unit)? = null
}
@ -84,7 +85,7 @@ sealed class TangemTokenRowUM : TangemRowUM {
override val headIconUM: TangemIconUM.Currency,
override val titleUM: TitleUM,
override val subtitleUM: SubtitleUM,
override val tailUM: TailUM,
override val tailUM: TangemRowTailUM,
override val onItemClick: (() -> Unit)?,
override val onItemLongClick: (() -> Unit)?,
override val topEndContentUM: EndContentUM = EndContentUM.Empty,
@ -154,15 +155,4 @@ sealed class TangemTokenRowUM : TangemRowUM {
data object Empty : PromoBannerUM()
}
@Immutable
sealed class TailUM {
data class Text(
val text: TextReference,
) : TailUM()
data object Draggable : TailUM()
data object Empty : TailUM()
}
}

View file

@ -7,6 +7,7 @@ import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.internal.TangemRowTailUM
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.stringReference
@ -123,7 +124,7 @@ internal object TangemTokenRowPreviewData {
topEndContentUM = topEndContentUM,
bottomEndContentUM = bottomEndContentUM,
promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty,
tailUM = TangemTokenRowUM.TailUM.Empty,
tailUM = TangemRowTailUM.Empty,
onItemClick = {},
onItemLongClick = {},
)
@ -150,7 +151,7 @@ internal object TangemTokenRowPreviewData {
),
bottomEndContentUM = bottomEndContentUM,
promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty,
tailUM = TangemTokenRowUM.TailUM.Empty,
tailUM = TangemRowTailUM.Empty,
onItemClick = {},
onItemLongClick = {},
)
@ -164,7 +165,7 @@ internal object TangemTokenRowPreviewData {
topEndContentUM = topEndContentUM,
bottomEndContentUM = bottomEndContentUM,
promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty,
tailUM = TangemTokenRowUM.TailUM.Empty,
tailUM = TangemRowTailUM.Empty,
onItemClick = {},
onItemLongClick = {},
)
@ -178,7 +179,7 @@ internal object TangemTokenRowPreviewData {
topEndContentUM = topEndContentUM,
bottomEndContentUM = bottomEndContentUM,
promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty,
tailUM = TangemTokenRowUM.TailUM.Empty,
tailUM = TangemRowTailUM.Empty,
onItemClick = {},
onItemLongClick = {},
)
@ -189,7 +190,7 @@ internal object TangemTokenRowPreviewData {
headIconUM = coinIconState,
titleUM = titleUM,
subtitleUM = subtitleUM,
tailUM = TangemTokenRowUM.TailUM.Draggable,
tailUM = TangemRowTailUM.Draggable(R.drawable.ic_drag_24),
onItemClick = {},
onItemLongClick = {},
)
@ -202,7 +203,7 @@ internal object TangemTokenRowPreviewData {
subtitleUM = subtitleUM,
topEndContentUM = topEndContentUM,
bottomEndContentUM = bottomEndContentUM,
tailUM = TangemTokenRowUM.TailUM.Draggable,
tailUM = TangemRowTailUM.Draggable(R.drawable.ic_drag_24),
onItemClick = {},
onItemLongClick = {},
)

View file

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3.333,14H9.825C9.922,14 10,14.078 10,14.175V20.667M9.949,14.051L3,21"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M20.667,10H14.175C14.078,10 14,9.922 14,9.825V3.333M14.051,9.949L21,3"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M7,18.759L7,5.5M11.5,10L7.185,5.207C7.086,5.096 6.913,5.096 6.814,5.207L2.5,10M17.314,5L17.314,18.759M12.814,14.259L17.128,19.052C17.228,19.163 17.4,19.163 17.5,19.052L21.814,14.259"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>