Updated on 2026-08-14

This commit is contained in:
Tangem 2024-02-12 17:07:25 +03:00
parent 67a80fc097
commit 8ee78ad042
8 changed files with 169 additions and 100 deletions

View file

@ -1,10 +1,16 @@
package com.tangem.core.ui.components.buttons.segmentedbutton
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.*
@ -58,11 +64,16 @@ inline fun <reified T> SegmentedButtons(
val leftRadius = if (index == 0) TangemTheme.dimens.radius26 else TangemTheme.dimens.radius0
val rightRadius = if (index == config.lastIndex) TangemTheme.dimens.radius26 else TangemTheme.dimens.radius0
val animateColor by animateColorAsState(
targetValue = if (index == selected) selectedColor else color,
label = "Segmented Button Selected Color Animation",
animationSpec = spring(stiffness = Spring.StiffnessMedium),
)
Box(
modifier = Modifier
.weight(1f)
.background(
color = if (index == selected) selectedColor else color,
color = animateColor,
shape = RoundedCornerShape(
topStart = leftRadius,
topEnd = rightRadius,

View file

@ -7,6 +7,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment.Companion.CenterEnd
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -16,6 +17,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.tangem.core.ui.R
import com.tangem.core.ui.components.fields.SimpleTextField
import com.tangem.core.ui.components.icons.identicon.IdentIcon
import com.tangem.core.ui.components.inputrow.inner.CrossIcon
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
import com.tangem.core.ui.components.inputrow.inner.PasteButton
import com.tangem.core.ui.extensions.TextReference
@ -73,44 +75,37 @@ fun InputRowRecipient(
color = color,
)
}
Row(
Box(
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing8),
) {
AnimatedContent(
targetState = isLoading,
label = "Indicator Show Change",
modifier = Modifier
.align(CenterVertically)
.clip(RoundedCornerShape(TangemTheme.dimens.radius18))
.size(TangemTheme.dimens.size36)
.background(TangemTheme.colors.background.tertiary),
) { showIndicator ->
if (showIndicator) {
CircularProgressIndicator(
color = TangemTheme.colors.icon.informative,
modifier = Modifier
.padding(TangemTheme.dimens.spacing8),
)
} else {
IdentIcon(address = value)
}
Row {
InputIcon(
isLoading = isLoading,
value = value,
)
SimpleTextField(
value = value,
placeholder = placeholder,
onValueChange = onValueChange,
singleLine = singleLine,
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing12)
.weight(1f)
.align(CenterVertically),
)
CrossIcon(
onClick = onPasteClick,
modifier = Modifier
.align(CenterVertically)
.padding(start = TangemTheme.dimens.spacing8),
)
}
SimpleTextField(
value = value,
placeholder = placeholder,
onValueChange = onValueChange,
singleLine = singleLine,
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing12)
.weight(1f)
.align(CenterVertically),
)
PasteButton(
isPasteButtonVisible = value.isBlank(),
onClick = onPasteClick,
modifier = Modifier
.align(CenterVertically)
.align(CenterEnd)
.padding(start = TangemTheme.dimens.spacing8),
)
}
@ -118,6 +113,29 @@ fun InputRowRecipient(
}
}
@Composable
private fun RowScope.InputIcon(isLoading: Boolean, value: String) {
AnimatedContent(
targetState = isLoading,
label = "Indicator Show Change",
modifier = Modifier
.align(CenterVertically)
.clip(RoundedCornerShape(TangemTheme.dimens.radius18))
.size(TangemTheme.dimens.size36)
.background(TangemTheme.colors.background.tertiary),
) { showIndicator ->
if (showIndicator) {
CircularProgressIndicator(
color = TangemTheme.colors.icon.informative,
modifier = Modifier
.padding(TangemTheme.dimens.spacing8),
)
} else {
IdentIcon(address = value)
}
}
}
//region preview
@Preview
@Composable

View file

@ -1,24 +1,28 @@
package com.tangem.core.ui.components.inputrow.inner
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.DEFAULT_ANIMATION_DURATION
/**
* Paste button with cross icon. Retrieves text from clipboard.
@ -33,48 +37,67 @@ fun PasteButton(isPasteButtonVisible: Boolean, onClick: (String) -> Unit, modifi
val clipboardManager = LocalClipboardManager.current
val hapticFeedback = LocalHapticFeedback.current
if (isPasteButtonVisible) {
Box(modifier = modifier) {
Text(
text = "Paste",
style = TangemTheme.typography.button,
color = TangemTheme.colors.text.primary2,
modifier = Modifier
.background(
color = TangemTheme.colors.button.primary,
shape = TangemTheme.shapes.roundedCornersXMedium,
)
.padding(
horizontal = TangemTheme.dimens.spacing10,
vertical = TangemTheme.dimens.spacing2,
)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(radius = TangemTheme.dimens.radius8),
onClick = {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
onClick(
clipboardManager
.getText()
?.toString()
.orEmpty(),
)
},
),
)
}
} else {
Icon(
painter = painterResource(id = R.drawable.ic_close_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = stringResource(R.string.common_close),
modifier = modifier
.size(TangemTheme.dimens.size20)
AnimatedVisibility(
visible = isPasteButtonVisible,
label = "Paste Button Visibility Animation",
enter = fadeIn(),
exit = fadeOut(animationSpec = tween(DEFAULT_ANIMATION_DURATION)),
modifier = modifier,
) {
Text(
text = "Paste",
style = TangemTheme.typography.button,
color = TangemTheme.colors.text.primary2,
modifier = Modifier
.background(
color = TangemTheme.colors.button.primary,
shape = TangemTheme.shapes.roundedCornersXMedium,
)
.padding(
horizontal = TangemTheme.dimens.spacing10,
vertical = TangemTheme.dimens.spacing2,
)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(radius = TangemTheme.dimens.radius10),
onClick = { onClick("") },
indication = rememberRipple(radius = TangemTheme.dimens.radius8),
onClick = {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
onClick(
clipboardManager
.getText()
?.toString()
.orEmpty(),
)
},
),
)
}
}
@Composable
fun CrossIcon(onClick: (String) -> Unit, modifier: Modifier = Modifier) {
Icon(
painter = painterResource(id = R.drawable.ic_close_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = stringResource(R.string.common_close),
modifier = modifier
.size(TangemTheme.dimens.size20)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(radius = TangemTheme.dimens.radius10),
onClick = { onClick("") },
),
)
}
@Preview
@Composable
private fun PasteButtonPreview() {
var isVisible by remember { mutableStateOf(true) }
TangemTheme {
PasteButton(
isPasteButtonVisible = isVisible,
onClick = { isVisible = !isVisible },
)
}
}

View file

@ -0,0 +1,3 @@
package com.tangem.core.ui.utils
const val DEFAULT_ANIMATION_DURATION = 300

View file

@ -5,6 +5,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.state.SendUiState
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import com.tangem.utils.isNullOrZero
internal class SendAmountFieldMaxAmountConverter(
private val currentStateProvider: Provider<SendUiState>,
@ -23,6 +24,8 @@ internal class SendAmountFieldMaxAmountConverter(
val decimalCryptoValue = cryptoCurrencyStatus.value.amount
val decimalFiatValue = cryptoCurrencyStatus.value.fiatAmount
if (decimalCryptoValue.isNullOrZero()) return state
val cryptoValue = decimalCryptoValue?.parseBigDecimal(cryptoDecimals).orEmpty()
val fiatValue = decimalFiatValue?.parseBigDecimal(fiatDecimals).orEmpty()
return state.copy(

View file

@ -3,6 +3,8 @@ package com.tangem.features.send.impl.presentation.ui
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandHorizontally
import androidx.compose.animation.shrinkHorizontally
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
@ -14,6 +16,7 @@ import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
@ -54,6 +57,8 @@ private fun SendSecondaryNavigationButton(uiState: SendUiState) {
val isCorrectScreen = currentState.value == SendUiStateType.Amount || currentState.value == SendUiStateType.Fee
AnimatedVisibility(
visible = !isEditingDisabled && isCorrectScreen,
enter = expandHorizontally(expandFrom = Alignment.End),
exit = shrinkHorizontally(shrinkTowards = Alignment.End),
) {
Icon(
modifier = Modifier

View file

@ -46,6 +46,7 @@ internal fun SendAmountContent(
.weight(1f)
.height(TangemTheme.dimens.size40),
config = amountState.segmentedButtonConfig,
showIndication = false,
onClick = { clickIntents.onCurrencyChangeClick(it.isFiat) },
) {
SendAmountCurrencyButton(it)

View file

@ -1,15 +1,14 @@
package com.tangem.features.send.impl.presentation.ui.recipient
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment.Companion.CenterEnd
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import com.tangem.core.ui.components.fields.SimpleTextField
import com.tangem.core.ui.components.inputrow.inner.CrossIcon
import com.tangem.core.ui.components.inputrow.inner.PasteButton
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
@ -35,40 +34,46 @@ internal fun TextFieldWithPaste(
else -> label to TangemTheme.colors.text.secondary
}
FooterContainer(modifier, footer) {
Row(
Box(
modifier = Modifier
.background(
color = TangemTheme.colors.background.action,
shape = TangemTheme.shapes.roundedCornersXMedium,
),
) {
Column(
modifier = Modifier
.weight(1f)
.padding(TangemTheme.dimens.spacing12),
) {
Text(
text = title.resolveReference(),
style = TangemTheme.typography.body2,
color = color,
)
SimpleTextField(
value = value,
placeholder = placeholder,
onValueChange = onValueChange,
readOnly = isReadOnly,
.padding(end = TangemTheme.dimens.spacing16),
) {
Row {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = TangemTheme.dimens.spacing6),
.weight(1f)
.padding(TangemTheme.dimens.spacing12),
) {
Text(
text = title.resolveReference(),
style = TangemTheme.typography.body2,
color = color,
)
SimpleTextField(
value = value,
placeholder = placeholder,
onValueChange = onValueChange,
readOnly = isReadOnly,
modifier = Modifier
.fillMaxWidth()
.padding(top = TangemTheme.dimens.spacing6),
)
}
CrossIcon(
onClick = onPasteClick,
modifier = Modifier
.align(CenterVertically),
)
}
if (!isReadOnly) {
PasteButton(
isPasteButtonVisible = value.isBlank(),
onClick = onPasteClick,
modifier = Modifier
.align(CenterVertically)
.padding(end = TangemTheme.dimens.spacing16),
modifier = Modifier.align(CenterEnd),
)
}
}