diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/dropdownmenu/TangemDropdownMenu.kt b/core/ui/src/main/java/com/tangem/core/ui/components/dropdownmenu/TangemDropdownMenu.kt index fa6f78a262..899a888c8a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/dropdownmenu/TangemDropdownMenu.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/dropdownmenu/TangemDropdownMenu.kt @@ -79,7 +79,7 @@ private fun DropdownMenuContent( content: @Composable ColumnScope.() -> Unit, ) { // Menu open/close animation. - val transition = updateTransition(expandedStates, "DropDownMenu") + val transition = rememberTransition(expandedStates, "DropDownMenu") val scale by transition.animateFloat( transitionSpec = { diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/checkbox/TangemCheckbox.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/checkbox/TangemCheckbox.kt index 9abdda6653..f1bf0d3fdb 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/checkbox/TangemCheckbox.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/checkbox/TangemCheckbox.kt @@ -48,9 +48,9 @@ fun TangemCheckbox( isEnabled: Boolean = true, ) { val shape = if (isRounded) { - RoundedCornerShape(TangemTheme.dimens2.x1) - } else { CircleShape + } else { + RoundedCornerShape(TangemTheme.dimens2.x1) } Box( modifier = modifier diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/contextmenu/TangemContextMenu.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/contextmenu/TangemContextMenu.kt new file mode 100644 index 0000000000..69a475c2a8 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/contextmenu/TangemContextMenu.kt @@ -0,0 +1,288 @@ +package com.tangem.core.ui.ds.contextmenu + +import android.content.res.Configuration +import androidx.compose.animation.core.* +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.HorizontalDivider +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.TransformOrigin +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.* +import androidx.compose.ui.window.Popup +import androidx.compose.ui.window.PopupPositionProvider +import androidx.compose.ui.window.PopupProperties +import com.tangem.core.ui.components.haze.hazeEffectTangem +import com.tangem.core.ui.components.haze.hazeSourceTangem +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreviewRedesign +import com.tangem.core.ui.test.PopUpMenuTestTags +import dev.chrisbanes.haze.rememberHazeState +import kotlin.math.max +import kotlin.math.min + +/** + * Just copy paste [DropdownMenu] from material3 with deleting vertical paddings. + */ +@Composable +fun TangemContextMenu( + expanded: Boolean, + onDismissRequest: () -> Unit, + modifier: Modifier = Modifier, + offset: DpOffset = DpOffset.Zero, + properties: PopupProperties = PopupProperties(focusable = true), + content: @Composable ColumnScope.() -> Unit, +) { + val expandedStates = remember { MutableTransitionState(false) } + expandedStates.targetState = expanded + + if (expandedStates.currentState || expandedStates.targetState) { + val transformOriginState = remember { mutableStateOf(TransformOrigin.Center) } + val density = LocalDensity.current + val popupPositionProvider = DropdownMenuPositionProvider( + offset, + density, + ) { parentBounds, menuBounds -> + transformOriginState.value = calculateTransformOrigin(parentBounds, menuBounds) + } + + Popup( + onDismissRequest = onDismissRequest, + popupPositionProvider = popupPositionProvider, + properties = properties, + ) { + DropdownMenuContent( + expandedStates = expandedStates, + transformOriginState = transformOriginState, + modifier = modifier, + content = content, + ) + } + } +} + +private const val IN_TRANSITION_DURATION = 120 +private const val OUT_TRANSITION_DURATION = 75 + +@Suppress("ReusedModifierInstance", "MagicNumber") +@Composable +private fun DropdownMenuContent( + expandedStates: MutableTransitionState, + transformOriginState: MutableState, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit, +) { + // Menu open/close animation. + val transition = rememberTransition(expandedStates, "DropDownMenu") + + val scale by transition.animateFloat( + transitionSpec = { + if (false isTransitioningTo true) { + // Dismissed to expanded + tween( + durationMillis = IN_TRANSITION_DURATION, + easing = LinearOutSlowInEasing, + ) + } else { + // Expanded to dismissed. + tween( + durationMillis = 1, + delayMillis = OUT_TRANSITION_DURATION - 1, + ) + } + }, + label = "", + ) { isExpanded -> + if (isExpanded) { + // Menu is expanded. + 1f + } else { + // Menu is dismissed. + 0.8f + } + } + + val alpha by transition.animateFloat( + transitionSpec = { + if (false isTransitioningTo true) { + // Dismissed to expanded + tween(durationMillis = 30) + } else { + // Expanded to dismissed. + tween(durationMillis = OUT_TRANSITION_DURATION) + } + }, + label = "", + ) { isExpanded -> + if (isExpanded) { + // Menu is expanded. + 1f + } else { + // Menu is dismissed. + 0f + } + } + Card( + modifier = Modifier + .clip(RoundedCornerShape(TangemTheme.dimens2.x5)) + .graphicsLayer { + scaleX = scale + scaleY = scale + this.alpha = alpha + transformOrigin = transformOriginState.value + }, + elevation = CardDefaults.cardElevation(), + ) { + Column( + modifier = modifier + .width(IntrinsicSize.Max) + .verticalScroll(rememberScrollState()) + .clip(RoundedCornerShape(TangemTheme.dimens2.x5)) + .background(TangemTheme.colors2.contextMenu.background) + .testTag(PopUpMenuTestTags.CONTAINER), + content = content, + ) + } +} + +private fun calculateTransformOrigin(parentBounds: IntRect, menuBounds: IntRect): TransformOrigin { + val pivotX = when { + menuBounds.left >= parentBounds.right -> 0f + menuBounds.right <= parentBounds.left -> 1f + menuBounds.width == 0 -> 0f + else -> { + val intersectionCenter = + (max(parentBounds.left, menuBounds.left) + min(parentBounds.right, menuBounds.right)) / 2 + (intersectionCenter - menuBounds.left).toFloat() / menuBounds.width + } + } + val pivotY = when { + menuBounds.top >= parentBounds.bottom -> 0f + menuBounds.bottom <= parentBounds.top -> 1f + menuBounds.height == 0 -> 0f + else -> { + val intersectionCenter = + (max(parentBounds.top, menuBounds.top) + min(parentBounds.bottom, menuBounds.bottom)) / 2 + (intersectionCenter - menuBounds.top).toFloat() / menuBounds.height + } + } + return TransformOrigin(pivotX, pivotY) +} + +private val MenuVerticalMargin = 48.dp + +@Immutable +internal data class DropdownMenuPositionProvider( + val contentOffset: DpOffset, + val density: Density, + val onPositionCalculated: (IntRect, IntRect) -> Unit = { _, _ -> }, +) : PopupPositionProvider { + override fun calculatePosition( + anchorBounds: IntRect, + windowSize: IntSize, + layoutDirection: LayoutDirection, + popupContentSize: IntSize, + ): IntOffset { + // The min margin above and below the menu, relative to the screen. + val verticalMargin = with(density) { MenuVerticalMargin.roundToPx() } + // The content offset specified using the dropdown offset parameter. + val contentOffsetX = with(density) { contentOffset.x.roundToPx() } + val contentOffsetY = with(density) { contentOffset.y.roundToPx() } + + // Compute horizontal position. + val toRight = anchorBounds.left + contentOffsetX + val toLeft = anchorBounds.right - contentOffsetX - popupContentSize.width + val toDisplayRight = windowSize.width - popupContentSize.width + val toDisplayLeft = 0 + val x = if (layoutDirection == LayoutDirection.Ltr) { + sequenceOf( + toRight, + toLeft, + // If the anchor gets outside of the window on the left, we want to position + // toDisplayLeft for proximity to the anchor. Otherwise, toDisplayRight. + if (anchorBounds.left >= 0) toDisplayRight else toDisplayLeft, + ) + } else { + sequenceOf( + toLeft, + toRight, + // If the anchor gets outside of the window on the right, we want to position + // toDisplayRight for proximity to the anchor. Otherwise, toDisplayLeft. + if (anchorBounds.right <= windowSize.width) toDisplayLeft else toDisplayRight, + ) + }.firstOrNull { + it >= 0 && it + popupContentSize.width <= windowSize.width + } ?: toLeft + + // Compute vertical position. + val toBottom = maxOf(anchorBounds.bottom + contentOffsetY, verticalMargin) + val toTop = anchorBounds.top - contentOffsetY - popupContentSize.height + val toCenter = anchorBounds.top - popupContentSize.height / 2 + val toDisplayBottom = windowSize.height - popupContentSize.height - verticalMargin + val y = sequenceOf(toBottom, toTop, toCenter, toDisplayBottom).firstOrNull { element -> + element >= verticalMargin && + element + popupContentSize.height <= windowSize.height - verticalMargin + } ?: toTop + + onPositionCalculated( + anchorBounds, + IntRect( + left = x, + top = y, + right = x + popupContentSize.width, + bottom = y + popupContentSize.height, + ), + ) + return IntOffset(x, y) + } +} + +// region Preview +@Composable +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun TangemContextMenu_Preview() { + TangemThemePreviewRedesign { + val hazeState = rememberHazeState() + Column( + modifier = Modifier + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1) + .hazeSourceTangem(state = hazeState, zIndex = -1f), + ) { + TangemContextMenu( + expanded = true, + onDismissRequest = { }, + modifier = Modifier.hazeEffectTangem(state = hazeState), + ) { + TangemContextMenuCheckboxItem( + title = stringReference("Sort by balance"), + isChecked = true, + onClick = {}, + ) + HorizontalDivider( + thickness = 0.5.dp, + color = TangemTheme.colors2.border.neutral.quaternary, + ) + TangemContextMenuCheckboxItem( + title = stringReference("Group tokens"), + isChecked = false, + onClick = {}, + ) + } + } + } +} +// endregion \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/contextmenu/TangemContextMenuCheckboxItem.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/contextmenu/TangemContextMenuCheckboxItem.kt new file mode 100644 index 0000000000..6ff6196041 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/contextmenu/TangemContextMenuCheckboxItem.kt @@ -0,0 +1,46 @@ +package com.tangem.core.ui.ds.contextmenu + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +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.checkbox.TangemCheckbox +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.clickableSingle +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme + +/** + * Item with checkbox for [TangemContextMenu]. + */ +@Composable +fun TangemContextMenuCheckboxItem(title: TextReference, isChecked: Boolean, onClick: () -> Unit) { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .width(238.dp) + .clickableSingle(onClick = onClick) + .padding( + vertical = TangemTheme.dimens2.x5, + horizontal = TangemTheme.dimens2.x4, + ), + ) { + Text( + text = title.resolveReference(), + style = TangemTheme.typography2.headingSemibold17, + color = TangemTheme.colors2.text.neutral.primary, + ) + TangemCheckbox( + modifier = Modifier, + isRounded = true, + isChecked = isChecked, + onCheckedChange = { /* no-op */ }, + ) + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRow.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRow.kt index c635dff1a6..64d73f459d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRow.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRow.kt @@ -1,22 +1,18 @@ package com.tangem.core.ui.ds.row.header import android.content.res.Configuration -import androidx.annotation.DrawableRes import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider @@ -25,10 +21,13 @@ import com.tangem.core.ui.components.SpacerWMax import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.ds.image.TangemIcon import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds.row.internal.TangemRowTail +import com.tangem.core.ui.ds.row.internal.TangemRowTailUM import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.ui.test.TokenElementsTestTags +import org.burnoutcrew.reorderable.ReorderableLazyListState /** * UI model for header row component @@ -37,13 +36,19 @@ import com.tangem.core.ui.test.TokenElementsTestTags * @param modifier Modifier for the composable */ @Composable -fun TangemHeaderRow(headerRowUM: TangemHeaderRowUM, modifier: Modifier = Modifier, isBalanceHidden: Boolean = false) { +fun TangemHeaderRow( + headerRowUM: TangemHeaderRowUM, + modifier: Modifier = Modifier, + reorderableState: ReorderableLazyListState? = null, + isBalanceHidden: Boolean = false, +) { TangemHeaderRow( headTangemIconUM = headerRowUM.startIconUM, - footerTangemIconRes = headerRowUM.endIconRes, + tailUM = headerRowUM.tailUM, title = headerRowUM.title, subtitle = headerRowUM.subtitle, isBalanceHidden = isBalanceHidden, + reorderableState = reorderableState, modifier = modifier, ) } @@ -54,7 +59,7 @@ fun TangemHeaderRow(headerRowUM: TangemHeaderRowUM, modifier: Modifier = Modifie * @param modifier Modifier for the composable * @param subtitle Optional subtitle as a TextReference * @param onItemClick Optional click callback for the row - * @param footerTangemIconRes Optional drawable resource ID for the footer icon + * @param tailUM Optional TailUM for the tail content * @param titleContent Composable lambda for the title content * @param headContent Composable lambda for the head content */ @@ -64,7 +69,7 @@ fun TangemHeaderRow( isBalanceHidden: Boolean = false, subtitle: TextReference? = null, onItemClick: (() -> Unit)? = null, - @DrawableRes footerTangemIconRes: Int? = null, + tailUM: TangemRowTailUM = TangemRowTailUM.Empty, titleContent: @Composable (Modifier) -> Unit, headContent: @Composable (Modifier) -> Unit, ) { @@ -101,17 +106,7 @@ fun TangemHeaderRow( ) } SpacerWMax() - AnimatedVisibility( - visible = footerTangemIconRes != null, - ) { - val wrappedIconUM = remember(this) { requireNotNull(footerTangemIconRes) } - Icon( - imageVector = ImageVector.vectorResource(id = wrappedIconUM), - contentDescription = null, - tint = TangemTheme.colors2.graphic.neutral.secondary, - modifier = Modifier.size(TangemTheme.dimens2.x4), - ) - } + TangemRowTail(tangemRowTailUM = tailUM) } } @@ -133,7 +128,8 @@ fun TangemHeaderRow( isBalanceHidden: Boolean = false, subtitle: TextReference? = null, headTangemIconUM: TangemIconUM? = null, - @DrawableRes footerTangemIconRes: Int? = null, + tailUM: TangemRowTailUM = TangemRowTailUM.Empty, + reorderableState: ReorderableLazyListState? = null, isEnabled: Boolean = false, onItemClick: (() -> Unit)? = null, ) { @@ -182,17 +178,10 @@ fun TangemHeaderRow( ) } SpacerWMax() - AnimatedVisibility( - visible = footerTangemIconRes != null, - ) { - val wrappedIconUM = remember(this) { requireNotNull(footerTangemIconRes) } - Icon( - imageVector = ImageVector.vectorResource(id = wrappedIconUM), - contentDescription = null, - tint = TangemTheme.colors2.graphic.neutral.secondary, - modifier = Modifier.size(TangemTheme.dimens2.x4), - ) - } + TangemRowTail( + tangemRowTailUM = tailUM, + reorderableState = reorderableState, + ) } } @@ -217,7 +206,25 @@ private class PreviewProvider : PreviewParameterProvider { startIconUM = TangemIconUM.Currency( currencyIconState = CurrencyIconState.Locked, ), - endIconRes = R.drawable.ic_minimize_24, + tailUM = TangemRowTailUM.Empty, + title = stringReference("Account"), + subtitle = stringReference("\$ 42,900.17"), + ), + TangemHeaderRowUM( + id = "1", + startIconUM = TangemIconUM.Currency( + currencyIconState = CurrencyIconState.Locked, + ), + tailUM = TangemRowTailUM.Icon(R.drawable.ic_arrow_collapse_24), + title = stringReference("Account"), + subtitle = stringReference("\$ 42,900.17"), + ), + TangemHeaderRowUM( + id = "1", + startIconUM = TangemIconUM.Currency( + currencyIconState = CurrencyIconState.Locked, + ), + tailUM = TangemRowTailUM.Icon(R.drawable.ic_group_drop_24), title = stringReference("Account"), subtitle = stringReference("\$ 42,900.17"), ), diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRowUM.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRowUM.kt index fd69abb660..28c3d44501 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRowUM.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/header/TangemHeaderRowUM.kt @@ -1,9 +1,9 @@ package com.tangem.core.ui.ds.row.header -import androidx.annotation.DrawableRes import androidx.compose.runtime.Immutable import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.row.TangemRowUM +import com.tangem.core.ui.ds.row.internal.TangemRowTailUM import com.tangem.core.ui.extensions.TextReference /** @@ -13,7 +13,7 @@ import com.tangem.core.ui.extensions.TextReference * @param title Title text reference * @param subtitle Subtitle text reference (optional) * @param startIconUM Icon UI model (optional) - * @param endIconRes Icon UI model (optional) + * @param tailUM Tail UI model (optional) * @param isEnabled Flag indicating if click is enabled * @param onItemClick Callback for item click (optional) */ @@ -23,7 +23,7 @@ data class TangemHeaderRowUM( val title: TextReference, val subtitle: TextReference? = null, val startIconUM: TangemIconUM? = null, - @DrawableRes val endIconRes: Int? = null, + val tailUM: TangemRowTailUM = TangemRowTailUM.Empty, val isEnabled: Boolean = false, val onItemClick: (() -> Unit)? = null, ) : TangemRowUM \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowTail.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/internal/TangemRowTail.kt similarity index 59% rename from core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowTail.kt rename to core/ui/src/main/java/com/tangem/core/ui/ds/row/internal/TangemRowTail.kt index f3416f8a29..913d8b2bea 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TokenRowTail.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/internal/TangemRowTail.kt @@ -1,7 +1,9 @@ -package com.tangem.core.ui.ds.row.token.internal +package com.tangem.core.ui.ds.row.internal +import androidx.annotation.DrawableRes import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon @@ -9,11 +11,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow -import com.tangem.core.ui.R -import com.tangem.core.ui.ds.row.token.TangemTokenRowUM import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveAnnotatedReference import com.tangem.core.ui.res.TangemTheme @@ -22,37 +23,48 @@ import org.burnoutcrew.reorderable.ReorderableLazyListState import org.burnoutcrew.reorderable.detectReorder @Composable -internal fun TokenRowTail( - tailUM: TangemTokenRowUM.TailUM, - reorderableTokenListState: ReorderableLazyListState?, +internal fun TangemRowTail( + tangemRowTailUM: TangemRowTailUM, modifier: Modifier = Modifier, + reorderableState: ReorderableLazyListState? = null, ) { AnimatedContent( - targetState = tailUM, + targetState = tangemRowTailUM, label = "Update non content fiat block", - modifier = modifier, + modifier = modifier.height(TangemTheme.dimens2.x4), contentKey = { it::class.java }, ) { animatedState -> val innerModifier = Modifier.padding(start = TangemTheme.dimens2.x2) when (animatedState) { - TangemTokenRowUM.TailUM.Empty -> Unit - is TangemTokenRowUM.TailUM.Draggable -> DraggableImage( - reorderableTokenListState = reorderableTokenListState, + TangemRowTailUM.Empty -> Unit + is TangemRowTailUM.Draggable -> DraggableImage( + iconRes = animatedState.iconRes, + reorderableState = reorderableState, modifier = innerModifier, ) - is TangemTokenRowUM.TailUM.Text -> ContentText(text = animatedState.text, modifier = innerModifier) + is TangemRowTailUM.Text -> ContentText(text = animatedState.text, modifier = innerModifier) + is TangemRowTailUM.Icon -> Icon( + imageVector = ImageVector.vectorResource(animatedState.iconRes), + contentDescription = null, + modifier = modifier, + tint = TangemTheme.colors2.graphic.neutral.tertiaryConstant, + ) } } } @Composable -private fun DraggableImage(reorderableTokenListState: ReorderableLazyListState?, modifier: Modifier = Modifier) { +private fun DraggableImage( + @DrawableRes iconRes: Int, + reorderableState: ReorderableLazyListState?, + modifier: Modifier = Modifier, +) { Box( modifier = modifier .size(size = TangemTheme.dimens2.x6) .then( - other = if (reorderableTokenListState != null) { - Modifier.detectReorder(reorderableTokenListState) + other = if (reorderableState != null) { + Modifier.detectReorder(reorderableState) } else { Modifier }, @@ -61,7 +73,7 @@ private fun DraggableImage(reorderableTokenListState: ReorderableLazyListState?, contentAlignment = Alignment.Center, ) { Icon( - painter = painterResource(id = R.drawable.ic_drag_24), + imageVector = ImageVector.vectorResource(iconRes), tint = TangemTheme.colors2.graphic.neutral.tertiaryConstant, contentDescription = null, ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/internal/TangemRowTailUM.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/internal/TangemRowTailUM.kt new file mode 100644 index 0000000000..841faea488 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/internal/TangemRowTailUM.kt @@ -0,0 +1,25 @@ +package com.tangem.core.ui.ds.row.internal + +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Immutable +import com.tangem.core.ui.extensions.TextReference + +/** + * Tail UI model for row components + */ +@Immutable +sealed class TangemRowTailUM { + data class Text( + val text: TextReference, + ) : TangemRowTailUM() + + data class Icon( + @DrawableRes val iconRes: Int, + ) : TangemRowTailUM() + + data class Draggable( + @DrawableRes val iconRes: Int, + ) : TangemRowTailUM() + + data object Empty : TangemRowTailUM() +} \ No newline at end of file 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 6aaafb85b9..cd2b93a853 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 @@ -19,6 +19,7 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter import com.tangem.core.ui.ds.image.TangemIcon import com.tangem.core.ui.ds.row.TangemRowContainer import com.tangem.core.ui.ds.row.TangemRowLayoutId +import com.tangem.core.ui.ds.row.internal.TangemRowTail import com.tangem.core.ui.ds.row.token.internal.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreviewRedesign @@ -30,16 +31,16 @@ import org.burnoutcrew.reorderable.ReorderableLazyListState * * [Token Row](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8207-17583&t=k8dyaykorsNocGVq-4) * - * @param tokenRowUM The user model containing the data for the token row. - * @param isBalanceHidden A boolean indicating whether the balance should be hidden. - * @param reorderableTokenListState The state of the reorderable lazy list, if applicable. - * @param modifier The modifier to be applied to the row. + * @param tokenRowUM The user model containing the data for the token row. + * @param isBalanceHidden A boolean indicating whether the balance should be hidden. + * @param reorderableState The state of the reorderable lazy list, if applicable. + * @param modifier The modifier to be applied to the row. */ @Composable fun TangemTokenRow( tokenRowUM: TangemTokenRowUM, isBalanceHidden: Boolean, - reorderableTokenListState: ReorderableLazyListState?, + reorderableState: ReorderableLazyListState?, modifier: Modifier = Modifier, ) { TangemRowContainer( @@ -88,9 +89,9 @@ fun TangemTokenRow( .testTag(tag = TokenElementsTestTags.TOKEN_CRYPTO_AMOUNT), ) - TokenRowTail( - tailUM = tokenRowUM.tailUM, - reorderableTokenListState = reorderableTokenListState, + TangemRowTail( + tangemRowTailUM = tokenRowUM.tailUM, + reorderableState = reorderableState, modifier = Modifier .layoutId(layoutId = TangemRowLayoutId.TAIL) .testTag(tag = TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK), @@ -114,18 +115,18 @@ fun TangemTokenRow( * * [Token Row](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8207-17583&t=k8dyaykorsNocGVq-4) * - * @param tokenRowUM The user model containing the data for the token row. - * @param headComponent The composable function representing the head component. - * @param titleComponent The composable function representing the title component. - * @param isBalanceHidden A boolean indicating whether the balance should be hidden. - * @param reorderableTokenListState The state of the reorderable lazy list, if applicable. - * @param modifier The modifier to be applied to the row. + * @param tokenRowUM The user model containing the data for the token row. + * @param headComponent The composable function representing the head component. + * @param titleComponent The composable function representing the title component. + * @param isBalanceHidden A boolean indicating whether the balance should be hidden. + * @param reorderableState The state of the reorderable lazy list, if applicable. + * @param modifier The modifier to be applied to the row. */ @Composable fun TangemTokenRow( tokenRowUM: TangemTokenRowUM, isBalanceHidden: Boolean, - reorderableTokenListState: ReorderableLazyListState?, + reorderableState: ReorderableLazyListState?, modifier: Modifier = Modifier, headComponent: @Composable (Modifier) -> Unit, titleComponent: @Composable (Modifier) -> Unit, @@ -183,9 +184,9 @@ fun TangemTokenRow( .testTag(tag = TokenElementsTestTags.TOKEN_CRYPTO_AMOUNT), ) - TokenRowTail( - tailUM = tokenRowUM.tailUM, - reorderableTokenListState = reorderableTokenListState, + TangemRowTail( + tangemRowTailUM = tokenRowUM.tailUM, + reorderableState = reorderableState, modifier = Modifier .layoutId(layoutId = TangemRowLayoutId.TAIL) .testTag(tag = TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK), @@ -232,7 +233,7 @@ private fun TangemTokenRow_Preview( TangemTokenRow( tokenRowUM = tokenRowUM, isBalanceHidden = false, - reorderableTokenListState = null, + reorderableState = null, modifier = Modifier.background(TangemTheme.colors2.surface.level1), ) } diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRowUM.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRowUM.kt index b95af763cd..c183f9863b 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRowUM.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRowUM.kt @@ -6,6 +6,7 @@ import com.tangem.core.ui.components.marketprice.PriceChangeState import com.tangem.core.ui.ds.badge.TangemBadgeUM import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.row.TangemRowUM +import com.tangem.core.ui.ds.row.internal.TangemRowTailUM import com.tangem.core.ui.extensions.TextReference import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -32,7 +33,7 @@ sealed class TangemTokenRowUM : TangemRowUM { abstract val bottomEndContentUM: EndContentUM /** Token row tail UM */ - abstract val tailUM: TailUM + abstract val tailUM: TangemRowTailUM /** Promo banner UM */ abstract val promoBannerUM: PromoBannerUM @@ -54,7 +55,7 @@ sealed class TangemTokenRowUM : TangemRowUM { override val topEndContentUM: EndContentUM, override val bottomEndContentUM: EndContentUM, override val promoBannerUM: PromoBannerUM = PromoBannerUM.Empty, - override val tailUM: TailUM = TailUM.Empty, + override val tailUM: TangemRowTailUM = TangemRowTailUM.Empty, override val onItemClick: (() -> Unit)?, override val onItemLongClick: (() -> Unit)?, ) : TangemTokenRowUM() @@ -71,7 +72,7 @@ sealed class TangemTokenRowUM : TangemRowUM { override val topEndContentUM: EndContentUM = EndContentUM.Loading override val bottomEndContentUM: EndContentUM = EndContentUM.Loading override val promoBannerUM: PromoBannerUM = PromoBannerUM.Empty - override val tailUM: TailUM = TailUM.Empty + override val tailUM: TangemRowTailUM = TangemRowTailUM.Empty override val onItemClick: (() -> Unit)? = null override val onItemLongClick: (() -> Unit)? = null } @@ -84,7 +85,7 @@ sealed class TangemTokenRowUM : TangemRowUM { override val headIconUM: TangemIconUM.Currency, override val titleUM: TitleUM, override val subtitleUM: SubtitleUM, - override val tailUM: TailUM, + override val tailUM: TangemRowTailUM, override val onItemClick: (() -> Unit)?, override val onItemLongClick: (() -> Unit)?, override val topEndContentUM: EndContentUM = EndContentUM.Empty, @@ -154,15 +155,4 @@ sealed class TangemTokenRowUM : TangemRowUM { data object Empty : PromoBannerUM() } - - @Immutable - sealed class TailUM { - data class Text( - val text: TextReference, - ) : TailUM() - - data object Draggable : TailUM() - - data object Empty : TailUM() - } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TangemTokenRowPreviewData.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TangemTokenRowPreviewData.kt index a18f605cca..fb7b25744f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TangemTokenRowPreviewData.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/internal/TangemTokenRowPreviewData.kt @@ -7,6 +7,7 @@ import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.marketprice.PriceChangeState import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds.row.internal.TangemRowTailUM import com.tangem.core.ui.ds.row.token.TangemTokenRowUM import com.tangem.core.ui.extensions.combinedReference import com.tangem.core.ui.extensions.stringReference @@ -123,7 +124,7 @@ internal object TangemTokenRowPreviewData { topEndContentUM = topEndContentUM, bottomEndContentUM = bottomEndContentUM, promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty, - tailUM = TangemTokenRowUM.TailUM.Empty, + tailUM = TangemRowTailUM.Empty, onItemClick = {}, onItemLongClick = {}, ) @@ -150,7 +151,7 @@ internal object TangemTokenRowPreviewData { ), bottomEndContentUM = bottomEndContentUM, promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty, - tailUM = TangemTokenRowUM.TailUM.Empty, + tailUM = TangemRowTailUM.Empty, onItemClick = {}, onItemLongClick = {}, ) @@ -164,7 +165,7 @@ internal object TangemTokenRowPreviewData { topEndContentUM = topEndContentUM, bottomEndContentUM = bottomEndContentUM, promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty, - tailUM = TangemTokenRowUM.TailUM.Empty, + tailUM = TangemRowTailUM.Empty, onItemClick = {}, onItemLongClick = {}, ) @@ -178,7 +179,7 @@ internal object TangemTokenRowPreviewData { topEndContentUM = topEndContentUM, bottomEndContentUM = bottomEndContentUM, promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty, - tailUM = TangemTokenRowUM.TailUM.Empty, + tailUM = TangemRowTailUM.Empty, onItemClick = {}, onItemLongClick = {}, ) @@ -189,7 +190,7 @@ internal object TangemTokenRowPreviewData { headIconUM = coinIconState, titleUM = titleUM, subtitleUM = subtitleUM, - tailUM = TangemTokenRowUM.TailUM.Draggable, + tailUM = TangemRowTailUM.Draggable(R.drawable.ic_drag_24), onItemClick = {}, onItemLongClick = {}, ) @@ -202,7 +203,7 @@ internal object TangemTokenRowPreviewData { subtitleUM = subtitleUM, topEndContentUM = topEndContentUM, bottomEndContentUM = bottomEndContentUM, - tailUM = TangemTokenRowUM.TailUM.Draggable, + tailUM = TangemRowTailUM.Draggable(R.drawable.ic_drag_24), onItemClick = {}, onItemLongClick = {}, ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBar.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBar.kt index b38389acc5..c9dca9b4fc 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBar.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBar.kt @@ -5,19 +5,23 @@ import androidx.annotation.DrawableRes import androidx.compose.animation.* import androidx.compose.foundation.background import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.lerp import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.Dp import com.tangem.core.ui.R import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme @@ -30,11 +34,9 @@ import com.tangem.core.ui.res.TangemThemePreviewRedesign * @param title The title text to be displayed in the center of the top bar. * @param modifier Modifier to be applied to the top bar. * @param subtitle Optional subtitle text to be displayed below the title. - * @param startIconRes Optional drawable resource ID for the start icon. - * @param onStartContentClick Optional click action for the start icon. - * @param endIconRes Optional drawable resource ID for the end icon. - * @param onEndContentClick Optional click action for the end icon. - * @param isGhostButtons Flag to determine if ghost button styling should be applied. + * @param startActionUM Optional action data for the start action icon. + * @param endActionUM Optional action data for the end action icon. + * @param titleIconRes Optional drawable resource ID for the icon to be displayed next to the title. * [REDACTED_AUTHOR] */ @@ -43,56 +45,95 @@ fun TangemTopBar( modifier: Modifier = Modifier, title: TextReference? = null, subtitle: TextReference? = null, - @DrawableRes startIconRes: Int? = null, - onStartContentClick: (() -> Unit)? = null, - @DrawableRes endIconRes: Int? = null, - onEndContentClick: (() -> Unit)? = null, + startActionUM: TangemTopBarActionUM? = null, + endActionUM: TangemTopBarActionUM? = null, @DrawableRes titleIconRes: Int? = null, - titleStyle: TextStyle = TangemTheme.typography2.headingSemibold17, - isGhostButtons: Boolean = false, ) { - TangemTopBarInner( + TangemTopBar( + title = title, + subtitle = subtitle, + titleIconRes = titleIconRes, modifier = modifier, - content = { - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x0_5), - ) { - TangemTopBarTitle(title = title, titleIconRes = titleIconRes, titleStyle = titleStyle) - AnimatedVisibility( - visible = subtitle != null, - label = "Subtitle Visibility", - ) { - val wrappedSubtitle = remember(this) { requireNotNull(subtitle) } - Text( - text = wrappedSubtitle.resolveAnnotatedReference(), - color = TangemTheme.colors2.text.neutral.secondary, - style = TangemTheme.typography2.bodyRegular15, - textAlign = TextAlign.Center, - maxLines = 1, - ) - } - } - }, - startContent = if (startIconRes != null) { - { TangemTopBarIcon(iconRes = startIconRes) } + startContent = if (startActionUM != null) { + { TangemTopBarActionContent(startActionUM) } } else { null }, - onStartContentClick = onStartContentClick, - endContent = if (endIconRes != null) { - { TangemTopBarIcon(iconRes = endIconRes) } + endContent = if (endActionUM != null) { + { TangemTopBarActionContent(endActionUM) } } else { null }, - onEndContentClick = onEndContentClick, - isGhostButtons = isGhostButtons, ) } +/** + * A top bar composable that displays a title and optional start and end icons. + * [Figma](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8435-74860&m=dev) + * + * @param title The title text to be displayed in the center of the top bar. + * @param modifier Modifier to be applied to the top bar. + * @param subtitle Optional subtitle text to be displayed below the title. + * +[REDACTED_AUTHOR] + */ @Composable -private fun TangemTopBarTitle(title: TextReference?, @DrawableRes titleIconRes: Int?, titleStyle: TextStyle) { +fun TangemTopBar( + modifier: Modifier = Modifier, + title: TextReference? = null, + subtitle: TextReference? = null, + @DrawableRes titleIconRes: Int? = null, + startContent: @Composable (() -> Unit)? = null, + endContent: @Composable (() -> Unit)? = null, +) { + Box( + modifier = modifier + .fillMaxWidth() + .height(TangemTheme.dimens2.x16) + .padding(TangemTheme.dimens2.x4, TangemTheme.dimens2.x3), + ) { + AnimatedVisibility( + visible = startContent != null, + modifier = Modifier.align(Alignment.CenterStart), + label = "Start Content Visibility", + ) { + startContent?.invoke() + } + + Column( + modifier = Modifier + .align(Alignment.Center) + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x0_5), + ) { + TangemTopBarTitle(title = title, titleIconRes = titleIconRes) + AnimatedVisibility( + visible = subtitle != null, + label = "Subtitle Visibility", + ) { + val wrappedSubtitle = remember(this) { requireNotNull(subtitle) } + Text( + text = wrappedSubtitle.resolveAnnotatedReference(), + color = TangemTheme.colors2.text.neutral.secondary, + style = TangemTheme.typography2.bodyRegular15, + textAlign = TextAlign.Center, + maxLines = 1, + ) + } + } + AnimatedVisibility( + visible = endContent != null, + modifier = Modifier.align(Alignment.CenterEnd), + label = "End Content Visibility", + ) { + endContent?.invoke() + } + } +} + +@Composable +private fun TangemTopBarTitle(title: TextReference?, @DrawableRes titleIconRes: Int?) { AnimatedVisibility( visible = title != null, label = "Title Visibility", @@ -127,7 +168,7 @@ private fun TangemTopBarTitle(title: TextReference?, @DrawableRes titleIconRes: Text( text = wrappedTitle.resolveAnnotatedReference(), color = TangemTheme.colors2.text.neutral.primary, - style = titleStyle, + style = TangemTheme.typography2.headingSemibold17, textAlign = TextAlign.Center, maxLines = 1, ) @@ -136,12 +177,29 @@ private fun TangemTopBarTitle(title: TextReference?, @DrawableRes titleIconRes: } @Composable -private fun TangemTopBarIcon(@DrawableRes iconRes: Int) { +fun TangemTopBarActionContent( + actionUM: TangemTopBarActionUM, + modifier: Modifier = Modifier, + iconSize: Dp = TangemTheme.dimens2.x8, +) { + val background = lerp( + start = Color.Transparent, + stop = TangemTheme.colors2.button.backgroundSecondary, + fraction = actionUM.ghostModeProgress, + ) + val padding = (TangemTheme.dimens2.x10 - iconSize) / 2 Icon( - imageVector = ImageVector.vectorResource(id = iconRes), + imageVector = ImageVector.vectorResource(id = actionUM.iconRes), contentDescription = null, tint = TangemTheme.colors2.graphic.neutral.primary, - modifier = Modifier.fillMaxSize(), + modifier = modifier + .size(TangemTheme.dimens2.x10) + .clip(CircleShape) + .conditional(actionUM.isActionable) { background(background) } + .conditionalCompose(actionUM.isActionable && actionUM.onClick != null) { + clickableSingle(onClick = requireNotNull(actionUM.onClick)) + } + .padding(padding), ) } @@ -154,13 +212,10 @@ private fun TangemTopBar_Preview(@PreviewParameter(PreviewProvider::class) param TangemTopBar( title = params.title, subtitle = params.subtitle, - startIconRes = params.startIconRes, - endIconRes = params.endIconRes, titleIconRes = params.titleIconRes, - isGhostButtons = params.isGhostButtons, - onStartContentClick = {}, - onEndContentClick = {}, modifier = Modifier.background(TangemTheme.colors2.surface.level1), + startContent = params.startActionUM?.let { { TangemTopBarActionContent(it) } }, + endContent = params.endActionUM?.let { { TangemTopBarActionContent(it) } }, ) } } @@ -168,10 +223,9 @@ private fun TangemTopBar_Preview(@PreviewParameter(PreviewProvider::class) param private class TangemTopBarPreviewData( val title: TextReference? = null, val subtitle: TextReference? = null, - val isGhostButtons: Boolean = false, val titleIconRes: Int? = null, - val startIconRes: Int? = null, - val endIconRes: Int? = null, + val startActionUM: TangemTopBarActionUM? = null, + val endActionUM: TangemTopBarActionUM? = null, ) private class PreviewProvider : PreviewParameterProvider { @@ -179,41 +233,79 @@ private class PreviewProvider : PreviewParameterProvider Unit)? = null, + @param:FloatRange(0.0, 1.0) val ghostModeProgress: Float = 0f, +) \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBarInner.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBarInner.kt deleted file mode 100644 index e445ea4a30..0000000000 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBarInner.kt +++ /dev/null @@ -1,95 +0,0 @@ -package com.tangem.core.ui.ds.topbar - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -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.platform.LocalDensity -import com.tangem.core.ui.extensions.clickableSingle -import com.tangem.core.ui.extensions.conditional -import com.tangem.core.ui.extensions.conditionalCompose -import com.tangem.core.ui.res.TangemTheme - -/** - * Internal top bar composable that arranges optional start, center, and end content. - * [Figma](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8435-74860&m=dev) - * - * @param modifier Modifier to be applied to the top bar. - * @param content Center content of the top bar. - * @param startContent Optional start content of the top bar. - * @param onStartContentClick Optional click action for the start content. - * @param endContent Optional end content of the top bar. - * @param onEndContentClick Optional click action for the end content. - * @param isGhostButtons Flag to determine if ghost button styling should be applied. - * -[REDACTED_AUTHOR] - */ -@Composable -internal fun TangemTopBarInner( - modifier: Modifier = Modifier, - content: (@Composable () -> Unit)? = null, - startContent: (@Composable () -> Unit)? = null, - onStartContentClick: (() -> Unit)? = null, - endContent: (@Composable () -> Unit)? = null, - onEndContentClick: (() -> Unit)? = null, - isGhostButtons: Boolean = false, -) { - val statusBarHeight = with(LocalDensity.current) { WindowInsets.statusBars.getTop(density = this).toDp() } - Box( - modifier = modifier - .height(TangemTheme.dimens2.x16 + statusBarHeight) - .fillMaxWidth() - .padding(top = statusBarHeight) - .padding(TangemTheme.dimens2.x4, TangemTheme.dimens2.x3), - ) { - val iconModifier = Modifier - .size(TangemTheme.dimens2.x10) - .clip(RoundedCornerShape(TangemTheme.dimens2.x25)) - .conditionalCompose(isGhostButtons) { - background(TangemTheme.colors2.button.backgroundSecondary) - } - - AnimatedVisibility( - visible = startContent != null, - modifier = Modifier.align(Alignment.CenterStart), - label = "Start Content Visibility", - ) { - Box( - modifier = iconModifier - .conditional(onStartContentClick != null) { - clickableSingle { onStartContentClick?.invoke() } - } - .conditionalCompose(isGhostButtons) { padding(TangemTheme.dimens2.x1) }, - ) { - startContent?.invoke() - } - } - - AnimatedVisibility( - visible = content != null, - modifier = Modifier.align(Alignment.Center), - ) { - content?.invoke() - } - - AnimatedVisibility( - visible = endContent != null, - modifier = Modifier.align(Alignment.CenterEnd), - label = "End Content Visibility", - ) { - Box( - modifier = iconModifier - .conditional(onEndContentClick != null) { - clickableSingle { onEndContentClick?.invoke() } - } - .conditionalCompose(isGhostButtons) { padding(TangemTheme.dimens2.x1) }, - ) { - endContent?.invoke() - } - } - } -} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemColorPalette.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemColorPalette.kt index 0906b9626d..9b81ff94d3 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemColorPalette.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemColorPalette.kt @@ -20,6 +20,7 @@ object TangemColorPalette { // endregion Dark // region Dark Alpha + val Dark_05 = Color(0x0D1E1E1E) val Dark_10 = Color(0x1A1E1E1E) val Dark_20 = Color(0x331E1E1E) val Dark_30 = Color(0x4D1E1E1E) diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemColors2.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemColors2.kt index 1f0df03ae4..61f297696c 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemColors2.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemColors2.kt @@ -22,6 +22,7 @@ class TangemColors2 internal constructor( val skeleton: Skeleton, val markers: Markers, val tabs: Tabs, + val contextMenu: ContextMenu, ) { @Stable @@ -335,15 +336,23 @@ class TangemColors2 internal constructor( class Neutral internal constructor( primary: Color, secondary: Color, + tertiary: Color, + quaternary: Color, ) { var primary by mutableStateOf(primary) private set var secondary by mutableStateOf(secondary) private set + var tertiary by mutableStateOf(tertiary) + private set + var quaternary by mutableStateOf(quaternary) + private set fun update(other: Neutral) { primary = other.primary secondary = other.secondary + tertiary = other.tertiary + quaternary = other.quaternary } } @@ -616,6 +625,18 @@ class TangemColors2 internal constructor( } } + @Stable + class ContextMenu internal constructor( + background: Color, + ) { + var background by mutableStateOf(background) + private set + + fun update(other: ContextMenu) { + background = other.background + } + } + fun update(other: TangemColors2) { text.update(other.text) graphic.update(other.graphic) @@ -629,5 +650,6 @@ class TangemColors2 internal constructor( skeleton.update(other.skeleton) markers.update(other.markers) tabs.update(other.tabs) + contextMenu.update(other.contextMenu) } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemeRedesign.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemeRedesign.kt index ef46023d32..ad14b94dbf 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemeRedesign.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemThemeRedesign.kt @@ -79,6 +79,8 @@ private fun lightThemeColors2(): TangemColors2 { neutral = TangemColors2.Border.Neutral( primary = TangemColorPalette.Light3, secondary = TangemColorPalette.Light5, + tertiary = TangemColorPalette.Light_10, + quaternary = TangemColorPalette.Dark_10, ), status = TangemColors2.Border.Status( accent = TangemColorPalette.Azure, @@ -187,6 +189,9 @@ private fun lightThemeColors2(): TangemColors2 { backgroundTertiary = TangemColorPalette.White, backgroundQuaternary = TangemColorPalette.Dark_20, ) + val contextMenu = TangemColors2.ContextMenu( + background = TangemColorPalette.Dark_05, + ) return TangemColors2( text = text, graphic = graphic, @@ -200,6 +205,7 @@ private fun lightThemeColors2(): TangemColors2 { skeleton = skeleton, markers = markers, tabs = tabs, + contextMenu = contextMenu, ) } @@ -242,6 +248,8 @@ private fun darkThemeColors2(): TangemColors2 { neutral = TangemColors2.Border.Neutral( primary = TangemColorPalette.Dark4, secondary = TangemColorPalette.Dark4, + tertiary = TangemColorPalette.Light_10, + quaternary = TangemColorPalette.Light_10, ), status = TangemColors2.Border.Status( accent = TangemColorPalette.Azure, @@ -350,6 +358,9 @@ private fun darkThemeColors2(): TangemColors2 { backgroundTertiary = TangemColorPalette.Light_10, backgroundQuaternary = TangemColorPalette.Light_10, ) + val contextMenu = TangemColors2.ContextMenu( + background = TangemColorPalette.Light_10, + ) return TangemColors2( text = text, graphic = graphic, @@ -363,5 +374,6 @@ private fun darkThemeColors2(): TangemColors2 { skeleton = skeleton, markers = markers, tabs = tabs, + contextMenu = contextMenu, ) } \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/ic_arrow_collapse_24.xml b/core/ui/src/main/res/drawable/ic_arrow_collapse_24.xml new file mode 100644 index 0000000000..3a1012ac04 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_arrow_collapse_24.xml @@ -0,0 +1,18 @@ + + + + diff --git a/core/ui/src/main/res/drawable/ic_exchange_mini_24.xml b/core/ui/src/main/res/drawable/ic_exchange_mini_24.xml new file mode 100644 index 0000000000..10e97276fb --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_exchange_mini_24.xml @@ -0,0 +1,12 @@ + + + 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 3fb899ac8e..66cb344ef0 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 @@ -43,4 +43,14 @@ internal data class NorthernLightsStory( internal data class TangemTokenRowStory( val isBalanceHidden: Boolean, val onBalanceHiddenToggle: () -> Unit, +) : StoryBookPage + +internal data class TangemContextMenuStory( + val isExpanded: Boolean, + val onExpandedChange: (Boolean) -> Unit, +) : StoryBookPage + +internal data class TangemHeaderRowStory( + 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/contextmenu/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/contextmenu/Build.kt new file mode 100644 index 0000000000..87ed7bf20e --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/contextmenu/Build.kt @@ -0,0 +1,17 @@ +package com.tangem.feature.tester.presentation.storybook.page.contextmenu + +import com.tangem.feature.tester.presentation.storybook.entity.TangemContextMenuStory +import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater +import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory + +internal fun StateUpdater.build(): TangemContextMenuStory { + return TangemContextMenuStory( + isExpanded = false, + onExpandedChange = { expanded -> + updateStory { it.copy(isExpanded = expanded) } + }, + ) +} + +internal val tangemContextMenuStoryFactory + 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/contextmenu/TangemContextMenuStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/contextmenu/TangemContextMenuStory.kt new file mode 100644 index 0000000000..8bac2d248c --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/contextmenu/TangemContextMenuStory.kt @@ -0,0 +1,80 @@ +package com.tangem.feature.tester.presentation.storybook.page.contextmenu + +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.DpOffset +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.haze.hazeEffectTangem +import com.tangem.core.ui.components.haze.hazeSourceTangem +import com.tangem.core.ui.ds.contextmenu.TangemContextMenu +import com.tangem.core.ui.ds.contextmenu.TangemContextMenuCheckboxItem +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.res.TangemTheme +import dev.chrisbanes.haze.rememberHazeState +import com.tangem.feature.tester.presentation.storybook.entity.TangemContextMenuStory as TangemContextMenuStoryState + +@Composable +internal fun TangemContextMenuStory(state: TangemContextMenuStoryState, modifier: Modifier = Modifier) { + val hazeState = rememberHazeState() + LazyColumn( + contentPadding = PaddingValues(vertical = 16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = modifier + .statusBarsPadding() + .fillMaxSize() + .background(TangemTheme.colors2.surface.level1) + .hazeSourceTangem(state = hazeState, zIndex = -1f), + ) { + item("context_menu") { + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) { + Text( + text = "TangemContextMenu", + style = TangemTheme.typography2.headingSemibold17, + color = TangemTheme.colors2.text.neutral.primary, + ) + + Box { + PrimaryButton( + text = "Show Context Menu", + onClick = { state.onExpandedChange(true) }, + ) + + TangemContextMenu( + expanded = state.isExpanded, + onDismissRequest = { state.onExpandedChange(false) }, + offset = DpOffset(0.dp, 4.dp), + modifier = Modifier.hazeEffectTangem(hazeState), + ) { + TangemContextMenuCheckboxItem( + title = TextReference.Str("Sort by balance"), + isChecked = true, + onClick = {}, + ) + HorizontalDivider( + thickness = 0.5.dp, + color = TangemTheme.colors2.border.neutral.quaternary, + ) + TangemContextMenuCheckboxItem( + title = TextReference.Str("Group tokens"), + isChecked = false, + onClick = {}, + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/headerrow/Build.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/headerrow/Build.kt new file mode 100644 index 0000000000..200ac7a796 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/headerrow/Build.kt @@ -0,0 +1,17 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.headerrow + +import com.tangem.feature.tester.presentation.storybook.entity.TangemHeaderRowStory +import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater +import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory + +internal fun StateUpdater.build(): TangemHeaderRowStory { + return TangemHeaderRowStory( + isBalanceHidden = false, + onBalanceHiddenToggle = { updateStory { it.copy(isBalanceHidden = !it.isBalanceHidden) } }, + ) +} + +internal val tangemHeaderRowStoryFactory + 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/headerrow/TangemHeaderRowStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/headerrow/TangemHeaderRowStory.kt new file mode 100644 index 0000000000..95a5c349d7 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/headerrow/TangemHeaderRowStory.kt @@ -0,0 +1,111 @@ +@file:Suppress("MagicNumber", "LongMethod") + +package com.tangem.feature.tester.presentation.storybook.page.headerrow + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +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.R +import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds.row.header.TangemHeaderRow +import com.tangem.core.ui.ds.row.header.TangemHeaderRowUM +import com.tangem.core.ui.ds.row.internal.TangemRowTailUM +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.tester.presentation.storybook.entity.TangemHeaderRowStory + +@Composable +internal fun TangemHeaderRowStory(state: TangemHeaderRowStory, modifier: Modifier = Modifier) { + val rows = remember { buildSampleRows() } + + 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 -> + TangemHeaderRow( + headerRowUM = um, + isBalanceHidden = state.isBalanceHidden, + modifier = Modifier.background(TangemTheme.colors2.surface.level3), + ) + HorizontalDivider( + color = TangemTheme.colors2.border.neutral.secondary, + modifier = Modifier.padding(start = 16.dp), + ) + } + } +} + +private fun buildSampleRows(): List = listOf( + TangemHeaderRowUM( + id = "icon_subtitle_no_tail", + startIconUM = TangemIconUM.Currency(currencyIconState = CurrencyIconState.Locked), + tailUM = TangemRowTailUM.Empty, + title = stringReference("Account"), + subtitle = stringReference("\$ 42,900.17"), + ), + TangemHeaderRowUM( + id = "icon_subtitle_collapse", + startIconUM = TangemIconUM.Currency(currencyIconState = CurrencyIconState.Locked), + tailUM = TangemRowTailUM.Icon(R.drawable.ic_arrow_collapse_24), + title = stringReference("Account"), + subtitle = stringReference("\$ 42,900.17"), + ), + TangemHeaderRowUM( + id = "icon_subtitle_group_drop", + startIconUM = TangemIconUM.Currency(currencyIconState = CurrencyIconState.Locked), + tailUM = TangemRowTailUM.Icon(R.drawable.ic_group_drop_24), + title = stringReference("Account"), + subtitle = stringReference("\$ 42,900.17"), + ), + TangemHeaderRowUM( + id = "title_only", + title = stringReference("Account"), + ), +) + +@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/page/tokenrow/TangemTokenRowStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/tokenrow/TangemTokenRowStory.kt index 65c44d281d..d87c97f642 100644 --- 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 @@ -1,4 +1,5 @@ @file:Suppress("MagicNumber", "LongMethod") + package com.tangem.feature.tester.presentation.storybook.page.tokenrow import androidx.compose.foundation.background @@ -44,7 +45,7 @@ internal fun TangemTokenRowStory(state: TangemTokenRowStory, modifier: Modifier TangemTokenRow( tokenRowUM = um, isBalanceHidden = state.isBalanceHidden, - reorderableTokenListState = null, + reorderableState = null, modifier = Modifier.background(TangemTheme.colors2.surface.level1), ) HorizontalDivider( 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 0aa6cb3681..6fc4111d73 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 @@ -18,9 +18,11 @@ 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.contextmenu.tangemContextMenuStoryFactory +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.tabs.tangemSegmentedPickerStoryFactory import com.tangem.feature.tester.presentation.storybook.page.tokenrow.tangemTokenRowStoryFactory @@ -35,6 +37,8 @@ private fun buildStories() = listOf( StoryItem(title = "🗂️ Segmented Picker", factory = tangemSegmentedPickerStoryFactory), StoryItem(title = "☑️ Checkbox", factory = tangemCheckboxStoryFactory), StoryItem(title = "🪙 Token Row", factory = tangemTokenRowStoryFactory), + StoryItem(title = "📑 Header Row", factory = tangemHeaderRowStoryFactory), + StoryItem(title = "📋 Context Menu", factory = tangemContextMenuStoryFactory), ) @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 11a78b7e28..f3e58f0000 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 @@ -11,6 +11,8 @@ 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.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.TangemSegmentedPickerStory import com.tangem.feature.tester.presentation.storybook.entity.TangemTokenRowStory @@ -22,6 +24,8 @@ import com.tangem.feature.tester.presentation.storybook.page.checkbox.TangemChec 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 +import com.tangem.feature.tester.presentation.storybook.page.headerrow.TangemHeaderRowStory +import com.tangem.feature.tester.presentation.storybook.page.contextmenu.TangemContextMenuStory @Composable internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier) { @@ -42,6 +46,8 @@ internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier) is TangemCheckboxStory -> TangemCheckboxStory(state = storyState) TangemSegmentedPickerStory -> TangemSegmentedPickerStory() is TangemTokenRowStory -> TangemTokenRowStory(state = storyState) + is TangemHeaderRowStory -> TangemHeaderRowStory(state = storyState) + is TangemContextMenuStory -> TangemContextMenuStory(state = storyState) } } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/preview/WalletScreenPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/preview/WalletScreenPreviewData.kt index 3f563dbb3d..2b44fa4727 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/preview/WalletScreenPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/preview/WalletScreenPreviewData.kt @@ -5,6 +5,7 @@ import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.ds.button.TangemButtonType import com.tangem.core.ui.ds.button.TangemButtonUM import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.ds.row.internal.TangemRowTailUM import com.tangem.core.ui.ds.row.token.TangemTokenRowUM import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.extensions.resourceReference @@ -34,7 +35,7 @@ internal object WalletScreenPreviewData { text = stringReference("0,12345678 BTC"), ), promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty, - tailUM = TangemTokenRowUM.TailUM.Empty, + tailUM = TangemRowTailUM.Empty, onItemClick = {}, onItemLongClick = {}, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt index 8438399294..7782f92342 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt @@ -1,23 +1,22 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common import android.content.res.Configuration +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.material3.* import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview -import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.ui.components.haze.hazeEffectTangem import com.tangem.core.ui.ds.topbar.TangemTopBar +import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM import com.tangem.core.ui.ds.topbar.collapsing.TangemCollapsingAppBarBehavior import com.tangem.core.ui.ds.topbar.collapsing.rememberTangemExitUntilCollapsedScrollBehavior import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.stringReference -import com.tangem.core.ui.res.LocalPowerSavingState import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemThemePreviewRedesign @@ -46,23 +45,27 @@ internal fun WalletTopBar( color = Color.Unspecified, contentColor = Color.Unspecified, modifier = Modifier.hazeEffectTangem { - progressive = - HazeProgressive.verticalGradient(startIntensity = 1f, endIntensity = 0f) + progressive = HazeProgressive.verticalGradient(startIntensity = 1f, endIntensity = 0f) }, ) { - val isPowerSaving by LocalPowerSavingState.current.isPowerSavingModeEnabled.collectAsStateWithLifecycle() - val wrappedBalance = remember(behavior.state.collapsedFraction) { if (behavior.state.collapsedFraction > VISIBILITY_THRESHOLD) walletBalance else null } TangemTopBar( title = wrappedBalance, - startIconRes = R.drawable.ic_tangem_24, - endIconRes = R.drawable.ic_more_default_24, - onEndContentClick = topBarConfig.onDetailsClick, - isGhostButtons = !isPowerSaving, + startActionUM = TangemTopBarActionUM( + iconRes = R.drawable.ic_tangem_24, + isActionable = false, + ), + endActionUM = TangemTopBarActionUM( + iconRes = R.drawable.ic_more_default_24, + isActionable = true, + onClick = topBarConfig.onDetailsClick, + ghostModeProgress = behavior.state.collapsedFraction, + ), modifier = Modifier + .statusBarsPadding() .testTag(MainScreenTestTags.TOP_BAR), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index 905ba692e0..5041832a6b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -31,6 +31,7 @@ import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.core.ui.ds.image.TangemIcon import com.tangem.core.ui.ds.row.header.TangemHeaderRow import com.tangem.core.ui.ds.row.header.TangemHeaderRowUM +import com.tangem.core.ui.ds.row.internal.TangemRowTailUM import com.tangem.core.ui.ds.row.token.TangemTokenRow import com.tangem.core.ui.ds.row.token.TangemTokenRowUM import com.tangem.core.ui.ds.row.token.internal.TokenRowTitle @@ -146,7 +147,7 @@ private fun LazyListScope.tokenItem( is TangemTokenRowUM -> TangemTokenRow( tokenRowUM = tokenRowUM, isBalanceHidden = isBalanceHidden, - reorderableTokenListState = null, + reorderableState = null, modifier = itemModifier, ) is TangemHeaderRowUM -> TangemHeaderRow( @@ -199,7 +200,7 @@ private fun LazyListScope.portfolioItem( is TangemTokenRowUM -> TangemTokenRow( tokenRowUM = tokenRowUM, isBalanceHidden = isBalanceHidden, - reorderableTokenListState = null, + reorderableState = null, modifier = itemModifier, ) is TangemHeaderRowUM -> TangemHeaderRow( @@ -368,7 +369,7 @@ internal fun PortfolioRowItem( ?.text?.orMaskWithStars(isBalanceHidden), titleContent = composables.title, headContent = composables.icon, - footerTangemIconRes = R.drawable.ic_minimize_24, + tailUM = TangemRowTailUM.Icon(R.drawable.ic_minimize_24), onItemClick = item.tokenRowUM.onItemClick, ) } else { @@ -377,7 +378,7 @@ internal fun PortfolioRowItem( headComponent = composables.icon, titleComponent = composables.title, isBalanceHidden = isBalanceHidden, - reorderableTokenListState = null, + reorderableState = null, ) } }