Updated on 2026-08-14
This commit is contained in:
parent
ddba5fe3cf
commit
5ce84d1500
14 changed files with 2832 additions and 0 deletions
|
|
@ -84,6 +84,7 @@ dependencies {
|
|||
api(projects.libs.blockchainSdk)
|
||||
implementation(projects.common)
|
||||
implementation(projects.common.ui)
|
||||
implementation(projects.common.uiCharts)
|
||||
implementation(projects.libs.auth)
|
||||
implementation(projects.libs.crypto)
|
||||
implementation(projects.libs.tangemSdkApi)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.tangem.core.ui.ds2.row.TangemRowContentLead
|
|||
import com.tangem.core.ui.ds2.row.TangemRowVerticalAlignment
|
||||
import com.tangem.core.ui.ds2.tokenicon.TangemTokenIcon
|
||||
import com.tangem.core.ui.ds2.topnavigation.TangemTopNavigation
|
||||
import com.tangem.core.ui.ds2.util.TangemPriceChange
|
||||
|
||||
internal sealed interface StoryBookPage
|
||||
|
||||
|
|
@ -446,6 +447,64 @@ internal data class TextStyleStory(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("BooleanPropertyNaming")
|
||||
internal data class TangemTokenRowV2Story(
|
||||
val variant: Variant,
|
||||
val direction: TangemPriceChange.Direction,
|
||||
val hasBadge: Boolean,
|
||||
val isBadgeFilled: Boolean,
|
||||
val hasPending: Boolean,
|
||||
val hasQuote: Boolean,
|
||||
val hasPriceChange: Boolean,
|
||||
val hasCryptoBalance: Boolean,
|
||||
val hasContractWarning: Boolean,
|
||||
val hasUpdateWarning: Boolean,
|
||||
val hasMessageBubble: Boolean,
|
||||
val isBalanceHidden: Boolean,
|
||||
val isQuoteFlickering: Boolean,
|
||||
val isBalanceFlickering: Boolean,
|
||||
val onVariantChange: (Variant) -> Unit,
|
||||
val onDirectionChange: (TangemPriceChange.Direction) -> Unit,
|
||||
val onBadgeToggle: () -> Unit,
|
||||
val onBadgeFilledToggle: () -> Unit,
|
||||
val onPendingToggle: () -> Unit,
|
||||
val onQuoteToggle: () -> Unit,
|
||||
val onPriceChangeToggle: () -> Unit,
|
||||
val onCryptoBalanceToggle: () -> Unit,
|
||||
val onContractWarningToggle: () -> Unit,
|
||||
val onUpdateWarningToggle: () -> Unit,
|
||||
val onMessageBubbleToggle: () -> Unit,
|
||||
val onBalanceHiddenToggle: () -> Unit,
|
||||
val onQuoteFlickeringToggle: () -> Unit,
|
||||
val onBalanceFlickeringToggle: () -> Unit,
|
||||
) : DsStoryBookPage {
|
||||
|
||||
/** Which [com.tangem.core.ui.ds2.tokenrow.TangemTokenRow] variant/preset is rendered. */
|
||||
enum class Variant { Default, Organize, Unreachable, NoAddress, Shimmer }
|
||||
}
|
||||
|
||||
@Suppress("BooleanPropertyNaming")
|
||||
internal data class TangemTokenRowMarketStory(
|
||||
val direction: TangemPriceChange.Direction,
|
||||
val isShimmer: Boolean,
|
||||
val hasTicker: Boolean,
|
||||
val hasPosition: Boolean,
|
||||
val hasCapitalization: Boolean,
|
||||
val hasPrice: Boolean,
|
||||
val hasPriceChange: Boolean,
|
||||
val hasChart: Boolean,
|
||||
val longTitle: Boolean,
|
||||
val onDirectionChange: (TangemPriceChange.Direction) -> Unit,
|
||||
val onShimmerToggle: () -> Unit,
|
||||
val onTickerToggle: () -> Unit,
|
||||
val onPositionToggle: () -> Unit,
|
||||
val onCapitalizationToggle: () -> Unit,
|
||||
val onPriceToggle: () -> Unit,
|
||||
val onPriceChangeToggle: () -> Unit,
|
||||
val onChartToggle: () -> Unit,
|
||||
val onLongTitleToggle: () -> Unit,
|
||||
) : DsStoryBookPage
|
||||
|
||||
internal data class TangemBadgeV2Story(
|
||||
val variant: TangemBadge.Variant,
|
||||
val status: TangemBadge.Status,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ import com.tangem.feature.tester.presentation.storybook.page.ds.search.tangemSea
|
|||
import com.tangem.feature.tester.presentation.storybook.page.ds.shimmer.tangemShimmerStoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.textstyle.textStyleStoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.tokenicon.tangemTokenIconStoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.tokenrowmarket.tangemTokenRowMarketStoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.tokenrowv2.tangemTokenRowV2StoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.topnavigation.tangemTopNavigationStoryFactory
|
||||
|
||||
private data class DsStoryItem(val title: String, val factory: StoryPageFactory)
|
||||
|
|
@ -40,6 +42,8 @@ private fun buildDsStories() = listOf(
|
|||
DsStoryItem(title = "⭕ TangemCheckmark", factory = tangemCheckmarkStoryFactory),
|
||||
DsStoryItem(title = "🎨 TangemTokenIcon", factory = tangemTokenIconStoryFactory),
|
||||
DsStoryItem(title = "📋 TangemRow", factory = tangemRowStoryFactory),
|
||||
DsStoryItem(title = "🪙 TangemTokenRow", factory = tangemTokenRowV2StoryFactory),
|
||||
DsStoryItem(title = "📈 TangemTokenRowMarket", factory = tangemTokenRowMarketStoryFactory),
|
||||
DsStoryItem(title = "🔎 TangemSearch", factory = tangemSearchStoryFactory),
|
||||
DsStoryItem(title = "✨ TangemShimmer", factory = tangemShimmerStoryFactory),
|
||||
DsStoryItem(title = "🌫️ TangemFade", factory = tangemFadeStoryFactory),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.page.ds.tokenrowmarket
|
||||
|
||||
import com.tangem.core.ui.ds2.util.TangemPriceChange
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemTokenRowMarketStory
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory
|
||||
|
||||
internal fun StateUpdater<TangemTokenRowMarketStory>.build(): TangemTokenRowMarketStory {
|
||||
return TangemTokenRowMarketStory(
|
||||
direction = TangemPriceChange.Direction.Up,
|
||||
isShimmer = false,
|
||||
hasTicker = true,
|
||||
hasPosition = true,
|
||||
hasCapitalization = true,
|
||||
hasPrice = true,
|
||||
hasPriceChange = true,
|
||||
hasChart = true,
|
||||
longTitle = false,
|
||||
onDirectionChange = { direction -> updateStory { it.copy(direction = direction) } },
|
||||
onShimmerToggle = { updateStory { it.copy(isShimmer = !it.isShimmer) } },
|
||||
onTickerToggle = { updateStory { it.copy(hasTicker = !it.hasTicker) } },
|
||||
onPositionToggle = { updateStory { it.copy(hasPosition = !it.hasPosition) } },
|
||||
onCapitalizationToggle = { updateStory { it.copy(hasCapitalization = !it.hasCapitalization) } },
|
||||
onPriceToggle = { updateStory { it.copy(hasPrice = !it.hasPrice) } },
|
||||
onPriceChangeToggle = { updateStory { it.copy(hasPriceChange = !it.hasPriceChange) } },
|
||||
onChartToggle = { updateStory { it.copy(hasChart = !it.hasChart) } },
|
||||
onLongTitleToggle = { updateStory { it.copy(longTitle = !it.longTitle) } },
|
||||
)
|
||||
}
|
||||
|
||||
internal val tangemTokenRowMarketStoryFactory
|
||||
get() = storyPageFactory(StateUpdater<TangemTokenRowMarketStory>::build)
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
@file:Suppress("MagicNumber")
|
||||
|
||||
package com.tangem.feature.tester.presentation.storybook.page.ds.tokenrowmarket
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.charts.MarketChartMini
|
||||
import com.tangem.common.ui.charts.state.MarketChartLook
|
||||
import com.tangem.common.ui.charts.state.MarketChartRawData
|
||||
import com.tangem.core.ui.ds2.tokenicon.TangemTokenIcon
|
||||
import com.tangem.core.ui.ds2.tokenrow.Shimmer
|
||||
import com.tangem.core.ui.ds2.tokenrow.TangemTokenRowMarket
|
||||
import com.tangem.core.ui.ds2.util.TangemPriceChange
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemTokenRowMarketStory
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
private const val SAMPLE_ICON_URL = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/bitcoin.png"
|
||||
|
||||
@Composable
|
||||
internal fun TangemTokenRowMarketStory(state: TangemTokenRowMarketStory, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(vertical = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
ComponentPreview(state = state)
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
DirectionSelector(selected = state.direction, onSelect = state.onDirectionChange)
|
||||
Toggles(state = state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ComponentPreview(state: TangemTokenRowMarketStory) {
|
||||
val icon = TangemTokenIcon.UiState.Token(TangemTokenIcon.State(url = SAMPLE_ICON_URL))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(TangemTheme.colors3.bg.primary)
|
||||
.padding(vertical = 16.dp),
|
||||
) {
|
||||
if (state.isShimmer) {
|
||||
TangemTokenRowMarket.Shimmer()
|
||||
} else {
|
||||
TangemTokenRowMarket(
|
||||
icon = icon,
|
||||
title = if (state.longTitle) {
|
||||
stringReference("Very Long Token Name Coin")
|
||||
} else {
|
||||
stringReference("Bitcoin")
|
||||
},
|
||||
ticker = if (state.hasTicker) stringReference("BTC") else null,
|
||||
position = if (state.hasPosition) stringReference("2") else null,
|
||||
capitalization = if (state.hasCapitalization) stringReference("1.196T") else null,
|
||||
price = if (state.hasPrice) stringReference("$59,723.24") else null,
|
||||
priceChange = if (state.hasPriceChange) {
|
||||
TangemPriceChange.State(value = stringReference("2.08%"), direction = state.direction)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
chart = if (state.hasChart) {
|
||||
{ MarketChart(direction = state.direction) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The graph slot is filled with the real MarketChartMini from :common:ui-charts, sized 24x32
|
||||
// (collapsed) per the Figma spec, showing how a feature wires the chart into the DS slot.
|
||||
@Composable
|
||||
private fun MarketChart(direction: TangemPriceChange.Direction) {
|
||||
val type = when (direction) {
|
||||
TangemPriceChange.Direction.Up -> MarketChartLook.Type.Growing
|
||||
TangemPriceChange.Direction.Down -> MarketChartLook.Type.Falling
|
||||
TangemPriceChange.Direction.Neutral -> MarketChartLook.Type.Neutral
|
||||
}
|
||||
MarketChartMini(
|
||||
rawData = PreviewChartData,
|
||||
type = type,
|
||||
modifier = Modifier.size(width = 24.dp, height = 32.dp),
|
||||
)
|
||||
}
|
||||
|
||||
private val PreviewChartData = MarketChartRawData(
|
||||
y = persistentListOf(0.4, 0.2, 0.4, 0.1, 0.4, 2.0, 5.0, 0.1, 2.0, 2.0, 3.0),
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun DirectionSelector(selected: TangemPriceChange.Direction, onSelect: (TangemPriceChange.Direction) -> Unit) {
|
||||
Section(label = "Price change") {
|
||||
ChipGrid(
|
||||
items = TangemPriceChange.Direction.entries,
|
||||
label = { it.name },
|
||||
isSelected = { it == selected },
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Toggles(state: TangemTokenRowMarketStory) {
|
||||
Section(label = "Flags") {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
ToggleRow(label = "shimmer", checked = state.isShimmer, onToggle = state.onShimmerToggle)
|
||||
ToggleRow(label = "ticker", checked = state.hasTicker, onToggle = state.onTickerToggle)
|
||||
ToggleRow(label = "position", checked = state.hasPosition, onToggle = state.onPositionToggle)
|
||||
ToggleRow(
|
||||
label = "capitalization",
|
||||
checked = state.hasCapitalization,
|
||||
onToggle = state.onCapitalizationToggle,
|
||||
)
|
||||
ToggleRow(label = "price", checked = state.hasPrice, onToggle = state.onPriceToggle)
|
||||
ToggleRow(label = "priceChange", checked = state.hasPriceChange, onToggle = state.onPriceChangeToggle)
|
||||
ToggleRow(label = "chart", checked = state.hasChart, onToggle = state.onChartToggle)
|
||||
ToggleRow(label = "longTitle", checked = state.longTitle, onToggle = state.onLongTitleToggle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Section(label: String, content: @Composable () -> Unit) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
text = label,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun <T> ChipGrid(items: List<T>, label: (T) -> String, isSelected: (T) -> Boolean, onSelect: (T) -> Unit) {
|
||||
val shape = RoundedCornerShape(50)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(shape)
|
||||
.background(TangemTheme.colors2.surface.level2)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors2.border.neutral.secondary,
|
||||
shape = shape,
|
||||
)
|
||||
.padding(4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
items.forEach { item ->
|
||||
Chip(
|
||||
label = label(item),
|
||||
selected = isSelected(item),
|
||||
onClick = { onSelect(item) },
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Chip(label: String, selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
val chipShape = RoundedCornerShape(50)
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = modifier
|
||||
.clip(chipShape)
|
||||
.background(
|
||||
if (selected) TangemTheme.colors2.surface.level3 else TangemTheme.colors2.surface.level2,
|
||||
)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(vertical = 8.dp, horizontal = 4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = if (selected) TangemTheme.colors.text.primary1 else TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ToggleRow(label: String, checked: Boolean, onToggle: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(TangemTheme.colors2.surface.level2)
|
||||
.clickable(onClick = onToggle)
|
||||
.padding(horizontal = 12.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = if (checked) "ON" else "OFF",
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = if (checked) TangemTheme.colors.text.accent else TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.page.ds.tokenrowv2
|
||||
|
||||
import com.tangem.core.ui.ds2.util.TangemPriceChange
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemTokenRowV2Story
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory
|
||||
|
||||
internal fun StateUpdater<TangemTokenRowV2Story>.build(): TangemTokenRowV2Story {
|
||||
return TangemTokenRowV2Story(
|
||||
variant = TangemTokenRowV2Story.Variant.Default,
|
||||
direction = TangemPriceChange.Direction.Up,
|
||||
hasBadge = true,
|
||||
isBadgeFilled = false,
|
||||
hasPending = false,
|
||||
hasQuote = true,
|
||||
hasPriceChange = true,
|
||||
hasCryptoBalance = true,
|
||||
hasContractWarning = false,
|
||||
hasUpdateWarning = false,
|
||||
hasMessageBubble = true,
|
||||
isBalanceHidden = false,
|
||||
isQuoteFlickering = false,
|
||||
isBalanceFlickering = false,
|
||||
onVariantChange = { variant -> updateStory { it.copy(variant = variant) } },
|
||||
onDirectionChange = { direction -> updateStory { it.copy(direction = direction) } },
|
||||
onBadgeToggle = { updateStory { it.copy(hasBadge = !it.hasBadge) } },
|
||||
onBadgeFilledToggle = { updateStory { it.copy(isBadgeFilled = !it.isBadgeFilled) } },
|
||||
onPendingToggle = { updateStory { it.copy(hasPending = !it.hasPending) } },
|
||||
onQuoteToggle = { updateStory { it.copy(hasQuote = !it.hasQuote) } },
|
||||
onPriceChangeToggle = { updateStory { it.copy(hasPriceChange = !it.hasPriceChange) } },
|
||||
onCryptoBalanceToggle = { updateStory { it.copy(hasCryptoBalance = !it.hasCryptoBalance) } },
|
||||
onContractWarningToggle = { updateStory { it.copy(hasContractWarning = !it.hasContractWarning) } },
|
||||
onUpdateWarningToggle = { updateStory { it.copy(hasUpdateWarning = !it.hasUpdateWarning) } },
|
||||
onMessageBubbleToggle = { updateStory { it.copy(hasMessageBubble = !it.hasMessageBubble) } },
|
||||
onBalanceHiddenToggle = { updateStory { it.copy(isBalanceHidden = !it.isBalanceHidden) } },
|
||||
onQuoteFlickeringToggle = { updateStory { it.copy(isQuoteFlickering = !it.isQuoteFlickering) } },
|
||||
onBalanceFlickeringToggle = { updateStory { it.copy(isBalanceFlickering = !it.isBalanceFlickering) } },
|
||||
)
|
||||
}
|
||||
|
||||
internal val tangemTokenRowV2StoryFactory
|
||||
get() = storyPageFactory(StateUpdater<TangemTokenRowV2Story>::build)
|
||||
|
|
@ -0,0 +1,308 @@
|
|||
@file:Suppress("MagicNumber")
|
||||
|
||||
package com.tangem.feature.tester.presentation.storybook.page.ds.tokenrowv2
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.ds2.badge.TangemBadge
|
||||
import com.tangem.core.ui.ds2.messagebubble.TangemMessageBubble
|
||||
import com.tangem.core.ui.ds2.tokenicon.TangemTokenIcon
|
||||
import com.tangem.core.ui.ds2.tokenrow.NoAddress
|
||||
import com.tangem.core.ui.ds2.tokenrow.Organize
|
||||
import com.tangem.core.ui.ds2.tokenrow.Shimmer
|
||||
import com.tangem.core.ui.ds2.tokenrow.TangemTokenRow
|
||||
import com.tangem.core.ui.ds2.tokenrow.Unreachable
|
||||
import com.tangem.core.ui.ds2.util.TangemPriceChange
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.generated.icons.Icons
|
||||
import com.tangem.core.ui.res.generated.icons.ic_chart_bar_vertical_16
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemTokenRowV2Story
|
||||
|
||||
private const val SAMPLE_ICON_URL = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/bitcoin.png"
|
||||
|
||||
@Composable
|
||||
internal fun TangemTokenRowV2Story(state: TangemTokenRowV2Story, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(vertical = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
ComponentPreview(state = state)
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
VariantSelector(selected = state.variant, onSelect = state.onVariantChange)
|
||||
DirectionSelector(selected = state.direction, onSelect = state.onDirectionChange)
|
||||
Toggles(state = state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ComponentPreview(state: TangemTokenRowV2Story) {
|
||||
val icon = TangemTokenIcon.UiState.Token(TangemTokenIcon.State(url = SAMPLE_ICON_URL))
|
||||
val priceChange = if (state.hasPriceChange) {
|
||||
TangemPriceChange.State(value = stringReference("2.08%"), direction = state.direction)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(TangemTheme.colors3.bg.primary)
|
||||
.padding(vertical = 16.dp),
|
||||
) {
|
||||
when (state.variant) {
|
||||
TangemTokenRowV2Story.Variant.Default -> DefaultRowPreview(
|
||||
state = state,
|
||||
icon = icon,
|
||||
priceChange = priceChange,
|
||||
)
|
||||
TangemTokenRowV2Story.Variant.Organize -> TangemTokenRow.Organize(
|
||||
icon = icon,
|
||||
title = stringReference("Bitcoin"),
|
||||
ticker = stringReference("BTC"),
|
||||
fiatBalance = stringReference("$583.00"),
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
)
|
||||
TangemTokenRowV2Story.Variant.Unreachable -> TangemTokenRow.Unreachable(
|
||||
icon = icon,
|
||||
title = stringReference("Bitcoin"),
|
||||
badge = stringReference("Unreachable"),
|
||||
quote = if (state.hasQuote) stringReference("$1.00") else null,
|
||||
priceChange = priceChange,
|
||||
onClick = {},
|
||||
)
|
||||
TangemTokenRowV2Story.Variant.NoAddress -> TangemTokenRow.NoAddress(
|
||||
icon = icon,
|
||||
title = stringReference("Bitcoin"),
|
||||
message = stringReference("No address"),
|
||||
quote = if (state.hasQuote) stringReference("$1.00") else null,
|
||||
priceChange = priceChange,
|
||||
onClick = {},
|
||||
)
|
||||
TangemTokenRowV2Story.Variant.Shimmer -> TangemTokenRow.Shimmer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DefaultRowPreview(
|
||||
state: TangemTokenRowV2Story,
|
||||
icon: TangemTokenIcon.UiState,
|
||||
priceChange: TangemPriceChange.State?,
|
||||
) {
|
||||
TangemTokenRow(
|
||||
icon = icon,
|
||||
title = stringReference("Bitcoin"),
|
||||
badge = if (state.hasBadge) {
|
||||
TangemTokenRow.Badge(
|
||||
text = stringReference("APY 5.47%"),
|
||||
variant = if (state.isBadgeFilled) TangemBadge.Variant.Solid else TangemBadge.Variant.Tinted,
|
||||
status = if (state.isBadgeFilled) TangemBadge.Status.Success else TangemBadge.Status.Neutral,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
hasPending = state.hasPending,
|
||||
quote = if (state.hasQuote) stringReference("$1.00") else null,
|
||||
priceChange = priceChange,
|
||||
fiatBalance = stringReference("$583.00"),
|
||||
cryptoBalance = if (state.hasCryptoBalance) stringReference("0,000015 BTC") else null,
|
||||
showContractWarning = state.hasContractWarning,
|
||||
showUpdateWarning = state.hasUpdateWarning,
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
isQuoteFlickering = state.isQuoteFlickering,
|
||||
isBalanceFlickering = state.isBalanceFlickering,
|
||||
messageBubble = if (state.hasMessageBubble) {
|
||||
{
|
||||
TangemMessageBubble(
|
||||
text = stringReference("Enable 5.47% APY on your balance"),
|
||||
variant = TangemMessageBubble.Variant.Success,
|
||||
icon = Icons.ic_chart_bar_vertical_16,
|
||||
onClick = {},
|
||||
onClose = {},
|
||||
closeContentDescription = "Dismiss",
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VariantSelector(
|
||||
selected: TangemTokenRowV2Story.Variant,
|
||||
onSelect: (TangemTokenRowV2Story.Variant) -> Unit,
|
||||
) {
|
||||
Section(label = "Variant") {
|
||||
ChipGrid(
|
||||
items = TangemTokenRowV2Story.Variant.entries,
|
||||
label = { it.name },
|
||||
isSelected = { it == selected },
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DirectionSelector(selected: TangemPriceChange.Direction, onSelect: (TangemPriceChange.Direction) -> Unit) {
|
||||
Section(label = "Price change") {
|
||||
ChipGrid(
|
||||
items = TangemPriceChange.Direction.entries,
|
||||
label = { it.name },
|
||||
isSelected = { it == selected },
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Toggles(state: TangemTokenRowV2Story) {
|
||||
Section(label = "Flags") {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
ToggleRow(label = "badge", checked = state.hasBadge, onToggle = state.onBadgeToggle)
|
||||
ToggleRow(label = "badgeFilled", checked = state.isBadgeFilled, onToggle = state.onBadgeFilledToggle)
|
||||
ToggleRow(label = "pending", checked = state.hasPending, onToggle = state.onPendingToggle)
|
||||
ToggleRow(label = "quote", checked = state.hasQuote, onToggle = state.onQuoteToggle)
|
||||
ToggleRow(label = "priceChange", checked = state.hasPriceChange, onToggle = state.onPriceChangeToggle)
|
||||
ToggleRow(label = "cryptoBalance", checked = state.hasCryptoBalance, onToggle = state.onCryptoBalanceToggle)
|
||||
ToggleRow(
|
||||
label = "contractWarning",
|
||||
checked = state.hasContractWarning,
|
||||
onToggle = state.onContractWarningToggle,
|
||||
)
|
||||
ToggleRow(label = "updateWarning", checked = state.hasUpdateWarning, onToggle = state.onUpdateWarningToggle)
|
||||
ToggleRow(label = "messageBubble", checked = state.hasMessageBubble, onToggle = state.onMessageBubbleToggle)
|
||||
ToggleRow(label = "balanceHidden", checked = state.isBalanceHidden, onToggle = state.onBalanceHiddenToggle)
|
||||
ToggleRow(
|
||||
label = "quoteFlickering",
|
||||
checked = state.isQuoteFlickering,
|
||||
onToggle = state.onQuoteFlickeringToggle,
|
||||
)
|
||||
ToggleRow(
|
||||
label = "balanceFlickering",
|
||||
checked = state.isBalanceFlickering,
|
||||
onToggle = state.onBalanceFlickeringToggle,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Section(label: String, content: @Composable () -> Unit) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
text = label,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun <T> ChipGrid(items: List<T>, label: (T) -> String, isSelected: (T) -> Boolean, onSelect: (T) -> Unit) {
|
||||
val shape = RoundedCornerShape(50)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(shape)
|
||||
.background(TangemTheme.colors2.surface.level2)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors2.border.neutral.secondary,
|
||||
shape = shape,
|
||||
)
|
||||
.padding(4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
items.forEach { item ->
|
||||
Chip(
|
||||
label = label(item),
|
||||
selected = isSelected(item),
|
||||
onClick = { onSelect(item) },
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Chip(label: String, selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
val chipShape = RoundedCornerShape(50)
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = modifier
|
||||
.clip(chipShape)
|
||||
.background(
|
||||
if (selected) TangemTheme.colors2.surface.level3 else TangemTheme.colors2.surface.level2,
|
||||
)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(vertical = 8.dp, horizontal = 4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = if (selected) TangemTheme.colors.text.primary1 else TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ToggleRow(label: String, checked: Boolean, onToggle: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(TangemTheme.colors2.surface.level2)
|
||||
.clickable(onClick = onToggle)
|
||||
.padding(horizontal = 12.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = if (checked) "ON" else "OFF",
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = if (checked) TangemTheme.colors.text.accent else TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -50,6 +50,8 @@ import com.tangem.feature.tester.presentation.storybook.page.ds.search.TangemSea
|
|||
import com.tangem.feature.tester.presentation.storybook.page.ds.shimmer.TangemShimmerStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.textstyle.TextStyleStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.tokenicon.TangemTokenIconStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.tokenrowmarket.TangemTokenRowMarketStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.tokenrowv2.TangemTokenRowV2Story
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.topnavigation.TangemTopNavigationStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.headerrow.TangemHeaderRowStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.message.TangemMessageStory
|
||||
|
|
@ -108,6 +110,8 @@ internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier)
|
|||
is TangemFadeStory -> TangemFadeStory(state = storyState)
|
||||
is TangemTopNavigationStory -> TangemTopNavigationStory(state = storyState)
|
||||
is TangemMessageBannerStory -> TangemMessageBannerStory(state = storyState)
|
||||
is TangemTokenRowV2Story -> TangemTokenRowV2Story(state = storyState)
|
||||
is TangemTokenRowMarketStory -> TangemTokenRowMarketStory(state = storyState)
|
||||
is TextStyleStory -> TextStyleStory(state = storyState)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue