Updated on 2026-08-14
|
|
@ -42,7 +42,6 @@ dependencies {
|
|||
/** Compose */
|
||||
implementation(deps.compose.constraintLayout)
|
||||
implementation(deps.compose.foundation)
|
||||
implementation(deps.compose.material)
|
||||
implementation(deps.compose.material3)
|
||||
implementation(deps.compose.paging)
|
||||
implementation(deps.compose.ui.tooling)
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ fun AppBarWithBackButton(
|
|||
TangemTopAppBar(
|
||||
modifier = modifier,
|
||||
title = text,
|
||||
startButton = TopAppBarButtonUM(
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = iconRes ?: R.drawable.ic_back_24,
|
||||
onIconClicked = onBackClick,
|
||||
onClicked = onBackClick,
|
||||
),
|
||||
containerColor = containerColor,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -27,14 +27,14 @@ fun AppBarWithBackButtonAndIcon(
|
|||
title = text,
|
||||
subtitle = subtitle,
|
||||
containerColor = backgroundColor,
|
||||
startButton = TopAppBarButtonUM(
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = backIconRes ?: R.drawable.ic_back_24,
|
||||
onIconClicked = onBackClick,
|
||||
onClicked = onBackClick,
|
||||
),
|
||||
endButton = if (iconRes != null && onIconClick != null) {
|
||||
TopAppBarButtonUM(
|
||||
TopAppBarButtonUM.Icon(
|
||||
iconRes = iconRes,
|
||||
onIconClicked = onIconClick,
|
||||
onClicked = onIconClick,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.compose.runtime.ReadOnlyComposable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
|
|
@ -20,6 +21,7 @@ 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.test.TopAppBarTestTags
|
||||
|
||||
/**
|
||||
* [TangemTopAppBar] height options.
|
||||
|
|
@ -127,6 +129,7 @@ fun TangemTopAppBar(
|
|||
TopAppBarButton(
|
||||
button = endButton,
|
||||
tint = iconTint,
|
||||
modifier = Modifier.testTag(TopAppBarTestTags.MORE_BUTTON),
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
@ -172,6 +175,7 @@ fun TangemTopAppBar(
|
|||
TopAppBarButton(
|
||||
button = startButton,
|
||||
tint = iconTint,
|
||||
modifier = Modifier.testTag(TopAppBarTestTags.CLOSE_BUTTON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -222,6 +226,7 @@ private fun TopAppBarTitle(
|
|||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.testTag(TopAppBarTestTags.TITLE),
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
|
|
@ -296,25 +301,25 @@ private class BasicTopAppBarPMPreviewProvider : PreviewParameterProvider<BasicTo
|
|||
height = TangemTopAppBarHeight.BOTTOM_SHEET,
|
||||
),
|
||||
BasicTopAppBarPM(
|
||||
startButton = TopAppBarButtonUM(
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_scan_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
endButton = TopAppBarButtonUM(
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_more_vertical_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
),
|
||||
BasicTopAppBarPM(
|
||||
startButton = TopAppBarButtonUM(
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_scan_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
),
|
||||
BasicTopAppBarPM(
|
||||
endButton = TopAppBarButtonUM(
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_more_vertical_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
height = TangemTopAppBarHeight.BOTTOM_SHEET,
|
||||
),
|
||||
|
|
@ -322,13 +327,13 @@ private class BasicTopAppBarPMPreviewProvider : PreviewParameterProvider<BasicTo
|
|||
title = "1234567891011121314151617181920",
|
||||
subtitle = "12345678910111213141516171819202122232425",
|
||||
titleAlignment = Alignment.Start,
|
||||
startButton = TopAppBarButtonUM(
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_scan_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
endButton = TopAppBarButtonUM(
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_more_vertical_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,27 +1,49 @@
|
|||
package com.tangem.core.ui.components.appbar
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.extensions.conditional
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
fun TopAppBarButton(button: TopAppBarButtonUM, tint: Color, modifier: Modifier = Modifier) {
|
||||
IconButton(
|
||||
enabled = button.enabled,
|
||||
modifier = modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = button.onIconClicked,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = button.iconRes),
|
||||
tint = tint,
|
||||
contentDescription = null,
|
||||
)
|
||||
when (button) {
|
||||
is TopAppBarButtonUM.Icon -> {
|
||||
IconButton(
|
||||
enabled = button.enabled,
|
||||
modifier = modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = button.onClicked,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = button.iconRes),
|
||||
tint = tint,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
is TopAppBarButtonUM.Text -> {
|
||||
Text(
|
||||
modifier = modifier
|
||||
.conditional(button.enabled) {
|
||||
clickable { button.onClicked() }
|
||||
}
|
||||
.padding(4.dp),
|
||||
text = button.text.resolveReference(),
|
||||
color = tint,
|
||||
style = TangemTheme.typography.body1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,21 +2,39 @@ package com.tangem.core.ui.components.appbar.models
|
|||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
data class TopAppBarButtonUM(
|
||||
@DrawableRes val iconRes: Int,
|
||||
val onIconClicked: () -> Unit,
|
||||
val enabled: Boolean = true,
|
||||
sealed class TopAppBarButtonUM(
|
||||
open val onClicked: () -> Unit,
|
||||
open val enabled: Boolean = true,
|
||||
) {
|
||||
|
||||
data class Icon(
|
||||
@DrawableRes val iconRes: Int,
|
||||
override val onClicked: () -> Unit,
|
||||
override val enabled: Boolean = true,
|
||||
) : TopAppBarButtonUM(onClicked, enabled)
|
||||
|
||||
data class Text(
|
||||
val text: TextReference,
|
||||
override val onClicked: () -> Unit,
|
||||
override val enabled: Boolean = true,
|
||||
) : TopAppBarButtonUM(onClicked, enabled)
|
||||
|
||||
@Suppress("FunctionName")
|
||||
companion object {
|
||||
|
||||
fun Back(onBackClicked: () -> Unit) = Back(true, onBackClicked)
|
||||
|
||||
fun Back(enabled: Boolean = true, onBackClicked: () -> Unit) = TopAppBarButtonUM(
|
||||
fun Back(enabled: Boolean = true, onBackClicked: () -> Unit) = Icon(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onIconClicked = onBackClicked,
|
||||
onClicked = onBackClicked,
|
||||
enabled = enabled,
|
||||
)
|
||||
|
||||
fun Text(text: TextReference, onTextClicked: () -> Unit, enabled: Boolean = true) = Text(
|
||||
text = text,
|
||||
onClicked = onTextClicked,
|
||||
enabled = enabled,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.core.ui.components.block.model.BlockUM
|
||||
import com.tangem.core.ui.components.label.Label
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
|
|
@ -38,6 +39,7 @@ fun BlockItem(model: BlockUM, modifier: Modifier = Modifier) {
|
|||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = model.text.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = when (model.accentType) {
|
||||
|
|
@ -48,6 +50,8 @@ fun BlockItem(model: BlockUM, modifier: Modifier = Modifier) {
|
|||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
model.label?.let { Label(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.core.ui.components.block.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
data class BlockUM(
|
||||
|
|
@ -8,6 +9,7 @@ data class BlockUM(
|
|||
@DrawableRes val iconRes: Int,
|
||||
val onClick: () -> Unit,
|
||||
val accentType: AccentType = AccentType.NONE,
|
||||
val label: LabelUM? = null,
|
||||
) {
|
||||
|
||||
enum class AccentType {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
package com.tangem.core.ui.components.bottomsheets
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.inputrow.InputRowDefault
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* Generic options bottom sheet component
|
||||
*
|
||||
* @param config Bottom sheet configuration containing OptionsBottomSheetContent
|
||||
* @param title Title text for the bottom sheet
|
||||
* @param containerColor Background color of the bottom sheet
|
||||
*/
|
||||
@Composable
|
||||
fun OptionsBottomSheet(
|
||||
config: TangemBottomSheetConfig,
|
||||
title: TextReference,
|
||||
containerColor: androidx.compose.ui.graphics.Color = TangemTheme.colors.background.tertiary,
|
||||
) {
|
||||
TangemBottomSheet<OptionsBottomSheetContent>(
|
||||
config = config,
|
||||
titleText = title,
|
||||
containerColor = containerColor,
|
||||
content = { content ->
|
||||
OptionsBottomSheetContent(content = content)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OptionsBottomSheetContent(content: OptionsBottomSheetContent) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
) {
|
||||
content.options.forEachIndexed { index, option ->
|
||||
InputRowDefault(
|
||||
text = option.label,
|
||||
showDivider = index < content.options.size - 1,
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = content.options.size - 1,
|
||||
addDefaultPadding = false,
|
||||
)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clickable { content.onOptionClick(option.key) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun OptionsBottomSheetPreview() {
|
||||
TangemThemePreview {
|
||||
OptionsBottomSheet(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = {},
|
||||
content = OptionsBottomSheetContent(
|
||||
options = persistentListOf(
|
||||
BottomSheetOption(
|
||||
key = "option1",
|
||||
label = TextReference.Str("First Option"),
|
||||
),
|
||||
BottomSheetOption(
|
||||
key = "option2",
|
||||
label = TextReference.Str("Second Option"),
|
||||
),
|
||||
BottomSheetOption(
|
||||
key = "option3",
|
||||
label = TextReference.Str("Third Option"),
|
||||
),
|
||||
),
|
||||
onOptionClick = {},
|
||||
),
|
||||
),
|
||||
title = TextReference.Str("Select Option"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.core.ui.components.bottomsheets
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* @param key Unique identifier for the option
|
||||
* @param label Display text for the option
|
||||
*/
|
||||
data class BottomSheetOption(
|
||||
val key: String,
|
||||
val label: TextReference,
|
||||
)
|
||||
|
||||
/**
|
||||
* @param options List of options to display
|
||||
* @param onOptionClick Callback when an option is clicked, receives the option key
|
||||
*/
|
||||
data class OptionsBottomSheetContent(
|
||||
val options: ImmutableList<BottomSheetOption> = persistentListOf(),
|
||||
val onOptionClick: (String) -> Unit = {},
|
||||
) : TangemBottomSheetConfigContent
|
||||
|
|
@ -179,6 +179,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheet(
|
|||
onBack = onBack,
|
||||
dragHandle = null,
|
||||
content = bsContent,
|
||||
scrimColor = TangemTheme.colors.overlay.secondary,
|
||||
)
|
||||
} else {
|
||||
ModalBottomSheet(
|
||||
|
|
@ -190,6 +191,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheet(
|
|||
contentWindowInsets = { WindowInsetsZero },
|
||||
dragHandle = null,
|
||||
content = bsContent,
|
||||
scrimColor = TangemTheme.colors.overlay.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -200,58 +202,62 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheet(
|
|||
@Composable
|
||||
private fun TangemModalBottomSheet_Preview() {
|
||||
TangemThemePreview {
|
||||
TangemModalBottomSheet<TangemBottomSheetConfigContentPreviewConfig>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = {},
|
||||
content = TangemBottomSheetConfigContentPreviewConfig(),
|
||||
),
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = {},
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Column(
|
||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(56.dp)
|
||||
.clip(RoundedCornerShape(100))
|
||||
.background(TangemTheme.colors.icon.informative.copy(alpha = 0.1f))
|
||||
.padding(12.dp),
|
||||
painter = rememberVectorPainter(
|
||||
ImageVector.vectorResource(R.drawable.ic_alert_24),
|
||||
),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
Box(
|
||||
Modifier.background(TangemTheme.colors.background.tertiary),
|
||||
) {
|
||||
TangemModalBottomSheet<TangemBottomSheetConfigContentPreviewConfig>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = {},
|
||||
content = TangemBottomSheetConfigContentPreviewConfig(),
|
||||
),
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = {},
|
||||
)
|
||||
SpacerH24()
|
||||
Text(
|
||||
text = "Unsuported networks",
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH8()
|
||||
Text(
|
||||
text = "Tangem does not currently support a required network by React App.",
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH(48.dp)
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Go it",
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Column(
|
||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(56.dp)
|
||||
.clip(RoundedCornerShape(100))
|
||||
.background(TangemTheme.colors.icon.informative.copy(alpha = 0.1f))
|
||||
.padding(12.dp),
|
||||
painter = rememberVectorPainter(
|
||||
ImageVector.vectorResource(R.drawable.ic_alert_24),
|
||||
),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
SpacerH24()
|
||||
Text(
|
||||
text = "Unsuported networks",
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH8()
|
||||
Text(
|
||||
text = "Tangem does not currently support a required network by React App.",
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH(48.dp)
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Go it",
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import com.tangem.core.ui.res.LocalBottomSheetAlwaysVisible
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.WindowInsetsZero
|
||||
import com.tangem.core.ui.utils.toPx
|
||||
|
||||
/**
|
||||
* Modal bottom sheet with [content], [footer] and optional [title].
|
||||
|
|
@ -154,6 +155,21 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
|
|||
val initial = 0
|
||||
val scrollState = rememberScrollState(initial = initial)
|
||||
|
||||
val isKeyboardOpen by rememberIsKeyboardVisible()
|
||||
val buttonHeight = TangemTheme.dimens.spacing80
|
||||
val contentBottomPadding = TangemTheme.dimens.spacing80
|
||||
// Offset calculation for keyboard scroll adjustment:
|
||||
// 1) Button height (footer)
|
||||
// 2) Column content bottom padding
|
||||
// 3) Additional spacing (40dp) for visual comfort when keyboard is open
|
||||
val scrollOffset = buttonHeight.toPx() + buttonHeight.toPx() + 40.dp.toPx()
|
||||
|
||||
LaunchedEffect(isKeyboardOpen) {
|
||||
if (isKeyboardOpen) {
|
||||
scrollState.animateScrollTo(scrollState.value + scrollOffset.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.systemBarsPadding()
|
||||
|
|
@ -186,7 +202,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
|
|||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(state = scrollState)
|
||||
.padding(bottom = TangemTheme.dimens.spacing80),
|
||||
.padding(bottom = contentBottomPadding),
|
||||
) {
|
||||
content(model)
|
||||
}
|
||||
|
|
@ -199,7 +215,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(80.dp)
|
||||
.height(buttonHeight)
|
||||
.align(Alignment.BottomCenter),
|
||||
) {
|
||||
footer(model)
|
||||
|
|
@ -219,6 +235,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
|
|||
onBack = onBack,
|
||||
dragHandle = null,
|
||||
content = bsContent,
|
||||
scrimColor = TangemTheme.colors.overlay.secondary,
|
||||
)
|
||||
} else {
|
||||
ModalBottomSheet(
|
||||
|
|
@ -230,6 +247,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
|
|||
contentWindowInsets = { WindowInsetsZero },
|
||||
dragHandle = null,
|
||||
content = bsContent,
|
||||
scrimColor = TangemTheme.colors.overlay.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicBottomSheet(
|
|||
dragHandle = { TangemBottomSheetDraggableHeader(color = containerColor) },
|
||||
onBack = onBack,
|
||||
content = bsContent,
|
||||
scrimColor = TangemTheme.colors.overlay.secondary,
|
||||
)
|
||||
} else {
|
||||
ModalBottomSheet(
|
||||
|
|
@ -203,6 +204,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicBottomSheet(
|
|||
contentWindowInsets = { WindowInsetsZero },
|
||||
dragHandle = { TangemBottomSheetDraggableHeader(color = containerColor) },
|
||||
content = bsContent,
|
||||
scrimColor = TangemTheme.colors.overlay.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -57,9 +57,9 @@ private fun Preview_TangemBottomSheetTitle() {
|
|||
TangemThemePreview {
|
||||
TangemBottomSheetTitle(
|
||||
title = "Title",
|
||||
endButton = TopAppBarButtonUM(
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_information_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
|
|
@ -21,6 +22,7 @@ import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.TokenDetailsScreenTestTags
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -32,7 +34,9 @@ fun HorizontalActionChips(
|
|||
contentPadding: PaddingValues = PaddingValues(TangemTheme.dimens.spacing0),
|
||||
) {
|
||||
LazyRow(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(TokenDetailsScreenTestTags.HORIZONTAL_ACTION_CHIPS),
|
||||
horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing8),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = contentPadding,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -33,6 +34,7 @@ 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.test.TokenDetailsScreenTestTags
|
||||
|
||||
/**
|
||||
* Rounded action button
|
||||
|
|
@ -98,7 +100,7 @@ fun ActionButton(
|
|||
),
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
modifier = modifier.testTag(TokenDetailsScreenTestTags.ACTION_BUTTON),
|
||||
color = color,
|
||||
containerColor = containerColor,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.core.ui.components.ResizableText
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.DialogTestTags
|
||||
import com.tangem.core.ui.test.BaseButtonTestTags
|
||||
import com.tangem.core.ui.utils.MultipleClickPreventer
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LongMethod")
|
||||
@Composable
|
||||
fun TangemButton(
|
||||
text: String,
|
||||
|
|
@ -51,7 +51,7 @@ fun TangemButton(
|
|||
Button(
|
||||
modifier = modifier
|
||||
.heightIn(min = size.toHeightDp())
|
||||
.testTag(DialogTestTags.BUTTON),
|
||||
.testTag(BaseButtonTestTags.BUTTON),
|
||||
onClick = {
|
||||
multipleClickPreventer.processEvent { if (!showProgress) onClick() }
|
||||
},
|
||||
|
|
@ -78,7 +78,8 @@ fun TangemButton(
|
|||
ResizableText(
|
||||
modifier = Modifier
|
||||
.weight(1f, fill = false)
|
||||
.heightIn(MinButtonContentSize, maxContentSize),
|
||||
.heightIn(MinButtonContentSize, maxContentSize)
|
||||
.testTag(BaseButtonTestTags.TEXT),
|
||||
text = text,
|
||||
style = textStyle,
|
||||
color = colors.contentColor(enabled = enabled).value,
|
||||
|
|
@ -92,7 +93,8 @@ fun TangemButton(
|
|||
Icon(
|
||||
modifier = Modifier
|
||||
.buttonContentSize(maxContentSize)
|
||||
.padding(vertical = 2.dp),
|
||||
.padding(vertical = 2.dp)
|
||||
.testTag(BaseButtonTestTags.ICON),
|
||||
painter = painterResource(id = iconResId),
|
||||
tint = colors.contentColor(enabled = enabled).value,
|
||||
contentDescription = null,
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ package com.tangem.core.ui.components.buttons.small
|
|||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -41,20 +42,19 @@ fun TangemIconButton(
|
|||
background: Color = TangemTheme.colors.button.secondary,
|
||||
iconTint: Color = TangemTheme.colors.icon.secondary,
|
||||
) {
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
Icon(
|
||||
painter = rememberVectorPainter(ImageVector.vectorResource(iconRes)),
|
||||
contentDescription = "",
|
||||
tint = iconTint,
|
||||
modifier = modifier
|
||||
.size(24.dp)
|
||||
.clip(shape)
|
||||
.background(background)
|
||||
.size(24.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(ImageVector.vectorResource(iconRes)),
|
||||
contentDescription = "",
|
||||
tint = iconTint,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
}
|
||||
.padding(4.dp)
|
||||
.clickable(
|
||||
onClick = onClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// region Preview
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.CircleShimmer
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -28,11 +29,16 @@ import com.tangem.core.ui.utils.getGreyScaleColorFilter
|
|||
* @param shouldDisplayNetwork specifies whether to display network badge
|
||||
*/
|
||||
@Composable
|
||||
fun CurrencyIcon(state: CurrencyIconState, modifier: Modifier = Modifier, shouldDisplayNetwork: Boolean = true) {
|
||||
fun CurrencyIcon(
|
||||
state: CurrencyIconState,
|
||||
modifier: Modifier = Modifier,
|
||||
shouldDisplayNetwork: Boolean = true,
|
||||
iconSize: Dp = 36.dp,
|
||||
) {
|
||||
BaseContainer(modifier = modifier) {
|
||||
val iconModifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.size(TangemTheme.dimens.size36)
|
||||
.size(iconSize)
|
||||
|
||||
when (state) {
|
||||
is CurrencyIconState.Loading -> LoadingIcon(modifier = iconModifier)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
package com.tangem.core.ui.components.currency.icon
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.getTintForTokenIcon
|
||||
import com.tangem.core.ui.extensions.networkIconResId
|
||||
import com.tangem.core.ui.extensions.tryGetBackgroundForTokenIcon
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
||||
object CurrencyIconStateBuilder {
|
||||
|
||||
fun build(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
isGrayscale: Boolean = false,
|
||||
showCustomBadge: Boolean = true,
|
||||
): CurrencyIconState = when (cryptoCurrency) {
|
||||
is CryptoCurrency.Coin -> fromCoin(cryptoCurrency, isGrayscale, showCustomBadge)
|
||||
is CryptoCurrency.Token -> fromToken(cryptoCurrency, isGrayscale, showCustomBadge)
|
||||
}
|
||||
|
||||
private fun createCoinIcon(
|
||||
url: String? = null,
|
||||
@DrawableRes fallbackResId: Int = R.drawable.ic_empty_64,
|
||||
isGrayscale: Boolean = false,
|
||||
showCustomBadge: Boolean = false,
|
||||
): CurrencyIconState.CoinIcon = CurrencyIconState.CoinIcon(
|
||||
url = url,
|
||||
fallbackResId = fallbackResId,
|
||||
isGrayscale = isGrayscale,
|
||||
showCustomBadge = showCustomBadge,
|
||||
)
|
||||
|
||||
private fun createTokenIcon(
|
||||
url: String? = null,
|
||||
@DrawableRes topBadgeIconResId: Int? = null,
|
||||
isGrayscale: Boolean = false,
|
||||
showCustomBadge: Boolean = false,
|
||||
fallbackTint: Color = Color.Black,
|
||||
fallbackBackground: Color = Color.White,
|
||||
): CurrencyIconState.TokenIcon = CurrencyIconState.TokenIcon(
|
||||
url = url,
|
||||
topBadgeIconResId = topBadgeIconResId,
|
||||
isGrayscale = isGrayscale,
|
||||
fallbackTint = fallbackTint,
|
||||
fallbackBackground = fallbackBackground,
|
||||
showCustomBadge = showCustomBadge,
|
||||
)
|
||||
|
||||
private fun createCustomTokenIcon(
|
||||
tint: Color,
|
||||
background: Color,
|
||||
@DrawableRes topBadgeIconResId: Int,
|
||||
isGrayscale: Boolean = false,
|
||||
showCustomBadge: Boolean = true,
|
||||
): CurrencyIconState.CustomTokenIcon = CurrencyIconState.CustomTokenIcon(
|
||||
tint = tint,
|
||||
background = background,
|
||||
topBadgeIconResId = topBadgeIconResId,
|
||||
isGrayscale = isGrayscale,
|
||||
showCustomBadge = showCustomBadge,
|
||||
)
|
||||
|
||||
private fun fromCoin(
|
||||
coin: CryptoCurrency.Coin,
|
||||
isGrayscale: Boolean = false,
|
||||
showCustomBadge: Boolean = true,
|
||||
): CurrencyIconState.CoinIcon = createCoinIcon(
|
||||
url = coin.iconUrl,
|
||||
fallbackResId = coin.networkIconResId,
|
||||
isGrayscale = isGrayscale || coin.network.isTestnet,
|
||||
showCustomBadge = coin.isCustom && showCustomBadge,
|
||||
)
|
||||
|
||||
private fun fromToken(
|
||||
token: CryptoCurrency.Token,
|
||||
isGrayscale: Boolean = false,
|
||||
showCustomBadge: Boolean = true,
|
||||
): CurrencyIconState {
|
||||
val grayScale = isGrayscale || token.network.isTestnet
|
||||
val background = token.tryGetBackgroundForTokenIcon(grayScale)
|
||||
val tint = getTintForTokenIcon(background)
|
||||
|
||||
return if (token.isCustom && token.iconUrl == null) {
|
||||
createCustomTokenIcon(
|
||||
tint = tint,
|
||||
background = background,
|
||||
topBadgeIconResId = token.networkIconResId,
|
||||
isGrayscale = grayScale,
|
||||
showCustomBadge = showCustomBadge,
|
||||
)
|
||||
} else {
|
||||
createTokenIcon(
|
||||
url = token.iconUrl,
|
||||
topBadgeIconResId = token.networkIconResId,
|
||||
isGrayscale = grayScale,
|
||||
fallbackTint = tint,
|
||||
fallbackBackground = background,
|
||||
showCustomBadge = token.isCustom && showCustomBadge,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import com.tangem.core.ui.extensions.getTintForTokenIcon
|
|||
import com.tangem.core.ui.extensions.networkIconResId
|
||||
import com.tangem.core.ui.extensions.tryGetBackgroundForTokenIcon
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -12,12 +12,8 @@ import androidx.compose.ui.Alignment.Companion.TopCenter
|
|||
import androidx.compose.ui.Alignment.Companion.TopStart
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.ParagraphIntrinsics
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.createFontFamilyResolver
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -28,6 +24,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|||
import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.StakingSendScreenTestTags
|
||||
import com.tangem.core.ui.utils.*
|
||||
import java.math.BigDecimal
|
||||
import java.text.DecimalFormat
|
||||
|
|
@ -77,24 +74,10 @@ fun AmountTextField(
|
|||
) {
|
||||
val decimalFormat = rememberDecimalFormat()
|
||||
BoxWithConstraints(modifier = modifier) {
|
||||
var fontSize = textStyle.fontSize
|
||||
if (isAutoResize) {
|
||||
val calculateIntrinsics = @Composable {
|
||||
val transformedText = visualTransformation.filter(AnnotatedString(value)).text.text
|
||||
ParagraphIntrinsics(
|
||||
text = transformedText,
|
||||
style = textStyle.copy(fontSize = fontSize),
|
||||
density = LocalDensity.current,
|
||||
fontFamilyResolver = createFontFamilyResolver(LocalContext.current),
|
||||
)
|
||||
}
|
||||
var intrinsics = calculateIntrinsics()
|
||||
with(LocalDensity.current) {
|
||||
while (intrinsics.maxIntrinsicWidth > maxWidth.toPx()) {
|
||||
fontSize *= reduceFactor
|
||||
intrinsics = calculateIntrinsics()
|
||||
}
|
||||
}
|
||||
val fontSize = if (isAutoResize) {
|
||||
resizeFont(visualTransformation, value, textStyle, reduceFactor)
|
||||
} else {
|
||||
textStyle.fontSize
|
||||
}
|
||||
val textColor = if (value.isBlank()) TangemTheme.colors.text.disabled else color
|
||||
SimpleTextField(
|
||||
|
|
@ -121,7 +104,9 @@ fun AmountTextField(
|
|||
singleLine = true,
|
||||
readOnly = !isEnabled,
|
||||
visualTransformation = visualTransformation,
|
||||
modifier = Modifier.background(backgroundColor),
|
||||
modifier = Modifier
|
||||
.background(backgroundColor)
|
||||
.testTag(StakingSendScreenTestTags.INPUT_TEXT_FIELD),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,204 @@
|
|||
package com.tangem.core.ui.components.fields
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.BoxWithConstraintsScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.ParagraphIntrinsics
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.createFontFamilyResolver
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
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.TextUnit
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* Simple text field for auto size input.
|
||||
* Can display aligned placeholder.
|
||||
*
|
||||
* @param value initial text
|
||||
* @param onValueChange callback
|
||||
* @param isAutoResize is text font auto resize
|
||||
* @param reduceFactor font resize factor
|
||||
* @param textStyle text and placeholder styles
|
||||
* @param textFieldModifier modifier for [SimpleTextField]
|
||||
* @param boxModifier modifier for [BoxWithConstraints]
|
||||
* @see [SimpleTextField] for other text field params
|
||||
*/
|
||||
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||
@Composable
|
||||
fun AutoSizeTextField(
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
|
||||
// region AutoSize
|
||||
isAutoResize: Boolean = true,
|
||||
@FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
|
||||
reduceFactor: Double = 0.9,
|
||||
|
||||
// region TextField
|
||||
textFieldModifier: Modifier = Modifier,
|
||||
boxModifier: Modifier = Modifier,
|
||||
placeholder: TextReference? = null,
|
||||
singleLine: Boolean = isAutoResize,
|
||||
centered: Boolean = false,
|
||||
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
color: Color = TangemTheme.colors.text.primary1,
|
||||
textStyle: TextStyle = TangemTheme.typography.body2.copy(color = color),
|
||||
placeholderColor: Color = TangemTheme.colors.text.disabled,
|
||||
readOnly: Boolean = false,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
isValuePasted: Boolean = false,
|
||||
onValuePastedTriggerDismiss: () -> Unit = {},
|
||||
decorationBox: (@Composable (innerTextField: @Composable () -> Unit) -> Unit)? = null,
|
||||
) {
|
||||
BoxWithConstraints(modifier = boxModifier) {
|
||||
val fontSize = if (isAutoResize) {
|
||||
resizeFont(visualTransformation, value, textStyle, reduceFactor)
|
||||
} else {
|
||||
textStyle.fontSize
|
||||
}
|
||||
val textColor = if (value.isBlank()) TangemTheme.colors.text.disabled else color
|
||||
SimpleTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
textStyle = textStyle.copy(
|
||||
fontSize = fontSize,
|
||||
textDirection = TextDirection.ContentOrLtr,
|
||||
),
|
||||
isValuePasted = isValuePasted,
|
||||
onValuePastedTriggerDismiss = onValuePastedTriggerDismiss,
|
||||
color = textColor,
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
placeholder = placeholder,
|
||||
placeholderColor = placeholderColor,
|
||||
singleLine = singleLine,
|
||||
interactionSource = interactionSource,
|
||||
readOnly = readOnly,
|
||||
centered = centered,
|
||||
visualTransformation = visualTransformation,
|
||||
decorationBox = decorationBox,
|
||||
modifier = textFieldModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun BoxWithConstraintsScope.resizeFont(
|
||||
visualTransformation: VisualTransformation,
|
||||
value: String,
|
||||
textStyle: TextStyle,
|
||||
reduceFactor: Double,
|
||||
): TextUnit {
|
||||
var result = textStyle.fontSize
|
||||
val calculateIntrinsics = @Composable {
|
||||
val transformedText = visualTransformation.filter(AnnotatedString(value)).text.text
|
||||
ParagraphIntrinsics(
|
||||
text = transformedText,
|
||||
style = textStyle.copy(fontSize = result),
|
||||
density = LocalDensity.current,
|
||||
fontFamilyResolver = createFontFamilyResolver(LocalContext.current),
|
||||
)
|
||||
}
|
||||
var intrinsics = calculateIntrinsics()
|
||||
with(LocalDensity.current) {
|
||||
while (intrinsics.maxIntrinsicWidth > maxWidth.toPx()) {
|
||||
result *= reduceFactor
|
||||
intrinsics = calculateIntrinsics()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// region preview
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Composable
|
||||
private fun AmountTextFieldPreview(
|
||||
@PreviewParameter(AutoSizeTextFieldPreviewProvider::class) data: AutoSizeTextFieldPreviewData,
|
||||
) {
|
||||
var text by remember { mutableStateOf(data.value) }
|
||||
TangemThemePreview {
|
||||
AutoSizeTextField(
|
||||
textFieldModifier = Modifier.fillMaxWidth(),
|
||||
value = text,
|
||||
onValueChange = { text = it },
|
||||
centered = data.centered,
|
||||
isAutoResize = data.isAutoResize,
|
||||
placeholder = data.placeholder,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class AutoSizeTextFieldPreviewProvider : PreviewParameterProvider<AutoSizeTextFieldPreviewData> {
|
||||
override val values = sequenceOf(
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "AutoSizeTextField",
|
||||
placeholder = stringReference("placeholder"),
|
||||
isAutoResize = true,
|
||||
centered = false,
|
||||
),
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "AutoSizeTextFieldAutoSizeTextFieldAutoSizeTextFieldAutoSizeTextField",
|
||||
placeholder = stringReference("placeholder"),
|
||||
isAutoResize = true,
|
||||
centered = false,
|
||||
),
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "AutoSizeTextFieldAutoSizeTextFieldAutoSizeTextFieldAutoSizeTextField",
|
||||
placeholder = stringReference("Placeholder"),
|
||||
isAutoResize = true,
|
||||
centered = false,
|
||||
),
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "",
|
||||
placeholder = stringReference("Placeholder"),
|
||||
isAutoResize = true,
|
||||
centered = false,
|
||||
),
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "AutoSizeTextField",
|
||||
placeholder = stringReference("Placeholder"),
|
||||
isAutoResize = false,
|
||||
centered = true,
|
||||
),
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "AutoSizeTextFieldAutoSizeTextFieldAutoSizeTextFieldAutoSizeTextField",
|
||||
placeholder = stringReference("Placeholder"),
|
||||
isAutoResize = false,
|
||||
centered = true,
|
||||
),
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "",
|
||||
placeholder = stringReference("Placeholder"),
|
||||
isAutoResize = false,
|
||||
centered = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private data class AutoSizeTextFieldPreviewData(
|
||||
val value: String,
|
||||
val placeholder: TextReference,
|
||||
val isAutoResize: Boolean,
|
||||
val centered: Boolean,
|
||||
)
|
||||
// endregion
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.core.ui.components.fields
|
|||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
|
@ -36,9 +37,9 @@ fun PinTextField(
|
|||
value: String,
|
||||
length: Int,
|
||||
isPasswordVisual: Boolean,
|
||||
pinTextColor: PinTextColor,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
wrongCode: Boolean = false,
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val textFieldValue = remember(value) {
|
||||
|
|
@ -72,7 +73,7 @@ fun PinTextField(
|
|||
CellDecoration(
|
||||
length = length,
|
||||
isPasswordVisual = isPasswordVisual,
|
||||
wrongCode = wrongCode,
|
||||
pinTextColor = pinTextColor,
|
||||
value = value,
|
||||
)
|
||||
},
|
||||
|
|
@ -84,17 +85,25 @@ fun PinTextField(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
enum class PinTextColor {
|
||||
Primary,
|
||||
WrongCode,
|
||||
Success,
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber", "LongMethod")
|
||||
@Composable
|
||||
private fun CellDecoration(
|
||||
length: Int,
|
||||
wrongCode: Boolean,
|
||||
pinTextColor: PinTextColor,
|
||||
value: String,
|
||||
modifier: Modifier = Modifier,
|
||||
isPasswordVisual: Boolean = false,
|
||||
) {
|
||||
val textMeasurer = rememberTextMeasurer()
|
||||
val width = textMeasurer.measure("0")
|
||||
val minSize = textMeasurer.measure("0")
|
||||
val minWidth = maxOf(minSize.size.width.dp + 8.dp, 24.dp + 3.dp) // 24.dp is the minimum width of a pin cell
|
||||
val minHeight = maxOf(minSize.size.height.dp, 48.dp) // 48.dp is the minimum height of a pin cell
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
|
|
@ -107,6 +116,18 @@ private fun CellDecoration(
|
|||
""
|
||||
}
|
||||
|
||||
val color = when (pinTextColor) {
|
||||
PinTextColor.Primary -> {
|
||||
if (isPasswordVisual) {
|
||||
TangemTheme.colors.icon.informative
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
}
|
||||
}
|
||||
PinTextColor.WrongCode -> TangemTheme.colors.icon.warning
|
||||
PinTextColor.Success -> TangemTheme.colors.icon.accent
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
|
|
@ -119,26 +140,34 @@ private fun CellDecoration(
|
|||
targetState = char,
|
||||
transitionSpec = {
|
||||
(
|
||||
fadeIn(animationSpec = tween(220, delayMillis = 90)) +
|
||||
slideInVertically(animationSpec = tween(330, delayMillis = 0))
|
||||
fadeIn(animationSpec = tween(90, delayMillis = 90)) +
|
||||
slideInVertically(animationSpec = tween(220, delayMillis = 0))
|
||||
)
|
||||
.togetherWith(
|
||||
fadeOut(animationSpec = tween(90)) + slideOutVertically(tween(220)),
|
||||
)
|
||||
},
|
||||
) { text ->
|
||||
Text(
|
||||
modifier = Modifier.sizeIn(minWidth = width.size.width.dp + 8.dp, minHeight = 48.dp),
|
||||
text = text,
|
||||
style = TangemTheme.typography.h3,
|
||||
color = if (wrongCode) {
|
||||
TangemTheme.colors.text.warning
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = 48.sp,
|
||||
)
|
||||
if (isPasswordVisual && text.isNotEmpty()) {
|
||||
Canvas(
|
||||
Modifier.sizeIn(minWidth = minWidth, minHeight = minHeight),
|
||||
) {
|
||||
drawCircle(
|
||||
color = color,
|
||||
radius = 4.dp.toPx(),
|
||||
center = center,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
modifier = Modifier.sizeIn(minWidth = minWidth, minHeight = minHeight),
|
||||
text = text,
|
||||
style = TangemTheme.typography.h3,
|
||||
color = color,
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = 48.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -152,10 +181,18 @@ private fun Preview() {
|
|||
var text by remember { mutableStateOf("123") }
|
||||
|
||||
Column {
|
||||
PinTextField(
|
||||
value = text,
|
||||
onValueChange = { text = it },
|
||||
isPasswordVisual = true,
|
||||
pinTextColor = PinTextColor.Success,
|
||||
length = 6,
|
||||
)
|
||||
PinTextField(
|
||||
value = text,
|
||||
onValueChange = { text = it },
|
||||
isPasswordVisual = false,
|
||||
pinTextColor = PinTextColor.Primary,
|
||||
length = 6,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,16 +11,20 @@ import androidx.compose.foundation.text.KeyboardActions
|
|||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusManager
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.SoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
|
|
@ -35,6 +39,7 @@ import com.tangem.core.ui.extensions.resolveReference
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.SelectCountryBottomSheetTestTags
|
||||
|
||||
@Composable
|
||||
fun SearchBar(
|
||||
|
|
@ -45,6 +50,7 @@ fun SearchBar(
|
|||
) {
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
|
||||
BasicTextField(
|
||||
|
|
@ -57,7 +63,9 @@ fun SearchBar(
|
|||
} else {
|
||||
state.onActiveChange(false)
|
||||
}
|
||||
},
|
||||
}
|
||||
.focusRequester(focusRequester)
|
||||
.testTag(SelectCountryBottomSheetTestTags.SEARCH_BAR),
|
||||
enabled = enabled,
|
||||
value = state.query,
|
||||
onValueChange = state.onQueryChange,
|
||||
|
|
@ -89,6 +97,10 @@ fun SearchBar(
|
|||
)
|
||||
},
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import androidx.compose.foundation.text.KeyboardActions
|
|||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
|
|
@ -17,6 +18,7 @@ import androidx.compose.ui.text.TextRange
|
|||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -39,6 +41,7 @@ fun SimpleTextField(
|
|||
textStyle: TextStyle = TangemTheme.typography.body2.copy(color = color),
|
||||
placeholderColor: Color = TangemTheme.colors.text.disabled,
|
||||
readOnly: Boolean = false,
|
||||
centered: Boolean = false,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
isValuePasted: Boolean = false,
|
||||
onValuePastedTriggerDismiss: () -> Unit = {},
|
||||
|
|
@ -80,6 +83,8 @@ fun SimpleTextField(
|
|||
onValuePastedTriggerDismiss()
|
||||
}
|
||||
}
|
||||
var textStyle = textStyle.copy(color = color)
|
||||
if (centered) textStyle = textStyle.copy(textAlign = TextAlign.Center)
|
||||
|
||||
BasicTextField(
|
||||
value = textFieldValue,
|
||||
|
|
@ -91,7 +96,7 @@ fun SimpleTextField(
|
|||
|
||||
if (stringChangedSinceLastInvocation) onValueChange(newTextFieldValueState.text)
|
||||
},
|
||||
textStyle = textStyle.copy(color = color),
|
||||
textStyle = textStyle,
|
||||
cursorBrush = SolidColor(TangemTheme.colors.text.primary1),
|
||||
singleLine = singleLine,
|
||||
readOnly = readOnly,
|
||||
|
|
@ -105,6 +110,7 @@ fun SimpleTextField(
|
|||
value = value,
|
||||
textStyle = textStyle,
|
||||
textValue = textValue,
|
||||
centered = centered,
|
||||
color = placeholderColor,
|
||||
)
|
||||
},
|
||||
|
|
@ -118,10 +124,11 @@ private fun SimpleTextPlaceholder(
|
|||
placeholder: TextReference?,
|
||||
value: String,
|
||||
textStyle: TextStyle,
|
||||
centered: Boolean,
|
||||
textValue: @Composable () -> Unit,
|
||||
color: Color = TangemTheme.colors.text.disabled,
|
||||
) {
|
||||
Box {
|
||||
Box(contentAlignment = if (centered) Alignment.Center else Alignment.TopStart) {
|
||||
if (value.isBlank() && placeholder != null) {
|
||||
AnimatedContent(
|
||||
targetState = placeholder,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
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.compose.ui.tooling.preview.PreviewParameter
|
||||
|
|
@ -26,6 +27,7 @@ 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.test.BaseBlockTestTags
|
||||
|
||||
/**
|
||||
* [InputRowDefault](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-807&mode=design&t=86eKp9izWxUvmoCq-4)
|
||||
|
|
@ -64,20 +66,24 @@ fun InputRowDefault(
|
|||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f),
|
||||
.weight(1f)
|
||||
.testTag(BaseBlockTestTags.BLOCK),
|
||||
) {
|
||||
title?.let {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = titleColor,
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing8),
|
||||
modifier = Modifier
|
||||
.padding(bottom = TangemTheme.dimens.spacing8)
|
||||
.testTag(BaseBlockTestTags.BLOCK_TITLE),
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = textColor,
|
||||
modifier = Modifier.testTag(BaseBlockTestTags.BLOCK_TEXT),
|
||||
)
|
||||
}
|
||||
iconRes?.let {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment.Companion.CenterEnd
|
||||
|
|
@ -19,6 +20,7 @@ import androidx.compose.ui.draw.clip
|
|||
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.components.buttons.small.TangemIconButton
|
||||
import com.tangem.core.ui.components.fields.SimpleTextField
|
||||
|
|
@ -69,11 +71,12 @@ fun InputRowRecipient(
|
|||
showDivider: Boolean = false,
|
||||
isLoading: Boolean = false,
|
||||
isValuePasted: Boolean = false,
|
||||
resolvedAddress: String? = null,
|
||||
) {
|
||||
val (titleText, color) = if (isError && error != null) {
|
||||
error to TangemTheme.colors.text.warning
|
||||
} else {
|
||||
title to TangemTheme.colors.text.secondary
|
||||
title to TangemTheme.colors.text.tertiary
|
||||
}
|
||||
DividerContainer(
|
||||
modifier = modifier,
|
||||
|
|
@ -144,11 +147,15 @@ fun InputRowRecipient(
|
|||
} else {
|
||||
TangemTheme.colors.text.primary2
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens.spacing8),
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ResolvedAddressRow(
|
||||
isLoading = isLoading,
|
||||
resolvedAddress = resolvedAddress,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -203,6 +210,38 @@ private fun RowScope.InputIcon(isLoading: Boolean, value: String) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ResolvedAddressRow(isLoading: Boolean, resolvedAddress: String?) {
|
||||
AnimatedContent(
|
||||
targetState = if (resolvedAddress.isNullOrBlank() || isLoading) {
|
||||
ResolvedState.Hide
|
||||
} else {
|
||||
ResolvedState.Show(resolvedAddress)
|
||||
},
|
||||
label = "Resolved Address",
|
||||
) { state ->
|
||||
if (state is ResolvedState.Show) {
|
||||
Column {
|
||||
HorizontalDivider(
|
||||
thickness = 0.5.dp,
|
||||
modifier = Modifier.padding(top = 12.dp, bottom = 12.dp),
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
)
|
||||
Text(
|
||||
text = state.address,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed interface ResolvedState {
|
||||
data object Hide : ResolvedState
|
||||
data class Show(val address: String) : ResolvedState
|
||||
}
|
||||
|
||||
//region preview
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
|
|
@ -224,6 +263,7 @@ private fun InputRowRecipientPreview(
|
|||
onQrCodeClick = {},
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
isRedesignEnabled = false,
|
||||
resolvedAddress = value.resolvedAddress,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -232,6 +272,7 @@ private data class InputRowRecipientPreviewData(
|
|||
val value: String,
|
||||
val isError: Boolean,
|
||||
val isLoading: Boolean = false,
|
||||
val resolvedAddress: String? = null,
|
||||
)
|
||||
|
||||
private class InputRowRecipientPreviewDataProvider : PreviewParameterProvider<InputRowRecipientPreviewData> {
|
||||
|
|
@ -250,6 +291,12 @@ private class InputRowRecipientPreviewDataProvider : PreviewParameterProvider<In
|
|||
isLoading = true,
|
||||
isError = true,
|
||||
),
|
||||
InputRowRecipientPreviewData(
|
||||
value = "vitalik.eth",
|
||||
isLoading = false,
|
||||
isError = true,
|
||||
resolvedAddress = "0x391316d97a07027a0702c8A002c8A0C25d8470",
|
||||
),
|
||||
)
|
||||
}
|
||||
//endregion
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
package com.tangem.core.ui.components.label
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.label.entity.LabelStyle
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
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
|
||||
|
||||
/**
|
||||
* Label component
|
||||
*
|
||||
* @param state component state
|
||||
* @param modifier composable modifier
|
||||
*
|
||||
* @see <a href="https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=4480-1459&t=2QTpi1G7FeTexTFS-4">Figma</a>
|
||||
*/
|
||||
@Composable
|
||||
fun Label(state: LabelUM, modifier: Modifier = Modifier) {
|
||||
val backgroundColor by animateColorAsState(
|
||||
targetValue = when (state.style) {
|
||||
LabelStyle.ACCENT -> TangemTheme.colors.text.accent.copy(alpha = 0.1f)
|
||||
LabelStyle.REGULAR -> TangemTheme.colors.control.unchecked
|
||||
LabelStyle.WARNING -> TangemTheme.colors.text.warning.copy(alpha = 0.1f)
|
||||
},
|
||||
)
|
||||
|
||||
val textColor by animateColorAsState(
|
||||
targetValue = when (state.style) {
|
||||
LabelStyle.ACCENT -> TangemTheme.colors.text.accent
|
||||
LabelStyle.REGULAR -> TangemTheme.colors.text.secondary
|
||||
LabelStyle.WARNING -> TangemTheme.colors.text.warning
|
||||
},
|
||||
)
|
||||
|
||||
AnimatedContent(targetState = state.text) { text ->
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(horizontal = 4.dp)
|
||||
.background(
|
||||
color = backgroundColor,
|
||||
shape = TangemTheme.shapes.roundedCorners8,
|
||||
)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = textColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun LabelPreview() {
|
||||
TangemThemePreview {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
) {
|
||||
Label(
|
||||
state = LabelUM(
|
||||
text = TextReference.Str("Regular Label"),
|
||||
style = LabelStyle.REGULAR,
|
||||
),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Label(
|
||||
state = LabelUM(
|
||||
text = TextReference.Str("Accent Label"),
|
||||
style = LabelStyle.ACCENT,
|
||||
),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Label(
|
||||
state = LabelUM(
|
||||
text = TextReference.Str("Warning Label"),
|
||||
style = LabelStyle.WARNING,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.core.ui.components.label.entity
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
data class LabelUM(
|
||||
val text: TextReference,
|
||||
val style: LabelStyle,
|
||||
)
|
||||
|
||||
enum class LabelStyle {
|
||||
REGULAR, ACCENT, WARNING,
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ import com.tangem.core.ui.components.RectangleShimmer
|
|||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
|
||||
/**
|
||||
* Market price block
|
||||
|
|
@ -120,7 +120,7 @@ private fun PriceBlock(state: MarketPriceBlockState, priceWidthDp: Dp) {
|
|||
)
|
||||
}
|
||||
} else {
|
||||
Price(price = BigDecimalFormatter.EMPTY_BALANCE_SIGN, modifier = priceModifier)
|
||||
Price(price = DASH_SIGN, modifier = priceModifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ 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.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -35,6 +36,7 @@ import com.tangem.core.ui.extensions.resolveReference
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.NotificationTestTags
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState as NotificationButtonsState
|
||||
|
||||
/**
|
||||
|
|
@ -206,6 +208,7 @@ internal fun TextsBlock(
|
|||
text = titleText,
|
||||
color = titleColor,
|
||||
style = TangemTheme.typography.button,
|
||||
modifier = Modifier.testTag(NotificationTestTags.TITLE),
|
||||
)
|
||||
|
||||
SpacerH(height = TangemTheme.dimens.spacing2)
|
||||
|
|
@ -217,6 +220,7 @@ internal fun TextsBlock(
|
|||
text = subtitleText,
|
||||
color = subtitleColor,
|
||||
style = TangemTheme.typography.caption2,
|
||||
modifier = Modifier.testTag(NotificationTestTags.TEXT),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import androidx.constraintlayout.compose.Visibility
|
|||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.audits.AuditLabel
|
||||
import com.tangem.core.ui.components.audits.AuditLabelUM
|
||||
import com.tangem.core.ui.components.badge.Badge
|
||||
|
|
@ -50,8 +51,6 @@ private const val DISABLED_ICON_ALPHA = 0.4f
|
|||
fun ProviderChooseCrypto(providerChooseUM: ProviderChooseUM, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
ConstraintLayout(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.selectedBorder(isSelected = providerChooseUM.isSelected)
|
||||
.clickable(
|
||||
enabled = !providerChooseUM.hasError(),
|
||||
|
|
@ -133,13 +132,23 @@ private fun IconContent(iconUrl: String, modifier: Modifier = Modifier) {
|
|||
SubcomposeAsyncImage(
|
||||
modifier = modifier
|
||||
.size(40.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(TangemColorPalette.Light1),
|
||||
.clip(RoundedCornerShape(8.dp)),
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(iconUrl)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(false)
|
||||
.build(),
|
||||
loading = {
|
||||
RectangleShimmer(radius = 8.dp)
|
||||
},
|
||||
error = {
|
||||
Box(
|
||||
modifier = Modifier.background(
|
||||
color = TangemColorPalette.Light1,
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
),
|
||||
)
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Alignment
|
||||
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.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -23,6 +24,7 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.StakingDetailsScreenTestTags
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
|
|
@ -54,7 +56,8 @@ fun RoundableCornersRow(
|
|||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
)
|
||||
.testTag(StakingDetailsScreenTestTags.PARAMETER_BLOCK),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
|
|
@ -63,6 +66,7 @@ fun RoundableCornersRow(
|
|||
color = startTextColor,
|
||||
maxLines = 1,
|
||||
style = startTextStyle,
|
||||
modifier = Modifier.testTag(StakingDetailsScreenTestTags.PARAMETER_NAME),
|
||||
)
|
||||
if (iconResId != null && iconClick != null) {
|
||||
Icon(
|
||||
|
|
@ -85,6 +89,7 @@ fun RoundableCornersRow(
|
|||
color = endTextColor,
|
||||
maxLines = 1,
|
||||
style = endTextStyle,
|
||||
modifier = Modifier.testTag(StakingDetailsScreenTestTags.PARAMETER_VALUE),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.core.ui.components.rows
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
|
|
@ -13,6 +12,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -22,14 +22,15 @@ import com.tangem.core.ui.components.atoms.text.EllipsisText
|
|||
import com.tangem.core.ui.components.atoms.text.TextEllipsis
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.SelectNetworkFeeBottomSheetTestTags
|
||||
import com.tangem.utils.StringsSigns
|
||||
|
||||
@Composable
|
||||
fun SelectorRowItem(
|
||||
@StringRes titleRes: Int,
|
||||
title: TextReference,
|
||||
@DrawableRes iconRes: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
paddingValues: PaddingValues = PaddingValues(TangemTheme.dimens.spacing12),
|
||||
|
|
@ -68,7 +69,8 @@ fun SelectorRowItem(
|
|||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(paddingValues),
|
||||
.padding(paddingValues)
|
||||
.testTag(SelectNetworkFeeBottomSheetTestTags.SELECTOR_ITEM),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
|
|
@ -77,7 +79,7 @@ fun SelectorRowItem(
|
|||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(titleRes),
|
||||
text = title.resolveReference(),
|
||||
style = textStyle,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing8),
|
||||
|
|
@ -148,7 +150,7 @@ private fun RowScope.SelectorValueContent(
|
|||
private fun SelectorRowItemPreview() {
|
||||
TangemThemePreview {
|
||||
SelectorRowItem(
|
||||
titleRes = R.string.common_fee_selector_option_slow,
|
||||
title = resourceReference(R.string.common_fee_selector_option_slow),
|
||||
iconRes = R.drawable.ic_tortoise_24,
|
||||
preDot = TextReference.Str("1000 ETH"),
|
||||
postDot = TextReference.Str("1000 $"),
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -28,6 +29,7 @@ import com.tangem.core.ui.components.stories.model.StoryConfig
|
|||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.SwapStoriesScreenTestTags
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -101,7 +103,8 @@ inline fun <reified T : StoryConfig> StoriesContainer(
|
|||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = LocalIndication.current,
|
||||
onClick = { config.onClose(watchedCounter) },
|
||||
),
|
||||
)
|
||||
.testTag(SwapStoriesScreenTestTags.CLOSE_BUTTON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ fun getActiveIconRes(blockchainId: String): Int {
|
|||
"zklink", "zklink/test" -> R.drawable.img_zklink_22
|
||||
"vanar-chain", "vanar-chain/test" -> R.drawable.img_vanar_22
|
||||
"pepecoin", "pepecoin/test" -> R.drawable.img_pepecoin_22
|
||||
"hyperliquid", "hyperliquid/test" -> R.drawable.img_hyperliquid_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -186,6 +187,7 @@ fun getActiveIconResByCoinId(coinId: String): Int {
|
|||
"zklink", "zklink/test" -> R.drawable.img_zklink_22
|
||||
"vanar-chain", "vanar-chain/test" -> R.drawable.img_vanar_22
|
||||
"pepecoin-network", "pepecoin-network/test" -> R.drawable.img_pepecoin_22
|
||||
"hyperliquid", "hyperliquid/test" -> R.drawable.img_hyperliquid_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -281,6 +283,7 @@ fun getGreyedOutIconRes(blockchainId: String): Int {
|
|||
"zklink", "zklink/test" -> R.drawable.ic_zklink_22
|
||||
"vanar-chain", "vanar-chain/test" -> R.drawable.ic_vanar_22
|
||||
"pepecoin", "pepecoin/test" -> R.drawable.ic_pepecoin_22
|
||||
"hyperliquid", "hyperliquid/test" -> R.drawable.ic_hyperliquid_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
package com.tangem.core.ui.extensions
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavController
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* The ViewModel is scoped to the parent route Navigation graph
|
||||
* and is provided using the Hilt-generated ViewModel factory
|
||||
*
|
||||
* ```
|
||||
* val navController = rememberNavController()
|
||||
*
|
||||
* navigation(
|
||||
* route = "parent",
|
||||
* startDestination = "parent/1"
|
||||
* ) {
|
||||
* composable("route/1") { entry ->
|
||||
* val viewModel = entry.parentHiltViewModel(navController)
|
||||
* }
|
||||
* composable("route/2") { entry ->
|
||||
* val viewModel = entry.parentHiltViewModel(navController)
|
||||
* }
|
||||
* composable("route/3") { entry ->
|
||||
* val viewModel = entry.parentHiltViewModel(navController)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param navController NavController within the common NavGraph
|
||||
* @throws Exception if there is no parent route
|
||||
*/
|
||||
@Composable
|
||||
inline fun <reified T : ViewModel> NavBackStackEntry.parentHiltViewModel(navController: NavController): T {
|
||||
val viewModelStoreOwner = remember(this) {
|
||||
try {
|
||||
navController.getBackStackEntry(this.destination.parent!!.id)
|
||||
} catch (e: Exception) {
|
||||
Timber.tag("scopedViewModel").e(e, "There is no parent route'")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
return hiltViewModel<T>(viewModelStoreOwner)
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package com.tangem.core.ui.extensions
|
||||
|
||||
import android.R
|
||||
import android.content.Context
|
||||
import android.graphics.Color.*
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@Deprecated("Use only in legacy fragments")
|
||||
fun Fragment.setStatusBarColor(@ColorRes colorResId: Int) {
|
||||
with(requireActivity().window) {
|
||||
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||
statusBarColor = ContextCompat.getColor(requireContext(), colorResId)
|
||||
val view = view ?: return
|
||||
val windowInsetsController = WindowCompat.getInsetsController(this, view)
|
||||
windowInsetsController.isAppearanceLightStatusBars = luminance(requireContext(), colorResId)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO replace by android.graphics.luminance() after bump min API to 24
|
||||
@Suppress("MagicNumber")
|
||||
fun luminance(context: Context, @ColorRes colorRes: Int): Boolean {
|
||||
val color = context.resources.getColor(colorRes, null)
|
||||
if (R.color.transparent == color) return true
|
||||
var rtnValue = false
|
||||
val rgb = intArrayOf(red(color), green(color), blue(color))
|
||||
val brightness = sqrt(
|
||||
rgb[0] * rgb[0] * .241 +
|
||||
rgb[1] * rgb[1] * .691 +
|
||||
rgb[2] * rgb[2] * .068,
|
||||
).toInt()
|
||||
|
||||
// color is light
|
||||
if (brightness >= 200) {
|
||||
rtnValue = true
|
||||
}
|
||||
return rtnValue
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import androidx.compose.foundation.LocalIndication
|
|||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -72,26 +71,24 @@ fun Modifier.conditionalCompose(
|
|||
fun Modifier.selectedBorder(
|
||||
isSelected: Boolean,
|
||||
width: Dp = 2.5.dp,
|
||||
color: Color = TangemTheme.colors.text.accent.copy(alpha = 0.1f),
|
||||
color: Color = TangemTheme.colors.text.accent,
|
||||
radius: Dp = 16.dp,
|
||||
) = conditionalCompose(
|
||||
condition = isSelected,
|
||||
modifier = {
|
||||
border(
|
||||
outsetBorder(
|
||||
width = width,
|
||||
color = color,
|
||||
shape = RoundedCornerShape(radius),
|
||||
color = color.copy(alpha = 0.15f),
|
||||
shape = RoundedCornerShape(radius + 2.dp),
|
||||
)
|
||||
.padding(width)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors.text.accent,
|
||||
shape = RoundedCornerShape(radius - 2.dp),
|
||||
color = color,
|
||||
shape = RoundedCornerShape(radius),
|
||||
)
|
||||
.clip(RoundedCornerShape(radius - 2.dp))
|
||||
.clip(RoundedCornerShape(radius))
|
||||
},
|
||||
otherModifier = {
|
||||
padding(width)
|
||||
.clip(RoundedCornerShape(radius - 2.dp))
|
||||
clip(RoundedCornerShape(radius))
|
||||
},
|
||||
)
|
||||
|
|
@ -121,7 +121,10 @@ fun BigDecimalFiatFormat.price(): BigDecimalFormat = BigDecimalFormat { value ->
|
|||
|
||||
private fun BigDecimal.isLessThanThreshold() = this > BigDecimal.ZERO && this < FIAT_FORMAT_THRESHOLD
|
||||
|
||||
private fun getFiatPriceAmountWithScale(value: BigDecimal): Pair<BigDecimal, Int> {
|
||||
/**
|
||||
* Returns amount with correct scale
|
||||
*/
|
||||
fun getFiatPriceAmountWithScale(value: BigDecimal): Pair<BigDecimal, Int> {
|
||||
return if (value < BigDecimal.ONE) {
|
||||
val leadingZeroes = value.scale() - value.precision()
|
||||
val scale = leadingZeroes + FRACTIONAL_PART_LENGTH_AFTER_LEADING_ZEROES
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class TangemColors internal constructor(
|
|||
control: Control,
|
||||
stroke: Stroke,
|
||||
field: Field,
|
||||
overlay: Overlay,
|
||||
) {
|
||||
var text by mutableStateOf(text)
|
||||
private set
|
||||
|
|
@ -31,6 +32,7 @@ class TangemColors internal constructor(
|
|||
private set
|
||||
var field by mutableStateOf(field)
|
||||
private set
|
||||
var overlay by mutableStateOf(overlay)
|
||||
|
||||
@Stable
|
||||
class Text internal constructor(
|
||||
|
|
@ -220,6 +222,22 @@ class TangemColors internal constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class Overlay internal constructor(
|
||||
primary: Color,
|
||||
secondary: Color,
|
||||
) {
|
||||
var primary by mutableStateOf(primary)
|
||||
private set
|
||||
var secondary by mutableStateOf(secondary)
|
||||
private set
|
||||
|
||||
fun update(other: Overlay) {
|
||||
primary = other.primary
|
||||
secondary = other.secondary
|
||||
}
|
||||
}
|
||||
|
||||
fun update(other: TangemColors) {
|
||||
text.update(other.text)
|
||||
icon.update(other.icon)
|
||||
|
|
@ -228,5 +246,6 @@ class TangemColors internal constructor(
|
|||
control.update(other.control)
|
||||
stroke.update(other.stroke)
|
||||
field.update(other.field)
|
||||
overlay.update(other.overlay)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,9 @@ import android.app.Activity
|
|||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||
import androidx.compose.material.Colors
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.ProvideTextStyle
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -90,7 +90,7 @@ fun TangemTheme(
|
|||
val rootBackgroundColor = rememberedColors.background.secondary
|
||||
|
||||
MaterialTheme(
|
||||
colors = materialThemeColors(colors = themeColors, isDark = isDark),
|
||||
colorScheme = tangemColorScheme(colors = themeColors),
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalTangemColors provides rememberedColors,
|
||||
|
|
@ -143,21 +143,51 @@ object TangemTheme {
|
|||
|
||||
@Stable
|
||||
@Composable
|
||||
private fun materialThemeColors(colors: TangemColors, isDark: Boolean): Colors {
|
||||
return Colors(
|
||||
private fun tangemColorScheme(colors: TangemColors): ColorScheme {
|
||||
return ColorScheme(
|
||||
primary = colors.background.primary,
|
||||
primaryVariant = colors.background.secondary,
|
||||
secondary = colors.button.primary,
|
||||
secondaryVariant = colors.text.accent,
|
||||
background = colors.background.primary,
|
||||
surface = colors.background.secondary,
|
||||
error = colors.text.warning,
|
||||
onPrimary = colors.text.primary1,
|
||||
primaryContainer = colors.background.secondary,
|
||||
onPrimaryContainer = colors.background.action,
|
||||
inversePrimary = colors.background.action,
|
||||
|
||||
secondary = colors.button.primary,
|
||||
onSecondary = colors.text.primary1,
|
||||
secondaryContainer = colors.background.secondary,
|
||||
onSecondaryContainer = colors.text.primary1,
|
||||
|
||||
tertiary = colors.background.tertiary,
|
||||
onTertiary = colors.text.tertiary,
|
||||
tertiaryContainer = colors.background.tertiary,
|
||||
onTertiaryContainer = colors.text.tertiary,
|
||||
|
||||
background = colors.background.primary,
|
||||
onBackground = colors.text.primary1,
|
||||
|
||||
surface = colors.background.secondary,
|
||||
surfaceVariant = colors.background.tertiary,
|
||||
onSurface = colors.text.primary1,
|
||||
onSurfaceVariant = colors.text.secondary,
|
||||
surfaceTint = colors.background.tertiary,
|
||||
inverseSurface = colors.button.disabled,
|
||||
inverseOnSurface = colors.button.primary,
|
||||
surfaceBright = colors.background.secondary,
|
||||
surfaceDim = colors.background.tertiary,
|
||||
surfaceContainer = colors.background.tertiary,
|
||||
surfaceContainerHigh = colors.background.tertiary,
|
||||
surfaceContainerHighest = colors.background.tertiary,
|
||||
surfaceContainerLow = colors.background.tertiary,
|
||||
surfaceContainerLowest = colors.background.tertiary,
|
||||
|
||||
error = colors.text.warning,
|
||||
errorContainer = colors.background.tertiary,
|
||||
onErrorContainer = colors.text.primary2,
|
||||
onError = colors.text.primary2,
|
||||
isLight = !isDark,
|
||||
|
||||
outline = colors.stroke.primary,
|
||||
outlineVariant = colors.stroke.secondary,
|
||||
|
||||
scrim = colors.stroke.transparency,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +238,10 @@ private fun lightThemeColors(): TangemColors {
|
|||
primary = TangemColorPalette.Light1,
|
||||
focused = TangemColorPalette.Light2,
|
||||
),
|
||||
overlay = TangemColors.Overlay(
|
||||
primary = TangemColorPalette.Black.copy(alpha = 0.4f),
|
||||
secondary = TangemColorPalette.Black.copy(alpha = 0.7f),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -258,6 +292,10 @@ private fun darkThemeColors(): TangemColors {
|
|||
primary = TangemColorPalette.Dark5,
|
||||
focused = TangemColorPalette.Dark4,
|
||||
),
|
||||
overlay = TangemColors.Overlay(
|
||||
primary = TangemColorPalette.Black.copy(alpha = 0.4f),
|
||||
secondary = TangemColorPalette.Black.copy(alpha = 0.7f),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
package com.tangem.core.ui.screen
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* An abstract base class for bottom sheet dialogs that use Compose for UI rendering.
|
||||
* Extends [BottomSheetDialogFragment] and implements [ComposeScreen] interface.
|
||||
*/
|
||||
abstract class ComposeBottomSheetFragment : BottomSheetDialogFragment(), ComposeScreen {
|
||||
|
||||
/**
|
||||
* The initial state of the bottom sheet. Default is [BottomSheetBehavior.STATE_EXPANDED].
|
||||
*/
|
||||
open val initialBottomSheetState = BottomSheetBehavior.STATE_EXPANDED
|
||||
|
||||
/**
|
||||
* The fraction of the screen height that the bottom sheet should take when expanded.
|
||||
* Default is `null`, indicating that the height will be determined by the content.
|
||||
*/
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
open val expandedHeightFraction: Float? = null
|
||||
|
||||
override val screenModifier: Modifier
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = Modifier
|
||||
.fillMaxWidth()
|
||||
.let {
|
||||
if (expandedHeightFraction != null) it.fillMaxHeight(expandedHeightFraction!!) else it
|
||||
}
|
||||
.background(
|
||||
color = TangemTheme.colors.background.primary,
|
||||
shape = TangemTheme.shapes.bottomSheet,
|
||||
)
|
||||
|
||||
override fun getTheme(): Int = R.style.AppTheme_TransparentBottomSheetDialog
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
return createComposeView(
|
||||
context = inflater.context,
|
||||
activity = requireActivity(),
|
||||
overrideSystemBarColors = false,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
|
||||
(dialog as BottomSheetDialog).behavior.apply {
|
||||
state = initialBottomSheetState
|
||||
skipCollapsed = true
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
package com.tangem.core.ui.screen
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.tangem.core.ui.R
|
||||
|
||||
/**
|
||||
* An abstract base class for fragments that use Compose for UI rendering.
|
||||
* Extends [Fragment] and implements [ComposeScreen] interface.
|
||||
*/
|
||||
abstract class ComposeFragment : Fragment(), ComposeScreen {
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val isTransitionsInflated = TransitionInflater.from(requireContext()).inflateTransitions()
|
||||
|
||||
return createComposeView(inflater.context, requireActivity()).also {
|
||||
it.isTransitionGroup = isTransitionsInflated
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
|
||||
/*
|
||||
* We need to manually dispatch configuration changes to the Compose view.
|
||||
*
|
||||
|
||||
* `android:configChanges="uiMode"` is set in the manifest.
|
||||
* */
|
||||
view?.dispatchConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates transitions for the fragment. Override this method to customize
|
||||
* enter and exit transitions for the fragment.
|
||||
*
|
||||
* @return `true` if transitions were inflated; `false` otherwise.
|
||||
*/
|
||||
protected open fun TransitionInflater.inflateTransitions(): Boolean {
|
||||
enterTransition = inflateTransition(R.transition.fade)
|
||||
exitTransition = inflateTransition(R.transition.fade)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object BaseBlockTestTags {
|
||||
const val BLOCK = "BASE_BLOCK"
|
||||
const val BLOCK_TITLE = "BASE_BLOCK_TITLE"
|
||||
const val BLOCK_TEXT = "BASE_BLOCK_REWARDS_TEXT"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object BaseButtonTestTags {
|
||||
const val BUTTON = "BASE_BUTTON"
|
||||
const val ICON = "BASE_BUTTON_ICON"
|
||||
const val TEXT = "BASE_BUTTON_TEXT"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object BuyTokenDetailsScreenTestTags {
|
||||
const val EXPAND_FIAT_LIST_BUTTON = "BUY_TOKEN_DETAILS_SCREEN_EXPAND_FIAT_LIST_BUTTON"
|
||||
const val FIAT_CURRENCY_ICON = "BUY_TOKEN_DETAILS_SCREEN_FIAT_CURRENCY_ICON"
|
||||
const val FIAT_AMOUNT_TEXT_FIELD = "BUY_TOKEN_DETAILS_SCREEN_FIAT_AMOUNT_TEXT_FIELD"
|
||||
const val TOKEN_AMOUNT = "BUY_TOKEN_DETAILS_SCREEN_TOKEN_AMOUNT"
|
||||
|
||||
const val PROVIDER_LOADING_TITLE = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_LOADING_TITLE"
|
||||
const val PROVIDER_LOADING_TEXT = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_LOADING_TITLE"
|
||||
|
||||
const val PROVIDER_TITLE = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_TITLE"
|
||||
const val PROVIDER_TEXT = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_TEXT"
|
||||
|
||||
const val TOS_BLOCK = "BUY_TOKEN_DETAILS_SCREEN_TOS_BLOCK"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object BuyTokenFiatListTestTags {
|
||||
const val LAZY_LIST = "BUY_TOKEN_FIAT_LIST_LAZY_LIST"
|
||||
const val LAZY_LIST_ITEM = "BUY_TOKEN_FIAT_LIST_LAZY_LIST_ITEM"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object BuyTokenScreenTestTags {
|
||||
const val LAZY_LIST = "BUY_TOKEN_SCREEN_LAZY_LIST"
|
||||
const val LAZY_LIST_ITEM = "BUY_TOKEN_SCREEN_LAZY_LIST_ITEM"
|
||||
}
|
||||
|
|
@ -2,5 +2,4 @@ package com.tangem.core.ui.test
|
|||
|
||||
object DialogTestTags {
|
||||
const val DIALOG_CONTAINER = "DIALOG_CONTAINER"
|
||||
const val BUTTON = "DIALOG_BUTTON"
|
||||
}
|
||||
|
|
@ -3,4 +3,5 @@ package com.tangem.core.ui.test
|
|||
object DisclaimerScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "DISCLAIMER_SCREEN_CONTAINER"
|
||||
const val ACCEPT_BUTTON = "DISCLAIMER_SCREEN_ACCEPT_BUTTON"
|
||||
const val WEB_VIEW = "DISCLAIMER_SCREEN_WEB_VIEW"
|
||||
}
|
||||
|
|
@ -8,4 +8,5 @@ object MainScreenTestTags {
|
|||
const val WALLET_BALANCE = "MAIN_SCREEN_WALLET_BALANCE"
|
||||
const val WALLET_LIST_ITEM = "MAIN_SCREEN_WALLET_LIST_ITEM"
|
||||
const val ORGANIZE_TOKENS_BUTTON = "MAIN_SCREEN_ORGANIZE_TOKENS_BUTTON"
|
||||
const val MULTI_CURRENCY_ACTION_BUTTON = "MAIN_SCREEN_MULTI_CURRENCY_ACTION_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object NotificationTestTags {
|
||||
const val TITLE = "NOTIFICATION_TITLE"
|
||||
const val TEXT = "NOTIFICATION_TEXT"
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object ReferralProgramScreenTestTags {
|
||||
const val IMAGE = "REFERRAL_PROGRAM_SCREEN_IMAGE"
|
||||
const val CONDITION_BLOCK = "REFERRAL_PROGRAM_SCREEN_CONDITION_BLOCK"
|
||||
const val INFO_FOR_YOU_TEXT = "REFERRAL_PROGRAM_SCREEN_INFO_FOR_YOU_TEXT"
|
||||
const val INFO_FOR_YOUR_FRIEND_TEXT = "REFERRAL_PROGRAM_INFO_FOR_YOUR_FRIEND_TEXT"
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object ResidenceSettingsScreenTestTags {
|
||||
const val COUNTRY_NAME = "RESIDENCE_SETTINGS_SCREEN_COUNTRY_NAME"
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SelectCountryBottomSheetTestTags {
|
||||
|
||||
const val LAZY_LIST = "SELECT_COUNTRY_BOTTOM_SHEET_LAZY_LIST"
|
||||
const val COUNTRY_ITEM = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_ITEM"
|
||||
const val UNAVAILABLE_COUNTRY_ITEM = "SELECT_COUNTRY_BOTTOM_SHEET_UNAVAILABLE_COUNTRY_ITEM"
|
||||
const val SEARCH_BAR = "SELECT_COUNTRY_BOTTOM_SHEET_SEARCH_BAR"
|
||||
const val COUNTRY_ICON = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_ICON"
|
||||
const val UNAVAILABLE_COUNTRY_ICON = "SELECT_COUNTRY_BOTTOM_SHEET_UNAVAILABLE_COUNTRY_ICON"
|
||||
const val COUNTRY_NAME = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_NAME"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SelectNetworkFeeBottomSheetTestTags {
|
||||
const val READ_MORE_TEXT = "SELECT_NETWORK_FEE_READ_MORE_TEXT"
|
||||
const val SELECTOR_ITEM = "SELECT_NETWORK_FEE_SELECTOR_ITEM"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SelectPaymentMethodBottomSheetTestTags {
|
||||
|
||||
const val LAZY_LIST = "SELECT_PAYMENT_METHOD_LAZY_LIST"
|
||||
const val PAYMENT_METHOD_ICON = "PAYMENT_METHOD_NAME"
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SelectProviderBottomSheetTestTags {
|
||||
|
||||
const val PAYMENT_METHOD_ICON = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_ICON"
|
||||
const val PAYMENT_METHOD_TITLE = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_TITLE"
|
||||
const val PAYMENT_METHOD_NAME = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_NAME"
|
||||
const val PAYMENT_METHOD_EXPAND_BUTTON = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_EXPAND_BUTTON"
|
||||
const val TOKEN_AMOUNT = "SELECT_PROVIDER_BOTTOM_SHEET_TOKEN_AMOUNT"
|
||||
const val AVAILABLE_PROVIDER_NAME = "SELECT_PROVIDER_BOTTOM_SHEET_AVAILABLE_PROVIDER_NAME"
|
||||
const val AVAILABLE_PROVIDER_ITEM = "SELECT_PROVIDER_BOTTOM_SHEET_AVAILABLE_PROVIDER_ITEM"
|
||||
const val UNAVAILABLE_PROVIDER_ITEM = "SELECT_PROVIDER_BOTTOM_SHEET_UNAVAILABLE_PROVIDER_ITEM"
|
||||
const val UNAVAILABLE_PROVIDER_NAME = "SELECT_PROVIDER_BOTTOM_SHEET_UNAVAILABLE_PROVIDER_NAME"
|
||||
const val UNAVAILABLE_PROVIDER_SUBTITLE = "SELECT_PROVIDER_BOTTOM_SHEET_UNAVAILABLE_PROVIDER_SUBTITLE"
|
||||
const val MORE_PROVIDERS_ICON = "SELECT_PROVIDER_BOTTOM_SHEET_MORE_PROVIDERS_ICON"
|
||||
const val MORE_PROVIDERS_TEXT = "SELECT_PROVIDER_BOTTOM_SHEET_MORE_PROVIDERS_TEXT"
|
||||
const val BEST_RATE_LABEL = "SELECT_PROVIDER_BOTTOM_SHEET_BEST_RATE_LABEL"
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object StakingDetailsScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "TOKEN_DETAILS_SCREEN_CONTAINER"
|
||||
|
||||
const val BANNER_IMAGE = "TOKEN_DETAILS_SCREEN_BANNER_IMAGE"
|
||||
const val BANNER_TEXT = "TOKEN_DETAILS_SCREEN_BANNER_TEXT"
|
||||
|
||||
const val PARAMETER_BLOCK = "STAKING_DETAILS_PARAMETER_BLOCK"
|
||||
const val PARAMETER_NAME = "STAKING_DETAILS_PARAMETER_NAME"
|
||||
const val PARAMETER_VALUE = "STAKING_DETAILS_PARAMETER_VALUE"
|
||||
const val TOS_TEXT = "STAKING_DETAILS_TOS_TEXT"
|
||||
|
||||
const val ACTIVE_STAKING_BLOCK = "STAKING_DETAILS_ACTIVE_STAKING_BLOCK"
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object StakingSendDetailsScreenTestTags {
|
||||
|
||||
const val PRIMARY_AMOUNT = "STAKING_SEND_DETAILS_SCREEN_PRIMARY_AMOUNT"
|
||||
const val SECONDARY_AMOUNT = "TAKING_SEND_DETAILS_SCREEN_SECONDARY_AMOUNT"
|
||||
|
||||
const val VALIDATOR_BLOCK = "TAKING_SEND_DETAILS_SCREEN_VALIDATOR_BLOCK"
|
||||
const val NETWORK_FEE_BLOCK = "TAKING_SEND_DETAILS_SCREEN_NETWORK_FEE_BLOCK"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object StakingSendScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "STAKING_SEND_SCREEN_CONTAINER"
|
||||
|
||||
const val AMOUNT_CONTAINER_TITLE = "STAKING_SEND_SCREEN_AMOUNT_CONTAINER_TITLE"
|
||||
const val AMOUNT_CONTAINER_TEXT = "STAKING_SEND_SCREEN_AMOUNT_CONTAINER_TEXT"
|
||||
const val INPUT_TEXT_FIELD = "STAKING_SEND_SCREEN_INPUT_TEXT_FIELD"
|
||||
const val SECONDARY_AMOUNT = "STAKING_SEND_SCREEN_SECONDARY_AMOUNT"
|
||||
|
||||
const val CURRENCY_BUTTON = "STAKING_SEND_SCREEN_CURRENCY_BUTTON"
|
||||
const val FIAT_ICON = "STAKING_SEND_SCREEN_FIAT_ICON"
|
||||
const val CURRENCY_ICON = "STAKING_SEND_SCREEN_CURRENCY_ICON"
|
||||
const val MAX_BUTTON = "STAKING_SEND_SCREEN_MAX_BUTTON"
|
||||
const val PREVIOUS_BUTTON = "STAKING_SEND_SCREEN_PREVIOUS_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SwapStoriesScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "SWAP_STORIES_SCREEN_CONTAINER"
|
||||
const val CLOSE_BUTTON = "SWAP_STORIES_SCREEN_CLOSE_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SwapTokenScreenTestTags {
|
||||
const val SWAP_BLOCK_HEADER = "SWAP_TOKEN_SCREEN_SWAP_BLOCK"
|
||||
const val BALANCE = "SWAP_TOKEN_SCREEN_BALANCE"
|
||||
const val SWAP_TEXT_FIELD = "SWAP_TOKEN_SCREEN_SWAP_TEXT_FIELD"
|
||||
const val RECEIVE_TEXT_FIELD = "SWAP_TOKEN_SCREEN_RECEIVE_TEXT_FIELD"
|
||||
const val RECEIVE_AMOUNT_SHIMMER = "SWAP_TOKEN_SCREEN_RECEIVE_AMOUNT_SHIMMER"
|
||||
const val PROVIDERS_BLOCK = "SWAP_TOKEN_SCREEN_PROVIDERS_BLOCK"
|
||||
const val SWAP_BUTTON = "SWAP_TOKEN_SCREEN_SWAP_BUTTON"
|
||||
const val TOKEN = "SWAP_TOKEN_SCREEN_TOKEN"
|
||||
const val TOKEN_NAME = "SWAP_TOKEN_SCREEN_TOKEN_NAME"
|
||||
const val TOKEN_ICON = "SWAP_TOKEN_SCREEN_TOKEN_ICON"
|
||||
}
|
||||
|
|
@ -2,4 +2,19 @@ package com.tangem.core.ui.test
|
|||
|
||||
object TokenDetailsScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "TOKEN_DETAILS_SCREEN_CONTAINER"
|
||||
|
||||
const val TOKEN_TITLE = "TOKEN_DETAILS_SCREEN_TOKEN_TITLE"
|
||||
const val ACTION_BUTTON = "TOKEN_DETAILS_SCREEN_ACTION_BUTTON"
|
||||
const val HORIZONTAL_ACTION_CHIPS = "TOKEN_DETAILS_SCREEN_HORIZONTAL_ACTION_CHIPS"
|
||||
|
||||
const val STAKING_BLOCK = "TOKEN_DETAILS_SCREEN_STAKING_BLOCK"
|
||||
const val STAKING_AVAILABLE_BLOCK = "TOKEN_DETAILS_SCREEN_STAKING_AVAILABLE_BLOCK"
|
||||
const val STAKING_CURRENCY_ICON = "TOKEN_DETAILS_SCREEN_STAKING_STAKING_CURRENCY_ICON"
|
||||
const val STAKING_SERVICE_TITLE = "TOKEN_DETAILS_SCREEN_STAKING_STAKING_SERVICE_TITLE"
|
||||
const val STAKING_SERVICE_TEXT = "TOKEN_DETAILS_SCREEN_STAKING_STAKING_SERVICE_TEXT"
|
||||
const val STAKING_FIAT_AMOUNT = "TOKEN_DETAILS_SCREEN_STAKING_FIAT_AMOUNT"
|
||||
const val STAKING_DOT = "TOKEN_DETAILS_SCREEN_STAKING_DOT"
|
||||
const val STAKING_TOKEN_AMOUNT = "TOKEN_DETAILS_SCREEN_STAKING_TOKEN_AMOUNT"
|
||||
const val STAKING_REWARD_VALUE = "TOKEN_DETAILS_SCREEN_STAKING_REWARD_VALUE"
|
||||
const val STAKING_CHEVRON_ICON = "TOKEN_DETAILS_SCREEN_STAKING_CHEVRON_ICON"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object TopAppBarTestTags {
|
||||
const val TITLE = "TOP_APP_BAR_TITLE"
|
||||
const val MORE_BUTTON = "TOP_APP_BAR_MORE_BUTTON"
|
||||
const val CLOSE_BUTTON = "TOP_APP_BAR_CLOSE_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.AnimationVector1D
|
||||
import androidx.compose.animation.core.Easing
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
typealias AnimatedValue = Pair<Float, Float>
|
||||
|
||||
@Composable
|
||||
fun AnimatedValue.toAnimatable(
|
||||
isPaused: Boolean,
|
||||
duration: Int,
|
||||
easing: Easing = LinearEasing,
|
||||
): Animatable<Float, AnimationVector1D> {
|
||||
return animatable(
|
||||
values = this,
|
||||
isPaused = isPaused,
|
||||
duration = duration,
|
||||
easing = easing,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun animatable(
|
||||
values: AnimatedValue,
|
||||
duration: Int,
|
||||
isPaused: Boolean = false,
|
||||
easing: Easing = LinearEasing,
|
||||
): Animatable<Float, AnimationVector1D> {
|
||||
val animatable = remember { Animatable(values.first) }
|
||||
|
||||
LaunchedEffect(isPaused) {
|
||||
if (isPaused) {
|
||||
animatable.stop()
|
||||
} else {
|
||||
animatable.animateTo(
|
||||
targetValue = values.second,
|
||||
animationSpec = tween(
|
||||
durationMillis = duration,
|
||||
easing = easing,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
return animatable
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import com.tangem.utils.StringsSigns.LOWER_SIGN
|
||||
import com.tangem.utils.StringsSigns.TILDE_SIGN
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.text.NumberFormat
|
||||
import java.util.Currency
|
||||
import java.util.Locale
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
object BigDecimalFormatter {
|
||||
|
||||
const val EMPTY_BALANCE_SIGN = DASH_SIGN
|
||||
private const val CAN_BE_LOWER_SIGN = LOWER_SIGN
|
||||
|
||||
private val FIAT_FORMAT_THRESHOLD = BigDecimal("0.01")
|
||||
|
||||
private const val FIAT_MARKET_DEFAULT_DIGITS = 2
|
||||
private const val FIAT_MARKET_EXTENDED_DIGITS = 6
|
||||
private const val FRACTIONAL_PART_LENGTH_AFTER_LEADING_ZEROES = 4
|
||||
|
||||
private val usdCurrency = Currency.getInstance("USD")
|
||||
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
fun formatFiatAmount(
|
||||
fiatAmount: BigDecimal?,
|
||||
fiatCurrencyCode: String,
|
||||
fiatCurrencySymbol: String,
|
||||
decimals: Int = FIAT_MARKET_DEFAULT_DIGITS,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
withApproximateSign: Boolean = false,
|
||||
): String {
|
||||
if (fiatAmount == null) return EMPTY_BALANCE_SIGN
|
||||
|
||||
val formatterCurrency = getCurrency(fiatCurrencyCode)
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = formatterCurrency
|
||||
maximumFractionDigits = decimals
|
||||
minimumFractionDigits = decimals
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
return if (fiatAmount.checkFiatThreshold()) {
|
||||
buildString {
|
||||
append(CAN_BE_LOWER_SIGN)
|
||||
append(
|
||||
formatter.format(FIAT_FORMAT_THRESHOLD)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val formattedAmount = formatter.format(fiatAmount)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
|
||||
if (withApproximateSign) {
|
||||
buildString {
|
||||
append(TILDE_SIGN)
|
||||
append(formattedAmount)
|
||||
}
|
||||
} else {
|
||||
formattedAmount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
fun formatFiatAmountUncapped(
|
||||
fiatAmount: BigDecimal?,
|
||||
fiatCurrencyCode: String,
|
||||
fiatCurrencySymbol: String,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): String {
|
||||
if (fiatAmount == null) return EMPTY_BALANCE_SIGN
|
||||
val formatterCurrency = getCurrency(fiatCurrencyCode)
|
||||
|
||||
val digits = if (fiatAmount.checkFiatThreshold()) {
|
||||
FIAT_MARKET_EXTENDED_DIGITS
|
||||
} else {
|
||||
FIAT_MARKET_DEFAULT_DIGITS
|
||||
}
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = formatterCurrency
|
||||
maximumFractionDigits = digits
|
||||
minimumFractionDigits = FIAT_MARKET_DEFAULT_DIGITS
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
return formatter.format(fiatAmount)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
}
|
||||
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
fun formatFiatPriceUncapped(
|
||||
fiatAmount: BigDecimal?,
|
||||
fiatCurrencyCode: String,
|
||||
fiatCurrencySymbol: String,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): String {
|
||||
if (fiatAmount == null) return EMPTY_BALANCE_SIGN
|
||||
val formatterCurrency = getCurrency(fiatCurrencyCode)
|
||||
|
||||
val (formattedAmount, finalScale) = getFiatPriceUncappedWithScale(value = fiatAmount)
|
||||
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = formatterCurrency
|
||||
maximumFractionDigits = finalScale
|
||||
minimumFractionDigits = FIAT_MARKET_DEFAULT_DIGITS
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
return formatter.format(formattedAmount)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
}
|
||||
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
fun getFiatPriceUncappedWithScale(value: BigDecimal): Pair<BigDecimal, Int> {
|
||||
return if (value < BigDecimal.ONE) {
|
||||
val leadingZeroes = value.scale() - value.precision()
|
||||
val scale = leadingZeroes + FRACTIONAL_PART_LENGTH_AFTER_LEADING_ZEROES
|
||||
|
||||
val amount = value
|
||||
.setScale(scale, RoundingMode.HALF_UP)
|
||||
.stripTrailingZeros()
|
||||
|
||||
amount to amount.scale()
|
||||
} else {
|
||||
value to FIAT_MARKET_DEFAULT_DIGITS
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrency(code: String): Currency {
|
||||
return runCatching { Currency.getInstance(code) }
|
||||
.getOrElse { e ->
|
||||
// Currency code is not valid ISO 4217 code
|
||||
if (e is IllegalArgumentException) {
|
||||
usdCurrency
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BigDecimal.checkFiatThreshold() = this > BigDecimal.ZERO && this < FIAT_FORMAT_THRESHOLD
|
||||
}
|
||||
|
|
@ -1,9 +1,15 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Stable
|
||||
@Composable
|
||||
|
|
@ -15,4 +21,16 @@ fun convertPxToDp(px: Float): Dp = convertPxToDp(px, density = LocalDensity.curr
|
|||
|
||||
fun Dp.toPx(density: Float): Float = this.value * density
|
||||
|
||||
fun convertPxToDp(px: Float, density: Float): Dp = Dp(value = px / density)
|
||||
fun convertPxToDp(px: Float, density: Float): Dp = Dp(value = px / density)
|
||||
|
||||
fun Context.dpToPx(dp: Float): Float = dp * resources.displayMetrics.density
|
||||
fun Context.pxToDp(px: Float): Float = (px / resources.displayMetrics.density).roundToInt().toFloat()
|
||||
|
||||
@Composable
|
||||
fun Painter.dpSize(): DpSize = DpSize(
|
||||
intrinsicSize.width.pxToDp().dp,
|
||||
intrinsicSize.height.pxToDp().dp,
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun Float.pxToDp(): Float = LocalContext.current.pxToDp(this)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
fun asImageBitmap(@DrawableRes drawableId: Int): ImageBitmap {
|
||||
val drawable = requireNotNull(AppCompatResources.getDrawable(LocalContext.current, drawableId)) {
|
||||
"drawable is null"
|
||||
}
|
||||
return drawable.toBitmap().asImageBitmap()
|
||||
}
|
||||
BIN
core/ui/src/main/res/drawable-hdpi/img_meet_tangem.webp
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
core/ui/src/main/res/drawable-hdpi/img_revolutionary_wallet.webp
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
core/ui/src/main/res/drawable-hdpi/img_tangem_for_everyone.webp
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
core/ui/src/main/res/drawable-mdpi/img_meet_tangem.webp
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
core/ui/src/main/res/drawable-mdpi/img_revolutionary_wallet.webp
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
core/ui/src/main/res/drawable-mdpi/img_tangem_for_everyone.webp
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
core/ui/src/main/res/drawable-xhdpi/img_meet_tangem.webp
Normal file
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 86 KiB |
BIN
core/ui/src/main/res/drawable-xhdpi/img_tangem_for_everyone.webp
Normal file
|
After Width: | Height: | Size: 222 KiB |
BIN
core/ui/src/main/res/drawable-xxhdpi/img_meet_tangem.webp
Normal file
|
After Width: | Height: | Size: 509 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 484 KiB |
BIN
core/ui/src/main/res/drawable-xxxhdpi/img_meet_tangem.webp
Normal file
|
After Width: | Height: | Size: 901 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 818 KiB |
BIN
core/ui/src/main/res/drawable/currency0.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
core/ui/src/main/res/drawable/currency1.webp
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
core/ui/src/main/res/drawable/currency2.webp
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
core/ui/src/main/res/drawable/currency3.webp
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
core/ui/src/main/res/drawable/currency4.webp
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
core/ui/src/main/res/drawable/dapps1.webp
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
core/ui/src/main/res/drawable/dapps2.webp
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
core/ui/src/main/res/drawable/dapps3.webp
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
core/ui/src/main/res/drawable/dapps4.webp
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
core/ui/src/main/res/drawable/dapps5.webp
Normal file
|
After Width: | Height: | Size: 128 KiB |
13
core/ui/src/main/res/drawable/ic_archive_24.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h24v24h-24z"/>
|
||||
<path
|
||||
android:pathData="M12,18L16,14L14.6,12.6L13,14.2V10H11V14.2L9.4,12.6L8,14L12,18ZM5,8V19H19V8H5ZM5,21C4.45,21 3.979,20.804 3.588,20.413C3.196,20.021 3,19.55 3,19V6.525C3,6.292 3.037,6.067 3.112,5.85C3.188,5.633 3.3,5.433 3.45,5.25L4.7,3.725C4.883,3.492 5.113,3.313 5.387,3.188C5.662,3.063 5.95,3 6.25,3H17.75C18.05,3 18.337,3.063 18.612,3.188C18.888,3.313 19.117,3.492 19.3,3.725L20.55,5.25C20.7,5.433 20.813,5.633 20.888,5.85C20.962,6.067 21,6.292 21,6.525V19C21,19.55 20.804,20.021 20.413,20.413C20.021,20.804 19.55,21 19,21H5ZM5.4,6H18.6L17.75,5H6.25L5.4,6Z"
|
||||
android:fillColor="#000000"/>
|
||||
</group>
|
||||
</vector>
|
||||
12
core/ui/src/main/res/drawable/ic_copy_new_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="M21.099,15C21.099,13.56 21.098,12.571 20.998,11.828C20.901,11.111 20.729,10.759 20.485,10.514C20.241,10.27 19.889,10.098 19.172,10.002C18.429,9.902 17.439,9.9 16,9.9H15C13.56,9.9 12.571,9.902 11.828,10.002C11.111,10.098 10.759,10.27 10.514,10.514C10.27,10.759 10.098,11.111 10.002,11.828C9.902,12.571 9.9,13.56 9.9,15V16C9.9,17.44 9.902,18.429 10.002,19.172C10.098,19.889 10.27,20.241 10.514,20.485C10.759,20.729 11.111,20.902 11.828,20.998C12.571,21.098 13.56,21.1 15,21.1H16C17.439,21.1 18.429,21.098 19.172,20.998C19.889,20.902 20.241,20.729 20.485,20.485C20.729,20.241 20.901,19.889 20.998,19.172C21.098,18.429 21.099,17.44 21.099,16V15ZM22.9,16C22.9,17.389 22.902,18.521 22.782,19.412C22.659,20.329 22.392,21.123 21.757,21.758C21.123,22.392 20.328,22.659 19.412,22.782C18.521,22.902 17.389,22.9 16,22.9H15C13.611,22.9 12.479,22.902 11.588,22.782C10.671,22.659 9.876,22.392 9.242,21.758C8.608,21.123 8.341,20.329 8.217,19.412C8.098,18.521 8.099,17.389 8.099,16V15C8.099,13.611 8.098,12.479 8.217,11.588C8.341,10.671 8.608,9.876 9.242,9.242C9.876,8.608 10.671,8.341 11.588,8.218C12.479,8.098 13.611,8.099 15,8.099H16C17.389,8.099 18.521,8.098 19.412,8.218C20.328,8.341 21.123,8.608 21.757,9.242C22.392,9.876 22.659,10.671 22.782,11.588C22.902,12.479 22.9,13.611 22.9,15V16Z"
|
||||
android:fillColor="#919191"/>
|
||||
<path
|
||||
android:pathData="M2.099,9.999V8.999L2.099,8.945C2.099,7.581 2.099,6.466 2.217,5.587C2.34,4.67 2.607,3.876 3.241,3.241C3.876,2.607 4.67,2.34 5.587,2.217C6.466,2.099 7.581,2.099 8.945,2.099L8.999,2.099H9.999L10.054,2.099C11.417,2.099 12.532,2.099 13.411,2.217C14.328,2.34 15.123,2.607 15.757,3.241C15.978,3.463 16.16,3.709 16.307,3.983C16.542,4.42 16.378,4.966 15.941,5.201C15.503,5.437 14.957,5.273 14.722,4.835C14.656,4.713 14.579,4.608 14.484,4.514C14.24,4.27 13.888,4.098 13.171,4.001C12.428,3.901 11.439,3.9 9.999,3.9H8.999C7.559,3.9 6.57,3.901 5.827,4.001C5.11,4.098 4.758,4.27 4.514,4.514C4.269,4.758 4.097,5.11 4.001,5.827C3.901,6.57 3.899,7.559 3.899,8.999V9.999C3.899,11.439 3.901,12.428 4.001,13.171C4.097,13.888 4.269,14.24 4.514,14.484C4.692,14.663 4.923,14.797 5.292,14.895C5.772,15.022 6.059,15.515 5.932,15.995C5.812,16.446 5.371,16.726 4.921,16.654L4.831,16.635L4.606,16.569C4.09,16.404 3.633,16.149 3.241,15.757C2.607,15.123 2.34,14.328 2.217,13.411C2.099,12.532 2.099,11.417 2.099,10.054L2.099,9.999Z"
|
||||
android:fillColor="#919191"/>
|
||||
</vector>
|
||||
38
core/ui/src/main/res/drawable/ic_ens_36.xml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="36dp"
|
||||
android:height="37dp"
|
||||
android:viewportWidth="36"
|
||||
android:viewportHeight="37">
|
||||
<path
|
||||
android:pathData="M0,18.658C0,8.717 8.059,0.658 18,0.658C27.941,0.658 36,8.717 36,18.658C36,28.599 27.941,36.658 18,36.658C8.059,36.658 0,28.599 0,18.658Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="7.08"
|
||||
android:startY="6.638"
|
||||
android:endX="38.587"
|
||||
android:endY="33.361"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FF8BA0FF"/>
|
||||
<item android:offset="0.524" android:color="#FF65B1FF"/>
|
||||
<item android:offset="1" android:color="#FF91D0FF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M9.314,8.766h17.372v19.784h-17.372z"/>
|
||||
<path
|
||||
android:pathData="M17.55,9.187L12.219,17.958C12.177,18.027 12.08,18.035 12.028,17.973C11.559,17.416 9.811,15.045 11.974,12.885C13.948,10.913 16.463,9.507 17.395,9.021C17.501,8.966 17.612,9.085 17.55,9.187Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M17.255,28.285C17.362,28.36 17.493,28.233 17.422,28.124C16.231,26.313 12.273,20.287 11.726,19.383C11.187,18.491 10.126,17.008 10.038,15.739C10.029,15.613 9.854,15.587 9.809,15.706C9.738,15.898 9.663,16.127 9.592,16.389C8.702,19.695 9.995,23.203 12.802,25.168L17.255,28.285Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M17.988,28.129L23.319,19.358C23.36,19.289 23.458,19.281 23.509,19.343C23.979,19.9 25.727,22.271 23.564,24.431C21.589,26.403 19.075,27.809 18.143,28.295C18.037,28.35 17.926,28.231 17.988,28.129Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M18.288,9.03C18.182,8.955 18.051,9.082 18.122,9.191C19.313,11.002 23.271,17.028 23.818,17.932C24.357,18.824 25.418,20.307 25.506,21.576C25.515,21.702 25.69,21.728 25.734,21.609C25.805,21.417 25.881,21.188 25.951,20.926C26.841,17.62 25.549,14.112 22.742,12.147L18.288,9.03Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</group>
|
||||
</vector>
|
||||
9
core/ui/src/main/res/drawable/ic_hyperliquid_22.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<path
|
||||
android:pathData="M20,10.912C20,17.156 16.358,19.16 14.439,17.376C12.86,15.921 12.39,12.847 10.014,12.531C6.999,12.133 6.738,16.346 4.754,16.346C2.444,16.346 2,12.819 2,11.008C2,9.155 2.496,6.63 4.467,6.63C6.764,6.63 6.895,10.24 9.767,10.047C12.625,9.842 12.677,6.081 14.531,4.476C16.149,3.09 20,4.585 20,10.912Z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||