From 6cfb8213f02850755f3039fb4b46f463fc437793 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 27 Feb 2026 15:54:11 +0300 Subject: [PATCH] Updated on 2026-08-14 --- core/ui/build.gradle.kts | 2 +- .../core/ui/ds/row/TangemRowContainer.kt | 2 +- .../core/ui/ds/row/token/TangemTokenRow.kt | 5 +- features/tester/STORYBOOK.md | 45 ++++ .../storybook/entity/StoryBookPage.kt | 31 ++- .../storybook/page/background/Build.kt | 1 + .../page/background/NorthernLightsStory.kt | 2 +- .../storybook/page/badge/Build.kt | 19 ++ .../storybook/page/badge/TangemBadgeStory.kt | 222 +++++++++++++++++ .../storybook/page/buttons/Build.kt | 1 + .../storybook/page/buttons/ButtonsStory.kt | 2 + .../storybook/page/checkbox/Build.kt | 22 ++ .../page/checkbox/TangemCheckboxStory.kt | 137 ++++++++++ .../storybook/page/message/Build.kt | 19 ++ .../page/message/TangemMessageStory.kt | 233 ++++++++++++++++++ .../storybook/page/opportunities/Build.kt | 7 + .../opportunities/OpportunitiesBGStory.kt | 122 +++++++++ .../presentation/storybook/page/tabs/Build.kt | 7 + .../page/tabs/TangemSegmentedPickerStory.kt | 171 +++++++++++++ .../storybook/page/tokenrow/Build.kt | 16 ++ .../page/tokenrow/TangemTokenRowStory.kt | 72 ++++++ .../storybook/ui/StoryBookListScreen.kt | 12 + .../storybook/ui/StoryBookScreen.kt | 19 ++ 23 files changed, 1163 insertions(+), 6 deletions(-) create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/Build.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/TangemBadgeStory.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/checkbox/Build.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/checkbox/TangemCheckboxStory.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/message/Build.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/message/TangemMessageStory.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/opportunities/Build.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/opportunities/OpportunitiesBGStory.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tabs/Build.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tabs/TangemSegmentedPickerStory.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tokenrow/Build.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tokenrow/TangemTokenRowStory.kt diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 482fb4ddd7..f9b0a562a0 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -49,7 +49,7 @@ dependencies { implementation(deps.compose.coil) implementation(deps.compose.navigation) implementation(deps.compose.navigation.hilt) - implementation(deps.compose.reorderable) + api(deps.compose.reorderable) /** Other libraries */ implementation(deps.compose.accompanist.systemUiController) diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/TangemRowContainer.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/TangemRowContainer.kt index d917b39b7f..026dc41e70 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/TangemRowContainer.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/TangemRowContainer.kt @@ -46,7 +46,7 @@ fun TangemRowContainer( content = content, modifier = modifier, ) { measurables, constraints -> - val layoutWidth = constraints.maxWidth - contentStartPadding - contentEndPadding + val layoutWidth = max(0, constraints.maxWidth - contentStartPadding - contentEndPadding) val startTopMinWidth = (layoutWidth * TITLE_MIN_WIDTH_COEFFICIENT).toInt() val startBottomMinWidth = (layoutWidth * PRICE_MIN_WIDTH_COEFFICIENT).toInt() diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRow.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRow.kt index da31f1d96a..6aaafb85b9 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRow.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRow.kt @@ -226,7 +226,7 @@ private fun Modifier.tokenClickable(tokenRowUM: TangemTokenRowUM): Modifier = co @Preview(showBackground = true, widthDp = 360) @Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) private fun TangemTokenRow_Preview( - @PreviewParameter(TangemTokenRowPreviewProvider::class) tokenRowUM: TangemTokenRowUM, + @PreviewParameter(TangemTokenRow_PreviewProvider::class) tokenRowUM: TangemTokenRowUM, ) { TangemThemePreviewRedesign { TangemTokenRow( @@ -238,7 +238,8 @@ private fun TangemTokenRow_Preview( } } -private class TangemTokenRowPreviewProvider : CollectionPreviewParameterProvider( +@Suppress("ClassNaming") +class TangemTokenRow_PreviewProvider : CollectionPreviewParameterProvider( collection = listOf( TangemTokenRowPreviewData.defaultState, TangemTokenRowPreviewData.defaultEllipsisState, diff --git a/features/tester/STORYBOOK.md b/features/tester/STORYBOOK.md index f9142cdd1b..c8dfee51c3 100644 --- a/features/tester/STORYBOOK.md +++ b/features/tester/STORYBOOK.md @@ -171,11 +171,56 @@ Show every meaningful axis of variation in one place: | **Shapes** (Default, Rounded) | One labeled group (`ShapeGroup`) per shape, iterate `TangemButtonShape.entries` | | **Content** (text+icon vs icon-only) | Two columns per row | | **Sizes** | Separate `LazyColumn` item per size group if needed | +| **Styles / Effects** (e.g. `TangemMessageEffect`) | Chip toggle — see below | > **Prefer vertical stacking over horizontal.** A row should contain at most > 2–3 components; more than that overflows on narrow screens. Use > `Modifier.weight(1f)` on columns instead of fixed widths. +### Toggle for style/effect axes + +When a discrete axis (e.g. a visual effect enum) would produce too many full-width +components on one screen, use a **sticky chip-picker** instead of stacking all values. +Make the page **stateful** and store the selected value in the `StoryBookPage` data class. + +``` +┌─────────────────────────────────┐ ← stickyHeader +│ Magic │ Card │ Warning │ None│ ← chip row (EffectToggle) +└─────────────────────────────────┘ + No icon, no buttons + [ message with selected effect ] + With icon + [ message with selected effect ] + … +``` + +**Pattern:** + +1. Add the selected value + callback to the `StoryBookPage` data class: + ```kotlin + internal data class FooStory( + val selectedVariant: Variant, + val onVariantChange: (Variant) -> Unit, + ) : StoryBookPage + ``` +2. Use a stateful `Build.kt` (see [Step 2](#2-create-pagefoobuildk)). +3. In the story composable, add a `stickyHeader` with a chip row: + ```kotlin + stickyHeader("toggle") { + VariantToggle( + selected = state.selectedVariant, + onSelect = state.onVariantChange, + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors2.surface.level1) + .padding(horizontal = 16.dp, vertical = 8.dp), + ) + } + ``` +4. Each `item` below uses `state.selectedVariant` for the component under test. + +See `TangemMessageStory` for a complete example. + ### Section structure (component grids) Follow the pattern used in `ButtonsStory`: 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 5967dd9e21..3fb899ac8e 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 @@ -1,11 +1,35 @@ package com.tangem.feature.tester.presentation.storybook.entity +import com.tangem.core.ui.ds.badge.TangemBadgeColor +import com.tangem.core.ui.ds.message.TangemMessageEffect + internal sealed interface StoryBookPage internal data object StoryList : StoryBookPage internal data object ButtonsStory : StoryBookPage +internal data class TangemBadgeStory( + val selectedColor: TangemBadgeColor, + val onColorChange: (TangemBadgeColor) -> Unit, +) : StoryBookPage + +internal data object OpportunitiesBGStory : StoryBookPage + +internal data class TangemCheckboxStory( + val isRoundedChecked: Boolean, + val onRoundedCheckedChange: (Boolean) -> Unit, + val isCircleChecked: Boolean, + val onCircleCheckedChange: (Boolean) -> Unit, +) : StoryBookPage + +internal data object TangemSegmentedPickerStory : StoryBookPage + +internal data class TangemMessageStory( + val selectedEffect: TangemMessageEffect, + val onEffectChange: (TangemMessageEffect) -> Unit, +) : StoryBookPage + internal data class NorthernLightsStory( val variant: Variant, val onVariantChange: (Variant) -> Unit, @@ -14,4 +38,9 @@ internal data class NorthernLightsStory( Shader, Simple, } -} \ No newline at end of file +} + +internal data class TangemTokenRowStory( + val isBalanceHidden: Boolean, + val onBalanceHiddenToggle: () -> Unit, +) : StoryBookPage \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/background/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/background/Build.kt index 522c3d4ae1..e78069bc35 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/background/Build.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/background/Build.kt @@ -1,3 +1,4 @@ +@file:Suppress("MagicNumber", "LongMethod") package com.tangem.feature.tester.presentation.storybook.page.background import com.tangem.feature.tester.presentation.storybook.entity.NorthernLightsStory diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/background/NorthernLightsStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/background/NorthernLightsStory.kt index a5764e2be5..3f96ca9d74 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/background/NorthernLightsStory.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/background/NorthernLightsStory.kt @@ -1,4 +1,4 @@ -@file:Suppress("MagicNumber") +@file:Suppress("MagicNumber", "LongMethod") package com.tangem.feature.tester.presentation.storybook.page.background import androidx.compose.foundation.background diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/Build.kt new file mode 100644 index 0000000000..28eba8975c --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/Build.kt @@ -0,0 +1,19 @@ +@file:Suppress("MagicNumber", "LongMethod") +package com.tangem.feature.tester.presentation.storybook.page.badge + +import com.tangem.core.ui.ds.badge.TangemBadgeColor +import com.tangem.feature.tester.presentation.storybook.entity.TangemBadgeStory +import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater +import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory + +internal fun StateUpdater.build(): TangemBadgeStory { + return TangemBadgeStory( + selectedColor = TangemBadgeColor.Blue, + onColorChange = { color -> + updateStory { it.copy(selectedColor = color) } + }, + ) +} + +internal val tangemBadgeStoryFactory + 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/badge/TangemBadgeStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/TangemBadgeStory.kt new file mode 100644 index 0000000000..2caf62dae0 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/TangemBadgeStory.kt @@ -0,0 +1,222 @@ +@file:Suppress("MagicNumber", "LongMethod") +package com.tangem.feature.tester.presentation.storybook.page.badge + +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.badge.* +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.tester.presentation.storybook.entity.TangemBadgeStory + +private const val STATE_LABEL_WIDTH = 80 + +@Composable +internal fun TangemBadgeStory(state: TangemBadgeStory, modifier: Modifier = Modifier) { + LazyColumn( + contentPadding = PaddingValues(bottom = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier + .statusBarsPadding() + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1), + ) { + stickyHeader("color_toggle") { + ColorToggle( + selected = state.selectedColor, + onSelect = state.onColorChange, + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors2.surface.level1) + .padding(horizontal = 16.dp, vertical = 8.dp), + ) + } + TangemBadgeSize.entries.forEach { size -> + item(size.name) { + BadgeSizeSection(size = size, color = state.selectedColor) + } + } + } +} + +@Composable +private fun ColorToggle( + selected: TangemBadgeColor, + onSelect: (TangemBadgeColor) -> 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), + ) { + TangemBadgeColor.entries.forEach { color -> + ColorChip( + label = color.name, + selected = color == selected, + onClick = { onSelect(color) }, + modifier = Modifier.weight(1f), + ) + } + } +} + +@Composable +private fun ColorChip(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 BadgeSizeSection(size: TangemBadgeSize, color: TangemBadgeColor) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors2.surface.level1) + .padding(horizontal = 16.dp, vertical = 12.dp), + ) { + Text( + text = size.name, + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + ) + TangemBadgeShape.entries.forEach { shape -> + BadgeShapeGroup(size = size, shape = shape, color = color) + } + } + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(horizontal = 16.dp), + ) +} + +@Composable +private fun BadgeShapeGroup(size: TangemBadgeSize, shape: TangemBadgeShape, color: TangemBadgeColor) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text( + text = shape.name, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + ColumnHeaderRow() + TangemBadgeType.entries.forEach { type -> + BadgeTypeRow(size = size, shape = shape, color = color, type = type) + } + } +} + +@Composable +private fun ColumnHeaderRow() { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Spacer(Modifier.width(STATE_LABEL_WIDTH.dp)) + Text( + text = "Text + Icon", + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.weight(1f), + ) + Text( + text = "Text", + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.weight(1f), + ) + Text( + text = "Icon", + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.weight(1f), + ) + } +} + +@Composable +private fun BadgeTypeRow( + size: TangemBadgeSize, + shape: TangemBadgeShape, + color: TangemBadgeColor, + type: TangemBadgeType, +) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = type.name, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.width(STATE_LABEL_WIDTH.dp), + ) + Box( + contentAlignment = Alignment.CenterStart, + modifier = Modifier.weight(1f), + ) { + TangemBadge( + text = stringReference("New"), + iconRes = R.drawable.ic_information_24, + size = size, + shape = shape, + color = color, + type = type, + iconPosition = TangemBadgeIconPosition.Start, + ) + } + Box( + contentAlignment = Alignment.CenterStart, + modifier = Modifier.weight(1f), + ) { + TangemBadge( + text = stringReference("New"), + size = size, + shape = shape, + color = color, + type = type, + ) + } + Box( + contentAlignment = Alignment.CenterStart, + modifier = Modifier.weight(1f), + ) { + TangemBadge( + iconRes = R.drawable.ic_information_24, + size = size, + shape = shape, + color = color, + type = type, + iconPosition = TangemBadgeIconPosition.Start, + ) + } + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/buttons/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/buttons/Build.kt index 4ea6b7efbb..00af9f20b0 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/buttons/Build.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/buttons/Build.kt @@ -1,3 +1,4 @@ +@file:Suppress("MagicNumber", "LongMethod") package com.tangem.feature.tester.presentation.storybook.page.buttons import com.tangem.feature.tester.presentation.storybook.entity.ButtonsStory 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 d57ebfdd8a..9d9a6a9c09 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 @@ -1,3 +1,4 @@ +@file:Suppress("MagicNumber", "LongMethod") package com.tangem.feature.tester.presentation.storybook.page.buttons import androidx.compose.foundation.background @@ -24,6 +25,7 @@ internal fun ButtonsStory(modifier: Modifier = Modifier) { contentPadding = PaddingValues(vertical = 16.dp), verticalArrangement = Arrangement.spacedBy(8.dp), modifier = modifier + .statusBarsPadding() .fillMaxSize() .background(TangemTheme.colors2.surface.level1), ) { diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/checkbox/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/checkbox/Build.kt new file mode 100644 index 0000000000..cc54b661b4 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/checkbox/Build.kt @@ -0,0 +1,22 @@ +@file:Suppress("MagicNumber", "LongMethod") +package com.tangem.feature.tester.presentation.storybook.page.checkbox + +import com.tangem.feature.tester.presentation.storybook.entity.TangemCheckboxStory +import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater +import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory + +internal fun StateUpdater.build(): TangemCheckboxStory { + return TangemCheckboxStory( + isRoundedChecked = false, + onRoundedCheckedChange = { checked -> + updateStory { it.copy(isRoundedChecked = checked) } + }, + isCircleChecked = false, + onCircleCheckedChange = { checked -> + updateStory { it.copy(isCircleChecked = checked) } + }, + ) +} + +internal val tangemCheckboxStoryFactory + 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/checkbox/TangemCheckboxStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/checkbox/TangemCheckboxStory.kt new file mode 100644 index 0000000000..fbba0e7b64 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/checkbox/TangemCheckboxStory.kt @@ -0,0 +1,137 @@ +@file:Suppress("MagicNumber", "LongMethod") +package com.tangem.feature.tester.presentation.storybook.page.checkbox + +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.unit.dp +import com.tangem.core.ui.ds.checkbox.TangemCheckbox +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.tester.presentation.storybook.entity.TangemCheckboxStory + +private const val STATE_LABEL_WIDTH = 80 + +@Composable +internal fun TangemCheckboxStory(state: TangemCheckboxStory, modifier: Modifier = Modifier) { + LazyColumn( + contentPadding = PaddingValues(vertical = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1), + ) { + item("grid") { + CheckboxGrid(state = state) + } + } +} + +@Composable +private fun CheckboxGrid(state: TangemCheckboxStory) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors2.surface.level1) + .padding(horizontal = 16.dp, vertical = 12.dp), + ) { + ColumnHeaderRow() + CheckboxRow( + label = "Rounded", + isChecked = state.isRoundedChecked, + onCheckedChange = state.onRoundedCheckedChange, + isRounded = true, + ) + CheckboxRow( + label = "Circle", + isChecked = state.isCircleChecked, + onCheckedChange = state.onCircleCheckedChange, + isRounded = false, + ) + } + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(horizontal = 16.dp), + ) +} + +@Composable +private fun ColumnHeaderRow() { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Spacer(Modifier.width(STATE_LABEL_WIDTH.dp)) + Text( + text = "Enabled", + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.weight(1f), + ) + Text( + text = "Disabled", + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.weight(1f), + ) + Text( + text = "Disabled (on)", + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.weight(1f), + ) + } +} + +@Composable +private fun CheckboxRow(label: String, isChecked: Boolean, onCheckedChange: (Boolean) -> Unit, isRounded: Boolean) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = label, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.width(STATE_LABEL_WIDTH.dp), + ) + Box( + contentAlignment = Alignment.CenterStart, + modifier = Modifier.weight(1f), + ) { + TangemCheckbox( + isChecked = isChecked, + isRounded = isRounded, + isEnabled = true, + onCheckedChange = onCheckedChange, + ) + } + Box( + contentAlignment = Alignment.CenterStart, + modifier = Modifier.weight(1f), + ) { + TangemCheckbox( + isChecked = false, + isRounded = isRounded, + isEnabled = false, + onCheckedChange = {}, + ) + } + Box( + contentAlignment = Alignment.CenterStart, + modifier = Modifier.weight(1f), + ) { + TangemCheckbox( + isChecked = true, + isRounded = isRounded, + isEnabled = false, + onCheckedChange = {}, + ) + } + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/message/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/message/Build.kt new file mode 100644 index 0000000000..8f357d4941 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/message/Build.kt @@ -0,0 +1,19 @@ +@file:Suppress("MagicNumber", "LongMethod") +package com.tangem.feature.tester.presentation.storybook.page.message + +import com.tangem.core.ui.ds.message.TangemMessageEffect +import com.tangem.feature.tester.presentation.storybook.entity.TangemMessageStory +import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater +import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory + +internal fun StateUpdater.build(): TangemMessageStory { + return TangemMessageStory( + selectedEffect = TangemMessageEffect.None, + onEffectChange = { newEffect -> + updateStory { it.copy(selectedEffect = newEffect) } + }, + ) +} + +internal val tangemMessageStoryFactory + 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/message/TangemMessageStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/message/TangemMessageStory.kt new file mode 100644 index 0000000000..f43e8ec903 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/message/TangemMessageStory.kt @@ -0,0 +1,233 @@ +@file:Suppress("MagicNumber", "LongMethod") +package com.tangem.feature.tester.presentation.storybook.page.message + +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.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.button.TangemButtonType +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds.message.* +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.tester.presentation.storybook.entity.TangemMessageStory +import kotlinx.collections.immutable.persistentListOf + +@Composable +internal fun TangemMessageStory(state: TangemMessageStory, modifier: Modifier = Modifier) { + LazyColumn( + contentPadding = PaddingValues(bottom = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier + .fillMaxSize() + .statusBarsPadding() + .background(TangemTheme.colors2.surface.level1), + ) { + stickyHeader("effect_toggle") { + EffectToggle( + selected = state.selectedEffect, + onSelect = state.onEffectChange, + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors2.surface.level1) + .padding(horizontal = 16.dp, vertical = 8.dp), + ) + } + + item("plain") { + VariantSection(label = "No icon, no buttons") { + TangemMessage( + messageUM = TangemMessageUM( + id = "plain", + title = stringReference("Update available"), + subtitle = stringReference("A new firmware version is ready to install on your card."), + messageEffect = state.selectedEffect, + ), + modifier = Modifier.fillMaxWidth(), + ) + } + } + + item("icon") { + VariantSection(label = "With icon") { + TangemMessage( + messageUM = TangemMessageUM( + id = "icon", + title = stringReference("Wallet backup missing"), + subtitle = stringReference("To protect your assets, complete the backup process."), + messageEffect = state.selectedEffect, + iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24), + ), + modifier = Modifier.fillMaxWidth(), + ) + } + } + + item("centered") { + VariantSection(label = "Centered") { + TangemMessage( + messageUM = TangemMessageUM( + id = "centered", + title = stringReference("Scan your card"), + subtitle = stringReference("Hold the card to the back of your phone."), + messageEffect = state.selectedEffect, + iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24), + isCentered = true, + ), + modifier = Modifier.fillMaxWidth(), + ) + } + } + + item("1btn") { + VariantSection(label = "With 1 button") { + TangemMessage( + messageUM = TangemMessageUM( + id = "1btn", + title = stringReference("Generate addresses"), + subtitle = stringReference("Generate addresses for 2 new networks using your card."), + messageEffect = state.selectedEffect, + iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24), + buttonsUM = persistentListOf( + TangemMessageButtonUM( + text = stringReference("Generate"), + type = TangemButtonType.Primary, + iconRes = R.drawable.ic_tangem_24, + onClick = {}, + ), + ), + ), + modifier = Modifier.fillMaxWidth(), + ) + } + } + + item("2btn") { + VariantSection(label = "With 2 buttons") { + TangemMessage( + messageUM = TangemMessageUM( + id = "2btn", + title = stringReference("Rate the app"), + subtitle = stringReference("How do you like Tangem so far?"), + messageEffect = state.selectedEffect, + iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24), + buttonsUM = persistentListOf( + TangemMessageButtonUM( + text = stringReference("Love it!"), + type = TangemButtonType.PrimaryInverse, + onClick = {}, + ), + TangemMessageButtonUM( + text = stringReference("Can be better"), + type = TangemButtonType.Primary, + onClick = {}, + ), + ), + ), + modifier = Modifier.fillMaxWidth(), + ) + } + } + + item("close") { + VariantSection(label = "With close button") { + TangemMessage( + messageUM = TangemMessageUM( + id = "close", + title = stringReference("Note top up"), + subtitle = stringReference("To activate the card, top it up with at least 1 XLM."), + messageEffect = state.selectedEffect, + iconUM = TangemIconUM.Icon(R.drawable.ic_attention_default_24), + onCloseClick = {}, + ), + modifier = Modifier.fillMaxWidth(), + ) + } + } + } +} + +@Composable +private fun EffectToggle( + selected: TangemMessageEffect, + onSelect: (TangemMessageEffect) -> 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), + ) { + TangemMessageEffect.entries.forEach { effect -> + EffectChip( + label = effect.name, + selected = effect == selected, + onClick = { onSelect(effect) }, + modifier = Modifier.weight(1f), + ) + } + } +} + +@Composable +private fun EffectChip(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 VariantSection(label: String, content: @Composable ColumnScope.() -> Unit) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.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/opportunities/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/opportunities/Build.kt new file mode 100644 index 0000000000..12f5d224e7 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/opportunities/Build.kt @@ -0,0 +1,7 @@ +@file:Suppress("MagicNumber", "LongMethod") +package com.tangem.feature.tester.presentation.storybook.page.opportunities + +import com.tangem.feature.tester.presentation.storybook.entity.OpportunitiesBGStory +import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory + +internal val opportunitiesBGStoryFactory: StoryPageFactory = StoryPageFactory { OpportunitiesBGStory } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/opportunities/OpportunitiesBGStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/opportunities/OpportunitiesBGStory.kt new file mode 100644 index 0000000000..ca09305652 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/opportunities/OpportunitiesBGStory.kt @@ -0,0 +1,122 @@ +@file:Suppress("MagicNumber", "LongMethod") +package com.tangem.feature.tester.presentation.storybook.page.opportunities + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape +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.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.opportunities.OpportunitiesBG +import com.tangem.core.ui.res.TangemTheme + +private data class IconVariant( + val label: String, + val icon: TangemIconUM, +) + +private val variants = listOf( + IconVariant( + label = "Bitcoin", + icon = TangemIconUM.Currency( + CurrencyIconState.CoinIcon( + url = null, + fallbackResId = R.drawable.img_btc_22, + isGrayscale = false, + shouldShowCustomBadge = false, + ), + ), + ), + IconVariant( + label = "Solana", + icon = TangemIconUM.Currency( + CurrencyIconState.CoinIcon( + url = null, + fallbackResId = R.drawable.img_solana_22, + isGrayscale = false, + shouldShowCustomBadge = false, + ), + ), + ), + IconVariant( + label = "Avalanche", + icon = TangemIconUM.Currency( + CurrencyIconState.CoinIcon( + url = null, + fallbackResId = R.drawable.img_avalanche_22, + isGrayscale = false, + shouldShowCustomBadge = false, + ), + ), + ), + IconVariant( + label = "BNB Smart Chain", + icon = TangemIconUM.Currency( + CurrencyIconState.CoinIcon( + url = null, + fallbackResId = R.drawable.img_bsc_22, + isGrayscale = false, + shouldShowCustomBadge = false, + ), + ), + ), + IconVariant( + label = "Cardano", + icon = TangemIconUM.Currency( + CurrencyIconState.CoinIcon( + url = null, + fallbackResId = R.drawable.img_cardano_22, + isGrayscale = false, + shouldShowCustomBadge = false, + ), + ), + ), +) + +@Composable +internal fun OpportunitiesBGStory(modifier: Modifier = Modifier) { + LazyColumn( + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 24.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = modifier + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1), + ) { + variants.forEach { variant -> + item(variant.label) { + OpportunitiesBG( + icon = variant.icon, + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(16.dp)), + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 24.dp), + ) { + TangemIcon( + tangemIconUM = variant.icon, + modifier = Modifier.size(40.dp), + ) + Text( + text = variant.label, + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tabs/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tabs/Build.kt new file mode 100644 index 0000000000..e8dd4f694f --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tabs/Build.kt @@ -0,0 +1,7 @@ +@file:Suppress("MagicNumber", "LongMethod") +package com.tangem.feature.tester.presentation.storybook.page.tabs + +import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory +import com.tangem.feature.tester.presentation.storybook.entity.TangemSegmentedPickerStory + +internal val tangemSegmentedPickerStoryFactory: StoryPageFactory = StoryPageFactory { TangemSegmentedPickerStory } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tabs/TangemSegmentedPickerStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tabs/TangemSegmentedPickerStory.kt new file mode 100644 index 0000000000..df03dcb6d4 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tabs/TangemSegmentedPickerStory.kt @@ -0,0 +1,171 @@ +@file:Suppress("MagicNumber", "LongMethod") +package com.tangem.feature.tester.presentation.storybook.page.tabs + +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.graphics.Color +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.ds.tabs.TangemSegmentUM +import com.tangem.core.ui.ds.tabs.TangemSegmentedPicker +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +private val items2 = persistentListOf( + TangemSegmentUM(id = "all", title = stringReference("All")), + TangemSegmentUM(id = "tokens", title = stringReference("Tokens")), +) + +private val items3 = persistentListOf( + TangemSegmentUM(id = "1d", title = stringReference("1D")), + TangemSegmentUM(id = "1w", title = stringReference("1W")), + TangemSegmentUM(id = "1m", title = stringReference("1M")), +) + +private val items4 = persistentListOf( + TangemSegmentUM(id = "1d", title = stringReference("1D")), + TangemSegmentUM(id = "1w", title = stringReference("1W")), + TangemSegmentUM(id = "1m", title = stringReference("1M")), + TangemSegmentUM(id = "1y", title = stringReference("1Y")), +) + +private val items5 = persistentListOf( + TangemSegmentUM(id = "send", title = stringReference("Send")), + TangemSegmentUM(id = "receive", title = stringReference("Receive")), + TangemSegmentUM(id = "swap", title = stringReference("Swap")), + TangemSegmentUM(id = "buy", title = stringReference("Buy")), + TangemSegmentUM(id = "sell", title = stringReference("Sell")), +) + +private data class PickerConfig( + val label: String, + val hasSeparator: Boolean, + val isFixed: Boolean, +) + +private val configs = listOf( + PickerConfig("Default", hasSeparator = false, isFixed = false), + PickerConfig("Separator", hasSeparator = true, isFixed = false), + PickerConfig("Fixed", hasSeparator = false, isFixed = true), + PickerConfig("Fixed + Separator", hasSeparator = true, isFixed = true), +) + +@Composable +internal fun TangemSegmentedPickerStory(modifier: Modifier = Modifier) { + LazyColumn( + contentPadding = PaddingValues(vertical = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1), + ) { + item("default_surface") { + PickerSection( + title = "Default surface", + isAltSurface = false, + background = TangemTheme.colors2.surface.level1, + ) + } + item("alt_surface") { + PickerSection( + title = "Alt surface", + isAltSurface = true, + background = TangemTheme.colors2.surface.level2, + ) + } + item("segment_count") { + SegmentCountSection() + } + } +} + +@Composable +private fun PickerSection(title: String, isAltSurface: Boolean, background: Color) { + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier + .fillMaxWidth() + .background(background) + .padding(horizontal = 16.dp, vertical = 12.dp), + ) { + Text( + text = title, + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + ) + configs.forEach { config -> + PickerRow(config = config, isAltSurface = isAltSurface) + } + } + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(horizontal = 16.dp), + ) +} + +@Composable +private fun PickerRow(config: PickerConfig, isAltSurface: Boolean) { + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + Text( + text = config.label, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + TangemSegmentedPicker( + items = items4, + hasSeparator = config.hasSeparator, + isFixed = config.isFixed, + isAltSurface = isAltSurface, + onClick = {}, + modifier = if (config.isFixed) Modifier.fillMaxWidth() else Modifier, + ) + } +} + +@Composable +private fun SegmentCountSection() { + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors2.surface.level1) + .padding(horizontal = 16.dp, vertical = 12.dp), + ) { + Text( + text = "Segment count", + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + ) + SegmentCountRow(label = "2 segments", items = items2) + SegmentCountRow(label = "3 segments", items = items3) + SegmentCountRow(label = "4 segments", items = items4) + SegmentCountRow(label = "5 segments", items = items5) + } + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(horizontal = 16.dp), + ) +} + +@Composable +private fun SegmentCountRow(label: String, items: ImmutableList) { + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + Text( + text = label, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + TangemSegmentedPicker( + items = items, + isFixed = true, + onClick = {}, + modifier = Modifier.fillMaxWidth(), + ) + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tokenrow/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tokenrow/Build.kt new file mode 100644 index 0000000000..988f75af8a --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tokenrow/Build.kt @@ -0,0 +1,16 @@ +@file:Suppress("MagicNumber", "LongMethod") +package com.tangem.feature.tester.presentation.storybook.page.tokenrow + +import com.tangem.feature.tester.presentation.storybook.entity.TangemTokenRowStory +import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater +import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory + +internal fun StateUpdater.build(): TangemTokenRowStory { + return TangemTokenRowStory( + isBalanceHidden = false, + onBalanceHiddenToggle = { updateStory { it.copy(isBalanceHidden = !it.isBalanceHidden) } }, + ) +} + +internal val tangemTokenRowStoryFactory + 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/tokenrow/TangemTokenRowStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tokenrow/TangemTokenRowStory.kt new file mode 100644 index 0000000000..65c44d281d --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tokenrow/TangemTokenRowStory.kt @@ -0,0 +1,72 @@ +@file:Suppress("MagicNumber", "LongMethod") +package com.tangem.feature.tester.presentation.storybook.page.tokenrow + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Switch +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.unit.dp +import com.tangem.core.ui.ds.row.token.TangemTokenRow +import com.tangem.core.ui.ds.row.token.TangemTokenRow_PreviewProvider +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.tester.presentation.storybook.entity.TangemTokenRowStory + +@Composable +internal fun TangemTokenRowStory(state: TangemTokenRowStory, modifier: Modifier = Modifier) { + val rows = remember { TangemTokenRow_PreviewProvider().values.toList() } + + LazyColumn( + contentPadding = PaddingValues(bottom = 16.dp), + modifier = modifier + .statusBarsPadding() + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1), + ) { + stickyHeader("balance_toggle") { + BalanceToggle( + isHidden = state.isBalanceHidden, + onToggle = state.onBalanceHiddenToggle, + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors2.surface.level1) + .padding(horizontal = 16.dp, vertical = 8.dp), + ) + } + + items(rows, key = { it.id }) { um -> + TangemTokenRow( + tokenRowUM = um, + isBalanceHidden = state.isBalanceHidden, + reorderableTokenListState = null, + modifier = Modifier.background(TangemTheme.colors2.surface.level1), + ) + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(start = 16.dp), + ) + } + } +} + +@Composable +private fun BalanceToggle(isHidden: Boolean, onToggle: () -> Unit, modifier: Modifier = Modifier) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = modifier, + ) { + Text( + text = "isBalanceHidden", + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + ) + Switch(checked = isHidden, onCheckedChange = { onToggle() }) + } +} \ 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 4dc59eac12..0aa6cb3681 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 @@ -16,13 +16,25 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.tester.presentation.storybook.entity.StoryBookUM import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory import com.tangem.feature.tester.presentation.storybook.page.background.northernLightsStoryFactory +import com.tangem.feature.tester.presentation.storybook.page.badge.tangemBadgeStoryFactory import com.tangem.feature.tester.presentation.storybook.page.buttons.buttonsStoryFactory +import com.tangem.feature.tester.presentation.storybook.page.opportunities.opportunitiesBGStoryFactory +import com.tangem.feature.tester.presentation.storybook.page.message.tangemMessageStoryFactory +import com.tangem.feature.tester.presentation.storybook.page.checkbox.tangemCheckboxStoryFactory +import com.tangem.feature.tester.presentation.storybook.page.tabs.tangemSegmentedPickerStoryFactory +import com.tangem.feature.tester.presentation.storybook.page.tokenrow.tangemTokenRowStoryFactory private data class StoryItem(val title: String, val factory: StoryPageFactory) private fun buildStories() = listOf( StoryItem(title = "🔘 Buttons", factory = buttonsStoryFactory), + StoryItem(title = "🏷️ Badge", factory = tangemBadgeStoryFactory), + StoryItem(title = "✨ Opportunities BG", factory = opportunitiesBGStoryFactory), StoryItem(title = "🌌 Northern Lights Background", factory = northernLightsStoryFactory), + StoryItem(title = "💬 Message", factory = tangemMessageStoryFactory), + StoryItem(title = "🗂️ Segmented Picker", factory = tangemSegmentedPickerStoryFactory), + StoryItem(title = "☑️ Checkbox", factory = tangemCheckboxStoryFactory), + StoryItem(title = "🪙 Token Row", factory = tangemTokenRowStoryFactory), ) @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 1806965ad7..11a78b7e28 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 @@ -6,10 +6,22 @@ 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.NorthernLightsStory +import com.tangem.feature.tester.presentation.storybook.entity.OpportunitiesBGStory +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 +import com.tangem.feature.tester.presentation.storybook.entity.TangemCheckboxStory +import com.tangem.feature.tester.presentation.storybook.entity.TangemMessageStory +import com.tangem.feature.tester.presentation.storybook.entity.TangemSegmentedPickerStory +import com.tangem.feature.tester.presentation.storybook.entity.TangemTokenRowStory 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.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.tabs.TangemSegmentedPickerStory +import com.tangem.feature.tester.presentation.storybook.page.tokenrow.TangemTokenRowStory @Composable internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier) { @@ -17,12 +29,19 @@ internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier) AnimatedContent( targetState = state.currentPage, + contentKey = { it::class }, modifier = modifier, ) { storyState -> when (storyState) { StoryList -> StoryBookListScreen(state = state) is NorthernLightsStory -> NorthernLightsStory(state = storyState) ButtonsStory -> ButtonsStory() + is TangemBadgeStory -> TangemBadgeStory(state = storyState) + OpportunitiesBGStory -> OpportunitiesBGStory() + is TangemMessageStory -> TangemMessageStory(state = storyState) + is TangemCheckboxStory -> TangemCheckboxStory(state = storyState) + TangemSegmentedPickerStory -> TangemSegmentedPickerStory() + is TangemTokenRowStory -> TangemTokenRowStory(state = storyState) } } } \ No newline at end of file