Updated on 2026-08-14
This commit is contained in:
commit
3ae32901d4
30 changed files with 992 additions and 282 deletions
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Boolean>,
|
||||
transformOriginState: MutableState<TransformOrigin>,
|
||||
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
|
||||
|
|
@ -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 */ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,18 @@
|
|||
package com.tangem.core.ui.ds.row.header
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
|
|
@ -25,10 +21,13 @@ import com.tangem.core.ui.components.SpacerWMax
|
|||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.ds.image.TangemIcon
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.ds.row.internal.TangemRowTail
|
||||
import com.tangem.core.ui.ds.row.internal.TangemRowTailUM
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.core.ui.test.TokenElementsTestTags
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
|
||||
/**
|
||||
* UI model for header row component
|
||||
|
|
@ -37,13 +36,19 @@ import com.tangem.core.ui.test.TokenElementsTestTags
|
|||
* @param modifier Modifier for the composable
|
||||
*/
|
||||
@Composable
|
||||
fun TangemHeaderRow(headerRowUM: TangemHeaderRowUM, modifier: Modifier = Modifier, isBalanceHidden: Boolean = false) {
|
||||
fun TangemHeaderRow(
|
||||
headerRowUM: TangemHeaderRowUM,
|
||||
modifier: Modifier = Modifier,
|
||||
reorderableState: ReorderableLazyListState? = null,
|
||||
isBalanceHidden: Boolean = false,
|
||||
) {
|
||||
TangemHeaderRow(
|
||||
headTangemIconUM = headerRowUM.startIconUM,
|
||||
footerTangemIconRes = headerRowUM.endIconRes,
|
||||
tailUM = headerRowUM.tailUM,
|
||||
title = headerRowUM.title,
|
||||
subtitle = headerRowUM.subtitle,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
reorderableState = reorderableState,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
@ -54,7 +59,7 @@ fun TangemHeaderRow(headerRowUM: TangemHeaderRowUM, modifier: Modifier = Modifie
|
|||
* @param modifier Modifier for the composable
|
||||
* @param subtitle Optional subtitle as a TextReference
|
||||
* @param onItemClick Optional click callback for the row
|
||||
* @param footerTangemIconRes Optional drawable resource ID for the footer icon
|
||||
* @param tailUM Optional TailUM for the tail content
|
||||
* @param titleContent Composable lambda for the title content
|
||||
* @param headContent Composable lambda for the head content
|
||||
*/
|
||||
|
|
@ -64,7 +69,7 @@ fun TangemHeaderRow(
|
|||
isBalanceHidden: Boolean = false,
|
||||
subtitle: TextReference? = null,
|
||||
onItemClick: (() -> Unit)? = null,
|
||||
@DrawableRes footerTangemIconRes: Int? = null,
|
||||
tailUM: TangemRowTailUM = TangemRowTailUM.Empty,
|
||||
titleContent: @Composable (Modifier) -> Unit,
|
||||
headContent: @Composable (Modifier) -> Unit,
|
||||
) {
|
||||
|
|
@ -101,17 +106,7 @@ fun TangemHeaderRow(
|
|||
)
|
||||
}
|
||||
SpacerWMax()
|
||||
AnimatedVisibility(
|
||||
visible = footerTangemIconRes != null,
|
||||
) {
|
||||
val wrappedIconUM = remember(this) { requireNotNull(footerTangemIconRes) }
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = wrappedIconUM),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.secondary,
|
||||
modifier = Modifier.size(TangemTheme.dimens2.x4),
|
||||
)
|
||||
}
|
||||
TangemRowTail(tangemRowTailUM = tailUM)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +128,8 @@ fun TangemHeaderRow(
|
|||
isBalanceHidden: Boolean = false,
|
||||
subtitle: TextReference? = null,
|
||||
headTangemIconUM: TangemIconUM? = null,
|
||||
@DrawableRes footerTangemIconRes: Int? = null,
|
||||
tailUM: TangemRowTailUM = TangemRowTailUM.Empty,
|
||||
reorderableState: ReorderableLazyListState? = null,
|
||||
isEnabled: Boolean = false,
|
||||
onItemClick: (() -> Unit)? = null,
|
||||
) {
|
||||
|
|
@ -182,17 +178,10 @@ fun TangemHeaderRow(
|
|||
)
|
||||
}
|
||||
SpacerWMax()
|
||||
AnimatedVisibility(
|
||||
visible = footerTangemIconRes != null,
|
||||
) {
|
||||
val wrappedIconUM = remember(this) { requireNotNull(footerTangemIconRes) }
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = wrappedIconUM),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.secondary,
|
||||
modifier = Modifier.size(TangemTheme.dimens2.x4),
|
||||
)
|
||||
}
|
||||
TangemRowTail(
|
||||
tangemRowTailUM = tailUM,
|
||||
reorderableState = reorderableState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -217,7 +206,25 @@ private class PreviewProvider : PreviewParameterProvider<TangemHeaderRowUM> {
|
|||
startIconUM = TangemIconUM.Currency(
|
||||
currencyIconState = CurrencyIconState.Locked,
|
||||
),
|
||||
endIconRes = R.drawable.ic_minimize_24,
|
||||
tailUM = TangemRowTailUM.Empty,
|
||||
title = stringReference("Account"),
|
||||
subtitle = stringReference("\$ 42,900.17"),
|
||||
),
|
||||
TangemHeaderRowUM(
|
||||
id = "1",
|
||||
startIconUM = TangemIconUM.Currency(
|
||||
currencyIconState = CurrencyIconState.Locked,
|
||||
),
|
||||
tailUM = TangemRowTailUM.Icon(R.drawable.ic_arrow_collapse_24),
|
||||
title = stringReference("Account"),
|
||||
subtitle = stringReference("\$ 42,900.17"),
|
||||
),
|
||||
TangemHeaderRowUM(
|
||||
id = "1",
|
||||
startIconUM = TangemIconUM.Currency(
|
||||
currencyIconState = CurrencyIconState.Locked,
|
||||
),
|
||||
tailUM = TangemRowTailUM.Icon(R.drawable.ic_group_drop_24),
|
||||
title = stringReference("Account"),
|
||||
subtitle = stringReference("\$ 42,900.17"),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = {},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<TangemTopBarPreviewData> {
|
||||
|
|
@ -179,41 +233,79 @@ private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData
|
|||
get() = sequenceOf(
|
||||
TangemTopBarPreviewData(
|
||||
title = stringReference("Title"),
|
||||
startIconRes = R.drawable.ic_tangem_24,
|
||||
endIconRes = R.drawable.ic_more_vertical_24,
|
||||
isGhostButtons = true,
|
||||
startActionUM = TangemTopBarActionUM(
|
||||
iconRes = R.drawable.ic_tangem_24,
|
||||
onClick = {},
|
||||
isActionable = false,
|
||||
),
|
||||
endActionUM = TangemTopBarActionUM(
|
||||
iconRes = R.drawable.ic_more_default_24,
|
||||
onClick = {},
|
||||
isActionable = false,
|
||||
),
|
||||
),
|
||||
TangemTopBarPreviewData(
|
||||
title = stringReference("Title"),
|
||||
subtitle = stringReference("Subtitle"),
|
||||
startIconRes = R.drawable.ic_tangem_24,
|
||||
endIconRes = R.drawable.ic_more_vertical_24,
|
||||
isGhostButtons = true,
|
||||
startActionUM = TangemTopBarActionUM(
|
||||
iconRes = R.drawable.ic_tangem_24,
|
||||
onClick = {},
|
||||
isActionable = true,
|
||||
ghostModeProgress = 1f,
|
||||
),
|
||||
endActionUM = TangemTopBarActionUM(
|
||||
iconRes = R.drawable.ic_more_default_24,
|
||||
onClick = {},
|
||||
isActionable = true,
|
||||
ghostModeProgress = 1f,
|
||||
),
|
||||
),
|
||||
TangemTopBarPreviewData(
|
||||
title = stringReference("Title"),
|
||||
subtitle = stringReference("Subtitle"),
|
||||
titleIconRes = R.drawable.ic_tangem_24,
|
||||
startIconRes = R.drawable.ic_tangem_24,
|
||||
endIconRes = R.drawable.ic_more_vertical_24,
|
||||
isGhostButtons = true,
|
||||
startActionUM = TangemTopBarActionUM(
|
||||
iconRes = R.drawable.ic_tangem_24,
|
||||
onClick = {},
|
||||
isActionable = false,
|
||||
),
|
||||
endActionUM = TangemTopBarActionUM(
|
||||
iconRes = R.drawable.ic_more_default_24,
|
||||
onClick = {},
|
||||
isActionable = true,
|
||||
ghostModeProgress = 1f,
|
||||
),
|
||||
),
|
||||
TangemTopBarPreviewData(
|
||||
subtitle = stringReference("Subtitle"),
|
||||
titleIconRes = R.drawable.ic_tangem_24,
|
||||
startIconRes = R.drawable.ic_tangem_24,
|
||||
endIconRes = R.drawable.ic_more_vertical_24,
|
||||
isGhostButtons = true,
|
||||
startActionUM = TangemTopBarActionUM(
|
||||
iconRes = R.drawable.ic_tangem_24,
|
||||
onClick = {},
|
||||
isActionable = true,
|
||||
),
|
||||
endActionUM = TangemTopBarActionUM(
|
||||
iconRes = R.drawable.ic_more_default_24,
|
||||
onClick = {},
|
||||
isActionable = false,
|
||||
),
|
||||
),
|
||||
TangemTopBarPreviewData(
|
||||
title = stringReference("Title"),
|
||||
endIconRes = R.drawable.ic_more_vertical_24,
|
||||
isGhostButtons = true,
|
||||
endActionUM = TangemTopBarActionUM(
|
||||
iconRes = R.drawable.ic_more_default_24,
|
||||
onClick = {},
|
||||
isActionable = false,
|
||||
),
|
||||
),
|
||||
TangemTopBarPreviewData(
|
||||
title = stringReference("Title"),
|
||||
startIconRes = R.drawable.ic_tangem_24,
|
||||
isGhostButtons = true,
|
||||
startActionUM = TangemTopBarActionUM(
|
||||
iconRes = R.drawable.ic_tangem_24,
|
||||
onClick = {},
|
||||
isActionable = true,
|
||||
ghostModeProgress = 1f,
|
||||
),
|
||||
),
|
||||
TangemTopBarPreviewData(
|
||||
title = combinedReference(
|
||||
|
|
@ -238,8 +330,16 @@ private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData
|
|||
},
|
||||
),
|
||||
),
|
||||
startIconRes = R.drawable.ic_tangem_24,
|
||||
endIconRes = R.drawable.ic_more_vertical_24,
|
||||
startActionUM = TangemTopBarActionUM(
|
||||
iconRes = R.drawable.ic_tangem_24,
|
||||
onClick = {},
|
||||
isActionable = true,
|
||||
),
|
||||
endActionUM = TangemTopBarActionUM(
|
||||
iconRes = R.drawable.ic_more_default_24,
|
||||
onClick = {},
|
||||
isActionable = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.core.ui.ds.topbar
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.FloatRange
|
||||
|
||||
/**
|
||||
* User model for top bar action
|
||||
*
|
||||
* @property iconRes resource id of action icon
|
||||
* @property isActionable if true, action will be clickable, otherwise - not
|
||||
* @property onClick lambda be invoked when action component is clicked. If null, action will not be clickable
|
||||
* @property ghostModeProgress progress of ghost mode animation, from 0f to 1f.
|
||||
*/
|
||||
data class TangemTopBarActionUM(
|
||||
@param:DrawableRes val iconRes: Int,
|
||||
val isActionable: Boolean = true,
|
||||
val onClick: (() -> Unit)? = null,
|
||||
@param:FloatRange(0.0, 1.0) val ghostModeProgress: Float = 0f,
|
||||
)
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
18
core/ui/src/main/res/drawable/ic_arrow_collapse_24.xml
Normal file
18
core/ui/src/main/res/drawable/ic_arrow_collapse_24.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M3.333,14H9.825C9.922,14 10,14.078 10,14.175V20.667M9.949,14.051L3,21"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M20.667,10H14.175C14.078,10 14,9.922 14,9.825V3.333M14.051,9.949L21,3"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
12
core/ui/src/main/res/drawable/ic_exchange_mini_24.xml
Normal file
12
core/ui/src/main/res/drawable/ic_exchange_mini_24.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M7,18.759L7,5.5M11.5,10L7.185,5.207C7.086,5.096 6.913,5.096 6.814,5.207L2.5,10M17.314,5L17.314,18.759M12.814,14.259L17.128,19.052C17.228,19.163 17.4,19.163 17.5,19.052L21.814,14.259"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
|
|
@ -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
|
||||
|
|
@ -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<TangemContextMenuStory>.build(): TangemContextMenuStory {
|
||||
return TangemContextMenuStory(
|
||||
isExpanded = false,
|
||||
onExpandedChange = { expanded ->
|
||||
updateStory { it.copy(isExpanded = expanded) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
internal val tangemContextMenuStoryFactory
|
||||
get() = storyPageFactory(StateUpdater<TangemContextMenuStory>::build)
|
||||
|
|
@ -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 = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TangemHeaderRowStory>.build(): TangemHeaderRowStory {
|
||||
return TangemHeaderRowStory(
|
||||
isBalanceHidden = false,
|
||||
onBalanceHiddenToggle = { updateStory { it.copy(isBalanceHidden = !it.isBalanceHidden) } },
|
||||
)
|
||||
}
|
||||
|
||||
internal val tangemHeaderRowStoryFactory
|
||||
get() = storyPageFactory(StateUpdater<TangemHeaderRowStory>::build)
|
||||
|
|
@ -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<TangemHeaderRowUM> = 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() })
|
||||
}
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = {},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue