Updated on 2026-08-14
This commit is contained in:
parent
f9feacf074
commit
07f63b47e4
15 changed files with 245 additions and 32 deletions
|
|
@ -9,7 +9,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resolveAnnotatedReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
|
|
@ -32,7 +32,7 @@ fun FooterContainer(
|
|||
AnimatedVisibility(visible = footer != null) {
|
||||
val footerWrapped = remember(this) { requireNotNull(footer) }
|
||||
Text(
|
||||
text = footerWrapped.resolveReference(),
|
||||
text = footerWrapped.resolveAnnotatedReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ package com.tangem.core.ui.components.inputrow
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
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.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
|
|
@ -16,6 +20,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.buttons.small.TangemIconButton
|
||||
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
|
||||
|
|
@ -25,24 +30,29 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.DEFAULT_ANIMATION_DURATION
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
/**
|
||||
* [Input Row Recipient](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-826&mode=design&t=IQ5lBJEkFGU4WSvi-4)
|
||||
* * [Design Reference](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-826&mode=design&t=IQ5lBJEkFGU4WSvi-4)
|
||||
*
|
||||
* @param title title reference
|
||||
* @param value recipient address
|
||||
* @param placeholder placeholder
|
||||
* @param onValueChange callback for value change
|
||||
* @param onPasteClick callback for paste
|
||||
* @param modifier composable modifier
|
||||
* @param singleLine is single line text
|
||||
* @param error error text
|
||||
* @param isError is error flag
|
||||
* @param showDivider show divider
|
||||
* @param title The title text reference to display above the input field
|
||||
* @param value The current recipient address value to display in the input field
|
||||
* @param placeholder The placeholder text reference to show when the input is empty
|
||||
* @param onValueChange Callback invoked when the input value changes, receives the new string value
|
||||
* @param onPasteClick Callback invoked when the paste button is clicked, receives the pasted string
|
||||
* @param onQrCodeClick Callback invoked when the QR code scan button is clicked
|
||||
* @param modifier Optional modifier to apply to the composable
|
||||
* @param singleLine Whether the text field should be constrained to a single line (default: false)
|
||||
* @param error Optional error text reference to display when validation fails
|
||||
* @param isError Whether the input is in an error state, affects styling and title color
|
||||
* @param showDivider Whether to show a divider below the component (default: false)
|
||||
* @param isLoading Whether to show a loading indicator instead of the identicon (default: false)
|
||||
* @param isValuePasted Whether the current value was pasted, affects visual feedback (default: false)
|
||||
*
|
||||
* @see InputRowRecipientDefault for readonly version
|
||||
* @see InputRowRecipientDefault for a read-only version of this component
|
||||
*/
|
||||
@Suppress("LongParameterList", "LongMethod")
|
||||
@Composable
|
||||
fun InputRowRecipient(
|
||||
title: TextReference,
|
||||
|
|
@ -50,6 +60,8 @@ fun InputRowRecipient(
|
|||
placeholder: TextReference,
|
||||
onValueChange: (String) -> Unit,
|
||||
onPasteClick: (String) -> Unit,
|
||||
onQrCodeClick: () -> Unit,
|
||||
isRedesignEnabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
singleLine: Boolean = false,
|
||||
error: TextReference? = null,
|
||||
|
|
@ -109,18 +121,55 @@ fun InputRowRecipient(
|
|||
.padding(start = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
}
|
||||
PasteButton(
|
||||
isPasteButtonVisible = value.isBlank(),
|
||||
onClick = onPasteClick,
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(CenterEnd)
|
||||
.padding(start = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
.align(CenterEnd),
|
||||
) {
|
||||
if (isRedesignEnabled) {
|
||||
QrButton(
|
||||
visible = !singleLine,
|
||||
onQrCodeClick = onQrCodeClick,
|
||||
)
|
||||
}
|
||||
PasteButton(
|
||||
isPasteButtonVisible = value.isBlank(),
|
||||
onClick = onPasteClick,
|
||||
backgroundColorEnabled = if (isRedesignEnabled) {
|
||||
TangemTheme.colors.button.secondary
|
||||
} else {
|
||||
TangemTheme.colors.button.primary
|
||||
},
|
||||
textColor = if (isRedesignEnabled) {
|
||||
TangemTheme.colors.text.primary1
|
||||
} else {
|
||||
TangemTheme.colors.text.primary2
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun QrButton(visible: Boolean, onQrCodeClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
AnimatedVisibility(
|
||||
visible = visible,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(animationSpec = tween(DEFAULT_ANIMATION_DURATION)),
|
||||
modifier = modifier,
|
||||
) {
|
||||
TangemIconButton(
|
||||
iconRes = R.drawable.ic_scan_16,
|
||||
onClick = onQrCodeClick,
|
||||
background = TangemTheme.colors.button.secondary,
|
||||
iconTint = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.InputIcon(isLoading: Boolean, value: String) {
|
||||
var isLoadingProxy by remember { mutableStateOf(isLoading) }
|
||||
|
|
@ -172,7 +221,9 @@ private fun InputRowRecipientPreview(
|
|||
showDivider = true,
|
||||
onValueChange = {},
|
||||
onPasteClick = {},
|
||||
onQrCodeClick = {},
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
isRedesignEnabled = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
|
|
@ -34,14 +35,21 @@ import com.tangem.core.ui.utils.DEFAULT_ANIMATION_DURATION
|
|||
* @param modifier composable modifier
|
||||
*/
|
||||
@Composable
|
||||
fun PasteButton(isPasteButtonVisible: Boolean, onClick: (String) -> Unit, modifier: Modifier = Modifier) {
|
||||
fun PasteButton(
|
||||
isPasteButtonVisible: Boolean,
|
||||
onClick: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
backgroundColorEnabled: Color = TangemTheme.colors.button.primary,
|
||||
backgroundColorDisabled: Color = TangemTheme.colors.button.secondary,
|
||||
textColor: Color = TangemTheme.colors.text.primary2,
|
||||
) {
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
val hapticFeedback = LocalHapticFeedback.current
|
||||
val isPasteEnabled = !clipboardManager.getText()?.text.isNullOrEmpty()
|
||||
val color = if (isPasteEnabled) {
|
||||
TangemTheme.colors.button.primary
|
||||
backgroundColorEnabled
|
||||
} else {
|
||||
TangemTheme.colors.button.secondary
|
||||
backgroundColorDisabled
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = isPasteButtonVisible,
|
||||
|
|
@ -53,7 +61,7 @@ fun PasteButton(isPasteButtonVisible: Boolean, onClick: (String) -> Unit, modifi
|
|||
Text(
|
||||
text = stringResourceSafe(R.string.common_paste),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.primary2,
|
||||
color = textColor,
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = color,
|
||||
|
|
|
|||
|
|
@ -60,4 +60,40 @@ fun AnnotatedString.Builder.appendSpace() = append(" ")
|
|||
|
||||
fun AnnotatedString.Builder.appendColored(text: String, color: Color) = withStyle(SpanStyle(color = color)) {
|
||||
append(text)
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends text from a template string to the AnnotatedString.Builder, replacing a placeholder (default "%s")
|
||||
* with custom styled content provided by a lambda. The lambda allows you to insert styled or complex content
|
||||
* (e.g., colored, bold, or annotated text) at the placeholder position.
|
||||
*
|
||||
* If the placeholder is not found in the template, the function does nothing.
|
||||
*
|
||||
* Example usage:
|
||||
* builder.appendWithStyledPlaceholder(
|
||||
* template = "Ensure you %s network address, as errors may result in lost transfers"
|
||||
* ) {
|
||||
* withStyle(SpanStyle(color = Color.Red, fontWeight = FontWeight.Bold)) {
|
||||
* append("Ethereum")
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @param template The template string containing the placeholder to be replaced.
|
||||
* @param placeholder The placeholder string to be replaced by styled content. Default is "%s".
|
||||
* @param styledContent Lambda to build the styled content to insert at the placeholder position.
|
||||
*/
|
||||
fun AnnotatedString.Builder.appendWithStyledPlaceholder(
|
||||
template: String,
|
||||
placeholder: String = "%s",
|
||||
styledContent: AnnotatedString.Builder.() -> Unit,
|
||||
) {
|
||||
val index = template.indexOf(placeholder)
|
||||
if (index < 0) return
|
||||
// Append before placeholder
|
||||
if (index > 0) append(template.substring(0, index))
|
||||
// Append styled content
|
||||
styledContent()
|
||||
// Append after placeholder
|
||||
val afterIndex = index + placeholder.length
|
||||
if (afterIndex < template.length) append(template.substring(afterIndex))
|
||||
}
|
||||
10
core/ui/src/main/res/drawable/ic_scan_16.xml
Normal file
10
core/ui/src/main/res/drawable/ic_scan_16.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="17dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="17">
|
||||
<path
|
||||
android:pathData="M6.666,2.391H4C2.895,2.391 2,3.287 2,4.391V7.391L3.333,7.391V4.391C3.333,4.023 3.631,3.725 4,3.725H6.666V2.391ZM2,10.391V12.391C2,13.496 2.895,14.391 4,14.391H6.666V13.058H4C3.631,13.058 3.333,12.759 3.333,12.391V10.391H12.666V12.391C12.666,12.759 12.368,13.058 12,13.058H9.333V14.391H12C13.104,14.391 14,13.496 14,12.391V10.391H14.666V9.058H1.333V10.391H2ZM14,4.391V7.391H12.666V4.391C12.666,4.023 12.368,3.725 12,3.725H9.333V2.391H12C13.104,2.391 14,3.287 14,4.391Z"
|
||||
android:fillColor="#1E1E1E"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
Loading…
Add table
Add a link
Reference in a new issue