From 0b52308ff130ff045b1c3705298b75e90217497e Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 8 Apr 2026 22:03:44 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../storybook/entity/StoryBookPage.kt | 21 +- .../storybook/page/badge/TangemBadgeStory.kt | 27 +- .../storybook/page/buttons/ButtonsStory.kt | 140 ++++++++-- .../storybook/page/deviceicon/Build.kt | 9 + .../page/deviceicon/DeviceIconStory.kt | 184 +++++++++++++ .../page/headerrow/TangemHeaderRowStory.kt | 23 ++ .../storybook/page/pagerindicator/Build.kt | 9 + .../TangemPagerIndicatorStory.kt | 142 ++++++++++ .../storybook/page/placeholder/Build.kt | 9 + .../page/placeholder/PlaceholderStory.kt | 254 +++++++++++++++++ .../storybook/page/progress/Build.kt | 9 + .../page/progress/ProgressIndicatorStory.kt | 132 +++++++++ .../presentation/storybook/page/tab/Build.kt | 19 ++ .../storybook/page/tab/TangemTabStory.kt | 127 +++++++++ .../storybook/page/topbar/Build.kt | 20 ++ .../page/topbar/TangemTopBarStory.kt | 259 ++++++++++++++++++ .../storybook/ui/StoryBookListScreen.kt | 12 + .../storybook/ui/StoryBookScreen.kt | 19 ++ 18 files changed, 1394 insertions(+), 21 deletions(-) create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/deviceicon/Build.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/deviceicon/DeviceIconStory.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/pagerindicator/Build.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/pagerindicator/TangemPagerIndicatorStory.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/placeholder/Build.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/placeholder/PlaceholderStory.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/progress/Build.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/progress/ProgressIndicatorStory.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tab/Build.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tab/TangemTabStory.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/topbar/Build.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/topbar/TangemTopBarStory.kt diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/entity/StoryBookPage.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/entity/StoryBookPage.kt index bf95156d7d..5bd49223ea 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/entity/StoryBookPage.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/entity/StoryBookPage.kt @@ -3,6 +3,7 @@ package com.tangem.feature.tester.presentation.storybook.entity import com.tangem.core.ui.ds.badge.TangemBadgeColor import com.tangem.core.ui.ds.field.search.TangemFieldShape import com.tangem.core.ui.ds.message.TangemMessageEffect +import com.tangem.core.ui.ds.topbar.TangemTopBarType internal sealed interface StoryBookPage @@ -64,4 +65,22 @@ internal data class TangemSearchFieldStory( internal data class TypographyStory( val isFontScaleDefault: Boolean, val onFontScaleToggle: () -> Unit, -) : StoryBookPage \ No newline at end of file +) : StoryBookPage + +internal data class TangemTopBarStory( + val selectedType: TangemTopBarType, + val onTypeChange: (TangemTopBarType) -> Unit, +) : StoryBookPage + +internal data class TangemTabStory( + val checkedIndex: Int, + val onCheckedIndexChange: (Int) -> Unit, +) : StoryBookPage + +internal data object TangemPagerIndicatorStory : StoryBookPage + +internal data object PlaceholderStory : StoryBookPage + +internal data object ProgressIndicatorStory : StoryBookPage + +internal data object DeviceIconStory : StoryBookPage \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/TangemBadgeStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/TangemBadgeStory.kt index c3c6cea2a1..c158b5f7d1 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/TangemBadgeStory.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/TangemBadgeStory.kt @@ -139,12 +139,18 @@ private fun BadgeShapeGroup(size: TangemBadgeSize, shape: TangemBadgeShape, colo @Composable private fun ColumnHeaderRow() { Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically, ) { Spacer(Modifier.width(STATE_LABEL_WIDTH.dp)) Text( - text = "Text + Icon", + text = "Icon Start", + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.weight(1f), + ) + Text( + text = "Icon End", style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, modifier = Modifier.weight(1f), @@ -172,7 +178,7 @@ private fun BadgeTypeRow( type: TangemBadgeType, ) { Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically, ) { Text( @@ -196,6 +202,21 @@ private fun BadgeTypeRow( onClick = {}, ) } + Box( + contentAlignment = Alignment.CenterStart, + modifier = Modifier.weight(1f), + ) { + TangemBadge( + text = stringReference("New"), + tangemIconUM = TangemIconUM.Icon(iconRes = R.drawable.ic_information_24), + size = size, + shape = shape, + color = color, + type = type, + iconPosition = TangemBadgeIconPosition.End, + onClick = {}, + ) + } Box( contentAlignment = Alignment.CenterStart, modifier = Modifier.weight(1f), diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/buttons/ButtonsStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/buttons/ButtonsStory.kt index d1bd85a0e6..f4c2e96d65 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/buttons/ButtonsStory.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/buttons/ButtonsStory.kt @@ -32,7 +32,7 @@ internal fun ButtonsStory(modifier: Modifier = Modifier) { .background(TangemTheme.colors2.surface.level1), ) { item("primary") { - ButtonSection(title = "Primary") { isEnabled, text, shape -> + ButtonSection(title = "Primary") { isEnabled, isLoading, text, shape, iconPosition -> PrimaryTangemButton( onClick = {}, text = if (text) stringReference("Continue") else null, @@ -46,14 +46,16 @@ internal fun ButtonsStory(modifier: Modifier = Modifier) { } }, ), + iconPosition = iconPosition, size = TangemButtonSize.X10, isEnabled = isEnabled, + isLoading = isLoading, shape = shape, ) } } item("secondary") { - ButtonSection(title = "Secondary") { isEnabled, text, shape -> + ButtonSection(title = "Secondary") { isEnabled, isLoading, text, shape, iconPosition -> SecondaryTangemButton( onClick = {}, text = if (text) stringReference("Continue") else null, @@ -67,8 +69,10 @@ internal fun ButtonsStory(modifier: Modifier = Modifier) { } }, ), + iconPosition = iconPosition, size = TangemButtonSize.X10, isEnabled = isEnabled, + isLoading = isLoading, shape = shape, ) } @@ -77,7 +81,7 @@ internal fun ButtonsStory(modifier: Modifier = Modifier) { ButtonSection( title = "PrimaryInverse", background = TangemTheme.colors2.surface.level2, - ) { isEnabled, text, shape -> + ) { isEnabled, isLoading, text, shape, iconPosition -> PrimaryInverseTangemButton( onClick = {}, text = if (text) stringReference("Continue") else null, @@ -91,14 +95,16 @@ internal fun ButtonsStory(modifier: Modifier = Modifier) { } }, ), + iconPosition = iconPosition, size = TangemButtonSize.X10, isEnabled = isEnabled, + isLoading = isLoading, shape = shape, ) } } item("outline") { - ButtonSection(title = "Outline") { isEnabled, text, shape -> + ButtonSection(title = "Outline") { isEnabled, isLoading, text, shape, iconPosition -> OutlineTangemButton( onClick = {}, text = if (text) stringReference("Continue") else null, @@ -112,14 +118,16 @@ internal fun ButtonsStory(modifier: Modifier = Modifier) { } }, ), + iconPosition = iconPosition, size = TangemButtonSize.X10, isEnabled = isEnabled, + isLoading = isLoading, shape = shape, ) } } item("accent") { - ButtonSection(title = "Accent") { isEnabled, text, shape -> + ButtonSection(title = "Accent") { isEnabled, isLoading, text, shape, iconPosition -> StatusTangemButton( onClick = {}, text = if (text) stringReference("Continue") else null, @@ -133,14 +141,16 @@ internal fun ButtonsStory(modifier: Modifier = Modifier) { } }, ), + iconPosition = iconPosition, size = TangemButtonSize.X10, isEnabled = isEnabled, + isLoading = isLoading, shape = shape, ) } } item("positive") { - ButtonSection(title = "Positive") { isEnabled, text, shape -> + ButtonSection(title = "Positive") { isEnabled, isLoading, text, shape, iconPosition -> StatusTangemButton( onClick = {}, text = if (text) stringReference("Continue") else null, @@ -154,15 +164,17 @@ internal fun ButtonsStory(modifier: Modifier = Modifier) { } }, ), + iconPosition = iconPosition, size = TangemButtonSize.X10, type = TangemButtonType.Positive, isEnabled = isEnabled, + isLoading = isLoading, shape = shape, ) } } item("ghost") { - ButtonSection(title = "Ghost") { isEnabled, text, shape -> + ButtonSection(title = "Ghost") { isEnabled, isLoading, text, shape, iconPosition -> GhostTangemButton( onClick = {}, text = if (text) stringReference("Continue") else null, @@ -176,21 +188,74 @@ internal fun ButtonsStory(modifier: Modifier = Modifier) { } }, ), + iconPosition = iconPosition, size = TangemButtonSize.X10, isEnabled = isEnabled, + isLoading = isLoading, shape = shape, ) } } + item("sizes") { + SizeShowcase() + } } } +@Composable +private fun SizeShowcase() { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp), + ) { + Text( + text = "Sizes", + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + ) + TangemButtonSize.entries.forEach { size -> + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = size.name, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.width(STATE_LABEL_WIDTH.dp), + ) + PrimaryTangemButton( + onClick = {}, + text = stringReference("Button"), + tangemIconUM = TangemIconUM.Icon( + iconRes = R.drawable.ic_tangem_24, + tintReference = { TangemTheme.colors2.graphic.neutral.primaryInverted }, + ), + size = size, + ) + } + } + } + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(horizontal = 16.dp), + ) +} + @Composable private fun ButtonSection( title: String, background: Color = TangemTheme.colors2.surface.level1, shapes: List = TangemButtonShape.entries, - button: @Composable (isEnabled: Boolean, text: Boolean, shape: TangemButtonShape) -> Unit, + button: @Composable ( + isEnabled: Boolean, + isLoading: Boolean, + text: Boolean, + shape: TangemButtonShape, + iconPosition: TangemButtonIconPosition, + ) -> Unit, ) { Column( verticalArrangement = Arrangement.spacedBy(8.dp), @@ -205,7 +270,9 @@ private fun ButtonSection( color = TangemTheme.colors.text.primary1, ) shapes.forEach { shape -> - ShapeGroup(shape = shape, button = button) + TangemButtonIconPosition.entries.forEach { iconPosition -> + ShapeGroup(shape = shape, iconPosition = iconPosition, button = button) + } } } HorizontalDivider( @@ -217,17 +284,46 @@ private fun ButtonSection( @Composable private fun ShapeGroup( shape: TangemButtonShape, - button: @Composable (isEnabled: Boolean, text: Boolean, shape: TangemButtonShape) -> Unit, + iconPosition: TangemButtonIconPosition, + button: @Composable ( + isEnabled: Boolean, + isLoading: Boolean, + text: Boolean, + shape: TangemButtonShape, + iconPosition: TangemButtonIconPosition, + ) -> Unit, ) { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Text( - text = shape.name, + text = "${shape.name} / Icon ${iconPosition.name}", style = TangemTheme.typography.body2, color = TangemTheme.colors.text.secondary, ) ColumnHeaderRow() - StateRow(isEnabled = true, shape = shape, button = button) - StateRow(isEnabled = false, shape = shape, button = button) + StateRow( + label = "Enabled", + isEnabled = true, + isLoading = false, + shape = shape, + iconPosition = iconPosition, + button = button, + ) + StateRow( + label = "Disabled", + isEnabled = false, + isLoading = false, + shape = shape, + iconPosition = iconPosition, + button = button, + ) + StateRow( + label = "Loading", + isEnabled = true, + isLoading = true, + shape = shape, + iconPosition = iconPosition, + button = button, + ) } } @@ -253,27 +349,37 @@ private fun ColumnHeaderRow() { } } +@Suppress("LongParameterList") @Composable private fun StateRow( + label: String, isEnabled: Boolean, + isLoading: Boolean, shape: TangemButtonShape, - button: @Composable (isEnabled: Boolean, text: Boolean, shape: TangemButtonShape) -> Unit, + iconPosition: TangemButtonIconPosition, + button: @Composable ( + isEnabled: Boolean, + isLoading: Boolean, + text: Boolean, + shape: TangemButtonShape, + iconPosition: TangemButtonIconPosition, + ) -> Unit, ) { Row( horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically, ) { Text( - text = if (isEnabled) "Enabled" else "Disabled", + text = label, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, modifier = Modifier.width(STATE_LABEL_WIDTH.dp), ) Box(modifier = Modifier.weight(1f)) { - button(isEnabled, true, shape) + button(isEnabled, isLoading, true, shape, iconPosition) } Box(modifier = Modifier.weight(1f)) { - button(isEnabled, false, shape) + button(isEnabled, isLoading, false, shape, iconPosition) } } } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/deviceicon/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/deviceicon/Build.kt new file mode 100644 index 0000000000..aaa3b8fc51 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/deviceicon/Build.kt @@ -0,0 +1,9 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.deviceicon + +import com.tangem.feature.tester.presentation.storybook.entity.DeviceIconStory +import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory + +internal val deviceIconStoryFactory: StoryPageFactory = + StoryPageFactory { DeviceIconStory } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/deviceicon/DeviceIconStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/deviceicon/DeviceIconStory.kt new file mode 100644 index 0000000000..0c3708b35d --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/deviceicon/DeviceIconStory.kt @@ -0,0 +1,184 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.deviceicon + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.HorizontalDivider +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.Color +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.ds.image.DeviceIconUM +import com.tangem.core.ui.ds.image.TangemDeviceIcon +import com.tangem.core.ui.res.TangemTheme + +private val CardBlue = Color(0xFF1C5FBF) +private val CardGold = Color(0xFFD4A017) +private val CardPurple = Color(0xFF7B2FBE) +private val RingGreen = Color(0xFF2ECC71) + +@Composable +internal fun DeviceIconStory(modifier: Modifier = Modifier) { + LazyColumn( + contentPadding = PaddingValues(vertical = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier + .statusBarsPadding() + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1), + ) { + item("section_cards") { + SectionTitle(text = "Cards") + } + + item("card_1") { + DeviceIconRow( + label = "Single card", + state = DeviceIconUM.Card(mainColor = CardBlue, secondColor = null), + ) + } + + item("card_2") { + DeviceIconRow( + label = "Two cards", + state = DeviceIconUM.Card(mainColor = CardBlue, secondColor = CardGold), + ) + } + + item("card_3") { + DeviceIconRow( + label = "Three cards", + state = DeviceIconUM.Card( + mainColor = CardBlue, + secondColor = CardGold, + thirdColor = CardPurple, + ), + ) + } + + item("section_rings") { + SectionTitle(text = "Rings") + } + + item("ring_solo") { + DeviceIconRow( + label = "Ring only", + state = DeviceIconUM.Ring(mainColor = RingGreen), + ) + } + + item("ring_card") { + DeviceIconRow( + label = "Ring + card", + state = DeviceIconUM.Ring(mainColor = RingGreen, cardColor = CardBlue), + ) + } + + item("ring_two_cards") { + DeviceIconRow( + label = "Ring + 2 cards", + state = DeviceIconUM.Ring( + mainColor = RingGreen, + cardColor = CardBlue, + secondCardColor = CardGold, + ), + ) + } + + item("section_stubs") { + SectionTitle(text = "Stubs") + } + + repeat(3) { count -> + item("stub_$count") { + DeviceIconRow( + label = "Stub ($count card${if (count > 0) "s" else ""})", + state = DeviceIconUM.Stub(cardsCount = count), + ) + } + } + + item("section_mobile") { + SectionTitle(text = "Mobile") + } + + item("mobile") { + DeviceIconRow( + label = "Mobile wallet", + state = DeviceIconUM.Mobile, + ) + } + + item("section_sizes") { + SectionTitle(text = "Sizes") + } + + item("sizes") { + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.Bottom, + modifier = Modifier.padding(horizontal = 16.dp), + ) { + listOf(24, 32, 40, 48).forEach { size -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + TangemDeviceIcon( + state = DeviceIconUM.Card( + mainColor = CardBlue, + secondColor = CardGold, + thirdColor = CardPurple, + ), + modifier = Modifier.size(size.dp), + ) + Text( + text = "${size}dp", + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + } + } + } + } + } +} + +@Composable +private fun SectionTitle(text: String) { + Text( + text = text, + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp), + ) + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(horizontal = 16.dp), + ) +} + +@Composable +private fun DeviceIconRow(label: String, state: DeviceIconUM) { + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 4.dp), + ) { + TangemDeviceIcon( + state = state, + modifier = Modifier.size(40.dp), + ) + Text( + text = label, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/headerrow/TangemHeaderRowStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/headerrow/TangemHeaderRowStory.kt index 95a5c349d7..f034c7b02f 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/headerrow/TangemHeaderRowStory.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/headerrow/TangemHeaderRowStory.kt @@ -88,6 +88,29 @@ private fun buildSampleRows(): List = listOf( title = stringReference("Account"), subtitle = stringReference("\$ 42,900.17"), ), + TangemHeaderRowUM( + id = "tail_text", + startIconUM = TangemIconUM.Currency(currencyIconState = CurrencyIconState.Locked), + tailUM = TangemRowTailUM.Text(text = stringReference("12 tokens")), + title = stringReference("Account"), + subtitle = stringReference("\$ 42,900.17"), + ), + TangemHeaderRowUM( + id = "tail_draggable", + startIconUM = TangemIconUM.Currency(currencyIconState = CurrencyIconState.Locked), + tailUM = TangemRowTailUM.Draggable(iconRes = R.drawable.ic_drag_24), + title = stringReference("Account"), + subtitle = stringReference("\$ 42,900.17"), + ), + TangemHeaderRowUM( + id = "clickable", + startIconUM = TangemIconUM.Currency(currencyIconState = CurrencyIconState.Locked), + tailUM = TangemRowTailUM.Icon(R.drawable.ic_arrow_collapse_24), + title = stringReference("Clickable row"), + subtitle = stringReference("\$ 42,900.17"), + isEnabled = true, + onItemClick = {}, + ), TangemHeaderRowUM( id = "title_only", title = stringReference("Account"), diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/pagerindicator/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/pagerindicator/Build.kt new file mode 100644 index 0000000000..22832e4de4 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/pagerindicator/Build.kt @@ -0,0 +1,9 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.pagerindicator + +import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory +import com.tangem.feature.tester.presentation.storybook.entity.TangemPagerIndicatorStory + +internal val tangemPagerIndicatorStoryFactory: StoryPageFactory = + StoryPageFactory { TangemPagerIndicatorStory } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/pagerindicator/TangemPagerIndicatorStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/pagerindicator/TangemPagerIndicatorStory.kt new file mode 100644 index 0000000000..fc770c4015 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/pagerindicator/TangemPagerIndicatorStory.kt @@ -0,0 +1,142 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.pagerindicator + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.ds.TangemPagerIndicator +import com.tangem.core.ui.ds.TangemPagerIndicatorColors +import com.tangem.core.ui.res.TangemTheme + +@Composable +internal fun TangemPagerIndicatorStory(modifier: Modifier = Modifier) { + LazyColumn( + contentPadding = PaddingValues(vertical = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier + .statusBarsPadding() + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1), + ) { + item("section_page_counts") { + SectionTitle(text = "Page counts") + } + + listOf(1, 2, 3, 4, 5).forEach { pageCount -> + item("count_$pageCount") { + IndicatorRow(label = "$pageCount page(s)", pageCount = pageCount, currentPage = 0) + } + } + + item("section_many_pages") { + SectionTitle(text = "Many pages (>5)") + } + + item("many_start") { + IndicatorRow(label = "7 pages, at start", pageCount = 7, currentPage = 0) + } + + item("many_middle") { + IndicatorRow(label = "7 pages, at middle", pageCount = 7, currentPage = 3) + } + + item("many_end") { + IndicatorRow(label = "7 pages, at end", pageCount = 7, currentPage = 6) + } + + item("ten_start") { + IndicatorRow(label = "10 pages, at start", pageCount = 10, currentPage = 0) + } + + item("ten_middle") { + IndicatorRow(label = "10 pages, at middle", pageCount = 10, currentPage = 5) + } + + item("ten_end") { + IndicatorRow(label = "10 pages, at end", pageCount = 10, currentPage = 9) + } + + item("section_active_positions") { + SectionTitle(text = "Active dot positions (5 pages)") + } + + repeat(4) { page -> + item("active_$page") { + IndicatorRow(label = "Active: page ${page + 1}", pageCount = 5, currentPage = page) + } + } + + item("section_overlay") { + SectionTitle(text = "With overlay background") + } + + item("overlay") { + IndicatorRowWithOverlay(pageCount = 5, currentPage = 2) + } + } +} + +@Composable +private fun SectionTitle(text: String) { + Text( + text = text, + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp), + ) + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(horizontal = 16.dp), + ) +} + +@Composable +private fun IndicatorRow(label: String, pageCount: Int, currentPage: Int) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 4.dp), + ) { + Text( + text = label, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + TangemPagerIndicator( + pagerState = rememberPagerState(currentPage) { pageCount }, + ) + } +} + +@Composable +private fun IndicatorRowWithOverlay(pageCount: Int, currentPage: Int) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 4.dp), + ) { + Text( + text = "With overlay", + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + TangemPagerIndicator( + pagerState = rememberPagerState(currentPage) { pageCount }, + colors = TangemPagerIndicatorColors.copy( + overlay = TangemTheme.colors2.tabs.backgroundSecondary, + ), + ) + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/placeholder/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/placeholder/Build.kt new file mode 100644 index 0000000000..947666e5a7 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/placeholder/Build.kt @@ -0,0 +1,9 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.placeholder + +import com.tangem.feature.tester.presentation.storybook.entity.PlaceholderStory +import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory + +internal val placeholderStoryFactory: StoryPageFactory = + StoryPageFactory { PlaceholderStory } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/placeholder/PlaceholderStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/placeholder/PlaceholderStory.kt new file mode 100644 index 0000000000..5eb7aa7d41 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/placeholder/PlaceholderStory.kt @@ -0,0 +1,254 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.placeholder + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.components.ChipShimmer +import com.tangem.core.ui.components.CircleShimmer +import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.components.SmallButtonShimmer +import com.tangem.core.ui.components.TextShimmer +import com.tangem.core.ui.res.TangemTheme + +@Composable +internal fun PlaceholderStory(modifier: Modifier = Modifier) { + LazyColumn( + contentPadding = PaddingValues(vertical = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier + .statusBarsPadding() + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1), + ) { + item("section_rectangle") { + SectionTitle(text = "RectangleShimmer") + } + + item("rect_default") { + ShimmerRow(label = "Default") { + RectangleShimmer( + modifier = Modifier + .fillMaxWidth() + .height(24.dp), + ) + } + } + + item("rect_narrow") { + ShimmerRow(label = "Narrow (40%)") { + RectangleShimmer( + modifier = Modifier + .fillMaxWidth(fraction = 0.4f) + .height(16.dp), + ) + } + } + + item("rect_tall") { + ShimmerRow(label = "Tall (48dp)") { + RectangleShimmer( + modifier = Modifier + .fillMaxWidth() + .height(48.dp), + ) + } + } + + item("rect_custom_radius") { + ShimmerRow(label = "Custom radius (16dp)") { + RectangleShimmer( + modifier = Modifier + .fillMaxWidth() + .height(24.dp), + radius = 16.dp, + ) + } + } + + item("section_circle") { + SectionTitle(text = "CircleShimmer") + } + + item("circle_sizes") { + ShimmerRow(label = "Sizes: 24, 32, 40, 48") { + Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { + CircleShimmer(modifier = Modifier.size(24.dp)) + CircleShimmer(modifier = Modifier.size(32.dp)) + CircleShimmer(modifier = Modifier.size(40.dp)) + CircleShimmer(modifier = Modifier.size(48.dp)) + } + } + } + + item("section_text") { + SectionTitle(text = "TextShimmer") + } + + item("text_title") { + ShimmerRow(label = "titleRegular44") { + TextShimmer( + style = TangemTheme.typography2.titleRegular44, + modifier = Modifier.fillMaxWidth(fraction = 0.5f), + ) + } + } + + item("text_heading") { + ShimmerRow(label = "headingSemibold22") { + TextShimmer( + style = TangemTheme.typography2.headingSemibold22, + modifier = Modifier.fillMaxWidth(fraction = 0.6f), + ) + } + } + + item("text_body") { + ShimmerRow(label = "bodyRegular16") { + TextShimmer( + style = TangemTheme.typography2.bodyRegular16, + modifier = Modifier.fillMaxWidth(fraction = 0.7f), + ) + } + } + + item("text_caption") { + ShimmerRow(label = "captionRegular12") { + TextShimmer( + style = TangemTheme.typography2.captionRegular12, + modifier = Modifier.fillMaxWidth(fraction = 0.4f), + ) + } + } + + item("text_size_height") { + ShimmerRow(label = "bodyRegular16 (textSizeHeight)") { + TextShimmer( + style = TangemTheme.typography2.bodyRegular16, + textSizeHeight = true, + modifier = Modifier.fillMaxWidth(fraction = 0.5f), + ) + } + } + + item("section_button_chip") { + SectionTitle(text = "SmallButtonShimmer & ChipShimmer") + } + + item("small_button") { + ShimmerRow(label = "SmallButtonShimmer") { + SmallButtonShimmer() + } + } + + item("small_button_icon") { + ShimmerRow(label = "SmallButtonShimmer (with icon)") { + SmallButtonShimmer(withIcon = true) + } + } + + item("chip") { + ShimmerRow(label = "ChipShimmer") { + ChipShimmer() + } + } + + item("section_composition") { + SectionTitle(text = "Skeleton composition") + } + + item("card_skeleton") { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(horizontal = 16.dp), + ) { + Text( + text = "Typical card loading state", + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { + CircleShimmer(modifier = Modifier.size(40.dp)) + Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { + TextShimmer( + style = TangemTheme.typography2.headingSemibold17, + modifier = Modifier.width(120.dp), + ) + TextShimmer( + style = TangemTheme.typography2.captionRegular13, + modifier = Modifier.width(80.dp), + ) + } + } + } + } + + item("list_skeleton") { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(horizontal = 16.dp), + ) { + Text( + text = "List loading state", + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + repeat(3) { + Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { + RectangleShimmer(modifier = Modifier.size(40.dp)) + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier.weight(1f), + ) { + TextShimmer( + style = TangemTheme.typography2.bodyMedium16, + modifier = Modifier.fillMaxWidth(fraction = 0.6f), + ) + TextShimmer( + style = TangemTheme.typography2.captionRegular13, + modifier = Modifier.fillMaxWidth(fraction = 0.4f), + ) + } + } + } + } + } + } +} + +@Composable +private fun SectionTitle(text: String) { + Text( + text = text, + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp), + ) + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(horizontal = 16.dp), + ) +} + +@Composable +private fun ShimmerRow(label: String, content: @Composable () -> Unit) { + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 4.dp), + ) { + Text( + text = label, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + content() + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/progress/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/progress/Build.kt new file mode 100644 index 0000000000..9855967b04 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/progress/Build.kt @@ -0,0 +1,9 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.progress + +import com.tangem.feature.tester.presentation.storybook.entity.ProgressIndicatorStory +import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory + +internal val progressIndicatorStoryFactory: StoryPageFactory = + StoryPageFactory { ProgressIndicatorStory } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/progress/ProgressIndicatorStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/progress/ProgressIndicatorStory.kt new file mode 100644 index 0000000000..85ef01a6ea --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/progress/ProgressIndicatorStory.kt @@ -0,0 +1,132 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.progress + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.ds.progress.TangemLinearProgressIndicatorWithDot +import com.tangem.core.ui.res.TangemTheme + +@Composable +internal fun ProgressIndicatorStory(modifier: Modifier = Modifier) { + LazyColumn( + contentPadding = PaddingValues(vertical = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier + .statusBarsPadding() + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1), + ) { + item("section_progress") { + SectionTitle(text = "Progress values") + } + + listOf(0f, 0.25f, 0.5f, 0.75f, 1f).forEach { progress -> + item("progress_$progress") { + ProgressRow(label = "${(progress * 100).toInt()}%", progress = progress) + } + } + + item("section_heights") { + SectionTitle(text = "Track heights") + } + + listOf(4, 6, 8).forEach { height -> + item("height_$height") { + ProgressRow(label = "${height}dp track", progress = 0.5f, height = height) + } + } + + item("section_colors") { + SectionTitle(text = "Color variants") + } + + item("accent") { + ProgressRowWithColors( + label = "Accent", + progress = 0.6f, + dotColor = TangemTheme.colors2.fill.status.accent, + bgColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant, + ) + } + + item("warning") { + ProgressRowWithColors( + label = "Warning", + progress = 0.4f, + dotColor = TangemTheme.colors2.fill.status.warning, + bgColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant, + ) + } + + item("attention") { + ProgressRowWithColors( + label = "Attention", + progress = 0.8f, + dotColor = TangemTheme.colors2.fill.status.attention, + bgColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant, + ) + } + } +} + +@Composable +private fun SectionTitle(text: String) { + Text( + text = text, + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp), + ) + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(horizontal = 16.dp), + ) +} + +@Composable +private fun ProgressRow(label: String, progress: Float, height: Int = 6) { + ProgressRowWithColors( + label = label, + progress = progress, + height = height, + dotColor = TangemTheme.colors2.fill.status.accent, + bgColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant, + ) +} + +@Composable +private fun ProgressRowWithColors( + label: String, + progress: Float, + dotColor: androidx.compose.ui.graphics.Color, + bgColor: androidx.compose.ui.graphics.Color, + height: Int = 6, +) { + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 4.dp), + ) { + Text( + text = label, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + TangemLinearProgressIndicatorWithDot( + progress = { progress }, + dotColor = dotColor, + backgroundColor = bgColor, + modifier = Modifier + .fillMaxWidth() + .height(height.dp), + ) + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tab/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tab/Build.kt new file mode 100644 index 0000000000..21b9a31e14 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tab/Build.kt @@ -0,0 +1,19 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.tab + +import com.tangem.feature.tester.presentation.storybook.entity.TangemTabStory +import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater +import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory + +internal fun StateUpdater.build(): TangemTabStory { + return TangemTabStory( + checkedIndex = 0, + onCheckedIndexChange = { index -> + updateStory { it.copy(checkedIndex = index) } + }, + ) +} + +internal val tangemTabStoryFactory + get() = storyPageFactory(StateUpdater::build) \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tab/TangemTabStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tab/TangemTabStory.kt new file mode 100644 index 0000000000..f734c1027c --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tab/TangemTabStory.kt @@ -0,0 +1,127 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.tab + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.ds.tabs.TangemTab +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.tester.presentation.storybook.entity.TangemTabStory + +@Composable +internal fun TangemTabStory(state: TangemTabStory, modifier: Modifier = Modifier) { + LazyColumn( + contentPadding = PaddingValues(vertical = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier + .statusBarsPadding() + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1), + ) { + item("interactive") { + TabSection(title = "Interactive") { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + listOf("Markets", "Portfolio", "Activity").forEachIndexed { index, label -> + TangemTab( + text = stringReference(label), + isChecked = state.checkedIndex == index, + onCheckedChange = { if (it) state.onCheckedIndexChange(index) }, + ) + } + } + } + } + + item("checked") { + TabSection(title = "Checked") { + TangemTab( + text = stringReference("Markets"), + isChecked = true, + onCheckedChange = {}, + ) + } + } + + item("unchecked") { + TabSection(title = "Unchecked") { + TangemTab( + text = stringReference("Markets"), + isChecked = false, + onCheckedChange = {}, + ) + } + } + + item("disabled_checked") { + TabSection(title = "Disabled (Checked)") { + TangemTab( + text = stringReference("Markets"), + isChecked = true, + onCheckedChange = {}, + isEnabled = false, + ) + } + } + + item("disabled_unchecked") { + TabSection(title = "Disabled (Unchecked)") { + TangemTab( + text = stringReference("Markets"), + isChecked = false, + onCheckedChange = {}, + isEnabled = false, + ) + } + } + + item("multiple_tabs") { + TabSection(title = "Multiple tabs row") { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + TangemTab( + text = stringReference("All"), + isChecked = true, + onCheckedChange = {}, + ) + TangemTab( + text = stringReference("Gainers"), + isChecked = false, + onCheckedChange = {}, + ) + TangemTab( + text = stringReference("Losers"), + isChecked = false, + onCheckedChange = {}, + ) + } + } + } + } +} + +@Composable +private fun TabSection(title: String, content: @Composable () -> Unit) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 4.dp), + ) { + Text( + text = title, + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + ) + content() + } + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(horizontal = 16.dp), + ) +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/topbar/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/topbar/Build.kt new file mode 100644 index 0000000000..494ab36f6b --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/topbar/Build.kt @@ -0,0 +1,20 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.topbar + +import com.tangem.core.ui.ds.topbar.TangemTopBarType +import com.tangem.feature.tester.presentation.storybook.entity.TangemTopBarStory +import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater +import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory + +internal fun StateUpdater.build(): TangemTopBarStory { + return TangemTopBarStory( + selectedType = TangemTopBarType.Default, + onTypeChange = { type -> + updateStory { it.copy(selectedType = type) } + }, + ) +} + +internal val tangemTopBarStoryFactory + get() = storyPageFactory(StateUpdater::build) \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/topbar/TangemTopBarStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/topbar/TangemTopBarStory.kt new file mode 100644 index 0000000000..4a0d026c91 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/topbar/TangemTopBarStory.kt @@ -0,0 +1,259 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.topbar + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.HorizontalDivider +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.R +import com.tangem.core.ui.ds.topbar.TangemTopBar +import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM +import com.tangem.core.ui.ds.topbar.TangemTopBarType +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.tester.presentation.storybook.entity.TangemTopBarStory +import kotlinx.collections.immutable.persistentListOf + +@Composable +internal fun TangemTopBarStory(state: TangemTopBarStory, modifier: Modifier = Modifier) { + LazyColumn( + contentPadding = PaddingValues(bottom = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier + .statusBarsPadding() + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1), + ) { + stickyHeader("type_toggle") { + TypeToggle( + selected = state.selectedType, + onSelect = state.onTypeChange, + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors2.surface.level1) + .padding(horizontal = 16.dp, vertical = 8.dp), + ) + } + + item("title_only") { + TopBarVariant(label = "Title only") { + TangemTopBar( + title = stringReference("Wallet"), + type = state.selectedType, + startAction = null, + ) + } + } + + item("title_subtitle") { + TopBarVariant(label = "Title + Subtitle") { + TangemTopBar( + title = stringReference("Wallet"), + subtitle = stringReference("3 cards"), + type = state.selectedType, + startAction = null, + ) + } + } + + item("back_action") { + TopBarVariant(label = "Back action + Title") { + TangemTopBar( + title = stringReference("Send"), + type = state.selectedType, + startAction = TangemTopBarActionUM( + iconRes = R.drawable.ic_back_24, + onClick = {}, + ), + ) + } + } + + item("back_and_end") { + TopBarVariant(label = "Back + Title + End action") { + TangemTopBar( + title = stringReference("Token Details"), + type = state.selectedType, + startAction = TangemTopBarActionUM( + iconRes = R.drawable.ic_back_24, + onClick = {}, + ), + endActions = persistentListOf( + TangemTopBarActionUM( + iconRes = R.drawable.ic_more_default_24, + onClick = {}, + ), + ), + ) + } + } + + item("back_and_two_end") { + TopBarVariant(label = "Back + Title + 2 End actions") { + TangemTopBar( + title = stringReference("Settings"), + type = state.selectedType, + startAction = TangemTopBarActionUM( + iconRes = R.drawable.ic_back_24, + onClick = {}, + ), + endActions = persistentListOf( + TangemTopBarActionUM( + iconRes = R.drawable.ic_information_24, + onClick = {}, + ), + TangemTopBarActionUM( + iconRes = R.drawable.ic_close_24, + onClick = {}, + ), + ), + ) + } + } + + item("ghost_actions") { + TopBarVariant(label = "Ghost mode actions (progress=1)") { + TangemTopBar( + title = stringReference("Portfolio"), + type = state.selectedType, + startAction = TangemTopBarActionUM( + iconRes = R.drawable.ic_tangem_24, + onClick = {}, + ghostModeProgress = 1f, + ), + endActions = persistentListOf( + TangemTopBarActionUM( + iconRes = R.drawable.ic_more_default_24, + onClick = {}, + ghostModeProgress = 1f, + ), + ), + ) + } + } + + item("non_actionable") { + TopBarVariant(label = "Non-actionable icons") { + TangemTopBar( + title = stringReference("Details"), + type = state.selectedType, + startAction = TangemTopBarActionUM( + iconRes = R.drawable.ic_tangem_24, + isActionable = false, + ), + endActions = persistentListOf( + TangemTopBarActionUM( + iconRes = R.drawable.ic_more_default_24, + isActionable = false, + ), + ), + ) + } + } + + item("title_icon") { + TopBarVariant(label = "Title with icon") { + TangemTopBar( + title = stringReference("Wallet"), + titleIconRes = R.drawable.ic_tangem_24, + type = state.selectedType, + startAction = TangemTopBarActionUM( + iconRes = R.drawable.ic_back_24, + onClick = {}, + ), + ) + } + } + + item("no_title") { + TopBarVariant(label = "No title (end action only)") { + TangemTopBar( + type = state.selectedType, + startAction = null, + endActions = persistentListOf( + TangemTopBarActionUM( + iconRes = R.drawable.ic_close_24, + onClick = {}, + ), + ), + ) + } + } + } +} + +@Composable +private fun TypeToggle( + selected: TangemTopBarType, + onSelect: (TangemTopBarType) -> Unit, + modifier: Modifier = Modifier, +) { + val shape = RoundedCornerShape(50) + Row( + modifier = modifier + .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), + ) { + TangemTopBarType.entries.forEach { type -> + TypeChip( + label = type.name, + selected = type == selected, + onClick = { onSelect(type) }, + modifier = Modifier.weight(1f), + ) + } + } +} + +@Composable +private fun TypeChip(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 TopBarVariant(label: String, content: @Composable () -> Unit) { + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 4.dp), + ) { + Text( + text = label, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + content() + } + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(horizontal = 16.dp), + ) +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/ui/StoryBookListScreen.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/ui/StoryBookListScreen.kt index 3a8b75942d..26d7f561c6 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/ui/StoryBookListScreen.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/ui/StoryBookListScreen.kt @@ -20,12 +20,18 @@ import com.tangem.feature.tester.presentation.storybook.page.badge.tangemBadgeSt import com.tangem.feature.tester.presentation.storybook.page.buttons.buttonsStoryFactory import com.tangem.feature.tester.presentation.storybook.page.checkbox.tangemCheckboxStoryFactory import com.tangem.feature.tester.presentation.storybook.page.contextmenu.tangemContextMenuStoryFactory +import com.tangem.feature.tester.presentation.storybook.page.deviceicon.deviceIconStoryFactory import com.tangem.feature.tester.presentation.storybook.page.headerrow.tangemHeaderRowStoryFactory import com.tangem.feature.tester.presentation.storybook.page.message.tangemMessageStoryFactory import com.tangem.feature.tester.presentation.storybook.page.opportunities.opportunitiesBGStoryFactory +import com.tangem.feature.tester.presentation.storybook.page.pagerindicator.tangemPagerIndicatorStoryFactory +import com.tangem.feature.tester.presentation.storybook.page.placeholder.placeholderStoryFactory +import com.tangem.feature.tester.presentation.storybook.page.progress.progressIndicatorStoryFactory import com.tangem.feature.tester.presentation.storybook.page.searchfield.tangemSearchFieldStoryFactory +import com.tangem.feature.tester.presentation.storybook.page.tab.tangemTabStoryFactory import com.tangem.feature.tester.presentation.storybook.page.tabs.tangemSegmentedPickerStoryFactory import com.tangem.feature.tester.presentation.storybook.page.tokenrow.tangemTokenRowStoryFactory +import com.tangem.feature.tester.presentation.storybook.page.topbar.tangemTopBarStoryFactory import com.tangem.feature.tester.presentation.storybook.page.typography.typographyStoryFactory private data class StoryItem(val title: String, val factory: StoryPageFactory) @@ -43,6 +49,12 @@ private fun buildStories() = listOf( StoryItem(title = "📋 Context Menu", factory = tangemContextMenuStoryFactory), StoryItem(title = "🔍 Search Field", factory = tangemSearchFieldStoryFactory), StoryItem(title = "🔤 Typography", factory = typographyStoryFactory), + StoryItem(title = "🧭 Top Bar", factory = tangemTopBarStoryFactory), + StoryItem(title = "🔀 Tab", factory = tangemTabStoryFactory), + StoryItem(title = "⚫ Pager Indicator", factory = tangemPagerIndicatorStoryFactory), + StoryItem(title = "💀 Placeholder", factory = placeholderStoryFactory), + StoryItem(title = "⏳ Progress Indicator", factory = progressIndicatorStoryFactory), + StoryItem(title = "💳 Device Icon", factory = deviceIconStoryFactory), ) @Composable diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/ui/StoryBookScreen.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/ui/StoryBookScreen.kt index e8e17c0645..7bce52bb28 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/ui/StoryBookScreen.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/ui/StoryBookScreen.kt @@ -5,8 +5,11 @@ import androidx.compose.animation.AnimatedContent import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.tangem.feature.tester.presentation.storybook.entity.ButtonsStory +import com.tangem.feature.tester.presentation.storybook.entity.DeviceIconStory import com.tangem.feature.tester.presentation.storybook.entity.NorthernLightsStory import com.tangem.feature.tester.presentation.storybook.entity.OpportunitiesBGStory +import com.tangem.feature.tester.presentation.storybook.entity.PlaceholderStory +import com.tangem.feature.tester.presentation.storybook.entity.ProgressIndicatorStory import com.tangem.feature.tester.presentation.storybook.entity.TangemBadgeStory import com.tangem.feature.tester.presentation.storybook.entity.StoryBookUM import com.tangem.feature.tester.presentation.storybook.entity.StoryList @@ -14,23 +17,33 @@ import com.tangem.feature.tester.presentation.storybook.entity.TangemCheckboxSto import com.tangem.feature.tester.presentation.storybook.entity.TangemHeaderRowStory import com.tangem.feature.tester.presentation.storybook.entity.TangemContextMenuStory import com.tangem.feature.tester.presentation.storybook.entity.TangemMessageStory +import com.tangem.feature.tester.presentation.storybook.entity.TangemPagerIndicatorStory import com.tangem.feature.tester.presentation.storybook.entity.TangemSearchFieldStory import com.tangem.feature.tester.presentation.storybook.entity.TangemSegmentedPickerStory +import com.tangem.feature.tester.presentation.storybook.entity.TangemTabStory import com.tangem.feature.tester.presentation.storybook.entity.TangemTokenRowStory +import com.tangem.feature.tester.presentation.storybook.entity.TangemTopBarStory import com.tangem.feature.tester.presentation.storybook.entity.TypographyStory import com.tangem.feature.tester.presentation.storybook.page.background.NorthernLightsStory import com.tangem.feature.tester.presentation.storybook.page.badge.TangemBadgeStory import com.tangem.feature.tester.presentation.storybook.page.buttons.ButtonsStory +import com.tangem.feature.tester.presentation.storybook.page.deviceicon.DeviceIconStory import com.tangem.feature.tester.presentation.storybook.page.opportunities.OpportunitiesBGStory import com.tangem.feature.tester.presentation.storybook.page.checkbox.TangemCheckboxStory import com.tangem.feature.tester.presentation.storybook.page.message.TangemMessageStory +import com.tangem.feature.tester.presentation.storybook.page.pagerindicator.TangemPagerIndicatorStory +import com.tangem.feature.tester.presentation.storybook.page.placeholder.PlaceholderStory +import com.tangem.feature.tester.presentation.storybook.page.progress.ProgressIndicatorStory +import com.tangem.feature.tester.presentation.storybook.page.tab.TangemTabStory import com.tangem.feature.tester.presentation.storybook.page.tabs.TangemSegmentedPickerStory import com.tangem.feature.tester.presentation.storybook.page.tokenrow.TangemTokenRowStory +import com.tangem.feature.tester.presentation.storybook.page.topbar.TangemTopBarStory import com.tangem.feature.tester.presentation.storybook.page.headerrow.TangemHeaderRowStory import com.tangem.feature.tester.presentation.storybook.page.contextmenu.TangemContextMenuStory import com.tangem.feature.tester.presentation.storybook.page.searchfield.TangemSearchFieldStory import com.tangem.feature.tester.presentation.storybook.page.typography.TypographyStory +@Suppress("CyclomaticComplexMethod") @Composable internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier) { BackHandler(onBack = state.onBackClick) @@ -54,6 +67,12 @@ internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier) is TangemContextMenuStory -> TangemContextMenuStory(state = storyState) is TangemSearchFieldStory -> TangemSearchFieldStory(state = storyState) is TypographyStory -> TypographyStory(state = storyState) + is TangemTopBarStory -> TangemTopBarStory(state = storyState) + is TangemTabStory -> TangemTabStory(state = storyState) + TangemPagerIndicatorStory -> TangemPagerIndicatorStory() + PlaceholderStory -> PlaceholderStory() + ProgressIndicatorStory -> ProgressIndicatorStory() + DeviceIconStory -> DeviceIconStory() } } } \ No newline at end of file