Updated on 2026-08-14
This commit is contained in:
parent
464f8f8a80
commit
d10cf0ac2e
23 changed files with 1117 additions and 10 deletions
|
|
@ -6,6 +6,7 @@ import androidx.compose.material3.ModalBottomSheet
|
|||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -20,6 +21,7 @@ import kotlinx.coroutines.launch
|
|||
@Composable
|
||||
inline fun <reified T : TangemBottomSheetConfigContent> TangemBottomSheet(
|
||||
config: TangemBottomSheetConfig,
|
||||
color: Color = TangemTheme.colors.background.primary,
|
||||
crossinline content: @Composable ColumnScope.(T) -> Unit,
|
||||
) {
|
||||
var isVisible by remember { mutableStateOf(value = config.isShow) }
|
||||
|
|
@ -29,9 +31,9 @@ inline fun <reified T : TangemBottomSheetConfigContent> TangemBottomSheet(
|
|||
ModalBottomSheet(
|
||||
onDismissRequest = config.onDismissRequest,
|
||||
sheetState = sheetState,
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
containerColor = color,
|
||||
shape = TangemTheme.shapes.bottomSheetLarge,
|
||||
dragHandle = { TangemBottomSheetDraggableHeader() },
|
||||
dragHandle = { TangemBottomSheetDraggableHeader(color) },
|
||||
) {
|
||||
content(config.content)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
package com.tangem.core.ui.components.inputrow
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.currency.tokenicon.TokenIcon
|
||||
import com.tangem.core.ui.components.currency.tokenicon.TokenIconState
|
||||
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* [Input Row Approx](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2207-810&mode=design&t=fM1ZU6zQF6g3CaTv-4)
|
||||
*
|
||||
* @param leftIcon left token state
|
||||
* @param leftTitle left token title
|
||||
* @param leftSubtitle left token subtitle
|
||||
* @param rightIcon right token state
|
||||
* @param rightTitle right token title
|
||||
* @param rightSubtitle right token subtitle
|
||||
* @param modifier composable modifier
|
||||
* @param showDivider show divider
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
fun InputRowApprox(
|
||||
leftIcon: TokenIconState,
|
||||
leftTitle: TextReference,
|
||||
leftSubtitle: TextReference,
|
||||
rightIcon: TokenIconState,
|
||||
rightTitle: TextReference,
|
||||
rightSubtitle: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
showDivider: Boolean = false,
|
||||
) {
|
||||
DividerContainer(
|
||||
showDivider = showDivider,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens.spacing12)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
InputRowApproxItem(
|
||||
iconState = leftIcon,
|
||||
title = leftTitle,
|
||||
subtitle = leftSubtitle,
|
||||
)
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_approx_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing8,
|
||||
vertical = TangemTheme.dimens.spacing10,
|
||||
),
|
||||
)
|
||||
InputRowApproxItem(
|
||||
iconState = rightIcon,
|
||||
title = rightTitle,
|
||||
subtitle = rightSubtitle,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InputRowApproxItem(
|
||||
iconState: TokenIconState,
|
||||
title: TextReference,
|
||||
subtitle: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
) {
|
||||
TokenIcon(
|
||||
state = iconState,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size36),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = subtitle.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing2),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//region Preview
|
||||
@Preview
|
||||
@Composable
|
||||
private fun InputRowApproxPreview_Light() {
|
||||
TangemTheme {
|
||||
InputRowApprox(
|
||||
leftIcon = TokenIconState.Loading,
|
||||
leftTitle = TextReference.Str("Left title"),
|
||||
leftSubtitle = TextReference.Str("Left subtitle"),
|
||||
rightIcon = TokenIconState.Loading,
|
||||
rightTitle = TextReference.Str("Right title"),
|
||||
rightSubtitle = TextReference.Str("Right subtitle"),
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun InputRowApproxPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
InputRowApprox(
|
||||
leftIcon = TokenIconState.Loading,
|
||||
leftTitle = TextReference.Str("Left title"),
|
||||
leftSubtitle = TextReference.Str("Left subtitle"),
|
||||
rightIcon = TokenIconState.Loading,
|
||||
rightTitle = TextReference.Str("Right title"),
|
||||
rightSubtitle = TextReference.Str("Right subtitle"),
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
package com.tangem.core.ui.components.inputrow
|
||||
|
||||
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.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
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.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.currency.tokenicon.LoadingIcon
|
||||
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* [Input Row Best Rate](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-889&mode=dev)
|
||||
*
|
||||
* @param imageUrl image source url
|
||||
* @param title title
|
||||
* @param titleExtra title extra
|
||||
* @param subtitle subtitle
|
||||
* @param modifier composable modifier
|
||||
* @param showTag show tag
|
||||
* @param showDivider show divider
|
||||
* @param onIconClick icon click
|
||||
*/
|
||||
@Composable
|
||||
fun InputRowBestRate(
|
||||
imageUrl: String,
|
||||
title: TextReference,
|
||||
titleExtra: TextReference,
|
||||
subtitle: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
showTag: Boolean = false,
|
||||
showDivider: Boolean = false,
|
||||
onIconClick: (() -> Unit)? = null,
|
||||
) {
|
||||
DividerContainer(
|
||||
showDivider = showDivider,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
InnerIcon(imageUrl = imageUrl)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
InnerTitle(
|
||||
title = title,
|
||||
titleExtra = titleExtra,
|
||||
showTag = showTag,
|
||||
)
|
||||
Text(
|
||||
text = subtitle.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
}
|
||||
SpacerWMax()
|
||||
onIconClick?.let {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_chevron_right_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
modifier = Modifier
|
||||
.padding(vertical = TangemTheme.dimens.spacing10)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(bounded = false),
|
||||
onClick = onIconClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InnerTitle(title: TextReference, titleExtra: TextReference, showTag: Boolean = false) {
|
||||
Row {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = titleExtra.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens.spacing4),
|
||||
)
|
||||
if (showTag) {
|
||||
Text(
|
||||
text = stringResource(R.string.express_provider_best_rate),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.icon.accent,
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens.spacing4)
|
||||
.background(
|
||||
color = TangemTheme.colors.icon.accent.copy(alpha = 0.1f),
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius20),
|
||||
)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing6),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InnerIcon(imageUrl: String) {
|
||||
SubcomposeAsyncImage(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size40),
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(imageUrl)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(enable = false)
|
||||
.build(),
|
||||
loading = { LoadingIcon() },
|
||||
error = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.tertiary,
|
||||
shape = CircleShape,
|
||||
),
|
||||
)
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
//region preview
|
||||
@Preview
|
||||
@Composable
|
||||
private fun InputRowBestRatePreview_Light(
|
||||
@PreviewParameter(InputRowBestRatePreviewDataProvider::class) data: InputRowBestRatePreviewData,
|
||||
) {
|
||||
TangemTheme {
|
||||
InputRowBestRate(
|
||||
imageUrl = "",
|
||||
title = data.title,
|
||||
titleExtra = data.titleExtra,
|
||||
subtitle = data.subtitle,
|
||||
showTag = data.showTag,
|
||||
onIconClick = data.iconClick,
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun InputRowBestRatePreview_Dark(
|
||||
@PreviewParameter(InputRowBestRatePreviewDataProvider::class) data: InputRowBestRatePreviewData,
|
||||
) {
|
||||
TangemTheme(isDark = true) {
|
||||
InputRowBestRate(
|
||||
imageUrl = "",
|
||||
title = data.title,
|
||||
titleExtra = data.titleExtra,
|
||||
subtitle = data.subtitle,
|
||||
showTag = data.showTag,
|
||||
onIconClick = data.iconClick,
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private data class InputRowBestRatePreviewData(
|
||||
val title: TextReference,
|
||||
val titleExtra: TextReference,
|
||||
val showTag: Boolean,
|
||||
val subtitle: TextReference,
|
||||
val iconClick: (() -> Unit)?,
|
||||
)
|
||||
|
||||
private class InputRowBestRatePreviewDataProvider : PreviewParameterProvider<InputRowBestRatePreviewData> {
|
||||
override val values: Sequence<InputRowBestRatePreviewData>
|
||||
get() = sequenceOf(
|
||||
InputRowBestRatePreviewData(
|
||||
title = TextReference.Str("1inch"),
|
||||
titleExtra = TextReference.Str("DEX"),
|
||||
subtitle = TextReference.Str("0,64554846 DAI ≈ 1 MATIC "),
|
||||
showTag = true,
|
||||
iconClick = {},
|
||||
),
|
||||
InputRowBestRatePreviewData(
|
||||
title = TextReference.Str("ChangeNow"),
|
||||
titleExtra = TextReference.Str("CEX"),
|
||||
subtitle = TextReference.Str("0,64554846 DAI ≈ 1 MATIC "),
|
||||
showTag = false,
|
||||
iconClick = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
//endregion
|
||||
|
|
@ -41,6 +41,7 @@ data class TangemDimens internal constructor(
|
|||
val size0: Dp = 0.dp,
|
||||
val size0_5: Dp = 0.5.dp,
|
||||
val size1: Dp = 1.dp,
|
||||
val size1_5: Dp = 1.5.dp,
|
||||
val size2: Dp = 2.dp,
|
||||
val size4: Dp = 4.dp,
|
||||
val size5: Dp = 5.dp,
|
||||
|
|
|
|||
10
core/ui/src/main/res/drawable/ic_alert_triangle_20.xml
Normal file
10
core/ui/src/main/res/drawable/ic_alert_triangle_20.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="M9.135,2.328L1.702,15.165C1.316,15.832 1.797,16.667 2.568,16.667H17.432C18.202,16.667 18.684,15.832 18.298,15.165L10.865,2.328C10.48,1.663 9.52,1.663 9.135,2.328ZM10.833,11.667H9.167V6.667H10.833V11.667ZM10.833,15H9.167V13.333H10.833V15Z"
|
||||
android:fillColor="#FFB71B"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
9
core/ui/src/main/res/drawable/ic_approx_24.xml
Normal file
9
core/ui/src/main/res/drawable/ic_approx_24.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<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="M14.153,11.13C12.544,11.13 11.142,9.695 9.731,9.695C8.625,9.695 7.825,10.153 7.25,10.798V9.076C7.816,8.457 8.652,8 9.847,8C11.456,8 12.867,9.435 14.269,9.435C15.366,9.435 16.175,8.978 16.75,8.332V10.054C16.184,10.673 15.348,11.13 14.153,11.13ZM14.153,16C12.544,16 11.142,14.565 9.731,14.565C8.625,14.565 7.825,15.022 7.25,15.668V13.946C7.816,13.327 8.652,12.87 9.847,12.87C11.456,12.87 12.867,14.305 14.269,14.305C15.366,14.305 16.175,13.856 16.75,13.202V14.924C16.184,15.543 15.348,16 14.153,16Z"
|
||||
android:fillColor="#909090"/>
|
||||
</vector>
|
||||
12
core/ui/src/main/res/drawable/ic_exclamation_24.xml
Normal file
12
core/ui/src/main/res/drawable/ic_exclamation_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="M11,13H13V7H11V13Z"
|
||||
android:fillColor="#FFB71B"/>
|
||||
<path
|
||||
android:pathData="M11,17H13V15H11V17Z"
|
||||
android:fillColor="#FFB71B"/>
|
||||
</vector>
|
||||
9
core/ui/src/main/res/drawable/ic_forward_24.xml
Normal file
9
core/ui/src/main/res/drawable/ic_forward_24.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<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="M4.16,11V13H16.16L10.66,18.5L12.08,19.92L20,12L12.08,4.08L10.66,5.5L16.16,11H4.16Z"
|
||||
android:fillColor="#1E1E1E"/>
|
||||
</vector>
|
||||
Loading…
Add table
Add a link
Reference in a new issue